From afc18668f3d3a140ca3eed11edb0f7f6584df034 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 12:43:54 -0400 Subject: [PATCH 001/278] tweak sip ws code to avoid double free --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/tport/tport_type_ws.c | 34 +++++++++---------- .../libsofia-sip-ua/tport/tport_ws.h | 2 +- libs/sofia-sip/libsofia-sip-ua/tport/ws.c | 8 ++--- libs/sofia-sip/libsofia-sip-ua/tport/ws.h | 2 +- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index 91bc2e3e8a..0440205a53 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Tue Jun 25 09:28:40 CDT 2013 +Wed Jun 26 12:43:51 EDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c index c6846be1e1..d9ed0b6980 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c @@ -207,23 +207,14 @@ int tport_recv_stream_ws(tport_t *self) int err; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; tport_ws_t *wstp = (tport_ws_t *)self; - wsh_t *ws = wstp->ws; - tport_ws_primary_t *wspri = (tport_ws_primary_t *)self->tp_pri; uint8_t *data; ws_opcode_t oc; if (wstp->ws_initialized < 0) { return -1; - } else if (wstp->ws_initialized == 0) { - if (ws_init(ws, self->tp_socket, 65336, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) == -2) { - return 2; - } - wstp->ws_initialized = 1; - self->tp_pre_framed = 1; - return 1; } - N = ws_read_frame(ws, &oc, &data); + N = ws_read_frame(&wstp->ws, &oc, &data); if (N == -2) { return 2; @@ -276,7 +267,6 @@ ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg, size_t i, j, n, m, size = 0; ssize_t nerror; tport_ws_t *wstp = (tport_ws_t *)self; - wsh_t *ws = wstp->ws; enum { WSBUFSIZE = 2048 }; @@ -311,10 +301,10 @@ ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg, iov[j].siv_base = buf, iov[j].siv_len = m; } - nerror = ws_feed_buf(ws, buf, m); + nerror = ws_feed_buf(&wstp->ws, buf, m); SU_DEBUG_9(("tport_ws_writevec: vec %p %p %lu ("MOD_ZD")\n", - (void *)ws, (void *)iov[i].siv_base, (LU)iov[i].siv_len, + (void *)&wstp->ws, (void *)iov[i].siv_base, (LU)iov[i].siv_len, nerror)); if (nerror == -1) { @@ -333,7 +323,7 @@ ssize_t tport_send_stream_ws(tport_t const *self, msg_t *msg, break; } - ws_send_buf(ws, WSOC_TEXT); + ws_send_buf(&wstp->ws, WSOC_TEXT); return size; @@ -453,6 +443,15 @@ int tport_ws_init_secondary(tport_t *self, int socket, int accepted, if ( wspri->ws_secure ) wstp->ws_secure = 1; + + memset(&wstp->ws, 0, sizeof(wstp->ws)); + if (ws_init(&wstp->ws, socket, 65336, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { + return *return_reason = "WS_INIT", -1; + } + + wstp->ws_initialized = 1; + self->tp_pre_framed = 1; + return 0; } @@ -461,10 +460,9 @@ static void tport_ws_deinit_secondary(tport_t *self) tport_ws_t *wstp = (tport_ws_t *)self; if (wstp->ws_initialized == 1) { - wsh_t *wsh = wstp->ws; - SU_DEBUG_1(("%p destroy ws%s transport %p.\n", (void *) self, wstp->ws_secure ? "s" : "", (void *) wsh)); - ws_destroy(&wsh); - wstp->ws_initialized = 1; + SU_DEBUG_1(("%p destroy ws%s transport %p.\n", (void *) self, wstp->ws_secure ? "s" : "", (void *) &wstp->ws)); + ws_destroy(&wstp->ws); + wstp->ws_initialized = -1; } } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_ws.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_ws.h index 034b6f8fde..b4a5d42b5a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_ws.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_ws.h @@ -55,7 +55,7 @@ typedef enum { typedef struct tport_ws_s { tport_t wstp_tp[1]; - wsh_t ws[1]; + wsh_t ws; char *wstp_buffer; SU_S8_T ws_initialized; unsigned ws_secure:1; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index 938b3f6d04..46e41d133b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -402,17 +402,13 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int c return 0; } -void ws_destroy(wsh_t **wshp) +void ws_destroy(wsh_t *wsh) { - wsh_t *wsh; - if (!wshp || ! *wshp) { + if (!wsh) { return; } - wsh = *wshp; - *wshp = NULL; - if (!wsh->down) { ws_close(wsh, WS_NONE); } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h index 2182960c14..13e5c27b2b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h @@ -85,7 +85,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int close_sock); issize_t ws_close(wsh_t *wsh, int16_t reason); -void ws_destroy(wsh_t **wshp); +void ws_destroy(wsh_t *wsh); void init_ssl(void); void deinit_ssl(void); From 13dacdcde4e14a509377516966ebd5dc173a214b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 09:17:15 -0500 Subject: [PATCH 002/278] FS-5546 --resolve oddly that is the thing I was trying to fix in the first place --- src/switch_rtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 9a02479e35..cc73563968 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -840,7 +840,7 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d xlen += 4 + switch_stun_attribute_padded_length(attr); } while (xlen <= packet->header.length); - if ((ice->type && ICE_GOOGLE_JINGLE) && ok) { + if ((ice->type & ICE_GOOGLE_JINGLE) && ok) { ok = !strcmp(ice->user_ice, username); } @@ -997,7 +997,7 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d memset(stunbuf, 0, sizeof(stunbuf)); rpacket = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, packet->header.id, stunbuf); - if ((ice->type && ICE_GOOGLE_JINGLE)) { + if ((ice->type & ICE_GOOGLE_JINGLE)) { switch_stun_packet_attribute_add_username(rpacket, username, (uint16_t)strlen(username)); } From bf5fa172e5c00e362c0e7fa6102402b854e16c1c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 10:47:40 -0500 Subject: [PATCH 003/278] FS-5547 --resolve --- src/include/switch_core_media.h | 1 - src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 20 ++++++++++---------- src/mod/endpoints/mod_sofia/sofia_glue.c | 4 +++- src/switch_core_media.c | 10 +++++----- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h index 624d3bddd0..844a1bbb76 100644 --- a/src/include/switch_core_media.h +++ b/src/include/switch_core_media.h @@ -73,7 +73,6 @@ typedef enum { SCMF_DISABLE_HOLD, SCMF_RENEG_ON_HOLD, SCMF_RENEG_ON_REINVITE, - SCMF_T38_PASSTHRU, SCMF_LIBERAL_DTMF, SCMF_SUPPRESS_CNG, SCMF_DISABLE_RTP_AUTOADJ, diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 49247f5c9b..90e8dad19e 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -234,6 +234,7 @@ typedef enum { PFLAG_MESSAGE_QUERY_ON_REGISTER, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER, PFLAG_MANUAL_REDIRECT, + PFLAG_T38_PASSTHRU, PFLAG_AUTO_NAT, PFLAG_SIPCOMPACT, PFLAG_PRESENCE_PRIVACY, diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index b14b852e16..e9f5b9fea4 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -3834,9 +3834,9 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } } else if (!strcasecmp(var, "t38-passthru")) { if (switch_true(val)) { - sofia_set_media_flag(profile, SCMF_T38_PASSTHRU); + sofia_set_pflag(profile, PFLAG_T38_PASSTHRU); } else { - sofia_clear_media_flag(profile, SCMF_T38_PASSTHRU); + sofia_clear_pflag(profile, PFLAG_T38_PASSTHRU); } } else if (!strcasecmp(var, "presence-disable-early")) { if (switch_true(val)) { @@ -5306,7 +5306,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { - switch_media_handle_clear_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU); + switch_channel_clear_flag(tech_pvt->channel, CF_T38_PASSTHRU); has_t38 = 0; } @@ -5330,7 +5330,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (status > 199 && (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA) || - (switch_media_handle_test_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU) && (has_t38 || status > 299)))) { + (switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && (has_t38 || status > 299)))) { if (sofia_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) { sofia_clear_flag_locked(tech_pvt, TFLAG_SENT_UPDATE); @@ -5350,7 +5350,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing %d %s to other leg\n", status, phrase); - if (status == 491 && (switch_media_handle_test_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU) || + if (status == 491 && (switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) || switch_channel_test_flag(channel, CF_PROXY_MODE))) { nua_respond(other_tech_pvt->nh, SIP_491_REQUEST_PENDING, TAG_END()); switch_core_session_rwunlock(other_session); @@ -5358,12 +5358,12 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } else if (status > 299) { switch_channel_set_private(channel, "t38_options", NULL); switch_channel_set_private(other_channel, "t38_options", NULL); - switch_media_handle_clear_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU); - switch_media_handle_clear_media_flag(other_tech_pvt->media_handle, SCMF_T38_PASSTHRU); + switch_channel_clear_flag(tech_pvt->channel, CF_T38_PASSTHRU); + switch_channel_clear_flag(other_tech_pvt->channel, CF_T38_PASSTHRU); switch_channel_clear_app_flag_key("T38", tech_pvt->channel, CF_APP_T38); switch_channel_clear_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_REQ); switch_channel_set_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_FAIL); - } else if (status == 200 && switch_media_handle_test_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU) && has_t38 && sip->sip_payload && sip->sip_payload->pl_data) { + } else if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38 && sip->sip_payload && sip->sip_payload->pl_data) { switch_t38_options_t *t38_options = switch_core_media_extract_t38_options(session, sip->sip_payload->pl_data); if (!t38_options) { @@ -5384,14 +5384,14 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status msg->numeric_arg = status; msg->string_arg = switch_core_session_strdup(other_session, phrase); - if (status == 200 && switch_media_handle_test_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU) && has_t38) { + if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38) { msg->pointer_arg = switch_core_session_strdup(other_session, "t38"); } else if (r_sdp) { msg->pointer_arg = switch_core_session_strdup(other_session, r_sdp); msg->pointer_arg_size = strlen(r_sdp); } - if (status == 200 && switch_media_handle_test_media_flag(tech_pvt->media_handle, SCMF_T38_PASSTHRU) && has_t38) { + if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38) { if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO) && switch_core_media_ready(other_tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { switch_channel_clear_flag(tech_pvt->channel, CF_NOTIMER_DURING_BRIDGE); diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 2e35ab3a5b..fccc577fa8 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -133,7 +133,9 @@ void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t * switch_channel_set_flag(tech_pvt->channel, CF_RTP_NOTIMER_DURING_BRIDGE); } - + if (sofia_test_pflag(tech_pvt->profile, PFLAG_T38_PASSTHRU)) { + switch_channel_set_flag(tech_pvt->channel, CF_T38_PASSTHRU); + } switch_core_media_check_dtmf_type(session); switch_channel_set_cap(tech_pvt->channel, CC_MEDIA_ACK); diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 346eddc4b2..9270b0b729 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -2494,7 +2494,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s goto done; } else { const char *var = switch_channel_get_variable(channel, "t38_passthru"); - int pass = switch_media_handle_test_media_flag(smh, SCMF_T38_PASSTHRU); + int pass = switch_channel_test_flag(smh->session->channel, CF_T38_PASSTHRU); if (switch_channel_test_app_flag_key("T38", session->channel, CF_APP_T38)) { @@ -2509,7 +2509,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s } } - if ((pass == 2 && switch_media_handle_test_media_flag(smh, SCMF_T38_PASSTHRU)) + if ((pass == 2 && switch_channel_test_flag(smh->session->channel, CF_T38_PASSTHRU)) || !switch_channel_test_flag(session->channel, CF_REINVITE) || switch_channel_test_flag(session->channel, CF_PROXY_MODE) || @@ -2572,9 +2572,9 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s switch_core_media_copy_t38_options(t38_options, other_session); - switch_media_handle_set_media_flag(smh, SCMF_T38_PASSTHRU); - switch_media_handle_set_media_flag(other_session->media_handle, SCMF_T38_PASSTHRU); - + switch_channel_set_flag(smh->session->channel, CF_T38_PASSTHRU); + switch_channel_set_flag(other_session->channel, CF_T38_PASSTHRU); + msg = switch_core_session_alloc(other_session, sizeof(*msg)); msg->message_id = SWITCH_MESSAGE_INDICATE_REQUEST_IMAGE_MEDIA; msg->from = __FILE__; From c903934628841fde1bb5e38e48cc96e290c57fd9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 10:51:44 -0500 Subject: [PATCH 004/278] FS-5550 --resolve --- src/mod/endpoints/mod_loopback/mod_loopback.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod/endpoints/mod_loopback/mod_loopback.c b/src/mod/endpoints/mod_loopback/mod_loopback.c index 9d3371ef3d..105b96564a 100644 --- a/src/mod/endpoints/mod_loopback/mod_loopback.c +++ b/src/mod/endpoints/mod_loopback/mod_loopback.c @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Emmanuel Schmidbauer * * * mod_loopback.c -- Loopback Endpoint Module @@ -534,6 +535,7 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) channel = switch_core_session_get_channel(session); switch_assert(channel != NULL); + switch_channel_set_variable(channel, "is_loopback", "1"); tech_pvt = switch_core_session_get_private(session); switch_assert(tech_pvt != NULL); From 384cf3381cf2087c11b8789697bf8e96b121a583 Mon Sep 17 00:00:00 2001 From: Brian West Date: Wed, 26 Jun 2013 15:17:41 -0500 Subject: [PATCH 005/278] use the correct repeat key to replay messages --- src/mod/applications/mod_voicemail/mod_voicemail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 6ad806ac00..83bb8eaf23 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -1596,7 +1596,7 @@ static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t args.input_callback = cancel_on_dtmf; - switch_snprintf(key_buf, sizeof(key_buf), "%s:%s:%s:%s:%s:%s%s%s", profile->listen_file_key, profile->save_file_key, + switch_snprintf(key_buf, sizeof(key_buf), "%s:%s:%s:%s:%s:%s%s%s", profile->repeat_msg_key, profile->save_file_key, profile->delete_file_key, profile->email_key, profile->callback_key, profile->forward_key, cbt->email ? ":" : "", cbt->email ? cbt->email : ""); From 7eb86bba75c278c8f3f56851dcfb8d1ded4e0a81 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 16:04:28 -0500 Subject: [PATCH 006/278] fix empty +flags{} pre-empting conference_member_flags variable --- src/mod/applications/mod_conference/mod_conference.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 5df93f60b9..50bf670aad 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -7354,7 +7354,7 @@ SWITCH_STANDARD_APP(conference_function) char *bridgeto = NULL; char *profile_name = NULL; switch_xml_t cxml = NULL, cfg = NULL, profiles = NULL; - const char *flags_str; + const char *flags_str, *v_flags_str; member_flag_t mflags = 0; switch_core_session_message_t msg = { 0 }; uint8_t rl = 0, isbr = 0; @@ -7406,8 +7406,14 @@ SWITCH_STANDARD_APP(conference_function) if ((p = strchr(flags_str, '}'))) { *p = '\0'; } - } else { - flags_str = switch_channel_get_variable(channel, "conference_member_flags"); + } + + if ((v_flags_str = switch_channel_get_variable(channel, "conference_member_flags"))) { + if (zstr(flags_str)) { + flags_str = v_flags_str; + } else { + flags_str = switch_core_session_sprintf(session, "%s|%s", flags_str, v_flags_str); + } } /* is this a bridging conference ? */ From 3879a85bfb9c644909adea2eeae1d125361db75c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 18:35:06 -0500 Subject: [PATCH 007/278] set rtp params in skinny for rtp flush on bridge --- src/mod/endpoints/mod_skinny/mod_skinny.c | 49 +++++++++++++------- src/mod/endpoints/mod_skinny/skinny_server.c | 10 +++- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 889af65001..7dff9b7209 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1051,27 +1051,40 @@ switch_status_t channel_receive_message(switch_core_session_t *session, switch_c private_t *tech_pvt = switch_core_session_get_private(session); switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_ANSWER: - switch_clear_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + case SWITCH_MESSAGE_INDICATE_ANSWER: + switch_clear_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + return channel_answer_channel(session); + + case SWITCH_MESSAGE_INDICATE_DISPLAY: + skinny_session_send_call_info_all(session); + break; + + case SWITCH_MESSAGE_INDICATE_PROGRESS: + if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { + /* early media */ + switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); return channel_answer_channel(session); - - case SWITCH_MESSAGE_INDICATE_DISPLAY: - skinny_session_send_call_info_all(session); - return SWITCH_STATUS_SUCCESS; - - case SWITCH_MESSAGE_INDICATE_PROGRESS: - if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { - /* early media */ - switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); - return channel_answer_channel(session); - } - return SWITCH_STATUS_SUCCESS; - - default: - return SWITCH_STATUS_SUCCESS; - + } + break; + + case SWITCH_MESSAGE_INDICATE_BRIDGE: + if (switch_rtp_ready(tech_pvt->rtp_session)) { + rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_STICK); + } + break; + case SWITCH_MESSAGE_INDICATE_UNBRIDGE: + if (switch_rtp_ready(tech_pvt->rtp_session)) { + rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_UNSTICK); + } + break; + + default: + break; + } + return SWITCH_STATUS_SUCCESS; + } /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 6d1f4b765d..02296298f1 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -58,6 +58,8 @@ uint32_t soft_key_template_default_textids[] = { SKINNY_TEXTID_IDIVERT }; +#define TEXT_ID_LEN 20 + uint32_t soft_key_template_default_events[] = { SOFTKEY_REDIAL, SOFTKEY_NEWCALL, @@ -1732,6 +1734,10 @@ switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *liste struct in_addr addr; switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID] = {0}; + flags[SWITCH_RTP_FLAG_DATAWAIT]++; + flags[SWITCH_RTP_FLAG_AUTOADJ]++; + flags[SWITCH_RTP_FLAG_RAW_WRITE]++; + tech_pvt = switch_core_session_get_private(session); channel = switch_core_session_get_channel(session); @@ -1955,7 +1961,7 @@ switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, skinny_message_t *request) { - int i; + size_t i; skinny_message_t *message; switch_assert(listener->profile); @@ -1970,7 +1976,7 @@ switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, sk message->data.soft_key_template.total_soft_key_count = 21; memset(message->data.soft_key_template.soft_key, 0, sizeof(message->data.soft_key_template)); - for (i=0; idata.soft_key_template.soft_key[i].soft_key_label, skinny_textid2raw(soft_key_template_default_textids[i])); switch_safe_free(label); From 375c102ae01b365b69b7279457a83c15f91260b0 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Wed, 26 Jun 2013 21:55:38 -0500 Subject: [PATCH 008/278] typo --- src/mod/endpoints/mod_skinny/skinny_server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 02296298f1..0cbde82e47 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -986,9 +986,9 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r request->data.reg.device_name, request->data.reg.instance, &listener2); if (listener2) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Device %s:%d is already registred on another listener.\n", + "Device %s:%d is already registered on another listener.\n", request->data.reg.device_name, request->data.reg.instance); - send_register_reject(listener, "Device is already registred on another listener"); + send_register_reject(listener, "Device is already registered on another listener"); status = SWITCH_STATUS_FALSE; goto end; } From 5fefae03fd9b540baf6a634d5c4527e381af004e Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Wed, 26 Jun 2013 21:56:10 -0500 Subject: [PATCH 009/278] improve skinny behavior in HA deployment --- src/mod/endpoints/mod_skinny/mod_skinny.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 7dff9b7209..f5c01481d4 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1995,10 +1995,10 @@ static switch_status_t load_skinny_config(void) if ((dbh = skinny_get_db_handle(profile))) { - switch_cache_db_test_reactive(dbh, "DELETE FROM skinny_devices", "DROP TABLE skinny_devices", devices_sql); - switch_cache_db_test_reactive(dbh, "DELETE FROM skinny_lines", "DROP TABLE skinny_lines", lines_sql); - switch_cache_db_test_reactive(dbh, "DELETE FROM skinny_buttons", "DROP TABLE skinny_buttons", buttons_sql); - switch_cache_db_test_reactive(dbh, "DELETE FROM skinny_active_lines", "DROP TABLE skinny_active_lines", active_lines_sql); + switch_cache_db_test_reactive(dbh, "select count(*) from skinny_devices", NULL, devices_sql); + switch_cache_db_test_reactive(dbh, "select count(*) from skinny_lines", NULL, lines_sql); + switch_cache_db_test_reactive(dbh, "select count(*) from skinny_buttons", NULL, buttons_sql); + switch_cache_db_test_reactive(dbh, "select count(*) from skinny_active_lines", NULL, active_lines_sql); switch_cache_db_release_db_handle(&dbh); } From a26ab6e3e05f8a433b790a890b5ac2ebf4e9d6b6 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 26 Jun 2013 23:10:19 -0400 Subject: [PATCH 010/278] fix ssl connect race --- libs/sofia-sip/.update | 2 +- libs/sofia-sip/libsofia-sip-ua/tport/ws.c | 29 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index 0440205a53..d8abd49c6d 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Jun 26 12:43:51 EDT 2013 +Wed Jun 26 23:10:11 EDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index 46e41d133b..1a0a2f3250 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -379,7 +379,8 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int c if (wsh->secure) { int code; - + int sanity = 500; + wsh->ssl = SSL_new(ssl_ctx); assert(wsh->ssl); @@ -387,8 +388,32 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int c do { code = SSL_accept(wsh->ssl); - } while (code == -1 && SSL_get_error(wsh->ssl, code) == SSL_ERROR_WANT_READ); + if (code == 1) { + break; + } + + if (code == 0) { + return -1; + } + + if (code < 0) { + if (code == -1 && SSL_get_error(wsh->ssl, code) != SSL_ERROR_WANT_READ) { + return -1; + } + } +#ifndef _MSC_VER + usleep(10000); +#else + Sleep(10); +#endif + + } while (--sanity > 0); + + if (!sanity) { + return -1; + } + } while (!wsh->down && !wsh->handshake) { From 5324063f44e8170bc91ff6bfff3bbc4168c23aa5 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 26 Jun 2013 23:17:49 -0400 Subject: [PATCH 011/278] Added support for blind transfer notification of negative responses --- src/mod/endpoints/mod_sofia/mod_sofia.c | 30 +++++++++++++++++++++---- src/mod/endpoints/mod_sofia/sofia.c | 7 ++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 98d2228e96..716587cdbb 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -1199,13 +1199,35 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi char *xdest; if (event && uuid) { + char payload_str[255] = "SIP/2.0 403 Forbidden\r\n"; + if (msg->numeric_arg) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "%s Completing blind transfer with success\n", switch_channel_get_name(channel)); + switch_set_string(payload_str, "SIP/2.0 200 OK\r\n"); + } else if (uuid) { + switch_core_session_t *other_session = switch_core_session_locate(uuid); + if (other_session) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + const char *invite_failure_status = switch_channel_get_variable(other_channel, "sip_invite_failure_status"); + const char *invite_failure_str = switch_channel_get_variable(other_channel, "sip_invite_failure_status"); + if (!zstr(invite_failure_status) && !zstr(invite_failure_str)) { + snprintf(payload_str, sizeof(payload_str), "SIP/2.0 %s %s\r\n", invite_failure_status, invite_failure_str); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "%s Completing blind transfer with custom failure: %s %s\n", + switch_channel_get_name(channel), invite_failure_status, invite_failure_str); + } + switch_core_session_rwunlock(other_session); + } + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "%s Completing blind transfer with status: %s\n", switch_channel_get_name(channel), payload_str); nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), - SIPTAG_PAYLOAD_STR(msg->numeric_arg ? "SIP/2.0 200 OK\r\n" : "SIP/2.0 403 Forbidden\r\n"), - SIPTAG_EVENT_STR(event), TAG_END()); + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), + SIPTAG_PAYLOAD_STR(payload_str), + SIPTAG_EVENT_STR(event), TAG_END()); + - if (!msg->numeric_arg) { xdest = switch_core_session_sprintf(session, "intercept:%s", uuid); switch_ivr_session_transfer(session, xdest, "inline", NULL); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index e9f5b9fea4..a4dd9e24bb 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -4985,6 +4985,13 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status switch_channel_set_variable(channel, "sip_hangup_disposition", "recv_refuse"); } + if (status >= 400) { + char status_str[5]; + switch_snprintf(status_str, sizeof(status_str), "%d", status); + switch_channel_set_variable_partner(channel, "sip_invite_failure_status", status_str); + switch_channel_set_variable_partner(channel, "sip_invite_failure_phrase", phrase); + } + if (status >= 400 && sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850") || !strcasecmp(sip->sip_reason->re_protocol, "FreeSWITCH") || !strcasecmp(sip->sip_reason->re_protocol, profile->sdp_username)) && sip->sip_reason->re_cause) { From cd56d774a049986e48a95b7481f768442f269232 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 27 Jun 2013 13:00:20 -0500 Subject: [PATCH 012/278] parse error response for code to only change roles on 487 --- src/include/switch_stun.h | 21 ++++++++++++++++++ src/switch_rtp.c | 46 ++++++++++++++++++++------------------- src/switch_stun.c | 7 +++++- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/include/switch_stun.h b/src/include/switch_stun.h index fdf1fbd74d..b6de510159 100644 --- a/src/include/switch_stun.h +++ b/src/include/switch_stun.h @@ -142,6 +142,27 @@ typedef struct { } switch_stun_ip_t; +#if SWITCH_BYTE_ORDER == __BIG_ENDIAN + +typedef struct { + unsigned padding:21; + unsigned code:3; + unsigned number:8; + char reason[764]; +} switch_stun_error_code_t; + +#else + +typedef struct { + unsigned number:8; + unsigned code:3; + unsigned padding:21; + char reason[764]; +} switch_stun_error_code_t; + +#endif + + /*! \brief Writes random characters into a buffer \param buf the buffer diff --git a/src/switch_rtp.c b/src/switch_rtp.c index cc73563968..821c375384 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -227,7 +227,6 @@ typedef struct { uint8_t ready; uint8_t rready; int missed_count; - int flips; char last_sent_id[12]; } switch_rtp_ice_t; @@ -784,27 +783,6 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d } - - if (packet->header.type == SWITCH_STUN_BINDING_ERROR_RESPONSE) { - if ((ice->type & ICE_VANILLA)) { - if (ice->flips < 4) { - if ((ice->type & ICE_CONTROLLED)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "Changing role to CONTROLLING\n"); - ice->type &= ~ICE_CONTROLLED; - ice->flips++; - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "Changing role to CONTROLLED\n"); - ice->type |= ICE_CONTROLLED; - ice->flips++; - } - packet->header.type = SWITCH_STUN_BINDING_RESPONSE; - } - } - - } else { - ice->flips = 0; - } - end_buf = buf + ((sizeof(buf) > packet->header.length) ? packet->header.length : sizeof(buf)); rtp_session->last_stun = switch_micro_time_now(); @@ -813,6 +791,30 @@ static void handle_ice(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice, void *d do { switch (attr->type) { + case SWITCH_STUN_ATTR_ERROR_CODE: + { + switch_stun_error_code_t *err = (switch_stun_error_code_t *) attr->value; + uint32_t code = (err->code * 100) + err->number; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "%s got stun binding response %u %s\n", + switch_core_session_get_name(rtp_session->session), + code, + err->reason + ); + + if ((ice->type & ICE_VANILLA) && code == 487) { + if ((ice->type & ICE_CONTROLLED)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "Changing role to CONTROLLING\n"); + ice->type &= ~ICE_CONTROLLED; + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_WARNING, "Changing role to CONTROLLED\n"); + ice->type |= ICE_CONTROLLED; + } + packet->header.type = SWITCH_STUN_BINDING_RESPONSE; + } + + } + break; case SWITCH_STUN_ATTR_MAPPED_ADDRESS: if (attr->type) { char ip[16]; diff --git a/src/switch_stun.c b/src/switch_stun.c index f2af463f15..8be191e782 100644 --- a/src/switch_stun.c +++ b/src/switch_stun.c @@ -257,11 +257,16 @@ SWITCH_DECLARE(switch_stun_packet_t *) switch_stun_packet_parse(uint8_t *buf, ui *val = ntohl(*val); /* should we do this here? */ } break; + case SWITCH_STUN_ATTR_ERROR_CODE: /* ErrorCode */ + { + uint32_t *u = (uint32_t *) attr->value; + *u = htonl(*u); + } + break; case SWITCH_STUN_ATTR_USERNAME: /* ByteString, multiple of 4 bytes */ case SWITCH_STUN_ATTR_PASSWORD: /* ByteString, multiple of 4 bytes */ case SWITCH_STUN_ATTR_DATA: /* ByteString */ - case SWITCH_STUN_ATTR_ERROR_CODE: /* ErrorCode */ case SWITCH_STUN_ATTR_TRANSPORT_PREFERENCES: /* TransportPrefs */ /* * No length checking here, since we already checked against the padded length From b2e06346d44f68bcbc6c674372b8a491570ce3e8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 27 Jun 2013 14:04:13 -0500 Subject: [PATCH 013/278] some more ws transport tweaks --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/tport/tport_type_ws.c | 23 +++++++++++++--- libs/sofia-sip/libsofia-sip-ua/tport/ws.c | 26 +++++++++++-------- libs/sofia-sip/libsofia-sip-ua/tport/ws.h | 7 ++--- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index d8abd49c6d..932e4da087 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Jun 26 23:10:11 EDT 2013 +Thu Jun 27 14:04:11 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c index d9ed0b6980..8b7ecfaa7b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c @@ -432,26 +432,43 @@ int tport_ws_init_secondary(tport_t *self, int socket, int accepted, int one = 1; tport_ws_primary_t *wspri = (tport_ws_primary_t *)self->tp_pri; tport_ws_t *wstp = (tport_ws_t *)self; + char *buffer, *wbuffer; self->tp_has_connection = 1; if (setsockopt(socket, SOL_TCP, TCP_NODELAY, (void *)&one, sizeof one) == -1) - return *return_reason = "TCP_NODELAY", -1; + return *return_reason = "TCP_NODELAY", -1; + +#if defined(SO_KEEPALIVE) + setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof one); +#endif + one = 30; +#if defined(TCP_KEEPIDLE) + setsockopt(socket, SOL_TCP, TCP_KEEPIDLE, (void *)&one, sizeof one); +#endif +#if defined(TCP_KEEPINTVL) + setsockopt(socket, SOL_TCP, TCP_KEEPINTVL, (void *)&one, sizeof one); +#endif + if (!accepted) tport_ws_setsndbuf(socket, 64 * 1024); if ( wspri->ws_secure ) wstp->ws_secure = 1; - memset(&wstp->ws, 0, sizeof(wstp->ws)); - if (ws_init(&wstp->ws, socket, 65336, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { + + buffer = (char *) su_alloc((su_home_t *)self, 65536); + wbuffer = (char *) su_alloc((su_home_t *)self, 65536); + + if (ws_init(&wstp->ws, socket, buffer, wbuffer, 65336, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { return *return_reason = "WS_INIT", -1; } wstp->ws_initialized = 1; self->tp_pre_framed = 1; + return 0; } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index 1a0a2f3250..6ee35e54a3 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -352,7 +352,7 @@ issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) return r; } -int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int close_sock) +int ws_init(wsh_t *wsh, ws_socket_t sock, char *buffer, char *wbuffer, size_t buflen, SSL_CTX *ssl_ctx, int close_sock) { memset(wsh, 0, sizeof(*wsh)); wsh->sock = sock; @@ -372,9 +372,20 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int c wsh->buflen = buflen; wsh->secure = ssl_ctx ? 1 : 0; - if (!wsh->buffer) { + if (buffer) { + wsh->buffer = buffer; + } else if (!wsh->buffer) { wsh->buffer = malloc(wsh->buflen); assert(wsh->buffer); + wsh->free_buffer = 1; + } + + if (wbuffer) { + wsh->wbuffer = wbuffer; + } else if (!wsh->wbuffer) { + wsh->wbuffer = malloc(wsh->buflen); + assert(wsh->wbuffer); + wsh->free_wbuffer = 1; } if (wsh->secure) { @@ -454,12 +465,12 @@ void ws_destroy(wsh_t *wsh) wsh->ssl = NULL; } - if (wsh->buffer) { + if (wsh->free_buffer && wsh->buffer) { free(wsh->buffer); wsh->buffer = NULL; } - if (wsh->wbuffer) { + if (wsh->free_wbuffer && wsh->wbuffer) { free(wsh->wbuffer); wsh->wbuffer = NULL; } @@ -653,13 +664,6 @@ issize_t ws_feed_buf(wsh_t *wsh, void *data, size_t bytes) return -1; } - - if (!wsh->wbuffer) { - wsh->wbuffer = malloc(wsh->buflen); - assert(wsh->wbuffer); - } - - memcpy(wsh->wbuffer + wsh->wdatalen, data, bytes); wsh->wdatalen += bytes; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h index 13e5c27b2b..84aa46fceb 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h @@ -71,8 +71,9 @@ typedef struct wsh_s { int handshake; uint8_t down; int secure; - unsigned close_sock:1; - unsigned :0; + uint8_t free_buffer; + uint8_t free_wbuffer; + uint8_t close_sock; } wsh_t; issize_t ws_send_buf(wsh_t *wsh, ws_opcode_t oc); @@ -83,7 +84,7 @@ issize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes); issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes); issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); -int ws_init(wsh_t *wsh, ws_socket_t sock, size_t buflen, SSL_CTX *ssl_ctx, int close_sock); +int ws_init(wsh_t *wsh, ws_socket_t sock, char *buffer, char *wbuffer, size_t buflen, SSL_CTX *ssl_ctx, int close_sock); issize_t ws_close(wsh_t *wsh, int16_t reason); void ws_destroy(wsh_t *wsh); void init_ssl(void); From 41c1c1cb75ff3eda08e85cbcab9153af2bb64d2c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 27 Jun 2013 16:25:59 -0500 Subject: [PATCH 014/278] add answer work tag to httapi --- .../mod_httapi/docs/mod_httapi_doc.txt | 6 ++++++ src/mod/applications/mod_httapi/mod_httapi.c | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt b/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt index f659db39af..399ea1a335 100644 --- a/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt +++ b/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt @@ -230,6 +230,12 @@ action : Change url to submit to. temp-action : Change url to submit to. just for the next loop. + + : Answer the call + +ATTRS: +is-conference : true|false (set the conference flag for RFC4579 stuff. + : Hangup the call diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c index 005f66bdbd..8b76f36101 100644 --- a/src/mod/applications/mod_httapi/mod_httapi.c +++ b/src/mod/applications/mod_httapi/mod_httapi.c @@ -877,6 +877,19 @@ static switch_status_t parse_hangup(const char *tag_name, client_t *client, swit return SWITCH_STATUS_FALSE; } +static switch_status_t parse_answer(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) +{ + const char *conf = switch_xml_attr(tag, "is-conference"); + + if (conf && switch_true(conf)) { + switch_channel_set_flag(client->channel, CF_CONFERENCE); + } + + switch_channel_answer(client->channel); + + return SWITCH_STATUS_FALSE; +} + static switch_status_t parse_record_call(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) { const char *limit_ = switch_xml_attr(tag, "limit"); @@ -2997,6 +3010,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load) bind_parser("sms", parse_sms); bind_parser("dial", parse_dial); bind_parser("pause", parse_playback); + bind_parser("answer", parse_answer); bind_parser("hangup", parse_hangup); bind_parser("record", parse_record); bind_parser("recordCall", parse_record_call); From 2ea31f5d95859d0dad34e56791e0d76f6eacd4b6 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 27 Jun 2013 16:31:29 -0500 Subject: [PATCH 015/278] add preAnswer and ringReady too --- .../mod_httapi/docs/mod_httapi_doc.txt | 7 +++++++ src/mod/applications/mod_httapi/mod_httapi.c | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt b/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt index 399ea1a335..b313dc5e15 100644 --- a/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt +++ b/src/mod/applications/mod_httapi/docs/mod_httapi_doc.txt @@ -237,6 +237,13 @@ ATTRS: is-conference : true|false (set the conference flag for RFC4579 stuff. + + : Establish media on the call without answering. + + + : Indicate ringing to an unaswered channel. + + : Hangup the call diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c index 8b76f36101..52e4a21a6b 100644 --- a/src/mod/applications/mod_httapi/mod_httapi.c +++ b/src/mod/applications/mod_httapi/mod_httapi.c @@ -879,14 +879,21 @@ static switch_status_t parse_hangup(const char *tag_name, client_t *client, swit static switch_status_t parse_answer(const char *tag_name, client_t *client, switch_xml_t tag, const char *body) { - const char *conf = switch_xml_attr(tag, "is-conference"); - if (conf && switch_true(conf)) { - switch_channel_set_flag(client->channel, CF_CONFERENCE); + if (!strcasecmp(tag_name, "answer")) { + const char *conf = switch_xml_attr(tag, "is-conference"); + + if (conf && switch_true(conf)) { + switch_channel_set_flag(client->channel, CF_CONFERENCE); + } + + switch_channel_answer(client->channel); + } else if (!strcasecmp(tag_name, "preAnswer")) { + switch_channel_pre_answer(client->channel); + } else if (!strcasecmp(tag_name, "ringReady")) { + switch_channel_ring_ready(client->channel); } - switch_channel_answer(client->channel); - return SWITCH_STATUS_FALSE; } @@ -3011,6 +3018,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load) bind_parser("dial", parse_dial); bind_parser("pause", parse_playback); bind_parser("answer", parse_answer); + bind_parser("preAnswer", parse_answer); + bind_parser("ringReady", parse_answer); bind_parser("hangup", parse_hangup); bind_parser("record", parse_record); bind_parser("recordCall", parse_record_call); From a8e0cbfcde8b2b8559e288271b497cbba1c1f1e4 Mon Sep 17 00:00:00 2001 From: Brian West Date: Fri, 28 Jun 2013 09:48:28 -0500 Subject: [PATCH 016/278] punt without return --- src/switch_core_media_bug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/switch_core_media_bug.c b/src/switch_core_media_bug.c index 0ad3f11183..9831d0099f 100644 --- a/src/switch_core_media_bug.c +++ b/src/switch_core_media_bug.c @@ -430,6 +430,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t if (punt) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Only one bug of this type allowed!\n"); + return SWITCH_STATUS_GENERR; } From 72aad689653d8a1b5880a8b8e67c542414839f94 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Fri, 28 Jun 2013 11:16:06 -0400 Subject: [PATCH 017/278] mod_rayo: dtmf #*A-D was broken --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 4 +++ src/mod/event_handlers/mod_rayo/nlsml.c | 33 +++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index e6b2d20322..66f59c9def 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -3674,6 +3674,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) "0123456789]]>" "" ""); + rayo_add_cmd_alias("input", "" + "" + "0123456789*#]]>" + ""); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/event_handlers/mod_rayo/nlsml.c b/src/mod/event_handlers/mod_rayo/nlsml.c index 81f45dc6fb..e446d1bfd0 100644 --- a/src/mod/event_handlers/mod_rayo/nlsml.c +++ b/src/mod/event_handlers/mod_rayo/nlsml.c @@ -363,6 +363,37 @@ iks *nlsml_normalize(const char *result) return result_xml; } +/** + * @return true if digit is a DTMF + */ +static int isdtmf(const char digit) +{ + switch(digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '*': + case '#': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + return 1; + } + return 0; +} + /** * Construct an NLSML result for digit match * @param digits the matching digits @@ -387,7 +418,7 @@ iks *nlsml_create_dtmf_match(const char *digits) SWITCH_STANDARD_STREAM(stream); for (i = 0; i < num_digits; i++) { - if (isdigit(digits[i])) { + if (isdtmf(digits[i])) { if (first) { stream.write_function(&stream, "%c", digits[i]); first = 0; From dac93d7936ad1a0c8f34af344d279f23b89f4860 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 28 Jun 2013 10:42:01 -0500 Subject: [PATCH 018/278] FS-5527 --resolve --- libs/sofia-sip/.update | 2 +- libs/sofia-sip/libsofia-sip-ua/nta/nta.c | 45 ++++++++++--------- .../libsofia-sip-ua/su/sofia-sip/su_time.h | 7 ++- libs/sofia-sip/libsofia-sip-ua/su/su_time0.c | 14 ++++++ src/mod/endpoints/mod_sofia/sofia.c | 19 ++++++++ 5 files changed, 65 insertions(+), 22 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index 932e4da087..8024eeba38 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Thu Jun 27 14:04:11 CDT 2013 +Fri Jun 28 10:39:50 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c index f5d61e4530..68563978cd 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c +++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c @@ -152,9 +152,7 @@ struct nta_agent_s nta_error_magic_t *sa_error_magic; nta_error_tport_f *sa_error_tport; - su_time_t sa_now; /**< Timestamp in microsecond resolution. */ uint32_t sa_next; /**< Timestamp for next agent_timer. */ - uint32_t sa_millisec; /**< Timestamp in milliseconds. */ msg_mclass_t const *sa_mclass; uint32_t sa_flags; /**< SIP message flags */ @@ -1242,15 +1240,12 @@ void agent_timer(su_root_magic_t *rm, su_timer_t *timer, nta_agent_t *agent) agent->sa_next = 0; - agent->sa_now = stamp; - agent->sa_millisec = now; agent->sa_in_timer = 1; + _nta_outgoing_timer(agent); _nta_incoming_timer(agent); - /* agent->sa_now is used only if sa_millisec != 0 */ - agent->sa_millisec = 0; agent->sa_in_timer = 0; /* Calculate next timeout */ @@ -1335,12 +1330,12 @@ uint32_t set_timeout(nta_agent_t *agent, uint32_t offset) if (offset == 0) return 0; - if (agent->sa_millisec) /* Avoid expensive call to su_now() */ - now = agent->sa_now, ms = agent->sa_millisec; - else - now = su_now(), ms = su_time_ms(now); + now = su_now(); + ms = su_time_ms(now); - next = ms + offset; if (next == 0) next = 1; + next = ms + offset; + + if (next == 0) next = 1; if (agent->sa_in_timer) /* Currently executing timer */ return next; @@ -1365,9 +1360,6 @@ uint32_t set_timeout(nta_agent_t *agent, uint32_t offset) static su_time_t agent_now(nta_agent_t const *agent) { - if (agent && agent->sa_millisec != 0) - return agent->sa_now; - else return su_now(); } @@ -2784,8 +2776,6 @@ void agent_recv_message(nta_agent_t *agent, { sip_t *sip = sip_object(msg); - agent->sa_millisec = su_time_ms(agent->sa_now = now); - if (sip && sip->sip_request) { agent_recv_request(agent, msg, sip, tport); } @@ -2795,8 +2785,6 @@ void agent_recv_message(nta_agent_t *agent, else { agent_recv_garbage(agent, msg, tport); } - - agent->sa_millisec = 0; } /** @internal Handle incoming requests. */ @@ -6869,7 +6857,7 @@ enum { static void _nta_incoming_timer(nta_agent_t *sa) { - uint32_t now = sa->sa_millisec; + uint32_t now = su_time_ms(su_now()); nta_incoming_t *irq, *irq_next; size_t retransmitted = 0, timeout = 0, terminated = 0, destroyed = 0; size_t unconfirmed = @@ -6886,6 +6874,9 @@ _nta_incoming_timer(nta_agent_t *sa) /* Handle retry queue */ while ((irq = sa->sa_in.re_list)) { + + now = su_time_ms(su_now()); + if ((int32_t)(irq->irq_retry - now) > 0) break; if (retransmitted >= timer_max_retransmit) @@ -6943,6 +6934,8 @@ _nta_incoming_timer(nta_agent_t *sa) } while ((irq = sa->sa_in.final_failed->q_head)) { + + incoming_remove(irq); irq->irq_final_failed = 0; @@ -6974,6 +6967,8 @@ _nta_incoming_timer(nta_agent_t *sa) assert(irq->irq_status < 200); assert(irq->irq_timeout); + now = su_time_ms(su_now()); + if ((int32_t)(irq->irq_timeout - now) > 0) break; if (timeout >= timer_max_timeout) @@ -6994,6 +6989,8 @@ _nta_incoming_timer(nta_agent_t *sa) assert(irq->irq_timeout); assert(irq->irq_method == sip_method_invite); + now = su_time_ms(su_now()); + if ((int32_t)(irq->irq_timeout - now) > 0 || timeout >= timer_max_timeout || terminated >= timer_max_terminate) @@ -7022,6 +7019,8 @@ _nta_incoming_timer(nta_agent_t *sa) assert(irq->irq_status >= 200); assert(irq->irq_method == sip_method_invite); + now = su_time_ms(su_now()); + if ((int32_t)(irq->irq_timeout - now) > 0 || terminated >= timer_max_terminate) break; @@ -7044,6 +7043,8 @@ _nta_incoming_timer(nta_agent_t *sa) assert(irq->irq_timeout); assert(irq->irq_method != sip_method_invite); + now = su_time_ms(su_now()); + if ((int32_t)(irq->irq_timeout - now) > 0 || terminated >= timer_max_terminate) break; @@ -7063,6 +7064,7 @@ _nta_incoming_timer(nta_agent_t *sa) } for (irq = sa->sa_in.terminated->q_head; irq; irq = irq_next) { + irq_next = irq->irq_next; if (irq->irq_destroyed) incoming_free_queue(rq, irq); @@ -8727,7 +8729,7 @@ void outgoing_destroy(nta_outgoing_t *orq) static void _nta_outgoing_timer(nta_agent_t *sa) { - uint32_t now = sa->sa_millisec; + uint32_t now = su_time_ms(su_now()); nta_outgoing_t *orq; outgoing_queue_t rq[1]; size_t retransmitted = 0, terminated = 0, timeout = 0, destroyed; @@ -8741,6 +8743,9 @@ _nta_outgoing_timer(nta_agent_t *sa) outgoing_queue_init(sa->sa_out.free = rq, 0); while ((orq = sa->sa_out.re_list)) { + + now = su_time_ms(su_now()); + if ((int32_t)(orq->orq_retry - now) > 0) break; if (retransmitted >= timer_max_retransmit) diff --git a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h index b4acba6a7d..ea347e80a0 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h +++ b/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h @@ -85,6 +85,11 @@ typedef uint64_t su_nanotime_t; #define SU_E9 (1000000000U) +typedef void (*su_time_func_t)(su_time_t *tv); + + +SOFIAPUBFUN void su_set_time_func(su_time_func_t func); + SOFIAPUBFUN su_nanotime_t su_nanotime(su_nanotime_t *return_time); SOFIAPUBFUN su_nanotime_t su_monotime(su_nanotime_t *return_time); @@ -138,7 +143,7 @@ su_inline uint32_t su_ntp_fraq(su_time_t t) /** Time as milliseconds. */ su_inline uint32_t su_time_ms(su_time_t t) { - return t.tv_sec * 1000 + (t.tv_usec + 500) / 1000; + return (t.tv_sec * 1000) + ((t.tv_usec + 500) / 1000); } #endif diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c b/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c index 0543e45aa0..7267213156 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c @@ -69,6 +69,13 @@ void (*_su_time)(su_time_t *tv); uint64_t (*_su_nanotime)(uint64_t *); +static su_time_func_t custom_time_func = NULL; + +void su_set_time_func(su_time_func_t func) { + custom_time_func = func; +} + + /** Get current time. * * The function @c su_time() fills its argument with the current NTP @@ -79,6 +86,13 @@ uint64_t (*_su_nanotime)(uint64_t *); void su_time(su_time_t *tv) { su_time_t ltv = {0,0}; + + if (custom_time_func) { + custom_time_func(<v); + if (tv) *tv = ltv; + return; + } + #if HAVE_CLOCK_GETTIME struct timespec ctv = {0}; if (clock_gettime(CLOCK_REALTIME, &ctv) == 0) { diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index e9f5b9fea4..54b683441c 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -3488,6 +3488,21 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) } } +#ifdef SOFIA_CUSTOM_TIME +/* appears to not be granular enough */ +static void sofia_time(su_time_t *tv) +{ + switch_time_t now; + + if (tv) { + now = switch_micro_time_now(); + tv->tv_sec = ((uint32_t) (now / 1000000)) + 2208988800UL; + tv->tv_usec = (uint32_t) (now % 1000000); + } + +} +#endif + switch_status_t sofia_init(void) { su_init(); @@ -3496,6 +3511,10 @@ switch_status_t sofia_init(void) return SWITCH_STATUS_GENERR; } +#ifdef SOFIA_TIME + su_set_time_func(sofia_time); +#endif + /* Redirect loggers in sofia */ su_log_redirect(su_log_default, logger, NULL); su_log_redirect(tport_log, logger, NULL); From a52a604fbb561dbb604676b1a17888bd74625101 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Fri, 28 Jun 2013 11:05:27 -0500 Subject: [PATCH 019/278] FS-5527 fix compiler error windows --- libs/sofia-sip/libsofia-sip-ua/su/su_time0.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c b/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c index 7267213156..cae14a35a8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c +++ b/libs/sofia-sip/libsofia-sip-ua/su/su_time0.c @@ -85,6 +85,12 @@ void su_set_time_func(su_time_func_t func) { */ void su_time(su_time_t *tv) { +#if HAVE_FILETIME + union { + FILETIME ft[1]; + ULARGE_INTEGER ull[1]; + } date; +#endif su_time_t ltv = {0,0}; if (custom_time_func) { @@ -105,10 +111,6 @@ void su_time(su_time_t *tv) ltv.tv_sec += NTP_EPOCH; #elif HAVE_FILETIME - union { - FILETIME ft[1]; - ULARGE_INTEGER ull[1]; - } date; GetSystemTimeAsFileTime(date.ft); From bbdd77ec3562d0fbe0590c38e339c8f4f0ec9ab8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 28 Jun 2013 14:20:58 -0500 Subject: [PATCH 020/278] fix race condition on device state destroy --- src/mod/applications/mod_skel/mod_skel.c | 25 ++++++++++++++++++ src/switch_channel.c | 33 ++++++++++++++---------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/mod/applications/mod_skel/mod_skel.c b/src/mod/applications/mod_skel/mod_skel.c index 4722b77d58..37f155aef6 100644 --- a/src/mod/applications/mod_skel/mod_skel.c +++ b/src/mod/applications/mod_skel/mod_skel.c @@ -214,6 +214,28 @@ SWITCH_STANDARD_API(skel_function) return SWITCH_STATUS_SUCCESS; } +static void mycb(switch_core_session_t *session, switch_channel_callstate_t callstate, switch_device_record_t *drec) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, + "%s device: %s\nState: %s Dev State: %s/%s Total:%u Offhook:%u Active:%u Held:%u Hungup:%u Dur: %u %s\n", + switch_channel_get_name(channel), + drec->device_id, + switch_channel_callstate2str(callstate), + switch_channel_device_state2str(drec->last_state), + switch_channel_device_state2str(drec->state), + drec->stats.total, + drec->stats.offhook, + drec->stats.active, + drec->stats.held, + drec->stats.hup, + drec->active_stop ? (uint32_t)(drec->active_stop - drec->active_start) / 1000 : 0, + switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG) ? "FINAL LEG" : ""); + +} + + /* Macro expands to: switch_status_t mod_skel_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) { @@ -227,6 +249,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) SWITCH_ADD_API(api_interface, "skel", "Skel API", skel_function, "syntax"); + switch_channel_bind_device_state_handler(mycb, NULL); + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } @@ -237,6 +261,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skel_shutdown) { /* Cleanup dynamically allocated config settings */ + switch_channel_unbind_device_state_handler(mycb); switch_xml_config_cleanup(instructions); return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch_channel.c b/src/switch_channel.c index 429a48d73f..da41b92739 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -4701,31 +4701,36 @@ SWITCH_DECLARE(void) switch_channel_clear_device_record(switch_channel_t *channe static void process_device_hup(switch_channel_t *channel) { switch_hold_record_t *hr, *newhr, *last = NULL; + switch_device_record_t *drec = NULL; + switch_device_node_t *node; if (!channel->device_node) { return; } switch_mutex_lock(globals.device_mutex); - channel->device_node->hup_profile = switch_caller_profile_dup(channel->device_node->parent->pool, channel->caller_profile); - fetch_device_stats(channel->device_node->parent); + node = channel->device_node; + drec = channel->device_node->parent; - switch_ivr_generate_xml_cdr(channel->session, &channel->device_node->xml_cdr); - if (switch_event_create(&channel->device_node->event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) { - switch_channel_event_set_extended_data(channel, channel->device_node->event); + node->hup_profile = switch_caller_profile_dup(drec->pool, channel->caller_profile); + fetch_device_stats(drec); + + switch_ivr_generate_xml_cdr(channel->session, &node->xml_cdr); + if (switch_event_create(&node->event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_extended_data(channel, node->event); } for (hr = channel->hold_record; hr; hr = hr->next) { - newhr = switch_core_alloc(channel->device_node->parent->pool, sizeof(*newhr)); + newhr = switch_core_alloc(drec->pool, sizeof(*newhr)); newhr->on = hr->on; newhr->off = hr->off; if (hr->uuid) { - newhr->uuid = switch_core_strdup(channel->device_node->parent->pool, hr->uuid); + newhr->uuid = switch_core_strdup(drec->pool, hr->uuid); } - if (!channel->device_node->hold_record) { - channel->device_node->hold_record = newhr; + if (!node->hold_record) { + node->hold_record = newhr; } else { last->next = newhr; } @@ -4733,15 +4738,17 @@ static void process_device_hup(switch_channel_t *channel) last = newhr; } - if (!channel->device_node->parent->stats.offhook) { /* this is final call */ + if (!drec->stats.offhook) { /* this is final call */ - switch_core_hash_delete(globals.device_hash, channel->device_node->parent->device_id); + switch_core_hash_delete(globals.device_hash, drec->device_id); switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Processing last call from device [%s]\n", - channel->device_node->parent->device_id); + drec->device_id); switch_channel_set_flag(channel, CF_FINAL_DEVICE_LEG); + } else { + channel->device_node = NULL; } - channel->device_node->parent->refs--; + drec->refs--; switch_mutex_unlock(globals.device_mutex); From 16133053c195ba255539375d3a5dd289bd567ffa Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Fri, 28 Jun 2013 14:38:31 -0500 Subject: [PATCH 021/278] vp8 rpm and minor tweaks --- freeswitch.spec | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/freeswitch.spec b/freeswitch.spec index 0e638daf66..9379323e73 100644 --- a/freeswitch.spec +++ b/freeswitch.spec @@ -700,6 +700,14 @@ Requires: %{name} = %{version}-%{release} %description codec-isac iSAC Codec support for FreeSWITCH open source telephony platform +%package codec-vp8 +Summary: vp8 Codec support for FreeSWITCH open source telephony platform +Group: System/Libraries +Requires: %{name} = %{version}-%{release} + +%description codec-vp8 +iSAC Codec support for FreeSWITCH open source telephony platform + %package codec-mp4v Summary: MP4V Video Codec support for FreeSWITCH open source telephony platform Group: System/Libraries @@ -1329,7 +1337,7 @@ ASR_TTS_MODULES="asr_tts/mod_flite asr_tts/mod_pocketsphinx asr_tts/mod_tts_comm ###################################################################################################################### CODECS_MODULES="codecs/mod_amr codecs/mod_amrwb codecs/mod_bv codecs/mod_celt codecs/mod_codec2 codecs/mod_g723_1 \ codecs/mod_g729 codecs/mod_h26x codecs/mod_ilbc codecs/mod_isac codecs/mod_mp4v codecs/mod_opus codecs/mod_silk \ - codecs/mod_siren codecs/mod_speex codecs/mod_theora " + codecs/mod_siren codecs/mod_speex codecs/mod_theora mod_vp8" # %if %{build_sng_tc} CODECS_MODULES+="codecs/mod_sangoma_codec" @@ -2040,6 +2048,10 @@ fi %defattr(-,freeswitch,daemon) %{MODINSTDIR}/mod_mp4v.so* +%files codec-mp4v +%defattr(-,freeswitch,daemon) +%{MODINSTDIR}/mod_vp8.so* + %files codec-opus %defattr(-,freeswitch,daemon) %{MODINSTDIR}/mod_opus.so* @@ -2374,7 +2386,9 @@ fi # ###################################################################################################################### %changelog -* Thu June 19 2013 - krice@freeswitch.org +* Thu Jun 28 2013 - krice@freeswitch.org +- Add module for VP8 +* Thu Jun 19 2013 - krice@freeswitch.org - tweak files included for vanilla configs * Thu Sep 19 2012 - krice@freeswitch.org - Add support for Spanish and Portugese say language modules From 58e368034d47bc46ba8dfc9f4240df2d26f75a71 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Fri, 28 Jun 2013 15:08:44 -0500 Subject: [PATCH 022/278] ooops --- freeswitch.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freeswitch.spec b/freeswitch.spec index 9379323e73..10f45c08af 100644 --- a/freeswitch.spec +++ b/freeswitch.spec @@ -2048,7 +2048,7 @@ fi %defattr(-,freeswitch,daemon) %{MODINSTDIR}/mod_mp4v.so* -%files codec-mp4v +%files codec-vp8 %defattr(-,freeswitch,daemon) %{MODINSTDIR}/mod_vp8.so* From e1ed386bb979651f76198bd63a2a8a845f8beeb5 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 1 Jul 2013 12:16:47 -0500 Subject: [PATCH 023/278] FS-5565 --resolve --- src/switch_rtp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 821c375384..177fbffcc5 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -681,10 +681,14 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice) ice->next_run = now + RTP_STUN_FREQ; - if (ice == &rtp_session->rtcp_ice) { + if (ice == &rtp_session->rtcp_ice && rtp_session->rtcp_sock_output) { sock_output = rtp_session->rtcp_sock_output; } + if (!sock_output) { + return SWITCH_STATUS_FALSE; + } + switch_assert(rtp_session != NULL); switch_assert(ice->ice_user != NULL); From 8bcff4ca4d3643518f98d01f52772cf71661699f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 1 Jul 2013 14:31:43 -0500 Subject: [PATCH 024/278] fix input event checker pre-empting return value from dtmf checker when both are present --- src/switch_ivr.c | 15 ++++++++++++--- src/switch_ivr_play_say.c | 21 +++++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index a99070d241..ba4ad8bead 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -275,7 +275,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, switch_event_t *event = NULL; if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_status_t ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if (ostatus != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } switch_event_destroy(&event); } } @@ -1092,7 +1095,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { if (args && args->input_callback) { - if ((status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen)) != SWITCH_STATUS_SUCCESS) { + switch_status_t ostatus; + + if ((ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen)) != SWITCH_STATUS_SUCCESS) { + status = ostatus; break; } } else { @@ -1210,7 +1216,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_s } if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_status_t ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if (ostatus != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } switch_event_destroy(&event); } diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index fe62b911a0..1f2f076a06 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -687,9 +687,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se if (args->input_callback) { switch_event_t *event = NULL; + switch_status_t ostatus; if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if ((ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen)) != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } + switch_event_destroy(&event); } } @@ -941,7 +945,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_gentones(switch_core_session_t *sessi switch_event_t *event; if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_status_t ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if (ostatus != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } switch_event_destroy(&event); } } @@ -1433,7 +1440,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_event_t *event; if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_status_t ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if (ostatus != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } switch_event_destroy(&event); } } @@ -2278,7 +2288,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session if (args->input_callback) { if (switch_core_session_dequeue_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - status = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + switch_status_t ostatus = args->input_callback(session, event, SWITCH_INPUT_TYPE_EVENT, args->buf, args->buflen); + if (ostatus != SWITCH_STATUS_SUCCESS) { + status = ostatus; + } switch_event_destroy(&event); } } From c2c8fba14a0352dfeecf31a0f818d83f83a93a85 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Mon, 1 Jul 2013 17:03:00 -0500 Subject: [PATCH 025/278] --resolve FS-5566 When you use $12345 in regex substitutions, it isn't obvious whenever you mean $1-concatenated-2345 or $12-concatenated-345 or any other variation. In all other languages, in order to solve this ambiguity, a braces {} are allowed to be used to separate variable name (or a reference) from surrounding text, like ${1}2345 or ${12}345. Use the same for freeswitch too. While at it, fix a buffer overflow as well: the index[] variable which is used to copy the "variable" name is 10 chars long, but it is used in the code without bounds checking, so a reference which is >9 chars long ($1234567890) will overflow the buffer, crashing freeswitch. And another overflow is in the way how size of the "substituted" variable is handled. First, in the outer loop, we compare the wrong variable with the size of `substituted' buffer (amount of bytes we took from the source instead of amount of bytes we used in `substituted'). And second, when actual regex match is being substitured, amount of room in `substituted' variable is not checked at all. Patch contributed by Michael Tokarev --- src/switch_regex.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/switch_regex.c b/src/switch_regex.c index 0a3479582e..2a98638134 100644 --- a/src/switch_regex.c +++ b/src/switch_regex.c @@ -132,20 +132,37 @@ SWITCH_DECLARE(void) switch_perform_substitution(switch_regex_t *re, int match_c const char *replace = NULL; switch_size_t x, y = 0, z = 0; int num = 0; + int brace; - for (x = 0; x < (len - 1) && x < strlen(data);) { + for (x = 0; y < (len - 1) && x < strlen(data);) { if (data[x] == '$') { x++; + brace = data[x] == '{'; + if (brace) { + x++; + } + if (!(data[x] > 47 && data[x] < 58)) { + x -= brace; substituted[y++] = data[x - 1]; continue; } - while (data[x] > 47 && data[x] < 58) { + while (data[x] > 47 && data[x] < 58 && z < sizeof(index) - 1) { index[z++] = data[x]; x++; } + if (brace) { + if (data[x] != '}') { + x -= z - 1; + substituted[y++] = data[x - 1]; + continue; + } + else { + x++; + } + } index[z++] = '\0'; z = 0; num = atoi(index); @@ -156,7 +173,7 @@ SWITCH_DECLARE(void) switch_perform_substitution(switch_regex_t *re, int match_c if (pcre_get_substring(field_data, ovector, match_count, num, &replace) > 0) { switch_size_t r; - for (r = 0; r < strlen(replace); r++) { + for (r = 0; r < strlen(replace) && y < (len - 1); r++) { substituted[y++] = replace[r]; } pcre_free_substring(replace); From a0043b8ca20a109d5b521426d8cdde510cbdc99b Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Mon, 1 Jul 2013 22:19:15 -0500 Subject: [PATCH 026/278] partial rollback of ec6754262863475136c6a53b012eb7b0bb62d7ba to fix skinny breakage --- src/mod/endpoints/mod_skinny/mod_skinny.c | 49 +++++++------------- src/mod/endpoints/mod_skinny/skinny_server.c | 4 -- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index f5c01481d4..6845ab2070 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1051,39 +1051,26 @@ switch_status_t channel_receive_message(switch_core_session_t *session, switch_c private_t *tech_pvt = switch_core_session_get_private(session); switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_ANSWER: - switch_clear_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); - return channel_answer_channel(session); - - case SWITCH_MESSAGE_INDICATE_DISPLAY: - skinny_session_send_call_info_all(session); - break; - - case SWITCH_MESSAGE_INDICATE_PROGRESS: - if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { - /* early media */ - switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + case SWITCH_MESSAGE_INDICATE_ANSWER: + switch_clear_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); return channel_answer_channel(session); - } - break; - - case SWITCH_MESSAGE_INDICATE_BRIDGE: - if (switch_rtp_ready(tech_pvt->rtp_session)) { - rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_STICK); - } - break; - case SWITCH_MESSAGE_INDICATE_UNBRIDGE: - if (switch_rtp_ready(tech_pvt->rtp_session)) { - rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_UNSTICK); - } - break; - - default: - break; - - } - return SWITCH_STATUS_SUCCESS; + case SWITCH_MESSAGE_INDICATE_DISPLAY: + skinny_session_send_call_info_all(session); + return SWITCH_STATUS_SUCCESS; + + case SWITCH_MESSAGE_INDICATE_PROGRESS: + if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { + /* early media */ + switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + return channel_answer_channel(session); + } + return SWITCH_STATUS_SUCCESS; + + default: + return SWITCH_STATUS_SUCCESS; + + } } diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 0cbde82e47..c3c8f9029b 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -1734,10 +1734,6 @@ switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *liste struct in_addr addr; switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID] = {0}; - flags[SWITCH_RTP_FLAG_DATAWAIT]++; - flags[SWITCH_RTP_FLAG_AUTOADJ]++; - flags[SWITCH_RTP_FLAG_RAW_WRITE]++; - tech_pvt = switch_core_session_get_private(session); channel = switch_core_session_get_channel(session); From d2a2e4ce1537bad92937c42178a0d8ec3f4cb438 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 2 Jul 2013 08:44:47 -0500 Subject: [PATCH 027/278] FS-5568 --resolve the real problem is that L=-1 is not valid because the L param is about how many times to generate the output while generating the tone so -1 is impossible because it can never end to let you hear it. The real fix is to not allow -1 loops=-1 is the correct way to do this because that is parsed after the tone is generated and repeats the entire thing. Doing something like L=100 actually generates the whole slinear audio 100 times into memore where loops=100 only generates the little snippet and repeats it after it was generated. --- libs/freetdm/src/libteletone_generate.c | 8 ++++++-- libs/libteletone/src/libteletone_generate.c | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libs/freetdm/src/libteletone_generate.c b/libs/freetdm/src/libteletone_generate.c index 3654d1ec34..ef7a7e03ec 100644 --- a/libs/freetdm/src/libteletone_generate.c +++ b/libs/freetdm/src/libteletone_generate.c @@ -382,9 +382,12 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm break; case 'L': if (!LOOPING) { - ts->LOOPS = atoi(cur + 2); + int L; + if ((L = atoi(cur + 2)) > 0) { + ts->LOOPS = L; + LOOPING++; + } } - LOOPING++; break; } } else { @@ -480,6 +483,7 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm bottom: free(data); data = NULL; + if (ts->LOOPS > 0) { ts->LOOPS--; } diff --git a/libs/libteletone/src/libteletone_generate.c b/libs/libteletone/src/libteletone_generate.c index 3654d1ec34..ef7a7e03ec 100644 --- a/libs/libteletone/src/libteletone_generate.c +++ b/libs/libteletone/src/libteletone_generate.c @@ -382,9 +382,12 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm break; case 'L': if (!LOOPING) { - ts->LOOPS = atoi(cur + 2); + int L; + if ((L = atoi(cur + 2)) > 0) { + ts->LOOPS = L; + LOOPING++; + } } - LOOPING++; break; } } else { @@ -480,6 +483,7 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm bottom: free(data); data = NULL; + if (ts->LOOPS > 0) { ts->LOOPS--; } From 134871872c458754c11a7e7c399e75e9840d29ed Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 2 Jul 2013 12:16:54 -0500 Subject: [PATCH 028/278] fix codec negotiation issue with the same codec at multiple rates --- src/switch_core_media.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 9270b0b729..4b926ed810 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -2788,7 +2788,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s if ((zstr(map->rm_encoding) || (smh->mparams->ndlb & SM_NDLB_ALLOW_BAD_IANANAME)) && map->rm_pt < 96) { match = (map->rm_pt == imp->ianacode) ? 1 : 0; } else { - match = strcasecmp(rm_encoding, imp->iananame) ? 0 : 1; + match = (!strcasecmp(rm_encoding, imp->iananame) && (map->rm_rate == codec_rate)) ? 1 : 0 } if (match && bit_rate && map_bit_rate && map_bit_rate != bit_rate && strcasecmp(map->rm_encoding, "ilbc") && From ba96b391d390f80c208da709361ce1d7fa6ec466 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Tue, 2 Jul 2013 12:41:15 -0500 Subject: [PATCH 029/278] fix trivial compiler error --- src/switch_core_media.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 4b926ed810..a52c9e08e7 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -2788,7 +2788,7 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s if ((zstr(map->rm_encoding) || (smh->mparams->ndlb & SM_NDLB_ALLOW_BAD_IANANAME)) && map->rm_pt < 96) { match = (map->rm_pt == imp->ianacode) ? 1 : 0; } else { - match = (!strcasecmp(rm_encoding, imp->iananame) && (map->rm_rate == codec_rate)) ? 1 : 0 + match = (!strcasecmp(rm_encoding, imp->iananame) && (map->rm_rate == codec_rate)) ? 1 : 0; } if (match && bit_rate && map_bit_rate && map_bit_rate != bit_rate && strcasecmp(map->rm_encoding, "ilbc") && From 20531a097a01f758bb417435eb932808653eca04 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Wed, 3 Jul 2013 02:39:09 +0800 Subject: [PATCH 030/278] Added a "nation" parameter to v18_init(), in preparation for automoding. --- libs/spandsp/src/spandsp/private/v18.h | 1 + libs/spandsp/src/spandsp/v18.h | 12 +- libs/spandsp/src/v18.c | 2 + .../test-data/local/lenna-colour-bilevel.tif | Bin 0 -> 990730 bytes libs/spandsp/tests/v18_tests.c | 334 +++++++++--------- .../mod_spandsp/mod_spandsp_dsp.c | 6 +- 6 files changed, 183 insertions(+), 172 deletions(-) create mode 100644 libs/spandsp/test-data/local/lenna-colour-bilevel.tif diff --git a/libs/spandsp/src/spandsp/private/v18.h b/libs/spandsp/src/spandsp/private/v18.h index a991cc5107..2ce0520108 100644 --- a/libs/spandsp/src/spandsp/private/v18.h +++ b/libs/spandsp/src/spandsp/private/v18.h @@ -31,6 +31,7 @@ struct v18_state_s /*! \brief TRUE if we are the calling modem */ int calling_party; int mode; + int nation; put_msg_func_t put_msg; void *user_data; int repeat_shifts; diff --git a/libs/spandsp/src/spandsp/v18.h b/libs/spandsp/src/spandsp/v18.h index fbd4994a59..b1fd500f43 100644 --- a/libs/spandsp/src/spandsp/v18.h +++ b/libs/spandsp/src/spandsp/v18.h @@ -54,12 +54,16 @@ enum /* V.18 Annex F - V.21 text telephone, V.21, duplex, ASCII (Sweden, Norway and Finland). */ V18_MODE_V21TEXTPHONE = 7, /* V.18 Annex G - V.18 text telephone mode. */ - V18_MODE_V18TEXTPHONE = 8 + V18_MODE_V18TEXTPHONE = 8, + /* Use repetitive shift characters where character set shifts are used */ + V18_MODE_REPETITIVE_SHIFTS_OPTION = 0x1000 }; /* Automoding sequences for different countries */ enum { + V18_AUTOMODING_GLOBAL = 0, + /* 5-bit, V.21, V.23, EDT, DTMF, Bell 103 */ V18_AUTOMODING_AUSTRALIA, V18_AUTOMODING_IRELAND, @@ -89,7 +93,9 @@ enum /* V.23, EDT, DTMF, 5-bit, V.21, Bell 103 */ V18_AUTOMODING_FRANCE, - V18_AUTOMODING_BELGIUM + V18_AUTOMODING_BELGIUM, + + V18_AUTOMODING_END }; #if defined(__cplusplus) @@ -104,6 +110,7 @@ SPAN_DECLARE(logging_state_t *) v18_get_logging_state(v18_state_t *s); \param s The V.18 context. \param calling_party TRUE if caller mode, else answerer mode. \param mode Mode of operation. + \param nation National variant for automoding. \param put_msg A callback routine called to deliver the received text to the application. \param user_data An opaque pointer for the callback routine. @@ -111,6 +118,7 @@ SPAN_DECLARE(logging_state_t *) v18_get_logging_state(v18_state_t *s); SPAN_DECLARE(v18_state_t *) v18_init(v18_state_t *s, int calling_party, int mode, + int nation, put_msg_func_t put_msg, void *user_data); diff --git a/libs/spandsp/src/v18.c b/libs/spandsp/src/v18.c index 16f5da5ac6..f3f21ad86f 100644 --- a/libs/spandsp/src/v18.c +++ b/libs/spandsp/src/v18.c @@ -954,6 +954,7 @@ SPAN_DECLARE(logging_state_t *) v18_get_logging_state(v18_state_t *s) SPAN_DECLARE(v18_state_t *) v18_init(v18_state_t *s, int calling_party, int mode, + int nation, put_msg_func_t put_msg, void *user_data) { @@ -1024,6 +1025,7 @@ SPAN_DECLARE(v18_state_t *) v18_init(v18_state_t *s, fsk_rx_init(&s->fskrx, &preset_fsk_specs[FSK_V21CH1], FSK_FRAME_MODE_7E1_FRAMES, v18_textphone_put_async_byte, s); break; } + s->nation = nation; queue_init(&s->queue.queue, 128, QUEUE_READ_ATOMIC | QUEUE_WRITE_ATOMIC); return s; } diff --git a/libs/spandsp/test-data/local/lenna-colour-bilevel.tif b/libs/spandsp/test-data/local/lenna-colour-bilevel.tif new file mode 100644 index 0000000000000000000000000000000000000000..9d79aa868e5a66cc5eed3d360ea28066a3493039 GIT binary patch literal 990730 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

8LDieweB^-1iV8F`5 zr{rtI6&2Ox0lzfH`4uWBx;`c6)aZN)1&otY*sZ#$%luH{p#X<`5cE;hKUfF%@?Z`D zK(x0|Cq}n>Km}rtt0z=WLjs)wTzD&)sII1|N;iU%c7g!tiNNINwyJa|F@ZTKO5$GF zJ>{g*k<~(#WXlpP1ryHgmVl%riVx_ETfew!H}Q0~`6{>+X*OU_LAzeqQT3*EFjaxV zGKdu>s<&vKXLrzd^wiiy!yWy6KiYH2q^??XR+~SQoB#XVE zc*sbtkGB8JOjTCcMLE|CPv9Pjz#P56#$&~VhcTwTLoA1CR)1|aRhmntI<`PwxQAoI zj}(zF@90N?8Cx@qBxh+$O|Tg&yFRd#l7H{mpb9tn@UG?W(C_VACF;>J+6lPgDa56* z#S1;*Oi>|FO$)}f2kp9h2b(3W7vEwRjOY`ojCHIVZQ~W<5t$0=;SwC=8}-SSnvNq~ zB=9-Hc29dDiM|8T_yfUDc-h!v6eS#eOlTO{xQ;JVC8og=_85<6;B3ee6tRt=Mkc9{jvz;Jt#65k+x6$Lll-d z6i)D)6rj!cq-R)6Y-VJlXadoDnTDeC0JK#8liVbp!HJQ?>M`4>)c^=UDx()j5lStj zI^u%DB5n3`+kR5_myI&?6eF)m(7Ump78w)y-UJOI*pgzbtQ&txB&A z_G2i<%ON8jbyDLIfBX)tRLRj{q7DEha2k>aM>1^fpmA&4@W(=VH23r7 z!(#qi(ubU9lo8}pPDvA<038YHUeeu4^shE?4r!b8UY~*4*YQ4Ke zW=yz^PhbOoW#Q#F?;hUsC)=A}A~+CsYV_!RrUr!!;(_wu=1E0GH7+3g!o1EKZ;9f^ z)e_+Jg$riP{>nbLX#4(f$H;eZsrr=^39KyH#>>8S7K7lq|FC8aPN2q{YZ6Y91^^Qqff&YG1Z7?CQw60r>-d=D{?xPAPYV zWtTG?`lg}E|48arIl(671WS5r|5P#J^Bl66-7>i!7MlNOeP+Uu-u7(SgFj@@wzS(K!r!c{^HVd=^Dk4#kmp9JtsxpDbE?94*gx1G4o%K4K)W(KBMv6Hcye8J!sm38@k1w|%1rzv$ z*wLR81INlgH`^Vykmj>wCv{gww@cLy=tP52*~OESSi9~j(s=yy)dEq5b#-0TGeC(D zy2oGMeqs2x(TNw1QR4kn1Fipmvoh#_Tv(<>;F-5+G-P_N# z9Q&JNfAj5lOZAC1IqPv8?+QQ^cvB6KuWe7i3@dnI-gDo-uIR?P|7y%g=whOB<0Q3fO_DfxLNqSCVX+?Y*(FYS=g2}DJcX3z?~=_K^km(;#}b}om6k-h zKnW=VF%c3u^h7kE%|r4Wo`6F~j>9?~Fyo$ZQ5R9(w&@b*kW{?XnOJG2%r8z3kLGCfdXTYe3`r7Mn!o&ov83d?*0{Tz;o6<^u1qAKFvFia5rZv^Y7(jw{!HzpI%#*!F z&rn60^JCS^7x1AYC&L{G$=ad6FN8=nslXX=PpWMYt*tw!H?l0mo)#&KxW`cRjq4LY zPbB^F9F-%3t3i}njl@!vAaT8|umD1TIW*X3m_EUxqAGpW&q*%ehj&Uo2jrY*U>eWf z+BWvK!6(~d1OW|@eVk{@j#;EkA!sjIfK~3WZf8F+%%%y14il#y(>ec9==M zYyAZ<6{nou;1oT?bD`-D8IgNQ&rZl7mo2W;s8V&S&df7o3k0V0lpLHNcH)0rnd2-H zNxqG-wwj5(*$EMPmZ?bR2;5?I!#n5U6HfpSb!8&}%dDWd;%!v@fuXEY6NdOspgwG; zQTE?E%^i1t<9Z%E5WM9UV?SGN`D2zpCdje-$zDcYe7uGsU8T!Fyc&9IIK3?XQ^Mbm z%Rd>g^$D$E{blf+$W9lyG<^8Lymf&iyM*EQ3oBz3EN%o!n_4F-wrt{bGEe%1U-Km4 zE@Nhub5jzFnb>zp{*bPKH^svQBrNo_B5q)e^1yFy+H2S^0ta*X=b!61cNFQgrsOXl z7(ZOhW{eB#UR)s86xYbXQENZ|2?!8&S#92ryA^+v1VhQ^w@9E9{K(O>B}JyQ^2zvA;}SJ zFi5)eBhaWy*|Cwc>8L-JoYj?`49$nw47Q&)Y0_Gf>9PMBG|B$1nmJ&5r?(&MSwv0JPuujsFmtr5Y)!!ds(SSQjo9jX zpn7gyy^RxZ5})u6rIX`jzI(~)m1IvKIp(`ME?sz`r1PvW6OaS7S=ZLVKU|~mHpk^+ z)bj-vRgEw=c?g-OpB8lpb!zCeVA3hP3Els`kkRPaVTqc>LGw+R6UYJE;< z^tUuEoQa6yz*WMf@zVSf2flPuYOQ!Nn^b@RXh(KS#3RO&d;w~4gPl8fHrf}`%bQjp zDRhw-GEEaddNJ-1!(|VBEv{SuNWKEy9GaNH@;vH@tkLC++{ypCLx&`;LmL}U)aK?f zW*B_Q`*q9|uvb461mu9)tm+$n$B8eqqFN`ldJj)%-_R zd};X4KvUd`9HF*=UV(9U;4a4fAUMD9^!qz|mOgi3c*$)f>A)}Da`B<{Gxft;qrjzV zBnH|d%65&WVpq6!*TSCSp)i6mPw9r4LHAEoYn(vQuGu~&8b{X|#F#dtpbA2IQ7#(j zBhmU*d&C+~A|_dSn!I=+{eyy|N*2x{CQWbZeU;=f^JI*LQL2dKlPP;Lv>Wcih^5ZG zg}C8L7$V5OrJ%AaaaAVtyHIT?KW+ScQ?%V{;Q>5irca=ps3p!ReX3(lfl|^z7;ja# zZU`fEFHKxS1!q_ctOmB46ZxRK&W=;;*5URoO@es{eZK+-au7+azRXroMgcSIUL(pV%j8c^xXH#*1P`du5pEc>9BD*RM=mop9>M(R;@>mdkv z!{<%*Xb*|R9>)|IOlqODb@Eg3j?yV=`FUEbO#r+@J29PT&4}b%eVB7yv#5y(v4k|#Rq^%xDn4w*~neBm$dpmv$!z1wl^CGtpR$6c#mUCUY^Bai)Rp)#3k4)&#E5_a_NNF(oneNH2Bf z4D(}Pc8Q2e`)_7Il_lHa-W;Iy%!;dz4<+y1%`K=BO}Qu9Ffco?F&07-Z2h9EnStbz zSIjoc^~c&tdqAG5wN!G$S&Aw(l+lHc5<4;@aGD}H6p(No`7wDvo=(S^MI^WaCcSj* zr3<8deDdXU-S7UgVmldYrz(?}HFfB4N7q82h8|7rv*hf!>0QzF8oj3A;f7O3l1END z4@jumgL|ArtSWu(Sa694V{{A$q^>)&hUv*t=K2bker(__VjnqaF_aRfi%KfZ`*073 z%+X!^Zdwx0DdLVvi8g3qP_6)GGd2glj<83k6@j1z^Rh#nPp$$*!PYH*@D~-Rh(9G5 zB|KkRkOp|6{ucbJU}&~NqtDEaf7kW77*{^QC?pxr0KDUH#<;l51X)4;TFp{?X+>zK z1TC9~7?iI$Os)17*swhc+{5=A z(dzb;>HV$P_tI*w#?I}Tt=jT1-tpAnn&8#lRi5P%k21B`a7cl=JGs)uip8LZc3AUW zy`^-4tFaM(PvMN5;lV~8>P-T1dE;$;?B~|*yItwgzV~+X_wX46Pc&kX!Bu7YnOB8D z!SJF2ethXtYxNArTKU~Pbf znDh$aW~G(yC+ijFl8Q$H zn>jr~N=VXT|DPnhzKspv4z>xI`+tDi+taea`Za_27SBf@u@glfTG15$Clj*;KDd!a zIbgdR)A`$-Be7)NG5fLMqIR-QygF^NYWs<&%34KWtw#V+kPZ2T3}bC7*2o8hPy<}) zHgdOHAzti?v*ys|tyDlp=>Righ+vKT{@J<|FfASXCXZer7A`#0TkbTmf?WZepf&4F zM!>S#Nn(W5a9f2GiendEH1cYU45}rZ5PM;zO{MwO9cCxDp6}QoNxTWqMwY2Z5w!pW z1#&_eG25^er)H!$$uH?>)34O{T zoCwJ>Iktf9{52CP2}l~EPV^rrsjQ))xz0Zi)@a|h$3@l@35<`-R~%^$}dm|0bwhStWjdro&gMptT|{6Lk- zbk@uaS|S~qj4tym7H4ehwM)-1oCJv)nXtSCpQ!$}+Jmtd+v@_uEv)nAV9zkAkm5PL(g__YXVptlFOBq94j^2v zT)}>tJ&kLG2SB1;(V@bm+V;%L)q<-Dn|MeR!v8hnW|JZX2hF)t*u!GxCv$!SF)z#t z;R&{Zfsqpn_hzoxh5O44Eiwk|iF2Q3%i$KbGig8mQM7Q)%q}v3uz!5*HQWf;FR+5O zrYZt8+tMP_O^JW1rCkmpD$nG7TE^&b9i zzo}~!{Ck3hSr+(VHJOH$8O@$DY3cBtBa_MrO8Mbh9+$Z0ElW>n%_S2;flmz${OK1B z#YTQ}{F_Oie&XRxJ-OmK!W+FP^Gt$@jx5f`+*NIO#vuCf!sH)SZOxDZmHfB3DU{^}5+ey$mG5pM57+$z_(rz!#(z)h*lrz>${# zR3U-`k*MrL{MgvgKBe8VB3FCP%j%DYb*~U7S5Mir@<9DPiIw`tFH{7aW-eHkDn*nq zt?o<(G7=i4A#~g(rcZIW7bBHpq&XEr@s&X;=DNs6I03MXt-|3&&K4xj~{n)9UVyWu8HrCqxo5dZp@= zpC9|1C(nKU+*5n+e`B1=HUHvCjDE-V-sOk^z_;Iuv9jm-AAkAXp5}Y6KY9GR--|ZP z(#TQ_y%DMKACAUXfXOet#EdqF&@BJi{zYyXH3Qbbfq5MAii)=nm}|;Y7xx*)WUPoT z9cl#D3lIt0UX+c}7QJgaa{X}?@^S)*i;XxMh=Vlo@v~|9%5@|z=7b~e#PDqyJ4~`- zT%uU`quEdQM#y~p8mPf>^DGb%?^+gV!thWlZ&h!ejoJY94eYb9t)m*##2} z6r;}oI$2QWs%Zd2_WSUcwRO~S*?vk(-G~nX%mUiLTB?tlxf;MFg!k<|!|>1WAIga| ziCh4X0E?hw?t#1c$VBIwC1zD8yEJbxm=^`i{U6KK#E^Q`n}uG&jF3wwt|SL|A^>_0 z!iwo;b*1apRYXq|Evwt$I%D+=4l6dgRe&a<4bOqKmy<|Ez{0Dwu&yW;@cd?~c3z(bgQnE@`H$5ik24MKS^s49(*L#2#1FUppp z+C*&hxe?)bS?}|WHO8ZT0L{2jFu~}xJ_RL3yds=vn7~4770^erGq! z_42=+t`<5^y~X$N4F~V(1O*7%gCkVsM!*QGruikFfO0kdl-F{+Cj9SU%O;T-Hj}ix zDdATF6l#McyN{bcAgu+515(dYccoKaQW!s=uTyl3XW5A{?!5IH|_Cl-X#V3WS)Hj+Lxwdyekc&b03VXC4&>^=h zM<&xVExuRIkoc6|`2*Tm>_gib%**44zPKswj(mV5E@B<%u4aH8$rws&asSlAD*gz7 zSKup;RG5W87&t4$PRK=RRAH$QvN^_{f_*8$)Is(0jlK>fXKBt@WYA}>S@o^XA!Mw; z>I{j;=u(+9a^P}Vm#&=A)sFN1zQi$?ymKdvKYKDhE27HQmJj5yB5?fltc2*2kM%Z_ zj?@9Flom~$QPP~kD`LXSv(W%t!VgHyI<|vccKFVvw$mgG*-Dkg`Fy{*LW$Z zUz4o-o&t8X^A)8VfWZyIXH=*ttWp6H0)b@!DZxgisa4f{N&x1kQpzPP%3Q!uM^m?9 zstcnfaEOwhg95}k1we#<3O(ha1I5J@@m%A;#^jO%7} zswdh2O2L(7d>Uk2qx1C_1r%j)MWlhd)%fK3)?eG(7$kit4ub${&K<6{tPJlP4>_K1 zjQ*l~@wq3@E#6%{fz=r>qWJDR+4?x4q5t=en@*aF;#aayoNKCKp{4NCt5V`q4EK1%;`)f4rpiLB)O5Stb}EE4 zGEIbpI}cBVLdw{Lu#Pz+Z}8VKchqW3(*z~+00P~YW;d(PPodvNw)+#C8lJe+ zI${V9`&33q^0cdM(-a3~EPa9mb@NbxQftm=Bz;1JrFJ;fc{xSPo>KXtRDj?-8ySWW ze4>={JUnY70|IE(Zm20Q&nZ3HAA}0>N%$3ad<>U&5SR8n^kISxNvMWX39##qHAj1A zA*EzMwn>3PgqRAlt96i$v+%g%sS>G%)eMGV$9tdC1x%N%;2o!bSAg~O*avoj%L@0H zFXSO(wRC}b6WizX!NrA1Wl>3{9+!Z~bV$g&)ojY4IF#$Be#Wew@pYq=vwiFQBcJIP zmjc80?2*$n3n%94$vgG$vD}d50hi+)zI;`9Osq7M+Y|a3z7)6j@zi2RJ=4hW7LSC1odg3x7*pq2x8e>HWt{^~8!|fG7^M!u6v$65!jO zk}7x)v3!6-)DV8HdE3G>Z#WBR(B<1ct@FD#^$L) zgdYK0xh{Lhc8B?%u0bKP6E|#+-5h(liNO!=BvC#q-;LDTRiu!*%fuMnyug&#y>HzK zYW;!2Yd8Hfq*SH+u97Iv{q{ZjMB%xU3{7M@*n8Kz>egFtxaI4jlHGR!a8P`vLa>NS z^nTI)1sOyU>pp&bZ%kSo%^N^97zzFtZC`gA0v-ex#q1+2k>U`f)e&AWN(H~XHD~{S z>hJVQqjBDE}#iC+1YzE$m(yAvM;kUDB1rjoy_CuhHq ztXl=T*y##%=0-lKV7WSg3Jb*mUB!-{!cPq*OZL#OGTs>x?Rr7GdC0s`;hw5Xx#pY? z&`<`x8hP*7F#K(rsF4NXj!pmRq4)mG(bI}a_er87^w_&}SAu zf9fdcW=q|b3Vw<*J$~8g&7l1BngLTG*ZSs>dk?2_ofg&trN-_DH0TeJ3_44J5I}{f zKo>jG4P_|DrCXu0&ZGW7mLVn16h;YpN{4Fcq{CsX5REhAmWJhXZl{L2T3N;f7mpKF zcQ<&5fMQ;wK1f}H{8M_?3!PvEJHVwr!1hI29_4@%z+dGX^FB6`BS*$ZvYmT$R4^A0 zC`;k65yadVPg10gNr1R3Haa1lafp_0AUAOYDCJkxF_I&2Z-l^`;2cRg4|0VrA50br zM5aSWy^8GfKYXoyGt>m}_g4St*zya-oB3E&R+A;@_*-cyC#lK!;TdN+rYtP~-<%{F zJ2h!CY~ZLP1S%r}xfX^$PK^~%TxfcC3^5jXkTgUIn+aiJc;Go~aW)N*ynOlIKiskJ zJ4?3!r5=Y|=+MA|np}STE7bjnW7M27kQrt)Fm;Tv*P1!e-05&nP3X8^{uoYzvM?xB zDj+*=2F~NnLKVM1hKq^KTke;+VS6X=F6XC2QUYp1_6ZASN?OkOYeHIbb4 zq)@uVIoh#`4fbpFkWk%`Tq~LnMy=C4<%LN0S`H<=DNE8E<%IC#mdn0)>9*F{%q`Q|B9-WZ__ zW)lsGD>h3C2rdamzf+L_T*n~E!+slhHa7mXb8XKPny~gf(tx#JkKDVX`KgC_$gRW1 z1hDNgFX!hUAY(prY0sW+LRFYd$G`IR3x*sN31l;_a(_UpjkB}0ZbDrD=%cF66^6_; zE6gHA(W;U;W3wiAD${sx8BjzK;a&(V5CtN|Sy4|=IYFZzWViP!9^`D1usrJyJ!s$I zZ2E8>B?kIe`a%J`j_e@4p7NPcxR8)I+S|0w)waTCXscv4()tD1AXEr;l zCiIr)@;Jj&aRz~?WeikDR3wgM{%tUi8yLJMjG9CQ5g>t#%H(kWXdvXS4_@Ii$eg5i zz4#NYaXtqU7-Ta0g@ zZ+b43pqeb01%MJI{_fEo!3(j%{c=0Dmv`gLv;@{6%00RfBb3nP+_@9;jrfK#AS=n-j^1^~#5IzVb1tq=moPaCKgY|(g=7Xez8VXwLZKO zd{R&0mAKYU$(ilv{(>>0d6orc3>-9Ate_()xq3krv0u87Cjcq6tc+)c^-k!*?w8F9 zT@_H8$5`P+QRB*|%P?1OR(}Kym60h_(WkIbL8Ap9rorXW*T*rf3NUhL3rOX*dC=t? zCxfub=U$+eO{9_B@{Nc7Z1e7d*(U+j)6xL5huO>}9xUkB%7OVt@+QBv(H>jH*#HLMFgY15^>;)yE zfDsxd<%~tn?L1TzmWkqLG|;k*@HNs_4sL!Q)PaX*;>95Zek}><%Bm`YOxKJQK)_!T zc3rpvID1r+fh(S@<{J=dQV3`Q#38D|rH(7QVIw3eanr{>^5UD6yu${jW>YLdCcyJh zjfE~F$Am)t8%En`HPkZ#3k?>kAC>%h#LhT0zz}lPOnm(#-W)C;Z3PM>wl0;u;1Q6R zx>cZkYhH$6WBjL$XfH+q1&Iz8d<^xju*F$brw zkN*92`l>Y!gM5G9jL`-1!nGnWiM3`8p31F#$n<_Q&!oh{FHyz^dd1H2er zix)UvO+pBMi9g9!z#K7Q^VGi?>MC#thYpIam@bTMIeGEpyL<;sU=lb1lm=ckZ9(}Y z1HN!Q^>P^FJCnQsWHE9Fy5SRI`7>I!hes!cFg>F>i`ADQ6&>)Nqddr~w=kNKq2SO& zJ5~0;>dc7*%aIQWRKvf(W@j}2X;?@w-BNU1&q3!%GjND#DzZU|ib&F!K_K`X<;PKW z8mukTAr*IeZaqEr=-C-VpID{97uwI%#`tD<9oL02U@4rPaq+@iP-{?_`KJCnWV94z zCo}cOuSu|!ROL^R+g*2G_?c&OAeUvFX|$FaCBLtns4G{WW<39uu39 z=DsA`oHV?^Bn8)ycL1ZV_BzyEaM7>S0}e89j%)p!mOW*Z1*TggulTnhaR*N(yWFT* zo=Qp#;U9i8P*0Ryr@o$MaB2lXXSGt1QS6yKty+i}Ieq< zd2`dRV;r|zyixC5^6Y8zsu)SLjTq#3M&dij2F~qvZdtq3DVB!~qMa~I_01zo>pfPN z(rtfnn>1!drfGLT5dw`~Fzb;wrNt)lGvR$kq}eR^2^yqDAbxh3(AjSvK78nKN3W4M zTX0Y?{?2Yk(8ELO|D?ze%jpkQxC=WArG)0?nkYt^PQ@^y~%9}IKgcRBp{0+I#Df>!UtWrEwBjBr<@)e5ehS5 z_YxjLo^l)AaH`0-=+k7j)L5nJm$`_J74BdHECudFRWzbP5UA#8-W=jQRd=A5?X2)a z{W2BT!X*pe(dcPuOvBVx%y6MTc&9oG%1O4x0c*I7>qp+VQj}R7pz5bW%#ff8A1s>^ zPR4Bq0Gt6pIUHHhd47 z+L2|QgZxfO_V_%p(StBp+dO5i$T8fETOI@w^^NW(PcEK-@U9h)?3!|kPxraAvoaU{ zKuAIb)cEcd#quHpg1CvdY zgO`h7DnZ2^Lz{m={EF$vm2nJ2HvF@6gr;7;6J(DebZLI{`MD_ zY6K$eyl_eKNo%yHkobwG4S!|?6nQf(M!YA%awlIJhFsD+Tz2SlG1@w02x$~JPXKjL zAj6VXpfB(juDmPsax-TSA>|e}b6OD6PVz7gVX0xF^ci(?lFTzHoK?!d2)dyINP4J+ zqVAXibJw*4hDHVWSd>Ehrn5mw-(>5(&b;<8kc0HUlUi~VVBF7^X z+A-Y#omb8>Q;LRM9a(@ON|(5@K1|yR8m5o0A8-O-H~U@6k#*+rr)0O@?dgDUE}Vd2 z@<&)pd1T|0T~JJrvCK1omEm7xcRzHfU7xz?DNZ9VHHvSRgY9dcfp^1IzvaN%TbA#T zdc@a%E#CbUec|cr?+-tSscr4QdwIpX+cDYB_Kd+P5B8iy>*1902|{DuuD@i8x}g02 z_)S+d!yZhIJumch?IkSx)7ydAfXw@>t%gO8S|DoG_){G$)dbe8pqRi~%y7tx3{&M7 zy`XDv7BJ(O+YlK``lB`TTdX$c0f9Wuiae)Ag7GKbK2v762Z564Sk2cf z0D`9!ip*3eFaMEp_Aasg90|B9P|@Im+x%;wz=~@IY9Zl37I7FDkyt(_ms`g zc!0#$XpzgD+KQ+|zzD|0m?@%Y+7nTcdDDvXnqUFhKn?Vm1!y_Kkrzz_7Dg_jX5hWXBg$gz} ztqvwCC0#KYfdv($s`PikR{>6-mjbP>LW$4xyfFmTi%-EJ|7f5iY-|mS9RB0{3r6x- z7^Nz+Lg=2j8)*+rM55G608OxuadHL9h%&3#JhF=iRPB@+;K!H9i&QQj(} zRELBD5!0~G0lB`eKwBMgR;{$gp%f<6& zfU8hcq!b43^uOQzrTLFEA2|Ilp8Vm4+i4cHU3xCO@a%W> zCoq`R94Z*OfDKxs3%&2OnJ%PT?X1GgC^lu=$e1;e5FvcY?e!yy}QA zP9gGPs3g8j^2WN>q#ov2y>R83dY4Ma@^e<7n{0pZ1!D1&i~%{obqClO>Alg^OY{vm z>LGV>WDUR>CiFe>Cvb<&9g@tQ-md$>d96e=Cy`Up(Q$fzh|!Y^pyL$SlV&3v&2W1K zBOdQ`2#m1C1I0aoTAL4fJFfMiS9#DiQC{CnKuW%yq%dg+C~ z1}kYF!Gr1KqF*hPK$H;@v>Hx5&{3<&hdmD2fqms0IEmj5pJd2D^YFFp2NC?WT`6m3 z;M4aPtXc=fG%;)mAcj}DiVaD3RmK>HH%j_uY6L21HDfR+t73*fL*CA`oTpfD^Az|m zKpU-r$C?iVlMB1@uA$AmX@YUnc5393;2TLa8;`=`u=r_(%UQ5~kk5wlR#q!R{B$Yy%ulBxUJwrt^M3u$HIH zy~r%SVC+^*{ms3vvdV}Xh#Mf#srKN1Fsr1#)F^Tz`bn5H9$gM09mt$Q%0EYVnmdUH`N7BT5d1WXD5+9Oq>`}Qu#yV@Bczl^o}p%xn@xV za5a_icC~c`K=G4jO{-Ii$84wV1j`|8G|B4xKYC%$0)j7lANn78DSbx0YM?@weuPD2 z_p64^QF>(u>TMfpv|teYao!g~ExBde`{E?>9g2k{RHC=Fv;+fr8GM)pW8f7)Pu}qc z;Xp`pnGe9hXh{pEh^0*JT>Oc5it_wR5U{K|Big!7z61oQ##PbjN`PU(Z2|}0^Q}6D z_$OX8tZxBBwo(2gSf~IH;^}jx5=alB^m84Xax7-dbXPzTP$8x$07;T%Ss|Iy@wjw3 z%4wSXj(G4Mfkpu%6cA1&JW{wjlJ`8F06H(5UY23bDEdSS+?g{1xk(ueUvfq@$uUZ!G z-hLRA>6!lrF8U8+c>C$m9W%e&ans{V8(cxe1xr;Kp)6(kvz(9yrZ7tka1uBrf+a!= z!IexriwKKO94VW`J(OZ_^Mj4Wg%I4Z*5L6X(NKDcgM*9pCMJJNp>fnU9rzZ}c!e8If@eF2iGu4KBJZ%fQq{HS&TrwuRLx4SSj1=>^cY_3 zWE5Du5J-hk_oTSEYjOc%$t7VdNc!B8Pz7}e{7d>2&P~e@M#L-80GOhoeq4%&hCn>@ z3x^Yg=}1b`u$euKxIKffv5WvGTne~S#|szkIz{YUpJEEb z8&H;91FHsU930^}vgtIn?;_tb&oHlv|At|b95R3$@5|4cQ3OwuDgwl3+vaY`kHMP0 zy}Ry9pg5coL=%ae4z@D`X0X;Knd&t#2i}Dwcuho*8)-Si(3*2|Y!LSf+B8fMpVnGX zfhEYO%{zd126a}Ous&2JNi;zjBZTY!DLl8C9?et6GFhq@RBYS_mrApVTH=sFAAT`A3!IP3QZtIMklYEQJP!F7E%(`>QIt3J>tIldhr3H3M&}{yR>q@tVYjOKu+ela@{Pi41{G40 zEec*=-wIFdOFufKFmamk%>=iSFANjmEJja;cNJfY^EMJPLKpP+_H3R32lia3!S*e& z#2rs3B$)%Au{R|4PAhroJSs z1okw9J4VG>9P$xD8kk97wkykH^J(1$ux*BVCk}>zgcY}2=^7bfk!9I|?X?V#EWFXk72a?va6aO`uyl^Sui=tCe zvM&?_QxvnxzIZF^Cw}j?Icm7P3}vBduFy%v%4Zr&0R?Rp_=vH@KHR)uhl0aoMXCe5 zUC>&ilff}+_yWWV_-W811yO*JuDWi`!AJp2l^L#B-m1{Kqv(DtaT@C$X@+SEAOjhC zmlCnZk@o>44G38BQi&P2J^g+V2Pd16UFwei#nNWgl^bqbjsJW}F%U0y`$VT4)*usc zSOJl9kq|elaLwGK-but6Tvg7ftYm>$T&H0|V<<##p~EWJSq*)`M`9_z9AIITLzhpR zETN3QQE*$aw|(imPd>JHYWsgXy8T}tn|Iwc*Zs=}cAPl((MLVMN-lwXWtI2N5cOq^ zBm4{WB_Ta{2vY4s2d!P_x1n$zh?6$?!I4+3RU%Aw^uU4PR~Dgf6=KP)Rzo!D^tgUA zszZE3?8f_@r>0nEZ0{*O9&!QzaW_leBAaI{p4oa92L!xtIV-yq#8VppWcnC;UPY8U zrPC#cF*b;qaVz9vHit%eB4H{?K3FO%Ap$9KO0-GA3YBO=OfC7D;7>6swi2Y^S*T8k z)M?$Y5$0Ga6CI}4jQ+ma{FClH!cA?Sohrh4KI9T%e%KqKL-a%ftwi(jhv8lF(6G&V zXd)J~b2Rt}VHm-roI)<88IuzPld<2*j&vnr( zN%ExBbm~rtbKNg#-8o!NebS9E=x4{n%Z#ulHS2KwW9fKv=2!HUflW4^B=wQ zWlODW(eW0k)vG&pD=ZTw&i}i)h#0F+eXDcv!j8eLPi7HPp-d9y3GTJe&cwE6imvlG zuNX(Uzm~iO!x&gLJ34Oui%(A6ldN=>>LF;jOoS$Vc^pU!y9rBl%_gpi{8m$@s@X&W zQk{VY7fQ5D`OGvC;{*gxBan*QQ|}Df0&zSmO$BM~~5VC~cI}^kD2Ht}ftgV}4G<`;dh$>6 zpjVNAsr%~}oGa|q1Vx|D1|FDE2LuiLP+ssJ`Gj_&>3S{<RO0<7>jCfwzBKGvwubf;GDs51yZ}Koq*tJ=J3vfF!oihp6?9aFJ}pVt z3Z4`;6{zDFRUj-a<7RV)O@LJ%3oQ+JuHSqP`l3lc&)^*1^o0ZSA9>CCbl~T`$9KGN z*SkFzZYBLGMCG|#ultu)qWh$tuF?g|_vN>}f*;s86XsmN)b| z?ILiOY7bRt8yjGqx;a*iAgvweaJDSh?Vsvs2KO|L-G2(v<*Kd|#Y1R_8N`F!4|)cv zr;beGK6D077;ynAHVl?Cs)_wS?v8re%-?lCLa-1q5qv5$2xV~*_=n;U#qBm^Q~Fo6 z2H`{wCJE-c*-9~kHX*kk;XJfm63Xo` zx_E)iweBfydx|!VPMMnZSIL=5*FlrHis3!$B42)o! z@BlXqv&IdS&cth-YtEnqw5G9D|3!(D);|qmj~`qGwEg#-frrXQ5+>o!lVpM$Yvi2_ z*V3l%Nr~r17pw4U4+@9@MS>u-A6C~pm?NhtK_afJT0t@)DS%Z~zhkgv-oVhbmEA&4 ztx_n!VlfEOvN3L#(>ue)1UuUbo;d|XvITQ7yGG)F#3qNQ*0D9oiuMM$ zj*%>voe~6*L^3lkG!t;>Ea@d+j0VUn08a}rkj>T$fcx^g<9iq3er*GOkK|K-6spY& zVJe5xjgOqRJW}hD@m6;_POc5IbiQZVIi#3WDPgfg%o8sYN=W4Aa?pbB91fWXWR#Ph zLqIZ+-j{jjd~)URnT}IaKJ+Enkj)gvoaH~%8p%_h;JJUmlRdyDV1pSb5mzLkGfGx5 zqwpgkljxrz1SFZr5>%zl-#Iipass5yXE?X{>=Yu3KI3fHVjT$fUVHxJWdhnHj@}ta z!0YG*WdJWB6mjn-1Hgr)58(0NasdMyRhU$!QG}Hut?&5j$113ieZK~?`IJK0^(qnQ z=Z;PEP+aa7Y;0hq3oEwavkhxR4YwdAX$N_dIPmHNgXRR&EDNcFy0<_IZa2v2^@hEgDC8>A62*PWCeGt{x^ByZLVft_ z<%-XJ|HkWgEqU(y&ym}E*K^N3_u}n67)@M?GTtrx{3IEqk^cBx!!%HOpg0(X>^367 zYGDSQTp$13P%Qk=Pj>;Qk_vj08{TB63;`~1A|xg|XVP0o8B9EER2oYejS#hg?2yPo zX`x4kEKD%THV$pr44B$5g;FGg-)WYk+3_A!ydlCkb)E}(99z2QB6*T@Cxudk3WIKK zw$6XE*hthAssUr6H?XSH>FOs-y+b2?`9>&=L)xSct?rkG#J$R!K&`}DqsTA!8nmwJ z(-ud!>dE-bMG4fJB)^~; zoZaqfEP3U%JEJ6SmQupqWv%s~`ly}x-dM+%iT}r}W*U=s$f&fa_1zup>@AmKmZZwm z*5ZBp`H6`}YKdaWXl+2R4!ClQ@w8Y->I# z=wP&DE4PwGp!opKKO<69ao~xa$WkQeBGhM)?2!?s1@}@9hH0W#y2&&DKDol>J|Rf# zCv=q2#s0tx^vuv@_t(>WN~0#<{0Y3G&I``^r|HO9u$DUAlnJf$qmwvJ+7cf%_|f(? z7r0QFC<5{-&!l%*fW}8HW5Z!e>E-_HePr*Ot{qG@vT6}M*1C$Ig|~=4r-ZUKTzuvd zYiPAWeX!8X{B4%gfw3b;CZWOJBw8E#;=ni=mWtM+!RmM1VuFqQRMCQ}P%5mPMI+NI zFys+e85W?h1A3Qqto-x)%rZKIG?-ZpPe}BmKDj5P8h@+j_$j%gwLj}2_tvi3Z`CWH zz~yEDGlnn{iP1#Y|K`)pFPXZ_lDtBt)>bo zfKo-EZB(b{?3cvTPA|$A`CwUmGgKqOyO|=Al1&EpGfj<9-GDrS_ha8R@h}UGtr64+ zE^d-(42Cwi52f9FFng`E2!J$nbs%N-I(`u& zf-n1ofh5JA>%O8-DrpaA7qZ&4=UfTNOrgvW)}Tf2P3O4WIQ$nJFiEwX+2NKZo%QH+JAtbKBHEneWQ#u zqDch}Kl{@c=6_*R&rNDn0MiR`$EHlmiFvE>KSqn2B`7AQddQYKU5kt+nfK9{vKM&S zm^;#Xbi7?TG3j+vXFsHgP{LUB8jAdP<|_OK+L6+b{n%MlI>${tv{TLO1$-C45=5c; z&*}E^Dxe6lQ@>v2kW>`A2%y3Y)r&?(Z!pradMF(438Qjoj)Qjs;3S^BcDt_LOl6iLg3G)vf*FepCVfm{{U#AiWJa&sk*Ld8PeBO1HP{d~H?^ig2sjYIeZ zT{)hP$K0M(gHau8fTi-_^6|hR@9Af`TArKJ!$nozjiOmH9JDg&SRyn_?_74LL%&Ql(q{I}FbyUBuZ*aTay9;`1|xvScTQ5MyJH z)|vWCt%Mzz>C9haV(fWIsHm?F!i^oDBnqYP6jlNO6DmFno-FnC2I=K~#*@@k5tFI< zq|ZXwh*0`ZST>aw6t#wm>~FE2nLd>QAsVbD;7ERNS#yQP6{~>R%!yqQT|$wwrvwu+ zW{exXGI1rikm6X#O*H<;&?H(20Wz(9BOjVUV6cCTJfrBUBm~6uq<0Ve(x6Fp7d)`m ztZ>4Df8-k=g(o$i#&}0nIaDY^f=YZCC+;LB1PqX8QrD>VgQ>?0p#{e@(cX=FlZIX7 zf0DjrvZ)7>2S+0nBe)qP4sQpZKE(44H-tZhVPE-U;;|qHhEw3u%p73)VEBdlbVkwr zpOn4uxDoJHEP-Zobh+%HG%5hb%3v%i$3Tw>iG?Snj0~i`$ zh+&o7bTgJvn5`rkZ{K>_GzZC%-IxZj6u_gl4@YWvjV)Gncl1-df}bp%jW?qfM|HzO zBeE}Wr-APqf7nHR#*;+A88*dQVqwuWASrI&*u6Oq{DfuOv_9Z%x(v^V68ku@fc>-o zieqUsPbzhX6AeJJQk5y{ADf(Zqa*$xPn4_*W?|wSREbgz*k&}>i!AgV&$-ij`2h%^ zc}&L-S7`*K6AH)u>6v1>f1KHK0jzxdrE`NzsV?_`Mp_9>(AE|zvHl~|$)wY)Vcv;R z)A$2o4Rl))WW7fvUo#6(FfBK{rhfqjJfcV-yYwFry^6btpfj*!l!Wg(dsP5bpMDqR zieL#GsBh1{tiWMoHHchwiXctqK`d%?&(6r_PwXL5WBd~rq@!7EQ|7`*#lX$Pypnf` zfd-=BIWYE6_IK&vu2+mP0gv!tAN|xmPH7WI0Ru3TTVB8W%(qMf9*IKu`J-(^Y#quZ z>(I~P2X|)sN^T|hWPPL`vl#R|=It4vXL;H-g;y*1lj*{m%2W|*&1>JhMtEhsU2z<6 zv_;?0OMJx57^@?GkVH!$Llv~y<%tvvT9jYwvO71% z4dG*}Fjq2*o=U+(3;|2}=3UgIDXy<(Tlx6Kgw3Wf6NGiJ)bhRLE?K@8r^z@r{p$Y! zSgGj60V>*J#958n-}W+jAu-I|cH84Xs_iILkk+x*F9gPbwlKJO2!@G&5LCqzM_-(x zkGa@z)O^%>-HZhaD|8AP7pki;DWD`#oWS5klQC9l8Vkiyz(Y`zaE&;=3RVfc0BoWN zuBzwxyl8$&pyE4J^nV?^mCMZq7#z9kx>Vr-j{%Mv#|!j&WW{i0suNxj^R=BKAdpf& z?8Ar2bAuVgq@jxnH^2-yyf5h_yu-7*aBMjVM0Jlz7cQJ2$#>%`Fj6#A zr+g}86A%=fQ$azF^D5A?PW0KM0-nNc6~rY(3g<}M3Z+$HU!Ph)kdL7njQRu0RlH^H zXj=kLD;QS+w1THH$_GyW&9UG3_PPK0@b>?>Y30j*w`0Y<+xYqa{uBuQ!i#t9+i}xv z*Zt@^OZA!-q_-uMg3G}ICpy)Zq9|y+#d;Wkng)A-Mi5lgEWo({zXFO`%nMIN?a&!01i%83v;ogy*b&3qk|e306^xc3h-=U&GC&RB3H8EyCS9e< zML7`MGjB@NplS&l&9VM=MgN0;=&2E;g(|$)U-QXG1ZjkXaTP4o*Hb=1H>z(}_9!mJEXlpDQYz)Jc z-C1nXNm|P1Ls~@@k~@ctIW%QpqZOb6snmC*?8H*z7awIK{-cFe{eq6)Ja()u?Iz4=*;i*Q>%-}Yx?}clo;1uCI8lVe{NOX#TGpL|m{(hZl@=|^ z9MP}EttQUQ+!o8p=dNL9Sz}5fMnnm)1}F$Z6}EkUfFcB9;NL`)5&(meZ3dOK5};$u z+(9w*NQb1;X(bpRlH=q%aYf+nr{7ea<+W@iwM*@dO4>z6mDSa@7 za>I=2?Bd%gWOcv%)zb9qIfy1WoBo1JpeOijNAD{#Zy(^Oi@WQ_jriqq9k80y8RV)1 z?47OXsD~gu1lnpa(}tDxz+S;*fzBk_Akua@#POJ1-EvXpn$(#EO2JQw0_zqRPz|fX zWdnf=r$&I(;55zAovpLbjL&Rg4x#Db+01cItF^h=1zDDQHM#+)6Cwx?fh;ik8r_d4 z9P9yMTp<}W>aj!wQBrMJxE)^lltnR-_vqSN#2-49!8#cyFo;oiDVZl97$Gfu2Z*VS ztO87keHilJl8DLN54YfEEjw$CHDh6V+l*F0RBwkjO!+(iecSuNq(_`&*>%|g_ z0X=Ad$~qZK$IfCax^Dw!QeoThNmake63$-_o5^zy_vB;bv2hunMDjj5Oh=B~p=Y&} zPT(%q-4z(qu+EZ_HpIChkirG9_?c`}*wz*4LcX>#fAaHUj!|4KjX|a zN0U`$J}SFQ;TO>Bt8q|9o#TN*e#vE8;65Ts&XE6oq^E>^di-Tm3#_J6%1%z)$&Z~o z;Yy2fmMLoKdK zkGc-sRROcYg@i?$i-uJ8jh+h@)a!G7n6ADZfTX>y@JQuTb3)+sUdo#Wea(M_KzU99 zBUOQNq+ocrbfH+{3MTsh348nCF3tY^Ev4BhlSz{rguq!N3xyHe9?u-h#>viXoZZ#InDmU}H8{L1GagKL z=^4h81e4A&lRr*!hRpad$L4%K-~0IqVY_=IA%6P3JTLciKiBoWzt{J=uH?FT*kuH2 z5GV(oesrFdC-u09&H)fq7CdtkVyO<@)2T4c4MbC&;GlHk-K>xArIoDSbVZb)o{=V(>tvND@OSpDk(M;r{}zm_mgF@Mz_)n z5_HWF=gpHr0$>^-lh%jjAAAyqTuY-F;8p@PZ9ot*CChv<1%^Zy6zeT&S$Jq}w_)!& zOdzrxkxp*vvVy}nVM`LnxSPlrQgQBkSIGzGY<36S_FGuwL>S9$9Hk2Xzjn2v4X!9r zk!b(23Ss2ZP*&`~mH?em93WEp?jQgRL8Bk3zg9KQ>n#0`VnJErSGnSPY1|~+EI%n2 zq-n>TT^d;#>jf{x5Mbu0tBX0QD!E~LaBk&cl1Y__!O`ywX8pG~nuBsqH-5v@!K<|GVAYPksKnw>_u8^xgBU640kxt6RO9O7 z83attJ!qQ7VI%f6Qe41Fga)WuY(%fj3Z>XNs}BKsXCesrOBGD5b32a#eb5n6m@%1g zh*~Yg0N5zi0w&vV+1(>#q>VI_w_{WVw{%8WlMV}y)VE6ol1DZ?@_l1_$eQStTsK!( zqGseO!)E#yOK6Hu2WX@;PPwR@^tULh4$i=Dj4!nx1z;sb8=-f|=w!fmk>-hCl_H9nEL^1G-2<75SUfRo6-}0@N-Z8vmVlCpV{D%Qt26vD zP9J={CX7JQzm*!Mgrz_}TrZ^mbR4`w(WIhGV%2YGRW75MR)x;6WH!w;z#S6*x}tOE?zWFjL;a#R`DNwh!-I?0_w*ov z17zQ^{*Lu@wD`QGmu3)^Hx?^6+JMR;Q$iS^`UOLi*j*}Jq^JNUhOA#+Z{=p|5RU065;L}sB+qVf*FY;?U zN@~h_=iX;paBGW{T5NvAQ4*;G+3_JhxT;^9lMP&37|_s0pbqhaH(vGQZJC)xQkqv` z3xa7gGUzd*{aTX3VHZ~&5w|^fhT-43m3m2!yT_8_SE(qoL8e4)VJ5aN7#fU(EvyF! zim+FjV8V~YQlXYiqR7Em6re&zq%xpxBoa$BPSfEss))L;QLtA@+o}*mh?uw`c3`6> zUzy&6p=3#>lEB28t4RG^)P1Xu)K)c<%=vd*OL)bY532eP4^hGP z=TME$a;bq&qsvdsCq##8^7fAwrMyv#v4m4}hyfi{uxsx-Pj^c)SSaA(5?q^g3YU0^ zrY31{0{V%2q)){?n0FMFYG$(Cnk=s6rDJ1tWVRUJ@NC z=H`*?QK+OF@W<@8ypQVK)#vlX4|#!?wm~S}b8j^eoM-Ke!vNORzJen&LaKr{{*hsF--4m3YoOocpg~NS(4wWvLYBmZl}JMy%*?rFd0r5jul&ioZ?&A1^sjprs_w5z^_g z=F)0gHEG+xMQ2K{ioye@c1S#+{k{=^u>Pz9Rh9@TT|rnY;~;H2Y34mQ{JWqdv>%u! z%+bLjg6%$81U3N)cJCr~iMS4_FP|($1;KUMQ z5GC`~^T&K)8uSS}HdDJvRAk_v*vz|)1{XC>7^S3tYi~^5%Szfm;3< zM8tHx)lhnWPPNA4NCU7q=RhiM2n`OF8Hoh4ErUO;?f5rNfbbSZ$r{}OLCBdfwjMlt zaA_~DJCwLwd=KL!RSH=)W=7l5V&+!lXt zXoT`OX(~C03UcXX?RR9Way+3%*$90axTD_BUEf877FrV@YN_u1~e=N z8?D~JugBur{(cgVV{8ElZGZdpwgxDmNYP4MCktwWemcA@7n4Uv_zK=`vn_N@mS$?w z8}8*vOS*Hi`U8HN-`(0+9#P2Jai_{ROb-9|F-JxwzHiy0LLQB*!R6i33(Z2>f-{2H z*o6x}1(LbNhUuxnORTWkwT97#^Q9(X*p;z4{Q zTcN2#{g;09(zHliN+F$zy55U|qZWlttM^E3fr&2fgFONhnHnDb+sOUQF4&_v-$UO2 zKgbo}5SgW6Ym!3p>~T0qYg9Np=SSWm;5tK+32L(ORDMzXH?%cWDK1 zS9lrul8(Rx%Qlu=WU54? zi(~<2t7Pn$j`m#H0XJpg5^j1fM?3b!&@sC7P(t1po}Yp0f~K=87GiX(CVST#C~uI0 z)M8OoOIs~FrSvEh@hBp zPP_?P%|Ryd^jKOSFcpglWtmACOWwfW{9f}lB(fNpH!y`;fBMGne?$Fd7qz&Mg*IVz zmY?r9VNxVt#8#7hJHtgz`^xe_GN7Vb-U}2klmZUA^Yv4E?!tM$Eqyb5*XYnCVs3=s z2yJ~9d9%^7{VyCFk|miDZ7bXbAOU>v*ZN$rO#x51W#Cecvmg^LvT*Xv*}wi5fTGi1 z5@#LI6EcPaPsMQBf!qKgJN;0>2n7>(5(QxeGE@kmupd$~g^$U*YDl4+ea`s94-KAG z>hItra8ON|Br2L5v%XRnH3K654on&}wNHM_Fl2&p05$@bM4BY% zkfBlI6}BnBuOOQWm{ho=K%#QR?pgSw|BFL+$k&yZD-a}Bn&yGRQB@<6p(rd|!HA-> ze6*m`nY(423Q*-L3S!IYiCV>6e!K!?6=cf3r6;>KTSMUsFprlvl18wzqoL!ZF3Qu4+X1hRw{}JyO2S^cCIfYfqlB^>}ao%N$!Uai`U7poe^?iO1k&6V6}{Hq83^#l|kkc z#Tt3D6RF9~I}NyU6PH2&gw6Kg2PFYaljVHQyN36XcY0qK8O_yUoZ&U}jV}#RfOZ$` ze-_Kut;3J(ux?>MkUmDaR6oBbH(fi3$mMJiob*TfK++$UiIpv<6Y?C_$3oS3B4{!8 zR2ZPP8oHwgG3u1G2B(dMl({^>Wy#{aeeX;qb9&#&H-sjg)=o}q=sov z!Y2c!24>Ud4&ddev_w5g-~;du#LiA#9=j}$wFle9O+?jWnJowwpaaWYE{*&WBMv1& zfq3Z1;eu1N^$5hNO9Oi+P%;qg1>)Lt?{iXslA=swpXO=}buO90dqu;(W05O<$Qq{} z(}j!G{I1dyCLhI)3BPg--B6gkvT~MfXHSpQJ7w(Gh#8DL!B7N}1gD5ue8 zkPuYpspd3MpCm5AoD~^`H?B)5Ry z3^0IitNEx7HjZ`VDBB-n8Z|82GJX}wyB*~rlHdfEolh~PXQ^g-s=%G!EJ6M2*PnQo z&?EzBWR1dFf3yx<QOfe#U+1qe820;j1PU^unK zOTrI9cK$CROEV0JnlpPQTh1Es?o|Z|ga}*7S&S2!rNk)@ur6YFHsrW7l*+*(1!uX1 z@{tvn=v2Ce&w=2xG;*EAO?870R(nhJij8gS4Zi`ia3VQ>mdp;QV<@4T_lIrH9$ z!WHL^$JY6v?+LKVmoWpB$=Ke<%_J`6t|%sfKXV%FASlU`V3k(R4GP+q<)Aqim_`8I z$}8~olx0d1FZRSwGX^_?vfekO89CXLfOnio538q<<&+CJWlF#%l#h>goanR~i30Bf zCqMt@VArpHd~ zt8TDrw5%H;OQi}z#joc_4Rg85J165mB$3KSxR#cMLLS#tK-ao zO8^0CW*&1$ouHv|oQ#=RCFmurkc`tb+VR!$K4IGo9Fz?MQ^T``XbO0$Mug9Zh60BK zUTmLNW*m%H&=D1Yxs?x(Bw@!Nj4^~B+&a+TI+Edb|MoqPHeLR+r-BB7lBRwM+Nt`<-faTM-e@eq}54i+&p z_i&ccWgoV>!ji})kSt9C5Rn2nZ}=mFD|e6T4F;~zEt0hM@W?!y*c>N@UyCONTIFiUB!N zSK<<%0SX)M)omnTWY=ENRiAT=_fA^kktwLRIWnyc^IE*;8XLK&cCHiPEf(vOlLJK+reM~41eZ!QY997tSVBhRr=#Bd!r;a>w z`uA4$WRkISnwN$1O59{zr+AQe8#Vz;BkN{j0S!H8-)10bWzZAP6sinH&DlQ{j%V%F z3l}ap9pqBc)B&H#TW{c!3(iVN1z-+C%j|%l{3g5*seiokipg34JEj}~AR>(hNu<*9 zcHkjgt+kd&j%SLYRpK)*;tU5kKrTf?KY_0kbnPNOi+qTzao10UPO2mLMtP+J$J5^A zQ@RWFA4KVrzG3Y4L8r0X0oo=oXHkH z0hlzXU(2i4YA_6@Q;2OOmki36U}zF8Hm)Kl$0z}U&U8UR6PWGoDmUms=xnb_>%o=W zb#pEKwq(mK0pdS#)dw7tHDb%MPqK1rj#yvGl^>BcuL7tVR)xPNGVT)@?Za*FFx(rs zQZIqI8v1QG%%TsV(TwSvMJlUG0`L;+%q%~Vvq^fj8lnfo^Cbkc(zHNFMffLw``)dyIQQic= zT0eBDLB1qX8)QmCq_tmo9D;$bojc##iF_GFH(#(1q5+)d+l5prKuvdCks%guDo$HS zq61zVw_f-P-&HX(d!D(!3cw|JB+Had5Us+U7rYQ!rRLlV1C!Ij(Tnl?%K65T&v5b~ zgldUyCG3_$+-Q-0IJhS@1b}8t62w`I_}fRr@|)X7;BeDkQqv>Y_57SL(FH(C<>XTF;dRD zy27HU0)q-fE<;w081D%JD&ajpQHIRtrH}%O%Ghy*a>XiYl1=8_fn)N74kN=NmZYiM z>z`fvcoRbPpMko`1>CzF!3B)*`ia_0v0ZY42-SUr5p+wJz2{4dvAkGF5&KiciEoPt4NS4q`jwK0e*jbi=WUFDZ zeu7y7$F*Ag#PnkA5GW6ViMId)4ceJDjvR(hJfV5x83J}}0y_Vw1*loN+j8TZkD2?c ztHb7lV?V%+o+bm?vz*hifB7kTRa%}~4dwj#bbuQr&4ipcp8IFQZg4hoDY>To)>J0G zLIHP34izbPthY((!J;c6CV%4s_O4!qT<-&*&zKKDkA5i2N>eO{k4BZF<24P8TML%Vvk)q2K%( z<-i?or1t#PaJv00h)7C`+&j=aj<GSAPOd6yhP$+cjk@fIkN(y|1HX=w`j z2r{9ZT2suT(2-zwNtDJqQtNq;>Bey6((B=b+UF@n@9G|e#Dod)r4AdVJ59-Cl5lUt zb`jigjJ%P1h97v2R}3X`7SaoN%~IeMct_1ch_>@f(02ng08&y5uSRp|?F zW4hAmp~Hj!I<>gD+C$VEJVE6NB5%}RLV?YEn2&S(*ae#KGsskck^=Ahh{z(>{uRr$ z3OE(iL{J9WO=9VmULYBq5*1D8eA)XVq_iGZ{Riz)L;x!?jKi=uDs{9k}@ zV3JTxN1CxS!ow;b_2|)7DVI#6pj#o-(JuoqyiNnv@+c`NTQX^RGC3Gr%?PBp-x~TU z{FK_7-nN?$ak%q$0rgzAIY~+}idw01?r;{6SS4(2%?e{~qwP+U99qM$>NL%zz4x_W zn=r!hu?`YDS&kLUNdbxp>yH>e^C)uyoDoLE+LIGU?Gxwf?We%@_p|V`DPHNVET6{q z%;a_B=d)yG{z!4X&q9^~_~4l=sZMEqs3Jjq$|2gq;dN&GY&iKpl62q95y%n2NGx3n zr}XV-W}mXC;>rC><~>TgxZ-j3`|}CFU2fp1Pu{bT64T)%Bu>BsGr!`8g9d~wNQa;& zEG=0;0w}^~-G4sB(+PN4`m)5+bkgSB2T|ytL(EXTh8$O@!eVbTpl!Z_bHRm-xMit2b}nOx05A@-yYD#s-Lu!5?zrun-88 z)VaW#mTyCEDhcNQAysuG5o9V@xY5l?&OCpr>_Ns%#K6V0>AObJXHZSE4v{&6ohNNn z{{q4y(SCf=dbQ@LG`OmdhRNJJ8j6F(tS`=@*_9Q#*}Kb@dG`ZA0DQ*)$*=}k`jdc< z1(?OckixP!7h_q;+vz?3YqH*6{MIkNb%kIR7J>U8DPV@!^+$b(E56NOKzMcOztZSY~1x-v_; zIkfzy;)0xZK1s8FL< zQ_mYS87Cyo#RFg%#T7UYB-k<_`#4L)PW3cT0aNqa$^+;mfVZl<%g^mk5^n24sYHeV z<8iAm^IH^IPw$iFY69q-%5F$8l5EkD?A_w1N4-v*UB< z@70G2YS#lJvJT+&DfvqSX53ye~I|m;T9ZMC;+C=8D1E9klI|mjkKTr zH%wWgY3i$-_7)*bdCnY4;hnamNEgJDVzwtU;%bOO~wqNtC^2PC_TSz`*fYdB+1rL$j?|N_1iI=MGpB7pGDi;E8KkY? zD!4fDe)J`(TX1^i?u9+#glOr=t;{K=#>2UZ$@xmaI23OLh^B%M`7GN2p>%V*di@#R zEd`pj^iA<1Ptq)s%JJe;+U0PAu9+^C23(?s`t_I6Vom*5x2vNy5=O*Ojc-v));Xp5 zD>Kxao=d@*1e$q^GoLZ1X-dw70|&t;F;PsaOpuO3z=XU7Pq6SQx{3C|(g+NlCY`?~ zS~pkuA30^uq>rL)EW0o_S+0rLnDwG#rjR%%asX#a-}&==m`;dWZsyYk0G1%p5Z2f zL2AAZ;9;8Edc5Y-2xk_)*+FT+%0ucA)#_5y8v*z6J@?8JJ8k0|K1Gld-evG|R8bWUo4xQyVua$V z!3h!5SjQkyva&<|sysFaE!L`lMqz2D;YW$9)joFvCxMmtcn}k!qu9?Dw}ILJ4#0h)WhbctRO8(E>?i)_m%xT- zVEEM<`CetLP?x0IpmJ`7n&hA)$F2MYNaC+fyd!J7kp{$ro!FvO!cz}km$ADT$ zWLC~BT)CYe6r8N?-_OliEKKs7cL+GlQSNN~tKKP5Q#ZN^BYPXBuym*_OC?|ITfMpu zEuLFDTBo1pz+DvY7&yxUaRN6t{)mjgkby5YIIiCEr-PPvEK5l;gI)kE#|w?6TXp@&U~o>{80=NN!vw65CB}Xr-rNtK=Bg1 z+A<7P{jOX7#=bCiqVS}%Jj5>3w3igMHx+K9NimE@?swc)389pQe)aplmgk386uFv{ zf_W1q5_UW?e+fv&gHQ^qbuRP>t#sK{%=~H|;mehZl<6|(5~If=8R@i6e$f`fTFqb5 z;J2HoM$8WCfQyB{DCI0u%nC#Ovhc`R0#PbYqVz`+s9)!~@-aUFZ^(z4IYanLT}tJ> zsqBvV_mHN4GWh(d=;Zyr*Kkw`=IMY{HkB+S5EZ*hj>2Z$3x{p2@Y1=4)vb%9K2oP# zU3an`cn%&_wN->u=665&00;Mg@$B0edYcQ|H1D>kdGY?JWz047nktrZQp1jlc98B; z(-1F5*zy8*1V9gD13X?!zd)c81uK)V!0jQKu^73n=c{fAqDdw|#;B1l|SHDR$uFpoz{~`7|v2+I%@Ze1cOpzCg z3~Ap6CewiBlN2RUeoX>c zql7|BB!Czj7Fu}kR`)N;!$^}kVaPfzrR`97FUXu@35)q4GmI`wHRHl`m}3eGOSC?h zLevYz8f&vf6+my_buJM%a^N+q9VKdD3l=OcW@x0swH$lI zvYDDT3x1`6cA>+TaLn1UlUJzbd$JrOp|30&vK9j}nc@dYq%@1_8ZvIpW2q`2^^S$) zBv=d@NAvJ7dnKq!NDlQ?sTINPCt9qQUQ{uS95QJQyr1L%?JK>OT*_rLNGCBlZm+UM z=)-AD$@sp{Bkrlsvi_n_DMU3hp?oyP$Gk+QVaqE zow@Xcifp_vFFmfn2tR30kH8us0MGJ)yuCa}5hV%{gxx63KMKcTUGPFfMFb6u_~oBw zAH|sm234qF+UJKuiAM*!6ijh^nn#+E_m||g8cw1#{D6_rCMSkw)Qe9JU>cJT5iZ-4#Nto1|3Pt9i@DNwp)?Zc!plDsr??IvN&k7iL{ z9Agz48{hAOK84*14bF|%(?eGBZ05SDN(UR}Z$bt;(y(c~0p3(9#hM-jL4ZEEz_M<0 z>zEXJoMZmWT5in-@xft2X~}ylt2``2YyDI!?cq{{5FdfR+Qfofvj&}FOKtT(u4^D$ zFdA64eqm3X8xPvzTv4w8C$><;G7EfoVIxhLKkjTESH#Ep3qBBvoQEtjtd7Am`eL1M zLng9W3VD-Ow+1iFo5^9rjh`V|to6wr#>O60pt*$ITvNuS>0B|&9$b`6Iq!{&U!1F` ziXl|u6*>&Jmoz{fkG9h@J*>6SZFtZuzO+xRk3EL=L3aBZ%TfBCf_o@}Ly%?UCuj}; zcZOJwDqrHwmj2f&+ z9-uZ zHA-BR%j|yVN`X>(BDxN`88Y1>_zps9$8w9pdBM>>CqZKGHhwM~!;?@;huP@lJfFGu zhz<;;PC<$sP4xqy$Rn{S2t}esd`bn&Gpzg1B3V;$F8|c9rGY$eW#NO|6=$V0!DV!L z9=mnd7D={!0(8(}grb)&% zI&NHYdy@X49k~O}IKw$(s7a_-Dsyv)1mKu_96hsD;O9KS(Sf5CrB4v|rH;VYsTz+0 z9DF4z=bRfT%!<3`{WyN3bd*A$9H#Ok69+T{d5BJT7aZg$+BY(nmMuF$o8>t-nH_Bb z{`y!unGrkwDe5yM{K<^@`0SKp*nTz=QiXMqb z{O18S%-zrUo93ppdX-}ogX4lpw3U&&dJMH1nn4#)lWiNqxm~MZLp4=caOk2TZQ;9tv-abMXdYY> zRm6>uEj$R2E!}YS*n$h6^=s&d6MNo7tYUW%C%yc1$fD6;6;Lx2_d@}YwY&?y(sp%h>WcKKo- z91Rp;HRb|b-+eP$-1c5IZWX~)Q9>E52$C{b1-6yX*HD`mQh@4S@_Hcl^al%A3nCU_ z?QUC2#@mW7kTASd=OX~(U>0=Xd zTGXs$TlLrI-zbH|0>=`I<+UKCyvx7Mt|t^<)>VrPEbux8_Gm@JV=T^;(sNUM{tqq@ z8DV7vK%?7ix-nHed=EC&Q*CIA-%mlsXtK_PP{IkDXC7hY=#xQs^aS14*72vJjN0|>V>fhxenit>56Be)R?FNRYwWCW@N zZqZWLDAH}FGPB-?eN%lKp6F9#x_K4`u2}J|h|HDZpP3r^F^L7kB!VTl2A+}}l6@is z1yMthN5InXIJ}BDp07T*K zz!_-0h|qvxYuCyL?>`ZV*sFzFq(jCE`it#fS9Br4Bc);tR}0OXS; ztzXcLgkgj`l+xx>j6B#!bgo7kg^A?(;4Bvc;7=59bBQNE1d$Xd?kF=4m3Cthdaa;l z9SA@!b(Y#OY8e2CUE@ug;i-hINiboyB-%Es;1q7rpZaD_9C@@09n{evn7MGiF5gTQ zK-wP_B8u-U8A6?M)tHan^~!Ux<)5Lfk&QuKa~L_H9i8U}W+|v6B&kM^g8yD>&W+T!C*|TU zUVDX1N;yut>T+0oj=|vbTd5e8x4PA>eV^eS*C$|Nv3>iB*#Y?JK@9DG{^1bQr-vFx zX$jDJi5%OH#N1}kDV0;z|M7)C*C8&s{T5WpkOX^P*clH=Q6-~nK{dF0B1H2RK5Ur) zyeMiWxuOc#K%A839Vmr(0l%MM5dOWyn$q|r&Yb{Eo~1H%MIpgI6{KYj-VONUrVD5j zE7Y)%9^4w;;wo_ih(R2OP|E0$+r+)=^$kT;+=_oy2u+Mr`F^l}-+&90 zA=`OHjD5=7|8H}4oC^H^pt3jU<0u?Rga}1Xw8BA5E zqw+`fnCGXr8&)Px7QcSl9rx> zprRBsGrI{rZ|L7{ZH=AUuR(?K6TYK8?#Bd(b6Q zDW}lGJ5Vfz29Rpnh%niNXZ>$cXOS4apL`}-hpEAuWvS+R0*$QdZ(faJX|A77HnvH* z#!2!*rY;i1m6Ogw42&C0xU2C92qZD=(wCa=`@Iy6nGbP)2`>f0))DvN5$BF zz0~Pxq2cMn$I0);b<2-?%}w*4f8bIwnpp7n)E~czXyUXochEZ_>fEvg*PZa%7}x0_ zs%^B~Y(VK#VlolJqh>2@mz6F7qTuI}B_L7P@O(r}WuoiPOc_wG1VB*y=W-lHhwT&2<7GxkA2K50(Pn4gJt1&~MwOS}lF{7zNHZ zzh*X{Z)NT%j>94P5%S2XwF3y`hFvn{K7p2=6bx?bmAQ0Fe)ZRxePeifLUsz;S5QUU zWF`oN=7z=}TOSADghc5KkQ_Q;{pfe1eCxN#eAgW_C?{*WUH^<|6TyzLRO(hxJ>X69 z>EN06LQ4?+|HE&*V--c_BorlH7U4IG764>v6{l1wio3-B)hc2=Fj6k(K)8u@ijrGx zDd#VH#xVozxlC)g)B%{7Qt-0Mgmc-BVNN!%2yD+a1Eo8` zEc>MpPC&iArfbS9YR}|hRl`_TKLS&>!_6K+P#;-wbFnPiA^W0CDX2EcxN&@t^GPFa z@;_GzN^WyOxW+ZWxw$O}NO>FK?x+DJwjD!G!3KJqVkXoG9Ghaw5K|1R88V)W5z16= z;`pkjNnjxOBC#Al9s)EuQK*3sIsvV34$Z0IH zE6hzebB8URy$uJq?@v$N9s;adoCCuMT^ld!beKX|qB-j+U^;4Kw=|~w7zm~ByNP!S z!65dCmpu2u1q!@sLEpAP2b*o|I#;=d8?^LfGu_bxCo@~95vNHef8xn0bF0b^m{W#4&cN$ zYAxcMbc*@Lv*41;P*0_@Qh|^GNkNIz;jbAe z&%v8edi=F>Yh_O?aqQeiJpT`FIhiydGH%eKc4)Jzah2NFK&;|uoMW~ zkwFTbbh!jdtRG;v%VN1%%b~)XmCCuK#i{9bZ7ea+iBhjF)p1zz@IG7=WRjLcqm`0y zm?~B^z@90z27Pu*BBsQRJez8Avx#S*PQsGl)mSleZzd1eWzbPpIg3ih6!)=-v|2B2 zV8Ou@|9e9VDzbI07yfJlBRqis!yWJ`DlQi*6=T3%oM-*r$ZH50#U51vZ=!D39M*gl zposy3(qH+`vLChfKCzid9^aDv)V0AfNM<7_ZvKY=Xg+|%L`rHoI~;)@MIobNL4TeIBFh+y{SSQ`TM2|>8|oC0ZatMz)A zEST~rFag?gZb+J^hmClkPBeYgk_`F39@{sM1*32Y$vXm;(jZmngx^!=q_4YUz0FM! z=LRbgRUrWPh8+W44$tW#mQqSe9Arl^w>2%0%{|gKdF6KWY#<_dsMQ;8VW24)vAMt{ ziuEz%vU+8Ddn;B^9RbyikKAZcr{;j}ud4NZ#q6PhP&vU>po*z)6vb&WKInWzS$8cDH zK1=(A%$O-+p(lhvB*}AM8(gwrZpPf$vheqI@ zng`BY9J9O*uGA*>xl!m0x6ABthSQ?i^hAC3(&@p&LnGg@E*A)VgqYldHwXK{OAlX$ zQrbYWWa*ScN}OZ=p&w{r3us8rM7TsQ9UBTHG;A!zojz|&Cu4Gfibm^7dA9&cX9tnG z;l}47a6y{7`7l9(ALIf7Kw=2vY=+OdCj7vm0n29@gYveuQ6$tF{QHJ8-@?l<* z6z8=AHDfF2$g5D2n-XKyR>{FRE#qP5S;#Ee($#x$RU({}uzHyrvK0G;>&wZ*GK!f@ z(6?w4$KX^H>kx4#Aidcgurpua!9}h(Z>Ltj0lb{UB14G=C?V3oj970P!#T|?N6*Y* z1Q@5&%_hHfHmW+cX|#QbCmA83 zKG<>R>$LGGVLEDMN5_Q1dnwVq!dyhu!-nF~rGG5NSgSUP%LPgBSN2y>O~F{=A92Hb z1!t*!dS=O5(2`%cbRbiuAFn_J`X|aBfJK5c%4cDP0tm9Zyz(J<6ku3`1vgM*?U~57 zE!};H+DimBg;F+rGvZdUN?;D;+`s$Z1X4h$AKL1yAf^yd|FoDh4C9oD90_uTZZe9= zAU#ezj-jz3+TJDif;1xN;+g+lFv4inaBS^}W*lSCQb9b7P4$->;DYTBAGB^7l~#xd zP!+1EASJH|;~_%I@Yetum)=nWJb^zeo=zm2_vPgLV8LD$Dl3Oo%^7g=-tM6WGT<&X z5O5q^u=i)r37}7&_-sPFpZKWNI)7chLhCAUqPk+0zT1Q2^hV>p!!||)^N4- zhsiUm`jPC&pesP?K>&V!1kygMKEfhVm=HEf>Inu3`#OH)*I1xbdg0{GBP{5ngs6-N z-3Z_e4;`jXv<#Ic4#Y7 zW(Z$EyiN0M`g5w2lFtvWq&CACN+m$eA}BVUS}t|l=C%DCWXUTdK4l>*I4PKf-HDj= zg}ba*r0gO(^MfJ-D6K$Bukz96?Rpj?v)MIqQS+5py;~0**AZH;CPYarN-_x%H%W!0 zfq_l|7|>JV)2sxTJ*)O~Q=W`IAWT16vHeTR@NpegNB!+_R6d`6_Nzdl|7t=6`!wvB zM0bM{xV&(V1tv*W^bU;Sa)}wj>(1!{Ce86#Fd$omVwn7j5!?5}S>$So*wtop@8+hg zYMs*{@b94eaL+)VK8oS!<)2{Ua4|oas{rZTXCPU}CSPm=EE$-wG?kF0AFUI;ay)h3 z&if2-8(0E$E07yAlzWO|qn2c+1MVUod=;HHoB%qMeHP6be7 zhBQeUiR&oXTbd<}xAtL5Vxz!L4-(1_x0Glq7mam@7r%gTH?yi=&O76o(jKCcNxnH` z=_R2y1F)SiG$Zz>6!dJfxKQ)JHg*)`bM4UK>*9y%r&?+O0lifNKd32}PDxJnR3$CJ zR&?fznNlQRLJ8**`v&eE*h%i8p4Ak6Gh1dpEfXTf!rsNy#QfNqC=q_hqTxoCvbnh& z6s;Wp#5092I(pP+a)1{vPNj!pmm@XE%!qm8Wcx&VbdBU~a@N!)QI-_O(&d&GiMe1w zij?yrpi5UAV6?-@g1hmd5|7>gmrYrN32P5Ly}XM~4+zqZA3)VjaW`^fF(N#>i+XM~ ze^)_K$*{g#rtf`?KVNGr8JetOjT($Ciu0ZuJu8AM_wdoP9 zhLjg_c|J6q0A_c-zUWlP$nSg5avLS#ygg-&jA#+Q#t*WjQ>n6pBBcaWMUUd@OB}SkO@MbVi@ox-sBt)&C<18EOa0W;MAXl z^Lns%YUNpsm+MC$8}CHyv%9qiYGVyR2BWii;Eui4M;Q823x2yuGc2^|!VfUT0WX@` zNbht#wmR=K1eyX27@LD+>_C2lZs?v4VN{vW+JV(|&c^Dz#Da{SoANhz@(7Rt$_6wS z6I=|Hoyf@OcER^bU=|C!)(s3`$xw;aPKk1I;ZUDq=rDSoHRyuGZSkw6aOhxS9d10> zaLjN+{j48vZ(`B@*^gcCzn{+|c&0}!hzA^GC)CDedzQl|o@zUI4w%Hn$E;gD$)eo- zCMH{hdEY(|(ryqF)Z&@uo-^1-u8>?>>gX8@I35e^dE!ODwRZEt68hPFI{;>EeAq}tgDc zC+H6V3jQhOY|7$<+!@lOY<%AD{VyPmt71-^8MxWMRk1ByOozeO<&`l!EQ z%& z>Ky46_?OSFfKd9&Ch96BpkjpODGD+JzoDtD~JUdUOlsM>;|7F@?tf zk4%wSA!!4zA3#9#)E_03`};I`sd3qp7zkPh5@a%kLb#A<_;YU|=1%XUASQ0*DJB_g&*xPMk=a#)zR7b21DWWvm7ZO<*aj9d^ynHgOm8xA`>sqmr2d zWczeeS5JkQ1cOvz=U$_i5KF3_-s5wvJ|cfl!6XT$grHO9g)w<(g)~#DZ?aES2XFM&4%7Vp4*mD;GJGH-}hE*-hy$(_wPiDi>)4?t#5Mt5{W%f?EVj6`H8| zo0ca@Osy>-BeE-U#xc#hbyHXYN{hAM%vJOQ?&K z4apvb;n4#ZEEF`9r1<@_sbgs%&DdMMgh-MVx>aZ(NC_Ojo)SJtdGGHcCGH<*@owMs!>;?j)DB!bHn@OTo8dLV;3a~ONgd?;*1gTPR#Z5U z%)T4P9h?<_!lMe<6{;tor3S+|odS<>v321H~l@u%Up|^al&^QzLq+lVzY}e8jq9 z&1n>si(Ph^r+G)UOE+GzPB-vWMy9-jaBJP(cUFx4tbXZIwA-)GqN>C%2kS@Qfj|23 zEW&Eh|CM`U#&5#QJz;3Uh1ZkPEo3)vn=6j19L>)#cf& zv9RPD5qBp$qm&%UY8@IOWnq%NCOslbB3i_6R;#StDAy+Kf)-JmlzT~Yprqm?G=)+~dD%qWmlGk!{EM+P0I}`-A10aqx!>_8gjymUeqW<7 z@R)dLqW_NDdoN(A022ri0B?s$D&qeyb|`Sb#c$XKk-yJ==>sfq548OKyJRIj*Nudd z)vYE9ff8WP%H&B^>ZzZiV6He0mk}sAJm7K{;)D%erf>a#<(p`Wfq)3BbNzZe{}z*# z0D0i7SYwW~<=}&Yq@yEbA8jpBkmUPgX6203Xv^g@3R}TJM_!(!&m}g=?C+yij{7OD z%qmwzOvh;CzN(4!`KV1sM*ab+r@u-6!xDablXO7hst1k}VcXG*Zkw-fA!U((z-4ga z2G1l3@aidfVswbuPtWRtrI|>}ly2{vZK_MY;vlV%9PPIv_A@d7yIY6(XH6gmIG{N` zNmSzuFt$L|Ze03A%WQQ+w{*T>oR(M>Lt05l2m?^0E0ap}++bn=u#B3;to5x-AwEGW zTLB}Gf(Rj_L%~xjM~<6(EvmkF5xbxGQ!2oaLjSv#-RDHp zLsdc-w<09TVn!ghV0{sH0kbDf4-ksc?}uG++}=<)-1u6MK4|~t$wNn7R@`O{M$;gS z>mijwH#6{y#7Dm=B?-$IsSD@H?P3WD^)W!fUsqH|smT43g~lQ}X7RxgL5|^ihtXwk zxMVrn#XuQiXm)+~dpmRKz$K_fdYvvB`^;a^i40hE1wW^257@JtjX8j!U~;k_|xXG&Og>Pp^BjQo|GJ?4t#ES9)%Jh zF_fsYs?CfE=u;Wquxyxyl%j8aU&!V$s!Vgo-1Rvd4Wu`kix_SxM1giza+y_L&k2L{ zK;wMy2@^!&UyrL}o9dJw2TIS*kZ}QF4xT4SqsJ0i~bI-j^w0=veZfYta;eM~9jm zNLj*Zz5(t?oe^~cD3x^FklAlVzm4>$e1zi$H2sHvI{vw@t5N#zq{I#V+REFX1S4(h z0K61HLfp{`n8K!=l1U(xw}YPo9t%BI5KiH7ZXE9nL=sTLcnNX%n!Wb%w=KTJaRw+> zq>#!YMye1=zPlhI0cOER(|fBfHgLH5sL;3u%VwaZ8#Gb^Z8jucaF5e~LI6Mel%Y1wk{W|2y4L>!PI_Nr&6{=r5VD zNK#Ny(ib-biB@}6mb+!qt6xPUAkX5u9Ur?1OK0fYUg|c$^0T0d9$)2 z$*gb5NhSrf2p_4k;MLj`jdBC+b0zq7PXJb+(^&uJk-=WOkw8L7L(NinWig3Vj$o8V z29qt#wF4iqb^aS!2^kT1-7r`02yWQ|co}PMk*pBn za(V{ie=Evy_ffqs&vPCzg4j2u^ibcQHJ!RvU)DHh*E=-*PL1DtnF^c|+OhB8`^)T&MQyG9HRt7^oH`a4SF9toplRHj0c#tS97Vf1(hcSma?ss1zBv0-e*L9L~ z22nV96snM*@Ui6tm(L+U)S{XkqFj*A^Xa85$24f~Qm2W7j!Bv_{Je$2?i%5|$lt4m zdxpqhw1R&>4NXoQFyuxdx6c!iE$we4B0U!iEJ=_xESM33n*ovpDK*Dji_Iiv0|t4# z>DxdT6x%d=1A;_2T~fQQC`q|q(E2iqatu@e*{jzOSo$Yv=o z0vdSPk1LTeK@18;q(CP*08-lRTA(_$lO7jQiK*O*Q96M^4e^vDx1fUpm@2MB!4FSN zLnuGtXKGKB(4S;UPCw@o@N|Pq3KlA_8fYsaHfk#QsNFJ3HZ!RJ6ZmS2S#g(+f9@~# z^XNa$=UtQ-_`&mcgOC6;!V3<4|Ll9W`B=Dfg2uy~8sm9#Pl{7AFb-9{J76`{i9Tq>p;aAbR>fuj0{U6mZlb1^B zt~P`PpScsI=cisDedd|F-t4Gf`fx|xyuI%{y>~fvCU2QH@7|_QeAH5L!(8Vds{oc< z+!p<*!)$g5_ZCHx0r^ytBpfgX!vvee9SY4}_#}Q4vG24YqcMOpV|${wft?O*k}#SJ z+vp)N(?s+kPYiwp`83c%8*@ZhZOw))SfxlrbbE*nCA~33D3k;=F?k22VMcC~Y+1b+ zv26ENv(#J`QlVCyFr@(!N^tvfAG7L{wZ3AbBdaKvA6{cw!@(@M_KL-=p-QlV+}YYs zvR~M#jK*#oSSFbjw~ebGG6vGPuxQl(?yGm~9Xr-s{}v7it#V8KJc|dOGl9#>btVGp z01dRk06Zk*?5X;CSbRziCCo@O4O6X5bPYzSI=CxVAelI9Vz-={^o^(@VuTEZT5-}# zWR6nqjmnaTsh|lp^_ZT_|8ts$Z|(ie28t`QM8)q5v9@c$c#k-zaThGN*+@Z5KergJ zWwWqN=AMw6#L}LgCaDDX5q*;06~C&g1CY!TUg<7Lb5K#rFn2xhsV|t&=1&RNMYC)+)Mt_|0d<}ng6J4|#+5Kj4+C5hPbo^Egf>wjGV$?m5K zzM_k7IgL{*c=ihyL&4bK(W==afiCrHHNP#9e_mJO52xNZzXeSlseq^ zH+_C|!=*VOCpFxu+*L0AB~?tF&85Qo(Q4Lb#^xA-UspL)s&(q0dx+O@S1~)aLneeU zXRHJuMrW$|3)PMZ=F|3w8;)~yOiZ8Tp(et07eI&;CW7y3g)@`~bS)S08&}S%Bq0a; zprg3d0h+6*pNNISnr)}S921FKGRY>2<_>NIYgNIyDXdy0^tO2AbB679kQ>&HEm@Q1 zlFlXXQBcM7?e2+FE2+;a=-sIcz{&Hf#3W-oE>(6xQ_1W+{8EbxEP3KT;LcjqHT$iD ztjoSTc$zz{)T4y?R>0JHALho$_kEU2h|HZ)^V`fymM)}?u(ET>K?CtEfkRfR%w^!3 ze4TG*!n@(SG)PPc>_;DZf6B!%e@c8oH7fT&M4Zf*Tuh9w>tH})J2G&{?Qsm0R!2)@ zQuPMTPQ|EO%>L{@Xkk~kyKf)$B=9=>n%4JstZF)N`80@Xd~lTYoVgzR48qOSv)l}q zY7R9N!+SfeJ!$l45!r1&ra)y}uqp2;60pD+9scrP5CD4G*0rXtp;rcD$q%hHpay8s zf#(Y>Fq8sEP(tdnWm{xH08s0MHu`IsAc}z(M&7;W%in9b?@QkUFa6+)1oNm%sb+iDyoIAV7RxK-#1p)vOqF9) zoe9SZ2C41(!w(cqdn6gBV3z`xb_%e#5=(`L>uT4}m}7=CG*q=M9#20iz(}0ru=9Mz zl8F&%_D`#qd0jk|xlPa(m?%y^6%4qW^~qK}7~%=JEnw2Bx=tCT))qNz8{Ca4D8Zh0Zva5m zO}4R?Q@|YMRxu{1x-sZh6J8zT-O+V!1L*0p&%q<#NqHeg!t8(Hp9Z$5sI!O<&-r4q|aAmsv%8PSf z1@&RMy*9v_x|S2D0O&GtiU6Ftp&YKyFI$FHibAAmJtPQ~B&bmB*JsSSgLN@GUy(O< zwv1!&u&G-9s!x!twECJxYao{E#4Z0TcMDR){xFD1CU85+dg z7GMuK&T{p{L7jdnRO9y|aE3jp$jpH^ulqm1)ehWbHqd1;HcG zcQ^%*=^iGfktF8=&ZOg;ZCTfO!496Yz+&aQWeTykR_Bgv{n-~f?u82b>&NP96v za0Q9Qu^m|7AaKNFDH4Ed_8hpC0$9qj;1Kr#qT%=};ZF6AT~0=n^N$)RH89pzpE6E< z=-MFyhqeO3RT&}T&a?QG)8ssV*i(?Y6I90}`O*TFlZT)OQpl27H8la?R^4V3Z6mrQ zM^anjAMkJUN#_3E5XzI$7` zgKw8(oCXYB+w@`$zT20!j1W*xB{vPxgMboAA-Ak0NuZ63jH3sn@D~HPrr$d6xA_j9 z2agc#5@Nx0lPV9Y>K$iECYq9uu{2VDiNWzQ3p2EfuA<=om?P$O5s-XEiGjIXECvT` zrj#|AyKQ|R<0FG3A>%!XMcVQ1D|cgZIDgh1?vGdHI7R0aokiLR<~b}0%VqM)9c(@P zZ|_UH+3(i60PO|({K2J$^ECc39ETc;JFd`i1PqK-rvGNo!DjDEm^z~fkkrb$L&x6^$az_rYRP8<7cBTD_0esUG0s|2|rAb39n)8+D<|EV`Va3GRX`(X_oXA(M zDd|qJ8PZ5>XcDT)wJ?!u{f*Zh$r}&Mbdw|Wy0dQUr%B-F-yFo!@xyuzvYVxQsR57P zd)auJ!rjucNa^FP|2%FAb}7!gf-1t1;rl6*qk4t%N^c|erXAeEFWmW?l@CAuI$+2o zcW4t}mfEF!DOsKYe|!v5(z#2uSmheDgHP5GE8va~mu2!P0(bY*AMM7M=%7$c<(-wk zR{x&2GW9$X$UwQXm|rIY_^R4~bi1&xRZmn8~oh~zQ3 z)qpFJk>e;3eJ@W1`^On7 zL&nTiZ)tcA?xjEY28N1&H|zqdl>y9kQBPtOnauFv^4;d+VyehD@khv%KDS*VoS5E5 zz-?(&ppySQ-mo<)Z%s$?#T-@5l>oFaA(m}PFu;+VG6 zqMO=JD&PU}`gL%&>ymSd^XhDVD%BZ3MQtT;Q|NvU|7xvdX0mFvL}qZ~xQX?1`nUBi zUq(oeiv>+E{s;q!Oq56-s4E#nOQ;@LTAGeSF7*+ML)c_0x9t;NTnHM0LWF;!XYTIS z-3e07)7`2z4ZCTuj-&fKBRpm~1*mlq0HopR z3&xbF>BU5WWJhgx63B# zRe7B@l3V{4=cu3?=ko5d`v6VtsM>p+MXUGhWh`R3#9Z>54Vow=o~o!;7jJw|FWHDd zwZL5g&t0ssCg3J%8GtUC-PSX!VY`ig7^?O3;4!4q6J}SYp zT)RYPK{C9W&vNq_9Of8#v$>bNaJSr+b}X@Bs7~bpMCb@ojz2=VBM=%x9}BgL6QdQ8 zmkBIU#Oznh#bm-Xd&+Ymh*inifKtG=(qDiOE(X$hln!H{r=0i3D5)Bkm6aPzS^cVM zYR&q;hy)y|1CrmrBMV2Og|}LMC`tvn?7B%7-0D;wbBg6@$+c+VIK7d51p{)`qLQAt z2nQVpB`DfKgGX3h13n2m{NXp)-CH10-o%}9l)5CGEDc`xdo#I|U*|~}CQF=V3CBq% z|L&W87U1NRy}yp4fZMl^GJgFX%N9M_(MTN$I79M5M-3j{(lTxz0gH0MV`4ftT%x|j zObAq5`c!K3w7eho$v>F^F-HyC9F!@X8)^0Ry`tzH%?!DvXcfYTli%m~Ub6eSGU zPa$k!k8mlZNTf|mb@lrFU}OLv8j`o~X$pdLH$AO7X=FaDbfrY*;|w9{#L_s=drJsWY?!#mY^bBGi|e>h|r zZDY$od*(NRAwz47XNINh=mxZL{Eeo4;c;I9of6?ILmQSTVYY(15^@VWlyU<#n3U5G z_zI%(%J5a~#tjRTf{|wubAI{+eb;q35LNm@p4dXzOzR@-wnN8I!E+2dxw@l(f1$EbsjV7yk>?Xd6wjRmN% z$MSB)j!uq<#JEQ6PY4edWLH2#wz$XCaB}r;Nu=c`=~{n}tdv^UP~OTZi0Q-8G=u8K zTT!~A^CI|SjH#@Cmm&LdSy~ULW1#2_*8JVDI{1IuSWU3X%z<8Iu?g@17~r)K7IWmd zY#r0MO0H5t9XI*ZJUABy_$cU8VE!h8^!B$&*rBB7->D4pGY~662 zo7NPjY82b%hpZe3h%R|>Y#Fb?LRW?Wj8H481o8%??>0!l@ZN_NhmNxyLlIvbR8&gv zV^=cV_-~SYIWn=GHeDq&f`XOl;gc|<$Mjg}xc5)sG}VWJwF)5vCNiuiS-^>RTH5i5 z#Tv~!ub-lX4kNvJWDVd7Upf&5_&KUn4~PTI0|0eQ8R_hl-@IM~X7U7yQeCjb1c9xw z$wL5n4vaOyCnb8o@w3|r8WLRuF^a*Vg132;PGIM`r$YBG&lT& zOD=b}jA=yL;AtQv9!X9BnkBi4tBHNTk=%;rgByWD-Z0c7lqh5N z6X4T}FC}&F^P@!AU?%y!pUxEgcb-sgol+ww0A&S_L#REyoa`s=ldmHGP8ABK_J`UA zBb~2R3iq~3 z0QhV#wuF<4B|SM4QUN2C)lsgg{Fa5;48?SW$-yGY_vTYcI|=9!{7|8Hyi}>54Nv6` z7{Z5`SpBxHWN8x?#hz=JDY1a%Pq;P6XXE#^Hb^f2@aOyzQX@&Q=g>G1@BGPE*ufft zx<^-<$;z8LiOaIj!pEuw5bG>zWicm|Bp%koszQ+f_>KMV11xPKD*WA$-(S3w&mo+1 z0sO2S97fDgEW$%V-|6q2q}<$Zq!B#X8eX*f0%z}%ff}RLp?b9ZgBY@GJ>VzZ4T;XW zzIKsSC&2PMr^bw;k~>wnz)!$&>qKdUJ*A=z%%o%bX};mt3zEW)6`9lS)}E2&Rv76w z5m)Dw{!1?7sHZa%mh`S9NL?ZAudZqc^tj;UGd%4Qr!E_ zFjNJ4=5QY9nwHXP*WlZ|qM?c;5)>KZu_mJq+OMTTqIGfI>m)^jc-VBwW;06;6;MRp zVL+%vl)o!fWV#%P2VfLeAG9Rq5LsmpLMArqUTbjY{|P1tQ?-_}ocWUAuf{g1^#&wa zrdtxp_}ib6CY+pdx?_&=h8{9Ls z#q!_396}PmgvKx(-(I44iwc~>RPb(kojorVJ94rGS%&bbh@ya~8~duE9QIlPAk^QC z<>O7SpV&`X0-+CMnin%{)kg(~Ga;Jeoog%)SOiL(O$N^>T{x50o+ zOp*l)UW{KlP;is7nk&`{391_KhZa1LcH{bu~AF8}0>2f{^_2jUDcra*KG|7z5llSsaX^8r%9|?{4VRqp^@5TcZ zfN8wIgm`nDW;+73O*N>8Bep(!3^5B{ti63?OHLuJyJYSj595I&B3_7~S=^T1aHUsZ z3;wr@!d>7NFxL1n#M#V8-YtB_GlLv;bF}KW<>&KRiq1uoggW0(ZX%$&OzcZ7-aNWDD5-y90CRq5M?P~yGA0*A*>05Q`4I_Sf8rlAvh8! z4j9|RUlE=IlazN!E}_~55gOY%U7lKfpVj4$0?xRTuBX6R+3#cv>g@Z6>M$Ji&=FLT z|3vf!Y4|8!(ZUrQR_F@rhnuKM3iI^A4K-h{)%DpGcQ6M`o+C6FWT>akiE3}akObr! zfT8n&<*D_>A4GV1S(PMl#qMzsirhEB>D-6mFj*flcUa4%5XofgepV*Pc_wLL`<4J% zyhMp?;kuABYnP{HFMj2mAyJ~xKr-&TEU~&@fB-zm&BX)(S(sEnypF}WP((BiMIz=HlMt%Y%J;VNWs+qDvoQG8)?Mx_1 zJin(0$-=?zicZqTz@`C>SnRoqQv}dT=86F?P8D^5jQQ_x`x+HCz7J<(nyTPaWu@O3 zE{5gcn8nfv3v0TwP=1Rs$>CFlzHj`4>(q;yOD&4`Q6@3h0YI#gSunDc>IvFPj*EeA z0hYPCmd~=3E}aELOgo%WbW0m#7lTiAeA9$#SYnhr9qyF&XiO!<>&}$j9uDC z!(9q|GjHmLF-a^mx>`*!p>gwXezY0NOyp3!Y{4>y(2j3MD3K3yujP zlhuVP+UdlJMe22kMg;^(FNH#quLxBlQ-n({Br;i1JSEiM2`+IaTg{KcNGNzl|Ja;C|R13&-j4?$7Krs{u4tpNxp&W{75 zyB-O1fh?RlEZ)yfE(%RXg$26Gg$+y0(v9BOSA~|sj1{=VtA#1bs0l=UH-8w^d|Vv* zo;#fheF||^&{7mk0;LKB6`l#E&Y={2Wy}OpHOyHyRSr=8&QFpR`7rx2O1HCviru&W z_S!GpS@&?qf=8R`?zw00lgp0{p;Fo27nurk7z#q}H7W`_SEm1^?5zxUg@YP!V_4|q ztrYW03A)lIwHT?ma9LC`#4s(`DXJ?0f1nCp|xkff~L`g z7UUX`FggmzU;eT9yBeavl~jrdK!630QG_zYGH+^CGJ`qr8~c*lIW2$ce^pFi*Gk2_ z_E^v>6I%iBI6AnofP$9pH-7&RfMcR4!?i5@G(Lplx0MuZaRZx9Q;%iH0$AFr@r81N z7NY10hoG#RYohfzZ>Z6=7-my$C_i8{j}iI+&<9w;D$(o2-ns%m7PxZ!@=&Y^HCUK9 z0(EBinw^7TifLMQ3gyXqmUReqNtr_+Kv%6av-~;b1NBQMA)r3}>*Y}3 zQ=B@lp`k+lVufniu*qB1eG>+dexO)^xp%$rPRmNrgjdioQBjcwa0cd#%|SsFH_s6C zgM$g0gt}WXm*F8cgDcCGBQs774`dadY0*XY#e6t7_DKDyw?YNcdv}##A}a_m-H27- z^}qzO3YNfe{PbmRuKDy7dKNT2{Kpn~Y^3j8Gq8Ki7P^Dm)EwigOzNa^yL2CH7AlpA ziK8@DBNTf2ni5!>(275hF)0{kyxpYr#NU1CrLT-Xpugbsaaj?+)ei^TwC$b~M@1Ig zFw*ZqlwE*CK0k#l1^n~3U#;I{~gV_l$Uaza3Yl=_obom!V!ZY;J6C{SL!f@*}THb2#ysOtb*D=83ZW z^z~5Kb{@=Oa-`Ld4SYOQ4rKp0AfW%9yY4{@hJObJf3gO?A)UCJFeo9Q@{8NG%r1~1 z6cx10WVHeVA(0)B^_#!iQsQlj{b86?E@3`q?HZ95+%SHQjw>)P^;1W=&st)1onxO%ZZGvrDGA%xz^HH=dSg?@s)iq&Jtk0C zwg?q{)=SAAR!c5|qC{(61@fG+ zbr-~f3~wK)L)wby-%gbocdn3^zjF+q}I`^5W>vR@~QwgT`|=-FBbiZ_ofOP^3z5ycAfP20u&P z3fQN-k`NTJCW`|kD1>jWidF_wSgyO)#U!s6%}@1twozQZpsC`gAd><|1ZBZe&@Ajs z@9$1(&_XBAE-L_a^Xbn3Cu*6hKPk0j<)xGthzjTigb`02I{UdhE-l_Ki3CI^sV??(Yn7E}CxRJdf-1$11`U<2l5+cV0FlRuOnc!*0 zl@d)BOelDwLU>EisByDu5G!EByK+IriVj~k9Tet}2P0MF5|@!-DzwrxG`o41c|Rv{ zrx5nZCPDOqd3)y)x&6Y!JK6XBFobENTW*6Hze#7%{IRA!k6$mHO(+#>YG+(MW{n^U zDLu(kbt$6L*NBlb&I+=;n$kn8R$SwNlRyCUnk0{E>~jyDSH_*#Je%Mqq))@R^(k>! zUxpoaiBo>?pTy$mr2ur;+Ss~*Km;zam)4aF>)yY9H@Vla94_5Z1F+D? zR4ks$ab+k*HX>Z2z&KB1V<^QP^rufG7Q~pr#Qmpq^OREtacl%3bnc^({PV+%5|(%) zfjO}yi@B^|ZxhXkleDHBm-S1`7CJvd1(R`$U)lJ=G;IVOt1uzSu>&aa-e?SJCRW>P zBVBr%Xl(6t!ifolEu|s>m7g8bGUoh^8#`+~iCAgQy_Z%$eA0kik)S;FPOG&C%uF6m zu#UPY@BkkpV*+z#=A>H(PDId%%sZfohT>3_U+kd5q8tOGhXjYfLTWNeC$$Dj)l=Y7 z@thsy9CIFquGI7f8CyQal{IlqA&<}Vf9SZ8b4-$BIvUg4@L(XfdNs5FSTsK1DlI>c zL_|6`gh{DnN?39=6|q=T7u1`17^rw5p-yWAfO}-QN z5&_r#Xr0#X>E8Fot+y~0zdOjT!P``bk5CR@2m^PR(hXuK_}?zJYLISM#WR@^MrDRB z(6kD$>V0WGG)ZVVXTT%~>6B?;IRss@U72ytRMnUgD(to|bPAOkQ0sIda9$tpoyWv_ z0v!ut!L4lQ<*R}!nN5Hg921KqMOzou3IjXMi;@MhL!9g+ywInq_C@i^1IN#F>_?7b z;rJ1c@YMWXYcw%QPWIBtqMD~=wz|BbYP{t4ICV=7B#N8{7^+$PSFDw{Ns%TK7l)~k zt=Ph>b8S)qDi$nJKWnDU$^!XVz+~(Z<_Y)?sI3M7M_?ncihZtu+-I7%A3PWE z2F=-86*n4Z1I)P6ON6Hd<;(|}J6DFWOV~R8)P_k@O&pQ=gWYxfSt?o=yIr0|Ng^q9 zB8Q~SQYW895KQI;c)qVc*1eS~aN{3*i-_ohcE3sUC^$$$(JnMQCm={C_k7K@q~K7z zL(gw86ENwlXvsAl+%#aR;hUl?{>nuVjG(wdY%Rm^rjNg%emp5`M1uAFqdb zmrMgQXHZGR!95Y90}KtG+{CY^>A?F*Aiy?M!BzxU+jL^nI_2Jc6~qMT5T!D_-G-aJcw-IPyRo~-Ump_@=W)vLt1SF z^aIh@4Wr%AJ$6oLVp<|3>st1o5n3`?nAsDHKa-qE+7-frU}kD5%bAQ#(_luh?P1%R ztl48rdnUV4YqkrO%$A!341b0#tHVsp7DXkt=viF1_LwbF<+^3u-|u?f4@f36XAz<4 z{=WBn-{*ba`@Zh$zV7=zaXmG2FMi4V9E6ktr=d>{Z6Pf`zP3KB>9-(iDd2&h!>2FA!a^K9?;I;;(!nbMKg>n+{B*<_^+_ zY0sPIkUD*Czw}|#&G#?-QTIQUh2w3lDFd;PP&h?S4kjDI^}3s5ns7xj_hkY>ZRmIJ z+yUu;D^Hd4BW;MgrNd`qjo9odtxL}zEDqMa*?^Az*0S1`r2_&ynm^o6ABF4Pmh_zS54X6mCkIKbGIA%xu+JuFiPt$tVlB{{7YVF}LU zmSWTH&G9YDP6c92pH4&atKO z18U~-QWl_pfg*gBsfMY2UlEAWWN@%noSViefC_O|8^cjxp(eJ|^830UT|SA~75k`e z2x%vIpOPC}7$QNTA%->2{qyo~eYaTiiWHRMrl7e*`;Q2_U0K1!x%u%Q2i%7TtumsT58j!F8 zz>{RVW$Bx`_6Vbo4%hhQ=9f#?R{zaGNIosuY)cO0T}CJ9 z{?V_sHWL0z9g%Qk1nL*gftdG^MWHjQj3aoTa4cu|4U%l4&5{e zAxLlot}CfJc2-JNH)jicPz`TM%QtH3s0IM)0Dj=scEI*)(F*^EEnySN!zmo+6_Ar$ zM=pJ}U)f|+1G|D09w&)Wky43h}-8l-BA5L z@Sf%q7!S5_A8B3NTGm@SUgjAqoD`S@_JL9$kF*l!I}VOH1{OMtzu$3eAGsHMF5Pn3 z3|V?mqRf#3Vce@NC{=c&Jn~nlq4#$d3?%@AR*B*WA201x(rae+)SZWh!JqFea6#;l zqxc7t>Y|YX1BDi;P*+{bQ;{^D1|I&>zv^FdCHZC@J0$0o>pvwLsiOoDlE}$)TU`+p z0Qthmsp}uwy`Fb`jpZXJ7a;Xh(9=F>D)MyKH&3f)qD8@O>hE^1UuT&e?Jq$bk0Dv# z7I<`I_ige_eByGx!H+5!DJc|O^BXwn(urL>!}3KaHP+`iOUvSlOp!|gHC?j$#gvzd zRxbf$Zfw@Cyi5+t%VQNeO4cZ+;@Mn=n|#^vrsezJU?(&&FPDKi@9kJ&Ux9Pex4yTd z_Aspmx{v?y8`Fup>N0FgG%_q_uBZ+GuG?L`@nj{|n`WqPN}s zY_so@gC5x}$7@W*?oPXy!Mh-?$kVk`b{&NPDv{7lC$&eBlsFu=k|%`{mScinwx7u; zgD_!Z5(KV-2Uy08fmZtKylne{w4xa$w(f$t>S4+OM#d)kFCv%2@S_ZJ#@X?szn*z;O#xdbvA~|P=K{~m=8eyD=9EnywP-V1PBj1uWW*Xr7r!t((gfRGUjsc6;N>TIKdk*3q*NFZkLHup136Nw4Iy$`a;J zn@&UOVGGLRzhmyr<xjxIEe)^Myq=l4@4Mot$!j19~!WEi9Ad zA0~edlTok=mtqORR6^trxvOj`X*A0W2oPb|2t&qre#&u4!Ph`U_Y1P~WUvN-Tg{HDZ9$Gg;ytXBvOf5NttJYWD983ALiVxrDK z|L8A9iOXogbE3KR1bCyTf z<=(wG32+e2b`)gI#<;;1wXPri#Gcv{QJ!EhmIufHneYg#!MEPG>`YjI-@w-H#``Zq zx`IW)KM!#6nvUwEq`Px+Cr;2xC(7`yP3uzBDm{1s40qa051RQg^t=7KiS`&c2?qwC_~lrj#Vg~??QnR= zOOQ+1J&N&ikN6V{U%&%jfv(gxmEW^y8sB@D!C>VBzEVv(5C_;l3hC5$W}z zWY?(3Gcg}*u4IRl0W=%ff8^L_Z>ksiHdISV5|p81(Zl#1jzLn}voxQ#8|@6_MyQi` ze0(Xnaqc{oygZvI#Y>9Uiqn)@s1ooYe04jmwAfZ7Zmc1)P#j#Ejb0P(ESN?9x{~<< zR2m+ncVu(BB`Ge4`A=?)Es5N9>?7ZI7nlbaBLX<~q%h&{zQJO;nJ^n{UZ z4|tsa*dPlvyW%2+Nk8{F;C`g+?}O(l_CxqEho>HwkZ!jg3`J_gI-1HW3iPoVd>Z5= z!KRsdUD-I2%PPq7AEB3$@O$Vl1-xt>8k%gkcd0c+_jEx77GqhFGK7}+)%7<{}{{@X~=G?J2Qh?eMV!9oBc z(F#10kjM@LBgOc3Ep9XFhGc^ zeS9nr^7A_PhvE)6UBW*sVB`nmF@EpNdc z?FLD~9QAZ=d|~aTw~oC1wQu(vPJ@EhWbHnQ@SC44-uObzX}n8+`ic2RuFqmZCoNc+ zqC=Vx+`o|8!T_WK0XZKo^uND&(s@Vq>A8^lc?KGR^XiNn?bpk?tZ;$}3&a)GF``S7 z_f-I>y6(WGTzUnExyR*Y-atPzcSIC*=QfV9;#kvFA9Gfm@R-`aU5wJ5e(biJ?!4wl zbm=-y7phy2?^|%o!jJvMrKZ=FJwm(+O}~1yD)9zq?u(pQ@PEe#g zNqQJ9GsfNUln1UZ;%zS~ZJitXUF+YB>+uJ82dDrnMZRP5Y;=er)iq`jjkfp041+BQ zouxe(U|E7+yr)zH_Q**D1k6}B(ikA71TR`N)tL4LX1WM!;5o*InxkF|#R+AIuR1b=Y}SWi zhF+eqZJZ}a3MH1at|HK&#pHo13EXA#;6B(-7_IMU-O)vv94Pj`B zsmNW!GzUtNf;DH}7}j&O{K5{|>7)-5gK(dY&G{DXl$xqI9{cs1d6S%Fq`6ph2f#t> zAX@r*3R(b3eFsL4tSv2olrrJl4iss?#r6z^i9F$GeXX$pHD1y$JQdNJxv_PCYdr6(zc+tASWj2!Hr7xF-^NWa`KSTa(#x zVvUkAn#h|$m03bm7>zzErYq2eIjGOv$ZG&o#masr?)9#b7k`;%r<5|9SD~LB||+U%^ON5fk+8 zsAUjf1tftEXv@%kQbWSi#XFjg$h2@OYv9_cF&V}5K~Tw*GjgB~n)uYn4KPXGO2HFp z**dg$sH4npe4^Mxz;T!g>Lj+b4H_?9oBiO`^#JtnM6kD+!*&Fz^3Q%H~fx4r<MNos74|u5&c6nSpbMH+GVIJFy$d($JQFY~HqHAh^w!J>$O;?v`HeDAR{n#nfEaKp)!0p9s;9P%P@gWA{LeD|iDGG780akp>sZitN-=LQ5t>?VUewXl z5I%s#%hS*Ek3B(~14bCft#v>Sz-6I7K;B5RZ`1&oOHR)7j5p?5v?|IXZ53nsg)yXy zG5vmmun8R6*3Xdj;3M8jH*Vpf|BIMjUrpvkPhLWP5+znTEqLhN=R2vtwgSZxCSX!J zaHZJW^5Cn@#dR_4%c`kGyZ>QDa%B%bHN6aW>BM&QObw6Rs)N&J@~M~q{{D-Im=s52 zl<}VM;|iH19K*8NiK!K*78>L-Jr1*F4lqC--`0%L+l%KTlf~*Z9r^u@ri{$4Rn6e( zg-51Tfk*)$F4%Z?9YQ_FfL)3!Ww4kcmtCpv8ZYge)>s9F2LJ$hqXA5lQ;<%i&1^o& zjR35Ku>+ae?^@pEMSi!L$ux(%;q({z!gj@{H+-axm$Q7wJGUy_-j@Lm&2vzv1NMW4 z$^;9$LY1(e7+)3yn8Y;ZX(sPd61eGv-{5jg*%4n@4p802L_Rdt0xDWHl{V?>*wLF<6lT6#qXyvXneA+uCAzNZ6A)NzzWLXDqE03K`c5)sQ-wk8{RME3+lZr8 z@2QQF{Z8bfI>&bUzrpSO*_*dDOK+^jReCM0ugm?atT$n%cc#_zBf=(Yf`5@@YH zO{a98Pc4#*&qF!JUwn(q)~Lz=`fwl$U@O4^Pmq+V76F?0^V1_DpBlA@bnv1-lwqM3 zXfGYqIPuy&3`FS?1I=z?=%f@jwfH=EQEsWA4ArLTmQZqq4!vsL@)=4^TUP^w_Mctb@C0n>jaB%4xxLc>8>G zH!F@c?Rnz}I&b|ZIHXCjQjx&y42+TgDGpvRROda!VfG<-?ftwe3{8VPVN5WTg~Y@Al)?mwHG%Yq^+f|IK2MgU(uT2r;RMq zS%&U5F;H&BxyP;(3__$aq_*lMkTW%B$gd!k>Km7#oC@x6CH(RV!73y?k=lb^_FNZ8 zRM3wrT%b_>q9or61LbE_Yhk9XesA-_f~i7P@pe9sFI8{<;0N-7O*bERsxEmwgzAl7 zK)W{g?27Y@klViluf~{Nei>Z;^7L5W^Z~5fNFPWYYmDWJA<v$2wKCcpVzmG7fz60Srs;;(I!5SeYbzTIB+m z`T~Nw2BLnpr5sV3gwO$;#t=^#e0Dyf2muUfe2MuFa#IwQBrbai0i{N**e5D2p0tfW zx>ROFP+f+x5_oL&&{`{edlUx4*a9vHe`Va24$Sy3os%{mnZfXsml8e+V8^GHAPr$R z!bxhq-!Z@xQfB0Z2AeM6Q_C^k&WC%m*Q_O@E`?k29a+^yrc8{JPr|f6ADh!|Svpay z=JTbAk6xzswj@Yq%dUk+!bV{JEP*NwtxyBsa1RsVf{q?A_NM$J(Kn~@$oGd$*7mvYns-?~8KXuyNQ-HXey z5B>{Xl;ub^iV{834Lvh+?FMiqZE)iDq*$vMVcCiK7BKMpQF|;gdDkA_yr+!+db0;# zx@tOks(ZLg#?0iyhPZD{?x!~g*G%T3eIBHa+&}f-UJfJPghwyYHTq@JWz=R-2S~6l zZYBT9#^pC4v`7Z4%%P>D1zDFGcCe&fnd#{Gkop^nFj25|V z-#x}nWEsK91Yh*PFcf1G0Z@2v$GX!tzl2fR04lPe7XpfoO=ub0@~C4HM@c&Q&tcAB z=Cf;qx)0rFhqtZHTeQ*@_+Z(a-M;0O_`nb8(janR<721Ut*C!wfb7CDM1Uw|1Ye|e z@*|S#1*4&>>dwqhV>su?Ni+Vhl@J&KOB*c4t2LOf)p7mP(!B0NEZ6nDd-d#8llX3`nV1xh!JH-Q*(eXLF0Ij%h zpj=%TASD#8qhKD~dgb|xzpqF!UlzpVg>Vu;h&svgAAcPkTx_(I!xlnk-^RuErvw|J zfPedHTMBgkv}3;X5dUz396bX5kuMLfeO6oqyZ~rDIDPf>)h{xi_xuI=2JCrr=yCc} zKD_XoqzK{VcJ34*QV?jO%?r8wI>aV^vrZ)y)Rarh1psWAP1oe8zFTO8HJ6IRvvt}> ze(oZ$sxLzMW`JXk&C&t0JbYRGOYiWl;Hjwi=ih=rdoF6EhUK;i<<$3FN2B+B3zlDV zC4?gX{OZF`ZU3#thzImVo=S&cI}9GF11xJ#tk>rmty@r&bSx|^;cv*K1GK#$*Fk)n z&@%ySgt!?di;u)K;QT62WTHNubg}CftqxHIN7FRsr>h zXo`UzClG~Tu&8RHW}LRVq)A$x+ge70N@Qz(%Bm*T!>aoqS{W}DV*<5Bnv6b_0;O*h zQk&@C5mNvvOcCov$cAHh5IYeuEQeGxF@uZJqibsI$Xu8tl{4emwlT79N!!7oefYqp z&50OM(Jn?5xu0S>{1_G#dt9<01u1cMZO6K~@3oT#boeW1tCo)6hLkQC5*so`8H(Gb zcM^xvwLgRSgq8uxkT|uY%Dj;SK{?WQV5(&qZC~sxOuK4o<@%sk$gV)c;35=?glM!c zlL9r?9icvuW@^SG6Xx5x4yC~lf(q0k;N$Mo#`0wT+za;ja{qBC-x&5%2u3TISFc#d z^^mv-*Sd!O69Z0G+<_hO&6C81b6ErGb!Dp zhwz&4Hssk%k4zefBjJYpvCj>OJ#NxCiM8UE*-Bxr7*A%VWD(qk4VTLAGA!lyrwRAb zH>GtvQ&c;CNBuKTP9DH2y~Okh@0zJvod=ov}7jq{X9-+50?av`*qiSaZwrqOW2!C`;+~py7L>Uq*b}{dUe9QUN-(O~H zWhTo%acy6Gy~*V=KkgGW@}|f#(@+57bQCNuxSVGGTus)A z1?Gve`A>n@h3>paAEnSvnG((uAl~etVdU&B>6sunkn?7X%iY@hwT;xa^{c54VNXHE z%;`~bnfGDeujTxX5_bD0R@;a=3#Zj$)6XPz@g6Pa`Vc~h0G@PszO_Fn|80abf4Um zteGHeV{JeN-BM6YA17z}4$TO z0%AQtL5S95vH3&$E76bTMaNEeCGfm3si-C!yMAGcZvueCyqwz>lEq+L4# zNY*L8ETOXzk(7`h4R*4Dcz{6xKZ=)3S>faV=b(=Y4Af!5C0JBG>=Lt7lt2|r!^!`~ zVpMAZ;2{gQ{`neMiG~2DK0|k<7ruCkK1?XKku3S&#?~jl`=>{Czx$bHkMTATYB%k? z$DO3Nz7=3|b_KBGY#Ysxn_v9%=;5WN9lp((ojTBWNEHiA5$jkFk}EA3Dox_ zoF^c$ix6QPVi27h4?p(VY@H!on%mjxjE6tQ=ES@x3gD$2&W*vadbxKhfjxwZG1=G! z1gto34uj6Y8k2$|cwQgs|G-qqR?beyK=)<@4V(w;`{2{j7-?ZB$0a!9qfPfGyHjR# z&R(~eTwAJf7xwfiRstK1F6qMplh*M)SN78?H{)TRY3&A1N&t}{kV%rM11;DE+YAI> zq0JExp!*=mbivB+cP#QAv!@%TgiJ>|QOP?KLn(1ghi!xFd+4v1+L{kP0x12`BHZ;% z{vzeS6?^2R7nwIJFve_^G&oLKT0_q>D`2n}k?Mcqm_cg|3j|1h;;~&4K+LzqYOyDF zkbp5YqR_Sh8Ofz{ym+!E3Gtv!J!cc$lSkZZ!vHCz5NCw?5d8Gv&8(_YB>=C#YbsHq zEtlZv$-NNl119Ey8XPjKM`JCSbBb5e&ejHWW~uZyzz+V&)EuNaZi^v6we8;*wC#iU z<0VpuMC2gvfdF>(PG)^;&CZ)+7z4^8T1N~oIhoAs00o1MlP#Mz_mZiFa2+6Fxgi|F z>IEP06Yj{*=v4jF`b4fSS)5-4al96H2>jQbHlX2{`x# z&xt;c7J@-}RPS{K8JBwmG=fml8zaC5DzS;&;&}_HPCOd7oj>?9ia>y4H!h54BMG9d zAnyx)$^Xt7BsWdd+VPjU_C{M0N~ew8g-UAw;(cFVi2Z0@S+m{5$dSmC7CSy?LJURM z+9U>#@$_24++tHVM=+@c;1<%uwAiBpY_v4`_bVTnpgDtu)Ol0Iivf&E1iAD1j)UMLv%QX7D1_9IQ;pd)o{ee-c-vLXr*P(St4@MG- zAAJOw{J~GZ4`umt8FH2Ugl7}*Kx(W6R&0sTIaE= zut?8$Hqt5p+tm0_qD_%9=}M5J-hm7Qu@*4coobCqKYhqSSev)3{s8$s8UiwI7-t%k zC#e0|6owcMjZ%{i_j71X1N1i7Mkk#p>t8f8k~_^EO}n}CXeH55?xhh8y-c!;_zwCv z|C~4ih_8@V~CPSve8oK5hXFs)kQKS z>$DB|joP5%vP-tCuHwe|n&`O+$OEN<=N3;rCK8falIn@`r|Q2#V|8ssol6zWAN%>7tM! zyFCZje)7QE5AOTI;W{w7mu8dKqd%gp()RB$-vq>T?{^|+qCvpYjn7Y=*gfms@0gDp z;zYQhhtE2D_(&^F5a^`DSls1aONCdG{t7<1j7kc6Dj+T|&1HIBWs&5>py1B^?VyVQ zA6KAGK}O;w*0#=;73{{r^~2T` z^RN0G<#azfew;?_H^VjkpNId|Up!YbZ9KMwzE;e4q@|+#rhC2gHp~-)ywUPb5*RgQ zlk8+w55A;)mAj{Ai%!(>BsTjnY){KIf@`DrVmXY`=IJMJWJX~ccK_pPGUgWj zkXV5_n3yZSeCR%#SKiIDY-KNYo+Dv%1T{t`_kr#>E>9ZeTu*+9uYG!*cG9{U)+I7% zUjV(L)xCl=2{{%q;a~V3>Xg&f%{IAL;QK9=8#^=r<9-<$8k?X) zO-Qib%nl$_w~-ZhMI&I+0#}pXwfHjEW*{+HyqBicx~u{K1w{LFYlLjlN^l$oXw54p zwj*?e;hA0*qqFx_d=L4yRY#t1Oh7XFea)C3YE>E_yG{LfGXf5Jy<#J(n<%=*)RDKL zdRMVVv|-A%EoP#ujZeZQN^4}|U?34cp>rgnwJUmN&C$Q{3%6GIA=SoIbl0g#c8S2J zKpQrOi=F#L695Q5x_CB&cIRL7$iF?oS+J(9yJk-YWFxGd(*yvb-6Ne%1FIpVt{kUw zk$v0T_QOD>pl;=8CpavNo)U0iyV0-P%rQ4%Cy__sW=gOwi6S%tY-{@gYyY{j40XNZ zLkBqnydxSliaO`m+uK&+OM1b0;8Ak2Ks8DN&=*-zL?(`Zh}TX5tIZB`wF3@58ImIq z72zXD%zfhH-)jCJ?|RZ%dbkdDnzK+pyR?K4moz znV&%M75D2Yr*j241N4u>A^qTjWzD7z_lel5IY62m`bnz}?b;62?glR_4T(KY7$!8#@65 z9-_V1;s}uuD#OHP^3<_RsMJfUXu+i18clJ(Ww(kzk5(+2yV9Pg(3m8ClOXq<7=t$D z&B168N&IbVR1oyDbM|=&1Pe^Q_D|aa5t`&qFm?!929tL8sllgrSdz=>D`+Fk9fX(Or5#iAoMu*=hcI=}WAm zc?hhJRsNK)A&vukbKfykC$xsmx10koK}uJjQYdRT8|hr6B=s~;9U=Uhi`H8TEp7@y zv2&*&W$jbjooliYUCOb{4>`-XNgFM=g@nxF%r?`bC#xhx5(Fh%22cv*O1i9SnB)uM z*3GEt%|om3UK}vQ2*3PW!BTuS!soMK0ADE1;+F)BBSXYT$g4JGqYP%&+P@e7@PIIz$)G2?jf@D7reyKk##xB4Zxy!G?BNQH^q zs;uFqY@D>%a-ihGA0B7E$Q9dsTj|Ag*=usu^-Ii(m7}|QRfiM-u1?UbgbHUv-pj#BR4O$@?pU4*FA zXenqEf1JL!zWA(;k2 zI)Zpe{MR)ka#@6P)+Sr66w(QgRg^Xfmid>aJ-N5y&f0JueD1LAwq?)6Q$wp+1U(rX z2(#pX;Yyig0~Al!L3-IMhZJ+e^}%@>0f>Llc_uYy``(m1F`O zof~Rt*5FiaDW?*aRbmV(iIAhC$;|^Jz`K!{n8PdIs3A(4_h6p|5`dJw*K+S11!1s_ zgnOKBJzUiq+^Pf^X=c3?uX!W;#Pq2>IO6P+hVX>~(=KD!Aa$ros-3?AoiWZCR01h`F6Kh^zb zH>RW}&BP=Vy5an*s3fsJ?x#^lE3q)RR}$E63h2vTR7i*>O9d=4Hb?}yFRcw-m5>}q zmJM8_!_F$xZ2YFxcv~OY)*kgbt*EMvzJ%MG=ySz-WWTUfaN6Q<-qA>6%K=Gdy3~^K zTDT)w-)g$I%>jnjG`B-$x17xO5Px1r%7yc= z{uF3v7Vf@Hh`EUh0Hd^J;)9s{_6%4p%14$PG6!iBb5cAT@$B5S?pFkFtdQ50e!Tsb z-J0Q*e!@)mqWQ8F017D!u3>mLYnz zxeEcz5lUI_Uv#icb>kPTp*cY@_B`%Ca(@su;QNW`OL?{5AdP;_R~F_(zX@S8eGIg+ zctwL%ZRH0qBuLGKDk(;#4qaBR`Bb zh96JkS!FElhZ90`R5~PCYicI&yt;FkKUBth25$XV&!b@@2_%g%)#NyeMHyIhUNi z*~wh3>nK47T4n6sGyB0iLLnDVsGHL=b~_i+)y1+Q;L^Z~YsBk3)qpg13q3iMuh{3NJK8 zgTGS!3TsXlsXXO`I}P0{nteC!!lzT`LHNJ}2|++lNK9Hotj?r5@3Z6|8Y$RdIy%NA zaeKnD=pGxo!4YV|Q2|&fq2`9@hS>MQhHq%RH@))!PpS4&_5p`UvSzm7V`KMXBFa91 zV1wzad_JFnkDx7)I)Vh58olDgPyovU0Fks_Ef_PM9J*RmG+LU0y@$a&+N7XI7WX2z zucI^=!<&?Wr(_gSBkhg|;T$7K=Op>_TKDoY1{@K?c=qtaAEwyi|*E*XDOQ zp5}-y(Fl&795b%-GVn|%iNWwlX-?B8=0PPw-V9lGN$g?JMooP*?YAgOs{}VbwCV`& z@e>=mf6JhfVld(EtTVGd5$3Jbu5eH-+7q_bim6!tBxziL@p0JLTu}P#6^(yA66kL4A;!jZunDWrV4^9__ah>}*5ukT;h= zouX`RBd8pH!IfLZ&_Dk$eLHN&fKA>%Gja5|$t`%vmO|}hV+9jbfNkBhnU~B)+)t+= zsl66fmiU=lWxB~bb^<*{vVSl^$d&P7L&W09us2zvhMOx)&?eyO096ZSXo>hEsB*WH z2!Rw$0eR|>p0RK#Lfbu4ke2QIizuJg+;j_+(}X{gH#oW)nLm$eq^tHc!n25;LdD_A zGu-rwb)!t|SzRao+!hGjqS(LPrkP7-q7oz1Nh$tY77h(Cs2zMPrs66mMZkG*_(M|T z=duXT3m$DJ$>WR@wPKXDp>%PRw-N=3^d>uCNa`@YjtW#o!og&bJsZOwMt@rdD2(k$m<^*Kr5Rd0y2kd?IqY zbj?79u?wiAmMD50ZTrI&%eWhDJd`Mi(6LQ>ew!5XLpjR74`GlP-MyE1^7GRzpft{c zy~sbn+dY5C|5%MTu>+3lHic@SM@V4fN52$Tww}OLy&0km=8e=EV;X@V_jEYo*|Xq; zbJj0Uyj+I1q3z^*+Sv#M$(I}JV5$aE?yJ-(2;JUfm|4i8E$^6?yPbf;7^BfdzciYI zO1^I_tHM?5v%8F%d^LMQDN<&3>v-UeGT~;i(1@ekP6C@4`Y~-F0NC`o{g-JYS)&3@ zpklsIIup(X72zz!F*sT&Bs%_(B)Wg2Y{+)u5oJy!!S>BMzO$&cOOI`UK*U8&dVSxG zX9lRKPeNaWCg*HqNttvOV#uMnXG=5br1*lbBzhu+6+9N4z z_cDW3fH9X4O*yYZoO#5&Mh42O_B|b3q57u#pV~ej4U){1OUdmhw^thN(mw*3>J$}= zRY5Qoho4l~i7(%W8>+2nM4In35^f>mP!!@H>)!s@(JS}7Nt1`GeutByIh$o{5Z6R# zG4b##35%E1E$% zT99-E07X4&N!OrrCAB8w=KdLGieoow$GQ=#?e2vlDCMd_XNllQQ8aen0GZBD6+~i_ zeC{-IILNt4?eG-W2G$YX*jK=(Pme8`fd&KApbZ~s+uFYxY2Z5Y46n2ku5lbK(?sW~ z(Uw!qAV2>3cQ{MZWW#q8LCO%R$Gj4;rQ1C<$-&^{dZFTEu(-`-qmojp~_J=Z9`mm>v z1mT>jCjSx!sv^6I=0~W%?jrgJ>I5V~lLwwPn-ftjQWmcIGo~&lyx`3GoJ7!I>iEmS zi4Tj>Eut%X6yVP2gESYh4vr4ZP!|9&x_>^Xmr`mrgE^Bi_60>(DAR;6qqj$t2JO-{ zV09CK)h;*aX?tr!t0DeWDUFd0z;UJJsAPa=BnCo|jiBvFXIZCUC9<`^EenCxInh%D zCb7clzyP_SOf2E?@Dphsw4m$4>p0Vsq9Br$;3ks-6VE~n)Yn45Q@a4vV^ib6rjnU5 zb&F0zy*^A49|LHc8f6{^)kwoju<;XkoL+{waTK{G&K4Iq{~9N{?6ON2`z=}|`~=Mq zQau^UX*AkxRwgnbY-M?_5?P_&Bb@QQlAtbg*GSR z(3>4MzwULPuvy?-pf%cS;W9U0eKm~>Y1`SGLN<=ToHzMeM!j|^Eyqj5;i+@0|SY13U+wb2g9C!wo1WCq7EzFdFuf>r7#9lDU|YhN9`# zn6ui73fs2YfvR)!=3Y2&QKZ+8wYE4p)yEENbQ6%VrArdp0O2>rN--N`cUpjY~=POZwUIpvMFlUXxWm-p{1J|Eab zFYj4Dj|NF7Q1Hac;3v^t{C=>KXrMwBQc4vB3fYuoT;XRtr-Byh988o#v_qmzWa%6E z?d`iaF246W$d1UPS?lk>+f#oEKthAGks|-1*}m6lZV!GtFv~$D-`+U>T`?5Lb+;9Mw8{e|5+$t$r4*n(l&lE*8LqaYl^u#jFoe_Z z+ctyH>On*(4~ONh@h@12!W?8qNm6CnBcTwRodb1Dr$t^o+01sHXP+^iJ%>_G#(f!| zC8Z|uBy1Wv#B)zCSCezH&-58F5j9jJHP=FFSNjlN0^Ny(&iQ<01PB9|t@B$jjN+bV#kER+3QVCgbc zYEiQ=(wQwq?26<4fF4aBeffh@S&JJ6^eoYwRQaZ;F}N-C=EHP(m;4COT^u^WY3~R0 zojilbCnbkQy9jHoVM2L>F2r*Co2JUy8%?WiW_Qh;LCFFbq1SvG2R4sHnViGRNK^qXybZQ)FG}0Jb zSaD;}FPWev_1bgjd`}-Zi0Xclt3Au!hF5q-jM=gaA&=@0f_a$T3T!S*&DOdNT!FW! z{-6tpP7f>9%g#t81d?#sB$_x*Kqf6BC5{{9xs+?Q-PIh$ZSrVS0Con~we(W6_&VPr zaYQu$;Fxc5s&>(mF@l6#27QAP36G_q+!kOpv#nqvwosB}dsksS-lCuzab6X<*`H02 z*2x(Xn(VTFARC6$DWb>*8h1I0jc(aJYO(kMKMwEJmiduc1RpWV?b&ZPSo&e&Ej1;g z7#>=FsEWvKwV(_B=wMrCvRNPsc*9a)0hk0Pz#?dictetFX%iV|9vOGa)PP1L%aT}Pa2u#e zN<@rO{ld2|r?QQAl_SP~hx6fWoRV)5$k>LlPN{$(qN3T!Pr|pzgZXl=u7Vo%G+Iy+ zhmh@&{z(xVs-fKg8fUNZ{ks`w3qRyHzr>H%Q^~*46q=}rayF{{d!Uo}#&2up1 zJWC53=LV{crs3<*5-0B$!_tBgw`q6|p`QRnV0T^i&3Aq$bupn!FIz9y4sZOIxF_ z5NUmy;%O856{(u?5(cGVL3py_d^#+{(mhG_S8T{|T%nxE19O>uHB!5koL|R+CH(lf1moZBXhbMpKZ@x;F7WWT?0%J!LJhV^EO_% zYqnh?@6oNX|3pBWxCV-mIzWn7_EWyr*u7J2j-(C!63iam1!2L54==%8y(HWr?_~j* zdy4&IGmF&-p4@8mRl1RW_(k~D1X6L}6~_X&(i$HyvxUVaiDp@nKA&VqZGiJbhpiUZ zu260^)+-%g|9ZvH`>#H_^VL7DU3JU;9#dMgQQ-IWkwG(7C$RS(Gq@d5Kc)j;=4Yna zWNnD4HSc|to|-OH(dB@%LNCb^=p85FtboTrTNfae-iq7=rr|r@Y8EE#75{y?@MkT*KL5t zZUS4eJL&AdH|1|F-+>#OCveHFQ5%|3cD9~vmNhk1W}@CZ&eI~I6UmJJ%s-KrGmmLlM`29nF*2BpK;cR2}&lh$<$L!OMe5F!k+8y5Vo`0eIh7H z$?lf6Z)U2_vM(GfLy&^k;1FpCxxd70rqH0guKsN$YL}BEh)N2oupc8>f^^r>g+Pnf zNC1)eS-Agkx*C~LGuegMg+hPDMt&pI%$-!^-$6lE=7$tOlm13=|E6`|mk@1RmnLE< zW&F}WU>)97)Y!^!Xs%3$9OL(@oiyR$%9pQ zA{^ZDPg5ZVvv!nrjPn6Mq2}rr7A)n-bL;9Hnimz3_)e_kEQK zYqXm>a{Q4WG_Bm;v2EdxjyD}YyyLO2GK){y*Y^20uYB(pzs5bMR8&aetVwM<6Am!~ zO$?(&A)C;iL{?%lqOV0hDHzS!F*evvzgEX50RToYICDU)oHWW$h+OrePfEWO$oqUt5C>8~$1rbBzJ{xJ^9E2DkBa8q?!XQMfI>fVInE_6dV_~OBf$^Nt z^Xy9FcEUFk`tb%lz%85T;7g2$YcQOjRB}TBULH)DA$$FRq$--=C812$q4rI72#6gj z6?MfDnFLX8L=EmA;kMfPsavN3TlkQ)L_boI23pd3(BJs|;rFRmt{obJrYA|dUjd8? z0UQ>sy*cBcp_0tlyVveUGy|ed@5dcn9~zOv1B{+&KsXFF@4rsuM;-JZh;L_Xe$&n@gMoorSmsa6`p-VIjCzMbL7Ap`fR!!Kch_3QmK; zIpakL_TW=Pcp(FHh&yQ<`@|18PesBDPrmgPu4e&{i#-f^bk6WFQhdHFFoz78eUdqV zO6VsjqdOfz8S&LQ!4gruhaP6GTh~)GK}tDWl(Y*QuJ|=6e|D7_8MV6i*?4-ijnQGY z;8OWWhCc`hfJGrA6AvkM10F}a#>bR8yPm$8#1B4P?5B!8avq6n^ma4d{ZxnI-ZNdV zQW1>ps7yFh)Cawj{+gN#~L+*D7%ciy?!|qX>ZR<5ZmzVj66`0TkNa|G|@NKeFKl zV8{tsGhq*F2G{@{^)5K`jG^W1PRT>su`{TTQ}M+LdK#ORDl+Lc+Xuy4vh|JK^Cg!xxkWO+&c zvw^vLhp25G1)A8p?{k~!^1Dp)q2tqZ0FxFe* zztcFvDAe>lp(xkJb&QrSwFxV@Zbe}uZy-n{r-pqn4_3HCwP#U(X9~kvI|~ zDGE-C3%4BUOGWd5WR{Z`;+Mcjm=c9Ghq7@|A%4U|X1VByRF(0AYRcRZ-v^Wmx5Fzg zCWC}FzMr3Qjs@>r0vPf@1ta9GRjr(efm186MEFs$BvB2?`#gE-Cv-EKruTI?BWlu^ z2_j>1FB=8qk3UClSH-WQ5V2{!D~*QT2p zw*s2sWLK1a1VM=9y6fjEWmB&35{#*TRe>c}70mZla*-s>I*=(T;d;!*2c^?fma5!A zA(EU}fr$k&{n{r;58Zq`7<2y3SIxM2&lNY_^o5-(5Z$j`IQuCJ)98{-zHIGN@9k*Z zzhlL=jfZ~?A7#sD*PF`i>}3Q6ygVIti1U#WQ2LSdnO>X-FI6CZ3&mk6-7hjoUQV(` zmr3bp!gl(FMD{c3BbCAk;?MX7avE*%H@z#$40n_xwwg^?gb*ZB#YAzHXDNQoE=Jv$ zusoEIC=a7ov~C6MacBQ9>g8vjf1Xhz^nvElJ#&U|40<=MXBp!L;2HiEonXrYj)}9X zr~eFf(%7pEi^rLPeiD`Cvq{47>e4-g`3WTMu^7F3bz@NOG%8A8HJmqY8*vnlh}e=Q zaW>O7#wVlpI;%yz+6BrKXO>znGWfzeTNbI?Em2t5p_eQe`&r$LEJzFelws^0dc&Gk zM5@m`daco|2^eYl{)aYm_JTR>Z~m7a`18#~mJg%KLH9iO(a=6(U7027J3zE3e$e~u zU@lRlksevPs8U=bc2at)vef|mlfNM#;=?}lBe8TWD4dJ>T#NC=c+}P6iw0}$Dba`e z(RxB^wCl@S|KPB_AF!ZU!eo3n$97C11?Zd<|3@j?Ncz;W9!R#5U`>TxCdcp!&$EGigvjgSSzLITk!&YoGHIgn&Jd-vAvM=OWQsDY$rbkUPUMT`s)f}@JbH^ z#}MUj?q~O`2MN$|g(4q9+YO`nEV0n+(dQmFE4K{Mw>qaupz9I&rSt7LrAVKGA>7WK z=NOwQXiiTz^T%vB{O!mP`sx0?DNb89)I0Kfl;}faQItK-|A9yFe?Bh4=3P_#35}%F zOE08HSBICZHMELey|tz$v#jc~W}DO_2B6p~#c?hI=$O%6`ZUwW;N|Dq@t-)vZ;?n^ zaozGAi|*Vp_vW7TSXk_n4-HeJDLLX_>rz*f;|Ky`>z4jS!od3+r2_pC=uJptx~&Z) z)radf)4>Q)dOk+wv4{a8kO(w>bWfTiqhqNzov#nfC^aDFC_j9it%Y)MDqV7tb^mWLvVV@wXDP(VJ(VwH9-1C+FtAVyFSLUYDjX{Z|O;w>0_DcN}bka1>Z z;CGY)niLyCJ6X1zAXGaBBTVp?6=cJEOHcCf@;AulITbx~^!Gr9kXtN<GOwb!qIPwFB<|B8K?qzQE<;GgBvj8^~qhf7l6dpd^1eJ+NVjmf{T?Y$<4p=SFc4 zU%>R(iBT4Z7YlNPIs6LV&}H)ra8q$OfDbw%c`l_S4F*VRsGu1zm%s4K(h}m8kUs^m zb3c@WWhYL3|rWyz|wpc$2fa<`>oq+cPwmV$lLLm%jwwJc+C%IUNz(RiiIn8 zJa&|t`|kI4P=$Z(KlNkm%@F={gvOEd+A0{Tr!JyouiJaw7}F$1Nx4(3K&Lsm3^p{j zrl?GuE;|D7L5L?Hkfr7+Hcu8rA@gHAgFndMiZT?*^=hfiBh8{zIEH0boB`R%n6OR*>hW-zd6ihX7@%Hpw~VY>Ycx$&{@y#JAg zX5OE{p_`&>qj5&t$;q~v(p2aousHdRh83eSd^evC;^As*jnuwIi!nv4uJ{`^h}ay% zf_)|LM@oLE2F;GS@$b1L(0ZLUOFU1(QY-e7NmyzUSuv|d2xlP44h>)>@FJSJkAxJb z&2?=^{)rzj7R9X6FnKYRr53eAX6xcLO7z_5=>_0iQ<|N?YeX`5ccc{Rs_M85OLg68L_l2(H7&sep zAg{;4^uGQn16Y%;pcDDD=bnd3apc&U=E3KlZSQ?uT{}FB38Gnp&vo|GsJL@8d$y)l zjQTgAy*n?JXNS6P9dN(1S1W9Qy2P@3E6{itp2O?QEAr)%zl1T|+}K0KID+snJxPbz zL=>*Z;UcuN%;_%z=QSqV;n>-SEOFPQ4siCJ> zOP&EJBDkC_G<0;1FHd-Cn~BNkevT4V6-GnCZQjS~ObtKxEbX@{-t{ z1)`1!NT$&Q*op0w2`PMSdTmEDjf)oI;uu`ajtgx-k4NHs_E(ikGsj{Dc>5KSjG0*k z1_~p@YNjEOd(mzXl$5bIO@oy>xike$ukTSx&q6Suq9Q-j1I-vz+}M3EW#`-|G}j&D za2;gd>%kUViTxYHLt!8(pKL1UsyVMV0UfuIJ;*X}&`WYNa7$tO#z9x)-3~~*v9PQz z!r;}TwB$M83awiqA)o}8Mr#41GIun;XAatd4Mp_S4gPGLCFY)`x4ygdkfO3v43lI- zvy-ME;-be}HF}Lxg@Vs|2_D05Gu;C^cUb1gqgt9lq_mTyip|@ zv`Ypnh5lfyk99N)aXEh7xzhG+lIKL?5mfl_5u^sB;)RGQJ8d9HNMS?c8lOaB(fd&^V5Kp+Gr zx#ovVQ^VK77NM}k^KOG?5w#RhBqkCSfk6;jCvT>!RzYng|MBB9kxd^L{1p6@*HTvR zj{?aa|BnJ9KJk78)xrh7yu>8QFjbHXbmJi_1W`c&@m7FRxh~Ex`Xlg4kcUbj5RCmA z{jYp4`LbE}lHigF+D*R8FUY8&rKSpv$+8))=F9hipGqf3;SvAtDF+cny~s~tmmZq_ z-1qJtf98vye5;YFyQIk|H2VTDM(D=X6qqIs5(b&@tE0FuITh-2HBlQ~XF-#yCcyfudqvVSBYAnA77H8lLfn%X9PLf{vESU`>&#P)yc!S&_~bYRc+`r-U!oj0LMrEBOEkF5~F2Me|ql? zc3t9{^;Ba+{~r8EtLrlNfx=luA+E>XPe9{?5){tRQ>9_ltHsC?Af^tNPv=5NFY)O$ zCr>budNuWMoG6#u^$U9i#n~`fAw-S+4+xIa*1+~LZCfg(CiSl7Gjz`IdyOBwJR#=~ zX%6fGNHG>Igf=c2Skw%7Y3OM?_bPXfv!-6*+-jYRf)xy;NptN%v(!1A+b zoFWMd+;Y&3A^^+6qbPt4pag_Qdnp3CavV6Yw}V6uK9nYlmfj~lgA3n9T1c=%i)Iw; zWWC^4Xfu?LOLl9t^$H*J4!(Z7NTd{zE zk-Aya79MN8GW#%goB{{}S7^={TqF7dPH+m*kimHI@R&a(TR}S+2Zhq(`l_T^jY1k8 z0do(SlHI7D?ST0*WFST79$psp2WLSQnn=U@R&v?IE@GFW0Gjx@c^PU2DB)p9+Igob zQ`+O;IqvQ~l1Yd%=uc%Tb3f^;Z@rJdweV>Hd(UVd*(=_59Z#B#2z@frny3?8>kwGR zeMCE(LNVpH=xXI}a97EQ(Yz5&HWDA3rl(j8MyMNTztXqS!zjBM2M_P~v--}uxW!VP zK6YQRPU2|OZYkBSB-NUo^g;`l2+}eeXPh=8Vruearu6M^jhE%CDKe`(&0LXX^+t0R zEjax3Kz16pk-Xa#go)jtdm?v-CSr?;0&!>{04+)ilWHdobYN_Z zK!wEy4NN@y?3@o5JhY{k5XHw^l7ziLPEEO{&kqp6Pfy<>_-y(MvA@iW-hI+EcMmcy zGv*U(nM1=X0}}s^pSSagFrX@=mJM|CN_%40+(;ko^0$1 z!ZMu0UfFb?PW+3S{s!oT#7F{K4IQoYCVpG4tfZIfNh8UlLcXLCttH);Eau)ee zdig0A+DH?>p|9p;Z{XaO5bb}1PJkKxfp@Qd>Ew$rNT_tUM;vg}>T^F#b^~vndNR8g z0br;` z#$%NIR$Xl_ zwcw?^HPo5%>Hq4z9p`HN^cYL7Ok5~cUEd{i#oCnB@@TBVaj?&^Zr&lobad{*hCOfI z`reKkp_(itJ4eYIv(WY*7jYudGAz(cJ%`cq8jJ{e z2%5jPQudiz`}^o59N!ghR#~P*wQ;_)r)XbkPdT4jK$iyp-ojNI2WxdiiLjKxe$83nr+_s<*f#TL}lyjuHZIoIfr zq7bY%zj=M@J&CojhKY1A!kVL5?--qTV4Jk4^vqY;fnPf?bpR5Hx*_n>UIZ&WwKRZB z3!vS-Bln?1Ai{jEy^>NF(~;IfGY zJZtc7ghS}^ehe!y3;mGW(^sId>W7px__w3FYPwDT$6fz^`!{#3;bt2Dx3Qbp-Vayneo$otKuI#R^%>4e7?;r${Fp{<1EYm7L< zz0H9{rZu++0wq%8lP~GApVPOU9vi3}91=qYF>nz!Zcx<*GgM5Ahl=bB1a=I_jZ0%6 zN=sT5d9KYyGjfTPXnpRYrTXwwGY1P;K*ItSYbUxe_^{LiBDR_Vp^U`nZBZr)x6)7k z@LltiF3E1Ur&~H;VJ-^n12@`K-pW>mG|(sPTP%|C5l_D1M-WW{x`|Igt?u)ak{)d` zWEA9sd^KXcl+;&}>F^*dF*J^y%M8L;Yji=Da-2p$z5Qg-?MZt1TdKzvk|xLx5&V(t z9(;Q32Q+NlqFDkwoK`wL-nq(VpR+r+Vgy+PIz6n82D^X@E{0#7(h@|@2f*2dr!?5$Q65T^}& zgr|os^v?6NkJZFtlwh?mTJO-p5$2~IZvbZHHt>t0_XZ%gU}g>Gkl?0}iyUC};33;H zmE43n|ISCTUcgve^)p2=h6aV%qN70dfzlWKiF11g+$k83{lIWuI$ZRQ=cLMUMATez zO8=yPV<8y|#ZY}MCW7p-(u-bw_*KSwF}vdCYwv&ftV-$WMjiP42&$)#v zj79LbnAqWeet~Q9w_gT%fNcubg9G2m%nEhbwiJT&01VL-@@)BBUWSh@!fRDA@%aHY zm$4w8t`wATsm>o2hRA>2Bp%>b^&QxzCipKwO-LP5V?jK-H@>i@F|8Rx4gqKUH^9XI zdHvFRY1?>j2VDgC4h8j=iuKUR`rgJTx6D8C%d2S9DYvPPF^qaY$yM0CoFO0#N(RbRbh;hw(K?RAI+u!Dl_v>zhXW4Si|Oe>RI8EWeE&0#VC zqRV-F9Y+b9^kY>tycDgFxFL0C;2;dX`U!k^P)34;pG{#)R+-r+?y}=G5UI3fq}Gxk zW*`q_f;W|dmOzI{)+2Dv*bc_!K%|g8hjPU{rI<;ZhQ9*4baq{+n1AH2q;!TeU;(}E_!uJ8%B%YYSaA*La z80qwwmCEE>X!v5cmu0U3gZ_a`1=SjmI5-co8d@7+kpF#lKSLOh6fFu3Ksm=T^CErd zUM8NsoN6pUh=F?6;JU~(F~A9>;0N{rr<4OGsq#15J$tl78!?qQf43aaTAAl{$6vi- z{62C$DTE$Eo^JXF@Pn?06xblZiA%ErtBN&6)ruKlKgI^qn#l@k{F~r*1;f{E7^CO2 zkqp*0VtuysJ<2xuCP{`L1Eav`hhUA!u72l#1MaM&Ta!69wiqgEG{fw#vF_2|Lk-Ox zFcm4G)VgY3_@=Ib&aGD9)3ldvzmN=@YpcLgd8p)oPZUQizwDG{|r1r@y z9e^=;d}cAi5{ZFO0>ggkztt6KOd^V50}+X7h3wrSlf&L=IsbhSHm&u?h))DL2V8RV z_K?5gUg=5pllX*y#IzaxV`2H${n|;gXCvaUDr(mIOMpJ&;KIE%taL&9u1Y}kmnqIefm9qNd>VukT zelE>u^HEq#Pqoipo_jke0Qx83N3UBaA^hT^e-=?VupXu-N|yW0L7m&aaVI1MF7n7) z9qp~a4`~TO3^U?kJZe!{3%!IGqrb@sFFoy8>B2T6hN>`8WSK_#;$FSxe@`SzVu8UQ$|NodVo=e|7SO)(N023nsLT zD4IOs`>Z}|G5y%b9@+KA^mGgJHn=v{Ht;Es>9QkTL`N5IFf7`hJ=dX)UPxP~<0~5W zoU4I~=!`I5^eVZ;ZQM^U#_bDdpG83}_va$!$d|z(b~|Cq>llNg)orE5VCvOd@Dk7g zlJCkE>y;U}FPs+D8^sD`J_8N=)7vcRGhn?o&Vli`kQ13ED}v4cDrYC%m}5`;>Q#>c;$>U-Ty;1mQsoK|z+I5RV$;)54=yCpU#?Mc#efgV+e9qt#)b*er~wBJ6};t2Qzo zc6elI<0L$Mk%8KVpm7JhbTtF^o#&aIXtV`deV6*&?yfY2mZ0ZWK+X|CQSgN>17=8M zlkq>qxkI!jE{&9Pe`UY8&kSS41SMeo{KF_Oo;ZfR($QeTXx4Gt!hXnSG}Xwn7p}yM z$pK7qwycRCOV{SCcX! zlCv2ebkB^|-3Oh=k>kx2L6g?00j00Nmo2Jb5Rqtdu%VA;&av4d3kuTo?$C5`G?0jW zvit%##{7kxh4Ze+9GV8uBQy^|^%O?Wea8znbC>eHHbQYcf$#$;eqStNs?_dvA!?FBe5x7Z^SJN3l z5AN3^FqGCUhvNZCBAzDeh4Y>cpOo1LGajL@qSVfTDTEkRh|Z8rqkqp$u?K1qlCwB9 zG9unSwwIas$NG_cFVE5_M>>*0{ zp5VPi8_Lu)}scJkJ1w+-;sV^i()od?Ny^;g^UOC_*IBK}Tvf zj9{7_#cd~wZ(eBQJmjcXs%JWyVL{T^RY-a66O4vez$)WBFR1irnlHFq)|Bz zTbwV_qrFt%cSrD;%@frlA)eI~*t*|ZYk!&Vtiy;xQdlJh7HV29R$;D{N+9qOIH?1t znQYXokG}>bwD0kQ{*%FH%Wv9d9XOs|#gMv|;4NJ6*?8Hy11IeRvN?)`Xt{U{$iPqb zkoN%yl-yaRsF6$watdb=(!w|Js{#Z{jLb4U3CHB=U&KhfO~e%dRRq?ci>k8`z^8uI zNr-W7rqEJECLn+z3M?ooGEor!9$(|}Ul_}PkGGq_N&q9U(v!2o6n&Q?zEE2Va(b5; zAWT4e+v+mF7qAL(kxh-J=RNq!mu?A|B3n3pD zZt^N+i~vftg{djK>SNa~cjzqdUg+>%p7GF1=fdpM+XTyD0!GUsB**&X z4-AoLNx=|h5Ny^(i@x+1}4Ovy-)u*+U^iASY{s8*=1zT zFs9)p@)|F;Yh*B~)XKI|Dyu4YVAD*V=!dr8UOgQ1FV^>tw!KTe%ppyYaU2eeLlGQv z`ac%xP*67BjuI%NrU}w1pdL%(<$9gZrhX-a_Q9knMwTVFunna46Wo= z2*wEn@jHb11WY(dH9ATmTKY#0?WQ~H+#jzIDQoFG4i56a z6FZt$YLuRM%u4$IkFvK9&bvO(d{13rB)`C8Ljc)6AlXuJifxK9;AUtRz0{VhhMJSz zP``wNWp6B=W{oVII0l*(*|9o? zmg}7Lhh$YRXI2Akk(pZn)+5j^+|QCNn=arY3Rg~x@GOh2D*A$L3!-M{>)*p?n-}9B z)X%d<7_q|5x*+Ai5(4)#@8UG;kxu4qKt)<`O6+VPMMn9xIariWE0_@ny0R5g1qN(0 z9n7tv@01nV>v&Fi$rB(*p10*R(?hyswxh599sDmzGRmjxDVa2WM@2gnnk zgahGkYlyxT%!_QxTad5KC8j)*cd<~?(-F7(WII>l%KQ{o8*knsT-uqoV_{a}4o1#y}+VseBqM!s(7W?EmIqh^>e5TcrE!P{-=EUlRFgbKV)_Dq>$U4l|C z_$OuRFqL!#v2!Pz3)Z${?YtgQ5>YDw-m)YP;TF1|kSvGm+92KRzWAYMQvdi#pX!q5 z=luL0_=O@m397aiwJ z=A4MiOEt2U1K329N%8TA`@()fvvSgj#aOpoPThs~Mu|?1ehy6>{+`pB zb6@*GPP6Vw6nV6}G9rYkw%tco2Bgs@EYFUn7K-ktH6NR!Pyk+1kh<_F4Y;s^tC!!) zo##n7_RL$X1}Q(TP3wm*|0-Qk!3@Pm*WiN=8PK9udE?4!!`(n9-NqPDd^Z*KSG_Sf z^Ozt?mT4^hnUeg#5R*YgC@t=R^MMocE4~|af!95=3-L5Lpg3SO+H92$M?f(&+g6H| z#2If{DI8E9#No&v)c`=zgO@DS(+)@#804r6+DP)qPw__xsxo4YA0&I&6cQ*U_58%2 zyf(Q0$bbLKfE_k~3IZ=#b5BW`={C1Vbi_mtCcW;ycWmvq^6fm{MTbT=rP%Dp4;>np zpYk(T%zC+pd zjaPlIfqP(~V8EDn!RAOmDwuB6?ngXjjACLe&4 zCYq7!xzBP_gWe)}4je+*K!NE!jA)Zt1C{3?5__b?HHjmdME> zjtl8HQwl(!&3!9PKwz^s*%73r?N1STEW!M=;31WEL$QEwCfp83PbwcD!0V1tz;wK5 zat&cCRvE)267=_sR`adCgSL7k(CF%D>7B_zO3;@MsdE97Ko=OEpGWC_OMxuEhUd2H3%f*`zF|Yh4C{lc zUz_>PIyvBVoERhQsabjRiuMMo7s=EJuh@?TXBaj97A1KZ_<^{dHag&;d!Us*Fp5QD z#wK-*%UM&Hdco#$GhsNE_6`cFz}pZ2+?v4I?x`P&8L;%*Gn<^sB5rt?rK!0|!AVh? zeGsRpP2$_%Sk`yLct(3f?1m$G4apENo?T^3$jEdw`>2#r+X%~TUrn!>;!;_C5}2U1 zaseWLwQ1d}CT7^_?stB0-K1A&gj{?5!c`J*W5SV*P zdUK(bt`BLo&*X~w`;Zc`^;+tKGPJ|RH|KMw{8j7FSrE8Ml)5J;C*z1(fg|9kjy@fw zq!Ge!2SY6V5EhbRGt)XSNb4MW^3+UL0!^uPWFVVN;-paC+sqXO#IuJFH@F}+LRUe4 zUIE=!d^j$?aSHPGifQ;Nvbj)!fFQ=3Iar}Oi45cpez$ptzv!>%*aqoSgmB4e46F*| zi%t*aU)9bmprPyP+P-jE{Kk@E`A0Iyem96_Nrz<$4NHfmAJ)sXFDs1$1!&+TtNiwv zCJGYMld7mA)0`pNqJirmD;NQk`h@{)jV+{j2n#O_4k2?+`NF!918{(R3|9ycTi?di zYC{TsWyTJ50I$~KBYUjLZ6YM%u9xY=JP-_mGl|W*G4+q*u272Ji(H7HhxD3D2Q+0f z+K3igPH>{&>5hK^z$FkFw;zWT5lS{knq(f=7`Z+4G~0>irb5u33DW43{8E zKuw6@`~WNx6u08=uv`r6=vU}(&)At%=q>MpwJEB|K5jjle{!G?Jml$(TMC z#9l03JNa2xKSstHmkfU4#Lo3hF+d1Y?-(2+La1oos<<8s5li5@ARTXmQ0jE++P>6o zZj=+HAzAz6)PVQwVD#&k&;Fs(!1;89%Y>>D$333%)lvbI0k4!9QN`wO@ad z9@cP1+fI?F+4xv8I{bj<2RD0E%mX82L06nDf+F%{04wD}j|f=UG&1d=FfwvLA3jb} zr3h!>NOo005wwYnPq_X9lWMg%!T*Tx{LV5gv`dEd0$|Do@$ z=dfnHMh@=Cy@02!gCyRTeg%2Z{>M0#FN!_TR~0Cf6DXg4T2*0+NiK1pI;%aW zCmMG=*&+KR1N8o5WWqSWuYWyP`TEboW9@tR?;kzn-p_1(-}x&JN7-|F;OP#_{b;Uv z>VqTO{>}G}?xvsiXRrDbI3zw}0n=rxMtAggzm$iW|)YbIbUbPt<@?_Bnpw ze@6jpR%@NDYr`rsv!t_-v~ zfwih9YM|-aJ$7$x?WQZ^txJ&=p%GV8v;u+wOC1YSKY;%ED7+33Xs#eeP7gCV3nSlF zG2X-GZN*02(Z#u0NsfJ_vtMB;@0JFZ-Xo1 zrA{o;wzNuvVlrIg##Xu>r~c~jv?>^iv-0{2h2R}Gqt%5BnOjea2PY{OlFrKLY$1^o zO69e1;USAE^pg~5uLldIAgBV9q;o##tyhfAK2?Vp^>hD$qUw#sMk~-Ev0(4&9d1Su z#CzC^0F<$#!Kh6EQ)+>o5T-OF@uP7#-opYk?m2?^(u3MYx?&&Ppo8`%UigkH2NH@bV_wneRzD z&BFd+G)D_=KBG`XK8hfJkc65oXpV11t;Q;@L)1@mbgKU?+$Jl@Au}1Z>63jLB~`d< zJ`>zTjEKX;yydB(8nhDMT|W*=piq~6Ivg1O{exrbSXm>`Uh21uJnav#y7j3376 z_|{SbU_T{py!)`ekJ7!u{SzPh%!(1gy}gde_s} zP}8lO4r?@jH3j;E9p=-(ISeG#Z=(&0>Zm{&^+u|#K|czdVVd~wc9^600Z@`fOpZ-1 zjr1Cbs743zL4Fp9hWh_VZv0)iq5_zL*BWp74tr5s43PqWvsb{4H{#Gkt*Gs(o|$Y; zarY*{5KC7fkfcu3)e=35h|2kAQ<$IcN-d!|atA{2#E%~m*6`n3gUHyq0wpzeagQP%Qjq01#YCKL zpj{|1C%^Gh27=UG`9F(rerxOh^MO0R<>Zg)1evjG9vAFZm@N?3#BHB_`>~ZTo!(XC zU3XozXTEcHSFWTPcH6O|PyS8YPq}|iS*zaj0I(Vu5=7u`&yn{K4r~~O9Kr0xuo5jq zz(PDw7G;?cl(49G*AE>^ z&qnPW5uA`BZT>=W;hgB1tEpiM=2%NrIUU`BPMhwJf;UdxT50=m8_ig@rU+rSIgXJ* zOs&-+J3%j!PS~Ycvj3PTE}rQYRLmF{x|?vUm_{4I;Lhqlnr-8uaAvft7O?_3sL)7c z401iNjS7$7zSnu2c;m_cBh+1*X!nhQ1mu(zy>nTi`AHTbc^a4{BAR>e8Qd1&<(O?| zJ#@mcYl-aG#WQbhS^$3ye*`O-k!f*szkM%mj_tS!Fe3oQ(O)h?tDM_n@Jd#YAXjTo zTnw_qS_*iOd>q4t=37uRt8E2)N{bn1zDneOg=8T~na^zmnFa4~upZlVoT#>b8v-Ko zWL2KVi()Yil*jU}&#)sPGVdl4YXkOb)t0%ByFeMBBw#PwgfcQ2ftQNYSF=s$F)`q@v~lk`*HS=^~3M~9H+(6Y;Rf7m3&1jE4td55izy0D7jO1zQpxrH^$>)qY)?} z>(9*Ce9`_WxT85m&|B2k7l}!y9lb9!?KV@!RQ#ic}6a*l#g=C zd(%vW0uPuB9#*cVW~$OP0Sm|7Oq>T4#;C9e^JaoNl9OIqlCU5m{xVOWz72@qwP8I& zcW(0~?{PIj=;@KN^Bk#$&E2*?iJqZnYe-OS^1aRao z$YlIiz*ke#d7|>jj2+3i3T4?hy|7tsP{;jS5tX-B_D~69)DXERmYm{!Qf*jpbKoRQ zS!&AqXK54AdvXmmB=C*1m!3EFWGzt@lKkw3xiq7orzEz$#$%_$Vk4y#log7JK`Y>! zW6ft#i+4n!2^Wg#em}@aw3o4vh|)nt$lTzSmoI=|%se4-@GSR23-MH^5Z0G?u|b(P zvTS>T>aSr_C}*b^XV_+8x$bdaE zLg9m`CM&tK8a+}4B;~h}1_fly{J{y{jpmy7Q_H{1nluDO5=UvJJfe|A#m1RXv>nW@xave}JPZ$HsA9U9&dl^HgeZK-KBJB^C21Pd*OO z^`%3ivy6!1ntyj>=hb9rw8NK}AuN-Y)Gx@RhWk{#Vm;L`f|-0e1RiLFM0Zyi1)yu+%D zli7X`AuZvP6DWDyc}jr-03Z_W=Pv9sPtReUr__VfmgdHLk5%Iw8ob^?xlPjc-N>Si zPH|r2$-<{}8|f2jl`$y`Lq_v~CDwu0sw8)Yh4frJeX3nh&ccvkS1@-rBr?f?RU)+S zuRKL9kP=UAFU%MNBmgf{c3s?DGdNT=P4-OnV}*(IxSsR8X58i*bJ z?PYfiLPBF7*OKAy*blV_5JS0z*D)ePa15CZl#1tI76Nt9rdpm!9hw?e`o}s_`d%cn zHvO&5zabT_V~S;~A#m5~A3tzS#~s20Fad&j;5SDUdg zZWg{+8WSnb{2F^R`;JNza$92B&I`EJki z-nzE^EEzN^;F__v>5XhwvA&FkIQ8Q`8tV1qyl|aTpC9AW8Z4(B6qg^Og}ea?GMy^zHazwN7rx`Y+5~)|V-C+8l z!+?$B79L)Epa_VDkANF_D{i&vDV}?Z<^X7Y$H5dlO5p0@79e;@u1h?^3RWkP14+{2 zeiv=7gwv#A2jPX7LZt+M&mxVBC2M%cBjS+b9O%o<$8}zp#E#w8f%Hth-m1UdY^#D) z55}!3tTUBEEB8|6BnyE)OK24`)F4eV2(H5fnqRiFUWgu?cSt#kthr1i%>YCT;7L-j zws#d*rzO}ggPqCNl8OQc*@cQLX1dlb*t5n&0U>5_W;3hGq-;=kZ54_f<~s$pYVuZ(LDQJ!GBFnBAoQAs*)97Fx)6mnJ}!bGRvelPUTVx8-B(%fCfZh24%X zX^^=g+n;(42p*yv5coOq#I4; z^Y856q?!n@K#qQvNNmgIzMjX?EE3{;w9h)&^>o6OWk~#{kQBF2N;Xi8Fs$AL{qhCP zXK{~MF$kHy9tgO0fFC5gom>)qM>s|q|Z?bj2M1S2oLy3`?k`L-EDb+M$0E8>MR4w;`gvCsC+LpMzH(~Zq(OUSJ0*k9E;lD~|) zgryYff~-FD($FN(Nph_AMVxgsSFM)s;zZLFC}0QlD#ON&9mrgaazijWC!RyCIO#w# zrIPP(>EBFEB%C8FXD|IOG&sQRp$Ttw9 z`4j~XReCYLNtx{=Cg|&?vj<18;er60Jo$Af_u+wu0L>mxa@LwBYy~M)-Y1N~urb9> zxYc|%tPm9-bNU~cyJC$=ZM=+Vw61yFwkA9T8CHV{`cZmfaqDRLxNYtpyL~c!dXG>W zY-Np_ToB8hFHb~o6h;az+u96CQ5x?_uYZChlA;J9bSZrn*-Dk&kqg>JW^9r0V=?7Q zyJuR?jcWPmbZ5cRe(`Fg);QSP$$ULxZzA1%p^0`0vzQfjy-kRCKd$}g(FR!$s0y&l zDYztd#z%^J$myUZ_OH~sC1oaSBv|-v?GOb+Fi#+h9JnZA$2Gi}{R*2r%91zYHz9^1 z76PDnC~AlDa!T*T9AY)A$o!SS=YCUY!8&55D3)#Z2s~ z`fWNnnj0jM_1*Q&YOlBMUeydo^w4*IN`{K_x%#FbdK^$bH3G;uduD29HSzdK1^R-ufQ7LLB=(i|jHqY1G zALDT3hnRJMu_=;j`B{P=t+|iM7+xL&%TY#$l#r);WQY(81Y=Ne@cl`j0D9vL(dl)0 z7lp2bP1OA;S-DShA(WZ5*=9mC7d5%_diH?VC8E)ys@L(Ucu^1ZmVL7!DRR9uc zwG^Yt?$~~oTT;Dd&K=9NLw7lQ6 z1aT$z;<`2VI3oVsTWM5&i`_Wzb={|608fEL5h6%thV+@WMzBEyXEd65^jVbE_$^{E zOcAM4rS!DQm>!D+$!mgHDeEkd6l| z9R$oRRy~U2Kl}RFg(qMLNZSrA`H;2=Pq<0|RrsG#`c>Z9yN>Zsha{kIwkCKN+W-sP zMk&>)O<_qd3KS-a7#k6rSGPYh30RK+ADG%z; zmb0E%QTUVDUu@rlRNjiJsxoRbhl*OR6auR7x|IU4u@~OSgqtE-S9lS@yz}YdG}?){ zyH5SaGCY*SXVAXU$APuRsR7{Y+B za#Es=q8w6V+{%S4GJIlID1kkkQaXgfM)2(7pXGSbum5^2IJ?MLv--`B@zl{vN;?q! zwm8TzEiRLjD8Q$VO9gbE2@$FJ$rl!*nj%Y&BFpP##55}0dH6txq-726qo$m<<5nYruj8O5kQ-%eu^cll!4g<#3ZFt_XTW zcBGZ-+Enh@;w(dn*7iSlfrry)CRufHIjGwB;vCOUBV!hyTrgdU(FvHZ z=Sbkq;EKAP!5Fz^A>pg7<~Vx3Qma4bLqmvUU`CO){5{8$@JoJi<{ z)m1tY@Ikny*M;tY`ooMW;Sd^Bp&6h7>`*{)0R7LsKvyyj5dQf$KI=ox?LOM5q7B1B zbMj=eKrZ0k-kq2sI=z$flRgE;n3ki^G35c|vwu^mG{ypInhrp_x}76UX4#!$-hz+{lNmKN9+b5vO0?NX~|iJ1AD9IOLUf z%x7{JNJ~_d3|DBMD#*D=7r~bRe+u#eO!4V3NY#WHHT^YAA&}tHYUjb36v6}@PHlCkP#tnri(S_DrHu(fQ2{nh{R8|)SgsV`97D0GX-nj z1xQ-izkScqZFQ@Ey6p{cSu{}`$=KS_lRy4VE}MguqU?h%}qwHcZajLExwJbVOTa1fe?gLgm+ z`S=2a4)e1(%F$#BBfCGMW?_Wxs5Y75qc&_nsZ28_;f~L-6+p!JZIiqo(wHgUSQj5{ zv>6k#t@Xib@*`g4bEXHK!AJ&r1&tZ>fS>{(<`+Ix56EN8=!IQJ1}`7>@UW2~!*tG6fw3MV871U5oI z+rbbsqbu8=8G?C7DGE6yu;q&4(*cfXal7VHTb$l8+V-j)kD4;QC1q!l?o z&l#r)UTWPws~s4H*8o~7pDx;I5>r=A8*A(1Q6n$I4w}w#qdMYHX<~ZbgiXhWjO{R~ zoB*en+Ai)75lpzke#o6(nixLgr_MTmYf{`6t#zIztIG=d+$q+$B9cx9M^Kpb8MCr` zxO?17ZYG~;(`lt)oi_VZ?_vB7s-LFSK!b!#pG8KJZ1~J#%OeO{x<~F*yYy=gbR9j2 zm@1sDV=O{`$jD?FN)E74oupW3A(bPd9hW<~n3kfGDpU|AN;V-2j7Y1=0y9&}7@f6- zWsL}(Fi-=YyZ!n3D$2uV?~$IgQbS@*Z*t19F|QT({n$X z0PrsnCvBCQA&g3aB~gfCG#IdqS+;p2R_P#Hn1?K^m{;(pK)q*yNb%z@|bgKCQ^tC3dcpHK?b&_aO)xZThz zNyen=ooTKN(^~mi@>q<+ny+%Ye%aj%zPFotGG8O7gdW;=Q`jW60BZqp6d^Tz8BJmN ziAa(2!6>fHmECybBQ}AIsz=DfZ2RbhaPm+&+e*oO)GJkTq`)P1aHDD>UVJ~4QGk<3 zYPY$u!VcYi3E>1HTuO)qq+AAj9AH+}*ibHCfe=FX49+2?1<{i{6NO+)MMnMs0u}A_ zzLR&50Hc>91!jlZUs?NYHy>=bNf9a`6-0CufM79swshaO`Q64NznLCIUeZ9mD)m-n zrwRtUc-*zDUtxr#w!CPd#>{86VtxS$F8YbzcrRD0{npZ7o42E90v0VJjR#Qs$%=W9 zS8#MUK-_J8c56kh>{(RTZ{4GM?cH_x&HBnc&mU~^qV zvv#YO=83-(`EzhW5KICcpbo_t{4zs0RG7A-O7dY%U9sP7>aovQUNhc%PsKD~s?yuN z9S$i&n&yv_Gb0Ct9R|GFI0PM!e9$@^pa~~LhU7y4MXe^=4f8p0Kn?+A(MR!}rU|d< z)G5d!iDIc#mO`b`>Uf4T8pz&k7S0GWSj1U_&qb|$3dF>2zF~B zb89nZnq5&V_0sHheF*B`9X)i=_rU_p55&yX&z(8&rExr0G$6Bd2?r1Te3X6T&d=c~ z{?mhPlUw>=3Qc$+;HMuQw||`fr=h6JxDSLKJQ>X!=Y0u4j^rJI5Tab#QCJA4R*Haq zI)_w$AGgq?!Bt}f)>M@}c@I%0~ll9=5xVFql{Xhhh<70=1kGU)bbP^~6B&q>C#aF2z?=Psn@2P`AxV%`_lo_lcq!f5N# zb&%drXw~6|z-e;A2kzs&<2>*ydhM=_{Uj`4r>TvBM%4@0PSU!+X_lsq+}>MO zyha~{{XuGWFa11Yn=o0&(M-aCV^@09`%@YT+h*rlEAA4dSjf>c@~K~fNHtCZ{BfDD zRsapx0#^A2>Y>WUm33F&L}?PhcQKxkMRmbSjSn@gp^TANok;#3i0U{X|Ei;{~ z|G^AXcpiE>!azKd{z7>B9rM9MWY4mTq{gb8N%YzRtpd6{Dl=lRPNIa&3@OJ}I3;sW zfmT)AbP;3tO_ElY;>Yzz41MYMRnDmV&V-kng(fN}!Eej^V7LlC0_lKcLKL}a#6%|L zkm|hh1&hzXLu12;iM}f>M*Hhq282!zF)?<1fnE|zP(??+0Q+R4??96()#ag8??TD< zRqse)a6FWJmpjJgu(l8MdDF3#Uz>R~orOqEa%_Vqzjt{~aK<4J(h@kVmwzzx2jBX$ z|Hi<&V@Dg0aaZ3WcXsWL(|?GF8ls5ikB$lX%7QzY_pyr`DwxgTh?sBWMh7oV!JJGo z_JL(i-UHBt0*5EfBF5^Zb?jf=7%%W#nbL{eg{Yjdj`6i1eGCv5x0X%AZELwG;Y>3| zIUYy?yW=V2p_5Q^I3!Eg`|oMRg@|`}s(GwPi7Cv%6k=JLE!T07YC?X129LL`7p01| zJd8nl4^H60&5i59HiXIAC5A98B{qH+M}Zhq;DxEF|A_22I;kVo|xQ~6(e_(%0H+F%{`Dn{#32K#$=019dMLOtGQkZ8c98qIh4F_d{s6tj- zgk*JDnM1|-Mw&auQPb5$hNpx|hOPwu26UUd`QONq<5t6}L%-Db@TRKTy z7584{z7eFmVh?jT z&9a%&02@0nD6{F4?cn>7tqlCYrSLOw5QwTFeEc2a^urI}u$e|7ShAuqpov9>dt$*5v=Yv%g)m9d zGgn1>5*IWXW_dL(N+Nik(AI)xNT_a+$G|sSHk82Gz&zF)BR7SiApr#ql zv~I=OY*J?KF*%QGg&>`KEpDGx1C{ATG?sP(-S3fE_C}fD6Y?MCr%{Wb0`k#v@%%yw z)31@(#bLlerJr#Z&}vMx2}t6p_;|T8<;5WemmmU#8T^k_}oGhd~0MjT}0}(i(bcN7KWzYUwi%xqMCa zOck0!!o$)`GsO{ahy4LcTUP+qL+wagw6FLSWE<_s;^w^iPANQK?L%ITpcz6IUPge} zOjg&=qA>S&!0b8L6)=LJo!tbPErw*l5cCcATGE&rHEcmCm#_3{jP`S*PfU)E!5uLi zn8jZ`e7e4qUM_%I*h_LMxvt8$FuAYyvWT>~pxA5=Az5u?Pk^MYqGve3YdOt~y3dgH zhjRkpju|l3UlDy9P$@HDohNDKYT`S06;KXUPlrYCxyB{IQ4&P}&V8@9N^ug*!B?)Z|qPnv4^Y=1r zwq9vQHy4}+*%&BG?~@+`1Ob?6P-3$QuSzFBqNtXgeml)$XS_(XZqoiZmZ`3J%trg0 z7CbYM5|U#S<6E3dSU|Lm_u6t60aYJIOWTHjmM^NJgVc&iZCrgl{f(DwJ+lj6iGw^d zaT^u*N1Fpq?kidy+-5yQ24D!v2%tk(BD`lfBM2eN%YX8t0&0SAGDl19L(<1ls3gCR zk{*&-!tsKUOnLpntdgXYnWM<2VTk;N$)@}wP(UCQ*p`<$IyKb-hbrocfSV#Q;0y~xs#2Dcun{pQhAsVCzDZb1$N&~VBt1@;WPhcRjf z0YNGu>XBG-m;8wk`}$Xgt>C8%(SGpI{^tcnUaw;`3FljojsmJGa{B~*R6U!3Gt+8S z=@d<{eb)^)KfATgR!=wPBjJc@i8TM+{lJT(w|x7Kxn%KvXBm}c+ZOLR&A{FT{fj8P z@23aR+l)5*smREBK>t6rI2pBqzU20}pWgdZpDb;yt$1ge6Cwi$l)VH6S(#1tMH5%I z{AU*VWa*k%kvrFrW4`5i@Jn%NXbvs?D9u0cu665p9;p`6ut2Bcl~fH?#W_FyZ9Tfk3|;J{;35yCUc z?iQo820d)qd{+6;Xw%szx`syWFKZO@p^q)CSX&t6!wYxz;{8d$z*@5{eh@P<{s3fC zL+isfRKq#(KU1ZOJt>}5%m`{ky23>v9>sPwda6)dyp$Zj{03;s4>Egix4pBH1f6e! z1strZ+v8B+77{QYc$qUed_eHV>FA}o07$9q7Dp=5bb^9sTmq%Gspj8V;m|qFk>2`u z_I3z|(!U0ar!Ih7VB;c8VA>+pKfkprvd3f3Oguvwe-yD$04WV4^tY13gM#oOlSXEa zd_BI5J4AianS2}v7E;2|mUPwgF8<>$Aa{pFf6KdECp&6%k)m5gkV$UL_^(GybO0uSgFzBXz#{u_a!t9xJ#>;9co&E@-qcq=K00fuc}Q+OsES3#nt%#` zP#nkGbWOZte$Jkeym5;?8_1=NEd^oUns$66=NOpdh3MJRR#^tb<$ZxcCOY^Mx4rZZ zFG*mI&)O`^W~o_w1h+W}EGG*~6QYJzRydPuhj|!Na(SYun_sewY>g#h3FPMmS0rjF zK7-XY%iJz-liOK_%JjGT7!vc-Oh{tP#x~V9$Pt17;=+Qx{4%;ciWMrr1vEH3^MZ^s zjB&xDLrEyxB6ETWWs&vgmY-ERU`J`&LY=Cu}3V#mwT?}lCMiAS^ zGPW5{CZD3&pIR)t=pAlsS!&2p|G0fMJ8s3yxuW`?BTQX*o2quW8IBTNN za)(>I!6l{2Biej6tx^ddih3a@aUtfatf;j4;w0eV1(${K19>NFjs#FdM7R@Cd3L}p zdR}n>56Ckp09w;tTNs5A#wa|q6eQIHWU|oAkP5jgR^z}nx@1!z8%`VJ%OCs`vVHX;CyNEE?WoLUR-aB6}PX#*yI z5culxg+kP+F~!UJQ7bJN#+BBG^$XD-^=xEJ@ucGM5>_=-W;uU*|rK; z;C~AHRB%;cl3eWbqZQ3+P7;qMA7}<`u+@EDw0?)^Z(KxEdKjPze{!8qzw^{Hr(ZOmtEd1T5}$~ zZ^0XH{EdTXpI!vGdfKAFuvG4_c3Y$hj~%OaPn$}~0lv^$FGeXr5hM&Lqd;IbO`OF1 z$cEthNEw)xFHNqK0~(GhATx>Zb1Cj@K8`8FH_mD8NK1ZfkvR!su6n&nTMP@f1vAq9 z5&G8aY?)YsIfGsX8JNNHF*ZKOP=SdSj}b=j;y6{uDs{TwZzakaBdn%u?ZM69A4`lx zTWD;vFF*+JDXiH@vzDlQ5iQ|0K6>IO#!O9pC&4M1NQ5v83$zMA_F5~+vK|G&o5u#A zO~9AvnXp5!8IUCStc(SK5&5$7CWN3JpllAu61}o;Y(Pj^cyB?B8SD8GAd%9vW(ko9ViO=AbSQ9 zqYEb}R%U9JhHo-7jhD@8mQ%?6@#+IkU~A}Fom%i;ym>t^24ODvtgc;6jsLO-pm@VO z{i#n&u|6wc;b4zKB`_2@G_5I6NMcM>gh18;{ee=agMc|0RZnxPsST!$MBRpV8T7fr z?6FG%7w;RYW5-OB2y5vu>`G&8*~S^FuYVA7QpfiobpsKSP{Q9#Ek9nI)5XQ8$`#;F z*>4A`l9F>k6)#jJ-%?At9L%(=oa2zFg8&i?KiCI+19q7eneOPl89TP}3RF>=Qd_kT z*5NbR6^{l$3n36qv27YJs-nR%q~&6GQr1r%57WBRP!ZE{J7|TJ0&KH(?q%m+ZRD)VCJD(Af8hHh)zVvLvH?694R_I4r&P(=Fj1O z>Azjqewto4m56;y5+&WUGBl9NRnQb*tNzI~#l3|ZRS3t7zs~0!m1dIVZB$GJxYxC- zY64r4s)ais$FjfqeI8I`V?7(VGB;gbT3e33{Jj}e&kZ1e>xd{AON4QvVMHjbTJ}vP zi-U&-fzqsPQ~ZfZR5qoNBLPI0fS>w`6$2xRNF^E0~(gQ8Ak7*L=s@iRNMNIC4>hEIF~NnY}9Er z3kX+^$@Wj&tr+6qaLDv@%(gV*g#EE(**JC10P{e3aOj^8c{VjEj=(X7>pMQqinlx- zPCo4*K4i}1Ik?FAkIUjBvoI6O+6xT`N}i+o4)TaOul&|4DdcGyezT%F3gy#ILI7zf zp&odOMpLM_R*=S3H1{^89l&f7_;%(u+)L|FB05@N1FID2)1TG`kfk~UbJ%KNf^M9f zg4pYpU&_J#Bkz2fn^q_sB%}Mb7vo)5)m`<*Zb+pCXWp7~H_dwq);$ zkO|ZvyJX#+c*(jo6-ykGh$^o(TgPTmn`LjW+;==bsO9#(G?rvA8`IPPG_R~(jFL%N z6Z`${`d9w)Cx1re+o_4$_~cM+3b=E6HRNPU)MLDymGbU*`gilkadlqn$!)tR^1uF- zcNJeLSrSbRp1k9s?>_L?)Z-=X_!;$6yE&7e0Nr+UZ2c{!%A)$-wY~Mm8}B8{+Q`gV ziv7V*mi@2ZR%gnyTp$%z*)3Gx#!uLvxaJw}^W_4Q z2u(#(rTIP)%R8p#5Gs^LNtmZ4ohPuqW*Nq?I`(PNKd~i=>T`4Ym&DAqjbKEuG6_$Z z--cm9M?$70h1b9$Tp6?wv6Ug?d=y5q9s}1==A$AvO(@?d8a|NOJ48c;A!67X6w;>R zyw0mJuI7+T<6${z3P%H*5Eqi)P;q6l1nN)gks979I&TWG39Br*#d?gcRF6v6 zzZgE$tsXa4lpQJ|emRu5US4ghD5H~F4xmgO7k7Qg_<_Fexfr9CE^wKMEK~tn02-g* zaP2PQX#jTJopWv5_v%TIl2Bf)z}ikVk!v?I0ZSi=m%z~(R-4glR@WLGxh-Xs>8ju- z1m@BKjkq+eAf;b!0x4?pvVtL!up6?7opSxGm;-by!4e`Tf{C&Woy3aMM-j@UMez#D z%POCHmIZ_#gVqG8pYB(}B>qfyE)nd$K)P7b%O>!Bx7YTHCqquKAQ*8mkHX*clyb@STo z^h3dZ11Zmu!5l$!#Z&%o7eZ?$Kb7O0&PNXemez~ zy;L87F0Cq-(X`hTYhD!s3b3i5{FX}X5n=YWHPayr1(Wd7zxUplAJ++M`BTJ*{ znC+7Q2bj>%?JgSa0wxCMP2Yi#$X4mU`NgDs{d6JNLF|{guRP?eCq;TmN!vCOi8~;v zkeU}4+0^TGVp^*la1P{>su!6R@j4 zyP`3H=4?v!#p?%4#SQJux-6{azjQ`tA_|Oj*SJgMH53Lk_ZM*eRVfgH_U8&!X0y5> zm${~_^o&}f&1okoWIz?T?4tbp07d@j=ondSdm73utw?#s-lS+e>)fr}x7xE_ub?&1 zAO3Ltd;gf0N_lt3@b#(?eq^gamS;l z7DGjukgFyk#g(7HewXl6fj70XM6TUkToPzND(WIW;yhQOsLG+|UEkce^bc(39Xz$( z29_MrH}3`H(IM&R^c^3Z8MuAVwz?gsZ#c%BwXexPK~FL0ZvOR;Q0Qo$FxQG>qW@+M z)P3c0JXK{4P^=($(#+F&h6g!CBpM-yRvJV^1A)=7ZfP&M{+VE|{v~7#>Y8miLNI~E zlyHsKY#1eCe((eoGB!t#Xk=DX<05(G$*?7Maw_PXq<0+`N$k9{(#wiZeg{OAd|0Hj z3!pP-2cn7U9(fQ_KyTw`>kBw>|n?@QK^v!r7+)Xy?}*o|{{m=9ew?d${G%yb`t%<{P z1m!sV+r$^mtA<``#pggUp;UOrQQY+`8k9OnQj{kng2M7qaj=GoX~5;MQgqWU&d*L= z%q)36E^Tgo?=b483FsN%L<>VYK<%%3zeW$V+Q@;`fNLk6WKwI8ask0wRiOsiV)Ys6 z3RW(YMnd$6OA7P{G=Q7ejkJ>MXoiR2c9PKJMTcgk%#NFn-^|J>7yG~q@`$Cy@TIdZ z7hwyzHe%ru0hrvxUY@wlo6tUqw+`q=*O9Rz4rf232b%GyO2oJ8-RuBv2Ox+AQb3%z zA;$zwJj-554zUWix1beqH7@2e%(`&f;Toh%Q7jL3^noWk8$Nf+1(<>Nvk?J1C61ti z&qYO)mBnGFh4&W6Ru%J%nWB-L~HYB)fJGD}vevv&W^HoSizFjik*` zYU*{)TL_WflQG;iXwe`xD}f)M=ly3vaSh-{*eOS) z9GpQKTTrPA6?$e zid_yOF2fmmUD=%ACR8$%zQGDzIW5hPq?xv*RZ&qqIcL`Qo|dQb+R1qPV04eaT-0as`yN zzd{e~#q`kj5B(cY&0qqWn*dX0P$&oMBuGOtg&^uP@J?v49P?&}rdO_eJ#MWHTU%Mz}xxird^CRal;tE|XCp9XtEXIb-F**3VP!o_-5L1|q1}O#n zb;;VXZV_s-u>hjVG1(HC=MyzwJeJMk;_2ud@SGFPzKHHtB+)P@b8q=XcncHyQp!$p zjC6_`JjjL=khY{L|K64phWM-O7(^~)sY!iY`H-z)r{k=#4)v{M33&{e%$81d)hD*4 z>!Tm$TX`Reg3?Wi@BYi=D9}BTW8x*QE@rLa87q0m!Bmc0rSM4~ zA&q7uEDnPok>2Q>5!1>zgg&A%PS}F2^&im#qWO4^*fZ83rLDl6)I8};9TpEJiO~$H zrL+Xx&baw7sVWs9Y|72mmG|eO-`KeehO#9LTIW?u@Xo;x)g@K3Ue=YCWEn_ zE=x+P`BDRxFBKBpAkgf?9iw0a*@)B=AT>sXr8#66)3!Rn5~PF)ItSCU4a`75H?9ns z9d?oql#mI_0YcYYuc#V*Ij1s4Ue3rL@}KXYTE?CFbNz!; z?JDIngAeh{`IhcXhe*qA#=ng1gA439!t1>>G6OgXhfNBzS*6#l9pE*RiFzQZun_(9 zj_Pf~@j{y3r z{Lg`VPg~n;d zl}LITBavC&HO_^@2h$Zinc~l#+WYyHKPKtTUFAkIKX zm4M1joatY-BOT-Lf;bQtjlD422Ox};U}KB}<(U`dq#2SWEaV7@vslP;*Zc?3i1(w4 zmRuUtJKNE*oV8JIB3(8qFqe%RTdLJC?=f<~RXTcDkY0?}apsMHkw8_}D-h>2&zyY< zO@v6i#dLC;w+z7&L^U*L<|Sx0Nh%hzg}H$B^qOUwTVXZL$7%1_bzetK1#&ByMGqc2 zY0_p2&!su>qvmYu+aF!KasaIMCb>SEcz%dYQh}yOjXldu^K=E$!J^|No}zf8 zGN;ve?nT6zP~@fm}{?QDmAUYb-ar=5wdNc337Z<%tcPfGcWew8XM*s7V# zVg=U$ra)LC2mTZ5`63jTubn~hEs7{7Q4q~P3l#(GI6M+})uh}-mJ09#%_Pahrv)y# z-KQ>oTxcjJgD8=5e;b?c|5i3H8w23voy^`ZbzXe~>()<2z0i!1F zDCp@nVG}?_NO%!0{S)5+Cxehmy3@?+(@$TsXWPX0KSP&+@)=b$#l$$FlKTs;GL;u( z$K;Z6j)PW}G;-ShQ;b>0AxSdP@r7_9)aM)L^RyGORR@w>%M-SD?Klq|{)0(TR=~h*bv6`Srv1`DBD0iRXW{cuo6^fr zqkA-1Yb6}QP=I<+b|MDGhRFP^LrktkOGXWqQT<0>!p~Rp_Qe9@`rjj@`%?dNUnn&fn!sm$9Uc(tb0m(J&8V;$D7KW}c43Jq z`Z>jsc3BOVL6T#HTcki7yQ_2s0T)r9*%+K`gL$oJ6qYdd!xKKo$t1|e&-vunzOb{K zu>~|BB7{$>g;7{XTEIzGhii$-mJDg2Y2Se!GJtH=Fnc%gW6UQI2<}R90fIgl3igL0 z84MLx2uJ6%ZUB_5);xy2t?r~B)ZdYB!xaWWr=aF`6bb)zrFjF72A^D-`I^`JB$!Id z3pi=kg5DGJ&=P^ulr4bKuzIm}UHET;%6YN^a*?<}n;75;*G{7y!4=^)=k-=|bM(z- zVbw}6fmrq?UWNo}$vdr2KG%G910~U*FCbgK>c(0lkHQn&mzEc{ekbVXrcE|&Dphue z>Yn_=;4D@OVE&2}caPoG_1^mBCUCgp!a)8Rj?~WTP0xF12?>x(cSkSB*37|u_gz6S z4&|k=tSZXw$xZfU)j`J-=nT$_J1HBJC3tvQ>|GYD@R) zhdc>q-sjPU4Fru?Y)OLkcX|6>^Mp%-j3-M|0vl|8h{TYZ!4Tinq4Bh*xUa@Q%=%tH7Fu`;vcvZC7Q)&%op&P8%6VVGmX96kIU8fJkJ z!4X{74Hu)mhUz6hKYTid{n}`(*Ick>ejtOspBai zj}Dttw|dKr7qF9Xc#9oh3zSwR(0y|$2&TVp{YRcC5dci(!iCi_&U7W|JI>Xha)>kH zDJLf%BCOb~rr~QX6@Cj^v};!;nP8qc1_C}}N~E0q!@Cv%+*k+P^}6N;+CE8i_EDAK zbu_~;ba>WMAKZ^#_$53Ql}~{n1uYb3U4<%2Z@@V9_X3x8?+<38cJ2`xM=qwXWwJXv zOz#PCb|X%HM1W;xig9bQMDx>sF)vyaS2h$$Rrh!_8gyDXF(oC4;#m`c{HHI!NX`_o zl^jf~hw0_U&M<29zQ5U|~S^gUfi;)>1RwutKQ?794d8`H*mz!FuG=hx^8u8o)gY+$kaf*U- zdrPNnuz6I$Ir8E*xjfO-uoUGAju?63a>BbM+NQlNDYYU%0c2nBd@8EwX{;3Gr5vSgQWC%HgDBe5$yc18-);7V>Q7T~EE~w)6SF<}922_bp4vBc z!cr&hm?d**9K;adjwp2d0YIcA2W|h@OqWM`Yrvh6sSIBwPjYH#m6;W& zd`K=Qf%U2#nN*sV=EZd&7t6v8GwR_T0V~?3z7qVt+HWm@ns85(U|OTroajtdM)>7 z!#qa2FMoCyk)XF-Y25O}_Q;Vzo0Fq{tdVE}yUTzR&5Ko-Qtt%@UDB~~4eu2+F%b^Y zi-ZGIWfo2xmaAzo9_nD3kuqC!o}Mr&p?pmPA9gZ*d%)68OO7m#HG)PdSD73jDWqUf-53|`AXDyB zgoO9h{ZZxih@T3^wRE1{2a|BHRD-9MK^=>M1>3Sb$aT9~#7pt>cs%%%Zsq_%eL|(5g?5zdA5v!TAzZQ;z{7xJu|wMa-$F`?+cTt47mvm!ZvV{U}x@C7l}DQ5I)+j zLZs{TZObVv_yIBUn-nmQ2t@S#1oz~Ce^f$$C<0;gBa4&S*z=edk*NW!Yb3! zELs?<#Vm6!8~P+ynge(+a57MG{(_`a7_?O)eGUx`hvS%vn_Tz4 zz4thC$dzm~NQCTsC|26>y(j0Ap)>OVc5gY=h2Tksdoli4G-AE;JxCRZ?2N>6A}s)tJf*tRN+zW(&buLajk+cPag2PYyXjszn1SR zw~xeO*I5VOkzdTfUUG|(puve%$mxCl8EGr4D{nxiw%~hY5N~5>9eLZ8H(phTqH4$f zm1mWz%P4vY1hih@YKApO_-i$wblJn&a{dA-BjOfBrPv274ghE}o#zY=3pLIvF4A&~ zN`z4swG36r8nEO$Pw+T&YK6#_Bc^KQNhO*v7)lgFAcOnZ+w$S->LgC9N=?y*BfVR~ zzN~;jl4ft|;StiVXxBj|2yP@;WX?4Zjf|?C$3EE=AWTT2ji+}drVSs1Y$$5<%TErq zFH%^acs9T~v(>wh%0o9Yr;8ocjl8r2@r;&(`sAaSLzckaG{-}=;i zjM=D9Is{ocPhR?HHU@fG6jjf7RSM1F7BOxqaN&T$ zxRIyOIzft8u@gU>*R^ea&7L=tq%1LGM4nW%r>KTrFnCaDCw(Z;C>F8g=Ek(8)&T*U zVE3ExNY$BdxU_9;=#W{AR18Tzf;j+)Z5f^$2~nk2x3zjZS=NZwLzI$6KzHAKoKY() z>yvT;KLJzGgi{}fW@dS>x!=ZdFCje+ioTA7tRgm@1o+4m#U`$>ac@e#Xvjlt)P)QC z4wsze!3STvqHUB+D{nDB*HqvBaJN(wH-%RAB$40U05ws4cN5GcJ2bxw?aR|KekWh1EY|GB#k9tz~4Qt@prPib=cDulx9u}?3MVW|UR6S)9;&C~ng z$Y4U9@~a~)5xH0tC`=Q1!O{S>bik|&tt7LFtx;YHcDA+yvyu`GoADOD$dHFcz(<^HB939I%}C3i zo2MfW*v_YJZxMpC3~V;hVgFx64qQpzSZ;a&CK5QrT_;9b7DzV%ovi^FqnH8IZE#in zqRsXNdjMc%{(8&O?%@MA_hgc#p1grPVeMDrBZRT(7W!LiF-L7nF0Y<>!s4&SdLQ`TGAZmEZUTyB! zFzRh62MlEi0SH~mu}m5lU@epO#L!Q)Hj1KU&xM4TDvmG-W*%_03BoXnvUl=P%nU%` zZhQ&n>O}|RN&|UFo+pa~OvxRPVtK&HqFk6!ZwGL^!+WExkE zeg0v!@V<6G<$@u~LU?ghgEN1C4C)I%8Ju}D#gw<*c;i)jZvX66GZr9!y7G<2n<1Iz z@A#k|8G5ezWd~G{9C_1nGr&-n`#Bzk@?~en%~~!~c=%ZBQby!?i-_P5RGB6vc&8;KuA1;y1{3dvNIW zLrDNyh(RZs$-u4X8g~aLgZX%gw$K(p&zz>SaWS2 z!U1xNt(hTDP`o!VgD4%VUu(L1g0xSL726a6l_G|d;77azU{yoxVIc&Dh8X6qB9Bp4 zV6{^JJv-$Bhzjv&RQ3_VIn%(-nSh!T3EgbkGsSnLY^@T;Rw)Mt_b;6wYP<3S1RVb2gH*n1 z!vdHlj+ZaBL*|7y&TZkR^I-G5myaGI2N?)nZ>a$MBupjk$4FS97+)MD#o)Q#;ZPc+ z#)MFocOiG;F#-AyjBr)lBx&G)FTSNcHqQfDH&P-}MNApOm5x(rUVQ|sCqjGy;YzRN z>FgW+&-}`?=Ji1t;y8}byIq=bs_->vTFWx?Ve`1nQ;z`Jy4)EI7sYp^is@W{2ebwq z1(Z43M&`?rx#KVDqX1+0z&(oq>fm(86@sx5`M_di>fg9k2(ooo{l!cEa=H`&r;~8AH_2YGzm&9qkQNLX+p+%S1S01z?eR##GPc z1G!>}&rUn6hGkQ|+bK%~=3ZwD0B{+|qrCy%6y(0pwg@K1=wl(yr7D(1MNM$(0@hN* zE7y~pZ^$bexf2<|##SjrcJa}79)R74Z!fz`6|RlbL+kL`)awT=%mpDZU;s`DE5>JH zMr#YOu5JIA6#NO}2^5$CFnn6EN3I>#9HMQ~l$?}C*-=36j;sJgrH>fvh=8g-Gkq|3 zMss6WECdG(g=nS~2!va2J@DU$0kWQ^rK9KglSAexiphGD%j_hrJsp#+bi+K0qA+LV z^i64UDDP@2mz#`(2<&9_YUVMLG$IRMUfKwQr%;NBw`qZono^NfTFG29GWRj(_%> z!Xic11UFS6QZ;3HQ3I94qDpY_%P)zZc%S{03R)u(m#zjTB0)?7DS;MqS3)vk4_MOhy3mOJ>Baz(>IsX@EAZaf&F9-xXaSL9VrA|IX6+u!mEM~a}tLo^eW158QQMhV~;^M}jkNJ2+;%mp*1Ui7D zI8N%$M!`IHrvfn$3sbf)q$8RuUPY%&lO{A0F!y4vI(nrUE~{Of2ODvCi2?yW4f0vJ zC8uKBIthadRgiN23Bd`CR_uka@4!B%OO4`^DbeSz*c^|CaS+gpf?0k#$h z^?RqlnrxgLkzT|moAw;m9hc4pWyXBz1)WMfaFv~)!p{({P5_&k05nG5(s4P7)Na;~3##zNZ(gyy>m%yCvOF(4Nm(X; zG?j$o{D_{EkAr9_qaq*o9`xBb1$JD5pL}T^Z4o;Q7T84FZ*W!R;EH+eCs7Q6Kg?Vw z-N>Cx4_0Y`*(lYf!zekQOx6KEWPasgaU6@9h#>(AbBGg}sH6M63~H1AArtyVEa>Ic!?VcJ#nnEQKgWM_o_FLLp_ScsKVeje`qprrxyl~13-o4 zB1i2*IaYCbJ6U)2{R`1AHm=_Dm$y;aH`b_~37_y9uF%g@j6d!~5O&kT{o?Mt)- z%PfNO(cc^#wP7wLE;QUaZ$?l#X5`_ZXc!-et;%WoV`aA-$G@EzqjwX>wh0O?!=t!q zw8?)B1ZBpt%m)(tLK(V+aV~^aSzd7F&7CW{nE!0btQi|e9{aj#cQ=Wr@+G`OL{}jk zgpr=te$CTvkJ`IhV4AG+Z5iRIK%u^M6A(-j1*&)K5s(`mtb>wEqDM zWzGvPjE4umFs=hK;`+42B&SwoKyILn-QN%H*!}@566v$#ib$IcKFzo~QfBYdc_96t zYM%q<5FqGVpPni0WTLIq-Q9E-86buLD{nI228&g6(^l=7Dk>yPK$G}@7vi8*LwT$z z3M|TTeqFrxKl-NdH8qauvd#F`S7nBhd#j$Jd#JWc68c=EbttF{wUgZxo=PNPZVo_V zA)SBxID0#xX!5+HHR97!lxK>LPX{FND}_Zx{xtFZ=%8lq`}U_kV?r~CY}M~F$3<3; z-H946gMpVIOKw-WQf~FMg9FFPmO+7L4NpBj_&_r)6UZ%IJ3|e7dQj1~)3ccve0bp7 zpPKmBgSQOK-0?x>?q|2rtzc~BsWSr|^n|F~&77EPuRM12kG5Z9JA~?vlT9pO39|9h zfsxSrM|2cbs7#>{B3O!hTCVg83(ScrHO)+5{e2u8j;X_4*C#rU^#U`eMC!x{2*guT4$eg@*^J5dI)WFfbV<>X)uzwgP-l9&ljENDH=m zH>wrB70(b@>~G$C7}ep{`uE1MUpOp7F0*<*MiJgrA9mkVIrqcKC(494_Ua2_p@Vc3 zX2Qwu)NNijMM7lr`HTriKu}Nt5>Axk_B+}Qy=HGi%`9dMugc?!<7z&Mwu(3uZfWnG zz1sRTjvUuI2n2Z<;!O)ZtVKp-i;f)JI7+)X0 z2t6XD`=)iLdsO?uRhvAlrsElL-&~{v1c{t{u8!l3iBg4h+yXrtzJwvf^7rkP|8s)4 zxlBFaR+6B@BtBWtd^h2nJtbC+G) z+!HE=tRwFg0bnojkZB-h62}hIRAfRmp-X`ds2m3@&AAIWgEnHP&7h&@*Q$0x(uE$P zuwz1qF&$ib3zFJ0vqBsA(30b<3z`n5E{~7n)YI7&wf;uZeJoTvmcq*bnxU9X+Dvx@ zkVncWS%3wNF!+4b9z6)=3duyTC%P-O_DsvXB8mp>{hS5IyY4*qg!iL+!>vmA|1Ct%AUOKUlMHbsELRs3`dk{cljOV=b_sPB;$7%Mm!9vFauBZIn%zn51cw|`$d zRH1iA|8Qq{tA6H zeA@$+Z-K6Qxak4_3EWNuN4@2;I5Uy+P|Hy1GIKUw*YF-)JGH1u5ULaX{K|uEQ}kx@ z@h=I0D*Y9k8UdVY?ErEk3&>K0O?A>)V9&ct9`Q+me1_-zy7Z)9S>U5CQym9A2b1;M!%#f@;r2ikLvxMsj7P#qV#@>JL zHWA5J=qvsfVCk;6A6t3G{0|MEggW#ULajuR9Z*M43P>svB#>58Zpv=1f5ho+A`)D4 zFb^*{n`HOXB$-V3Z9P*TKP)aN^>t_LLQrj%NohO?Dijo?%f#S6lz!YtkKG1Y<>$(6 z@SVWyGBV4TFaG|Y_=dDuNxxAu2UKwT+9yI0l@|b0fm4@0Pq1_GqxU_0ZP1ZiQBD$2 zSQCI33?{&e4E~ib&@huXf-&HgpqL!{TgWOpl2d7CUj+Vup`@u^8NOxUmTMmxTy^o_ zL5CNS)5Qbu14S(;yr_B1wtqWuM?RrsE5FHcL29j9#J^Ve$zz#Ny#KgpZIFq@$f_8* zw||t|XitJ2rN)IVVOfwjao_`Vay2a%53feJC5-I zZIF=Qsf_F6S;m~~XqpMd^9Z&RxTIcz#|OhRv_xe=Vmpi#b`6;}6qF8wK`r_@pE9iF z2+f}aFOezrAFeZ^%CrA_UvcZmZ!*gZa13(>m<|u zU&h`)ILq_A^PMKLBg_vA>^PC2wgl3KZY|Sb8rY7Yr9deyZM^flrIi#pnVli;ZkdBk=azIls|w{I!__%MYJ;L@cZ7K50Uki4!- zV&YKirdtANH%UrS@Jb;tTROrDNfQ{_GpcCmKT95A*!L}nbh zGMqvKk~dVUA>csW=OrI*ZS0H=Pqcx0jp0ZmXPhdLo7^o1N{RRx{ZKG&G~V`4nJE1z zng$rn#y#76W)>23E9N=AgbHCDZ(4!ek_?cHB4$GbO#f827s#J*ueeBb{M(4(A@MLT zj06V)Ja^#9D{qB3rnS#oV{?{P-2u;n;Mdv86c>6&0jz^F_?qfGq}dTwaRp(5!7g)_ zaO2T$L%Ve!CTeo{$swgS`!EeOomH`Vk!BK`lQMmKo}RdGzo0DGJnsVfCv;|guo zqF9B+7xRn=CPJ~o5MXrn6k3icb(E#t<;okDfvEkR97eA+8Nb|kr?4#dYUa?7-`Y=Z z3X?~%OlHyMm06E%f_buB7J+MGhq6$yB}ttOVzu=5j{`L*bahkn?~p;*K@5)Dd)w#A zBH*wbDCe9tLF9A^k5Rt@3`V@}H0vjBA>t-Nydxb4wN-G!uf~2fv$EsqUckG`J%_K) zbZkR5q)BoKbcwsfor!rWfA^y&@Ylkm{1(Vydh`lyN25ldSjNC5ke36*+-X7aull@E z{uB<1L|!HZ3r`B;4ZqmH+4YF7u>L%4)xOQ*&f>A#nZY68_M&XCrXASyDkl}Lwz5tmOcd=nvlHAjr~&oW=|Z<6dIFj% z+RMyUB+gTm7E&lg6ooes3Iu%bUSi{DR(f{(#cYbDEKg6FI%!TGHmonJyQ9c5wB08h z*H1D|bDgm+Qj`<7h-dxoJDCtK0 zv^k$j<0S5w8U!`2%7(ulCA1SH5(;c#nBsAO*@`1SxM82ZM`%$p=!XyHpTH@ql3Q3) z5CtnVJ{9#6aLTJscHu0Xs=unks>Cfr3cmc(SkPFY9@qb9xy&DKe)VDND4hP|3fuvt zj%7G_|yTlTq?{Y-L$A2V{WL}Vj}PNKE^;%@eB*4>e4?hELmbC;XYDd zV?lRph`n0!PMhhv*g959JVCB^{-XsnS-WD0loPeiTlgpaWOmH^F&2-tqQ%8EFsarz zuVbE>*xgdgHsywW(xBV;fXDDlb~~U0I5GnEzcFOc8lr@4vRpC6SD(=!?2Dukn@5nt;o% zOnhbZ`YT_$0$X9~aqHIgEsLAEG}oH&U+2iHVW5^TZ!sA~yF&{+kQ2fdeH=elY(e^T zmV5mp{l1BGYXC0-|2RK`54*uApWuWRp;o zA5g+Cl^f3o^^ccM%D1stRB+NuvbtI)YdqQ34D@S3F|Pu*2QT7ARzAf(K5rd9n2b7c z$=aB?!ZEq!Z9T7cjGM)<9w&qohs}ACF~Y5Jov2Src#*V1i(+-P9)o~nAxtPm#&Z{I z;DF4xZW^a84(;ihCkX?YOwQkAAANMe9tU%Lzs@=#V2NGUp`VME1VUoGG&^ z{)=dVT#fR0-Nq#yQXT;>t^z|T_!(%%l;E^6`ZM=5c8BosouWe zl`>1Fl+nR8;m;c8Iq2|Be4>I(*u||GCdP=?yzgdwUdSu5=kU=e`X@Rg#ezD46xs#H*`kg z2>F2c?-EtMQk)hvr5MyxBltf{q7sH7Tq_GTv?8UR@OmUfJppIsbcmeXlB~-q`vqrH zswu$g@r(j1$n|Zf1}k6^*|;_77>Pg%B3A^ku`S^?aibvRUUX2fP)gn8X}b;xh1wu8 z&H#Kw1u~N`9V2E>uD1!3^NgW2pT*S&=tol}J2*(oDa^>pg?eMcb;l90K9IQuu8o9U z^2@^`!eIHEU-I`SkyH&<)cv6BMul7~H@)_L$|*K|U6GS{aro1r+wOFmr}4TMK5rA2 zC*8+|lk>>Lf|3Gr8NFi{hVP(e{*#~qM8rD^rw%5;4uurR3!Qmsp9Xm)Mq&?=UzBd* z6sXf#XrC0xEbLYRLq_xF&Gp@LPClTc?&L$AczU&cg(@oB6wMQREPs+#R~@k;plawR zV^sSkcu6dk@5As&wE=#_7BEVp7BCdOgd-RSGj+o&{q!X2^K6nU5TU6W7f~gU2K0f( zQe&==>f;0h|5&n!S_O8;BRm#(STI!$00#AzaR8qtc8UglF)H+6wCujKG;9GXel{c( z#im4eVZ*HZG`_L%$j^Vm*%C2aw(N=f2;HrG^6P4z{_um=a*H4a7vG2U&0NMZmIe|7 zxVfn{Y+OQkzZAGDbI&KwN@Gm)q|!(E3xV#Ej~UxWt_yfdqDd~w>>6tIsHX4@IE9Ux zc1eEi6pv0ZKDFgECb)$1WYtYNtE_oCr@AjwTJ?)u_1sC_R>yY;H7JT+ZiOyQGiywf`Gee5JKi&J!6jt5nhlQ`mcN z`+s1ehW#(s3O+O>Bx7C*?!r_hYUeOSSgo(azk^qaF9P|->3@Y_0_#c;iB(;Tgcjv+ z8oD9ON8avg?QM@rQ-LN1OW{hX4UH?5Oo-k@S3_lEPE)ZM>0DZ^3CI>KG*P|Fb#a*k zou5uh8;}NWEpUut0NY>fvg0A{?f|h*eXPQmMuatmQVJ7^AM1WB3Z~}v-65+YpeF)I zx|G%)^AFGp;fBW|b>YttO;5i9gpp|~#uuY(*A`7U9tC|BU!syF3v_I@5gJiD_XZgq zf|Fs8J)^g;{n&$sXZTJp)u~1OZ7nB_u;RsM-j{#k&@@9a2E{1c!4orj0>@}2(W_rM zX)e&0*0;>?je^>vlrwULOLI(cq=f(Qnb4k_QCjs-ipCp#mdg@>G0j7r@l4rsgK)3c zd6DT1svRDF4y4wgb4!O8Czh#rYe>7pg`RaP5#f~vW6w8_i}fMj2y5w?f$E)Up6z*@ z2fcaPTz0P{K+o8C#~FU}3Y!`-J5;2U1Pbs{G>fTIK;YBr4QmF;>7-%K2&kp0L-y-m zuRA@pc=0wOdrwg0g7O4^w;OU(+DT28EEUCAqgpw1dRNl=$Q2oWPq@;!88M=8&{Iq4 z3o#T`6{zo&_xPKvykJtEwx;I?3FfdkAViEx3G34Y<=G_H#Kih%_ba*_932`ROeIYm zH*jL%_>i6Lpq86WzB9BmjJYcGO8hgi>6^;*PIT4EMj3 z`l@!agRPXUrVto!3YBOp%{?XOfB>=1Cp|h%a3Mip11b}>PLSgSs~w^IbWPFR{Qo=k zu0WHDV0apQ6!z!h;Hllus@+~jgl))D)JtzAc4yWbg?|X>SSJ9z4frZ4_DQnWx56&n zfB)-rHYuqQKrA6sg>mZd16agAY$Os0ca$$w`z+|EK%BX6`6WBmy&ekcv3od+ zB)LI1xiJI}e=Bj~w@#JWpdrbo>S$~ZpkWUb8i!=6Ak51n(pMFdvBD+Ue8l*O{Y5Rs zu0DyDO4L%euFgvhO+L1Qx3<`UB<8S`-mV@1%@5}%C z^iO`r_#$`iO{m7_C^3HO_C8|&&&)wV z1@)juy9^)A>$qIpkWs5D>_>zruTKl30;ZDxq9lPbA(If%5udA;Cee-Zl2`)LpH{CS z7o@Pk?ZOz?ES{Fes}Yy_+)NMCYC8`Q|1{C$`#rpcwrJb-{w3`JQ`Sr=tG+@wv$(Zr z98dMJ_SeR5GG9GbXuR`VuO3*1qK z>pF)a5(z%^^VGVV#6sZ+$G_Gyb1s!@ry4|1+>`66@Q5lROJG*(s(Q5Y4=V%80vzgRSm5e-IZdPtN+fH zQBf9vkce&AoMjkc$YGg@6BBDqvk_q$`Uy+d_iIr++02yxcJhHy0+|#w5Zb-Psl;NX zr4RLL2$8}k^-vg7?Vt8kQArc$#)1m9rwn&yEf^}^fJ3-TaI*W_dHPvUu!Ia^FWY{o6RMfnjl zivptCK0O8TiK9AptPK!Lo$vzrng-aqtLe!KsX4dubzzgm#EG6cb&#aU160wzRGM0; z_aeIKH8S`k1F%=tJzz1v`fEjZTGVHtJjC0!!rzi6kFs z@yL^nYX=&s0A)ReL2e2ySe8(E^{x8C-d^1uSL^RIRZns}-9dg`3+YrmzZRabyJ+4P)xj z^^9Fvi_3RO#1=kut_#6jQYe6dK~IB6&p4q*i3v^HAWrU)b&$qATkar!78^&)1A=+0 zQm5eR`!PZ?Gl|nHfpQv6Ly@1LelM`G%$|S~#zqYkZ!$8K5E}}PhyVQ4bSiDK{6eMa zd;u3tPlNTadOQJvEQlVu&t7$HA~09YC!DE%w}EtcM}$v(o! zy4(LGotT_OS9aR=#Dz4A45k4sOciJuAu~tWm0x%7;k$cQB2nZ3+HmF&d**Dg)3EX> zssW$^Bc;3(DAC&yquFSBFOU9$zuHY>&xc@|Eciv}&UT0`4|#j(GYuA|SXtXWi0D_L z3iGs>5@o=!`@iaq!&-L~j=?x94IAb}2NONq%5Ub~H=K2$9LB;j9EzyOX( z36x;QqL(lbYf}i*w0)f`XiA~d(EfXk(ZN6ojHn}2;LsC9-uzFd%B=|B`CpB3g2;Fp z_sqrw0u#xr$c{SL5%$~=FbY#uA(X-;&HW|lrFx^BU4`WQ#XrGOfgKg(%8__qj8zjr z@p(SAVp9QE(G!7lJ_{TYBfUH(*+VESbv(@C)ROH?E4yMYuHKEuZU>B@o z|2f9)$ZvkFcJP2Zs3y;^aArn7%eih}Q}k1_r$YGww;bvYTymdOno8c@Q^&hsMq{;g z@COv#M2`0IRO79mThwsq!)NzHDK2^w+V8^0>lz;Vr(KQQ`(+JxeZh19^BUb_B>&Z2 zs1ARlKpfOmWx4y{&Hh|DH%yDpm~2xWiB zfWu{D=rQ#?3;3;WJp_Hoz)`A^=$SxRiR0*+G@F48)Mbe{)?fw6>cIN6Dw6F2R53R9b9f0O5>$rIkl`EG0``aBby<0c4<` zaCpRQFiKhL$uaN&t2NoT5_z$~IN$h2KM0O3Wj!6m>eOOr0LH@MBkzoV;bj3QT!(F?c2Eew4kYmldwPwOeZ?)o&j%D zBOiGc-k)ifte3YSMY%>*dfhlf&38yw<1WDfmBuquObYH3NpyzhMBB`4F$<{`Yy?i~ zd6h0JF8&+3)@|+|eH^~hl{P34#jv1Bb$P`#-Eaa0DrOfxkm^CilS+I$ZTB0O=~hY9pGCQ zRloc75irXVShJxdEGe6& zPO^PF|F70z13P(bxk?^Lj?vi895zx$^BVe_8&k!_TYP|9#{BWIQsQrLKR?&uvm5vH zXj2-mN`B0+*5H>NU3=znFLn~xEts29Os@~@xW*A8UMhBHddx?i|A*fzbxVoVJ9+Fm zwU7+k(KQgo`ej#(ojQyyt}HAMiucH_#z#6636BGKcWHQGH*y|(}W z<%+GB%C$+L>1g?@UBmx#;jSJ1UVeCH8|joul|W0Stj0vJT0@i8c8Y2QW=*dV5_7s( zomG8snbS;nb@E{SDrnd>x6SX|xYArCP(m#vvRkAZ;zpV9$%FUWw`?7((K`omq>vt9 zr3;^5OXn2$nu9pWhhN=on-$>^Jk!HspxtlJBXna_DoXMM*a*?_RlBR;=cPjcRK|CZ z$?$fNQh1W6!r_xRvr5whtV*ouop+jVghN6GVT2*PIPD5QM-6qg=b#d{5b(%NRZu1@RrQ@&_*CFh+0CO!zomtMDeN=wD!Fy+*I1bQ zA;+$Op^#09?+KG~4Ak4!e&SpeJdJM@d=&=i6W3P3mxqlCq9^BQZ65Ejmp88b>7EP@ zfUJ;KHrJzh9#j>ds}YA_`r(5s9Et8W%0$rDn5H^^xbLgWe)P!qcmMg1hECmn|00T= z9{S)}@0FwMu!wGroAO(3OvaD#cf>wjWwEFpfFCG{F8EUYx=vbjZxWRJwt9h*?dzJo zNJ+?AokvSDDT2trH|pt%`WLB5)_876ydHE})zfrp!qL{$Pl)(UN5_ z|9hWH$dGgXf{Qek?O5kE6AP6PCqe*!0*^E@NEO5!s#rizfGEs->2IXQ!m=m_MznIs ze%d7hN|w|GTVv9?0FURYjgn1fL4le2P=x!QB5pdAC#5CbF%J~;V zB7o{88PI$&GR@d|qh0Os0ye1so!vmS{m+3MQj35}Py#(ebl|2B2^ld~MJYvej{wKM zOrZD=L5y4v6O=}wsa3>~b~BTvxoJ2JC;=Q_aeglDK_|fFfJ9hHMuCzSjs`QU!8U#6 zy0`V{Xjpk802_bi=hon&2ElVjUzsB0)_i^nc*(bDB_`hPn?|or)k54*>*>kRl(>5R z_$Y;0Q&pmo{3H=|ne+rHea)m|;ct;n*rD4Oap#!^o6e23ZvxxVklq4bLkRQUNV_`0 z)BEVmEic~N^ZWQ<$$*zYh(3Nu$==ZX&r4577P%^1DJGtEpal2M;sq zNtn62UH3tW(_kq17e1TSV?e&bRfKUo*v4GFU@?z;h*N{<^d5{FB;!l(k6t-~0e+?Z zjNium7FDd8rkCvQni6Suo|y{u$VIPIGm44w7@3~V4(LYm zai~PX%aA=CePaR|&Iw9SGnO8?6mKD}>m#|K+5NkWOiycs=q!I)=`xym#5qEKhP0=g z+%h31&!#je$4Z#N=D}R+psYLS$!Kd4S2cF_o{9MKn-Bcnw|KfqzqIZt$`0A(1m9@g z{HZoEIiE0n_C2uPJX}6Q^ESQiARHdOjW{pYYB2rfLb={6P}}GLjM~fvNS{On6=9RO z71Yq7f7<=$P)GMZOyV4Mz~71ZXoyH09qUd~Jp8_Q@sZEoYh8k%Ab^(i(W!$gkl*O- z5r7v2BOigPI59lCe?;cwCRQ0LiFdeUiZ9{31Ad79eXKV&h!#f4fE@qHTIt{mPO2QZ z8BLoHAK;4|gs*^wniw zGjVR?o9{Hgv+ivmDk+o*&umLrSS<#oFn{uQ(J$4nQQ8Tpd)JMbrSy`L zUpya$rm`F){pBS)&0=<%yDuY4&rxy7_}?Lx&zmJEj^n3~bqL4uN7jgTQLYf(I#vt$ ztjX<~ZK!x+aT~b?7S-L2lkL6Si_3exDUTS8pzHXEo#!k~n2fiUzfxv`SU@)18cp2T zXu_>1&8aHIC6`S5?_Ugju~@I++gagNXPQ zH)yInVwL$ur{tQ-pEwwje`4efvQcu80EY1`E=ckq3vuIbQjVS1-hkPmd<`oK<|`P7 z<;5Gv%kzQ9Vj(`8_wS^;wv>`jK|ju%ZCv_^iG`bHz++ptfuxAyp{$nyyQz!a;Kfq? z)$(G-FKA)2rMf_I1}J~w03khsUx`yNq@%=XaUh%Kv{*RC#~dAVO-(-ds%Kq_^kM zOwLdk{tkkHH1_-NA^xKG`)bGo020+Etr?&)8{D$=9qAR zwC$Dkp(2wJwu1@N1r;UCMh{uyJb2vuqU{bmD$t^EMTJhOook>}P>XTC%5j_L@1ctu zuZyXx?^O6=79lF=DR`p7Of~2Q6;!UE0w4JW!b!TOhLq%l3XTDb__pCRBtNo7o<*pk zk_sn-q;_1q0=DvVBYjpyBI%#Vo6sd^QW0>rs`K~H-SfUlb3&|y@+zTF9i|w$vw&vp z5Bo+N_1UL}9;ONu4OD)RXq#M^j1u54cqpUfelI-r+UB;$R(M$;*Z;h(5$)KbcMdMS zmHb5FZEF|3IsWvzM(Dzz+yp`kg(L(G!Bj4_HsG~8^ow7stP_vOgy9p-l0TpaOe0)% z5!_lDTB9nnB*P?A&5HwOr3?^mLtN58&oD~7vH@BKf+ob`)V++jeAC;q`bgswmUfo* zpc=u(f2*=!6HZ)i++&ti3H$(w*t|y7TQBoVvI|T#0`u|MI4zK?VVh^-O7tJ*xA4G< zG9(MUj^^`Mk6@GaSLdy=bWL<5hLPmv^Tw-V4K&iMU9@lcL(j`=cWkhsJwFq8oGp}4 z4nPTek2n{z#b@dR5E$}I$8gXtc;t8k3qnIwn4vN0vST(wU%kGCzywAMlqEXcdbarD z`Mk>+W02-$rKx|$s~z@x?bpif2=UOm?J(6-pyg4mXKmUf4F}-CJP~FJJd8`@grNpp zK0_8{VYdxLx!Oj)B}HxP-=??V@RT{)7O>1;K%%ObZzv!&1B0mtPSgy#$t-ei2FE>GPxd5W16O#@&V za7dOC&K9pQtM_url`mX6$!%phuWE@Af+U(`Y82n){v#sBoq}12{5k<9OQeSq zAPhHH_7nLUQMpSdPmG7dp7rMt9@nc>cv7 z-SU|~V;Mw>K?$O8V}F{;O%@&yF@?EYoNhYFwv|bpK~~dYDk#2>3(teb&cR4Exa6Te z`2)W}NRYavv`s-E6*fXbOA0GBD?s}ly|h^2QX8D1WEA&RO-kY&Z;PF z$BYvNJlAZ7;d0_~vv%ub%5%`VD}*g{wW3t4Q4N2W?ri9lvz71Ow*YjhY0?gKQ_);Ic* zOTp=LL~)%%WMh)_%bJB-`$p@0h7fF~1{>UU6|=%g_N~6s?`=dLPRNxBMtGC^JI3?z z0?>ClfnjQ4a=dW}+8C}(Sb*|CZcT0hMepH=SdiHhg{q|NvY;cSTRg0o=;wprwZ~a^ zR4-DcLiN;C-Ua)V7SLGr-bCiqSdO)Ws4euRWShF*g#Q1Y!~i0&3qaV*&IEwNMgz7n zV3c(lbh;Q5uVX%u;EU=P!{;%2c`N^je&rH(ha{e&)VW|Q-wfbQ^82SZ3Tg) z;DY<2j4>pbVgoQrilD@Gb6hoF6QR_Yrb1yAbX4cF3XrOQD*&gqiyPi8P^WzRqC%e9 zH$OJ+SRtoKoBgcJ_1oLFkkVb4-sJlLlYL<}VVRadnAr!;?2dj7eWzspx1UHK0bKcE^@aGOW= z+~)91M}ClZ_*ej!@Z77W=QeZoRApvO z${XklNF&Cm#ja7;46=0zIEVsgYA~=#?PlDoM9_RuScEn2Nc*1@W|}7-7l-njLQW)2 zt6zvc#F#a`xR6~VZt@Vg#L0FNf2l5~gy#&xBH>E~wzvTE<;g{%Wqtqj?_VQj+c+eu zrmK}3WD^uBmsf#dvPy$cDTriS&ngyEX{-reAfV^l=eCTrCk&%?d-Br4Yk%EP8!KoP zwr;0gguFJ$i)4ANp({AdKS)uJA#5UdIAWuNqsNXPmqAE?hfl$PUcR z)}H^UoEuncbsk25R%)Xg&qvaL3z%!aY)ZvAYS2g+OkH3W$&MY^*K(H>|)$S3oL;lDhtbGk#!o> zq&m2;!j$Go*1JQ9YF<4+Wz-b?vQ{_>Qzi(Yn5oeSvD8JmVCZ*x$k7GmfhEsBt`JW4 zz_Jre5E7nn->^eX8^BUbMJ54yALDWRg00)`@g|sXMnb<{i!Y@k2Hm-!32ysb)G7vX zyV7`(xgynY3~1CeAQa(JZkFR5=xX}V;si!Zp##1GPDDer^)f4elD>)SC5kx(P z=)x;*IAoN}k+|<8i>ExITc>;rHDAXxCKn)6+qUen#Cy1pmTg3BiXT2%RC@SUPt5Z$!ZqB+- z%8Dvc-2D)oCJDF>A<2ouAm|RL;zaxx%x#(hu|Z~&=l98b?9j}}^WLL6=g15Q5v7u*%8gS21MVT*QyvutcI% zQUgmO$b5j(s#u&{mIKEld}7#Vo$EY5%6*)i%fq_G?2n7PnN>Ubu5yQG`~YAzJw5zo z=Nb>x*u+r*&Mt-}$mS}5CoO)P*3*oq?8mtofX?G78c%jDCdGr!E079Cjny8=nZYfk zd^MVFR3#tfM1@7}-Hs>@fA-6Iu7^=b&mkDlejZ6VTnV?T7aeOjwr6OgGY2-JGr-af z@pX-o7>P5Glw*;NA`M-Xt;IjZv)4=9bv5!Oun|=!yj6-Py|MnWKDf;bWv2wpQ2K%J zHXYdBf1bK}JY823)rG_+!aFx9t`@GUh0SVUp#YK!tynwYW4gHZ$R7c9B%VHhD+QVO zyM*w7O=^*lG+ANM(%|O3rQBt(%@^-~d5U%+;Z%ZlfJh*&TT6}c(wxIGOW?0lKqtBa zLq37pz)j^pF%<-rDRdO7E+Mx#Xill@ma{4E7Z(j)@fch|fkO3-suzlQZn)UmcGX$> zA+;Z~^)K(CHf+@f<=3!I{Kg&nJ2Sh_917!5~3iGq^yY!plyICtIAO|F=@B}8EV@t zL5WZ;0IMp!&F!G26yu3GqmVcr+rPj`P&XBgVzHd&ZgM3cCVKW8OUjX1o$)}fvEVaa zP}ZX74vqpFJZQ89Jz|LB9*nd_Zt~FMv>F#$9J&1rW-?XIaSqrpRH>3CYz40q2AFEP zrk0nl)&9m_)0bkRJet_DYK6?(eAV9|0rY+`l`#*jCQVD;1DSRGE7`m7=ls+b%Ie}@ zW!G_!V2w`fqAS9KIB6EFU@@+u!&W6aD!m`yGXM}%xqxpDY$f@4!T1#n=yg1HDs5t* zrb#;Yx3lm%*hMgii%v|L4Hgo`?CwK4tl2WTtkf`Pk?Ot@EA$SS`qE}w7BC2^UXtWe zZU7Hx)uvPb;t&QcT~#PKfJ3P%!fCw4Z9#4fN)SJlRJv<5Sr4r>%e`y8#1PIdWkKP% z2V2Qw9p#>WgQ|xJovom{!UqAlaR2t~FhR)We4epMys@m4A{3e<$Y06AjwX2-X zj|Ef=VFg7&Y1Hu+k+*NdkIWYr;-ehGYv)LHcUjRyn2RD{<_wnE6WKToGfH*w*V}#$ zPsfewe}9UR;#M=)kcMpmcy)Gydy3kL4aOc9G8K_u0bkQlpU#q-EKTQB%FH~cr2O(t~d^bZO%Ez>x-B6ia)?2edh@ioYU50isdB>zAd z62n{M5e&lELkuO$z2>LGd)i2Y*(KA-R8UlO;yOT+*$=|9U}5cHq$I+p?eEJt26p+r zv+tNgFp3Fj1997|E?j8@RYGc}uqG$mB{wn?!+>6S(U8&s3)?Jwz#LHu;Od}jc;upR z({`B*l8&7sY*tuzloOGI1*3q8@Lgbk6Xe~|DiCO%0V=tYkO0@lvwfhPEJLNS<9s2c zDdjKSUif~*mOMwfRPN%M4R@3WCx0)gUe7aPRd!UHWSEpca1ioEKAX4q_276X7qT(u z%dv4f4cJNQx-9o|FVZ`w$}Ll?xqbV-QX=Zi6g(8~$e(q9^*R6VUc+BshE%rHE-T=!sgW?$EW!${Ir%B8B5u1fvm=GFC-O*#A7kY5cu1>aS1wQA=D(iEsQ z=gJGH2@t4GSh0+BtQ(H>$Rm(xOT7NY3WzinRFB=biu#sZ&r`pzi8wK3$pZWD$zd%iS`V@v% ztdDShP52^vs#Sifvun-^#ffHNpqlal6zk=#KSGK0Tgp*Jdct&q=Gf#viGb@9( zkV1%jSEj*Zvh2jh*f#(lF+F^OB@%=_vY-d&VNBk-sT3!HVnD+CiZUGGRb`Ucc@?dR zzA~xi*D2{%69Lyi0WR;gwyr546UWlX0V=R-sFZ*To#;4s7>Cqv@5xp}nO#EP~Z z>%DOMq4i0G+XW>>NcP~0)!4P<1?wEqm&0F$6hPCkeE9~*608?E;WbV-c){^0Khsj} zclTZGRKSoouOdVxs-w{Zf2Zhyh`8tmq}L2IyNnT zo(*k`G00eFg5X~FirILJZMzSTUGG?^(#L+$ra)QgWsp-2FI9ReDkDPe^J)X3>XkB6 zg8RhzO0@!93#jCA5ym=nM!5oow7iIG50kNtwfM|Vv|yksfNu%+xe4^uD`O}0$$jsi zSIa|T16aDvba8Dl>)e_FTJVUSa4 zwByG|7Hk3&k6EyQZg|E|n+DQXrDqG}6VTJWXbq{mx%&j~wr&S%ShWD6V(g`bk%*Nx z9*wIR8ngHq0+-s>8scZXof~~VExhy0>Dc=moMhY;=*D5nEIdo({DI@1zerr?bb`H z96la57gMrP02kQs_|sq~O9mO`D{*4p4;dyhFRvB0x66ss^kZP0s9)--L@tM13Mgd* z6Uqx%UTyj^vO&dfCjE^%rxKkqqgTN0Br%6Ui2{dAV2`KFXu4rfI!bR?d0{1$DLhxk zl4?%Qfdux7wYfwK7Pz0rtNaUO+%oe@1MyYwd-cPJbac6F8-BflPXOg86kOMdFE78} z+IN*v>zZM~W}T<@9oOE_z_5aw9Gh53w&ih*%lJ{?9%8q?B{{c)*i`1$@JlGH#P~cO z*z+ckej0T9l6{yi!L^T#q68gT?zN9POnA&{ zMp0whH$Bsj>AiLMqEpdu_LJa_EJ1-^Teb z13JBvkRBY==N9eVf7_FfJzbKlPEkooS__B6A*O6;0bKX$<@DELs%6ZWS>3`E)O(&rbhFCE`3sH& z_^?uBf&Rc<$h7|KBi@xOq}(a$H|R(;Y;S{?zew%V&x>z&uY?{1^3H};%d3)t=MAYs zdA}6X{J0jPsXff~r(&m@8b-QZqV;bLPh^1?un}^qLRvdou@L<+G8Dj_Znj4 z*u5aDv3A}f#mjoX*IDr$fq1t>w(OOiEPkem7bNHf4p4wZK>L@SnfRK5=@S z^kw^5qGId6eBO!=ml*&{Jr_qe(MhGk2}+GJHl9)S1F{%?=|y=$^>Q=^)uW;_ zCKjY3s%furJ2LKC2FpyLkT@sTyN^}BnFSPO#9sII!UHUzEwD}ABuGF`<{+5-{2UWv zYa_U236@DwVl)lmH+3&M^2QhGjZlzd1_y9;~nQtIU**Gz9-wy(B%$nIX#1d{<+fl zj<5W<^-OvTdq{*VlvElZNrS(1wl6IN1D$3b!wBJ`APMBjplaG|^#a(CyMe)KJAIz- zvlw^@gaxTdUca(txHDYu!nT&~!w96Da^j)wY`04~+0;8&NkAM#rR9gD%HrY;Q&Bq8 zf(MiiZ-(&WdUBpzyf|~jX{o+ke(SUY3w7kir9dPpRi6Q&lN+1Xi5rniHm|8Ktwc`c#9*XmDHSJSm9RQY0P5^P{ggzd8X-#X_;4O#lYoWM`ePK|NJ4 zyKe>!Am;{#0bjZ7VxJi_%%6#b(YJGW5-lBlw1e5@38qPM{>H{Y_*UC}U)DB0zwE_b;C##d8-y;6t( z>Bo?g#l1c~G)v8uzXbu8Oy6cCQ5H-gr4BS@P7A9v>w`5jgCM8BaLd}E$BxarPgH;p_r~n9hAJr3ap@Z|OI!?6kJNqrzL?BR@ zb)Rx7FcLIIN)?po96p$RdDMcCN|>sKm0+$!Tk+G>l&X^M*k*3bZx*lAzE)iOEDWk) znz<0%j78KEHO+*Ctd0oFg@gl{0Ds%|VUaa_UPHwT79UA8_5rlaGm zp*evh6$xZjL}Z6q10LRpe|iLBNo!?6oYC^5`Xl&VL$bb_AL zdtwN&%lL#8!q~aBr)i)@I=SdXrw*L9%N@1PE&ptN0{&x#4|Mt;z7G4*g_vA)$gFzc z0JWabrqg~JfeRs}o|SDyZPz$(2@;0lRSPb>g8KlP6kXh4(||-4x^x6p$Xg(vZg!H1 z7)S;an8?h4N(fGY5iU?xoGM4~b+8e7qCK!9+;V(e<0SX>%FG=N2Tjo8GZOB+I&R$i zQ`T)LZk~5$XioNuV}RFy5|Hb>)N$FCgoH{$7)C7zsW^F^Ky6Ug7~7uP837ry)7aj? zLK+M)Zz&DI!lAPfdWzEp^?;|IJ8Eviv124ponfMF=e4vSYjKOaM&gzB8l1AtL5RI% zF0fN@OP4hb?g4gik=8);ouadgv!ouug@UbGl8!g;A+D0dMYe8aZ90?9E+ z$Z870j)e$Z?dVYIv>Ij18Xpe8KSv`?cD{ir3N)B}ZO|BxzR zLrn=rkrN2r;)*6TRUS8~DIUr2<|$3rnDUq-ay2ZvPS7pq>des>fs*W<{>&emrg5S& zFV~$|bARM&8_-$uruYT~{*`L_!7`~p2S6|{H5c@iyL1;e#2_fb1q2KIpfIKeW6ji8 zIR_WUUbKm?%4}nG5UEe7x?-xcdowygOXXxjOgXc{Zc60$avEaVp0HjHdk;3ql`0=H zEnva9aEfjbAw;~Hj@Nr@?K11TF(Ty=(KkX7d5js1wsu+eG)f(*2hbPy(#%grh!bg(-u7?(zn1s^E|6ob$PL zGv^W^wJmCR=T}`RWD%&wY`JO_>M=)vN^J*>PelxQ7U7Y~)^p)j*q~q~QA>R{vv12v z1yU(og2N(;R`5_Ex>Q;5O4L=uN$RC$Pu>p#9~m3zsKBQhLk4o`UQsr7r=BnniYl0$ z|0vQ*NRfBl;mnfJu9AH72^9~qZ2g_$3PMwG1r6W1`8Q=uBzxi4=0)Bg$1bXDKXmT8G- zet=l5TMuJhnD`hcisZLd;p#+pU}u}c*B7RW1t=aL=f_fHz+MCp2sF#+qj;>QEmc93 zw_vr!d;DCDnT3zJC1 z7I>N=r4CXtWXEW2R?#SHixy%UZoL95oArt%G*_;!_V>0L(h`|@*+>~ooCzzXMMFY9 zPn&{O;7y3;Hz<(|XBNOxhKiTS%Rw7jDQYCp-3kM;L*3<`GgvapC60HX*e(G)EZq(g zO@?u}rLv&-N>J9b(`7A(A$9CDlp1&{Nk6O;Nxf5R41g?%36wTU5oLPnVa|$8;~hYi zg?{Yh;>x#h8|g_rh2x;S_g{WbbX4W!fp*hiOk=kEi()D~(4iIBFAW@b6MhQ|tfiHK z3W{LASojl>lx0M1=4uv^r_E-TM6Sg|HO^xwb~dIVOG-kSs)r217)R7;Vix)3m z$ZTrp25Buc0hIIv6v52As%jJdowyRDi8BM$N(USTXNhP9G^v7(*i+{Y=mk*aYC?A< zHPrSKAUBj(vUrwCvcVSjJ0S}0CFM@VpSyuEsIc&+@hb3_-1QKckUNoENKUy1#v1~9 z`k(0^k(cxL+D`+M1`d*TjpB870R#%fvYe$()N*3eW3<>HVcob46A6z8LwRneL6c5A zps`5WP&W;vZjU9acqE3!f!1aWg*KUP!(K4>Z$71+xF$iP_a&1LdKHFC2YQ_6kjcSf zg6G5{lP%n^c@)&{ZhDfhP-ok{2;3Y4eBm#GU>GCG4i+eT;c`G$$hM#t9KSwFOYs@YuGi$6WlW*|7$we-urUn=z{QYAGEJ_^m^aa3715o@=>}VrWMY&!ke*3wZ~++H zgrKv<)<&dnx%4jkd86`%7yvQKf z^a<)@5^eyn(XpBxY@uukQ{w2+7a^OGVqe@DdjX983WmgT1(|DHAM40aSp7ak4Zb0w z!*20A%O!Suy~7F_X*&Kxfh1%pDLzkhpd=KDr9%kGtbYx1Q2hIhHtrCOCvS-;7X*nx})D8p251MST zCCfUs1u^79>|qR-z|+hRO?fV!-bE1=Ezt5(IVG?gFhvh>>j=VK55GW@W!~_9Ar>KDs{3$O$*!*IO$EIdw6VM5-O)cJM7x{0g;|&C_^{95n>b>dinE!hBu!fxvJC>9h&70+)@%w0BMC{jt&ls#Jl;|10y#fd5h zI(rQTA`F6X+bu-kLr1KI95Y{GO;RkIh0M0g+5?$mIAeatM5qJ6LJ?G^nT@1q#UwiO!gG@hDg6xEWtO<0egrR;ol|@tgsLCE-%v3Z1fJc zEIVZ{xYe~K?1t?Sh0z}tAIe&nOd=9qA~1;A-U@CMD`mNdk%NQK)+Gs2rUj>QK_X>y zONaerI3+vjm}b24;L2}tr!J~Mp!VVSEmP+r{+(^h)Cv=;p}|FY)3Soml$^;KUXFXV z>S%thzrw$v(yZ8mn|G`tGp}vI@8G8%fFnsmjf*K_WgQ=Us%i8t@)P_O979y`A1zt2 zq75j@{gBBZ%8Jm-f#4h0ZVW||5rvQkPKN>lyaosLO~4V zbOw>_&dq~Hz%7on{Rbp1gku<}Nz>tod>F^V?_J>H48#myUyG=jC|Kt;`;%-1jypg4 z;TRhyxh{(LRl~q9{tyIf-1Bq&F1O{Gv))tE(T{_)c!k@l7}BMCe(tcfUNU~07#CBN zX~2|Y0F%Udf*XH+!Vn%0|&ISoTVn<1wp1?XL3UMz%CshR|?=`{i&`OAgjbGHIss6!5A^+ zynok7R`g$#Syhvi0QDyOu_U>lk9T-HO0FCy&WGU`Ye#)J8Dqs{I$^I#?cbR_r5!r6 zd#wF1n8OKkKBN_M#HjPmzW!-&EW5*YW%ioRoJmmV{$sWWpT?k#w}R-X1Y(<;oStc{ zRIuNeLM=48W6C(8G{GW$CK{pUF-=}VpZ3<=KoMew>jG_ZQbjYjY&7jsC7iTMEawEm;?wrQ{}STV9pZ;BON4=28QN>-YpV(9;=8(?KHZCZ7&G^aV>i z0g}Kc2+QdO8^US#MfoS=dPIHTlE~cKPr99*Tkxntl7cV%yD&JBmxGv~iumUJ3Zx(_ zth|Z(CRNL&Sl}?Mo@tP-r0G5Ow3;gR7Xn6nRFCl$Y=z?{Ht0`Iyh2byFm_f48^8Qw zE)rq8Uj?In>FaeaO6Vk3nuP{+FjcWgLL1NyXhm*`%|R`*z$#kqa7lH#>Sfa9493lF zRNH0so;zWLf)Z;RRo||oKe;E;^E|Rw%-}x?*Cf3J(e~62ALPq6+Y4xuW(w}gUX%1k zkp)iUw`VIjxR1}C9NPX<__KmHy(WQLk^G_+7hU z5lmJ0rgq-dRhAp{3FuWz#}Y$Sbm|0^1encBC$rl4r#9jXJW?>3&3B$qD&u`Fl4ul+X5z3?qio5|NxlWo0gAY_)>#jeRR!0-w(@iGdC z9R`t>b`>*-7oZRV4GYd--~ZKPWP{5?+i%6xB_P*JA=fRh(#O)@xPq0;d!Pb3JNvLj zt(S@rI!heNaKgu7P_*z_bMd&=sCHhm{Ry)4jQ35b^1$v*bX-P0l+6D|yfd#*KFu7V z0giP}+WJQc+f|Ma{ypD0f7KA8@NDcGKUZ|cyA9cy2h$)C4#3f}qKj#wG6bX4vy$cV z@UCzOdO<$IG=kR9=oe^(p#yr^`~}|X%FHS{VOgKts(d!KfRP&Qa9POWy~Sdh?R1p_ zPAz}x`-3YAsKOf#uRyK9y9RKgIuLc zK|i|09x-GAW}l@2Fwn`Y`QP21z z>&O)vF*FGP03C2~9qbO#Yp*DmgE}!8zm`=NIN3A+LqJgPw0Iq-;nQLK zrnA4P!989f=|DV{i9@Z-0il!MWNsvvN`3-vYP6lG9_6Ayozw?2A~VAfj?)ch62xWg z2s0!grM^MIAh`+-)VgM%F{CHQJla9s4~Qfn29dW`@ivV#aN(Rc3ziCFC#eA#h-CaNHc9rOVpaV)NmEgby#4ONM_i%qwBe= zS&orgaDH?_Xj3!;XrTZwOhiNOsi#?X04JGUzqnRjr<8khX0sSLFrS)Eq!5ffF7pbF z8w=tYv@s8?U4XghKn+DX;G7G(3yb$3bVEzm)O36?-+fL00Kqy&_x4Ek3=_22ZR+-) z5vj02OE1M#_?sMI&}y@}!3WknY!H~0)S8DcEL%H>$kR%FK~I!L23#{HWzN&xstm_1 zv#_b|Q*VF)c{ex!&W9E(7cDrw=yxEAV2Fs=of`V8!eTt%6DXsehGkl;o)~@^w*&*>gcw+3m|CWz1xPDpZmE1SeHMC?6C&T0e>U3(!zi z^`LsI zGsWB!o2j0|@lIet6whL%-d-wNV>7XFMe0pMyg6(X4L0VXivaQr-Vp(nC5CC_`qpQv znw}&*ukW@r2{AuxpGDekRzc2BM{nb#rBb?{L(A;CM|A{F!zIc-s&$Ihp0i#RFwJyY zVrV}2)(oI9UW^#Hi(DF{2SAjGP8aYRrEPuGqS^Z%br3Ht#K^;5#Vo)B%r0v%Bzd!_ z7#pvahnD4}ZN?CdMylW@oqZMYz;aIZg3baB*X&Zf!?jyV8Ob7Gm2l1qF2_6e(cd1c z;QpgKeh*blXJ3?)J95x+XuK|8Y3$m1di16DH>-42^%Vg^`qCkqf?r1yMPQ5INcA8puvW8l-R3=wY;v#VQK}5o8 zdDj!9*?M}j!h`61l9A_?&d>n9cDhN%%RmJ|a?s*6y%78kJ=ko6sODI*OAbuRK2meV z<_%cZ0>G_lh+;S`T~tpQiNU@vd0v4XK75fq?GR$yxTVjS9;tfY1~IYtH@xziG~)ki z<;=;I%l6*gw0<+b50Zz!O6OQe8 z+#tmS3dg`y>yc>U-tL$;+J5$YrWR4BE#xRdI_&v327>dGAa8?1@?|}vRF2M}1W&HM zfAyK*FtMGRBWr^E0!b~Mq&qc}Xp5i=A0)^jkY}3IX*XGAPAEs>ObCblcJQA-O?FJu zLii#Ey49Ni2y5)*`D$M?I4Xg}n7YKc;8ZwEY5da3>?OifDQ`cH1I8^<^jbw!-}6QM z=IODs*3+8}fhuev9tZ{X4{z-_wN7( z24-sdNPATdftWy{zMHPNG~p3Wq5{J)g5Fcsd%B{IoCmI2DjC7%C1%0$rjsO;e)Hx@ z-e7m^r%X9kg;&2~(RzkCK|a<8(q3fgtR5g{zr+#SN@Sf+O__#c!VFj$%7rQO@oXyL zu{(^mL=|tzLix3jcdvS%{jp120U35X760011F#=4!Q2QAs1%Rf?lquoeB#yA1ou&GcK`U zr@K>Y%yTEp&O)1@Ef^_kNI_8%*^4&ZUcP$Nzgvgi<|Nu__CAYGlJ<8WAPNj6UV3)v zUAMj?q!M3Uq%c*jT?+`Q_8$PmQ}84UH|7^--LO?TM*C$dCvhd^_RY=fgTCC{Ldfj`(DPx8uj+cVw z6k7oH{=-wp{piL~;J6+;gjc?#-W6C(=W?w=@|Qf6v7Jq1Q(eO(xmj)R8;Ky{lj)Hl6!l4sir zIDI{4^0yPPDlr~OC?wHmd=@EzUU^R`dqa-Uzlp{e=3<$xXVPxq7CW#J$3DW;5NPMO ziYwQKP$OmQyEwiWGW$Kmc=Lcx!?#=pY!z#OiD63>E5{1?0b9_Y(!^a*Qb*CHe}zLG zPB8^BQV0v8h@teij-(1YHjSO>TlYR!uoNMU&et5;oCt6gE6SDGZsTsw=nrsn4_2K) zF-}Souq@ICUaWQQj2-ZgtW_~z#$YI$?n=9X5Fo4P>H>Iy4;x>DHa*Q?{Co7Ttyk#K zCqIUJY0Cw%NlIl>;|IjEO_T#{hWKi6Zww9pRIW`!T|WfKkb@yp2sOp%7YB$laB%v5 zPa_J`0ak&eQYFK*aGn6Ih|1XxjS~2T_EPVO>KFw$l|0WX*vAy&0|ZgV{-|PvLuaKd z*2>fx|JMI@+von^Rg0}pa=*Y;$B_exrEFz-$oh)bM^GEgfc3#QSsdadBu=`-a%0Fu z`iPJLEAJiXJ&xN=?i*)Eju-Dwi=W!XMzmfHtppM&3c3*A+06fYpL;|6SbPa21>+WU zzT-3>Rv?o{3uylygF&SOn2Bj%Q#?`s7SIu#)O&E0df>B@2S8Cy>eC~?-OzmX2n&68 zBR@siN~!~qc1N#VLr0fcHIQ+|UHZ#iDw@0%{9S-k53`PO*kA^FuON{JWU4kF%9%A% zdF-ThV7)+bd^=fr-Hnsvooq)tR^V3;ftfYmJJ|s(1zdtvumJS|I=c{jZKbGO+IUbF zgYX@HBXI#%jNT+`Vuw(q%SyEis*pfMH|_$&j8Brv$ig?JMJiV1i~|nedSb^vP$fTT_lq3J=a#3cQE%sy>TKhC*hGkR-07 zn7@E^bz+IAf@BmU5Gl?`Gew7E@tCIy(`iFPb8gf7awn{y&qX2+07bF#wV(yadgVCl z=yI!M#y6VP;T2j&j9{HXKb|=l{8&lWa;$F6_8Z;^7#BHt|0wNCn3qIio^vQD)^4T? zxR&FOeI&0jfY@cu+kj;rnzBDvWH~yQRV!CshX;7wx-*`AA)xg!CS{=DqjaSC=VS_u@Be_VFGOg*{_fMp+Sq}qTYnb75Ey|U7Npd|auz4J>#jfat~1Q3WvkDx!)DGJ=#1W-qQ_kMgA& z4spxb4TE)Nu~Z$sR6wRMLPCuwn#!Jk74V{ZKm}NWKoYCtsw_pGVK0~}*h)gEI-;Ss zPePe;lis)P`wcO`+ILPy*M18ffjuCVGV(ox}Q_VAqL!N2UoA=}N$&8Gn>*A8f5qHN0mo=RpjK93665=|y5 zlZebi@;veKUg&paFewr2nqPrNA~py-5%fYRKps8&HPVKXFm)A&#gl{q!ZraLQYEU7 zfFF7nl(Bs-G*uP*dE7}SmLIW<&mQ1LFSK;edJGAtJ^Y|lPAN=XQ#KWAviG^QS1{w1 zX#o3#;`V7Z-$lj+i9J3l9qf3vd4N~#*daRy?waO{JtV7}z^w}5$V3+X?hMTndM zDK6Xu`-Dqg*tz~Z=Cu^I$LI=_lruYO1Z42f-`Gx^riAjKWPG2Kr67e>FDOwgBr~jM z4`T_aL~sYG8IF1dT9F_gvt*wsUKVLoGskS8M=*5fzPr!b4kqfR(Z_jTtrJCL{@|?+ zsy*RauhHuVeVQcziXT)^&qOnFgWEQX3O7eSX*d0Qp`q}g)SI9n5MiBuF2a-_R|;zf z7V^vGuQ0n`($O&!a;vs&CrSqg!#dyF%8yBOc`1ZB!tF3y>go)!9C=03v9B2uDat7rl^DIfv zWIySxWgV z;*482R*Bg#5rJrD73MvE%b!}|KwNqGnZo_T-!2rA+u+X91w?VB4UOO0tXRidH!6+F zFfd;(OsuFj{N+fEyuMU&V={=#S?x_@12Bum@>o@|TW-Du z1ZG~Tm3ne;iJW1OL^C869GQ5dFhJ9LpCgW(Zt(BCEd=IQgHX{66I)L;{`ibw8<{R1rY=#+*0n zp9U!<(3cudaz-e`A4T{5$BD(U|DWXJp9J;Ph$gX7h^HL9w&PGgg{T8LB^lB2<4Xk` z4(;#Rz4A`9O@}_bcyZ^F|Hpy2B z84OQw;2Z=BVKx{frRVJ7*MB_nxDm8tAFe!nq zfB5<+Z2L@tHb5tU5D$4_5U>=;o(RAhk|}nIV$kO$wyYDb)=9)*)Lwc^t!QzzsH+Tr zc#h>~2mj1l$VzVB-a8tO^QGeCpppKPb+sT-N-1r*#v_HTebKMCju6$cV;4h`1tO8W znN_uZd$@gKPdd<+h0Q0iTGn@xUrP53?h<$K`vm3a^GPuSf-Ltgql6PonLJT+?#1*X zYp+>i$bR%$R3%S^2QC3<^nU$}xM|QcV~R^sw*o)gkO-V*gCr*dfy(q~;+Lx5OZcU# zxSZa5YT> zpf5V3!2wvrK1@}@D#E9{08XLe$!{w~YJ7>)V)}IUy)fRv<6{%!=5B?-t@PsAn{rS4 z9-ubUy`>EY5jpC;(fs8ANZUO0X7cZtWL9umxnqQj?Bt3OO*1&(=x5sGs(l`L<=`Iu zbd8tvqpKYeiSw%I3NhXn+7*VYs-jd?DVPZxij5d41?QL;hGMC7l3sX^bh|b)`sQ*i zKRy*q_LQ~P;n%lg`4D+ zNluzX*D1?LTFD?^>4zg6rDM~MYdcR9$;_h-w01D9`eoFNS;0NSpu+tq&IVmO1XcgDcBzkaxpwsE(IZYoGx_=a&b?o<3R(68o|Boe@=1y$@sbL> zu5ko8;b@@X`~G*Aj2n?nt{yaU>fpHKYU`hbL5%1@CJ{w^*=<_FRIlp?;sW`44b@kN z=f)L9Y9bO0zycLgS4gEp=+tWzK88gPfIQChlr}@UsXPvRB23CvF|}1VUGBtQVsmP! zOe1=k0ps6K)uB_xMAV$}tp82pIUx&t5T%ILrw~=CRFGZeTxxsqi$QKV@rsb*&3`#7Aam~( zr282$&=eh4{QcO=i@{rbrNv;}c-W*!ohpK;RwR?X358HNc_dX%2OXJm+I6g0&ZG8r z&b<58?zXjnrWEMfb^Bdw@4tW1&37*P+V{yP_~N1>J#&bQSS?&-by;&5rgCH5Olrx? zt&V)pV-W#kvuoC_JV8{+%><|R5R1E19>5eOeRy@XMzmndd^@(p{~ZQT#jGvvC_Dl^ zgNf1E2jPpZA1zfo1PDr4o9w)fGlgeb0D(0{E*nQfmV{r#ce3`jqmw+9?LsJO=Nc37 z4i=QKN^pZzI}8+VSJeUEE&zpx*OsO*IG`!$=sW7!S-)1>O!i}q!?Mpr5g z1vU(?B^_S#a;U!0>Z<)7S7;ftft#7BbXLHbQm%y^6hNp!y3e(rwyCbfHs`*l6H z_qp0G^yD%RQ35c)DgN93tydqA9k9E(nJsWju->tP&E)gqzb*D-#t(VQl_Nyjd9I{N zed`|0+m#s&d3xGb#@rj~NAl{@5qWh+{a&?euRCtr`+#wM_Pz^iFFf&`eZ4F;o$Z3|?Z3-)MW)0s|HxHYKVjHKkgNUAF5~U-|?LtxSHTeO!gwQS1 zflvu=RzNC(*Z`ZW&JcZK(-n$PQ2H?A?ccz!xJ-RP-sEF=7bq7gxopVshQD+rzG$zd zdW;2|kGFaN?BlD9>_ppXJFEu5ujfi8@s#-HC15D@M`RUJJe#?-Y5KPUgls@P3#M_Y zwAcec0+mdoKx(gauIb6p2e||gHj@*I(p1B1uT*j@uI3eKk)`LU<9+6>FWkR-pG66b z%Tba51hs;vnG8E~>|mjOk34iZUzPmg1_&~q9hJ-Pjb3Lz4hYqXTrx=Zg1vu{xk=P1 zpLR{XVV2@CXbA2XjAY28lzDRM0K^M?3bAcTw|O0W2**5x3eKvXAx&Vl_q%q}PSExo z%U&VJ z*Siz6M5kYQZPitkORmB`HOS=qb9N$}sq`A9xAt9 z+r+@u>_ti!)qVdbzFfxxweu(eFz^cJp8`r1iBlbj{j!JJ?&jj#vZvZ7)#2MLDqbUs zDlH5h8C29MI$#nyh{G)p27hI_e*m|?+8GFoCR+79zc?drU;I&zx(W(I;S(X!ul z*E=7tYkGBe)erbS4N>ysq^OmYzp+ z93q80x$h<%LxCR(6O3a3i2MYei52LA0IXm$K5WswA{_3bS`;qQ^fD~)Mgw%nh%r(- z`gUTcvDPRk{-ySmV8xlM?~k5%Z%>bjQpH~L;A28`{2CWA*$mZJ+?8xn5;*_?WXM6{ z!9=DE}B|khv zkCped%_F=NRA>j!@*aPedMkE?BV;Ms<66Kns%-HcZ9^-_3BZS@AOZ4W6Qq9QF&6mZ zSwgXtEC4&rTf7+ViS~D-K@n_W+Vp~MPPZOzGN6W2OvOVYxmerAE<7Ks$F^-fthPDX zqc1{3~SlF0k!3?n!uDPL41}+Y+5~wnJ93yiBA`0hT!_Qn>RcL@NM-JKe zs7T7~YCl9&@Ui(OLwD@ApX}TFfV>Xynv2SI;L^^;@zAp z91cl!P4`d*f!YG}gG|-bzc@)lnow#G4#>&%kHBlP@z6$HkeXy`S0JkAlrLHAT=p!w z;M7p`#O|Iq+Wzj;`{4fvS|5{$q&FFLRMXh^(28bL>y{5uwTBjXsc!gt=`q~lV#RL9 zGa`kZJ1)J?VpOpSTCtiA+Won`snlIJ9^^>##xQ(7RIxUjZH1m<5naBjb;73$Ah}i0`_DIN}`x zD&`(~ySeZy93yHs{=sPY$gno!!zEFJOwnn}H1{7U4 zw?yz{>C2y3Q#?40v^f*`b`5op|9;uoEcpE7s|s-Dth?3f@BZ_P7aJG-yJd~{QML5@ zAl!rBxfjgjtrYy$#(NfV&3SVcz5eRKq;(6Ok(kcE^b=JcYvGjfXj+JMt5keuWs&h~ zM<|3SLaC14Z`rOPB)b0+Zb|eU?L#|@Lj!A3b^9+_&#aU0E)H_5HGe6QTR(j3MkR;w z(FVQDgtb~S){$%(i3}AS=~4jwVnIl05g-FVg$^S!G!L@#Ge&T1&_;yr${Pb1a~+yl zDl{PBZEHXQ!mhkg7p!>oMV&KUiuCcwSB%oJwlX$dQ&}H(UMnLN?PBq48vob2f}}8U zSG2TRIm0&XeNfJ#8msL2k7~|Rc|6UUWdN|&ODAz-l|J+TVeD;yt30nf&xDfQS4J`y znb;OESi(}Qo<`8dYEY+GFZ!5(P}&XMUG3(hRDI$J}z+_5bSfu{|YHLTd# z@zz)vcc&^L+p7|oWY=K>4j=0(7uHVLD#{ZZ=-x5qtr};S)Owf#?eBM<_r3QDJ3DJ+ zxVrbf@5l2#?>YbHfBxs33tC-%w3@oUBn)2J$M+k%9_|D!NVZ@FRrZWe-4zO}v1OhL z>rg(RuY7dlRDNxcjyOtJu`a9dR7D7lJLWA^{YhO-g96q~H*f4#C2bOCLH(JnUw+;Y z)rX968VUdQ8Wm5YF%C{K%U)u?mWU=2xUJKUEj9y9fk8YOz6|_CrLd>~O(R?5F{*s$ zHbuh~<=TpRfteFcgUZq&Fu(o3b|9}OvB=x`Okwf_6W}5pYNC5jWStv7#*KE_C02@x zw5WzQt}m0>^R+jc}WuGsyZT zUG4zocQSe2}bEumg0#V-L7DLN%qi z_t4NTafk6BqS-rkbe*z3q#%en&LlVlCbqo37eRb!0s>)dJUk);0a=FEi&>%aa!ZcxL;g2$5#nfJN9WE1n`;qgCNxN1C$FPBF78v4{^7|ECIpM zcC$$8C701E2zPZbVgw86|B--oJ_d#;vKx(&Lu!!BW3BzXjcnjprdW+JbL@;^eUFm#*+ z50PiXgXn|K<0wMFnDI=Jw&EhsB_O0OEpE19a$uBV>KUv9&rx7yU=COBlz4sSLn}pjSjQv#0hGh4fuWL}ON+Vu!_roP=`vO`-b|%0 z;w5x1;v!I{tr7s7*}{w~gt+9v*cnG^u+KMO9~neVoK`R0r?-x*8deJF1PX2AGA?7a zC1$G|MSEOZv}M?S$OHtJau}G+Aq}Ti0aMzMaKRZPSeWDpbTnSU_W))(2@GVLJgaINMhn7AX|WAlKnIW=eIz}@w;e>O>>;Z z8RgD7{CL@o$L@11Qz?V`LX1*6*ymqV;Hpsi#EDfLr#!Ct(Pn)ZkSW`1;F+9M`pw__ z0+g4ZM1aY}6=mF4=#PL$KN|l*AlW2wN(e3DbV5A1CH|-SrCKWSm!hTb97hU8$#F1J zhJ5px!6iOf;3_^NXD)zMd>y3BXfOJ$yJ&C-c0!W2xQ43p*RCLXdi*_m?4%hIfR*nB z)09}peE(lGLT(ulnFE%rUOb9E-6_F^$dCCO=TnU)wGANz$K)A$GiMl4=St#su?B>k zvSO3V#6ss><8{`q%@m(OgJbvN5yePCksAjmzB1f$AU#JexYTr9zG!L@%Re4i3iOaZ zMP3-X6%&4{n_Y+lEJRqC%F9Ez1(@La$rEHGoi84N1ctd!7ZCr>*z##-bS=7nJR z?k!zsrWvr%51qGWPW?KwZ(6LZyM%N^=JDv|aqGhnj)#_3K(0yR6CA2qiK+zOZ|Em=PB6us=-Y{? zYE<yu^(>O_6+G_TP*eiyd(Ait3!Gp z&3KkBjI08z1~p3>F2Gzx&`w0Kkr9jibl>;TP;t|w*X%>j9S)3>J%F%Ju`G;mFR=B{ z*LifQG%`Ha5iON~(g}<5q)0Q@j1_C61Xy15gz`%W1__&UKdJu)M+C;+?xP8a-0Dp5 zsH558xDh^F)A7EVfE$XUq#;v&Di0%fkTDGaMUV@_(>r}>W~OW~ZUT;O6B=0d$y)#_ zjUg9giF}Ags5ovhAeC7`w`oxBWD`TLc}QHB(-E10Liy0IbO%`0Z{ zUF%zde3n1?ea9eDADeP>`NAG`Nw^CJKoXxAIzeMpe0eA*S@sT91+KFzW3>AmwR zb(>D)8NwDWmm&MfmO!RyUJc7W7+Umyh0YPORqUV=Wlv%m7Xdq&s3_VKyp$9b>kz90 zgQrY@!Ny&%S7}o;j-pd%=R0*vfkivDDp(AjBMgwP`);6viPc^DTh^dH-JSBzB$$A| zlmTxJc1KB+tT1aZ3>?)$wEm{-(yiqZS>Jd(edVoa0vm>h@&c!DNb*)$Va)I&@fZsu zc)m+Ic^-5~sOv7i${S^`F>kC*E%+x5ruPZHfPPcZSm`a^#Z~g5(B^xd#tK{6mCwv`8@l%{T;zO z;e-=x@*j#eQ6}{wVZI<6fOHlFqx)Ewr3k<^O+O(IZ*7C-_jbfA!8QgM40Z99P`shQ zfLya&7?8}8*=^*A5v#izb+Mxu0L+QtXmaHB+PCo;L7Kr+<#XRX*e7pA2(A0Pkn{dlk`60nvYi6j=#4?4cnLgFtLykw*!sA zif&p-O9MNf9w6K8d{-1{_ z6yF=Upk{Lja+RwQtP>%r1z?^6E~sEYYdq_6TDgE{Odvoyz|PjV&Al|M?i zyfrVS%TRReuL9@^;){zY_81Ip_|Mbu9SUsh0Dqve+DtEy}e|Y1uhAt zwFEZ0as?GK-C!Sl01`nV)1$=c@T;qV6rT>hyh!GWI!kBHY+C&Dy{~)-s7QXcj^(|A((}k5zbQdL;nb@vn#W+*$)x3kOj(f|mlh zMSu?}IeI6-Q+9zRvd3+`?p&IdT_u$VI4<3xsdxF%xjn@0+Nm9Wb(lUZ1nvf|ot;l@ ziWskSo6|15cjbZSHq5@-Yv`3x4JdNcoYn+D7x1M7w^fhPyGlCnz5 zQk(onhkMO05Jzj(4L%}{Cj;*T1>Mk(Wm2k>YSY2KTb7`&K;UM0zj_O;6IuwqAVDTX`K9@Acdi6{MG>{v}y!{LXGX($U(t1D4@Ka9Sx? zP)NMJ-wIz5Ye&IV2EGb^5<5f+Tzd;s+$tgpbpn-qH`=-;%o#SWYutj55F2z7X|POT z&F1}_zw5)csMZu9OQo-?WWU9x$$?MTaq(_rRu6ZhM@fP`$SlEyXQDPG#SBARk_ywM z>?lMZl>fDiJcs$<@7R{$tSA#NXfAQs~W^6LAp{xvvT{)t>H zs~r%nJtxc6V0E2&jaK7X8x&MDYDqm;C>@2Cg&gIu5&#oTA8ZH<{Dw;h_>Yxb<-=+D*mo#L5ITBJ-1e zTtcnTS{EYvqZW!f9FwU$(l0Nbn)>yBm{CSoo;F}(>qg%+fs)}U|3q0Qaaz17PT}SB z$nD*?aGv-U93YC2-Wix3a6Kw1`YF08n^@F5L5-U;i*RFdN6+opQJ5TW0u)Vh4NRBR zW2Lcc+>#i0rn-e&COsVY01Z?e7HrMGJje$0kU2#$0t9qNPf`0^Zeztx**HGM7q1@v zn}P3lP;j7o;V=W1UEF>1to{UU$?hIgKGGhH)u<*{y8k4DmGYJe8s6l^u z>mg0d<@me@5P_7AP2-uR8=8^V?rv5)9s8rhxYRY{KN_+p;qp(Rij={bSrS<$G%Ba_ z5!t*p(Ci1~nC`~XtXsU`3XkgI+9B&B6k2yc8KG*9qp#mG_Did$M;H%mAu@M*guFn` z+G1!bsKK*7^^DHuvF4q%06rB|MDmU`8gCtgY-E#hU{b=!JbxrUX0VXEnXc8pg-5Cd zOg>2`p@tl@Kx{ZO1{s)$iDBiJFvScfVjyphDXgSyr134AY4S5b@Hc$cmvvl}YHfxX z6|Bh`kFX{Pt|`@{aHdf@VVrt=9}i_^gb!#$%+TetICGw08)=3eK~w>f3edw@Z12ic z@#zwL$VS`W8>HiDy-P6uH$%63VdT$Qal|THwu{DFKnYsw0X7v72@1P>86;-4Kw+QP z=M~H;*9uo#lOuqNm%fJ=7kCJnBzla&0dBxDD4k9p*k^S^iZijv;FSj-pbs!nIo_#% zWE*ik%3o5~5+HQCdUYU*2n;3XKO2(dZaE!F^fT|F)lck^8nTGyec~F$> z;C?xLLPVc^=JxPjp0zQzfK0JLiHZu86FUi>Ba20P>B(hZ3({rwxs`?{g9sUD$pF*m zU}u>l%0~1Hzg8OV>_q^E5~VcjD?w}JM@x6A1ZOJFZPs^AN7zkX$t~PGNx&276qJ(e zlgKD55~bjY@&wV%F7TDny2L$&VBF9hzh(`Pxlg4-rx#$JHFv!G3kTr6&)Pq3U+~Z) zYxs}eJT33FKiFQ3S`snJPAmg^v+hvZPw;2?aqd7=X=u6IUw-~}dBu!7fYZ^?@OY8< zR)itI#?cBzxr~2;SJ$rj4DpG?Km_g*m#$ZgXDcFkxJ`QoV}x27q2et#B+P+W5 z<#?)eqEigtju|Le9h>j)@Y3^6WEB>yw0tayT4wNMF48f2qB&pqgT?fppx$GdnZ1cU zOiH1t<{W7y=aIecbeQaE>Vn7BXz)y)MKc~#aErx*!_JwO5m!- z>c6#dS0Asyr~n7cFZkDG)#Gu(;Rg`I@4#JB$cgA`J{skNgN5z?LOLWBE-mCl><|bg zh|x0&pwL4swckcgI#p5dTQ~*vMwJV4H4&FzY>=t)EQ#Dj8=ku@Fdk?9H?mKx<_SQ} zc!uH{;NJ9Da39bKlvrh^Mpj=mq@G(lK@~$HE@B=k1rX$0G>K1~QZTd~3NIosD|&pB zt3Awnwh!x>KB?7b=ROEfJ>@a&8LBE@GRP+9lGp<3kNHypw7HMGtw_f2D}D*!1y!1G z`)$La33p{vHBV7bB0Ik1oFfQ>&N~F^l#3NOxuA#wL#3joX;#vdQ=+gDemA4fZb)XeMCE}&7EXUe1$uZMDj5EK zyB!guCQ-cVtSOr9|*v`{sQapV=I>NnI_;`#dMt_xfshLBVj)-%>|Y6K}#s37}jj#%+k3ksy)jB>smA==N;E zA#;khi9CAOXP&woqlGmK;>=%u>0edsP3TS-7&gAH>aTtQDvxKM!?zp|btjg|><-SF z34^l1w+7OA6ehneFqeZ<55cg9>JOeD z5KTQ{`(IUR2OmDC!C`4^8VN&R%c-7Hv2R`vS<80e^8!vpz*_Ud1LBz8JpBJ5eY(R@ zhNp*r;y=g-B5SMPvzPCE_x`-&-2CM3-q5+8Y2@z*A6No@LN8oBZ}sC4R|v=-+ok4- zKY^&&P--JXEbh`CEAby*b>Cm$a&@o*BI-D810f{{CEF8dNvbKu^T_(kYAN~!vi*X@ z5A$a(AAY zU;6xe<#XlL=Rf8GXQjerhZYq*S$Oiim@3g$5sOP_mi7YmWmNS^TG>HIh2Y7qE2)(D z0r;y467MARV)IIrBWk@9dw%D~{|w;Ne(gbWGC|ke#Km{pljX`DyEi|!B;AK>FWC(W zJX|r3+7x4hB!;mS| zVVcszshACu2}#N08oqynY6eU9S!0DY;xV`y^7`1A4jM^C%u16ABDEMBSQYTj^rb}v z*S-f=(nWSrevN4{l>KIW#kKT}ng3n$mZ7Xblt+)%?$ih;ae1~-Y>1M-`0?D8VlVNQ z(_5btsZpLzA{?m=2e?oH zjWnFUtPy+0!US2K6ju<-V0!_E*B4{rK*+(CSe`PNuldbXvm8{_bm(GKreKk}C82G^QApcp>q{L9TlOH-5G8!|y`ctlKhZ@i z8)R=o9YK*D*HL8vITcmde`L-JWP%ohli~0K_4qfrblct|z%O0SxF@52`|y2Wu2>c#AXq<_kTczTi2LJ-c$g zQV6p&!P1K;FAEvTwuzCv{ucYm@KyMe!1Ziw(M%mK(^@z*@icCj(GMUMArJ`zCYe)7 z89g$tz5)^MsAgqRaV1aV#&s7C5f6o<6di#(Nxe5bLlB0?@7A%I-2g3RcN0%L03Mip zOXk^x>m;+PsC}uPyxw}4hVZQ%&I}@MbI;)i58AgEs+2)d4sCF#i^@hvId#%Fxl+gD z&0`;i0_so8+RBJ$JB0iYPxL~Jw!xD^aCJxZ15poUb~P%$0%C)$(bECFxvdME5|zlt z)_0=yU*IRqKqRtw=1C20aVIQ;2#Z+`dh}4ze=LXUs*al};4FuZVJE-_=xuigFbuZx zkUDh{l#f6^^Jbir4M2@}k2K&^(TN9N>#`C`Pk_ZGF^xjM^wICu2|fH(bS86EU{Oo_tH0oD&sUU z<2!@+Y-$TtddPr0WeJQyJQxj%Cqin_Zl(Pid40qIPoJv}rYJ@7O6hpX{fe#dz^Bjs zKM`~*BPiiiMNG&Yd+-XGd3N~9GxAH>IYvlv*69Z5%Ri3oJaG9NrLR`JkTJ_Z&lhOC z{cbk&L1|^I)17x?HYBUqdp?|%_{B2bdEhMriylN)Y45<>5yzYRq{I}J%F!|<L#p#Z7SAL)I?{yHE>KT;xy@}MIJ6S^ zY9AM%jnNPoM!W!h;R@KrIG(l%RJISJ!1_<=ZDRzFR!&i!j_qkcGPD-yri*r#K08Sm z8rSiX>@FbWNEthoaZ1(l6FDE1`Aq2gut9hJ?GhBkqPwj4aSv3K-id9Pp{a}O+*I@+ z+Y0^E_D@b3M{)~vS$x+HVk_|pMgby!o`J@kW9OvQ-eug6(sv9_LC~biMIvN>qTl&l z9x0LTPz_`!b2>3x(@x|7E zCbbF0xOr3iiQYAgMvk38s<`w>sMl*d>B<^O2Q|?N#vZMDL=@v}w4{SrS{&f^xV+AG z?_8HMZrbg@!#P9JpRWCN3&of4qF09odbT{XeFxPUkTX5gLpQ`87z~vk1n*XTpYRMd zt>_6XFxmMP{tA!N+dCs3Fk^zN%kYiZ0%sf!$1L?taH)0^c;mJ09HixC^T-!tkb@~=7>c@K+Bw@GU^KE3OC<^Zh$y> zjwQ*Lo`;Ldl36PTW?FJb?l>ZG_RK`_6st3meGwq#oqM7eLChN{qQp#5kyB1Pb$hbs z3gxm{$T77dvJ}O7Q`G`Y)uy_^4&uMZO&YT#M}V4zSePzCry2JGVa0H%KZg<~gv^8^ zxR)qx#xCf@BZ1fs4FE-$oKf8E2Dlc{)1?uLPLP=gCX1zqiNN_B$RCuLO`=DtgYjvp zR9XflvM&&bPqQa%S=xsNkAcj0$eu%aXf3%xa2%@0RF0|x=D*CbvUMno89F!zesxbaRJGGyH6T8+J_M>(tojHuz zQB)`}#4KQv0&u)lW}f&O?}C3`zAu{Rk%JdXrX@K5qx`025R^0V9x*_$R}`iyl?j63 z;&>%MBM>1d$rVU_B^;E?m6L<#S~&l7X6a-TVoAHn!BuJ7oSsR$%D_vu6__a7E{>Ei z)g3q|l@X7zb{7do#rMTC*%{8>?H!{w8_nehQ7fK+UfMhYdOxBdf#3N4 zKq1T&lH@GR#fxw7@+;t?_|D-U@Qk(=HpmF zjLM64cE_RY#4;;Z-mnO5cAw$+pt6*Bndk%}an&8Fsjv)F$D6ACOqOLDr9yhNlR-?B zjGx92$#-enXs;e90>;4_=Q>i19cHbTVn7$mi^=?}?%?(%aE^!v3W<0TstdnXM+F0Y zBPeO1JWng0AMaGNlekN)5REli&AjQ59a@TgsUU)1^a24`Eu6m*$6Ek3;01g@epHIY z6x*&w@B_>`KZ>^KqLwbLPs!mRWF@qM(p9SN;as9}(F(?cbBWP#C<@QSfbdQsRrAqR za2&K5PF_Dw4``H(Cl9vx$?yO#?eI*&|?*bW?%c5<0Z>hJcFX z$oegHAmX&hAld4N@JUPp&mQLw86=C4CQzVie!XX3l#_P02*UV~(yBviBi>_^yRNtN zX$>EH%I}u=wW%9#eZ15}$Km}=uU%;}5_^8&pHfF}d&*+ubRsAD%bm{5h;H7?c(hzNigBe;8eV`j&(s2#8caxI@4oF6;k>6ySvF7rYZ4WzRQb<`MTKcdZ#ZyM%< z$X97eugsB}zp~#zYzFc00+*X8P=Q<(n>toRvE~V9s<@`fE9<Y^tZvp%j>i z3_ikozLhw!aL;VBMt|$~_}XKfP=(V4KxK~Bm{3aMbJO9V)^aBu-(eS4y)NUBJ0VVj zTOtL$I~!K+~Sz!qx4P{vslI5{a;TlcN`^L8KYWySUacjn+Hb9D1QK&^TMpS^TSDtMnb*Xv+ zQu0X3we(VS27yh$RIwB!QpCD&ye8UvT1p&q7VW3DcTs7_E~2;>n$stT2%C~w3{7b= z2se0`$uG1PR^bDB`$inYTgu{y-#$*Ca9t&Ai!$*N}dshb{_W?WT&S%Vp(5YtIjO zjcR#QVnK)1ngV_U$_?L zujnjl>hWT!x!dJPY)5HkfCo1ML*PdcPrX8p>Y)L&W+ICh)=rOiNF6Db`;ZzFhBcL2 zwIWL=Eg|kU#V{4hhYAG;76Y?02L3jenuU9Pqx6%O)M{>+ZH{DCH3W=LJugr9unr5)Sf3=^buyamt)TRtuXc!@lTp!-b&+I&7OvCRqus zkjbT(Ni=HJK^B^+WK{J+%>;qKKD9Lzyh3M1Zs;@;dgm!B^xt00!ebb=$Ch*Ol~9!H z4d4lKt)BVvUoM_sPl%RDNq9-#fZ|on)aaT{emnr@jTx$io3XPW3^6d(1kcMb9Vqtc)M*mkSvcL;|3T)` z`)zK*V^~*J8;=?!FhYNx2zu|Nq9!@M$)<-uSiBtsbCQ+v_(U~0Z-Zdp_*GVBonQR1 zph|jKxOwVEUOPDZFl{1q5?Ds~Z$-}01G2()ZWx1D1EC7`Ll=?P0%ISVo$UaAuBcYo z5$Vh793#afR%9fw(moL*7|4mhu97s2FN2Nc0S?Bz=Sesgac4`^9i7+H+5LxVHbxLU zQMKY*?Pq!@9u_U1z)4`Fm#)GF5`LxLE6gvH6San8A!4s$DD9m|U>LExY_So=Gk-%| zsqDYynZOhwecHRv;M`}QwGRagv}X^_a=}CEfGt;an*(dM`9@Nk51?zZBGdzqzh_&O z!MELI$)jh~LJn+TfZ&sYcVPEAVVl3`Vx^Tz0V)7hB9I1uY||Gvpjloy>&~+*K^i^{ zW(mw>ui}Kb5;jVRtn8dQn;h%RdK1LsvCA}Ad06h)4xXm$+kI93d|TpG%AhO(f*cvd zrgx4@`THdrru3F$7GKVUN0tCDA1#eBoYCHGPoqPH3Rg@ds zL+bRW0b(hxdH4siOvi2A5`Zjro&(8>(_vJ&2jH8msKMf&O`IJ3vyT9|1J5FVszMk= zTj<6A2e&fAg#m@*TN$t{Uq=b{#SJbWCB1?lWEvnyDBQ#7vMu3U%fMvLOX zZ)3N$4J`6v#o-G15Rd=>VSCb=iHX5aYU3=o7uVNSMD`&Rcp^t`yWXYY+=n*2NN!#m z!0H!sg`o4UjujYG-bgZVuD0-#kho;mt$6^x%<_4pdE-<9wp5M}o;iL^9dgqrxPoRX z9ONs%QSefXs7zfdr^xy)7FfQW0+O!iL_M51AcrtQ6gN-=)i1YXoMmkIU!Nu}6T1qH zO4z(Gc(!@NkG2`KW0l;5H}H_6w4CaKlc4Z~z+tTTD}x$Hb8U1#Vh;u4c+|C{*9PHN zD|vBWt*?myw9V0_ih(o>RD05mIu6-bNUOVYWzRBDn&!fmdlExgxs^en4&@GYN9w{YbA;Fnl*ErHp!jj6~)u}@Di24n6%(9n69C<1b^WOC=5Uj=bNqf?0fxi z&CWvY=CNTyFq`#z5sFDtsLMCkk>!rpjLI4ag^sp<^5!ulyMm_^abFk?U*N@_d6$cZ zSV4A+D)}~Z&N~7S%BRrh6l0Fw?GJCdnLiX1pYg@fT7GdFGjsQmA~--B8NmHBAb&Tc zPC@PFYgDA-^)!ou%4Ix3Y|ppkC*_!!4UKK!4EhHTHy|dL{PE<8 zX#?SJ?WV+ov(9&3im+cJ@m-+P93EdvYK}aHo<-Fht(_w?kL=qRS@S!XdYnr$r_55p z%CMYq>?!_^N?}ytAe?ubFkYfXLP}g#^}u52wo}vhLX7}DlqiZg!bpIr7FTPs z)Y0}SR&E6GBTfgs(FMff`xN@vIF>RuBcR)+Kp=`A3|m{o(D>`g=5<7p92bnl(oh@^ zksx#uqQzs1^er6*t&7D~EseCJI zd*plFIo^U9h26c}7m`+1QfQ6>5wgdxVt6{pLx7gzhMpgQyF``7 z)5R%TWQHoAkZHVFoQmtbNZyDgCZqEIM`vDgkYWwMQb9t<i zwSmRoU4u8ab`zDvpJUvAl|ZU|N&uuv>>4Mm z*r_ur8~ABi%x>fk&;`+?9!l978yPqQXIZ;ELtlZ+zI~ih2L|W3U0~Au;D8}t2jr%n zyqN&(fbt4#wfI_L37vxLA7O&fCis;_^B%D}`L(m>s^KKJ?a96X-Xe(!tz^|(!~j)R%kW_D9p{f@7(o5a6mq>5faOv zVl2B&hSNo7Fk%eh$kg7kzMb)Q6@O#4at(35rSfy&%rshb--?XmrX&=V{?1%Xa}Sh7 zpY&TK5W*O6J~*J&8f8-OIC(r1>+wa=W&_dz>m>D*`8v!Z@GLM_CXS)-c5&e(W+yY+ zMBvzq0%OK!2(>Y>ls3lwn@0xWek^4$r*SVVl-1%XK0OHCE*Zr?#0?o{BDY}OEGkKi zfPrV^GhC41pm@nQ7~wh3TZS@``e`KczgdL>vCwcM!0E15QG(i;uAj#imz?lz}!(YQxnO0UtJE&Ut(`u>+_*49$L;} zdkKT!E(g01OX27Y-YK9?*?-Q{mL6!E>&%LAD4j_Y^MPhSJ^jQ5!!j9J)F7F#wb-Xo zCpZk#scDB$0DnaSzu{mQ3ILt`ws{Goz!CBOmhp+=nd*V^L1BZ6pQA!3#B=d+mR;U) zF`S2QiPZV)7-@U@UY-{PVMpkzL0ov4e$D5}^?E;ggAkCt=Ud#ZGeGk0 zJ_?w>H@J`@)ZEfR6in2Q;teO)dxIiE2q|lhd+Ldian%;{-w+oEI!f0XlJqH&-=$U9JFa7X#-BKXg{t@b}C@cW&ZEk^i zVJ+2J+yYu)7Ae&$d`-HGI4F^F)XSk}bXE%bU zIK@^~Rg8Li4#$2;8wEm_q#|smri$A7Y&#zC&GdHi5=$K-3(s87v6b=5%aK**u={f( z!ui=R>8Kd3V&au2aC+5o5rgl7jh|`)q-q=&uRGFCb8>S-0*VI= zhQ4&UW*qf>Vu1|E7UP|ElZ;|7iIWw{@+2~2usaC#;0&L#^xSU-5GALTUH}SkcMm;+ zsENwp=gvKf77BFqC`zcRbpus^2dZmQ{9vxtHGw%GC{UGN_F4-ag9jEIe)kuWBV}h8 z`%P?7_{ZRq#2$J{FdFU|w6tioejB8e08n6)3~S(Im9Nt|hHr#N7d4fMj|@OnI%)iF zjz%&rOcYRtA!XPS4m`tWnJ=%PkobxlL|}@m{sP4GpLweS6wDszFFctFFj4wqoL~xQ z1|G`!BNHJzxvKB{cnh$|7++wgIQamrAgAMrs4{ft#sQhQIh2W_8(zSr2i=wqr(AsSl@|{_oDj8RUqVZ>kW+q7 zvg?d7D7{y@;_@h@Ni-*g_J|AJ=DBR;@l3husVrthxDDSa;!X9UfJPs~h4F6$swmkC*I0Tb0ZOBN=euF^6 z7{(yHoNcPwHuM@2^BL|tVByRtcyhN3TYBn6j?mjpFkL~fO^*$?^gzPXO#(bI)%dQh z75u7&-V9`PT>KEDQtexsdjC&N0E08r&S%z1gIF?Xw;;-=C4tmg#}1Rn&_ZBo3OC0a z4KGkw#EucW2w2kOD@tn6z=bDe5Fan-I2p$~+EoULdumd|(+ z`cd$~TUeUs60Nst{Slj9bW?%SfDBQJ{eK2>0Uc4p2OjE5+d(wJ*7ku&AOP!X|L-BG zgd&Yqn@<9|a9m=q8CaQHRYOiRVVrtEgZgH9f0Gd1s_tex%$tN*3RSh9@ z5KnC_wh<WCAFTLvG?Z6#pDO-co!SZQ1w zd-?z$K_x&bp_a8mO*x7g8u2nB0wNTv`f;?Tr$F&oGs5CC0H*~ zddtVkvhA#4REQEC+a1wvEmgy4x1r6^i9xv006dd5t=wVEMUHc@1ldumsQ%@`4;*%= ziAtb8G5JrhjoGPXlcRK|@q(pW2~cTVva9g3Eu``>7k=vVSh_g!XMVzxR3n0XiyB^* zd`nS!!Jyn4y`wkOZJ=gM3hJduu+|6LL!`9P8ovbc_;fwh$nM{8GTXU*jJXVxZdn#f zAToC;pysUQUNoyEx0m7=l+>$0tZry1?(4z-jiM%POe~pdLhR6Qt?+MjyTn@CJW@{F zL$EyYVKYLEJ;kW=_Fdwt+#zS>hRV>QuE?C;XLIT7(!PL1y@3{rD*`YcriOWV zIy3gR?Fki){p})^?*L^CKI1*AaQ+(K{*|l-Q6Q?CBT&dvd9_X+WHyn@YtPjnxDL#iP>~x&#)Ep&JivRMZHe77@fkG+?0G0cICz z3`ICy9I_x>sh%N~U$IS|(FBatICO!%Ps^ZW;TiC$R6Hnki^VHvW-oS$7YTEh_8)(<&cA+&VthxB znMT%iX$2c#$bwXF!JtO1OfNe02z3dr41@e~9NC zfR>MZEMUy0Nd>SBpafJP%cz3PO=`aWmadYxDu2sN-9A==TOfwqg@{5j=Whufjs=oy zAk@Zz!bt<}z)cZk3>e1ig(MA-(jGscLExbA%#_OqQDE#3zNf#i>GbxJt5%H<5S>eY6e;z@q5{1c;?TzdvqHT>rkcneyb^D)trP} z;GLL|rKuv{pd2cDgYatQq~dj43Y=81egeZ=!M~-$!4Tpq;h(Ihj^hV!Hf%HL5S40i&2Q1-j?t%4f5p^~C)$TUYNzsqP642%n$eX_ zG!B!R7hs?f(OOSu2Uf=C!o?j^(v;G^gf(q2rPcvcS5IpbyPHJP02v)YjNWhf`pBC@ttoA!Gm=4~o(%6$v~#L(a8$cd4Cc|&KB|fd!Z+qNbt*xpQpf7B_gVfg9W(2)y{orpD;!Q z2DrlO77l)dhon|#28BrQyIWi!%|Uls(gAnjz6U5{?&hSD;q<9)^!)@801YK)oj^_U z!U3lQIRqN>lIN+19+>H;#*%2@M{eJ)oFNIMmxZhZQwff-^Efa3f5KtHx3h+vad+ui@I+lK&_2Tn@B2dyDTgSfX-khDNxH7OwoR<+- zW0f2*v`}%Z;B|s}I?dO9;763*OqXFWFz6&cvb?71f5Ls=<_7Xdlyy&Vi4g|uoY;8& z7oxFXuIvtzdgTS~tvF3k)xnP*445n8nz(=Y;)7@Ym->0@J{>sQ0fsFuzV`BC_x>^G zExO9KOQZ5RqA%gEY-q}3pJKZKsdEJU*~!$ep(G{Fd;t=bQ}F~*^C_ry19dC_Sp{0q z+!wS;P!f^0SV`tbe zU18HTg)+z+*5?R_+MI-QSW=SK_q3{PK-Nv=Y7wJ!(QIQdFa1PAKUwhnZP4&Jikv%d zpp(jmK1Rr^wZ`fsYA8Lg>F%$c#zDfgQ5tL=K_eg^UO75b!=d2sOwg1U)ww9OdLN>* zKn)=Q*j&-)5HZ1}cJ1iwf(#?MElMA39Al8Eg5K0KIUddi-XOv))MiROJq9`Q;fC{8 zi6W}G5~0n-(8^!zWsTn0brOHWeZo5?U3EW!Kc63!{ChE`~%D+$d5JEohA3+sVwg_Ry zz%>;q-PQeo)mL9w3$(gy?5Q_(Xa)rn8E$FZFf9cGjNzi9U(nLM51&y&#d?c+i>$u- z&{rkin$qVBs3!z@2m3*sjK+ka86qCbQ;={$Ug)17JZY}q^CRYzwOwz^zGS8N%YzZ4 z%czca2SB=)RzbjSxpJe`%npPDSRT2Lh$&OfRq53sx*DepVvN@Mm88XxPDamRyT{a8 zJRw^gni#sj*ysLSh#aLN+1N(IwV+;0&N&YlNdBQU3cNhgz#~4RAYO30ApF)=z77fz zd;qJ`rSjpx~z=*+6Ky?I9aA94j+vGvEYivg;DU648dsb7~uDZuuw+jwo|bn zu~}VdB2|n@JmG!kC$lM|-T9D{UU$8B+T}5mCZ-;FnjD2;#kOGIY^djwhw*gsI>y>UzTuN?G9?w! zf9gW8*m>~}o-KmVoi$W-#c_04acaAAbxUbL5Drs$j%6KFoS7or&Ic2+lPkZ;LI!n2 zgoydbW!(w-$J`kflnlvTdJZ%~F)W$!0nt6+QoGT*hxT24dC|FW{S=qtQ<;w6MP(Ju z@TftB?h5z#=|I)Dw%K?Xzs>)l{mH^>vknAu0XwLL3Xi-R{AAGuD2;Hvyd45`B=BM_ zw*6IBgv!`od>BwB1rIaMDb8E)A!G^S3a=gTEWIUhm!TGL3PjmCNGiXiP`+{y@|a{o zOP>z5e-SQSr2rw2iCCVxbvFYpftw;asE+-~^IxIJ)bWA6Te2G+Bc6{EbtV>(rx&-u zA>e>Nx}Su-hk>K**Fa7~uRONvgFWqw*9}(fyBecaIaFfWbb+%_6>o-uEgusRX@q6( z_~6TVR0q$<($RUFeptk#8RwxxX`af*#N)x-r?}7gfpBsfpm;$z09&H~rX-s(^5?2$ zGwskoPyx5(t$HYwpkoi2%uyMbr%_g8bh|E{0?*L!px2Cg79}e!cT5FOMHAQnOn`68 zDTEQzJfs?Wh>}y=)&2`m+u$yl3f@eYG8~Y_Uo5k%lfV6m*J(pdh@Rp-V;fy|ffJ$} zo~B5HWNB`E_HU`pKAOp*5=LQHL{I!LI|BB8YiYd{o^_Ef;KpH-4Y6iq;6&yg3q!1L zWFhN83YWtIbE3^XBVP|pWV8Xl7pZ%}D(IL+clh`w%^OAy*No-k#zaqg@0Jlbn;PR@ zfF;2n9tse_it;DjDnY6C0{l&#(xeF(yNBXYTbiQbM<8bc^1=y~L0o`xXDg!=&UWw>qYU?=w zrKJ>FPoAK}H|GLbIrV$&!#=G>U%ag8k?#>OA=wBGIR($bwEbv{;v=_W>~D;dk0nq7 zAT$;59)zyz@el_M?m8~k;Q#oohVy3I(SW;A$I1sGD`UWrPc6&TRlfbXpRVzvvQ>R# znzn7(I(D>?0;0}8<|!Zvxl|~iobh+TelZ7p3hIHRtJhzSjvyVOqY5ciCW0}EUtX85 zlJ#c1$~I|$1cEnrgB71MZUqzoODX6++yu4_VRzTaPYFS?;`y#{C4ykXZBO7AfBzse zX8(RNx44(Utf95sP{6BgoEcWo1&{1b&Wwl8a|cL?kSc!E&qu6%>u+IkApCfIGHVl) z2GTH!=mN*2Pr*fr8V|DEqIEIbDP#p3E}}tJuyjM{(F17nv5jI({9|@wZ5m?LMtR%D z?!Z{43j)r+3jzZGTwo+}`@lWavJh*c8_$Q*$tcnR001r)HCbz3DYC9h*co66&!1SQ zw{Ge zR+R?F)mUlx*5`aStpVM2{?X2zo4qT4r$yU`0chgbJwt0*Yq1Rv1QBjhh5H(%#qXEQ z7EfuJ?v7nhl8h;y)}TCB6V$`yIxsWLanBd492O^j12zey#6?`d)vyML?WdXx=m!Y< zdr_1XBqm{{d_iGL&v*l&+2k}#e(6(0Imr0e34B?(t%qDB+8ZL$*dg@yVe(46avn++ zI#}Lg5W@<;g4;Z**_mBt>U0)AO5T2!UpK@iC1h3edk^m0H&^ULOs{%%0uvNNNZ(>Z zXehzYBfPT!!FuAEUS72B)4>A+YM~`Z{8mn!%;JO+{=k6)#E_=@^$b|aEk$H7e6%2` zUNoeq&JV83&-}O0YYk^Cr4!ye02m%TA+R`E{dq7Zf!sFoUB66S_kDAz*B!7+IA@H} zJy$YMRuX-7&FuFo*{DEyGFj<#W_?}gheAKjrF6pVkAeCuD*+YbBc-E){2!!~%+K@h zh|-FN7)RcdAYvqwP6|Hh-CxK;F~p4llz5RtsV5VqrHZPuIJ%IM3$)b>6;d~)@%zPT zRO`H?v^}+I^7 z(f2E{-I`L6CQ!rClpbP~T9|^fud^cFt1k6I4+^`4-NFU&=Nx(K)=IF53u#mS=Ng0` zB2B(nJhaSMO(!lL^H=o(Pu~W!oGpIK-VU({=9%SD2d60|`ALbLhNb2?;elNxz(G<& z-qxZmkkB-VQDUUUlCI}VSZw7FJGCqmwQpm{g&QmuF%w#IFGb3EdS>L8?30zSEodRn zu&;D`U@eG>0kJAk44&*>;u1F0kN&zj4R3ZJ{I{GM7bRwuDQ9Buv>w zARQKLm^ZSDE=nM4tEk__GAUUl|32X}b(I5`WyWK9a8AtNodRACQ z17reBU4)`%sQA7jb#~CYQ=Jmh4Yx}(D@cI+RRVENCY$tKz(FGG~SjJ%~{TfEQwc+o15@I1QDTi%G#yM}&w zxlU>T0tX1TuVF_tbioZHzH1v=W#nK#^id^4hQ0!*+L~(;Rz@lCq4HxQ~UNVf+!Qdg% zTep(<%YBKAs(<1vY$8>yE!dgFFsSujG@MJK38^KFX{>0_srf|4P!Qn+@cV=)is8Xk zx<^TTSekjy%j2j#xIRl46i+cvb-;cCLQPg>x%iF=t&LR^p8gTC4-;xKs#=<5C59Xx zDPu8B2!x4(bBa>uLq(Po{Vy$VT3(pshHxV}dO$1OH%~(qjiOidjGVYQ`qDf=MqfWq z7F~my4#epXgHqGl3vP|VT$Wl}HPw{qM>qJ`BT9^m)mlU_mpAriodX$pXVFzOhAwIh zbzSIeW1w2gb!`qoxs|V>G8RA?cY+ zQK04`DhuA{#_h!P+qPQJZC^t`7P~TJK0LDVYfDiNm~xb27fSICAgmNrX&S!YQ%R(WaDs#=D(J_k<(kS!Q0IM_dIu(1Ok|$^{3TfO{Qs5C1B1hbT5t@t$oDhor*qN_M;c@zRX1?g3R@k(28)FrztBA+lwI!2t; zcPI{Znra704#oZgE4_jC32q4h#GOGm$@| zl~+5+&Nm^^*9OkkKcL)PSo9CTD9^;O=Ns+qkKg|Y&`PlYz6i#q0qQjWfbKZ*l`1?T*$&+u<4 z^(y@xcxr=jx@5ExeUMqO088e=Hyxr8F~PfVnJv-?xFm`Ut;dd01+uuo5{s5!=J?a*UAR*<##$A3VV*>{zZ)?$BIF~J$IzBaP;j6G5zaqwl7v?OSb zmKVA@1qommy55**CVoTdToV4Po9RC>O0TV|RI)MI087R-@4yn#yj3$_oot0GVQRMY z-_i=ABl3yIgtSaFzMIZ*`YBtXQ=k-oi+H%Oy zx|^vzji_hqms4tcUT!8lj_0)#X)TRiXt|{@nC8{njMm{|QyXi}FJ5VBsgk+BKe2q7 zK&pMQVKu*S2-q}bx1U2^W;I?qF(PY@NMevoSHJRx{6*X#4!d47>$0~nhr-sF-j0j? z01Qw>5$c9Ai^qvaH^wyHPgo4(2zG2$XS9X;!>wfU&@kprq2V}@s4LSX5vAKIK3az8 zFD5VeT(*TLg06MkzQW&984H}++)aI^vE=ihC|0}xDk5~Gi*A0m^PSpiZUwtT;5}9q z0j0*mO!E}XKy?-2q%Lp?Gztj2d+gsn_?k9M?hQznMq|L1~CB8G1? zw($%zOM+a2G%OA;qOL2Ltnd=YU%T;8KlL~{$3JR6@Gvzr4lbad8vTmtH%%e@QL^~B zXVJ0gMT6`ByqJERKM$#9gogze3H6mGi%%}38w#YIA2(CT_Ty|Zmx@PZJcI)vGew37 zUzf>-v9Rs!6IVEBsY=Vz4K0mJvqCIJ?ed7QJ@QBajYopNB6ZkF0IHYhB?PIkv-7@( zI(y#vCl81fm7;`B{Q|@ z))^h#vXu(8&}jne3zrx2k!A~UCH)-Dxau!oL^lvjl<+rOo$?59rtz*zJ*^VBe8SID zBVte3$bZysVZF96#q6VL7*LA~CB|IU@dnJ;H@HGqR#+? z|Ava2T*(eGV#fiPmjIJ7jV$!)3nK!II!rNk;2wf}Qbs`&RB%$W;%|8U1OgtVK2|BU;vGJU_h!9sg z`mB(TpCeTgOr13v#IHv0^xz!lfJcQe1M%dM!9W2@x7={g9yi8oM_f;^m8g{L*T6tV znb?}|Q)UbI9$(G*B);&P*a1P<@MH1qj7G@@ile06vjG_Km3@T0(h&Md#YPIA2`F-k)z; zeDE*0{!a&Ari8&?*fJ>H+eFEEJ(7iSQrT`RuO=NRy<ZK7cY7F=yVZXu?RrB7_~#7O;SqkBZdxkgebW#ZcfR9wT13b~kM- z8pW^{H0;DC)`UD%WsZ$c;pV>?0Aq#WNM+QG?rDOjNioOvf1L#rk(V}-n?Y3P)6-r% zw2JEK8X->qFzzg_Wo^Uu4?^qWvrg(W?N%%-L<3jO1eq~g0~=7+>v$B&Z{N{{KNjEN zT&tniQsjxNUb?|2*A^mN)S+T6wYF7bd=<18CL{g*KC86YDNSNn=I=&$@$YHwTD95a zZgfJ2;qvOlfwsLz z$5H&LC1CJNl1ij9k4~`EUMS(GGAPeojKg?hj1C;(4V&J8wB*Sis^B@ZOLs#zpwX7R z$@)pcV&J5XhETeI=Ay(DJZAGjtJ83HFB#d{kAf>mP5>Bwvh93B_o0Zk9I=Bjz}yTj zt92%~1e6v;>}5?{#d%yD`QdKoHJO|l;Nzb_T^%&+B}&}**9=PVegw#l)o%W~hVw?6 zJ=M(+S0-n#GMu_~k=1ijUY(J#qfZ6OLj5xSYjPf2TPay7odk@7_EwB#Sqz;1n}LsW@~|+;NuFosGSP)Hyn3;T5F#PJVJaVu5WQMI3AKAzbj>LQKT%vL zjr`x8Ch(FKsS0W%P7wG{g$c05RNEG)CBcho#>R!J*iU0Dc;M@-NPwoQshg=owKCAN zVqxwl&$V4;!XbW(>`uf0+aPJ0s~HvwaCOnEHsmfi?Vigr&j@>hsWPSCXa2iidj5wM z^-IyiIKiy4P#YK=6BL@gm;MmJ4Mru?p3kQpb$2&er7pTQ&myH#S$yma$;1qN09xs? zxB;Z2*@)=b^YjJ1igHYeOBQu+26Gve5NU(|BL9tFXV+ zJ3661P2nsKNZan3S_X-H;*f_1Dvz36v5;G-CV`1a%uYDP@M(uf5q4=-669hfiL%Vg z6E(9Ld>fKirR9MPg`&`*w`#$3lSK~3(<_x}KR$8@m_R@$9`ukIz@A&hERvZ+UBI-V zW=kaU3Qd`zm`~+;dfL@DKF2L(o3kLB(DNkrA=D>av(P$*1KQ1=eR`^_S%3?p_HgKiKVF8 zi9FpMWN_;2nFD74NLB~?0N5E<;t?dKM20WE@Zt;mUU~7_gRAfT`GHp!5p+Fx6>ywk zv2<%vCm(i_-VQZXyv%QC+HLk$c56A(|CE28<5sFkeVUv4SR7hbcPrV_JP!?{C`@r~ zXbf-|mOc(ml)YN`sE(wpum!Pgf6&^I=NnW&!d6{;#Tb;<48Q-*T`UiUQ{4$w6*_CE z8-u4mZcZ&|NBB}pU2>l3Q|7Vwpp#x#vpLbcp&uIAh|gf%g`B+CNz`+prNXiZz^El_a|^w?7VZh)OTy zUiq2Uj%1)}@j#d-7SNIj>N;y9vdOZUa822K8GH%mYwW~EV1U7n#xp67CYgq#B>kCU zmg>Mh?gas#x6Mb>6=W`?EYqD`{|BOqfD9ssPBZPqw^*vCTL7autgxqk&A@R_VC9Tc z81Cy?QTX;wHi^XYFGmmK@_+#!OT&j}IX;yv>HhuEyBJ$p=wumhDxL(VY2ij2ldzeW zfH|_>>O1GM3WpS83BHt?Qeyz_Bpo)#0ZM2715+O}#CeTv099cq}Hx*ZT2PKI-AAY~C;IG^NF5CW$o$_4!rz98F<)x02^hlmMyFD0%< zHeRNd#Tdga@w$|}|57*08c-r@^J3qvb;Ro8(Tvz(i0noNx=_T|PePE0%PBr~5rGsq z*}l7NaIt@B1erDSo&DB%!##FP2at0)6vP!$2sJ6$F_%}A6KUfK0!-Hp3V;7bkO!Q>9YdIC{ zBePSSg`|}#zRDVwMg3RK zCjFe?pD;KK=XUPYQ)9PXRdbt2da1xv;d*BgRTY2&x<#Vu?A|xp$%Z>uZK$j|8p_}; z(MTbx0%yGV5v5AC?*xBkSS{NU|DCulh$^`%j%n~RZKQTxz6B^yio})9Bk?uqp#Lt8Y}TLO zv{FQ3u*xULe%Y&`#{ghUklfQ(Cs9$s-gF8pzd)s@5XuqXpblB z?6jwdt&qo;FGKjFT!gZtZ_e;h{v$dBdbXZy!uARhK_$F{x`f|Y zQ7tqG7G5yzAokAAP-$v~UA?x3z&y-tZ_Cb<*1wG2?1Xj9#R?mS5+hU#(LzW32+Rt> zG2t49FcJ-;HGG&=!Aa<(@GklZd>`#c8d-nhd&#XPPBZ_tYhXtFH!ldt?r&q2PrhCd zjR=R$b(}JWiDp~soRg1XrKV}fW{o&&|P6`*NVJg)v7KFV1CUWn+jhj8wZ|x`WBj~=AQp@IS@`4CUmIVtM})w4LBV}RR?MNKY7X)d{!uH z+-C84hBKDxuvz1bl~IlpCv+-iZS`9Q!D|L9t;cmNd<&V0dleu%4M&R2xU8ipL)bIR zkPk;2r!1R=#%fv=7>vq-gMt$tv8yU_d8{IK&NbzbQoU5qSo^_9U_wf_%k$)kxb+m} zQpMj_#)oaTH(A4VdmRN*4@Z9wt)J%tuP^}*C9>il9OMP-17*#IM!jbF%w=lrYMHV; z+bt&dv9LE+u?l4rcWCsql_~?SOMzesd5K$6`W~o*VdBe*)SY2r zl2{Q*yNofj-yjZ$AYQ6!d)M4K{|)ouM8g4CoQ6@eV8&v2l=ma4G;*(mUV8$3?CQEm zA8$a;%06Z$jj&mFq;BRAa9ejnL60|GfT99lXz1RIi0b=skFX)|HC!5I*OCBPRg$I- z8G9zCzhfQ5@ATF|@ed)#x|Ah~f0&1{8d-3V%m8-MeF*SE=s0cz6*fqzz-q=rU~?dh zD)vSy75<*g1+35#?hP^ltP9&km~WB`cOn3rREjw~0#3|jTG$%$bKPABAV0B;EwCmd z5NfiMo`l|DJysuk(#kYu!B_ZpVLNrv=g+@Dxx0jLqYsg1jFqy^SbL*m38v%sApgEiEZBL&;(>ze6s}2u)dhf&H8IA*qVxu@Ine1?URlt#ZW}I|l zGIZDgD8*X*_aBUwB_f3LCE%Dsj|m=0Ou2vjy-_HXRN?bq3&tdnjsWe05&+htnS7Gawd-Y6}~KzamAi z^+{9otmnz-B{EKukYeQ4SM}&;AC-@DCvF3Q#lL9EKlqF$iu))a1*|*x(MRoP&um(} zSUFjSl41aJbd=v2_7Hc#-Fd(`K>0Y@z~! zYoaCd?QR7Iz#Z3Zn4XD3ml)VdmJ%1nn%3iOl$N{BnJ{B*92RIYs(2Eb-)zA)7R)Tj z!fpE+fVGMf#=*h0n~Cn~$_0uVn(<|FKXcLTj?&_*%G57(5qi2TY0S8Q%Rne|7R>Yu zChOU?pp7F8+q0&s@5G(U&ot7_A}aUpeJsLc!Q!YiQYsUn36^fOL1RtS)eF;Lo>i>U z{$?OdDrH%G#BnH~3r%J%A`F92(TX_!x)4ot|2!OgMwlaJ!8b-p>LA2ZkpTF~H# zkx~I@iJ{eYGOMcp8axy347?Keu5U3G%GGH-NqmL+gJG0VHq#D-RWDq5IJQ_CM@@t0 ztV0;ZlhVR<1`RTinSe|z@B-i>o0>aBWtaG>hN}^!HF2f6d1j8wYc$CA7LsGhDLm4` z@?^qLqbFvPfZd_BG~at)imA{V6+7!Yle$ig@Nhqg)7M2zcosqut&(-5q_YUUbt+)Y z(Z-Kg0Brk48|P9rinTwB=Rg&)aA)dmL0*jYx3Chq=rTh`-=yTWEBj&}h?hHOvq>DL zZ9stFg-I_IG`)YClAg>ooWy$k6J!%4k9-INkMeAQXcD*+D~=$!hKHt!S)8(9_gOU(m#^$HVQUPmlJEwjBW`>FUJNs*30&7l_D6uy=+cK0KeF9&Un?YIz}mYh<7s6^nAZJObXT z+AC}ix(D<{t{*_^U&0X>aUoNWwjadsATL=%1<|pW`cY7?SFKE4i@c6bB$r?*i$u)X zPe`H&LvjX$Ujkrrx5)A|ru9uKoyW?^n%r?vsLn`mf%RZPmD1*uTa{me-7}J9@*LBm zNIY2UGjvf3Gt_UW;xp5eDzS7lSj$~j85`9c@~!)x8lk}qsB^e(i_D5qyWXur9oQFC zlQCH01fI~Kjz)gC$#Xrij8H$EA{`uXXglrJ4OdqU7qnxr%P0?! zHDq?yFhJX{KuffFvK6pFFrMF@s*ugM<_v(;#y;*!`u=Y@_Yp^?W z!GsHG{f&g*!oFBe<_=v@F(MJABk$LzB_sq;+Pr2hk}#6EeYEed)-Sl1!!XrD>WB@D z7^L?JT#JQO4MKVcJBP_bQ_-~tWya9~aUN_qnJ-Z{zi5Imi_K}C@xH}9V)CJjlSg5d zT(xxd!c+b_0@zv2PTNGy*nL2t6brhyt7dafB@dV@o^wc3c96T0^t&CBrJV`@INH7Y z-q3yONg-6ES->C^c6x2_Wtf^JRCfH;09zqtT5|8t|2e{)ElQ;++wGr{-%V6T_}J{d z@XGjL_z~nf>`2ECwId${zr}%6Vi|f%ejY^Pe?SD^CYHxp2yOsul0M?A;IA^mB%oMA zX6$*}9=-_JItxc+5O486i_a@~6oYb!WTN?>Od6nsa|2>MyZGnleg;B3cjjjY7oGcT z4|Go@mIz^Z0!Y$Khks`{@16&K3H3M7M+7OJF`O1jbBQzx>PZ?26?hcjgkT~h`t2(e z+YF$xB(NU!oEAK?hI6tXbgJ7BSVjtK?nA%zvx(2z7pxnsnzs%FOP=1kUmvUkNsQx15&BGWJ zH$*VbHnOUs1567kQWQ!tzGLc+@okIQftOHL5EHS?ZOvGbGI{xGZviVhyy1n0>y4Yo z(A4yd{gfKc5t8#secNJ)?89npfThH8W1O0RDwEJFQ`ogc+^{yXG(kf@?w5zT%Z?6i z7}eP_6%{4eF#>zF&s33%h#(G<6(R}p$O+<)bhqs-DEMtPUe@3){=!~b)H6{CEXreJ zQ+c(@uwtem;#r6n>|ggRXsJ7%e4CJo2kujtin{aI4DrFu&@$E<;AABwP;HV<9NvS( zs=#!{Ss*~X!T?pe<2Jz#7VBw^6Jw&017pQfdhCb0UC~98=TQZ|nY>Aq${+A7#u6Xv zayOJaDqkMeh{m>_xWzW@+(5dFcq+@#nE#U@V)&>locWL#8Q)MKd@DK? zv)HS}P7#_Rrocy}k7d-Fj$9b)BDRDmK?-QZi^NtUa(B;aa?pd)6JGPv3Mjk&JI_k*tF&!bI&2%Nce{}pj+AK(|Bp<9Hz+mXHw#$;51mL zVxLoGW#5(ik0g$c(wdh>mTljDfg7@ey7S`*UurgQE%xo)N&7vWg_jKqAEOGTA~8ml znWYlgY7N#^VoLF(xHgEJprbzm(Es$Uqx0uK<^82UAd>hzLVfUIYCy-6HF||dO;YC^ zSxO-RxyBpT1*u0!>nvh0N$}zfkaVZQJWLa~TL77GF(g^mGS|CZnvRt8Rye3do!h`9 zr3ux^+>1m$1KpxYzyed`=u4E^SUc3zwI42-EAu&KAd7_InMQFR4mU6clb%2tHrhLW z7oj|Vz~>amIGDC>K-UbG;uk@{`9V7Jh2XAX`wlB5g?DbI!cVJF6P5Akh$E% zQV|x*b)1E-(xzg|9*d`wxdvZDWXj;1DjE)*_#ScjsP^rtMoS1z zT#g8-eArx$!>^O$O)p(Lgs={_6yk>G`m0Nn$h&Im*uRzkDZLBO7=|V$z=|Q0s4R4@ zL>=*A9PQR>Z1PB?qVR~q_7wnj(3` zc6lavPinh~F)_T$Ey!ndG`MiZ7=ScZms}9I$X#hJ7&^YeieS3E`jH6Ndo||=7Opdq zg+^6127IZ+fQjevGT1N<3r};dAd%9$e!@ezTf0WRWzSNdFdnUq10H>`Q_QM4<-|*> z1@+mU@W|kkgLI70Hh8qdf+y(xj+;6 z{lrepRzX?BGgyuA)o9Q`Fs9w9MKTz)gaV(ENW!zAjQ~Y#n#GM)2Qso`6`w~4PizEa zk|6Sg0ur!0;aA3_L;#Op2`8o<5qMm^e9an$<)TbYoP)55hR)P?-}ezAE|?pxiZ~Y3 z*L_q70HB>Y^Rqf+^HN1+jDdi=2wHkz`ID--<9&&tz$AefgY+@D7I4VWoOKA?J`~}h zUZQz$sLDHKpp=JaigPg_V(`F@(0q_vk3X?zkBa_`o&%2Zl>ll>mK?lVd~L9B@7Ji` zum@5Jl{f{=`HyQzJXx*_mn6z1;zD4D*E0u|mc(7@&dmZ)j&XkFL1zWfdK_KTvg>%2 zn9}KQXitCFX_86ONcB0acy}M>NHS?uT>%kkgE~E~Cu@v0@b9wj>Pb3s!InQT6F z2$snZyenDth4F+hT~3m%{ocd}CkS}(0JH8*r8=oRXZNo4EB)G7(Gz1~ad!_u2G@Z; z+C-jy%#Zn0<9KsUro~W}%<#MF!*%lX18v%-!3uk|Wo(2(2=tMA`r|ii-zGbbN_=f# zIv-a44t$|r5gQ)@?l%0>$PaPmo9p0^*l2x7DdOZ|lTmfxNqqYX^>1`*CT-J1%zuPR zejh646Unbd8qWpy#u~l;vaJ_-I(sR1$oa9!A=+`ESBbJ#(V|512%C(HV?oN2TN~G6 z(I@KmboSOoY?4fOs5#9P##La;H3aFMy>aM=q1|Rs?@sAEIA4Q4_=fGToxXr0Hkr6l zgclYLLd0eK&i+qmt-IaK#cIAw_G1tOF%{B;2n&^d4;M~`*wg7U&Dd5AY zt&~^+BXPk*aJWOJKy8ZR_6kfw_%PAfHi+t*(GD^x-i+YZmO4(jRwaK|JN7w5BoZJ) zFe1eTufhxtQ5d=KX5)Di9!bUnKZCxDeZ?O(N7S^f>eg9!#rtQ)F8e#wn4fp|lqhgn7c=XsYX3x_M+aY@EF>BS7yFfq> z2VJ)Ao^^~iERji0+%jaae9OidJ=4EyQuokC;@T`fh&4zjQ9o_{mGxIBYQ&dbHCh?4 z2{bNEW2Y6Kp%`pI5EUm8Bt=`v(Wj-0fQc7IpH{5xGb$ntwA2d{Qp!Fa8rq1zB?>AS zDlgl(UFtwds~C#=un-_D7i8!9u~l>wNW{$_kuYEcE9C4z)u?1(G>oMTQIO}}<)jde zZLAI|YQB>Hs6(Fub&Z7!SPL5)90WD}%h1;t`7ykC*=KSRgkjuWzASOET$l=|E28hgR4B#GtUW;@ev>pm|zPuwggg8?;sot#*kT;*ZPQ&P}+@? zt?lqJ?PM&krFeya^QJNVuC1`Gggl#9M1&dHRpo*{nv!bf(&lfd;W`QL;8+ zwkXoJtj-3PYb$mwUFD?;w7=i|yyr-AdunHGS(c8@$NN6-^FGgYU-xxicOAGYAtf%6 zIrI4K?P(iATyBW@;gpaMP@s5oTqSAG$GHdS+p9tc)1+Ep~6qN~z=d3iambsk>Mms3XdqLZK zAYLPDpl(&97neYd6O$@9EKO>dIlUQa!Pdv-H}}gN0!Va2Xtg7uHu2R|%ZpglPE*LnGcOz>+l7A-tx3QGJb?UW{zeSpQ_RdIZbgMw4#?wK%-0?l_mA&@9B@AIQm?2i5CGD1b@5* zXNH$mf*P zMlHe~?3S7`PXY_+&Z_KI+sy!EgPwj09r&I=AeRsQ6V3Ai{0KI!qcfXlnVpcuQ@_p# z=fY;~X;K>++ z`PU(iAp^H59p~6*sLD&~j~~X#E_iA@LL|p*yQ9skv#$6>0aVm_$zZSi`gcEG|7dJ+ zwu~j?&kaEzv#KG9j5^}_>Xu97B(NNCy|rK{mL8H_rIt$MNxE2XE=yGb?O|@bydyr! ze&+!%_^pc&@b&(wtJbnqHy`F1iMYM^#{R$ixX@OtC|O*py|ygEmyWEN`gR(Bp}EDpiuWFP+nRF z!#Fo?|Mf>hzELN9OJGBi7SLWu}4OwGJ&q)ZRHuRMBBdke! zB_I%t#LC*1m!d#j$` z0ue#hG}4!Y8zBOGjRh6lTXvw)JM2_N4bTD?iPQ)BPkL=ZM?wYS#yjS>_f3{n+{$l- zUVYck)bCRE5wtVBY&W(G>~_)_fTK4*h=MC)r? z8#uQ*bq4J3=nmNDX(I=sWu)Uo1PskrZO7Nug^;Ywl@qu+xp&}UJT6nS0N$485>s$1 z>dcT`jimC178|L3nd_8lbhw)%#T&(@JU3NCOO5qc!C5u>jrEs6cch;~3l`mbyzjO5 zA|!KG>FLC5U~Y_9Jt%$2?bB~PFD-7;+KV6n+!KEHo(MM!b72ff=*yX5yH=GjKCH7V zQ;t->K-QJ8hCs$Z;+o~j$f&b065!u~H{YwR4ucG)kmOb(YdWS88E7$>xQGc`9uCwPVaNT78wyiO?QU94?EWnY;KGz%bzye@*^RvQMIgzzcx_QUkz%PO>Wq zqD#URfwz`PN@pMX@K6YWv^W!x;Wj?2cl84+?lv2^?a?ccQ8WGLdhg>6IGVwyfi9pR zFrXiA&$}ps1N;aXqjc`L1#k&EaT_q8%WBz-xHf?!Of{tf=_g@hR zzXTMCKIqkJNlSsa@HSRBP_V{{+B;>KY+B8PhTIDswq3terPF}8Y;*2^A=Dz`uh8fb zn2-VzR{U#N12?eRdRGT@@L8-)`ux?CVVgLAuSK+Bx+Gy7dp+A9c?cj>q=Y6Kwr(bR ziiKe^IhgCxgNm_5Q#tCTKqE>4cByfVVz~x#Qq?#Duit6elpA9y_c^DmT`sG!%oI9i z7i^MxEY`vDe~s*WN%fN{;Y5HZBfs+A(FcR=4^~3~fUAp57+8H&jIyp;QAf_F`Z8!v zV-(w6gs7c^BXf9I7U_sG(zuBP+*PvfumCU;0jy544+P`(KRl7UPT!MfQ5ANgMS)-yVkVl-gSGH zkdaG=Hs?`4I{R+TPY_E358z;aWSt(+lAtj{=kZngZ81RH7#rX6f zDYvv6x-S8MAvE3mn0LFFXz{5D1Mr=BCABv#0Syp zFL&}W4!9GjHB=4A3n0ajd2L7fvHG(xLur11y%{`4-i&`rBM)JP`2n$r4k`X5y!3AnDV}0n&C++Xe$E$f;x#+L5zj5oOp2^%Hd6YPgeDzh0_C{11noz`j z7i2A&oOA-?apoAylvE^3QRWwB=}9^f`8%jUgy8Q{{l^<7wY@)ng;4?7BeuQ&cx|02 zvkMoGZUKwzci*3(P1~c0my{rD1uhFUh)01qg2Mk5+t}hEoX(Zb-wdd^Jr~-t&wKv|^KlBc zn!~6MIttxM<$FhekQ6_LT_dVR!YU=eRb$o5 z7u@kyt%}}S`fi(bYbUUcdWZ1QrH`CZylXnjU++KYrh#2!k4Ku^Serl>qq>Pn?TSGk zQ>myuR#EWh!t(MnmW#;-&md{8B$_z0`2B2)2Rc=xvy1rT|a*kZcHstYGZER zgw2W5;!`$7`7-f-B<6HcVck*1&gaYIk1c+Q@4SQ|xEx!5AV!@Ph!d3>+=L7as>}dY zpC(`*3N)cej8c5V`N$SHtZld0S?JJ?L@BqHO9xoua`ctAZ7=;%59bwUf*4a+!#+>x z`ITPO5Y>BljnzCs@}UTl0KZkRD;*6=DITfsY7eW?w~GCQ<;^kP(|snsFf8B{mrYX# zXPKPb7V$jf9SgdftZbA#j6OV}%NKA*g_Gr$pi}@01k;uCcSQ>2AQ&gI!gPK`#GeEd zoJh>ad$6nrL@jOuBXtgKr9F!WVZeVm5iSENTigrDZJ4TyD{c_W0p>wakSSxKe8TRA zt!^=|uOK!?lJoFQwm<~Ekzo3i?_5+Vp2vxs%{HTio3vDpJEWGk8R6~m5`T`24f!iJ zX*Z16JEG)05F(}W8y^LQ?~qs1lD`oKl$bmOU$0yULxMl)UAu#`Q`^giShseJ{daB6 z-_v(ceIEl1=ft_XnOW-B)z!ws%y*qTsE^Vwo!ju}%m8N&#=}0pmuY^mCiG3@X;iSV z>yMKGkr)uEo-iVc+n)a$Dt$dl`ax!=#z}m>*uJMB$5zT|DZe2VlxU`j-VR-yuJo!$ z_J7POfD4nAwEFGvB;|mNaoEtZxlx!Qm>#j^`In4LnB}%WGp3^V&BCUby$0`pwdJ~$ ziapIK3KE=GfA)nQ1ODrg$7&=FGW&**++sP-P`Nk>Pp`*Twr(&6YZWziyq^)>4LVg@ z>c%CB!ZEddezG!dD=1ABMGPj-M|5W7qxY|9Gz}}gg>z$0yILH4Lq5+Xii9g`>NWGE zT}x}>n5a3}v*#M5sjRtXzcnwt#=YI(3<{*B`z0slG<}V!=ecq^01g|Q;B;bkvXh&6 zXj%>vIOI9GD1ocyIljY+M_#yp!}_%#7SfF;80l6Qs4yFsHQLmSl=bbAH<&dnqV#JZ zz32@9?KJ$1pk2fSDndGn;)#aq{aTtE1)9ljzACw2<<`IfIM?Zr$w|ekLWC?WX5tw{ z*V~?lx2!Ki3^nrg>7ngcm^F$ULe<2S`ZtxvbP-{M(S!myODhIMw1NyDsa$Ph7+4N|b5MT_196WO-;1R39ObCAa$B&;8F2yG%Y=hBxWVxV51WvK% z%byIA-XkZM4vqbt-->Jw0ARm=eMHkI$t0eo6_HPgeck8tY&k5E*wMfqo38__OvsV- zGr)PS2OqBeeq%`@Cz%3aIEtD18u|y zs$&u}iI|cHlW8-=-T{r`kCworW=r|GkfZrqlq|A%m(D2>#XkrAgbBpi_}63M{!0H5CET zSN|Y3P3&WVni6JR#9lynBH5AA1b3dIbWYd*ym@Z!LmBsI_e-vyJ3wNfpTwLT51X8l z$IR};7%z7E=)utf{S6yk-skqlf67tZ6@RC+_WU*tidlRRl-!P{Xok})=EP%Z)xn2H z#1P%y;BZQS3FYI@ba(CpRG9TLImGhROc_RG8*FBG8fDcCzzu!_-@cJIShRvaH}{p+ ztHxF9OSIUDHIno{Z}9qg*KRPB27?z?$Y*i zT^y@xl1Y|Qj0ypR(EwO~pcZ8B>c>OzVWbb9yoxoE5i!4Y$z z6j!gK$jW7F6KberY8Z+r9+XGm+#SXxJ6^U|247XtKn*eU*Y?3*z)~cz#E$2NXTH%g z#{R`5=OTN8{bfqn!Qhba1>dFwOY96HCpO8Qr>Kh~yFoG|^gCoUqazqoM%@k9)tLZ< zs3tk(XxkC6dR%c28vtk`0P7w|Mqr@{(0JUIE^f();cr@51-CSj40zZe*!Pk8F*F@ zB*DY2Ld7}25-n7-L;MWTsD}JVNWP&S*x&F{*iBcJjtOSfr7;pHZ&GMQd&qLjoRm4W zfdM_eO}Vp?X$t27&{c+_^FqG0@9bv>vDh_y?ceY;vb zoa|#$4z|<$;>@yVW5$sZT7+xQjP?XF4Fe40$E47?T|*e=1(Rw@AJ2vtN%$KWV<@M# zMUb`R9r)Ig?|&9MAPMB63X@{IZs05^$PfsMl>s&)2#N)0oOLgkn92MFO7i!) zq;Ym~PINR9TlGDz;}EB--iZnWZx0Zm?530ScS>~uAYh3bfjcACOoKViE}_a&l|FSS z+@};+0!zXlGS)MjXL?@Nmc+6JUbhIuJmu7Izx@mtOl_?;X}U#iBG{#aLRwpDKDO%P zt4>jt+lFx#;OIlu8b+fXaR+%Ksk@8wjRHDHaTW5n{^_6ub^-5-zRtS1=zJBXw>nGec{)_5i;6pU67#7{8;X;MDOa zh&v6O>F5Qe9Qy_n-JndKmBlC(nZEIsbZ-96gN-B;MxMd(7J`-KwRMIYfHhUL{>lZ^ z1jjb4*JpeHI`Le^6Eo~fD7p+n_go_ZT5EIUG$m3I3c$(V!BT{6gnFgeq3qUdQICPI zCK49zn{KX1qWkZGlL4$$zAt-J$-a9$>gg_!g99HWqs;5DJ&~P*tI^r%p3PyM`KT}e z+eTE=;rkfiCRN2H4k?t~6-v7Ii-M9XlB`oon+0Uzs3ex6JsoN$(3TL1XGG^bsi=gY z0xSg`#Us@ACC5wnDCrvC0pjV={g@P8IC}hPTn1L~j$k2b00Tw*&_cX5&CyJM z5$$v-pp#Pqd>~p^>Jv6fX5AQ*Ar**?WZ=E~U&VCk0-tgJX7p6a<^bf9Fs>|BKi4o(gP-;O2Ewujc3>cmP^jAZ()q zA>2fU5hFEP!=d-sy0ylITb}7oEW&w2Bv+A-_|df|YL*NNrdTgrAPRt@ofMn9{J_KH zS`yeYQnCu1_vmovKT6);w^fgMh@H%Ydi8`oFv-cq^3+_uzq5G*WQ^QIoMK6|-y)+R z^>M12mB~_ZJaX->26v$5irl)T>(%RVqOt$o;cry_*N2-A?|ll?;toCZA=}C8F@VOX zQ|>DsV${348pYD1mX$Rt#mfx$I=OCuRo1y6$4&?5&1z&GGT-kT!0F!#Lv>5wrSSG2a3#&Fu+I87W4S<4mALlY zZhx?C9g9(#0jxhZGtYi2{s44k=TK`xCQK`WC36Oag(n3^6~6%Jq9#7p&{6!r`GIxY z42cYkB+74pH^d}o0IVvW;O~RHN28KB8WE%5LPZmYotTfiQYQFrax1-!o#nrC2I<)D zQ$*|}463$rO_?rZ>2QEh6j+~FR{v`yBoS- z&Qbzs@?tM16Is%9Ff@3pF^R-FQjIvi3`bB4`kVP+V)3xFGT8!6&^XCuxdvoIndMX> zVBo;yKz8q)0aXQICO@``+w7_=Pk(#<8Jwhq=od7M^5hO2qPMEV{ zzfPB0)!^7^3+ZSi$RM#--v*t3nS2scaP1PyBLl@37vnAjba{PIxGPSNMu}!*vAGP* zbs}#{+sU=#R4wtc&{BM(e3a?4Nh7f6V!5;)AUr0$2K2Ci7fb}o zwOqcUNkT5ofX?SWeGnE8U;_LFfRA_lM7Us_7qiAWYA4K^wzI*#XekE=6QLbl+IMC z>wyX*xhLya@NQx{Ib*!3AQ8PluYgI~K7@X@Ct`Bm%@2=~(&nJ4FYwN09SRi_Z3sEQ zwtw5>VM2xPiZ#lC*b|M&d0tJwCc~)>0`o99F@i+1)_OpEaDGs07@$9x)Y%mEi=443 zN^lLGm07X*j3Lv*(#{tDX`tGo0|9wCdl^I(W@PKVY3w;4ZQnH^V3o0_)*Z*NRX^Ic z+0f0Q>43ZR&6!4)HE~7wq911>FVa>lkcxdhBSaE!lYmvony7J*bfO9pLronk?=oSr&pE7?USs7q^Mq$xna&?W5`QpkUya*5UjV zP$=jt*`d`0gXSzq>w&w>W)dMto zoR5keoB!vL%&5vCwKQ&Z`adu6WeHO8m*f9p^^)Xh%Av1;^4=mTCocc0O~7(He&qb zcv_7@@WrwFY0*Cz;Wpw{RTrZnTqe&!dZFGo*qFikYqO zxv+t96NCrHx{=+5s*trTVAYqCwQp?K1DjB{vsuU88G3JXCah7>+JH4nj+{R1g~X64 zMjTc=4vo}#8PpCvS%MjAA#X=$G|_XeD#**koStKi2K-$RWIBe zKs0L=T777=655gkuZ1-mXYcE$BMwr`Z(-SD`8~Hi$-s!aF%4UqrlO$Ofq<{t!ewkF71&CucM;i; zej2<~_oDX+7ey)uFp*&2VhRp@$8R;sz!E3E@nxn?0IZ6PnSr%dIhA1K&9la#lI&cf z`4S@nND+MTe;f`}Wjl?Km%wC_T|g(-(0Z!J9TAv~msw%jB$kOYV(4 zn$v@-%2!|JR$70`+AyFX$b2`H8>I$eX2348O2;jb7voma@HtR=Z@zyD8m1BE^7gth zJNOPf-aOC)E|N||?Zn)Z0vYd^xQ1Sfo_(Z%uhR+H9lEAXi;-r0!{l8pJmCEP_!3*A z85TeVm^uzkK5#vB;1^uH(y#A>Cp{f=ik%nWvzr>d_VT~YbK`R%^k=I~ZKf&L^tV>^ zPU|m1fY9&m(1wc@$Ap6V6>+ly?(n|Uz%pVdTqK!GK`dq76?RsxO&vAbZoSyHUzEBq3YrM~-HG4b+yXzI(rtM+X;SXPyO=SfA6oXyy^Hy`&y}tYyY_4Oo-jdz zlN)BBxr1pQoI$9`YhyK+@^4ZOjC_E*a&eoOlnaIFO0_~FSa#yIyu#`ygUmDK#?1)~ z!r4LTq~M#|KrWQA3?K-~3n~+~GdbQmf0#UK!teQ?4x9(2LqAK9laK7jkctV)0+zc) z^yyp`okrHqo1RGx*CfCZukQ9RGc6i)AvyzZlFlGA<2vw=!BqT>%Y!KDaNai`P({Ff z%=)x*!jc1I;<1>HQc`Ke&Z~#GyPj_xJI$&-B+i8fchFB5|VT%tt8gyzXgHS|J*SUOqxzkT(+?|w?JwXiah@5(qY`761M zWOTp}qMOfvMjjsCi~yMtR+8-m;hNL!zjOhY_1Oc9GRnN)siN{bxzr6=oo2hRjUq2AG}O=pNcut^YH7 z{;tn?Gs9GHmrimE$EL@=M>;O?-ysO1fF-LgM=!b6l4iTpu{jaTC(iezUH z6;C5p337k+z4ifB!9jA6>6;*Rk560>8&U`eVt1<9ihK$bj9w?@VE`EtX4J`=C52kT zC^aRBlBP%qX(`kzkOKh2!^BBjvaMr~in=7K%4-^AXrxjIQh)`6kok?WJT=NBNg-|G zmLBFnNhxJzPQG)_wUlpKrfl80x15cTOTk!C6xRUP5EKp=#AV4%gNu^lyVwBhc`F9Q zG3loGp7%;wzg9>OKwY9RSc+MA0|m=ELE+=E!@}Is(u~x^?qC!H#o;mHF+F&v+p||~ zWWNVH_xPAEWRP872uL}yO8HY-E=gryL7+^^Oc02D7O8efv3y*2&Ct`Q*9VrmD z)J{0u$?`qqxVcA$Q)ZGxu{Xe&uZ1DT#++)0D0Q>MgEoO7vLf#VghbkQE2P@I{PC^h z(9#-7Mq@hdEhiK%0sS}+s-M);IQ=eg20C%5BVtgwKckEt#Z0v15pzHz@n8Jvbmk|r z#K4Da)4t8r!lksN@otwRn-rLC;m7n#{}%qnbDLu82V}oT_yjk%BQ^F!QZ?iYYuC0SfY-H}}f$1Mn zJaq+1G3k1qR&cML<;oxj;1o)TQ8n^p>KLW*Qn+^_?Q9%LcB=p!Ec%X_k9-!$@^HA;Y_(tvZ1b+np>%|V3e{;`5yH$ zpVxl!=Z1SPuV59B04FK8CFBFBHU&>ZGk{>iIq_b#U+bGDV_k9xfAJt|+wD|NFgy3Y zznCj90FwD>zhWog1)E|KAEgaADXG(gJWm4vwB9r2`^=w}uDJXC_c7F_M+bz`xrg>Y z!hW52Ab;{Pi~-JRBLGUOeD*)EPk&R%G+8b6(%E*brYU~JpD{LUM9`5%yO&NNFiohY zLLg2>o^+MZRi-3n;CO^S;wq0LgAv&|B;JJ2<^z&o3AmvW+G05}dV@%)E>vIuS&nIC z#SWlE|I_=(45E9Y%#8nIYdI|-!*LsS%rxG`?LXig();V^=_d<}FtQB9k_}qYHhGR% zHKpel&sJhBkdOmF!tUO`ToZ4Yz}6u;(aA>zqH$1~GagOR778l{`a-yW$1s=@{1T{@ z+-j=FUKBCY;79%I20wc2quhQin`VyOg9p|DPMs`hf?|oAGbfsX%pyEWS1}Gcv3HER zj};JF;G^ua#Q?_@Of;1vH~p(7?6L_RKXkR-VmTw8G%1mU0!6SCj8ZD)Fmpm?i!DwX zmc$%;xg~SB52=%6%Bw4~Zeq<&gLe$_TAR{J=3-_LXz~holy<1#$!mNf9VV}mqGo&M zFZK0DEg!43ehS4cIXz-|T#s08T)l{wly6QFI;BrK(z~&(8Xn(^vZL%`5}F;<1RXlTBUhJdrrt)_-O%N8K)@2<$Z*i_*;pZ%ca9I1IJZgKbo+ zutys#vVsYk7EGTI7O>^{1dgI&Paq1odgv7@?AZ{Y<3jsfYv&aH12NqNUQ^f3J_27! zK~sR!=vpW7Byu&CLIT$vve7h{?bK_x{%dRrKow<(ZF8hm0OuhNW6zX4Vq{>7-qTIs z8y;C17?94W@(ckogFDGt1~P3;(DRyB;PX zc`l5vE>utC$8Wy#`zDGE3P3z-qkS23Nu3`Vqc;gL=*eL=bQq6nVK%+XIyhz0A!X+5 zkdIhO!KE;F$qpHP?8-ZC3jiJRL{D)EqE6A&vtRb_uM+rgjc7SBdUaPzGj%WM>f~GEGKK0>ikw6Ag*HskE=4+!baUqY1XfRhd`# z9m>gdPK9JbMeHBqD^M26^GzpcyvD**666aTZ16EjY8LeosEoqE$?25!cM1U}GQv{k z8<7yaOH&lxJJq;vnHVk!t_qz0T~yD4!luhlG4MFmwj0}Z?51u{jSoLFD$k^3Y^wpu zVPH~K8{V{nLHa8}B}1(Dy+-11gqlI_PA?&z0?WFG#sRvj61o8%mS>KIxEevWg)*8a zz(^8E$ace~TCQtY0p}z>4SF^5k1R?tqr&v-Y%QcrU`XgH8Mde^3{eCnq~KOE8b9E=}BAW14dnE?>msNAuODEW$FOa!13li5}J=QJ>CV+~93ctkcZ@b6*2O z?Fexxrm$i86d69^&t5Bi(wjCGXU8Y8TDgZT_!^Cem4-t(e0l$MTmLem!Vw_$PqtFk zGux$vW^(uo@Wd_F$@N@#^19CmUjnt-fF6|N3X4!a1<7%)_rC)zWcB#aDaB6~u>>TF zJlp-?o8Yx(X2=GTxg!|{T?N!}Z!gjz<_pDA&{01BndjPAyyr+O}jwUY$I573+ zYV1+IGX`9bFN#Cq`^>vyP+6w37ZTrKlDIQqYAC4FHHD}1XR2MoFMJm@>Hel(Pf5B~OQiJK?Cf9Oo9HuD=N^#gJ z%uI)zoq!WaEQ+c&zw{V*fvlANMM?V|kV0MnIs??YYYgIzD}lp;5p_B3Jz~Q{>JPvQ zQz~^(<>qKwQ18Rs;P6$`6;4hcg5RP7a4EfVKe4QDg3RF9!$3vVOiJLHKjS!ec677H zqmDmlFL|-yYusRsd8j#ZcEGK2^G6Vz+7CD)jzak!!=6EoYSPMs&uwkYRh>VhV1w== zuTsAD)?(7T%&MQ`q>3we#W9IXa@t-hEWuE+E2+B(#0A@!figiRRT5*(9RUmrP*`RxnKvgGgS!VYY2Y3#$gsAMCmIMHe7!c=BHohp>C81MjK z)dhsKCKPk<)qPgw64)hobG$6z|A6G8aiS0;Pu}l~Q@?LlyYd{>hqMOR&8_JX4d1K> zOw_l{R1r1s$j$sS6Tyc@S%*TT?g_vVeBClcmY5gR5(06f(+vRiK z#s-onXCE`SE6ZiL>js?3_W#(^@kyfD8vjUZocZiugOPwg=Bi@T_xUnNfUbaL97=kN zcot$Qb=&c3p2ycdL_ng1q{;f|!b49a71OR{z-1H(s9eN z-a3zmW6vNOX*VR?uxIMOK@I$jDqIy%fiEhYq>Nss+%6DOAey)NbC$yk-YCHwktC&e zzVXE~WCl@+aU2OO-tUKJSv7u~2a>)D(!`O)8$GbG^8G*f==VvGZrI*8m_+@E;lYH{ zx!~0}K8d>`{wZeu;&bDp=6;$tCDMzsBPpcsiulVz4WwnhC8nH{^vlzc&2rVwD6!+# zG#HYFDi10=bc_(G)O4@~1j<@u|B_6{%bglElyOf*ADm9`>SO6Jw4&Ma{ zr63{rWim;67eu)ge`C2EpozQ?225f8(7@Cx6}Iee>O<<4qISf8SRExyZO7z04|B zvJW9_guEFI2$&Lc{s9`HEx!oRL@CRM*)!un{Q0I7R{3~~wF7ofc{kR6x{ESVLd6^K zf>OZ%5o~SCBHzHSo(c$CNN-W@sHYRVHRFu1decGnck^FlkfZ1>_pesnn`8f{X5l;X zF6P@j9D-xXr|#T4iH)RxTk|?BUA7}lE7%eh@vIT zvXY=<{dy1ohhLmu`Ek|xdu>|kX7XU$i^76S|5+|(d1u=g>MOInN`5GEh*@sopG91R z1&l92IQBs7!Kd*E=))E>0NqfH(L`Q0fg08>G)jCzD##p)(_6C^ZfWUTDJ}JZJN{hww0h>|Kv)(gFP6OaHfwoVR*x){o`IOUKz>`-*FH7;45?wnmH;S3 z;BjiBEJJO#Y?qZwt({NRDud!Z6^&`N-dg~>b648N%>0J;`KD6cNY&({&_mdlEk3O? z5SPy~aj-PEt#}0hI~iM8TgtjXn;;rYrg44EMTpr@SX*Er(Jk>M^tKCSP)Y>`V&2A= zXajfsIVvHeXP}QLW?S~%>)Lz|ZU}3EXfSsLE%dXGRQ~i_#YXZ*dRu~Hru)}zQ`2<( z|DK?=TIR+S0-o%h6O2iWMesm=ATWmVJPal;R%GBt2&B+E(1Ax{GZl^@!3Fkden}8X z<|%_kqBfrpdL_|S@KWVaM&;!ADxT#PB-!qMKQkfl%&_Z(I(j5vV4Zvt>ADi6)qtp~ z0+|HjD$11vAsjI^fJvPlvLiu|4;NJ397kXbX(pvfoOE1W#R(zINI^o`8?BtMBUs=c>((yNvvW~FWsNAb#2P3>wFDVHmee% z!-J1yKD(J_37{lc3c5H8Hw+C3AQT#iK}?H536YVHg$WE|REPiy5CzsD?3Tm+TIKpH z8M)ojZHyDlE6WUNgE!&v-{dagCTh@+m*t5Sq%5Ecmu4^$ZqP|+P|tn(18hvZRhw&64?!a^jb)vkp!@EA!t~$Gl7VU zWE?7M!G?`h9mzRZdAmV%BG&Q*cORRr$2OtyMVCLq$I)uoG5fu`iMdLF6igBkOe%oY zscQURmUMTUr*LOu@rJKx96H31!zA3UFSUj`OpNgFW2Gi)Qa#(M=p(y6X%WFNV)h*r zovSy~EJn^~>oE`zTp&N1;t}&^|a?1vOA@~R8ZbG{R()9hZ|7GtPA5ngj ztPICu5k8mXHh+uW;{LH7sX!+DPDdl6n)Sk^p0)~n$Py%*mB5>34CEiPYRmPHM&{Gf z8VxMP+d6;um170o*g4d{db5>_onUQ5XDplj$dyUxaW-5o{{PpHMTx(JiId{SwvIpM z8UWLow1A3b9JO;R`bL~BW8}FAjYGd+T_+c8F#;ogLuPYnpl05^xACfOn_em9-n*x+ zs{h9)*@4}diiy!81fjo1irtquj&y}A2pZKrgl_K3Ar^CRqV0lH;v-5fUgei@u2r-7s^Zd^6rZxv@LzFbD-7C?t+TR7kA-Ln^~x<3a@*N*AyAJ$K6w zNIJXn4k$gO_!GSiSGb>{yjm!6y14R{x+gAp4W`p9E9}dVNZerJ$MTM&?Hq2xLot)) z%_@Z5oQ)9HCds2GN8JI1-S3FAvnTTRxtEmiGX)2fsMcV~#3X{~6<VbSBcpeQX77Y;gFAl_@Y|@b=Dd1@;oAkc z;yDB{!{>4rI(u$oR*9oh4R&=wi#|`(*dATo!k_b+_Mv)NrU<5%vjZ8itlFnqJ>?S3 zb?GUCF+Txqng-$jDeD-}_Nyk3@zU&73jV(HvH=i>7N^M>{n4vOT$sGs(QibWBDhC6 zd!A{w+I^(<1S18u^jjI`6C7pCgH%l!zcfzbx69*{!_Gd4;P!3KV~XZZ?4@%^DE7Z! z-XEn;-1*oRNj{NK+ocR9n4A?GaSF0Z3%e$AaZ)f3?GJSgV5MTk##cY5Mn8~CsWoN5 zhkx<6-~N9J{7GTIMTk@Qj7XNr7#%op09^E$8fSq5d-EKNVr3TzIp9>nK0YJ%aXF^g z-KC@ov`8E&zj9ok3Gse#9M$s*NE$tvp04~q5n%nnOG`=Q*^PyEBc|JMevEA2Z&=+2 z-s8v<)KLc>@BpO}UP~s>fKNpj6wmUJVj@mR;pjyw&T%C9n3PH2q6C@1N5Mx?CFh8I zp_ctZHLp3cC7@B_&G=KC?U%3DLNrq>yJNhTFJ*vU#niH=vgComuFCIJzJGBg9^lHr zxah_43lhmgOLIXq7NcIgJYVs{94so(NAr&>`)jOBscIK_0b&*Qbf|o&AnQLxJQn!a z@JoOsoM7)x1h?x?{3~T0#6((v5_It9_P&SwK?{VW7JXwhPLf56Fnb$Ix0TLI2*N>S>4sI#km ztg2fF@nDnRY&hcD!;Xjcg1{|q*=IRo-3K5RWU?{mLqAZKRMUr9 zq&7l_z$$%X-}$e))UVpT$*sS6Vfb~}-qJAy<^SvM_j zjgpX5yHP0OWjS_lB1yq8jPy3YUiHa?k>9Z*KhT*5MM2HihB$|_p1+OE|C#F-YUbq> z^>bohqre3113$8uLd+lAfT|evz!O?D4^Y39)J%yDVNh_L@laU(_c9G`zsL6AQWdbZ z@G;rq1@K6i={7Sa3BRjowHj;L!tWeYc|UG6a7j=^HtP$0YD$mzSlS{EwVPV2f}4pl zx`W86VLq_JgSW36V`97sFBCKyqHoml5=(8vBVv>xzy{q>NopxrWn-icGY7~m zSBJ*~XX`^6GYGp9tX*Icc7bRdM;d`28Blx&h!>P+z=llA$s|)|Q0(jMkIqOd(x8;i zy}WeHyAK9(a+1k%&>Wk&HF^k#{O1}6eN|{wQqQ9tNsdh13t;YLyC3XMrF<4{1MGC% zAe$;Xg!ct-!fgnkoL^>5y7UH}a8abiWwHj7ixz(Gd^%=k;*e=GitA}IpX5s1hSq@U zZWKhMIUZ@VJ_xLbQ{^}Vd2=Cz&oxBnnV3Tfvlt}rsyUm;mCaM#w6n^^KdBF<6nZ9+IAp0=^@TJ;pp-PbE7wU39^$&#lcfpPI_|GXNDe51Bm#W-j{EFT!!`aUJHzu+p$i*o|EzP7MyFQBB z6g1zc8G7eRirn&WTvMPu26{I#b;U~mZF|gGW8-EbH_8+mI~jw{=Qy9zzIUZ|V6SgQ z5iKj5jp%NO=CC+DEPbktH+`oBOUykD%F1a+_&eK=dX`icC#aWe#Fj-@x^0$NK@kaE z*oP4ZMYj%~`WgKvh1PLHYnX1cIFit@^X%S7li*az@#SwUL?G$d{H#l(&IOPtLd+z=84 zK*}<4zAmh%d?rXKW$A!VS-axi8H~ko(Y&sly@D>n0=*)5$=)d&F$ORDyaa9}ufB2lV{>UGD>^^!}(>LwcsOtasoo>6APdy zuF;nc&jIi)dB>oTvXxTN06%5{LK0@*jcfS)X(PwNh5%2aM7HIMix6Ih~# zX1Q|FCqVL>Leiz+8Pq+N!_g6&N^cg4h?`8iv25`45&A_L)JqC*7ZCkeTn40Q=O#l3 z&HeDL2(wM7!NKPV)oLyeJ%d?N@CXzGH4jJSK6{%bvC2qXj;XvIe+JVU;yhm8tPEU% z01OyWl4}e7LbShm#Yl(GYRRpAa;ye6`zX9eYypIQkWOxKNoCw-r&}D(;RM)5lpT4b ztdIOJ5kffk>;Zeg!S&xDM#i!*SH3t!f|xmNy!7}Ez8bH_!{4X2IU&JuczS_RRSd=9qx9G zoz&g4&uyL9^}9?Bxg!GM;wD_$2YoX6Jh%Yqj)=MW41~ZkbhKtJ&A}S{H{wSTdl!@* zDsGzf4)k2|5h`~Kx`7s439!c=FDvzYwe3tAM?c^*`^5%kF;kfqFz#su^hq*EZAkQh zfE>UdCkOe+t2U5A+e!$6#0+~<%2O=M3cc(t0eA&p$E6Dhf3X_1eTq@V$ zROY7B2|=Ia;w0j7Lgc{zC|b((pGSCv+bHTI(2z-EyOhCVygJhYnZ)j`g4<5dhv>{_ zC$gVlR=th;l?|5NYi@@F4W-(Em@gS{O1s_7Lgkxx_uMNAbMIz0^;|+?x^6KQue}hH zC=t{R1$#v3z--yUpPyuOGWm+{K)QpF&ncY_E|&aZHi2AYyLTCL)q+pdeKeRLnxpJ4 ztc4s6(DUJRHT)s?N~xvMGLp338jdGY!WyVprWoS3gEgk|myN_JmxC5*7l|*BQ!sfA z#Er=3`{AZ$Rp{{(iEu_vZYj?DiV&lkB)a3p; zJbVY^*k_BRb^1J4qNwxbS9<3x+*t#wEZA$aN1z%) zd<_%vS#RAjJRb3)+iEjDQ}%Pq;17S8WiRfD08#){UJKFY$mgFp};4 z8hRHplo=|Dl$#V%k{8WGP{k((R647{H~(h(r2mk^c1bBy$eBbJF>WLpEg9UHl73%x zZ8DVfOMg<}^vxUx{lo#XYrv#mwLWq6MlMF(l>?w8%nksd%E^Q>!lM|bd1|JBm~#;6 zKL($I@QM-6;MUwr3d6PeyZ`L_KVz$Xv7K+MUF)uS!-ZLzp=4yD$U${lx;k{86^9H; zN|~?^C?5nt3^_m3>3#;oV_Th<^;v{wZ3N4KBD9F+?)fGGFq}Z@{62uK*GNKQG62zqo zG;@cVweU*>bfjwy9EN@z8hPi=EVeZWn5k0&8G~*>PQB`%aFoOxUMhush6^+_@<#PU zl=~kfZ9`)<+RT8GZni(KRt?%OKO8OMXgfyv))w&&G>?fvNmO_Xe*51^cqGuN-&jaz zRfA}`APG7CI1=sOJcgocV565tm3q~IyxMEcT~t-#=Agg#Q7tPUlGPD~84Y;8>Aw3N8! zoB~cjVR=3K4||FxDpZn@+D3dQtpENd-6VD@Ul=1^nT} zMnTO`ZEGLHzqZyCeYjI`Erw5LI7mw9Bu$QGK#2vZ-P13fE1}3>Af(#AJxw^ZW<8a0 zqA%{90oFaeI;t*}?`gm4BEvsVfvOM-^wl8wvLKCY>JX|S@p917zj+Mj5xHoWm{)il z3NKXqkx>(5CWL&IVRT{n>aVC{yt&G7JUwOfEv60%qb1+porElDkIJ$(^N>hk?_x&; zWZ1U$?75Yu%KC99l>X5+5dliADxI z;l{Z^a`e6e=O}L4eDW`pccVaB-uHCn4-Vc%iQVr4Z)lZEhH)@#)RW8_u$Vq(A<^_z zZ=B9FKC_4;9h!_6n)@*uFCkQRu~yKJGEGW1GGgfX<>stXm#R)GUkHdlAb#bxH9Ava zKv2aU9dRrsNMUCkP=TpW^qw|2Gv$&&0Fj`N#1@1%tWi^7m0biHi&!F3=8gIxIhjky zlP$Q8FKzSm0+d>o456e=@1vS-cCM?)Uq{ZXF^Y@;i&Jfd{gZep8xLY-tn?J+u4RYg z=@P7~%(SrEDvAi1V};u=i%dY62+>*OkAztbH!`%&29!q2ulv_{T3P)jy2cD$%k^gbqryY*n z!V+-eS{5aStPVK!G0p?(3;G9!k?qV5-wf%F<5be9ive2@IJAaMxC;X42)pvKjk+OO z7_!y4E46K4bxT!w;Y`FQv|V#kg+?cTmqvZzcXDMu9?3D~Mcw`ucrI^-V^iBLE)oX; zSOR78M^B-f?4SG8Z@mfw)jHietr;nBC)hzr9T(W1pA_lou>P0Tc=3GHR`YdR!W%J(B@k|Bf~Wbd?&%iV$yADK>7?j8IqqMGjT zJ?s&x@)Ke{EWiXLFqKAWR7DEslv%uwn-AkppO`m3KTFe|V%BrRI22G~>vzru?eTxr zmUrB!CB6C_l?8#2Nqq_KyVdX!h8}dkzS51 z_jLe%)L{)o4=}8f=4rrE<3+C6EVSKe?4$PN>t4mkGF&a#w#n?dl^xmuf9S60?#=04 zd?i!!aJN`zJ}DJNPFST@@Jz*oN5ajdLV)Ui)3L^2KK}PSz>bbM-%Gt?*_H!Tx26g? zh$GLMUc_eH*=8AAjmx5JU2e+x#EjxAP%;Pt%pmP!U?TJ+)ATun2U7*dSl?v5iFG`$ zxx{YpyzqbA%)C3)JWMJHm7^u3i`(AW+;)t(#JG_C~|1IoyXU0jLv0KpfKv4+D_^T4xNB;uO+&c!chX`Z+O z@MsoOkj!LR6)sMIZk->@3zMY(5iJaP@Ac7<#QUmw?}IFg9Fax5Cu=^D%zh`qWVDgR zbttlo@qwqK^q5&rW^DJD_SE?sZjx6Q06}mvDvPg#xSDiU#l7sRf)a2}>Rt;DdIAg) z8{v#LK_VoY+=m9|2*^lPwpINP{L`W5uuCN6v}t+x7qFSbxCurmU!muLu|$ZBym*eldtG`bm$(7_fc*?q|IX~%+6{$ z^wMAjgK#>mydO!nqsJ+r;~n<%@WGER(77$fAeEiTHx-J=_GG@;_8$@C@cv+%zzhMG z3V;A)Fz?6WxfY7mGBI|-4~G)Y(PV&}T}c{!o6bSdAk6}Pq+C>Xp|V3spP3%Z4&j?Q zsUIJzLNq>oA9UI_oWXPVAN%MxdRA{+&Y01JZ}1~oGu|^J-6H#y#`XUL6=h(jhmpbJ56x-ABiLLP8pD6 zecu6Kh`a{f70`x590Yf5a8LeBxpgpHtaZ&+PbCV4MJl4zvOoQzYTs-v?Z90xKH1h` zmu-mXG6f#Qh6n&-^?)g_Z=_^epq$N@%nYqW9Mp`#7`F^&PrOKCiOC}6KmxI#C3E2!cWmH50>;g7! z6B@_x$jbvL!OM8{qZUXkVfEq)K5FO5lH_|p#~~`-5As~LH8;?-4f#I+ zdPD!-^6&f}05xtOvo8wJlnCZyePW2sx5r_&LzH1V%0?7+vY0nRcsP9_B$78I9CTmz8e? z0C9~wmO4*&Em`jsWllE<-C)FH0^y&pj z>%KLglPCsymF4en^`Jv3RFvHNq_#^%LGA_MyqqmeoMgC!t>h65k_xE&FgLJpR8VVR z9L@rKE?K_CZHb4X0+W`(wpy|x7R7-KQ_bz96NGV!OU7W9-z>}D_r%O$1G86eFq}1B8_wZ{<=P_6`kh<%)I+X6WqkW>mJG>B%} zyb{%DAOn$`2Ohh&AgVNUIQ+eGW*7OUGdm@Jx7aj)&sgFLHL7wSPuCC0)q%UT60I{+q-smEUd0g@=1%@)~w(8>yKvL zaE;ulA7hD%;Gd=x#^rz?d4qa(uRZcRBLJFwhYKaa?7)7U;ynT19=hr(r>4$G##i)fPCM`@n%EDSKKtzS$hq8U8KBa((H^Rc`TVV>2X>$WQkG^Gn z9Aq8VhV1>Zt_N%(h}VQkErH=QOT5Gb#qD9*V7zZL->p7JB91f+!Myn}@_DA&?vf}x zxC~)FIVYa6tZ|~eS6-siJ&cQt`gA1dHx!>8F-G($k)($~0xu&&D`^B@Od~)FM+Nd~ z`I1B&G1Y#OY=Eo~s+!odc3WiSHqLbBJ|J!mGX_OKBp|Q!tld!x3}`SeRyv~|LS>LY z%R}-r(@6;|(?>SLhvll^DAwO^Cd6X>>O+45=+?;;na3>-i!)I0!KYa>pk{Y$coc2f zXnhyzA!`kpdQ2AV6s=hl%Rr^@`gU*#qyVE|udbTR-i^J~2#M)G5I33(9r#(FQ;U9U ztqss&sI;$gan0y53F%?n(7~2AXr)7l>}{zB2lS%=cZpi0B-|a$=%I4QTJMmv{917g zk_2^DfVW{CDMwTfL7p&O(svG3_+FS<@fQ%CX*n1Oet3$85;tZI$Vjj;sjF@TW0Y2( z@TlQ0RVQdh8=RY5wKFSNl~XT2*dl%w1$om5h{Q|Ld4!+~_K4&l^RESsAe~0|vqBuD zK3;h_Y`mOlPcl;TF;w-F;^}4G&^{cn>qh=vIi%4Kyy8Uul8tqd`2J2o>Tfj5`sp6vIY^oE ze`Sih3*^SUJuz}W*;ZZH_t@k@MLk3JujZbTXO;HATLjH;=;>D_h-mYQ;&>zfqyB7= zwrmH)AR(=WRRTO9dU*)(jbsZ`DLA)?1kXT+X)t0F)7* z+k5sXE=+uObsT(ppX=G%FFD$CC<@Aob@OJx=CWlmZc%W?V`k5as-J{cQjRR1P(&{& z_jE7yVD``zOz1ie3_13(5X4Sz!o9-D&AUpz#pOe&i^L-v><0G))Ezw8eLFtunfd68 z1KU?`1oPQ5ZiQ!!MM8p%;*t#KWS{<3aO$f+FSq zWX2S9xnz097YzfM;D*=+6z#~p=%=BE*jyAh`zmUg`Zjqtvj@9CMT}{5lLecrs0$*H zAnQSMMu?oEu+gQ9MeFQ3Ux`tS8J8;WU;HTYqm(xodRjK!EmIsAf`e+41M1+Y8z;B# z#1M$ZpiB0f2u*SIGo3#K&~)H=cf}WkFoSNqns$I-Xj2Dg+)w2fWs<6|1TIVe2FB4%IO`DA7(W& z1WP!^gMXnpEb$5Yz3p9ch)O#@A=r=oVtZG+6^&DtOh0%f4}ZsiBUbC+o}BRFM)`LWJ^tcpu_ z+LFQYJ%>$`MR5}bHwq=#HB87@gB2PIZ5#v&jJv91c5P;!TlWz4d_9Szi=?txn;7Ak zlp`D~G|LmyK-rTjpLNbk2%>x|$8+m>2qNb3#F&;vn{s0@VQxSZu?r(Jy-L!Hgp@Om zIR)7Wp#?bNo6H552AY5=57Lo~fWTdrC;f+66t6hS1}^C5;>Yf9Zfo|%%j(<^(@6CM3}a{^sz$paCkmvwiS^({M^XJA4^Lt@53H`kNM6K_B$y3qHfNkc>f z0BY7+&Ei2)&|G>LJ}B5pPS(NSpw1`$>LeIZkIidNG?lNqR}`YPM}W9E@b1znC<+)` zF;$N390bWE3iM}3-#E@j22E!&7NXo*GoT0Q1-W->YFP93$Esfb&za)#bD*rb~*Thb%b!WY!*=kTlm`j3|ZJiIIfMGX8U}>}CqIUtPsa z_MAEJQytxfltKQ^Q;W#Deu1Tv zlFD8_ln*S}b{IfTD%xc0zrdn%Cz5HOfZ%PRjou$|)I4g)bXlS`zO?bO-(`=0LBI~w zWc_pmA0Irf*@7{LzHkn$4o{+S8p0wg-!T@VHVV|FhDa6y5A1#0`+z}Lb3p|uqh9ba zGk!KQA;1&Hg6`Tj{|HSD)#Lv2$n{9W*&8;ji`4hTQ@}+9@h^M4uSEoHaa&-E&9Cao zxXZ#fD#d_gN9~KolOB@hGMkpAd3b*i+v(@hwk$fuO4#qy_9d_|FGFh7UvPK8<7={Zzs-o>SmT8MOc3 z@Q72%n78;za3s`<%bFM^6w~mQ-{Rl6u@-(e_oD=rNL>*w$bzMtU_+GGz z!6(QQe53M^yL8I?ahs;1=%iJk!<=HF4rHVF<>2STvut?8O}%Fg-%57Zg|lMj(}Z;z z*+B~QyJLxDeJr_ME2QK&U#o3qF~aKx%??(v=0I+`C9pHh217hWA*tF4Ndk0^58r(8 zhe5xI$UsM0`?0LIvE|fRyM|Ca)C(9oSK*dV&;~SGY}-*{LV)SO27}9qURO;AoD&2Y zxsI}(L5{JHrShRvzD&coz>Jn#g=+yRN)D9m(na(uze0J$jvRl!ot_p6JT^D5Qy8cd zKCX3|&<{W8PYnF&$KYQNQEQ`o^KF#=Xgj@I~NH`8jge=)e|-zmgTwf9;V z$tn6UVjzp9=$?vV{yP*+F@q>mVa1L^k^uPozN#DRn&4NUDs`L%tJUV-CONQmbuPa>j#Xyo@@ zO?orfMT4z{Vdm`QiHMy6LW`?)9M`@P8d8}^dwx4@$#xubLt<1+s&uf_W2i9R!GVK_ z0=lttMZV)5t_u1yP>{U@vjLU-bU#B^A4(^QNB_I?o}C*cHwA1Unn?`9+3Q$; zG6IHN^pfpEY>6C072kn=pVTK3jIE0u7-&c0jM+~1hDS*JAt5p!J7rnjhV|SI06pec ztz;HOd88?6tg=u?zd(cHfk(H&w%C%Pg`xnw#&!=-sLQ@jYDX07@i_h10r$}5jvKi* zXdkFeacBf#aC0e03(U!?WJ237(`YP3@r&5!@SGy2K+CYcEK5Qp_4*F>Gv>yr>uAv{ zjZJ)@WKjh)(n!m_-UV?3Og-P)_dQWTlaA_nh7MrziAh)b87XuR!!)N*Hl}tp<_7W- zQR=}u2k?~;G+@C!l{=ZXlxmOR7`4FI0on%+MLCu;h)V#j20w7?&#^|bjThUtLnzij zbffxyo<`uz*#G&R8Wot!@}s@Yr0kyW?rzfy;jNQ8&OyhuVsK!KkW6Y6LLPptZJT;3 z3SbhGDZY1M({qa&hmwkG_O2sKZKk)D5;lqO+q3mvNm89M&_FpcT#eYLhN^;|V}Bxf z-omt>AeAowG-6O}r-PDZe2d!FjjI_R@vlauJV@?L ziL*S%hky5nY}iRtoc8gQ7t3-g*c8Umum<`KtR+xJ6)g$Ed? z)tFoY$5e!$g-E0GoM?|z(VeKM>B`^+pZ;s<0&z-bkG_5Y@pS0}v}R$<&YnXi)pK?P zGwg~71}mukV-Ul+i4o)y`A<61;+v(sSOP>zIwj3F@6W$+MSS-p$SXoWEAr-tdIjp#GJl!rh%o7cs!230IaisGRyBV8{ z2MR}=eA0rKUxQ@~5Bo&!TeB3YM>la+;20fCA~ac4t-&)7Th2o+n@HDCrXUg0L?c^; zBv)8_Q_%!o!t02TDY?OByY@0G^{sZg-}0FUYc6>_qi^9p7cxPuhbhTO;Y=J=-U2ID z_b%A4Zy}Z9*|WrSd}tM;s0!Vch<_QGQ<)>WH~dMXRdZEhzQ$$inYr&q&q4 zm~#-ezE-K=Hg+QmH`B9mBZAdvCMm&2#6NCLtowjCjHtM{c0~u*QCvSh88RBPVqoF0 z6Iz-2glb>dr;P%wHp)+RnDa@p>qxCW{&CaC`5SC21H`M;Ud5JPesk*iF6Bh*%xkqE zx;cP7^%+RJyyAz7CUTfzL>C4QXX|rkX(09(3L!v}C>lSXWu!vuKG=SALtN}*EZ`B< z0u!(F8mDWk*LHYoxO*a6|AB>NtALT<8v%}iadf0;2hg#f2yoz2Kuc`U&m&u5+8~Ov zZ3h`LkZa@{IzzbK!0k40`pz3vJ{cSTEL{NL&!uZL$KcUXg+a@(rhAWU&A_+DKn+AE z%D=hC=-~-@;)c0o$JWnD4}Dr2)`ifjObfU_{dCNQdW}!pD)f(qq1L?V-Z=oY(TIvf zDpQ753|_Mt3Wa{eZP((aOo=oSYcMDX;IiQfT|>${vIwP++_+irk6D1RP<{ydVJK$# z&aCk$63rN4e$2Ggt}$QIx0sk~2(m<)A~A4DfX+=z1Vv~P!IXqAfrq~Y6f$@U=gvU= zlph532uUaYV8!zKa<9?oOv%s?`E~M^{2y%^sxt-6tT=EicTBckJsFaCmIPY)fYIpf zX!eX3k*I>k=5~pu8JtbZyGajiHD6{!mQk_P!zR!}K2&tM{LQV7r0}>HU zd=jc{U?7VJ_wDD0|NM*d8z*p1tP8N-5@N4?gpTGFqPso_D@69P)vduZ$EQ@=8kT8H9 zOXLroTdfyQDLJt1KRAc}_gRojfC*(c->rlJBK0Jx!QRg1-oJF)v5)BC^_N#|BYXMp zRNEAG+8;MbDY)%ciiG&Q`^7rurw)9mm*oXi1(r(U<)llFin7oL zCDfu3;{uE@4-*veMLbqOIoT6HF41i^VIe_P`bi{43tQMTf1o?<4(sC_5D}jSlbNh~ z4viE(k^|u&AAMr`>TT4N-Sg-Z2&lIIZy#THX!)^&xD25YI(B>mW61kEh)JCtA5$Ft z|Fj<%n?%yZv1V5;0jC1|eS`^V2 z5m=&ylKO*%Jvi3H6REJUkzvLN#Zu+1MmU91h*~hrK9poo{?VJFx7gecFBSuRfG)9$ zMjmZbv`%XE);$&V8ZGv<9vbOYJ|M+w+JaT8rY*WJkmO-AqeE5q$LMjQ0*xTe4G+3W zPr2CAtbt?okL^P9yl6Mov=(Fef>at!gm<&dk1K@RVL%~s49IZO^2W{l-L_MX2{fBLC@FJ6qF<~_ zsgN#epNQXhz`sz>(p(3AM4!&iHW`AUdFxDZ!C|?)Ig4yDkwd{?)gVoAg;K((1akgY zf&bxAPRu;b?!@i86Ri67J`!NzE;6YYA)?+EFz5$>94~&P0l6^jNVY1&gox%!c35J< zkZQp4hXifiZnxv%#x)lk&znqZY*mC!vHr0vTX=*0fMyuC-@($_=%1}@Y2Rd37xM)| ziq;;M5ou5zi*YK_Sw@vm#{|U29?1@dOF?s|&iAe>6-f3N*;LJp^*ZP3!TQF zNz%VmZ+eOa}QhdDorzrwDAB#&-q1Q$kTo$ z%mn+$Y%)Ad)wnF2P)&p=6`|u>i*=K70uoE+8q51Tq8$f#$*yLwxbo&@)d=qcMnqbc zIm8`Q_YiZcvl9%IrG*<8rF1N27TT-MG)8klE#1fs!l`gBtjOF|A;z4bZL#!y8|ET>bE-Q{ z7HFx9lP2a-Sj5h9+>~HVqg`#?+K@ zp;G?op?mfV4$w`5Sg^REL<*lp41S&k^!yrN=dJY2@C1^bF$MIj`gbPr}Qw6H*g4)8aU}pNhZTH7hW^CgT=x-kWz_xKs{B?R4 zD)3h!fk-Is!o=X{LGylp@Y3;h5JLJt9RzwlfKmuB8KYJ}lk<+Zh*0Q_#NCHYxWIr?$ z1OV%=@=!W)gdY#EDc87$T6^+m_U3W*Fr~q-WIL=ICssyM-H80|k{c6pjXT00zWQE! zFj7yTfIh^Z;YEl{5XTqY`o@xfUhCRzybVEH4Ji=fJ|U6%Kjg-wCx|`R&-iZooh`T- za#nVoMf4m-J1K@_4?r?twd-hBaBpfG?^}*3OP_mN1CNOA%Bs)tzYiwO8kl~ngH*`F z98XMYQarqCj6(;c1?j_K$6`u^-}l-Z@1QG=O@Q$J7Je~I`(Mz* z=!rPX<)r;Lwse;7Yjk1Y!LsU8EM8MIGl5X+Y5#vId;j1n&+FbdB?7hp!A7RW0u>~X z0=`8!0TmRF$0ta-2BFr4W~MGbk~FbAi1>h|)PFD@H@1b4u#03_W#rS=GqFI)xY*xTTQ-ee!RhCXUl_Mh+6PAh=>{E47tt(8q z&3EuJhb_DBuno)-dYBi5sAVnvr<=l#!hTc(Mq4SX=jxI*UDtWMas+Ht^u(H@ccUw< zXK&H?W*%i^F?u53f=62@RAQaq%CFtg)wY+W3`Fdp4OLpW3qjJsdE^;FLh=ew`z63f zzr{-oU6-fB_1uQR_KHCJ=oQtK&Djk0WS3MIM;9mdm%~rpXRLATwS_dr69RI=4ldy= z8J^A_LAQ^T`a^OmTNbFqb~sI$I- z>U39McI|B61FfJM^wL~l%Edl<@r6)aA+dqL#hOQ&^#rCU(yI--8VG@cK!3~`kJbxf z3Sh!0Zz0=<@(?Pgl~9JO%>5<_$pykDAk}~1Lp~LFQUoLm6qL0*XdbUpY^N}X#1J~qC&@_}6z ze?#F|#Qu*ynle*w3u&MRaIp7_9cV|0{0DWA{0ypUArTKuoUG(OU`aPv$Y&7tFcNmb z;$_fBfna|8>_7Z6@?^YWViz)K5b!5Zi87&3Qkx}<%onQZZ}9pM3i0*@ib5#FqY9Fi zGU5JYYEpYqo$p+PQAv>}jY4*YV^5k>UHo7+{&+*=2EGQWtj^^e>0?|BU9so{i> zK>5=}*5p+Ni#dXW6jZ`N!Bh!=I8x53Kxm~R`2sU-LZ~>gJnR zg|&PE2gMN-FWBRU^PO-mYS>be3W&sSShJQGrsYbaFJAm|+n=59WN2$Iy#nm^v>MG* z-d+K;Rifh&U=QWIDco|3y=;`Sc5qt2kh@)eskA zh??_%?-nE%4+*3wn*EB;(IP<}Xf+z249T7W3-AJi(m+gX4cK8MKsMM2Lden~QxH0G zqbSY23K`%J((5dT2CVvyrD9rbh-I8?r+3)M<#&V3hxHgR*y7%P3+uTpMlF`oBm=kQ z_)UNk`6$PckrD67LS40f5eRB4RV{sL(vEIsdwIqr_A@7&>WBwT$3gy(?vM$_)R@l@ zBEgePMAa32LFSkaV9FV12OdO?gVo_oPR0$Ir@t$?ycz=FpH?CF2?;b+C13-L#5A{n zmYCn(*R*t}Z3@l~53FGp8zQ2hu$yRbOBE{Xdl+1&T{D4)t*Fmmz(wO<9YxYN_+p7= zXF9|O=awL|9BqRwl4OSOaWtxbqdvnDEzUBvnRe0$uB?|7PMf*=epJJk^HjjF%qcq( zrpBLA2bk@)MlT8#9NaQ_o=K zdmYa|nfm7B9>MJY3<%+JJ%HIpr~=bALsgsldAtUeobJqus_HfA@%*OaY%#c@{LZEI zjEgOPCx6V;tMyjqDEr7VT8($+nIr4d5D0Q}*m0E(*86y4n8-Y}_qo$S$O#j;NxTO1 zQ3}7s*0)`?_l9_k77d^tJ-xf6g-*o%N=;~w_^!@nd>3knUCpk(g5rHpSl*&+dFsY2 zJY*caHxg7L1ImFvFhzf<{)}5~;okT9=#q%ra?r#kx-nEx%O$jBvm0w)reh?x6Z?_9 z4-hWvZ8RUul>xMwQ*BZ1Qywj0=r;`m2K5D+_B1J=SHB=zM2DB9`)q_8anC?=XPHlS$lCTbGvYe%X})SFX2hTL#2 zsW+AWUxU|8twoGUkZ!Zt0nFZ+@M#{E$iJ2XbWkQLICw`uch>WRR8kV-V+n{29D^3N zF=9Gx>Dh6_YlMYhu;LgFL)HpaAyx63Ajsu?uBjStLB#;aJTSs$fjQwR#>;d(zp=$v zfQ^VlCl_q|OEGDdPle@tDRc%d!qO{H!TYY3P;2*T1?;(m6{l1B=3Y^8gqUo3is%*8 z6UXpe*=8P>J2Yk_%n;GTTeFJCiSzg|{hJE7*e419#50#u`c^}9{W>{Ry=lR#!S)mq zQ{fZ~Kgb+}ZzkCSwPVWY3@C%LF9wlOtrgCZfvCKUAzJ*OgwFkxnwxY%lLPWFha-1` zCfA;fe&+J!_xr#PBBzw;gK_4W*SA()!pH;{Di$~z^gZgB*C$0wqC8= zp;$2)Im*MIUhmNX4^A&EpG8|8pwFtifIxITT3G&O86|J!4MgYiHi-!=JMJZht$KuxS=Di-r+d7$iV3Mhv2oPQ^I zKjhL~*JDpCFJDHhEjs(`^XU!JuWczqlN@)PC|S_tw?flc@dl1iEOR z+D`ZE$mp@C4B9PR>B&oe<2UI-{~Irg14yrjCd%aG)6O;>*4je&Wwh|keu^^^?8cc_ zH+9_;CMBF0_hi5xvK812>Ifj8@mcia2;)!u?(G|DA2|z6Rm}b}LP0a*lyC`>N(Mzy zuS2s&@sLysVhqYwD{@iC+_6m#inw?0-b+*!$>5YN;r0$|gNPZ$A*x+kVDQkEW9CWl ziPpiF)t|MlCyK-fbrFc*@W5_*8xmFY<_I*A(++wo(rY=@yYV6-c(4XYgWDal-(DZ8 zaiqGyK_1@%PzA&}L442y+@D`5Cy-{$kVdeGW(5+^D_focky z**+kQ$Ts>yBimUPwWn(N#)J71Gf}m0KG`+e+A7g;7Awl-ugGnVrsly2Z0*OJwPRgL z6ES@Y#K2mk2jcqcof(cGGuTNTAMJLd*$cB-=i0TBOOFtV6RUJxvDdi6X+hRx zaz<-rJ1T1BIlEZ^&4^tkAMypF2bf-Fm z*pO6paO8bM&r<#RQ6d9_*BbhkWnvDm2R(mogT>^66U#Bp$C)27f1oQ`OwHCYc~L z>yc$fsg72Kb{%p<+%+)+(Wi}B$)1^km;E4H4k)Oct7~bhpQ|9Y`e>EHM6Jxo1K;R~ ztd(AAph~5IeN+!YF%^S~5FbPSF!ZP2K}((7;B>g_e79k2fUc!DeT-eQ*Ny|T&{r#Z z_%O5FiA`OA-!$X&w)&0!l^n5-v9oH%Cxx)3} z3K0+)`=lvzd30;<%-ke_vo;rA3uGXa_~4D1Cpj4Q@o)t#RTy=kZY^SQ8chQj zaBvZwn@NTIS&DZ9@j&w-i2@U-?P!=~4H%Qd=(QZlxzN!B;dBuebW&#ry@!riyUo%E;{lW>U+!8 ztGmc10XcEijCr6S06ff&8v^f3cF~UWv;_}@?5=_OTOI8+*AP)`r#511 zlJYoS)n1#Xl$_dwFn7x^Z~#!cPjNaxkvOZRb?;ur*77zee_DC*u(=~)Mu$y3Ad!8(8XE%W{5RCm(eEW_D_Mnsr1cQMU-=*dSTKK%82d=E9` zpEIY*HY)R3``&>AI8JenYGQOQ@+n{kJ}n@D+(x)hMACwnq;k0nm2-RyzTDAdd^D5@ zh2BAA009ylx(hyt^jgtoGFrBuMi>-{F}e&wOD$UlE}OV*HU;ob2kX9YdIJ83UV)b0 zx7jyGv_=*#4m7#&40&X%PcSwdr~eN`!i&;@O9}-h;@JJo3=N4Hvb7~&w)>TxJ_)M3 zkDf@;4uD^pTzdgr&@puk^Wf0swGys#sX3kLu;SzPB9u~{2hZorJFK%?e>W#}`1;)Nua;h)QPTKFnX#jQj>lzl=%*Bbtdhbd)FOAWwE7or5 zuia{LotDJZY_#kE?(lAr>T6XUfPS;jL1;3{F1SxJU!;MOpzAeH2HyH!Io+W5)G^OV z;fdKHHP3C_CeU)&Dik^I9(02t1q(HOio z_YPIKCM92q1ea_-E{kd%@(Ps6LxmyL8FN;GX#goaTnzC!%Sc+MA7zfXWhd&fGP(;k z+`6QmUPq)2@rkj|m<2`|`;VBowD28P6wL;^E9kwQtvmX0o35c?Gng=$DKZch4x`!= zT7MEkZ?|L0Ik8|2YTLgwzw&N8b7)}$3qcs|D7Oo6Fq-4JhlP7Y|o#OYNsG;kW9~Eew=E?b8)RO zkq^g-Svz|O5XHSb(l5rSu(2#v6>4*rs{D!__SCY|#{gl6NFLQ2!B7 zX=+X7Nlc07s5qt2gp-!qvM^Qgvp+{exxNPyTiDQrX*D&P{!E70h0Rmg5^j|M|Ctns zth<7r%*);2aBp&pnKuT=AXzpQ^GW+{R8n;~YjwX^f;xESV}<`B2gFz49R9OmV*CjOOd7BO9NQ1c;VXQH zpptkmO3^@4l#ln!UC?bqWK`2UdfO`UTW_LD5+`wt6!F^!XXCQoPufmzvMckZHl)Fj zDI5@ocN?UTxa+hcp8~$&a|-B3Rd~X1@#GEIA-%>mlp+ErB|uq#JAw9mR!p>;-ax2{ z01?WC0HIXG7s}w4FWzs1!-S1OeLdkJMw=kcgFm|uSmgER=T8sTeaIC!HDkdZyD?on z{lpW;nU2N+J7?BpRRF3pXVRK`RG-J-eD+idj|f}MSWf%JfWCsC&%PImpQ>UrH~m#t zcV*4VW8>lI9(fRLK_t7tJ&DOYOzRK3Xh7;z9*y?(Z0Wd1wJS~+iz&fLtXgqiMmlq!wlcedK~hjw+eR>kEJxD?N1q7ZgVo0G6uF;Y#8822w~ zmGA-9c{r4n!QC6$_iA)3mcVLYH$(IpF->@`+Rmt9!+L@iTCo4?qU|lOjykJM$6Qq3 z_#FaV^_-rqtce98+}7!Rt1%2q=*5aLp|u9(avbl=?=#UL^%vqMu8EW(O>m;AYKRTh zG!HNv&;%Ve46@y1jiP33i*?YB^${ucRP;4++?Y~G{t>7o$e-1w{6GQ0#zO>T)xKn} zsOGl(3Uz-=`O z6?<~)e3JfIfTXhCaSwCrM^fTuwBjv(fx_Hj#aGZZm$lz=%PnIk7S+?B_ohAPMo ztUm`0&4B)gDF;~l+aZ3e(24Cq3n`oJK#_EED==%fsom}N}W6cj;-PKQZKd-vlb;4If)eW(F$qwq?y4n(p z*UVguo!m&^s)tI{{EWS^cttT}>oB`mI01m9)^ZC8Vnt8`-#_bd5F{Am*y@Pme1-{h zfW0m{Uq=HMGj>t^Mi)fVs%19Fj`0u7kfPrsEg)<>1JJ5JYtx>oz47_q7d7>9?3}$1m107`7f>(@8d>Sr>0m&rL z+&T)Q^R;SzMBa2SXzsugHy@ zcv|!FfN{)FnH=(54x6PAbBlSE?F$UDqFJh+00t2gxY}YolU|WKO~+2&P31XbNx=H# z8iQ7stBo2B4FjeDG|E?O6q!JBp#-6<)@sQsmW+OAFK}Y-E1PGqYSTG(f*rvr6~qcf zPY%0Be-op`>o>^9dnY}Jk+Is1j9q#tiUuIMcCt-Ox)C8(uPu`0d{&%NZvb<|Sw+zszZIbkxJlDqRRH&8 zJJT_GTA%;#k&LI7Tl92xU1uNq4c&q!=V>H3@L8{yJ%W>@UY%r@NiUryMPNPMGz-$n z13lVt;)|Efe^UuGMd#GVhhKFVJvfPm$bcySAkoE>hgNz$%+P-w{pHCdma!KPFv^sa z;78{klo1oxW3~kuw($5oda_4#Q1Z~hglvR{s$)1ia%lS_o@A=nGK$h@LC80~7uxQE z-|9GC*Kz9SJ=V5-oOqt`#cz3kc8Kjw>8A|wOIvhwZ3WzZ6+bQHRiG%_t z39cl>GTpfnYfH@v@}Ts_LZs{h2td9pMtMjv_~N2iYHR-H(lNvK+yjtj7sm#M}6V(k3UFl zh@~urO3YJY7><(-5J^gu=$WNz6+ufANtXm}O7&n=oJKz0_&yV9a0^xXI_~9UUrinn z=^I!<(h{3s?R1X*t#-N?YzRpjzy@Iu+D>#Q3?dC7CwHPjRTF+xa6&S9Z4>QD;s^dl3+zHblhTbrR;LUQ)WhxN`V;IL;glm1SwH*i_&2x>R7^x) zk-@{>MlgyC@38>GnyVww9<{v@xv?x%n2+#c zan*$V9KXq%Od5T|%kcaEh|pMZ5*Ry*+&B(MJGuq=poHVIUvrXLv}Y3=S*FCmT#{o^ zw9^g>@j`}_EZ9a!#>4$BYU!(wjzup`LQW8KJw2xL9seC?<#462x> z{)z)s=(NR+K-?{q1Nmm_ zENmusaihsN!@T|z+v?8_gYTelA|%j)x?U0bM2Bd>!4%ZoDuQ z7Iv1l19_Cs^&AHhW(A_yVpbOwgTb&puaefu#zKrm7S4>7ZgppxktQ5+%kUr8G$((0 zWWBt6*e1*oX+nxKfVe9`Oflp~Yn7GAR~?<#lju(un0L%yPTS}zp{FU+$g;kMH}KFY zl=M^6KXwxRS$`uc3VtBy1M7yUs=|FcIh{Y7+ZLS>G!-w0M+#4B&Wmqxf=v)@LY)uZLTG+kc5>|XjtlWt-h zJ^3ciOL0<$xY8SEN^X%1r#^*WCW(`~qaZB(4%E?9;w%F?lP~m}SL+938ol4b9sPJ) zWYd`2(WbhZbJ@5yX25>0h&D^XmVx_3Y0HGAE~LkLre}Tch4VB#`u4$FdM|XMd1Bn^ z@;y*T`SPbu{hTx-NbhZ49xybLgBj6tCXrj&^~xShEYZJ12&Ft}fhukyP=Y@&IwG3Gj=n)S zp~0~rhbT{@BL(o2uaOc*k7(#|kWLjr%H(T%+4b0>ZG<%pH;7Rg_%)!AVPC^)l2Hy5 z6e6VYRAxe~{1Wqsv-nQFhfp1x@?59KUld6WF%fBZtx7zm#r7%uo0zx&z{|i1fpSk#I~xbC=XsYgLy%Fe)bn#3SQ8Jp(69 zkS{erHc>S%7l5Tuwe%@x@I8*SuhPB)l05<9bR4YVKW>2WfqXkyg*yf^cxuvLviasg4FAL16O(fY#}<7$I1Is3${pkYlE%e2S9UquOnGh^Era1N(j9U-HT6$ApTc zw-XUYYdR+_Qh%JrzzA8ADGblbxDv#0`C~(s#t$*pK@KvNhhjC>R9>*M*!F!0T zyh6MjT&SSRSrII08nb%s<)Y-aVoRky30JJ;&wx$ZBk6}Il>cJPpoiLn+>_yO7jajS z_IZHI#S%lCTDd7#0s(EWA1o37+iF!6EeO*E@jxNN0$dZL`7%0k3|2P66k$`Eaoa#O zhVEc|f-*|^E$q`O=Fs~$2wn8faR_qKXDXS|<$AR<@(p2;d`4*NXxf$W-q^C5CbUN8 z`XNXmio)lULW&k5>~7x~xDqgqvjAnTTvu*M=Vri6Di;sFgs^d)?24D{aJmIzg(H)W zt)(cgV~yQevs)8PnU$l4IB4>Q-ACDGoWT`GP{e{(7~;CmDn{1CI>&j8)YWYZHr^}XII`5?^1lA*xy*8>Y)q*%O>GhcQj3hACPC&eWhxjm zK=eVLyT{Dg#qU0lipVMZ*6+$)NOs78Ko<3YtpqGQhTDd-QLzjyRKO(^zF!O`$~>6( zhKi|gA4FA*e0$c$x zk$r;N7lA`zxTwTSC?^gr)abt*wn?x54V=+51ebxHFBGQsT)KWk_-@|kk%LvK{2jq+ z1Sg5IAspbZBA&^%?C*@$IsB}ll1o-S zhwlyUmAX%Md)GDiCrvYVfCUlZ8FmaXBX9uwzRfnw(TF)WJdZL|2!1SEwZh^^y%Rbq z^nd}u_(+=v%^%8Ox!Qh?4ya&KBmGTQ-9}~HuKu-d+!)Nroi`_j3(*mDcNuB==beM0 zFfl=&=_K=d}yD@py;Ol)xd;^bg9-SGtVTaJvbh1JSf7B8^l{7?Zy_JPGl9eSK+TP zx^znluqZcIw1cv>2%oSgR>A)}Iz^9lTsuS>Opc=JQkJ@KFo%z0>0$1$;-P_NX!J=7 z{!%I^bsZAlh!=~V7~=?|@D?b8r^H(()x76QPAUdnh+WQN5V;D$V>rH`iP9}vwm?yo zfE5QjTLnqo_@R-A60g>TO*R9@oK@XFMzb^i2YjhN$YmoP21>Hu4i^XR#?1*zV7Q$A zNgawVMqC3fEcE;FtXB4{RI9?)L;$+^mc^VIi;Vk;2v$v(tYAkTyg-*1PytTt0**2s z<%5AC@BnynK7E|@A%%eqFqpnAF#3)`Wup+N2SkkB5_F$L4xUCbD$0%$xEW?N8ii!| z4T|V#csCG&C{P=#7VTziB?Hc(CjkS~VN_dq)l7Q0wB$g)xTTAXGoUaa&OO)3ZIbo! zLhVdW43G@;jQMjBqH~gtlIKa~d=2j+nOEyNuYcO!cANN54m0~UJuvkZ8Z@Td`l9PE zSIm6;TA><@-^lpI_}w&@i;E<$k#-h27p;wdUD;<&RM{B`()-+%bzR&l3QN zwm*#DajgRY(P0{tXW{HXRqd8_om5`UX!<*+);bnTfFEh#h`88V!M!>jEOv|Fm*WGg zJ&8`5DI{Dluqay$K*X(`wd@Y;5R6rDJfy?muGUyfjFRkIW-WR>=yE}y8#!GOI@{=E&Byj? znr+S{2dH%tdlF=YmfIOH{*9EMv-Y-oGb@{+b5nXDu% zY?rZpm!+rC z^gvks-{;%PFXraIS-hb8JoFTRue)SFMQz6q0Dk~#v?2&~^lm5}(PBpFB+@DIF@9FY z%*4mW`2?!;fO%yCl#@rVFurhzlUIN8*+X-`!0aaO1V|i|1z~5B`LfrA#+mVs4Qv-b(OqCwpnXys8=&hz@)kOYc^s7Hk{@%^Hp_iX>y#xORx z)cjTeQ&JycTU0>UkD}gaRB2C|`-Naby#uaJE=T|uXID5QVi6oz7MYXYM(Du|Csu(I zTSIjWn#Gxw#~whPnv<9j@GQlpQKQqvkA<6;Zz$U&dGf+WBpkGE`SAJ&S{oXKIGcfj z3IumBlM3Ez;e5+RuLmEUBM)?p{YZl&;>k}ey)ELq7`8#k3o5@aS zbjmib+kfGpEsJr?3mMJ8ob%KVkt@mj#y?>3Ei zDWaMi{$(Q-Kswo}?sLAu8j}<00m{#xkM9A0QDUW>D?!d@;mm22jFg*%%i4B|%A@OZ zC_qgyG%5iKI%~^Bh?b8*$6zX@b+*)#rm~SC+zpvRa#`YJ+W2&hQR2DKgMsD7InNte#?!8M~{L< z*s<8^WUtW62;}hlTS>RDNrE_JeS`K||CLhZmR`W{Am=R`|$_uyY|Y`*66_ zZxunsWSt`A0SCx)Q8beqzNSW6CA5MJQ;0Bdr6w}C6ls6uhGW8QRT(axVtU9z@7OM! zAO0A0m!m6Q4W0}bsk;WkJ$EvudAACv^-|m=M-c_F-}1FP6FHKa0C5?*R-R(h+h!pV zS5kDn=r+VkFgzCcw>MTz*b|YH3v|WC{sR=-Ia)?I%D!-%>1d~YwVNZBCgXho&FmI% z5X}Itec|!{SBc$~Zv~QK557jxqm701_S}8g7l(;L9>?7p`4$MCBsM02nApID9c!`? zs-hZ_qLR&|p#T^1f}>KJF~m@+26jr@B1l4?*uVnGQ+~H zw#gVTE_#BN?90hS+92%I?5eG**NH$8LmJAW)BOl#Ei?vD99O z@F)txD3N;CbeR|4a(Zuh8OVrQn;2pN==0&?yKq{nmk^VI7c3H@P+T^Jk+x!dQoYo*{3k$ z8YChejQB8yh5+z*CJ5QD@a#Pohg&JiJ~mZzyMO<94A;@EQKWLfgq_=bK@M0yWZ2Qo7=I^ z5Ok3+h$K};1SP5_d1=rU!@yvQ=5B6#oQzJwlP1Zan)`C8Y%HORB#WYzp@{F${RpEIQI@zamWvva2*Qo3@^2S z?!7eq`ZqQmqn5-ikz#;=#bTlgFzyZS3@Q-tQ^gb-=%8*0q~U3#YE-S_S#}nADi?Y#TLN1Htk{2YLC{82#Dwox^LK3gRxPah6_Df6L)kys$<6n#@Pp&x%N%4)o^ zQ-yQf;;`Q|!x(&_8gOGYX)v~l92AotxV>p!C)+`b|8yMsr_`mRp2U1vJ%vN>gQ!kn z6N!hBDy@{z0>kyC7(K)Ugqc{=Gp7#Stpi=SwK+`RwWsC=M>2dV&pL;tt?bqvRs^&$Vz~KoU72YU^`5mzZnh*O=ZZ zJw>*brr=1JWF6FP5Jl+Bl!v%XprjS2AQq3nbw#<)3Q$w3&q^v?2cTt>!fc{w_n_L& zW|FephOM+A-q(8R2IhF`Xc;tzLU(Q`dN?9*k66!2)aaKuCsEI#OMyMI@k{)!Oa>#& z%=Tz!f}x5Vn;Y7~3$Ak*B{k{%Bj$<(Dz|qeyWp0EK~WB7o<4L@A`ls9I3I_)ZjJJJ z(!Lrj`av@tq;l)Sd}Rx%qAEekAUqZ4S^P?nV^%GBdAML*OA6U4>` zfiv(0Y9_U9&9`{hy^B1ue`sUeuQVZ$R%A_;RKWwqBD%drh4HcBLA`m0b z+ePKPJj^{o;T!0(ep&ZRxN9%_U)F9m`pZye+)=D>*(cd zKAt9NJ&iO~Kd@yG`cN?twulRt6FsptHPtll0{KQSsOkJ)T>i8PGI@M@wjhT_A z4ci3GC4F@_x*mW|jD4R)WxQ~~Jl)d`s*#+&HFx8^bw!*`-G6JZppo)vdM48-5p-ms zSvDgK^uQ{3&Vg!_EKh4$6jiEoLsFhp`=XnGcp5CO<;Y_8Hv3`0DV) z=w`NA#?TcBD)0g%RSIPzSjCbXhhzy3?nZ($0ncd6yWDG1>1CK*V%)A@a37yGeNaYq zv_7r{WeTS*ZG=V!G|2B6OIvhGIz3f^MF#8hsDCV^#t3wgm|cv2cnP|YX@A9ke{-^u zCT%n%sbWE#S6~TUPY9|jJ}l`eFjYk8*g?RU?tt!OW_+NKYvX`uLe`RbiT<#lo&iSo zPwG|_Z-x~Dyp^H$p=lph3O?EodJ+eNTt;f2V(s7=Xm6DEz`tRQ&V_4wUw(oJ@WhGl zU!t%Okm_Xb6mX_IhNnB$#mp_HEx?qh9R_v=ofcweFAc4ta<{(+M)u##sNq5Fld@w7 za@cq4aZI(~4$^_~b>}R4*2NfRagvSbh>qqzR;qWLN{{&1r2_HvyDU5%^P_Qos>$B5_( zgva%w`VbuStQ?PhBgmr=gFO=s(#v`iD@ta;wvhCqPYoq4f_aEoi!+ErCayAOjPKJ| z<1q>$e|;S^7bkJU*MjtC(qm8KKC%E`M=81hZ4<95PQPf{D7YBkSig(QA44`3l9r6Z!JqrK_Yc5W@h*=nuWUyaS<6 z?}csDiovmz?RoFO0Y`aw^a^Z(AxS@a2tMv%WngAnTc2r5Q+RUmT7f}&tI1yuED?;4 z>LDQ#Rq!F0Ml4h*EA$e-Qh8qi_AyKqWr)7 z;%x}6j;w^s;<4N6=G}Ey`43~dDr>|A5_oc2abDRuCJoVzXUM9?=|mPRJL|$@zX=PO zKFd%jzlrweLGA$ka<DA{j7=tEbG}~YTygn&Vg$jOzK^W93)9s87I2>iSzKAFhTH%?KWzhKEuv#Px5@03@wDn|N z+Q`Vm3&PT|o4|+)3(gS=$Vd6>@G>drv0+H!PDDQ6!c}pDGM%8Q|3@ztV8abJY9A)5 z-!NBuG*}(Fr4954YRSE9rx@iZ1?opQi0m8pmB0M0W4rBR7oUw}8CxXA1bMbLEFHFS zEB&xJ1)Q-KMoPkPp2Wpyg6m@+l65z{mBaef(29(oLWMfc*E(ohBYBfa%LC(LEZU3rlV4zVX}7NZtv z3JoPC^%F;$3fwp`=5IpuvkuhH;lDm6zJ=-Lm%lsJiUUBdE=>Lv?{m~H#@ftu(D~mf-X1K z93j*mrO2pkn6)ov=?w!|)<{@@UqXXo){3@;_HZ~iwwjhj5>llX`C{+qA}~*c2j;j) z{>|ZR`YyIeJE-H{^GYY`E4vEhNWn#}jln3Imq>ckaxA-6l!*8Vz@XI_MQ>MX!Crm=jIUv{Pz zB>?1zC3vbV`=*ft28fhoQ4fyUCWHhpX3A_(pGZv;L@n~l?7p*h?;_@d!~u4Rc0$-6 zbabZtVe%;r6uL>oLT^ToFJ1)83ry-$MHZHavBfmPX19wdK=ORv$ID5;GtCsvmEQBT zBy%az)TICMU2w8Du!u{!OdO}Gx+IO~BC?6FK-}}Zd7Q|A3E`zzmU5RQ02J0TYzd~4 z=q2uU^meW_K^4mE=u)sk=?@~wQ7S1S&f%wwRZE6XSXvbo!vl_ zaF_v|>Glw<9z2tf_fHuh9%h3YuK8XXWTF-Ltj{Ml~dXlAI&l> zrlQd6Qf36lg3G+0ArvRef*(+avM?!cmowvv0;=wnfE)BGwgzIH2F-#1CRUB%p(&t; z?peH+c7mM=Nj(it0S}q4Dgcwafn8LGC7ML_b9}st7^=p~FHTIH{`Pr>g95#t|9RPB zYQQf}RMcIp08aQ8WoJ-8Kk@TH@^p3O8+~h)OK7j{**JMVg-$*omDI&B&i}K5X5O=a zak6h*sSFBb3`>$zxm><$yfZHm^Bmg!<88TTe}55}@0HyvNr^!N`8uH*3livNW!+Qj zE$MfPNaUJaR+LPjL(owJO(;2|iTquUmT#y#{rVgdUELSIM)L0z6q*a8>MB8#^v*8A z4Or-=%fH2|e}@R(%J&qnO>!7PtK`Ycs$LWC6Y*l1@Wb4V5z1gpgg*pd z+~Xm|V7x`LFW3<+QN!w~In)p+K{gW{WzOC_JojIpv_}**=S%vxZdu$;o8Q*2G{Ol$ zvQ*g%(fL0FX@{6(fPmpBhs%va!GKd)Lw++PtkNxr4T8mOjQ;F+FT+~~UJ%7b^HPx< zZM!VL;nwcmavDPm;_1O$mM&Qb9b$eXBY>E*g)t)%vv}%KNW3vhmoOTo*P3@e@}MPJ zxknlt(|yg97nE$8vy^pIJ{K~MCENSgM3k6#2}zsO+s}SAlqgdpLO>aJG}eujjccst z1Xn71pf%_El%Q!W)wmMj`Yc^I*3l`E(*r0`$2K8N1=rrU`rgw`b#Ok$_9U84X^SAL zfjb~IY>p;f<;l>?Vz)pq?_))#OiFxzDb;gH1MO{&Szpjrdz+&b5Whd|)kwUv-)8tC zq*M!y1w*-w)#nuGpTk7*L_&Bu>@l(g)jKi+debdKWs^qy8I)!29Z2tGZ;I<#I}xs zF1y+Jx@u^#XvxCj03GQhXm3C>gVbQz0@_{fOGKU&&v9_lOVM@;0L;Mty z2L#4w1-w4-wKyZ;d<7(qwLfDIVYi`IUntqJi)3<5=B=id`>Hr1-|}Ig1k_~nXi(jG zD<$m#{k>T8^hjIj_h10PIWXMpq_mgN@-fP9V!{ZAEq4>O=wQKo?nM#y=ooKCeGYpV zBty}4G?NNMZLGew*5)6TR^UW_~j0^Tx8e^ef7Wh};2 z_ZHCyX}ix^>*L<9$IgR)3;!Sd&DMEi5%BRoNQLy1=54~>16?f1!x zb0)s~l1+8)%uobrs>IfWLq^mil2O$Y`7nIEe>}<thrN1{V z%#BNx#L?L~Of6%AHM}u`rT?0{Z#0U$5@{rO1nmUKiI28XME)B=d0N1HQnSA@;$2{rZ z9=!PC-zG73X6EC;R7^$*PWn@ANS0#5Ww>NK4^+64JZ}t&V__U59cb{JG8B1AvDC&K z<8_3Vh;$-ZkjhR|hYMACgP1;vjDq2jS+?621PWgnf;w+T(+}IQ3TOKc!QmqX9@>sY zQVoO2$3g|~vjL0G>>>g%?vPN;oQ0M}Gh|CY#YI>Zrc2|N{56RT`kr_=|FNGu-t zE#sEW82+GA$gq=qOFeV?I{yMXM7KtgLQb0Llzxt-`{-r~t!CJ-5b+VCy{Kog-mCTI zb|yb%v=@(Nq%C#Gu-|k*s%-c>`p#o=Vo-M{ao0S=1g;0d6b_&%c?~-ifpl28(9Gg! zK{jFIVSf@|p%JJc_yjg=IZaN*$rglKSj*II4T3y9Nc4qe&B}NX`8Wft5$Y(S=>C_? zqKp70EBZ#r^JC8=Bj?h7{=cgdR`C>Sf_Dcl?`jT+9G8OY6;Ll)w`g+>*To8dfvhJb z9QCF+B>ggn!P?v7P~nwoEueui(@jw+$fY9hfHRrJ2-MW_NvF+Lqr7i#qn%j(Rv|B= znYb*_aqC*XlrjbM zv9MeA!<30QMpLiG2%tb)L)MzpqWe`t^Ox2J;IE|Tuf!UR$Yz$9d` zab=~zf~EF+fjfQ4+-x&#BHbdTz#%$4H!xGOkoIvbMl*_Sh^avuH^3G_e+uFQ`l#=b zwXn{Z|7;{R-W5U@i3WU_spvS|#-p%;Oa!C90H}ckE==G&2`T(hY zBa#83;(N`@q&Xz>c94#s{phe$L_7n8M3{}edq1LC%x8AXQ1p~}q?s0WC(_(6?^J?x zd7Wwbtea61Ma{^1sFrZ^+q_0?OPC$n=S2H($V&iD4o0sliUAr;$$0y2${&034n)ww zNGraL`6m4x- z$A;szTU&{xn(EJ5U7^H^4TL5w)qA8Mh|ODrwnLN0>O)_duUD zuBlo-Qq)|X!+CHOCYICRnZGP~Ow6#ZWg;3s8F9GBtCRp_b(y7ASAn~Q+Y(E&Z(q$m z?mlNEe!UKSr=@_`qb^S?N*=iC=siuab-YbZJklr%;~wR@vkRC}$J}=tIlRk?(Q~$F z6C{rF$l&|XN|L`71koR#>{jsM6@&s%T856mZ0b%8#fVz^l#AK&o{E2BWsXeTyDRej zStk(fO(>gDCuL@ZD`Np$OqTZ?y+hyzKij&;YNX^2Z}gW*7VCryW-mZ5JVl$Cw*?H_ z97NSgkrluP@My=h0N$j409 zvP81*8oT|_4^irfetybZ08^2kD&7nsq`IYTto`Id7^1qm`E+)^ivzpO!uf5ynAiH^ zBXcQT@9w7AjUXrkHjpEQ(@gGv`3C_33g;~Mf}kg=&Hi0vgaR^7#%U54O#?!SxC ztx!{_wBN2Pn*I9ra(Wncucry3$iy>rwf!>*Y$m@;>{V9lWg^gHH~%$t2=RUT+_V@| zm0?roSy;#H)Ia{6sa(1+I)oh=twX1okwMe2s7Vv^fx=M_5CIQnFeS;2bG0Cv6P+}K zd6ontWlp6+RAzx?pnj{3w1F{|g-McxKJ+ea;Lao>|D^gSA9D9`Z{zlQ~ZD+(c?r80e)Ep5I8AJhB)CAH|4J|DMj^-3~)57!vUsr%6MI|1!r|?f3EN5^PKob82~q{ptT+D~D%lq;ZcMkaq%}>) z7+IjD0gz2#!j(b#*0^ka7-Wm8ZQiHiLn`3Ov#BV-3VIZ{)gH8yW-y>(Xm1_%QTo{W zYUPp$Y9XtDpLDP`vje|C0t2)Y1v#ehY&l2nj=~oThEBwg!ma_vE({FSTzcWf>b(zY z`1XDHGJ{Jw3=jZ8A~uR6ibQ)zB2(^yKB_mJZeZjbij@52rMPHvk$+hJFbN@jW(cd@ zFshZy7gpvraVr6}O(UaSU;;q)=uFCLNi#q%hgNp)$-RKv zNS`>t%1xZPd~y^Bb(k-Ac1g4~`D7+Zqh3W+r+6InJpLLV#I^tdis7Ii%TLJ$fuu!9lpZi3W>5I{sql)lZ2?lq~kf(wOrT^U&- zb`clP(X{VEA*H+wRp>-djpSN+k^x#nHX@33s)kObY`oKLr`R`hF?vV$7pBxqx zMIn+3mCh9c)Q~B zhWHg=19TULg{2R~8xa!5#yMT^V4Dt*zXDMAy%Kqp%gncMlS!9~O@}>h!jAqQWo297 zOr?fyH;T@8zp?V}PY$12l=3n~pb`H9jy+cF3zA0+jYlk5yeUW`USQK_xmj-lv9dMp zUDv9isEHy6&~R1sLA!m6r8K8ERMM~XCIc=7+rsiyF~aM*Q^vT%9QBhs7l#{L4AhHZ z5!xKZj2RI|3cjBHu$<1mnx=25FS2g_!opP-Y&^t~_(CqDaS4Y{{bFNukEcF;g*{M> zYVAM7rWz+rC8r%F_m~_Ej`?DabsxKx`|ZOQULLXoXS(n^`>(waNS7a}cpZY}2GLUQ zIY}Cm=eAXOp(cGY7c=FdGDlxDH|C}0 zmZCD=tcyCK`G4`A?S|oFUWcGrKmUs;pT7e5!E-Zoq6bkfu3c!r>*0LXzE_7hC;uqC zjXv4-&j((i4fAw-La$~fNfc6WiL?n}@(r&lprDhs4|4Ht;zoo%wqQPq7F{ z2lzco9n3!AhAf3Ja*|`Ok2|mSVM*w!wO|pn$7n^FnhhGEJ+JXOhASMCQk*~m&9_1i zk*O8CjOH(NtvO4^+8+CfO>d-HJ9rGtGfjw z1av@A2mogc4n9^~-I#CRB>OP1X`D)##Gf;G0&eD}q(aB@RMDC>N&jpKwl9&ysgPk_ z57FRknnw#?I1e})j+#^ywLq~rK$xB9u22jWnnl$ScqGiCAc{Jd(Vgeupy;q;00cbo z=`Cn)R8w%Cbj%<)|7vgfs;6KHWTKeLBss;4>6D~QGCMRJV+3`$aciEjmldrRKnq_c zUxd`c2pV`+I3v#`otuW#PVo4HEct_h-&wr z;dp|K@NbJi-{lM%_<7Xv)iKuL9`!^LSg!JM~4xUQpeti z$Atn43UH{Qb%B;CdSVT2eL^;beGethnX-E?nhR+fKK{Tzj%VMS?L2xw0 za2N8q@@?!tHrf9ZqKnd^iSYqD^3V_of9vp3w~K8P*v0`A1E>I7E+zVjfn8)Y;|$?; zqJ4qt96g;x6|W|_%VA$)#JkX}q7NhxQv61mQdUM0H#kvQd#wXd)LJXd<8_B^t0U(T zxBO}-&m@#FEMOJLdWURXO>t_EQK%{aI;7Os5P~ig#@ZS;YLxlStEA#nY-5?8L5xuj zb4<7Mzmck=Lp2Q)s%tR1RuT=-p&hdVt9sTr0+%mi92!8{VAg&T787Jvt8T8OzbbBv zE=sqzL?t(z2Ap!fC3gknRbAYlIuW~TSRC3pT@y0$pPhji4d`unb+Y7fJbLu?v8w&2 zYktBkuSm_SGjjrZ5!DVnGRV7|l>@%X`i|!5(CIT&gYt{Cx~Q@sKKS5hz>xA#tV%wb z2Q(xxTFCIRvb~l@6k2Y=Q$ zHur;j6V-ir7b>nS|HiX40aMI1uC~hn%DKqX8Jo|+Jq6bv{u2IWBOv0<;#H6XIM7@0 zd+if6HeTdgpSz^)Eo2m)c(9oCp7OH0sV`;RF_^d+sk^ z#F}yfo?rSH$SOXuy{){1Hj1mMo$pvmw(W1!V2Jb)JEG@Hq{l%E z$?|a9cry@&>jt8in{4U|C+`Kf9GY2@RT^j_6U;rga++@AOO&5+`^~e|sI1CyoOj~h zNN3QL2WN3Cs;AWbb|Neu1s;Hna9$d5Ucck-s2S=kr4?F81Bl>oWDO*=iI?O{u|4)1 z;Tz%$m<-^UVhi(YMYM}ZiJrtirCQ=pD?@`vf*Gn7n{{U4J1z(F35$%my#YB1%Wbvn zwz%+teNZUzTq$2Gny$Yvm0uqCQH<3JNDw3fCtyr(*;&*+KpdlTpsvQuTBD1>T%Aks zxDk<(^PnNndQZs?W;t57{PZ$I2P!!yI&(M}ka&BGb8vuqzCS_sWb_XWU`~vv{4xDal%48IG;HfkHjXq{8$tijfhJch| zTy$J(m^BzYdayREAHV3%U=Kwrop5mdRj@1+q?a%YGIGme zHCrSm00OioW~F8Mk3w_Ch9B8LGlFmRv)ljyE*@OWtw40NRIzIiUly}|4`Jr*T5v}b zil}qL92!jqJX?|@rHJy`HPR3)K3~Ye&8BJy$;dnQ9@Llf8@x+%Kd0t#iEn^kIxmlC zD+uK3L#?!j*^HbOOk++80hc^p+Uc^KXCy+nEI4WJ8Wkq)8&a4vBBc>a%Uy<0LiXjw z5*_dkQf&hxGoRSaWzQj?Pa!24Z!kNY4d)y( z918zwAD}L4ET|bwO>`HK6b{FX(cy4u1K?>grG(7!c78KmRHfO1<4w?d#9N@1b$}i6 zlb{+-a4S;x`k(M3oIYFSCq>R{CjpoQePB47D`RS;dhTI619*OFB3U+P2k)W9VvW2p zStn_Y&Wh+)s+5l-9Bp%frvy0WxtLu#>J{CuMc@ zx(J7driN>Mp7LhW!zE{FgBB{M7T;-2*3bV18s?>R-YLJB+b<9(qRt8$rxM@)boc%IZu#P$aez^-zD=+5t$D2jRcSNGU*OT4SL=Z{hkpN8NQ(#i9_;*w zLvQ_(LaReNA#?l-h7p+{$o@~vKc!538mb9qiUKia+=CETLWObSr^=RZrfO`#^4VV$ zFVbE4puHSvLH#Tzz^})j!T|y2621v`r@-yRc}VNTb3twuaNqP3EWF9oDSzgXs4dT& zoHi-_OW(!TBP2Ekutb31Nz&D@Rr)Zc%Kyw&Dhi$JcjM+}rI3B;f(GboKh%|hY-LdA%Q@pr{0+css zD#$d6P_$I$78m`cL_+K6FrQh(@?1rbgrgnK=93RrUkc_mYp%zO_!p%jwmbE?At!`6 z50hD319*pEw~3wfza&cpHgH%7JF0BL$k2`YNKcJI;iA*1qKY}}H}R3}$zrmx5~lEu zozdMz+r(3LvM{d@!bL6vfjeheXs3hNCQy)_Q3>G`QG~e+yD~ohF7^@!o9zG$kz+AD zlbpURPAfJA*r)&dACV3x$H1f4h}5sVi~}BGoY6a(Sf@)e~o`K0ws{Y%+zXOc}Lk~SxnYIkeNJ$l< znyw#}*b2^dD&#gX@%$9xTRqlKS@_`-1M{MIJXe!DDO;)f{*a6mZWrlI?vEy-5-iUN zpyiC%ozDgK$cAmF0Uab*fP4puaJQOyfU^Su4VBO&lYiSU+_D@JVjby9K(bmaI|hP6 z9V-_tnMiJt%?!+sPo|m9Xx~eK!I1~#6bjigj#&T1WGu+bfhryFE`?7IH6><;4a{Nb zqT`Gc;S-NEG50uE3zHzSpureKk+v&@ppp^QQgx21?I z8c~|2CeBk^+INzKu?tY&B$AKlqc|;DhTcQKnNL6g6Aj^$}ZQu$@ zyW4mG+#9(!8+54$1P=$<=R`JY469DtqLLFP?a7Z(>A@k zaoy0y%QRaajOfq~{}9GTSXXMm!~t0n7oiJ_p-_{)ydWs~_!E;m;tZ;rdoSda7Jt$P z>Ebl1a<~zSe!w^<4;_>mNZy}HZJP5P5Vxc@c9BEji^v5;0}m9V6dnLZJiUGyNvLJ$ zgrOrKZk#eM&0+%`9kGl9kjl&x2YKY_m56@qrCffI0a5ptmv>*FEXrS5YDv-gi>oDU z$bQ{VbG>h$Kkrmiy?vIu5sBP{P()rgJQxJZjqJ@$zY-^U<70*N(|x0bTo%uJ#IMTU zEP}gn2@ROm3{Y_$H=&2%}9PBT3I~?0Wf!+b9?N0w%Q@zcqHSFHFQ)a#$sqW$mG~xt<4SNCgWGQ zJ?+aL%rq6Ayg|C->}m=z-O}1zUmFd37&0+R0SWP^eNJZl&8Hp8DDvne$o0UqchfE; zDu6>^0Rv0fH7)=x&~wZ$Qr&{lYKknsKxxh@UfmDgNtum)0%bxbB`+Satt!ff(|4G@ zst376q2@D2-p2`8qlJhy)kD!5uI4Of`fT30LY#s?CSpsX#DPW%IaK15E>4B__E03E z3*>+||AEsqu_PAr0MhJ+IA(-&hqauJPM0B(JJ;!9o#5I)72=&!Wpq&SI}EWepc>fT zec}6fiqH;Y>02@W4++Or%31Va$^f-N7QQpQi6enx2vl|Xy8nB~B^XaKoam)|V;s)e z1 z8w58vTxMwi+DZSCcVJ_Gogx|j{;-W($T_2oP>FtdpvNshve7p01EiwdB>O<6Xe!v< za(+2_M&U`Vh=$%&a1RJ#nxi=gfSMd2pdaZ?2gP7GN|0I$5ar3a6rR79Fv$psssXZ` zz*=}5imIK6;b}A{;W^t#b!b$Cd4Bu@&>+Wv$`m3;;#46x5Xs@051YtJLpr(Y(Mrsi$S}{{0}}SQ(QIQZ zSoZ#Qq$^-Xl|dXkCsLRJs_f0iT{3Nl7XUI3IbES0yTm77C(?WVKVyTZQpYSAhD9>$ zW9;+Q0@lY$G$T5E8Mu@Y-uEr;vS8Q_Qc~%`eRaJyq6y0sOO+CNXqnzVq$6i(jV2a_9X{ zxZ1|lgDcY zpAcaIBm@c($5<04>+=tciD-NEp1>|9sK7OpQGFMjbKi%HmQ2ifsh+C%3^yn+S_b(7 zSZV)q+r_*IKmna=d4SreJ$IEq_4F2273r}X`6 z4MM+=qZoHH`^Lxt=f~R*K3rUx1u%?x!JJh`JETA65LfYRHPq>jqY@dzz4 z3dxu#n=2`TVTv<01xt;*Af^~4U5?h=a-q@C|6?%i&fC$~iQ1wu+RsC{hO^=|w3TOB zz*MqQ&sR=nr(&&GE-d*6NaE<<9IS>9%f3e-)nTPbu{1vJtl_ol7~NH}X`pepZ<<#} zp#_C9FYX|Ggt#HvGB~nT2lZJzMxa&CC6Uf8RV<_(=^{;{HH%AX3NHrc)K|@; z1u1rQJe>kVE+$4HPO@$t(BYyy1N4_EeC zyu{h-r4~L(XAdLP|En$f0MhOl2VgjB*R!C1wk)Ch5L-??*c;bNm^qZYGL@=ii!d@X z&}!3T(VLAh8wdy-wo6{qxzgA@I00`!U=O~!8IB*)X>2FVO>hPzT&%kT3?9QZ<<>Fy ziH<(Vx|Gr5jr&L&(u8=PbKYHAE;#vw+o5SnPqC}nc5rk{mw0=GJNCwiL>8`!qAEH` z8W(WxY&p6QSPe{zyFaNzGq%H0@3N{1KAv(pTY}?lU*q$-)_lZ{K%%xIG-%`Rs_?;C z@d7uJsff5=uG2Mqpmx^dEJ4Ulhpt0zPUmcD_IU}p$bd?zO72h(H2YtONU~%~S!#Lc zj2GNz(0^bd`;6N&5T#DZpkq9hK5Bdh=;;;de<*(xkCY2htV$qI!-M=0L^MJ`I0njEMaS>e8kFWn9H zIb6E`YBPO<;4|6YUINcY#IEJQx=2%1Q5Y7CAI#@^`~Jwz1`UpOfGxqip0_xzVtS`~ zys1F|nJL24kwM&xrt}i9z9|&N8kLG|nY2DNiZUO!X^iyLWH3M7#ZS5YN!lXwe?g-F zI!F}Z*ATjdvvlx+iJg1FCg^V#IO2RYsV18MlEd14fb0u(;_H zO0)RKeWYx7t*FLC6fY47>0OcgIK}{qCffQ=oZJq6naGo?QmOOA&(DBzZrg|u*Bs6# zZMpsBuMg_|1xk#@42ON>c-?A_Y?5WL=6t$pW-=!g&YoXP3K~3re9J0KYi%rcDFD2} zH}NaNiS#SrA?YtNQ{ouwqnL-K^7y{LCv)}pfq3e74=()ipYl}UtvSn$cC*3|o2-i&P2Gz+N0Jsq) z9bHyhbKBN_a>&WT-adA*4BZIfO%nY%HQb;oxME)1m|H>xRI2+n55hp;NNK~v;8FQy z%yE4adK>Z6KS{!PZxRz?Rxn8pwXSFH6aIP_G4jgtMIwc)|ChW@)DfKmUt#aCA@;`# z3`Pi%gf@gxY0g2dSt$d)Pw3d-i&h+a6B|x|TlyvyG7Gm~c^QOpDdT0b*+hOWy)X!^ zS(50$8VU(UW$wEEEG7_h4lU1J{L{fu+1O<)0wu#m4QYPMu-T}7g2yK-C4TWphYq3_ z@llIn`hNXvI$zv%BB}ATvMU&#nIY=|A7q#Sc?40QMwGr3VNa5xQM(h%a>k^%)4MF5 z*;16E%B{@J5~c|_P)XozD3IvCtfTXcay#kabLm-Xl3!DQj8Ua-xAh|`AAByh1p#*z zP5tRX5gM(`)FxV~@WW*UL#+tEBi}>sI4q{~uHXlp-$b98aSEv@zuM#l^g{K0i z56&DlMF%~CQ+ff1Kw7dZzkNx^HOMZZZBujF3e-u@O75%r5--M+w9~hUQQuUPfu8BP zSD8jOj4>IAF-lzDqm_p*V0Qpf9jg$I-;~F;yrCbYPB`uc= zx1G6!SaKfVg76v^zqsT3;DzB0BbD!&6Idr}U>E>@+De6r*%E0#s0x782;#LrnqUJA zl4dx|t9TE7$)obTB<*Nc95n*mH>4cNQQ=`g&>lBCXC3&CJcC<@CE?|N0(J8}i+`bX z;IWk8pj-!5>*xfWReSd`hxQy*PI15iV7Ld++DrQR@jq2&v5y{x2y?hT)jRzQXsgAd8lrdm^uk! zfZXBfxyW+#?rc1oBD&ti8znBf(55Ik#ukx(3gI3Gj2Nso;BH#xS(cE8*x9;=m#B;8 zM;Dm(tc~SasOYc?_7~G}d+?S*!P}V!=gsg8G`G}suBcfqACsP< zTl=o7C_=U+)TyV0ie?j>NEvu|%OV7hRHs^B=uRS>?RuZCA}NZ>&MA%^a0cqd8135S zJz+bjFs-?B8Xo2b?gS}+VEc`r{_>ZbKLd1bP|WI|r#rO}htM%^0fUu5DS`!@ZZAU~ zx^44EYu;3rymT?63eXGly9jI0w6cpFtfnLE-y_P`LjL=Y-DqJ}SN) zVgBO*COTL+#newg4<%b~{Bq9k(*4JGj?9$Dgc*1)NTxVlMm+(`WWECIg#QoRL!*8ocs|dfPqmP&o;m^o zD6ESBub%pwaV!sktIPrV3}8GlBxw^tH$Aw9KDg7#AaT5x95yq# zPC79>H4XLPTkIIDp8}V8${gR?N+DTH7$-Up1cBFpQ3!AXX(IYbdnra*!Qyy@b1pg1 zY<234J#5mHGPsC;U9EcoJ78zoFxq~w{P;FrgDPD;nB>3-^rZ?Die^NX!v2$y1Cv-{ zST?%=0t2DbX0$+aP+=jhG;FmEB!`0(6q<}RUBQZb7f^|flXDVO(@M_BWLAaOPe;#a ziCEU1Q)P;HtP_cHr}@WP?R@;hTNQGeS7{XKnNY3|9QMYj4(N}X=@1@_>hNkVTL0KO zdcx)3Su`JK5gce} zhqE5Y-t<*|#BdezkoS9GBhyyF$s{B=LZc zfD)n!MW~UBB{Yempky2q(=dR7Pt~+hR#H#@4H>(2os8@Xvn*}Q2`9dpgr-(IiW`Ak zZ?{zinC;~g_rVqMzQkQ-^v%FpuORMC1T5AxkUgd;n;l~TXO>dzfY05v%RDM)7cI~K zf5zTEILq@&^Q^|geq|5{L2QIHSOO_16@z1}G3s3}V||Q4sM`gdKT>>5JnkrKsVP88 zXSXSr69=IWNb?~ptf=mFS6f)#%vNk~ijpyzsl;u85ngwBv3i=`qG;QKOcp9tJ9ZVR z^-vAk-|yVd`$~{Z?_wEVy&uo}Joj_o_qon>u5*r;$S<}IrW!?O=gysx!6`+J@|cdK z7uwpkSKNHts3!~On)Plh><71Smt$V=(0MRpBoiBeV3_K-JHBWj=riyb2x199CIqr0 z;Kqp=@~Mk_iD^F4l>XE!1-ftOL~H zPZ;iCmX@!vQRSk_|K7~%LlpyaNk(73;7>^bFMenhdEn3PBigkPRSI3!8^KMDoC2^z zC2iiydwEJ=GNWGrCs}fK9#t?Zj7;BejZOaj1wtregRwi zuYJ}%4yL=>tL!*vm&CQqK$ZNJG(U!+XcQQXSBNo8O!-KMj zibLE)A^uopA7|{vH3|E_NU|{-SJO-;cVjs2e0RVK|sAc5a4M)S;J1p^CWMp1eO=K;ruJ|Kqox!!x7$ z7#k^By=yDM%C~UyEI1dygOg0M1l(us#iYT#D6-DAcKX~TgtKwTe4k$!D z|6|m~oEGP}5jd8n5|%Bpz*pdw5}gGrMW$5I)}9)+_%h3!744&RQ6eTqB<#*q_5fL> zocxkqxs$`V413g?)#oMLj$llQ82`8nf=X@=Rtw*JHApP7b)fmG$FMZK8TyY)EZcqm z0BfMOrG6tp2>2Vozrq}}TMM|#?*UFX8?zjLDGINllu$-b#pU*EBr+|-5l8++`VJXl z_UE$SG$I_uX%sWm{K8fdmnyU1Kv6X;A*2?C4(KaFn^)S+ig0nY)5P%~=mmOsQ~_^Z zH+Bi1K%Sq5%j_9$2xOFeA*o|r3*81%4Ywr)Y@E#{qgDY1OK%WAp_P=8O4T;1%>VEO zi>;8%lTdz8QU)@kybr9FC{3VTTvEL$@qz~|oUUU8P6WY4Beh~~PBz{axydy>A&)0% zEaZvV1zx6&LsbWk2XzN{Gq;w+1_m7TjXN}#5)YXBMhkzwG{yP^zc7Y)OYBNaa@MNw zSJs1}E6)Nu{>!8a;NF2-_W8ku)hEVcYIAWQhIC$tulUB>hHp3pI~DW*6ffOW5+|z^ zi#B$mB0fO7r|D^-N zz#jTPU)po+?Nf7pur-TN%@7fZ;+U~+0)qe|*k7SeZatl-APsC+1-3EJ;)cWX;oO*><>BJ9E0rXoGRo38X_6pyedt&bb=fN2jerG zPv7F*mA}ILmtKa{p;N>vX%OIC6CVoK=-MQ`t$XBCf+nmfrW@E>D(z;&4US z@sI!$TDm{s0ht)6tPtvY#)VM`Y*ZKx-WI?~PULRn5af{T49pOS=B=zxC#;bW=(HvM zh|cl3f5`pAyl-?^Mu?M$_Us-j`wl~N1D}#PHt-lER5Zg#U&VFT(a5TfIyr~F1-5JK z%Aw1d>H=D#eQMy$k0eod+>yQ6NZoaw0eX_4X67nof?`yCGdIbPhO8>lHog?EWE2p; zsDRGim%ac+1s2apI{zqTOAC<6$^Lff!k3A06PGq3q}!x*b^c?Aia&z>$5ALEnbuC6 z#KfZzSH~dlM6-jThD?stw28^AieRH~BE0Zyk5OdIA$LAZq`?~Z^R)~~xZ}15wc0w= z4zp6Hqs;PL)+8$(wc7EvB->NdII1x$a$_vAZ?`=SV%P6g7^s&X?nYFS(JvvmHm>|a zmC+TH(s+~4H5l(H?cHv$4ybjbWHmZB>$Q{FB$YHv2Kba_Ep49`g5-j*ATcjHVT;W+8{U6gVn3PR=t< znA>q{dnCXRh?`^^-F2<@I(@UXwivKiYCCD9J^am~Z?dPN^(*k^5;~>^6H>R<{?!qKa9mHZIwzLwGJgvxASy_%BbB9uCPjADIoDIu_{O@{=$E zqT+kG^J;FLkIsnkEfxxbxoPa^1OjD~M9~!0##Cfrz8Z&W>0YnPuOdk$dJA;XOXonu zj-Cs9YxD844-~K_aHmWMjge5}0@D zVS-iPL6v<|rp+g{@F=#kMHFX$$K%o)d;A>$iVB4f6IKz>XmOjU@-t=h0Tf<+>{b#!8(~3oSg^%4L;u&065lvIb7NnkKtVHA=*pP2Vr@4?AX`WP`qef#! zL={M8P*6Y2vTn-HMyyKmszl<>{QH!g6&J3L;zS+M~9D+O6z|d03wIjH1{%lB%zSx?2>ILC#C47tUdGu*gM+tIS;( zbe8~JSf*}X&DQAO!Z4sPk=x{U^iyjp##BhmB!Sf%>0Px3v_w40SJ-_{f?kx~vc4NH zG&A6}&2?^2dTKzX@*`*Eb*(I%EiSh?$QjAvC}53ViEt(}+;RfRBf-{3CI$h$=0|K! z^YWpU)eWHQe|L%y>My_(-dNm)X5?+0(U!r&d#;ER!A-!cPf(@NyQ$${h?OXL6Y`84o$yV`Nu{^Nx+L0m+h{ks$=w!2g=gAb~k__oR;w3<3YB}1+h zMN)3Tyvlpg7gzo&3-%SD$JEvT@L%I*gdN#E&8{`BPSAMgl}em>h1swK;)|Oobc``| zV6unFb8@p`#W@=-sL#wZl5%CmdgJh>mPWu`epUP*#0;|?kH{cJGkkX-Z zb85_o9w7D_gBi7~U8>>67Zy##s6*q89>K#ZXgxKZ{0SZ6~6dbR&2_i?T}h%L8q=qr^qkKzY)Y#q?rvD#2XC{{rtbvBca%^ib5|%Uo;>$Sqogqr150?R`#ugkBMBKNp0zkXUI|~2n(B_F;a+5Mp%oa9(K$M zRzOdKZ%x?h01%|n8xy4pac)D~9IlSZlI(AGqnS4p{RaV;j@e^;8Z7hk=*|G3E4f7i zl_})&dd|`IVh&jg^p#>Y#hPl3-s-DYr|L3t9w16&Svne-e(yj85QBjUrUY$`uT`^# zsacMY4>*BE$Am}wR3cFON9o%HZPZ*5<3Zzvkd(lej`RxlWM9B9R(BNC4pA+D6J0&8 z_Ww?cl`V#G!{8IZg7oYqF~O`kTSYQAYbYQD?t`naQ&fU3jCJ&1!U^K+)$lv4GLBJH zh$0w220!;V(}m3H1?)~Mmo@!UaEYgGyO-58qAvl#-dhX1kkb?cu--~2;jUX}MAAuX%=i-uBK zG}&ok@q(SFyXwFJB&mC<=SCXNYL_{2LOKCt5+L!*#$izS-wO|l=1ya8+wTw(-#rTH zqZtawW1|?qB;yHi!mWXE7W#k9^&$933ZcD1A!*gg~R}{rF9)oej`A!>8el=mots_bll=8k( zBuew#ILRMP%Q+a~aBGYVSyc<~9PrPYwl~y)vLJ1cHOz~4cckQ7`t6?!Eb8bQKQ~*gHiOxs|;&sfU4X$$v}@wl)Vh zK`jm_8v7d8vI*gGQ^@4;8^k}bGVh( zp^?`pdPi&o;}(*E`p@4ZiOlUAv#QO)#@40uDb>TE02GqU0X3Fc{4dV3rRA7Y9ZRsq zFo=I&+rZ3Mghaxah>^`2V2h>-mZGfadu_`~ZrNrPJ7mSa_;c~11%?nL7<=+2n0@PZ?3*U_c*3X)8gukWi85Ly|XS z<0LXS;U3aY2ebZZZoQcSz9p744a)uw5>Ps;pVG658)OZv1U!(lg~SsH5S998O_5#x zvOVeo_$;YZ&VMev@xns51?`x>45G#*4(MvJ;0}L5y+%__BPbDxxJ|9YG{wf=;=aOF zGE);q|Q`1HIsNf2ti(~fBGD6Ee_Dr1O$M?BS=4Q-3_n*`X{h+49BnAhFXa2J03DR z>;P$Ke(P^-z77b8ply=5#42{%wD6$J6l!67gFc#s_P$z^q{Dq&C#&`VVw@G06r*}! zq)OtyEp@R;Nwxg36^;^fgTCrt@m4U8(M$;TsFnqKDb2Ow7f6Wnykm2KoH6!PwjU-G zHxCdmO~X)IkeYw5Lt7^*6Xc(ztayP0Ag{kYI{gm8S|r4HPNic;YeB{xJK;X7I&h;> z-b_LeT19Q)f8CC2YY8+}SV(UDhs1X@a0^otNL8=oWhd-FdEl^vW;O|XLhT}^;eXtI zLO>xc7H+77>HaTZ5L3OA&HQz5Kq0DY!p1}X{O1Bm<6&ojT#6b6p(V%hL;(|U@+gGU zz+~3^pT`a9Y>6gCJj?i!nruQ)mhgM?fi>UAeS>ipNzp6(ewm%O81fMIeDyy<8}<3s z0X`liptNKaHGw|wI#;|QKMv_t>J-CQb&*6BC7So`qf06Eah9%QI20x)5NISX9WG;C z{y$D#E%M!$8bC9qhIillD*|(jP>BcVNiT96KcmJL+$RBFDT};AP9HS49VR<{8Hf`H zvN^$a5}TwF`9`=;n@+LIv*RNz-W>fIPE@xf|26ePv49I($+A>|496S~+#tU2X1Ip> z=)Z_%jP$Om`0a-;D-R)`aEw?LzMP?M}W^7<*!Sfj18RfE_;V?_20hDY{|jYQKf6TFm;&YPjqQSpco<=tQ99 z7SPPkn&>2kYZ}o%)mlAG1Uck{As@!KhOUvjK?L%^rm{`!xfBe=bIP4GgM)g9%J(9B z`L?W1{pYGo0ELkkB?;bHnJRh>U1-9+X3r3)1YY5yY-Elh3UVqSnW!O(P(+B&FgF21 z{)EwB56LHgh2`Z*46vJG6ei@VR7i*AveXTUU#%x`xlVL#jAuh>V}J0pzmn}EbcwzW ziA>{g6am9YCtMRb$lM*hJuilhotdFY0Ia~`phQQztDqI25_i)Ivn5%CBmj+XE+ZOc zlqIyK;g~|=HMMTDpdT@s@Xay*u-gJ-6Fx>4KX_$B1frll#u9|I`Wqz#C{PB{5ID57 z>{q#bP}YKzutHtPXk+VkPGRALjbzH)Ps=FOO&Iu?>KcR<(Rk^P(4kC3<&vU(uhTr9 zOSyq<+4tmR-bOPzoR;|>l0FK#vmcxsrN)sL&tz0agWC>kI! zGlk^jfHTWZ)?EKRcApwn_2jAJsgjLV@waS=%DK_(r-7SM^HO#{UJ ze|;L*hMD>}A`z$1hak7Q4Y!aXAtmNQn(xz9tBM+&NO6j|eAcuhG&aV(`s@u9Kup#A zY>G*n%}NoHk*DC7oY&$x`vBAtzCFe)hZb%j+R5FlZ)g9>M{-vb9VTffgjb4JK=F`? zOmuSP6&klTrH(Gn9#D`4pFzsrC#6(05CAO(o*Q}?IK=#{3B@_H;>TSP&S9Fk0HX}U z)fkzNZY#l=)<77$!%?i^iDdXHX+hnbG{d*x2#&x06vs6l_D(E_br#veggE2uOir*l->; zZv&^(3B8F~12?p78$qYYL6A0M-?5exZk5QyaII+xk?Q`ZfFAZF1($|rTSx;xq^uE6 zXJXO8E4qt(80~b%PHs)jMYxwCW&rea)4duljGwGT04^0hN_-DWbWP3koh;bI5VzfT zM;C^IMaw@3`XT>|rfkPvY@0S0YYXBal?h?-I+KuncHmnX8ldoAm{XcDfOL2}O_TsQ ziBixiTE`D)MBs|!?D!&jGX%ofd%Ei%v7VSuDTs3A8+_E?LSCG8_U>D}{zC*M6!05= z7bJ9bYAW^aTzuq8DIuL6q&3Y%t9bDA>8`4-L-P<8dm7oL{B9c`NF(0DRfNKqGtJCA znNI3qvdf$GAiK=`$1(_(u{@!!5hX$j38CJ6Nr6q1uc|agPy^%%;W2221gbY}YrzX6 zgYiQ88qa&-x(^#FNe5S$ou{$-j#fMfSGB0J@;Q>K99+V8msQwI9U7nrAmt6`3km~A znmQF6&Fu3HIw%5888KA2yu05eN}9R&cy(qty;BaY7%KZ~ zS%2rPdJ<6}8iLDY3snfw-DvqOpbguQe^cYr_I9i1HInBrcZ3ZHS25W3ty(bw09K1u zE+L!9a-&p$f-jWDwZ{IlAKP_cyqauGp#Ytz#%i{Hy@8VfbD+J%Nsn5BxhBi>{pA;E zpD{qLOHe^kwLx*}vf8Uj#)Hk8Wh?R#>aby{(v=X2%{%EXRn8HoT50vlC>>X=1Yel8l!UCbZN#7p*G?3`k8vgCy%s2pl z*7lxduRYLV+el=727;)1;})b!1hz?KP2jR6M<%D+^ka=s0ohRPk@n)|YQRbcEpE!P z)L4@n0^&eqOulN+B$2!xatUMMR3MDD66p@}cPh5*r6m_DT^72bQTAAT@Mf52oyQx) zmf|9{r!$LIby8H0f`)4U;?oiBg8MIpkw8JI;xNMe(rB4_!S+pf$vZ+uT#R;|^AC1q zzimDQ%tk&@9c$?UJtxh{o8ugzAR38Nno5lkg{`q-wC2FbF>w%Phci2(E`}{0SPiD* zy=9?c$JTxO{7Hg5Vx~vgsf}tfEeKFq-v!Pcv!sB#4B;djKf`BNCixspef(ilk2nws z)s0o1#Wd2o!FP9)z!Uf)4dy6a%D@A2V9Dl2_MNb#S&JSf{Wgu(3!|PMoWiBm>lGG| z1!H_DVqjb+#fzH`uA%fK3Q?n3=a($-N)M7&1>E_1L#VRKf(?s_G(>$!#^T!cHY{(- zH|v9(!Sqs*SP{|vPzUJWsGNY^C3X@v8T4k97lCn5Yct3oB zt!s#w9#TW0==q!59)UdEO-yO5myHtQ28z4J;1Cd79?Q&kD@Y+GDjI2_ z{`z1aJMaOU;GtY*HM4?Zt1%1(Sd*Cf@*48cn4o1ao~%;$O=dfrEAxZR$bQ5IG;tms zf-G|Zvd7Y)%bGe{sY|(9!c3FFAA5sR9L${(-$eK8X7m6@n8gG*b&5;1ZXv#;kvU7! z>sUz&q+m*B+kdAuHypt`0X2k9R-wbedQwdITcQ_$PGlOPbvaW!K^((3f@~tDfdLh* z5SGGBMo5FdgfX^L66XXfW2nO~K*_7cB!(rP;+-4{sm>XmgzeE#AGtD40l`;k06snf z>m$y3l!K4}ey$vX<7<77si-|fhwX;lbecN4|H!w1Pqbr5R@%tT(WnbN%WR8kQUq!y z>6_yRD!P-qnE@WNJ|24Ecz2quWnCff zg^ZZvnTB%)pcHV-|KA50n31=ME1ZRY_=>o>F-&}21g|po2O5W2NQOr~QE}E*rHfHS zFDRB>s{4g0b(%wEt6T)Z;79>yAV|Yk_etDi>CH{BrC4a92)G!g)1vD;!54)HR3MKf zBm_a#eZ4JQEyR5aaI)23I@V`cM5u$?3-vGIiXc{fg2ssH1o zMqqJ)XH5Gsd!juGaN$C|PeBsSyBkPfPflZOG&h%iYF_yZ!w3Pn2GZ>iKB2F2(hR1NqIc(~pn6r66ZE92N@s zfBsPOdaA~1m?fO}k|S_AIKoj5mw-?tzoj5Vq=aK^>fM(2U+BSTF&`8a+c8`TsR<_R zfYEYwtsF@XyP~Ym2E*jI+*oQEj$r@Vjl!RA$_GZF3uel8S9)E>R(b0flMA zMMXIg8Vd|pnXM6DIw$#VAvPl+b4Ypl4!od4rfJegM`LLqhcWyLaM_MQST_tC2W6@i z7yX=XW``!11&o_JdFpX`Cttd`r-YmJVrMfoh8&et2Rc7GX_%ZQJi2tj7Z(9eF2UK* z^PB~M=p0NJc9;%aVZ6{d$P}(1z1jIdPE0QV3!1g(NwdanpHLez4xr3&P~my1Zp%xd zMQ6y1fGB)dnf(yGOcH>=Ji1TceubC#0S%z=eiV>$3vBqE;#->W#c zEr-_=mTTy?*}NDqn$3UW)v=c-N3urC>YR=`3w*BS1uv#B9;G{T_XJCrvBs>OC_RuL zA~-I?Hs)yx@}tan6333njT)uGo+EO>fD?3MQb6<=2FHy14^K0Wf5Gd~fCZ9ja=MeAdUKo`z6A+RKu#(Ju)Zl{X zUQw=>!IV$YzLvGTpU(sq5;c;K*E6^j&5?MswPD3J^ebz*BRND@^vMztn3%=}5`T$7 za*Z<^GCPtel5@Ec!_KNipQ}2M;WqTyHWVD-aQ!m((q;;w8{j8ZitUVR@1<}Ne0pDV z-3YtSAphq@`n(&N0Kqr6Y-C(IMS6^SeBMfKLa=#skhk*`PM0{2$)&dl1df81a6FZZ zY%?0+LL54Xn%H7)q;l3AWm2_bJn0Ll7iFx%W4z%LDd9ohXY#anqW_g{vKdl8acPGv zB=fU>#uO%1h9IO-xs94v3i>@r+{z+<zDFfvmf95(s|}$8>5xbr^)!)d z^Mrw_y%^^7Cr}co28!si#yVL~Ogv#I1+qLvLU_ZG?%etX?@knx7Ip4Xs`k=*LGkf@ z9)927JbQ#V4UCUKXej^*&2s>%FxHd`{6A*Lr|!meg)DigmVh$_=Hj0lle>m1ll9z* zd_MRRRg9y=AA?LKoJgLEAeenOQRp1r_D1QOrEhMY_YGc)e0^ag#SfpW{4PqP^+z)0 zi-_lm7YGHCEmgd3`GR*3?B5>psJ3E)0o4~0|>7*TP9}s&9`skW0g|%n-F)ROi*|v z;fD-E;u$(95`{Gq#~6vgq?!{*BXR;lk?M)x^<)`=E^&(0CRmn{H3g!9!S0A>l}_-q zyF58n5AS4qnjc-DEz^be7$auU3imH8H#;-*P zSbU^{hgGzY#OR)h6B3R~nUc~Iz5TEdk)-|QHCDfI$Jt{6DvEuu5-&caYchGUjOcP2 z6n>rg8AW&d_Q#m2{?~P4FaiLV9weg@U-3S!sQ!pTYOlx)2Y0rnQsaK?m+UuW!Mo&B z(P(x|O1={@KnXAcXLh zxS!vTT_>HnWtJ0CGe*u04|q$6tBlv(G1Vf=nZwM)M5n)4b%ya;Y^nCHSOexN9Q0664_MPo@h4& z6w8U4R4+KBSpTgI7A0&DGx{+Co>6#q7a02WEl^SP3CENZ1)^F(q`c{C6FZ>$0>(ut zS!D3SYjDpKtdW{gt;D9TTM8;LVg+MwZj~c=h)I^SZG-|#?g@5`q2Jj_NB~FqA7>_k zgFz+Vs+~N&RUVIX|CkwssjtCvXNYG(!0clncG&z}&*Q3ho%rnVnDmr zCuqF^JaQ1uk_G(Bg;V3Y6g4xV3!+=*i!0P&g>2+L4%vw`GSZmk*dKx@_wC#433wbQ zmWVByJWK3Q9!Y}+DrVLJo#?)Gz6%e5v~csn(S#@RT)`M4HG2z zHe;=ge1r8&&}3ah60*S@8LAG{V;u%Q7Ap+^$+DySsoyjb{~^qJG4>P>Vjpyq--fGU zaCTWBoBU`}z?&kMTU-uaLA^ph-Y7w3FE_@MKn?AqsKCYg6bf7NSqCJs$jmlXPCy8-L}~05?m4sH$m8e1!MI%(abO_#(8^$vP`I-K zI1>P0tjm`rTt_Cjjt%il(&bBgtxDLV#M$nCTnx|_F(R!c)GiwHw3_+%nghqgiI-c2 z+kJZ@I${NBK$=_=n2psW1I}EblZP1Z7-?WB&{muHZ7wNF^Q8;;9hXA61^woim=sSr@fh7z9aC$ z@xd(dEnINIBzaV$aW6oe8ANb5NSG1mIE2}u04|9d3N4OIP6Va?Dw<1uhXgBLeRK0q z`p_0r(-`)Ozlmv~)R9jLZHqFZ2N&Ekuy17~vj_Sxr>qy@aVm*8Wl-AO?ehiSfPxgTLllJrRK1fB^{DNIc0CEfrI3SCW{eFo}gt~ehkOdrp}Qfa<8 z7@_&*q~m)QCK*mzd&GiI`N)0NIw&38{lnS`D;5#8GXwQ8FkA;&{)c7LQxt^zO&&^gKNh*zTJ?2~XDx6pU;yt`CU5)>; z(O9!2TKnQqVXYv_sePS#WmbL)BY>H_DG;m%uS(nE{oJMp4iXvG6Ham@TTd zA$dfEVR|oIY)*m;j3WK1;2}bT5~BsaQZ&p?EzZFrQp+hyyns|<+FFVJym(R3=(+MW zhOgMkKcw{nD4_L% zYhc6L;O&tZ4~_lCDb;UHyxQEdo~|IKHeSkf4G3_IJ1`EmtI#3ek2lUrYa|qf{N}#$ zrnZ}VU_T`k&ax78uO;y!SP4F4Zr0eWfQ73Z=g5KfN@~**d#Yqa3{1$asSy!H6X^L0 zp*PDVk`h*vE25gs!<L-E6SZwSw zdPyzyX#GJlfq9G&<3#~pEbSzh@$@-jnucIw44Hhqp#@;UER2v@Evvt^%yDxark|vd zV-b$L0YAEpGB?CR1=9={Q*d-S+kt;HG!|h zF6EKh$scr|scC(NT^aP((ri81P|M&Ia`z9i&L zt>nkmXU(>5$)Y9Wc1H}S;M;5dw3RlHI@;W2kjdY)frO4eg(#rH@IngOpbG)C4A9-l z+B4*qgKna`lnof7@%R&<7KE^NYyfmbr%QZ&qEy<)(8&l%mhVn|7!K_x35$=e54Dw@CTO+ss_cY{EtbFt|ec zfeV_k@5$`vP5s@NCQwn3K{KC9hslO7DSPtaDS}||IF|TJIpadAc!nqyANZ2r{^sBZ z|1wnr>{7aIJN3PE4z*P{SEjJruM+K*FIWRwS6P4d3fvWhSf!fR9w}vGHSJcHE zo%cyFDBdoKob1vHXPZ4>%35qP&R@2CaKEp3mGZEs2D*W|^F-B33|BPy&K#VexH$=4up=p>K^i5BCw z70ulY&LRl!ZaBRxG_?{3QpJ#7PDOY#W$bbETun*i^aCe=Tq;P-&oA9ARK{$2dm7FA1@~IY zFXUl_5^zQF$0_ciWYQ>&a|7N+<%u-lqu4dXDK8yri;dq>#RR8V1|6lFphoD?R8;++ zf_}m#MuhUtdBcdJU;;9LVe%yvad2l~rFT2oHr#onmG{Gjt!a1H=`Qh!_7ukNCp0OU0%}8+efGM8b*QV93I|~Uy34nm?BlsZBFjk zJGh^0K!p@eBy`5w*l0NKq7gTs%}p(Gvh@2Rut@`PYR0rH><11|@QN{|V1prb_8a13 zmN4k;fNrwyoP=={Uk=~+XsxncxYHjNNCFo?)DUQ4ad@Ni7 zMu(qPbCq<6$O~gj6~@6xpC9alw`E@V$8wpq{{#dK)bM=U%sRae5W1aTzB(@qW)85- zHpy=|jj)@J8JFpU#O^3xj}$%GFhT#lt!4iPP6wc8Ae5>EOu(Faemy8plIfuKn3YnC zGXvxI9c4I1=O%35z!Gru*_lvGL;6wdx*i8h4FJN&l%yEth4db8P`0NqOA@X#f=_8E zZ?dHCOb{+JJ|gB*VkPU#M%D0w8p#$a1$ipKGf$W#*Uz_=joG4q>0JPD2gxM0k!dK|_aQw@OfeG_z% z!hR~wNk|BGh@PFuZ7xI2G+ga^g80n>iWkFUKy1{JfmG_LJo+Ih>S1DIhbbO&g*X=9 zMl~650C;P8A~?LFnMbp()`ves4IP1*{PXe4Y&&0o5HVV_UiM-7HI5_I!~Ypj$pTP} zv0CPz*uJ4J35^9@GC+M#kxNb~!0K;^jf5j0Hb#i=<^n0%clBKHfFQ%~uwc*+o4b@? z`z#fm_buK{^RFs^Pr^N3BJkt08Pt<8I@t;3(OltjvIlXa^H}HZ7ZXUzgQ+0!cq>RJ zxH^N0p``+&vhgq#JBwqlamSftx=z}$AV+z!FmlK?jW52 zhB=B`$k$WBsw@&^quvTuD)~{=rRZgsnIMVtQOJ&VPSWq^v;+yLc&%E6L&6qswecWc z0~)wcL%4z|i=z)2&+7k$4(E&?#&B%oJRP241)FOl- zG3oKK_Ksldu^2=iq_Ye-upNVZ2+HG;fn~1nYhCpw5u$M~%-VKLrg{Pie5nxk9^e@k z8SF{MDdU2EnfN%?ir^O1mKwsy-bg)C)nT)RnH92Z!&;1eG54tQwGztwQoZMQ%CNki z>&fJ}uks5UF&BYCRMX9#t3mFEhqy%Isvh#iq53S6g-Re{DzXqzE+(9}v3X7i?{o@#)o0`@<7uP~;mzT4Wv6o4_!913d$<)@O<=u}vxWAFHo z(@&x;cj5KD|qIdU0ELbb(EVuH>)iDbb5fsm*Yf*IhS;!fxw`^OC7j_9J|Y_tt7j;fF*=QmBfwAhmlsrA>AJFb;K`0_FMhza z6J9aK%XG5-VG(d2a!a{2cL6XRvCt{mL!($wa@@0&<3=2&ebB%l4cfej{Dm-El&QII z!28{C`5{rp6F|JgmE#?hwy*vd1*nt5uw&@BOAO3yg2f5vBy`N2?~gX)oF5`?$eF;W zfIL`C>4^Y9MzE;<645S2T!V~&g)91va^+5PYE|bwd&U{BD_BJ1<78u%0!kpSZ}9#) zDG+*pb_y|RZz}kS@)_g~3KhXSQaCmyU+B8xkI`7>C0SsDSIN&$w5!meiIA5uFrlMG ze%+=36mCc~O=;GTac`aZG0CB`we&yoT*|(V=|S2NgUl45R~a$T_(1L?k*?P91b^TX zxu#=Lb<7PV05KX~_SYJQ%W~9Q6@?gLKm;HoK7L$hs)l)I>`^(oqouV`W z(tu6^hr8|ftmh)u6qlC0HKD%*V3Hlb6Cz}QC^(%2b^QAq=Rq%lN;0zN8{iZEm&+m7 z%KY)4e2F?afrVgwB9VORPY#E0N5B&yC?Mp(S8OC+F+goY&q@yG+{6?Stb6u-%eEDW zqF!yNeCWP^5yOEzD*ohA+H6o0q)R$VK%l}f%s`v$DMqP)Xnr?3^2%l%TS%HeoFBM4 z=Lh)ep*W3+7_}#vkVW9@2(eoZCoAN}-Y$*^-b6o&N4oCTGRDEWFxY3@LRsLZ2UbwT zjVk*O*BehiJ6G?@S#Bhma0&H^Lk4JBDl(Q>OUEFSJI*(~z!HMZp`t{+N42lqO5~YD zEkc6Wx6AH(@1kBij^?Hrp<3b7>K-j+NB-Yj*VTl5+` zMtY|L5@$ zat&mh4Ze$XqQz({gJJC;a~=kh@q%-{K!MC$}yR!}k0hl;F$ZWrW~g(BWy+wcJ0KCByL%>f#7C~?A4H%?`q;SSW{}aOesG|1{I|tW1j&nW=-&pJeCqReVPg442M^7lj2|I zP`<5G+I<%Fy#AV@{$)4Y?nH+yH-aexK0_{y4LSy`I*C>76EC zy)pgTi#sVMO$0r3kbwH_gQo<#kXm?CZUY(Rue|MFjbu@?>%?|pPyM%_j=%dikVX|` zkYDX>1_ykti{6}ZZb$6~!Fh5XDI^Vp# zPmGPxDPKq-pp=9aNGXIrK!Z28Q0h3Z;Gyq0%#Nt%AI|^%I7wMQq4)UtS^DV6Vw0!= zF4i3h?n;Oe;jHf5Zu73_O*J*em3ALmY+lw6k?hcrGyWLRiXrD5V}xg}Tb3+;%YVlM zKxy_Z1;7(Sn7`Qc(KsA8LG+Q z7k2Teq%(lKO5qFScbs{&?DS1#NBSwDrwQYoyoIau3tXF7JcrfDkK1qvN`XGDBu;Tv zF(Aa`bg_NUEuL^4zfG2;T^o1{CCe`&-n<4ce-eGmcIaa)6K@}hgVreuP^}F9Gvyrk zx2y@$nQ$L!-*<4t$`J8pnHm-!WoHdN0{a~&1jw_EF-3#ePNg0&%)-A&fZ%ho&5;O8 z(B=%jXKl0NDOh7)smOPS^vbx$FSgMPCrkA35;Kew=ZS19cXNPy1JgivJt})Bh3x30 zUDf z&1>@%j?=&cS484=c@3o)$)Ftzv9aNX?+Goa(p^*E-pM?vQ?2TJLiC}W2OLoZA%@o( zr$S(lydmr%mGT@kA#h^-qCiTL^l-NP5oy^9?8B}oYtK>8x0qcLfi|xREcrZOl_`D= z^%5A65%Oe#Ef{nRrnLw!ImkDM=QbWd4NIdZI5Nn;u^OrbzeB>g6^jhC!Sj^1M;^NW zR6C!x4Mo7pfF~0JyET$@7wESS3=#B%%(;v)LvR`;BnRI!wj%mRk3oNil`HyQ758x5 zxLAfD)mDJbQj5Wi(IK~Z$^?(hdrr8zk262~2)rMyJYH-gqskSbGZL&*9MY?d3C|W` z*3TPeVaIb3N(pHWSB1tq1B_4@eIZhoTUS=?I9%v)qS=383RqSP_S;1&TMEdW+=2 z_5W$8W=liv=4<(A9@E8mEEQ(9$&5cfM#X7=CpF&z1o=NDO*Q5wLYSN@CzagZZa^E_ zaH14omXi01ZVFEJ-f%s3aE`6@@J#x+~9=dOigwkHh20zVL&;dPcbaoMU!ZX|L^k3ZRsn?p-FwQL=CL7cwbR_jremcfkRy-_-bTOT zil#i#8JHn8IGBND$M2Q08Ff1!di+Lh>%Wf{z;skpg>jfqW$k~_(6JUZ`1aSfbpcVd z!T0o=HS$01hC$W(C@>%uR-OaRN;}&){%C3p#|n%zX4bn}4UO0k#_quBR|w<~Xx{e8 zg+ksVr^D`p&uJ0>-mwCeJ;)%|%1NcvhE%CMa*VbiFTGw8Wn`LJoqC6u{YmKI)kdd; zbT)Y4FC0`nEGZqS9X257l_1`2B~9nijvY7K;A>*XFg!wQ$dvA2mg-=2>x*b(srwTO zY|PXo*vz`4N-OZNB%T3!rbe*i(r8N+ZfH))wLre{{-w2?7h4F%<(=Q;eQ$>zBWP2y zP~m+4*V(>0Y7m`k^K6OOok|=Q$>oKn(XxEQW|_H%r61ZAaEYzltyXTriQtlymddozxk>Qo99)kT{*HAQtIHsn?cDggtHT3IN>NMH!N zRv5}rqhf#)_76*id*11PRDbUW zucyE#b7XY-4bU*WXX|M@^rny`9jUAe!O#Xx#+r665PRDco>DnBHt>TSPVOYCHidRn z2)9>?(O)cgrhe;x*pY*JwX#xM8N9&1+Q#sHkMCqAkV@4!Nj(OZAy-p1=dtf=OmvORL!ZdN49lFA(P^N6is^o{>*`W+*cr#{9pLbV0W2Zkg} z%t>3@m|Zo(Z%|SXEKBZEo~B`YvU>&YBN1Fzq;j>G%Wj-fj)$P<*dY61YfX?>+sG!_ z&eoX{!^JB!sqhlrdY0fJf_vC6qw6nA_Tapb2F20u9|#AD@1e4mH`j&8DU)f3#yV{s ziK>gYdp-c2Q^e*9TZVc+SFbKtF`45~H+PwNEL6rn+~Kcl&wd6Y(xm_c=%FoK`rOMH zxrx440Xl$#iDG2PJVAC4`BYJ3I2akBiV%o`b}~5r$=ANo4tpe;R#8&W0?5JW#20*K zOiT)=+&u6FMeBESYKN>l*nQ|*FAI`HRRiA!-#oj&8D?jELiPr&4^V1BPlVDu>vI$N zh};Yw$&@k$K*P5uFk?wR2Y?~)#SJ1hGRoFP_2cY(W-NU|ArWo@mxLe(Xz0N+bRZ)% z-uG4eHK;@GC2r+2kT}vfnI^fZn%90JtrLhLuSJUf6Ggx{E)?|WQNKWM#M6h5Hj|^D zTkv(D<==MKEqE6KYgKk%IB!neA%?c$Qqm!3vsQ(t3J~)JNHeRt-#{uJ2 zMQ}^=V>dVww?Yg@fPnbJ+Rvjvmw_p1W>^|Emi>!+R+eud`|3w*K4;`KAlun|@Hp7V z!|As3L6p1FE#x^(Dj-Ttv*S3NTv_}58Q+*u! z7ij|Q*SZ`KJ$UWQO4xv!aH-Igely`1bvNt+rVrtELx$s#@%y$YI~MJdBON}J5y2E(Yyw)>Wb%ZnV84FV1B|U4Gm{97pmWs;-d{b_%5?>Orks;Rh7A#RB=H zj_F=A(AWU~K|~{95oqNDoQS@e8jtArwY(p6S`@CvhN)R#*W|9xk-X6dHM~^12}33M z5G~T_4!|rj>WQKlWioSz^kFbA#NSEAwO$nh(B}OrX7Q7#Dxj%4#3;gI%f+~dml5mh zi-}MfB?*&F!gPrgiPiil(2KUeH5w6>S!ntyIX-5TYfl2k@`I@+3qOC0Qb>k&Dk#RqGgg<(opjdo%Q)8-Y5?f)2C<#pIEh!;l0Ye6RUf#6s}Nd2_Cbyk2Nf^bfk9WKxN9@WvzD zr2eL82x)K6n$~cVSFBjJmLR|FXgbH-44`<{hVnn+GMt3UM-yIAlY)H0`^KY=z_!a! z4+Q+hs*R-25O}b4FOD7R04dtBVo^^VqAvL^VnZxxc5V3a^ZFIAR{R=k5fk+I*ta`y zy>C{8V!1g2Uz8Zr!D{Lx%+~Tgrh}cupV8N9aatb_y=A0^Ob)eUr8fbS`umBU4An>@ zM-p;kY%K1XeKssWe<=VVG!J-=^q`_CjLzegPw?+KP}FfkHn(*V* z%608OXRJ~M<1I&xB>pagd@i= z)v)Db?cR+ukdnYGjt*tnPSFw!%eJ8dj{wDQrg0(s2+ZiW6~KIxoG92UwDZ;o|L2VK z-WxgbPS6qS^ujruOh|LG|C`nzpiMV%z?1mq!~S9FFDr7LZ1bsvSQFuAXIDnv;&wOy zeoB)`DI&zMSX#Sn8bV+mXHXNQF;P zv$Jz&eLJ_B?}(`x_Zcj1l1$JDzJRhKLYM;H=i3NRrRWrMfy5s+5sIgz!Y4)LmanCF zl))=r(Nu$wQzuFubM!4Kiy^Q8ayX$&z!zgh6i|maVH_p)$O{h@d8&wwEMH4Je+1!3 zNrJ`coRwt-oAIz_9X4v{FZCxNoY=gAsAD23U3X1#%fQ`RLyJ>YYT#N9SeDpgQ`C5} z71noX(fV}MOfvAt>Z7z!l9`jcFcr-Y{*HO>?zAWvNY^JMq!_{42G?kKzg3k<8r8IG zL?-7=t0P$$8@Y5z<0sNmB*@pcKv`Dzp;)#xZ7*Q9x8kH_6gU6~WwIc5t62>#7&i!^ z^x(HU@EiDw56p@u_C0O|bz|VxnVP@)=S9300~6`5l2O(bxZeWJOq!y7^EtJ| z|D1e#^7hHD<8o8S8GL|9qHkBM7STiM!?WM8*;KHDs1oargju|YVGjdI6lftYYRe0i?-4AD zgs4^Igg0ZE>(->MQWMro2|hrUCX>EKCX^oj7kl~tbk|>LG$9%8*|+~wJCp~qkVcRV z0M1;dF@x%n$0+spu2GY7Td?ZrOp3%)fBWgV;#HR$atM?;N;8K2r^ryk_O24gh$#G3 z&cpu_ZVQ$|j)H`3*qRUiK3(5>61p(ZeBjT(7H7_olnxx&yVn!tPyR@E95Ta0={2KC z$cX=)kcH0Sj+>7XjpLWg%~<_L1}0!aoSKj$0%jl5bF)WA1_V-MG|2C~dZ?K;RWDRN z{23LS2Nv(PC)LrTXXb$V5}0tHfEo_)tYiMx*?QU%Iy2l%5yBjJ{IgvJf8c5&)8&0;4@Q^8P6jo95ZiMYdB8fIyIGlohyIS|6^(M5?(z5}k~cgs zJH*ICOSjfdBlN3=qLKe0(2O;1pp8fyW6N#4Fz7G+5iWbU5peas8oRn&t!#6DY&PVH z;kI}o`7I{cc&KOU%A6WK&hbh&uAq*fG5zgP)Qv2dHo|m7To?&pM=x1cq+Pe!1Ck9| z)_Vgx#rUFw-Jq|QwP=qxm0`&b3^GCydSd_B`tXcG5hD#vQbajk*>fZTNfp~dAz3Q> z5Eda|6W$&Mhc*kBZbB<0Q&n}%x)p4X9Nypno|F&ptUxz9*2KQa`RpJ5=B1%I@HNk* zbl`YTe8e~VvmF`%-L9fZVSoxBSu~ZyKo%u4fq7ewm3|aB11v*^R7z-O^H+yJ$Ps%a zivq^spw-O{Yo<6fM=o8%;68BzVG7S9(!|FYI1rH=;(`@R-%*8 zC~k)2d)n5=%gCw2Ds`H}%h~Z$&mOL)N+v9!g|9czMx2VrM4!%-@5cUg5C-KGFwlTN zI5~g=XTZ9cewK#-H*m{ETcO{-0V!sHhw>%@kK7FAKK%ctq4vhX-*6wW?#7*9pkOjO zTFHchoI#1;6blN5PsvT@-X0bXMJ5!=8WqH_G)l=0g-)+NNIlI`7t{et(@mXSwP63= zv-ctCfE}Y!pJ(TY5g_q41T;2mZ0Ly+kpw0ebuwQlg{j<#$V*;8j%mm4jfbRB583uEV(Y4Z!!5ad%w|#PY8lx4Hrh<426YRC)RUKc|ON!DrpOEPc z_SF<))vO&zZe+@052<-lbbx8orcq>+I7;9g^g>!A+nVSl7BoqW*++0D!X0Q4(Am(w z1`)nlh-@yl>b2x~4%Pfwh|aLb)CPwpE2a5bV}$~@$SpG74IQR+MoBv;MHG!sButwr zJ$Ud;H4Bg_!fVW~7*Q4^6?_>})}Qq^ebrQTn4OJ^l^A(*0r;oqz&u@~KiE-vVg$N$ z=*Bzrzv7n17`_M^R=Y&{{Ud}f-7wAnvUnCXq?))S@+d(^#Fj_{V*{>toc=IO6r|K{Q~mA`^*iC*Jtb~G)gdp4v_5xfJ{ zUohQADoB-D{^4HA!F+Av@ICjC7W7;tY2a%v^M>Mw9;BB+(FUZ#L2WFgZFxSBlU+(e z8}avV;StaUP)48zACn*kryt=kzIFEwxNwQjn(f3i&!gb4+FMpcyh380) zIz)VqF=<%8U}9=7Cz)5%D2$FlosCioCzOAXB2szq!{GBm0%wK5Q{bH6lG8+ro9|CJ zi8qOy_)Rely)~`mhh#JoqhLU(&fB-2NG(~hR`Cq)MC&3Gz(zw)p=v5bK_P@e%Bq$> zz+w=xXirCQA*3$gPHJiW2ML3o4+SK`J)8&Bdu|-<2aQC?on+myD>v*IdfGh&w^|MY z$HZp61anz$T5nxyfRr5$N~w=;&uof;AO4XXC^h!kMDVsza0XW$3eQH8DM*=M@6ocI zZ8-DMg*~##8`rXeNKEluSWJl{A6@+cRYYA~!LtunHE#a(pS2F%+gRZfB!4py=rMw4 z(lw5XRbLO!BuFbqh^kc`?v`Wbu5Ng)C66Jk%@}y5Yn|%9e`RB@%*SyKGZiIzsFs zv3YRGlOgyC-~tJALsP`N(Cj%J3WX*wlFE*~F1^9W$3|Xz?Ks$O8(7917xt#qk1t>P z1~x`Bwkv056FLV|iIX1}s#KDrQdKZwQv08zS1voA$Bb-MX= zH>ZNBJ<(WJ0}6z$C>q7X1PGH|5QapunTAZ^~++t z0|#i_F}gY{7Z?aFtDREpAF6RAAQD@+_YJg1UPoBmgY#BXL1OVAJ9^~2SI z)tDQ=8`m~CGTH( z_(w2X%U1QQM7zX$HyYNq;1k%`Sn7p6pkU51tu+=C*U5r1z6*zoWM;0_wNXe_rXkoY zYkHgvRkenAK9TeQHukuN)Ptr%O;+PXp`AxjV>7^b^(`K}Q?u1w2l4nN3s?(;nSX;d zTPG)ttI1Tx{Tn4P*&d}NVlN5j6n`)Dh1QV>)+y@v?dCo=((jG8{J86v6b?I~HK>NI zv6dE>%|-~*ci+DGU^}C+>g8JER$3CPS=7eF;9CO^UR~W zQQd_6MO`u3*`!GU8SvKfm2xq${t3m+QTH&C%|cjXUFvZ`3Pjy_X4I#%%}1jMOz0kNnvhX)3b4|7@u zuRL1f6oM4_*^XBu!P=K+mS;8lIJO4VDdar zz@rRAg-|%;!ardE2{rYlcinH@;t~2XM7`qt82}b+S<1n?e>&bcQB4w9c79%3(ExP} z2Pkd0x$@ox?@gT9Qc0O&LU~p$QSV_Nm zGZIeByeAu4kPy3#T(GlARBf5eQ^TW7!p3cD>oi>xBH zofExTY>AjX%ML$eQ<*tC0DW*fRrk#_l~_J@-c9m z;Mm89F1+?)>+{x>(<~w7mLaFeF4KadD}Lae-?X9U z6X0mB!jyAT{-(0%HY{^b)%iK}kgAD-D3BeJ2N57-AR3O;W%?5DpTQXD8KUAg|~?q6EB7lXWj5HB_mIxnYc|_)y>z^tiPRt z5)Tv|W_{h|FpqMATV@CL7f zRfvFG^0XvEvt{8_0o_6SaQCJgd(hItt+-YqdYkKwCbto;qmVeB%-H5;Q}}}(4_d6VH~Q$aFf!YG9#f(9v|!NSGH9` zoaehcDTV-fX`B?-2y9NhVXjF^4`R%d+a8wa`Srh-eldle;Bkc zFf|(REb~WgNeEMC8^wbCpBgV-M`!AHwQCg*ZAgY6h+|n{6}sp+)+iu84AWs~g2oIO z-}Spe2I&t71q-WHG*FTczPx*c^zhV0Tw7{zBfWpZwU4w_4O zdg`Z#A6xL=nMaRY0Rk0M$4^b|;iLZ<^f3_9p4prY&?DHA2$#&5b4V8$i!jb8nN=_+ zE1x*3#7-2hov#L;!3-Tb`l|JHfO4U)&0jbaFmswyfp%a5iv!YUQm4SW&qox>A0Gq&Y=^$}!vnK)Pl_%_GKh50?#1ss% zlaJZO)+|W~PwZ6Og(X&-N5%nry=#@4rKzS2cEX4f+v%pdM^mNLsM2DUj~bjBeU6ic zgnKR!rLGH239cvMg_XIQsiA7Bu)>y>QR3H15sQ!RJzD}RLQIw~W-mWkTHA~lluQ$g z5+=iM$AO7Q#-3S!StlkoZe%C=HEdCU8)d505^XLG#cJB)Y9@Nx-DKls*(J&-8I}G3)IuK*yFiI7}AFpb4&|C^_1H zxD8@F{LOei%x25(GB#4e5eSo97))SbvaS_#%J$7B8QvpDjRUi7v*X5bEsJX4ZN-X- zEtAJb35kp7j3cPN6{KNuBf`d#K0h@Vt3)Z#IJ0f zwp8e(Dh500+ip+MHO5LJ;fdcGmbB4kt-eJFipNo2LQpZw*S7k4+LQvQF{suIJ^+zktd*~Dx_Tw?+lp$KRSq9~}-0XbtdKrbtrLdz6T#`&Wa+v7j!htz+c zp*U@xpx7R@2%YR!@`>C^sS+L{r6DLApUaU<2S!$pGn!$1tljPD?Rxw=McmsOnGhDf z*~ZqeVTu#|U4d2`mq|hanTh#m@FTuoXPFbWqiJz|TZ=9IaI z3Y6L#j>utvC@-H~wdP7QQ|FfrN!5B8#oqaL91J7vKmz%0l_RuLqica*HavQ z@#o)8q9g>&Ur)(R>YrmTgBX|fbfN=;plnYpbK4Gjc1hIX6BP+sPPy}$8Mn33n4L!2lO1BXEUD< zNKA4&son)$O{(W&lqQQtWzK!xGHaNR!qeC{yl=6U;s^|7d;mm^kBFlXmO=*u;UcKv zog{V>IH8*WYz}CLpcIh7KX-5dtLVnrHMKd4vS7J+8649yZ(uGciNb-02dFnpy_}L;#?=l+X{+ytinK3Zgos7@Qo%1eCm zj8BMgY5sj^V4%Vj`M!z?w9*YU_j=VzeKGP8asu#CO6c5{CtiT^B8H{3dGUh(k*YSm zN6E8KY>W3r2$|uv0!H(}EZBC}Is1`(sLU@+$C~|_7>qoKN(ai6I$4-6AM=V>8NmnO zHgVA?2?h4a_8&=d4Y7U%>%M5Dx?XP}_8vAPc8jKzWs+;`usa>4fuW5-JTf;un+;f7 zbywo%-c(Tv)u&DD=0znxqO{KOA$A|O6JZsMg!)&WbTe;4L}57yBkVd`Bn6dc1jQ)T zJ8Z`gC$R#>4SJhK-L)xg6RKd#FT`f#4&^qU`pN~j^ZCL1SlEG{A|y0XeeD@GyWIFA zt*8b}T8_MIRwZdGZYe4`wiDPMj(9|zv@)c4u{Z^|(*`WB9d=b9L|7$reggtg$|6w1 z5IjzM2YCz9Rcy1Q?OvD^T}&gcMC2EaY(!ZYVy^G&Yj&|s%TY&#cx-OkZwHue@5;%E5>UI)aJ-AEo0^Bl!r4_HO&bFH(1TZMcmVu;=cr9v6{ zfvbTJ+k}NMA6LrGHkS7vzd#trWILd)Vhbi0u$cyl*ni={AT9Y|>X{>|C8yX=IUcA5 z9nKb2y9jk{90V_e>CsX-s7i=2n2YG~`OSB8!xY29-a=7EEnvp@0CE3*kG? zy?c6C00lWcpM7%DiLE0&O;uHB$=?u>aTKl!!Y(|UDXvP}u{@9%!&d7BqX2r*rD~H+ z3zJgdUP|mNa?i&iL~0Rk`Pgr^Z-2yGo?h4##XM1DmP3q`+**w|OH2~cS|fT49W|kz zRpUjEfrGSH;7aVV0HLF(toaiX?$zlnopx3jJd29azRAs>|; zG{({$X};*>C7fBP(-%@Kg*MWOd!T$T;j3HDhPxjKhI{|97X%xn?{w|$RDJlKAAEM4 zXY*bZj|!?`3>y=%e)p9akC&iqSnkG}(|K*OGS!#YgJ|%qmT~#PnE-Kgf0)gs!1Q41G0V&~Gk8oBZ`m^@G`aPJdTv-!s+~17 zZCZ4SLyeM?$XFS&TrJkhEhDsi$|6}6+tg)^@P3Dlyg61Yr1k^szX9pE;pG-OJ;|NO z7n2eqxF+unkORj))Xo(8m|1k~9WLI;AJ;$-yb3iWJp@l9tKVtUyu}X!faiyE;u0M) zHJtt9GY*zp7Lsj}rp`fXIMK{Q{tz5@Q*VYDNO_Efo8wt`9M}pohyG%G#FnS>cmzJ!KOFcyy%($K?!iC0LU`Z&>u#U!1)sczhZq>b3fQV&&#euk3+A z8sQpd+nlddYIQ>8OCtn4bAIb=#`?5(q!l||AYR#BH%*=%slEOhJ5xUdm3)wD>}E0r zIN^dAB#p_jhp%r(y&%W|%?Kb;XKP3wWQQxG(B z*QE_&7y~TCH||KqNS@kHp-=)-#LNS% zaQ3G%&GFmAP*VK*_}E)2VZn0T>%{Z;uc<(dOa+w5lFTV|h1NFp({ppm(s-?PNFD_3 zFWu>bGr-aYi;!-j5Jo1)s3RAAA>gd8GKo_Ps}X&QkLQp%jRoi3G9cF1pC6#vWQ>hN z!!(2>MEE|3_`VabW$h6~V0K!JxPgc>R28;RP$Fc#AIKU_T~scEDy+xyJ3jm0%z$aU#$^CYuvr9nj^AG0+;NR$Z>~2I<;hqke#ULJ=M^pjm4vU;k;||x zh>WPt19-8AFOoIlJEA?3m+a(IC=_xeD%x0xCw$wu=Z*9qoL~Mtu5zy#cHFzD6O36@ zfx%d}Q99fh7=#}oI;tg@mQKMNKBcvjTma~9R7{OF^fiu5zlN>YaGL{gGyX0menDu7 zESOSCkQf>)T7v2out{723HO{tX0t*{ysK6GrUGZQ#74ZVTF5?5%R)V+HzMr?yB31u z-{33!hZMb_r`G%*e4O}CfU|RtUv5W1sa;k}AUH!ADo7QmS+EyN-wj3W9U(wCE2c2_ z0b&PmYzL}h>^ATh{D&etUaK{t$N)68T3Cxc3;3nXiEyZSdNmwdB;o`Zx={5#;cvK? z;A(1|Wi+AK5DrGWEl%vx8zwzV{w{mhkS3?}#|&Tyz3?Vz!Ni8-_${kJ)z8<&H*K8i zxb}=U=7y<>tT3*-1b1aJdiheiCU(@D0v&!BwnthkoNYL@fa|?Ey(c1g(sDXRls2NMm6BzFAFLH(^cP?Pduw2( zebY-*L8S+sY(m(I!z)AN=O9tUAu*be=2Y3D3Son z+ajJDOHPF$0z32i&-B{_;jX{fynS-Jp)7x0oxGu#KGk#YCT|1+nVe*{qBw5~)y<6e zJ@#S4xktmy12lj&dkw2-Nwu9%0Bhn+90;8X&BZaRYGMF2QJOf#ptR)$yBC2JWU90p zII~1M3}EP`WU)e@D_H!x-7j}H&<&glb{kOdm`48U7w{YqL56Qo81sfG-Ag*rJQ`9_ zw%CKOCjp)F1Wgps4Xh50oMwM@63XOj>VdRCX?(vTVxa+8Vz%0M2j2^ZF;mMx6HUzp zJe&&OR|s^?y08MWf+Tvg^vxgf_4(R1#s))V`~+EG9x}c$h8h_*XO6Il!R-1!KfQWx zga8)$BkxXpH|i%U7>WcZl0Roq{`D1&Yys+qIp zbrGl0JMWILa0bn!N9iWtarX?ZWjEi^$=}!)fQ9^tvF!LX~i#W)&dWVfYmFUlNNQixMo<(8FjPo|bdBP7O}MwS7A+! zABj7OT!{66CAWK!B93zu&o^SBP{kmO;|{zR$<~-1mL1bDis)Ga42IZ5*C?Fg0$}s`lAb z=Q%_0F6DuC9S03>tT zPh&<*{!GrKdn)_xgGc%ZH=rxP>1`Njpk0svB3%3iM_8Vrzv#e_A_ZK)ZdGg zUG9j!V!dBy_c@LnWa~$XpV2g*K0aNnW6g#OmCWDMmp=OvcP8!xyoAf;E^2@;w6nO# zMmCrIJV+PD9sv1|q#h}iuLo;xL;l)YTKJpb?j}tYPv5RDw4*L zy36b6QH0yA>ij7dqlR;;aV(fnzZ0fDp}3;w+U!O|fKohThN zG=6R&mq1EY0GjrEs7678#F%d~^IJT|xQcu3p(OKg%?7$Wcfi9HBj@D7#d9uXK)9$O zl%JuBZXZ3ate%7@@GBX3iCuGYRUdkYMprrQ6*Yh;s{-_5i7-4Y1O&wCcuWopO%x9p%W@r|t=LGXhw86hdxW2Y}$Bm?NJf5@Yu-<`MG;1O&$W>o8w{kP^fmPNkb2j%F{R{)=BE^#*C*jy{Y^ z19ukwON)~p+M?BE1ct3;AP1G(%kB6Y@hh#Y{Yv0G;37u)UV|0=E_?vh7+R*6Bf&y; zyzo7zPErSgPLu@~QH%yom>mNfpRq6OUJb^^4}n&zfQ`keo|NN7H9NDTmdSe|L~)+s zQLs|iMMv&Slo~CegoRT>gz8waq=#>$st6B>zqb#jjEl%@ViVdhRI4M@zVkU?>#iF1 z0UNTYQ#8T7mfKcA?d?T;Cz~do;W+LevkA;pylsqBZ#X{1%T-+_ij5HNt;vn@4pJ{O zjDzGLF*qs@x&q7rRhUF#HPD(wE63*(PgC!i3V-aXs)CHB(vJidv7aIN5@?&R!S^;E=*Lse;YSjl?&KeMNK`;PG3CmjCW2TqjW=>mM@^T%4$l zHwE08^?rUAI42J%R3Zmb0R%q2D$>UCkYd(=nkJrtXVu^vgw5nwMWT4b%fuB+@7)q3 z1{rAi%saM__jJD}z6Y=Mv`fj+x_#0Mlh+&%)c#?XZKJ+eP{w zup{(|0ynhnR%k~`fuX8>5A!W+((SV$+LVMs*uoPFS0WkIFd(?-!=*JT987>@g>tj5 zR`EuyNW%YH33;<|U>Y$JWG|!u7D-uYYsQ9KN)$Xa)J<9@zt?SNdtl>sSsFJ(@(>xE zID4T7L1pmj8CsyQxa(B@w4GmA{Ie1Ieqw%fKm0;zyPh2)A%V7u{!;{ydXIKQ9KGgB z@6q?%FX+LInJHO_;vb2^Yki;`aS6R`N}>8uOkkgFK1m-Tw4)EPlM#dJJqp$!1$Mk& zZGuOP{flrwo9?vMoY9!C*%z)TpLG947@})V(GfO%6h;rVe=uHWo-vS*a>R;B+?oE7 zAKdK)lR95ybJC)im)VtI57#q%p)G7rG0@z$CD)zxV7?kQH1C4OOe0*_JzTF0Ik*Rj-_Jc=CFvgsy zRb<&-YIbo2!eqva=mT}$L{1NU;y}i(+zue3Moox(>iT|v!8~$ch0o!lGVWpC11i3Y zgksqX)(Z-y8Ol_OnyfSGsS7T;VC-&ApC~4G4w|U?$~Y_p)<%(VXVW-}9l)C=3TGNM z8in9$)JfqEo#a#n=C1FnU;^q0 z`&5G^nX>aR{It_@*D5th*wOSq@baDO21H#j+5`ltilqgLYyed@Po_cTF&h zMyu>H1c+&@4u?fPPyz@b^{BwK{Xd?05GhadqF=@nW~mT|fAYS+XbTK3*GD@RHYyGn zb#O`=g8zfSdAx@%FQHnY5)5Mm&BM@I_Ri(UC@$Z0ymfrYo7dR46Vx0p zjaz3*AiU}?x$cL4Msb3wU@*x)LY5C$H71w_FZ#s*qmkafXTjWiKm zDJnJT0bBqv@o>vhkj?7yM2gcs^elVl@bPJDds>Cr=ypWpKJZGcQw-@O z26M-cHt8SG?1f)k^Nx&Z%K-T*2T>VvN+5yYG= z#K;DrVY_g2$6^2AU8Gt93+0f%cfhn}ANBmjFk5qz!11jr6x?s_rS(Di;cAGmch=Lo66rO<}*OsV~|k+yEI8 z-`PMWMWG;6NNP_oLBUQ^&s+(CRxlOo2j z+y!YYdGpJ^r92-9Momtnr1=gIlQJ3(P3Y;$r;Vtma~9w0mXs2zP-!_kn31N%OXrDJ zF1-AUN48hKUENqz`Syshu)q-Dy{Ov9sb|nf!X$#287L$p!{`QPiSO>vEm@>WxD;>y zCv=a$74*qm%^XrNlRwYYlwLU-j3!R;{`KM{IEjUGyjVLPCU+rZHP)d3TVNoF>Hc0w zZCHoIt;t_O8_OO7W_O9m)V;&E1?Z8SxMOpb-W%X_R9Rp^ApEE_E;G zPL+!qXrh7-lmoZ@_QQ9nlmCHh=eI-3F2?w1jB) zxFB4mxHg+h;gp-@LJ^rDYBX44J3W(xzD61Z(mfk`Bv5m%lXE!L_o`6|uEJ4KU!m@J?pyh1 z@1rjJ(#FblEwWgcU5FZ;>142d+{w0%^|ail^=zB;LdYoxr(Z4)UjRRVNLF>+T|6{F zDhLyK42{pJ{d5T%PAJMZz(OK)Hh~c)Cp1IhYYcKJYqUL8(3vug6P8v~(+Mv1gh4=1}bY#!YlZsP4|m&HXD+JF%+qFuG9P_iGX^P+w@banwB?@uRH*d zp~z;9Lbsf&%kJS2L_nN~R?o0D?~ zi*7XECrIabXu8fWBBB|$noiGrgEmkTSW_OCW12;lLIjen2W zm|}k!Y=JZb%?(_goXjowv&vUiKk#m2Mi9 zho55T?}(LW0iXD|XPo^RQIm)E5PW|J1kF4`-;J*Y6pzpZ+n4!PRb3xVPct9m%dRyU z$jAL2ik+cfugYjv>=1h~;H>*lDT7mTWX0t5#(HVcABg#*(_I`eHIuEn*3l2W9mQ9# zvBXnND{rAI@m zGG4NAATN+J-e%GAbURvjs$@#(=YLf-W^kKzjggF!?kMsEH#oZC;qw=8LzN;TUzlza zF+zVwq?;~5m_Ps?ks_T{Gn=yGqB&govu8tbbm(bgWSBJ)f|Yrb21N;ru=XmbajCst z(PbDMa-~oYUsMlS@~ZjGhrCwk05|^$LM+Rq+|>jo2~MPbpgIUoILM7$bNzNQP~1uA zi_C{*1jL*8UfLNr9Dj;y)x*YCaT}LaSLlT64Bv{)is4G_BiOCLNnz zsfp+C`gx+N@sU>b!D&SWeC7;iH4dQZX4(=1Sr4h#YuzUSG(j(d9i0kYYOX_`_{@m{ z5ycV$F+J%Le$x0DC!U~5fk(r%h7sBdA*1_Ek67tVFf`;I$bjO8HuESUz`@ZBZiYQ; zn;h$AO~*JOTh3Q;Fr(U=MXf$(q*m2MLwUW}H(g9{GyJ8>$lpYlkLicCv4$^YcB~PV z=1VB=LCU0JMGD!aVUlW^#y>0Cp}hy24DyZZ5kL;GgJ6mN9^eonSmDTIYrgfK!*Zc` zTkxNbY+Lh5f$mmH|ABP^-SCJb0HtD`4T(T-AWjc_7%!(*7+)qI=*THh=>0O4%czq3p<(+xd~UG9pQ zLL7`>;3tP&v96^IY;~{?@-$gq@mnI@`_9+0vD#6~{ne$<3a}C0Ox3RjaA?+yZNnR8 zGw6QE*m7q&Y}4N}ZHsZ`kc}X`u3a@Bz1f-aUjy#4Y1OMjh zK`@ znE6eL>m<2uveWaZTYSC=%L+nitOQ*M0t(hpQ3sjs6`)eje?YoYVSzjn%g;i9oF&&f z4EYWWIf(7&caCnH=uWbC-d207NPOX?F#I&3@CLz6284DEcw|zlsL=ywP;5g@u5@>I&eldo_siuIe>e9e+Piv6?9_s@4zoPn>+@} z5L7}iakyE}2eJ6?JhhLru9@!4VTZ9w*CCzJ!UMsRdcqV zRD}r-NU}WxG|FRpnD~*?qYDBGC>p6@o6hLd%a(a!w0Qh!lv@ps3a1 z9wgw->K3pxXd$agO5b+ZO*=DiBQ6ceBwFGSIe@*`sYrmO9AKyuU~w!@{Hcy_Cv{6# z=KeKnpD)cK8$kQe`4cBTKvB_4&qWq;WS1mmAJD9s1=|lt3&o?QAR8zAGtG#SBQJ5R z>ff-1KTC>l+t81wN$*De=Ih21h-FYfe+-k|HndlLL-JBnCK1f>zCT`@A}gzp;76kg z_0HL8A~;1pYV3?`vt8Fz^%5S0>d=a{?T>9HXF=5z?#X zhU031fsJf_np6oS`6QjasK&xoLFvRM$}4l*amVXILCb+Tx&>}9Ih<2=?!hZ59Tp}> z48h}+pFC`TAeGtct%`C0iUWWM`wjp@xYF9c>*yLpV}%;t_RH!x5mzWAleD-P0Rc%K zCRa>MOp%5m()1=Nfs7r}>@hc~tBq&|8IK+{Gh;678Zw+N;+|oX3;`;skZ~BS{&ve7 zN;DYoZDS<^d+V@shMc$=uDOOHb&_}38etm{M${cE;>`3Kd{t_t4h^E8-~eM$Kh)E>be>#2e@m zzAy7N3fIFE>r&9P#BNj8qY_5>KNm@i_q(WK9FA6kxfny{o5c9Ox+ZI5;7?m6$>aLL_9nIcd z^2`ht7KvvUIbO_gi6TKR`Xe-hBZ7=<|LF42@bS~92+F5igy&=) zK{Vfu+TUH*KLEX0itKppgAta>+uB)nDtM9K`Mqv;ShdF=r?b;kpG*ZYoBg&&kUH5v ze_hXhGQ$V1S{p`mL%I~CNf=b?;A#A@nu%n{i&{eAky&7AjATBs?*ll#fQ+Ji8qv4kRc>w0plkQ8sfV|x>m0A#5) zGkhjD+kjk3XIe_KJ4zNR1ApAzNYd4TMN|nUp0;?;V}{AN18jd>d|3K0_5T#BoH3(b zYKy2B{TIf*Ff*WFh&`lzo!Q;;Nw<)z3Vn`*O661uA*i=0xtzx!^Dpocpye7pEL3Zw zQKBX2)v88%M`hv}UY@}y>C)1r1lc08nmJnVXS3K9@8ixxodsZi2a}|?&D?lkz~6y4 zDZ~|Jw)BnIwJd>>hZIgse1_qO%VJK_N;pQ!LId^ja8{`jWqKj5OUl(HG0@D*RMEu! zh>bg1&!UrSKohJR!am3@qdwjSGwj!RBM0_!DjX#ikS-CKm2PJOh=}wObkd_V;6lC3 zZ(se>c?PDI9SFyOt=PpD%aXOsrj~YROl4G{ZI3)&qv*(GxK?~vmA>w^Zo*XtN-Uc^c$ zO8lUiEK)Fm4#FAJ$BXWS2_bo79pqvVu8FNBEK^>&88!QOe-=g{cN?AOr4W7!TaZnV z>TGLFZ;2)i0Tfd5!%Z^fPew;avQfA~Y`|_3ipD~xO}p#HRdz^%FUJn(E+cn9nL^pa zF3K~b%(TFDWP{BeA;HTOa7qy?xz61@O>0Q-Ry-l=y54#d3r@8W^^OT~=@!PGMQj2;@NQ1IJ6PT-=k8OEKX zF-svsF1?4E$8x;b^?#LP5EU_WXg6~8H&$XLc!2Ed>#t%KB%IDC2;uIJcP|R%k!p$V zL8q$11!)dSm8}<})xz+w?T=%s@VrVoqsA;hcjgY0h;-v{s;x2rCc@~no%hL88zI$o zBO9@sL*BEF04<_UGPt}c#mG(L;yLYaWP9@_2m;)0L$Uq;uEzuDNXt=`bkQLS0XceS z+Hs~U2?tshmS?>Yym1?1XPpc_>}D4Is5?q8AlyRpEP87rBdiqd(GKWds80_AZ`)L(X*5xI9rQ z$Oq^aBF_s2MKXQcV$$QlIHiAK`c>2~0IaNza-)*GkFAPO{S!D5(IP2$s{1zT1vo|y z6I&y53fRApAJd?qi~B5612OVMdG5(jbu7z$e=PRf@Wnk zfLMGK$sglF%(jrP;qHh}f=`PC5AK>Bbi&_$>VB?JCcqN2M&4;lY5`>}ODE7wfh7BN zThU3{%ECqyl!Qn(wXOZlnp?_+jVDdx*1{;=7`E?|_e_!^QwBJb1_0m+a*2(FP4b>Q ziBDM9yzPooE_uw5nNE?3`PwK{rtthJPlGTQIKs<~Q+Ed8?Z|HS5Gj5D)RUu<4cJIE8N-Jk=DjgxJOW>Y3L8a#JV`6KX z%{^ebhx;QU(QL9n4ZueZg4$QYIKeZp4-&`LX@MjsYoH&;o+yw@54f$Djv3;Ar}J@2 z5jP+DQ+Bc}Dcenm4V{Q|6haNZ*sUc3DKP8ozv~I)r+foI%mp=haW(jh_#xp_$hqRo zMkD}!7n+7Txkv6s34SW-Aii~ zAaFX4#QOAj&$=e{>yWkiGaXuVpR36z9lr4ly*hAYcVwJdmVC@nz|IqhMFN-KNZqU0qck_Z)8Z>QEJOBB0x2?jiV#Jz;r#|ZjgbW z4J~j2Njn4$8=@u#ID%*7#!D`X5|=$COPdRgY*?%~`T&>=Evxnz#aaMuXI|~2iU=0D z9T-LN$kd(>0c;44meH^Y>SA!DVVD zFtmX22LR(;GbgZSE(F_Gybf)G;1{lu2Hvs{h-39TbM~SLpBNg0Qg=1&<>DOtV1gn( z|Lnnf2jExSZJw{@(F9?`IED-1**4)v+i#JPXBIN{k=g5E2%Dl*rr4Sg%mc(RfiV}* zm2zsR6PgWhNh1JJmm!|+?R<$DLCSYKT>avdgj|g*@}(XIRAL-3b_koe4(ny;UC@X- zMFxCpIMse6`!f55$evsTvzy3C6ob>}hn$Q>U^g&wu`#<;yQ&%tUIlB;LdJ?3^ttbt z*^su@2p+UOiq~O@3Fg-)Hbl$q-O(+7t;~0v8{a^knT=VaUu#FP?QA^ z=U8kEl8~Y2?F(46G*y~0qsQfzD`%}zuI_>)%ff~Gt^x>4SCJ|hRkj!uR?>AIEz6#} zA7vM1uQPlOHVK`di9{?`{TUuKZFQHEofsSv>?+7$D@O$ki-0$D48Kk!NIr(JD*>Ox zp+Hpv?;TTl?FchYWaKO49n&V-#<-D~e8p*;o4JiQ>6FP1!nQ4!M#vTyF2Ff>+~Wk> zHo*j3_M}B|m>Y1a^XqnZM#lcincPGq5=H1?Q+cKK;~IlyQUJ2HyfmQpbZ}N`l@H++3(`Jpa}& zxTUub5Vqn#Xq30CU2DNS?`L*=Y8I5rf14me+#=s5y>9BN4USp89hu3!OQn%0XiTfg z1#ew=Wcym+9Lkf0K$7wNLonBRJw<_c@0x-~F~9s><;iCs;S=6O3g85DGi%Uh(8rzo zjQ8HnsqmucX%1#1_I20hXvb=>2}W780NCUnI>_}bnl~2~V?g>AN#^pi_%kRA ztj0<1rA0)d!Da9(oZ?nRP1&!ZX5BF<9BzkO)){svwXaQOIlQ|cf+7GP$9wA8+Y@;& z$IBv43{jLpk=}rOcf&%)c#+C?dTgobjoSxb6zNSR+09$Rr^7|%aKSMLSszC?DhwzS z*x}!@r{K!5cLiKwFb0Awo=9kkdygnaa7xJbNZT;~oZS`A$dXsBCrxEv(;0h%Er;St z@M`TQn7MOY8j>0#HSqh?cC!{>3`zV&S`ix(a0UIPq&;8-gz_T}t$#9sxmx8Q7aTI}krBGfR!uLcK-O`Uno54ywhvLQI)vM(}zCiASKF zuLBZIh6#KHZBFbBwDDJ;M%3gG3WjB5b8`-u7RU)_$FX!W!$gIIYR-dEcxl2psCPIY zcToF8!W#SmMM`!RnfpX<9wPSjm1R%S2tbR=R?gT_;)ikI2A?UnjtZpL>#r+gZed~s z93o%oK>#Wa=(_}i>a9#zFfvlx(8GMBWz@buuAJM~-#x!z$WIIDne<`dPx&l<_Bus( zO;f7l{>si2e<$J=Wnir_ROQ_)BBNwHcjyj6MD>IkfI)UJBVJ^IlN=2~u6I~P_le(s z*XN$dBPbM7rZGk=$w5>p_~TLl4w1!hx}GB)oIoeeIaL+7TZ#bTA1EzIe!+ z;s6CByCGFUp15E^2bH;prXV=mh(q4-FMQa#Ml+cXQa_}Qhu}D=0-K?=OFCVI656$! zSq;En^=({}WjMah-^6UdULsfiXF~|0-I7v7Zv=59gC3`am*hClSvM)Lq%ArI&c`WImc$(8|R$S~YXf_3HF){4=}QQ^&gLdlLk zl}HLqoIfYR;%>{x@8~Hx^&wSozeVQJ!PTlnKsPjHe0;kuJOxxp`{6_tOYdDr8Q@6* zFql!QtI%~C%Huz1beDpe{QF&uND$SQikvd5LV%G{O4 zSN6QtI8{gQrCA}JJ$n`@L+L&Exz9;Gso;S$d8!eR!{y7?aBu&0hH}ctj4X^JWJ<%B z;R^5A+nA*z35Mi%Ud})3^l%dPi0=W2@$o!6#4U6D^z5NaoH;h1M0Lr0i#6wRGn3D9 zxmr}jZb)4-!i4RGMXcbpO>E9CtgC_sp7?QcAIY$wf@~~sh}ia9!lNiOF z?)dadnxnXc4dCGN3FkE!y-@8IxfitTCJ&ia6NtgJMI4$>n0q5@0m0xWt&ktf4j)qj zmRLdcAuH)MQ9LVpO;hk~?_LrBR&TzEr1i}6?Rdy>t8AX^u1fLUZE$l5En9KRtu7VO zQ&j6d*IN42{-7>O`ao7e%P0iFzDd+Xt2TrEB~^#e z+G9k<oYv3|Y_c(^L?j`MW&3{88emk`^}4Epa;wg*gjgu$iTS*s z7j$xS<@WFHyJRh183lt;aCIx&FHy>myfFYE)ncGpR9Rwr9}4!FF)U8=1z6^!6q*=W z1i}(dnF>jEKRpg&R&+*v7%KipsSR7{NNgYrLw80vQ2sF?SVS%9?Eab0u zZBGj#BdM=PAhag|wu5J)Z3Sv-5DF8J^RJ^m9*LjrI!NnQiG>zW$BCVJ=J3$2kDz6oS zA%ytOWuICYf0WR&k;YCU?q)e~u1)%aIs{m9Ua&qmcn!E;06J@bQJIbyk3;*^C_Kz_ z8#K8QLPD+sKx&|q97HMrJCY0-^@fZ+dSYg-1m3cHdaM2tNlzv%wNjAf7-;*ImCv;7 z63?VuXNBCaC?y0%C~GVSRNFLe3VIYabO8Ngh?vLpwkG^S%6IHDW)!6b1BTy5wlv|A;CBBP+Ke0JY9bV*ky?^-at8C>y2R9XAu#?vKTK{tR z+_W1t)6kdd7Pk2YGhNIcW>!k;lye7~)&NiXl89p(Sg7w5J*I8~Y@*R$Aai6$DeBF; z`Nj1UwQZk$X+yMB%xqS^J%K-lv9ki9w*4OnCiSLL=2b|t*v}C>^IsJ3e@FI^epIQ>JQ+>@Q&HZo*8sC zIhlL6v6osYh-N+{z%oVLC9q50##^e6pjH&DyCOgd)`+XVLv7^mA>*aGcYo!__vO1R zsWJgkeKBxPgu=A_+$sGP=Ipt;WDU&J)c&&#X%sm2@*Rj{&sTF_ll!UAyJW{3J$3Z8 zdMzbA-@zdXDCLPLhc185cYl-6OUQoG8xpEY=r9}ppjc+3t#q(i0UuJ?`9$zw*_l56 z>`TLLCw8!t({MP5*~FF%XlHYv)lNeo6}=2a=AmELF#;u}+M{Hn=6YCV zi{|~uPtTS8mH8d)!&Y$Pf{cUk3%3PK8+?Q@W{PQRTI$lBbW-%-y7dIO7Vhz#1oHVQ z?lEm>Zto$S!?V)A3SaaBp(PFh>A#H+lL;wF3v#KY4UR14z`hRd*V`v@M&E><0c;PBSK0PRDgCBAEjK0# zz$EDp;fxw&W2>WOByQi5bvVqRLOlK2$?)Tn79ho(xXt_*ktes%M3qs%&S$OaEvhgO zHtxU?P6fYwA#;g%6BW#R(Vje{gRJ|c|4n1b$R|oi-)6tQ=3OC?W&bA*rk!*!VUwY< zh>Zw4u@Z*P*gm@wx&(&O&B8{X1cWdu%MjX{4P)yJ|?a4iW;s+_=7x68;(L zMCpXzsNTl&yc?QBrlDdNyYw*i3M>tyLBueqXT`R3QW4(G7;kt0rmxH=a%{Jov(3p% z$e-w|T+`N!c$Z5Rh+|GLsCuTzp1TTb{|8BtTNaJ4pm}tpS>tlzMxTIEibQW7p2&%h zP{G#%&;goO0Fw9OVd6?w$ZTAoT1;S0i-EzYH;tF1UaLjhh*-d4WseOOY~_Zkk_B_h za2SxhI93Z+TwU~^cAQ)Bcpskg8=U|d)>Ad3Z{RW74k81E=>ebrW3%1N2?#oYRELxOfrN;MxZ$%>wL#$U(rt{{@T?hxU`81-jG6ht zZXM!Szljo33{oUJRpAbuWqC)+kL8gp&r2j%zx;(tygGJ7go{Gly#;293I9HIH*&?1|7E z<}S2;Gm7o9fpmrh1?rZ3qt%t68KIqSKp25ogONdtc8RA5H?tsQ?AVNmJF*>K3@O`X z#@+jLcS%<=!7O-K36#3_huJPu`j=_Y?tJNj$rAM9Q*8g#DR@~_d^kgol3PY@VFV?@ zzK4GuwqKhU-g>hBnT<^D_P~cQ9kj$I=eKN&$`q1wjR%o#TxU)WZ`Mpt$za4Q?q6_WWCv74q!J^DUAh7Z4CQ$_6GB%n5Anv;4;w2f zj)Jasa&pPNOXq!l;KMTud2#BCyXZ3%@5=gdXZMh-e|A4-B5zrICRE|FED^KxenU6& ze(yr#zx^0}iZ`F-2uYZf7-`~*KOQ|G#6OQ46zCrQm;bwL3-6>%%at9CjSm3bqTG%6 z55+M-tAeG-f~UHv+5)9=W-`y9Jh0%cN2q*!U^zp;p9GQ8N!q7EyWUxdaAG0wLd)a zVCBCadZt42*MZJ%I*EjPtSdM?bW(ZfFli^IQ3wP?d_#3q=KfE*AD~RaI+}n9xIYW17Moa1XC)Fm>T@Rjp2Ui6j?dRe%dSO`B)T?}s5HSfHag9IkDu zYk*gvA^3n5&Vx&0#MrTn7Qe?Z^@dqGV(W~7N#ZZB}CRhffj;XB9 zK_s`NDRL?ZP1*bRO;tHZ4wV!)w;nP;WI0|3*xJqf%U7;xnD&gh%O*|ToU2PK(X*+; ztKtW0vh_Z+D}qBn8nShl0X}&K5tw4HWqZyVUr5C<;yJQyh}AES1PgeW$BY7bCDdZo zl~;hIYLe~RJTIqXJ+G5jEV#0kp~MgjyuAo1d+{#C)fIrtHO3W#s)!!Uw2 zIXqomTjroTt(xm(Hskiw?i+mfW!uOXO!6%HyBXHtW%OCO&14WHExD?p7dv825z*pl zV`J(Ws`RG&h`usuDyN5Frv4OQE5(%q_^t~nmM6{qlCyMQT0kSYms<(?& zq5O<>nNJ~(kayv7Q^+Im2ThS8;KSqhK75x77Ue6T_k@qpHxr;CzPK%K)xa<-S28m; zO--1b%122Xg9OCvC@ z*JD5_I&T@gfplKHik`VG(E#CxycLz4`NoXfP|vR$SFuD57jtWY?9lU0(9xWkIz)nd z>IGq#cbrWmcslnQQ4D2|IVPv#fFXU#Q8^hLMw+u+kZz%z)EHRL7$Svx%>V#4h9Hi4 zaE=J0Pb8vd)zM4^f5^n+Ad1+4czLTet%(|?`ERB*DPi8RLf%rJOZb3X3^_!iaS*P0 z>kucB*e=G3%zk;IiAZ}!sbnPif2kZkFblb8C_}$OJ-MkRE3PQ!@V{^Df+3;OKFziL z(3%Cks~3Ff-Nu~Js_ye6Q7sNUVP8lRgRsDcl}HMvc- z@-M-hCcPrSNOlBq9CpyXc}fK^9RtD=fr>*A#MWsvA=gwAR{t^B3{4)5UF4~%TOR0Fn>r+hZ1bJQeU^Jq4{sn!al$lG`nZsO9gZ({~TIX=E2Zyieora6sH(GG@aQ6!~bTzU(&TE4w>{& zU6kVE7Eb%@EU9Ym2_UBR@n{&}=*i^8DEha>wBsj^1XiX|lmnSa2p1x#jwLA)aZ$FD zwX?AIp%giC5)n7!rgT__Ttl%Pl=LyCSjMdpwJE{rM=95T>U|a}9#r@h4alb!uG@F^ z2(=a5wIHB)JaEWxqX!VB2Jm#c3`MG=0_WITmZ5!$Gurn$X;)=m0U8iFY*2l)A)$3j z!W!V(OEFT_yJ{Nn9EADp;k^Zw3iTCOB4~{;TpDHNuEcIH+0hmAE;K<|fMXtj`>~2XA*@fj8|j?%MLuz+ zELHOb0RUTmyfE^DA_oY5r8xs3YH`ElBf^pkCrct)M5KyRET%W9x^XP>@CBZR3cVyo$64tXkOOHiS|qhlyS*OXBPAx zteE%TY)9{cWH^th2%c3?9Zqb(s2=^r0UJ5~;un)*KY1$RP3gTN!&y));k}G!5liGR zDIn&oH{PLK(okowXV5VnGLKBMNstjb42q+YxFHV1&XQ+XAOo3KALZ-#u;)e&B2m?I zxH(XVdOIPdgtNudaPuvN53$gf;bYbWCdgU|@VI{KH+nDbo+Bd_m*)^xufI{3bIe zBa-_%Ogc8q^t>R3+y^OHsSJ28sz^2!Z39b+z%)^bM=cr7I!t@#d)}5UYau)MoJ4^P zKRZ1s%fzPyP)DYu0$S+chyHZaQhsKa83L;j1VD4F*rzNy0od&Els<^T4!k8u?y}KV zSp_Qmv(rUOIP`#No5l-e>*&<+r(v9Wn1;wrRbUkQW5lhRS_pK-?V@q?>FNn|unKM}4mcH#bL=i0Ob9 zvTrU)t32PiBfwRhVA8xuS+(b|H{1Kf4zTmnHzV5{DgqFtW51@4>L5c6AUp=0{nmJ! z0h$QK#I}n>TY=@Ac3>^Dp`iKVxfKhl7kpN^BKib4y8F~{0|RPcrnX3>k=|?NG?*%% zb^xTYp0<*P^1g|l7G?`0ftOoygIrjRqilp!<|194bb9_eVmIX+Dd`o z@DiSyG?6Ky7Yrwd<}Y)Brk~HP1Qm)|bc4YpxY#zKTs**SG4_~WuN?y49C=>HNcm=C zBrFW|zHkdrO)5QBK?r;vVLXt0)AQc3zB1L6xhQr36LEPVAPVz9#xBi>7#%WO4Gs7f zHQ6e2Nhf5$hHhNym?)tt4YP(La&ItHFp(C7AG*zPDdB@VQ^3-3K67!ddsIR9@()x2 zOj%oVt2{v>c&(RX)W`&#ETzakk1~#I3dU)~l#KPW5)u-C6yb$FC>W`F&gQav+fWUi zmp};d5UZPohr9z11(eh^hW>sjf|xd+B|KCo(?m(TbVp1#u%ToF8Z`#Ie>vuyBiyT+ z`b9K;_NR0_s;=Q~k;)@}`c7gJQ@m7qN0DzQ3VW2^L};i<#}%;6D3^h3MRyE&%f`Yi zC}7{YXU|cJAQmVZp{S;-E#kb?B-ZoeytqqzdI-rL4_E|M)#(Ecdb2L-GBo6@d1Xx3 zP&fK0W6fm*L1_2e%ATEVX_>p%_O51v*Z#9%7hva^73ko*WZ5wn)Nu(@S z0G;Bd0hGNpZ3zR37Qp_kOHLfL=8^22vbsZw^s^d7brX(KP`x$oC)T~qTRVMcu}Pv_ zvLC{_`Idkb-@XDGK|Dc$diA%fsYf5iA3OoVz*rWpNJh_^vxY#H+fl(R+1WuKxtT*;MB zckB=nq>xlPqt*Pgi;fmX{1jzp4S0RQdpo$uYZE%HhbnsHfybX}oTL{3yb)Q`E!_t; zqgL7c<3X8)DDOh73xuM<1{{eNOe4-rdYlLZ^bhwqYz`ckE7+PkKS?pTrKj^0$bqwn zgBEY8w89GiUm>Q(n-TF^P1t(?*H~T#(Jrr1vv5?EboWUaYublX!>s;jLv;AW&I`htl`LpBeSbXJ69U7Aq?ahkh`})h~@x zWCOF@scgWSPL2@so}>yFd`aJZ#%%(JNP4IafM~Ij5)3<9OP5D)NeX`tS9i6rk5zz+ zHa<+IzJAr%_%ypUkRB{hmhgMBa!Pxvsa3n|g1rO;3rVQAgo-5U_Eq(}_{;Ykb?dP} zlQq4f0?K3VB;qKQnIsumMuq6^M|JJf>Q0kUMy=9Cq1*Z!kyh(|hb@b!a|+Pl@kIAt zBVVAR_0rLwR275@wJQYs$Qu55^SNF0Q=7jKY5*Pw-?!{CLdT;P)}tL%bmijmSQn*wmq$8=0v55ZaiB zRafHlZ$tVb%_7G_(c_RV5RMke5J@UCb%A31BGSrr(KR#JgZPUpW&=e|$%`NW3-*Id z#8fn<8Mn4ddLgoz=Tc*kSG&lG52e*jF(lqu=z`e@4|I(=BL^d-w$F%s0h}50EczOx zOtWAXOtn%uUV3adL3+joGl!k`U%O$=;U!(lREx9*|B`7#5CyCQz(C}QEVrnp7gWNBL}jaAY$fw zLhUaM^F}i<{W1uOx!+a&a zyzaQX$Cx#2I1_?!GY0L*%w+&~$wekG8dfU2~xgT)0f|1TL z$=x*e&ccMp27%|^Oc0Fj1rd2SUC7>vwOeULbUAgEZ|H1aUQU9Lg`Qs{J4@GNCwIy^ z0hiVLIif};Jx11cCWj&VN`-Jk#9ng2t>?Q;=}R3g*eisx=xpJz3$S;T!>^*2JMatUGM;ijj zNpXk=G?kwH58=-yKGMf^~N%bpP_=y$aE4G)n zHU0fn+E8+;8P-Fjk`5&Z$scsqHyP~7FlR!P@tHfYC;zDgPV&QbXW;y9*3m^V3G8IQ z57O-0n?Li?$IK@CW~8w8OgY+#`{X=H0dn00d=HUYlDMqLm3Sw#CX~6dTB$0B(CfxO z5v=}yhtE&fvC`RPgxnG9|7c|=tBD;%+Lr&m|LM8q1EuTWRE#u->@ik2JP3P~Mqao= z2f{?I*u&0{Jqyw*Ur|X-rXq-vy{JgA7kEL~CfU#qKsK8#Bwg-ULN+;q$|scWEF%&SR7hVdGk07ED` z$hUp!%)YDGWyWqp%4Dd~D5^Y(k~ilOOQfA%G+Yx^;ZO%Q{+#Yy6-u|Tg^+iFl4X6|L&f+7Kphvc`rGaEJkggD2b z8xb9z7xzX<5KGSJI|qSalOkk3Ffw!&g1;)9^Y7~_OWT?oNW*zvryo4^9Hqa|chc30 ziMMt0vVb)DkCEHS0~;ifi2e1k0VFG&q@gW@&gh6xIxXLR^^2dwst<7u#bOyQ&)wMF zJ1XB?Xh$QnaQ#r-kr()E3(e$83yP5{IAmmd*Hgv~Tlb75;b^{OLRjdHrIcVVAT{V! z9-XPS0;De0xn2o%(7z<=~85J)r{ilvk!*5*DPBR~?CJWQBBUE%6w^+ynVW0%Leqp&krhr277M0e05zm(z; zpcWY+1u{-Lkyy}_gGL0b8MKxBvHmI%OH?}x_Qd&Q9vV~nl7c9|sUVdIYWB1$C%Q+2 zlri6tnZ^PDcH$tgSwK|;NUQ3)4)sXAqc0(lirJH&mOE4BIFGK=B&?AiWhwi|7?D$m z;=UO1I-=B7gJJj?pd1PVGd!-9-tqA_t6x59cyjCvV4Zmy4htWNDa-Z(3~`}Y zgFjGkxM2X9v0<9h%rnGGbb7#DRdP;KqN0(I$D#Y-QF# zw^4Kt=|i~gY-x|=0M)hzE&7Y>i7bRnD>9N>ZOm^h83% z^UM=E3PnPk?;{GAo{BpzbBQ1xhHy|=8ixdREJU+4XGpUrE4S)VQGkE8> zVaN!nU_xO#w9Da_Q-qcZEn4Id>e63GI_BOLYsE5ZM>Ib$b#Il;&EkV!ldgB!7*MOJbBj)y4 zfTobZ__??=Qk>Q-D1eDVguIRh1>GefhMoml@>paBVldtrVDl)6amgc;e+Jj&Zvq^b zJ_&gf2qh0F(=!sCMVu%PWP;1#_I5{N~cam)G}Nw?J-)jl1^Kqx^3ItwU{o4 zEb4qF4yyz+0@aF-O3LdoVoUs-RcK8;T@7yJM*C{9H4E9J{;r^qS^Xllp=%|OpsprGVU*hom%T7Rx;)G79~9_s}Q1nFaSP}n%*I(7w(O2Nh z(-=}R7y~s=>{Gin`JdFQBc8#g!x*sbn6gu>J2dp?ylbU?DPQCW$WKkGGDVSR<_*pl zwLAVdE;$g5D!TA-sKRU1$VwzU*z41$ze~Q5u2axPYXaK&7bS@KXZli7d1!PeCN80N zg6SPXpqrYs1rn67!NN&cgg7NP8in&pc*yjs0Rm>!L=Dns$JK-)a46lC!FILg^Jo`T z+3K%;J_s5{7ywYYuHnU!Hq;1@EI_9_#&5t5+EMmAG$W0aU%_S=&j-us4XXMT9w`qJ zkifqawL!(A7$%5yH|Sf;6|7fF##&(}WML4HllZpk5mk?}cn3ZTM{q+3@?wWKT{yrmVqg zHZIH{LycrIOHNdeK?+f*t&@gRx2SiV&Sc%6h$Cu(aGKpN_@7x7(;U#k%PPsh&_Epf zlNWD*+GENh=uyC4OnB_`FGcNz$izN=7$)i(JnfreGPjP1VG%L@lrQ|ESRoB^cG+&I+w7~q6t`3*zecu5W;n^ghFh}fY>7n9B=(-LKaaT&1Y z>?R8K+(S781yKj`1q$6V#jMvSUt@eJI@sSlElT401Wgq* z`~W!=ZJ4-^M~wiRdw%&S-NW5NQ*t|UlE+qYLNl!NYVxfl3x-eut7KwQh&@Ub4H>8O z*slfoWm3B-f-tBoz`<%}ddpr44Q$|f0psznm`6p1NHgAO0k<`HW4ic&GlM%tnhlEr zktA=(t#SK;T_Q}^JB1*5jALSRMM+;cAaGKXhib1lPx5sw`E?7lU zdwHuu)AI-Buc4g1aSJ*u@174%A&6l?+>}MLdi!RRgZapE&EokBfAL=%7q5wop1Yt> z#hwQ%ixMeNwshX-Kqas#7uw$?p&ZNN7hVsz zA!zYa{K>*ASa5TWk$Z4vVVonwiNE@nLAE$RI{;b&3JSQaSyh>-a`%Z!{eb*+lG0GiM);xftj$rEX9{h zoiyA;*Pb63-ETn*PvS&J%4{Aj+P1+Ec{4C%q^7-#|Irp;c2O^YPNLu>eR2dZQRhzX zwTS)6zS7Xw2FYsLzr?bvX{jSQl3hhN-OXj|b&BjxSCk{2v*4>aMoI-Gy=8CO&BbLu z*iXj0*s270d}$*2a5ZIsi6N!J+>tv1 zvCvP&xvq}-8xwS7oQ3c6wVjFh1uek7Tqsr<%3h5AcR7#08?k)S>&XBinod`7_tQr|F34VBbqajSxHm5QxTE;KX+; zH6>qul6oZ|=+(RpNB|;HYG@v`wJE@jxWBlhqC*r7Tx`^@pyP~X?#+6Z3ThGFB5IyS zE^EJsA}U&-BCKyCG`NXJjtxafM3BppXh3KZF4*Yvc!F_J`4jxQnrptc7K4kn{!iZ zd=AY?GnEFbE>^){GJ68?vMrT{D2_f5@l0Dv-p9rxe$kyU(4x8qaM6rAQ--R&knKxp zt@KCmbSyK2NWdi(M8h>QG$7UgRgg!byR20=N{}W0s0bRh9fB9bmjD~M2$KO>q!6@c ztHp4+Pc}Iyu80W|5bHK{boBqcfGGiRee!wUQ2?XTS1R8JXILM()H-|xq^pY_DR)G$ zxden;1S_}eI7vGd37zq4bf^8`nx@Yt8%7cn})^Kdg?1~ z5KxG)C$WtMd3O2S=SR$|VyiqBH^=5k@Xb}wojU_4wb~|=LNEn3VS|3?&~+%FOeOeZ3bt#1!9PSimc|_;s{C2B-9ptPBlf#t(IJs~Z1p zeDs4q7eoLGpe_spQH#GQ)r65!cxwQ%V@2hF=}OT}xdnT@w(_lNq#q0Y$D^GuTL-_g z{o9oTfQffM-LW!njDy9A7#H7#+~pk&w810(#8qi(XYkm^-M}MX0CxZ};Ay5_I6%=j zR4(HeIgZ$5#^`5!%MNZR@Qw&ay=;2JPd5Mf@JGM)BZjp6?a#@5dYe{4`2!?GAqPS} z5#kyO({NMk!GHXUGBq<8qq29=<6yBf3jr%%1O_hOv4h)2xy;aTrx`qnZy99~Or8aq z{cI8$lE*>$5fZKV==exrLiwWLYBIS@K+wFfWd_%!&xDYM+hM#i;3OIInU~&=Ip%h9 z%|tbRO5PJPEd4qBcUlA~&P_BcWHXD7Dv+`ZPqPWLKQOtCw6L`hj3JO<7OPZ*o?z6P zSaDvI(xeq<3&WuU_8fg!*D{jt+&AS7aE4=DMuu*L&GonqaROLCB=xqfk9jlZ0(?aKy?XUY!ZS?6+>lxboZ&f(p!` zGG)jDEVP^K)(-r)L^trJfC;#6>%w@6@uY_l6_(ckPY^Ng7&8Dn} zUGk)aKJ6DoFgyy}SKMXTm!>X2@S!3L`lxE|w;azg(u4GFq1LrR%!DZ?!6GFO z+q1_y9n@iAVj;OSEtTQkdGXx54OO|(^cccak5ixYOfA_!8%i2#XhyC@Zf@z?Him)2 z_eA1>Iho6aQ*|#?mX43GF!()(!D48;0V?u*B=ESE6Imt9h^bIodXZ9JUr#G8!ZdY{ zI52$^P9BZ7j(JzE98DR))3Tdz7}G29^fC3SgQLQ{0L+B`$-Om|l!~@i5;(M?JgU~o zWO1jFkfnd$w;aq5rvOER6yw5sgU{L-(PA$65I$VKRRQ+Vb=Xu1F&q~Tj8zK4)RJa1Ul*Kb1xb%wb#2AZ``R`$t7`V0 zsSY|uT8?E?NJ0%Fm#9rezzYOk zY8GWwjL87x@opx?9EKD7oAC* zr@Q@f)jmNkPC&`S)a^bz7kCXIbu;PYn%!SPA`Mh0r2LTa-sEmrcZwloP-y%Bq=9*a zoP=a@3)L^+{tZMqC$taGF$3O*S+nO$KJNS|4<23xWRhqWk{bDq8v_LUCA+Hzt;=7$ z;OPvjO&EwS|6Ho+l9@S-|L??&@~lM|y=3z+_GfleIPFZ?ngta5*Dz{L_mfL>FI8Be zS!~#2%N~HpWLU`yVhi%3?~}Ya`-=lDA9tf>C6iex#}vYk0|%EIE}E+Pv~fw}D~qV~ zkB#p|6!Kx!4y-ulny3!yU(URTJnLqTn(-Khfg*i5B)Pl)v#f}jIQ~hzO0xAB;j<)j z6L*PJCSXm*bztugqqhAC7rr}48n5vV_CRK`HY&Pk?_oUh>DIHc!*A~S!lVZPqjXzL zST1NSaW#WKGngrV#oP+Q+>yp2EB8$;*>m+n5L6cGF+CcZCQ&@p0zR`)lq2-9tW6fB zLZ)*UlM>e|{@_^%OEM^ue(tUfU1joz#`nPIitPE?Yzp$qI$rh6J)W|YceLF}ud zv>=vI|DSE%Sy;>FxC(c5;>-89uptTZ|I-`$=3z5r@JruTOyd$!DsVs*2-wAK!b7;% zu)(pTu_5WQ_iFk=R<?s)fIVJ?N?Gl62 zB^H2FD$ug4b4?4REXyiv$lP`tS7Tm{K4Xq@HIwM(ma6wRGy?TO(b_l+2EV*!1BZ_t zbW@0|GZDDn>=GcA3V`0bsg%Dho!@fRr_NT*c9oOv5)s(oOot&~fPI7pspw zdl4LV7MHy2-R}B#K`<6dkvG8Zg^D8}@cL#x$$%F|?4x>Yj{2FJr1(G>f)CR`UKU}`9%qB1o?N^(ovkU(N}{O;;|m-ca0P!xI#a{!R#MQ+BW(cNVbqHbu;+;L{1DXLtJEEU%zG-*=fyU* zL}bF)Oo1(kA`G!7*1@Ml-v+~(aeEGmiQ8T8P=)zB^C6_6XZ@eSBLj&U!*l_=9tDdb z`#N|3v1%sdN1&8;MrMwSY0!6ZFvbfSLsKtEhC2<~lvkKn+eqZ%f$&g%is%JL@=K0a zD9xmFP~ZC$fm74pb43z5vk;97j5L0OLF8)sJMKsI+0CJvwo-00_E&3lK|8s}Fq|_% zH~Of!PXqhC0LY}>VaxFG@>A5Xj4oA_YT+WVE6fz9u0ulu1L8!eURV?W zWA+qp!Q#2_93`vp!ka$h|d*@lIRF)`daih{bV>PGNDo=&BKLMraIan-T^fkGUB2=1KQd^2m#|}UCNJN zo&U=m^wrNfchrOt2zX8Tt4;6ao(M@PdqxAZ@Cx5mQMK42HQ+zGDso3_`D+QXAAnqA z?=E{-zw+9S##btY7IEbqkzi9g{M|=ScqW2Ep4%7mpW)7D@j8xQprABVjMo3p@zO!E z8a{%~*`i>6ilcsp0JYv1B*puenP=``v9>VZxC+4s2s>B`*z_d}*lmD0s2Z|;*S;Fq zEb~6?%O3xYR6|_H-mPML2I793|lG(B+DMd1xg|Q7~>x-gVA$q&l zY-}?HIpc&RsTL{fG0}|9)aq{?`3-w18&DT>x%~y8SAS5NX zSvV7A;oU)4kG)5GxN`v=FJ5M!TqsTY^|;d9%{AdfL^y~&TQ+0cwy~k%qHXGd-JD#r zw84$V%PP%bpQx|J>i-{wGigb44vhomfM2lDz}`aX0u+r4vHJ?=6tz&Km+gbgewu`& zf>P#?TZ|Y+tPnwb{8IKMJuQg{b~v(T(gkFHXBFuL99+n@Ygnv^7jluv54e&r z#_?BN_gx=8UagW4qg)3Atb()l8!4zHxN&l=r(_`I|rw zVJ~W@0wpg{3N;`EU~+^e(T(^lY=wx4@qWc43qW8s;m)DYdAnoru2FqKV-y0RQhL+{i>(*S-1 z2w<=VmS26i`pG}xMtq3YND-OP@OJ=NHLbjik`clMWE4Lmk1z2e*h%dKicbMSTYpo+u|arV#$mG%#ex0Q6p zpJ#UGlm=c)?g&Gp4-f>}$)<_JG0PLaiFI{q7|RZcA>3DTgOfGNP^M94h8Z%dA=hn~ z7>y7YD!0Xe3(YFqh05 zEXW+2jEM;gi(xlGe%IL-27|Od7TNR-19EiL^n24|phl4UD++Jxvk)LnHL?<|!fpWb za%q@IBhjPh5p)MI3ET~goi-bUq71Qp_&1)O?^I;R`}swo6w|hCD{&R`futo0+lVK~jKQ&=XVayJBczaisnY*EP%$lI~=%-GZA(qR4Cj! zdsgVeegB)cFI1cTL@LxQNb)U$ON>aL2eMy!; zg#wRIw??Q$WxS1l7GiUVO6gO<$)rt2M=8odxK}5_1v*vvE^YxHn@&czE(d!IPDTMF zur;};f`H`yJayngPQmIt+XN%}%{Th=wqpy46~~rwAANz_C*TH%x^s`R(qa|%cJ5tc z6s50$UF0^=eWay71>&@EWc5U*<9yv$sxlDX-^lT|5~)V#fWrd%BZmi>(GvXR)*L^y zWBEGd&f|bLra6h)15Ss@>LukMwR+<&0BM>ZHx`r2^@+GyNDlVg=Q=iBM*5+GH`1Q) zq!L?RvEqO*U~i3H0W?Z9O2n1mZTofD zJ~g|NI0j<}ebY;Ee@Hea7Pn>Qu)4xrjqLtt-C>{8*4$_f9D|a@4wVQQBY_|R@S7rx zMIi;o%&DYO_MD}AQOnl|-DuSRT8NSb&}eICXNvZTNs}cjGs5ua7Qj=3Ou@NGXD<2! zY;5af$;Q%T9*+Njf$6%ot|YVRh?rsKNR_L9v9NblCZUR!*^Um`<3VGw*0|9#IN#Br z5k$6$lDIxYE)~dR5Fl_bA%P@^83pwUacsu?ez2RkIPfx9rq@{`gc2}K29@}MOs-yU z4Wg%ZYvt$Y-bub~gfY!PDGh~|aQbi!1(^V`Q856$6evCQX2kQSBor&$T1s%dH6tgj z!4SZd4Cj$&S)%mn=|z9q(SNjK=*2e5AbxtT5JslFxqO9%E33f@*4P1x_?#K1e13Y= zyEW!{sAlV;HGPzPgI3nIgIjv1=z<2gq7RwmL;;^Ai1foA=1Q*fnxs+WFLriO!-mDg zUZBD(v?P0n5Xu2#(zpRrSQ#OB1*wigQQ5gbx2XluQ>daHDP?Gu^sBnINoR?54;RD_ghyCrjTW{NU{O# z%#Iz61zV^?o71HC)MD+&KVYdpHNx)TlUuN*J;*s4w11Dw}jYLiu1;W}diHMXzDU>KPxM#+uXE;Cv z2RwNIio=R}axF&Q{7@b$*;?s0JMR`m^9Gp|{s={&vPu}#^WXT8EG`tw-zX>RsJk(v zLj(@<0ymM4wRF$a`2!UzD#3SjzT`A1dn!f1#AkP*7_ZH{``us8+;LDi`44flID#el3W1L z*~|WJbrTIyayhdl&RuPqc=N{hsCu@2%s3T)Sq3gI7NIG^vSmFHc|xM^D1@)ud$gLi z_4oXc4BmOhMA$fWoFnFL!H+22VZJnS7w!K#6Rw^n!5E4@Yh8miT80CT@ybeGSn<`; zIEZx^ZbD_rRpfS|0dB?c-@4?}w4E9jgpGA)O9j9Bdg=qqk@hyh3P_LuIG~O>Ae=_$41hX_QK= zr%jRi2Thp)8M&~UZEWjbL&f{zd%K55*!yo|T-@+CLDi7Y!#pbu_M(o(lGQ6Doydq{ z$ju?1?un5?2eknUQc>Tt@EJ>b@Fro)3YP}r82}>pzJOf`C}nmLHS035;Zf@To`?Z57Zerjf>v{#Lrzu zoRhoBC<|}evl2tR#6}Q{NHjdsSJJ7IfJEO($q<>kSAtoW;?)zdtPD7ajHDK*H%12b zg~+7@FRp@Hi3AqV#1hHI74)GJ@Zj#bXRTn8&iQwZ= ztLJd1Vmt?RTNhxrWtitrn5_RRgz$a#Tk&6jjVWk!9N2I%GO-ER+C|!Tv{NN%n~9rT zKEhpo*3k$&>>coUx>$Hhp@Y2UR$VLNmI+YHQ!X1x;a|4a_N@&_mn&FX1|+aM&EnRdJP8XVRXX_&QYnbN0ZV7CQpOvC$Qyk;y{_FCECBKxLzdg0NP4 zDHG<#dA+tU*cmM%HNo2gs9d134Y-9{3OG`>1x-q#P9homE`SDgW2lHv!ia$I`~*kN zhR;h_^~;NCsAo_F)CtIqXk0WW*i%e(JYqxZNRp0{!2m>>IAxCpkocBTAT%_ETNpsb zX6;&lA3|SFC3ggSB#=pXi63UZkLT-2y`Y(!%RlzOsKTd&vl38C*B=PX!MO|i1ZMGA zoGX1Oh^uVSVwN17XSD{R_pCbjMfR_bz%9K@V+3kL0SA&gj$S|PH>mU#QudYNX!nmJ zf5qeZ@@7>Z5wAz~Pc?=5Hl%?ko{6w2D}j2`)cR-RjmVOnlIY7(PBN2;eQoD0Zj#JZ zYirA178_eqYi<{9tipc*L5W|HX% zcdAm;sfiu!-5?OHny@TE`4~4UFnfmCARINJY8%4X*tP>BD%{1~-|u<<_mWsWvj}*( zdGG(@{lD)y&-PP;AYDZDR&X-SqvBu1n=m*ZxG+IvA4t$hBv!>>3i$?b^|qpWm-Y6fO}vy z46I0QNUGGObzCE1v%eu}MyraD&{|ZSg+wi#GDwYg{Q*_JF+UIg7kaQ2*&#o-5sx(< zEYI5{Xv2luECA!M5Xr`y8Ctk2l`RN9K7sM@g@YRjkONyIr-D5wRY|1Ny2)48(u1~{ zJY77i?@rnBSs6w8KoJhWxh=%ks&qW9YtOO3L2^sYyZ!?C6a{4rF033xJ(EXj7qeZ*t?Y?*nnj0F|enBqen z4_U3!NoBCih7ZHUm8epEHQJaWn6Ri&y=uq-f$HdN5MZftq$!DnrKnXbg$c@l%3XdE zu}HcP4dPj0i-Jp-jg(5c7lQ(uH?nqFir6Ak(H$H_)jKLBFAI$`GMr%2Z=55`o%Nl+u6}LQ2d<$r$U|VUtW=kitm7*d>itZG7A{ zG<$Js7;>vLC5z%1gh1oSvO|z~BAC{*_Khg!c#c=~KBA5=NmK0W`d{BRdMl1T%RsX` z$d&*W7yytpymp*1zmrZ9?Q=I{TH$>r^S;BqahSaxaZ>7Ox{pkEE!d{vw;+nhg5Vt& zN(r)pzL+mIBZbl%c?87BkECZ5+_T&YmY2jtI<<2r${wyLDMs#4iY?(OTj`}Nh4j64 zMi+t+X}>Q)Y3E;lb%g2-hQvU$ubbe3m4SzR;n1)sn8sHrhc9xOm}vZKs*g|vx8oXj ztG7KetbZfov7HWrQ3bI2MdHT&8(x6>|hQR|j`nscGG~Cr}IlL(cmU!a2 z__jJpxf9x18&o3SX@Vn<_YP1I5ly(L3fSP^mBiQx0FFr)4cLNAGH}D^Cn5sQD@L)Q zWNt$uf^S$o=JIh3V}0$>2PQx{kw%ojfTx^+W0G$TC50S_^sODw%(kobU9wYSM`pZN zP6CefbFEGOVE**q{W_)W$jX8TDZjjx+o7@ebbaXSukuy}%6wgJDi6kQhaczN?VJ5c8h9+!aYgyzF9#h${P~7!Z){4cE%q+MUS7QN~ zMO2uHqsARY2|9?p1OC6NEk|^OFtx>&mhT(BD|@xt!+6hI^>vajEqAj_2RHE{R1sJo z&N-JX$P=4JHm!hUX`-h&( zylwr;*~YsTAKG=D`=Mg_=;6$c=M*d4c67F%6A#4$$)ZqtGdenv!L^ybzl`AjNfnD&sK1i;jN`$7c36t%ZsE%IbF)Mu@wp&vnB&(E0~f^9v&J44I^Gd z$x&;jOf41`Eik{xrXE>{Ch`bLo_HY`U$|e)pA4@XU@70*E>?RbDH)i75;YK*7ywYm zGny`saY(?K+pwkXph!n@v)66@l#t17xuvPf2&mg^K4@mO)nI`wCX5mDk0eGTSyD<6 zACp!(tK)ShM(M`GmjY`gBYN3__k6$=P#G0|VeXzjgb`6O>C|dAb50|hnW8-=NY~yl za>UF+MF10!K(>=`O!^vLD>)?MoO|imXmg4T5+(M@$q+X>580JdX!Tu)l#?{IVuoKF zk2CJy*!x)bzOOwD$eBhcqBe{4c#>^KM+ua$x2*MJx#6&w9`ILZYRvnUg9B|#x1nC3 z;4{MQ_9vScn~k(~d>o}i=si-;gHfbk(7ztyZ=(~5Ts1FyTs%PM^UN91Lpo=}k%tJ?xh8t$e0H787 zf~6O;*3E}+K1iC;8WPmdb&t?s+P|AoHGP-3V!RJ?g185%`965wdgGN(_?xbLwL=%z z!ruW~APX1{{)AhaLKQ$e2Ut0QQYkDZWHfC0!8k>cDz@TKR$y?WP=x&wcTw-3T7L43 zos^|e%%>(+{PmA_T?;J>OS5ar8Twc9^~v`xKY8lYuMrXxqx>Q%(?s>mQl3i}MOcWP zD;b$&I~pXyOu+ccyx0hZ4r;Z2)Aw!p5kW7tprPB+mzJu<&N1-IU>XUX<4ZePsA)Q| zdFALeJSbL|Q};YdA76B=M2ipL>@`IFL`Dv_iUE5)sl<$x@()+ip23cLiS5O+4=(*B zA?u4mTt8cm1U6v*<&>hpZBO6OD`<<{VSp7$79bX#XV-bjVDQkLP*qV3N2J|DhYVw7 z+-KU6rH+<(mp~X~BtJ1zO}n8y2!zLpbM+W(2yqb2)OLu;aGInRC>9n5YEUU8fg<-{ z$9{vx)?pxyXjAPJ&*m7eVED$1@^mCn6NUzYB_+QU$h1d$r>O(XhaPP$fPP@;9`M-J zPp`hr!szBMYh?9}bv{A&sHavk0YySm@|>?vFA2YS3k1Ng^ggN6EV9il#-&!GV2J#Oa_pzgUmfQ_3anJ3oTb4Xb4ZAJZlX?s*!{yLM;6|Dn0iTY& z`Li+7l@t@#63M!WGW)O@ff3@L0E;X6l3!BQ1ywB=>hTgq5`om6@dStx@}yi`x#&22 zGnu?)5RSK}R~NC=zaQw}-NA@d-|~t*g?9=hX#9(-Yd{D9*t~Tw{Lu;FL^(})*CmeyQW?5&hsKD>+ z@o|PJwnDWCHlQZ0s#u$Ow+jA}qn}vU`h$(y$hdjt4t%o>tORatF3P@tK6&_@Vu(3I%)4*Q*rR%(;$8IJ$t%db0>~o18P%93XU{>ke?Su^kKq~WW z_~L$?%AV|x^{%M4*`{Vf(5dy%3yRk!~xj@+> z+iVDJ^DKfd5(~7|fgD*+i@VW(vo5<%y|i_Kpa&;0Y&JC#AK7_HjjoB)6a&8g2VK{+ zKfmibLv?DbNV~&v0gI{LvUnvjjM?V926pcxN52k+*}=2%wy}_IpZ>&11*BvMH!_45 z$aAJOrup@gy4SZ5Afwf}3H0zw2%%n)=^_%~Jm9zLhRWYDe(zw=d)D{%ftcXP4z+*u zvLb`;_95kb2hL?NAc<2v(ECDrpO7cR$7F1tv1?IEhXAb`fzcKYLK4Ov+KgDS!gb73R z9@C$xg7@+k#+e@oWIQjIE$l77ty;#ve9DK0Kfp^vU3JnJdEY0@CqkFR$edF)G!^>x zoZ{99p@Ps3-qD7VQ>4>Ln2GU8cvYxpNf(b#>aog+M5)1`6>QVB?h}L9?XZV5!i^jn zG1ViwW^)m-PCSLrn6xeOw~PrFFq4pyXoSc7d^`DYP4sUxDH2<=Sd+k{c(jsM8Pvvl zfbUp(%X_G)Lq0n-S>A<>+4UjN+4kS8-$xBk$-Hg?Snu3PK^QDu85g-&JEDK*}?8?@mcIazO(^TL&W)s#TqYh=tkUVn-H^ocPf${A8 zC0lsu0|xOYJ5hqGm%A6e8yqa7z;Vc*AL4Vu)x-rNavUbm^#1;im$_JnSrux z=U<)%WA*8M8dF};(*zdpqiGv^PjmT5WX#&LH~H$zxz)y)f?^6oQx7ibx!sVvdq=Y} z{cV~!(vbPhDg)?dO2%Aw^Rmi2A3Z+#!Sd0%?{w2{XsW)cKR)^9<3T^*fJj!CK+#C@ zw330t4<6@4Gz{SLlvJ?|bxw~i*MJHf(GE(yOuu>_Z?wMs)j3IVI&!q-L6jwdOr$0Y zzZxnTUUE~v{R{gtu6w+~C06Alb`&sH{PF}ULO4>;NZsvw=Z)LRiwe4kJt{X}K5!*` z2tFyKP5E3-Uxz=@{~mbg7c{u^@D0abzm)28Prk>1P&l|}6M7^RNeE+}Z2dpGnsz)x zbE*>Yf+Zp6{RMKeVZ541aR=WVX))=&KsC`vIrZh+@GK3Qr8;-uUOjF!;m7syxboY4 zu)1BoUf|7@gH%FzUP2+Ic({PuXlio1UwC>`F==Qk-WUedpyN7BjXbIV=#BIkI*qIz zwYeqs4cm!IpAx57B%(Ffn9j`ncekMA;WY050?U0kP-3vA$VFGopQZilS#2Zn%-7%8 zkJ~mJW!YZPj)fr_F}!!);+`V))&a#N6*2N~ttG zIh=+2Rvci#JQ}6PKu&C!p4T2ls>6Z%Mo+V_H@q`A-;y|Pu99f(-cbg}|3q|HPvkQ# ziuSOsk_*NZ46AYA4eaTfx}nAQT7*M;i{^&BN`A_Bb@#FAn4L~ULMIDZqx~mEg#>DB zD_RJGP#F*g!dS=~pU6OIh;2oUliGL*rG%W$?BsdLPnd^t@0meldr6iUdCH^l-9DNx zF;qC0gG60;luB>K88c`>^$b44_|DWo-d^4q`9X4Fzj)lv%qRaByWKe&d|)OU@bq-l z22Y)rcnUkqRhnx>AYtytR`MTQg)+s=KX9QDsjUPG4Wu}BP=A5E3O^N$vXNb&pbDFg zth$s$n{^jei;kr*-oP!CszG34LKD7g<)F%~!8=YZG;~OOglcY+r;W`d?;f;dz(6{M zj-ia0j-P=Vm&<~~x+E>1tvPtu`9e02j)R}#7#LT6`>*CtpE@wI8C@F1@%DW($~R5E z{>b>?@Ry%^v^JBNNbM{DI$wETT7)o$p;MYN^`C&^Yz8<|A33i~%jX^~3AnuC?+n?l zXgQM!%3Ex-u<_~@HDcSs>lw9DO8snaBi&w z__)V#P4-4ibV%GN+$(~`PO2(GZ0(qOJxp!lqDCK$8_^P*y#YIakrK{cf1Iw$fFp1X zf3qH*XX%=V(1L&~CG^0$B`75sA#Q2f!(pi${v$K&H}wo{o4Lk*>Kr5RNX@X(8HF@- zl4oA>MUH)9^YgE~Vu2b}vVj5k)H}%Ci-6(A zJ_7A!W-uar6)<_q^0#Gt8h?9qVx<>oaTa!s*#t18%rCpA={C$>@rPZ+H9A1QqjPcF1@|#BB{syoIm!;So z43x}uR9Za87q9_qv7NVaW)=WfE;EPqSUY~}!pt1zHCnUvJp`^XN;EljmE<}zbvnSS z{*xu6snzHp|Kfm?K38T2pb*>;{F7G|=@7pr$MC${M$oA;wFK@rM$30WPr zOfF%b>!Q64yRn1>B+{)nA8F^?CNITVcG)>-$sF>ycW?bL@)-T!7WcNRONNeXEJCL6oJF33sfNB!mIVWn@%zE4fzaoLl}laI9de#;5sSP!*sHtqiTD z02fETI>*D2$wiLX)YjV`PqJ62Aj>O%h2;3H5@Fu(@i(IcDvv5))n{?V1FUYW4%$NC zG(K|~mQ4VA<)@cXT_8pBGm44&2rv#W*gt{~LM2FEZ+^SW zsw5PV38;spriXj(RWq@GDYL~c3~tBWs)owC66$=wJha4i69>c^Y(F$LRZFV94R|guP}3F%;ID!>Q486G;*-(a7@1+@XyW0-KLrNsW=3+A z8zAyZVw?d^#W#c+K43IqEs0Pf&`KPO8-G1Z#dbQf8dXec7>W<{N8ApwYCRW@{O0Qz zmJ3g1$HBy9@HnB<(ceTbAb$jv@^FoKOP)VMs-E@eaB;_SE!j;O=tyT)h4zsaTXT;A z1Ru)SnIi~OBa4$3t0<&rs3~GP(%h3md9DX#O$`t48(pW6s>8D9KJ0xD!L1?cEU-z&R! z_wTk%#L<&$T}sE$dEooszxo;eiXVIrmngY&Y&(Eu^CnS^u6P+4MAP3!)&sOCY!FeJ zhdIl00Gx9-U3>MiIn%%R+$K0Clc8|3JEnXvk;kcgIW3cHTKzD=9wo z4|DaG!9@rikp<4bP*qo8gYv-!7Pt&^KKE7#jFQpaIESGtFZj3@izw&cfxs$Gr-a$6 z>j@O&tX@F3-;lVq(0H&f$o_fddx=?wK_5kFoO4*4%`G_j*(3UI4bBR z^w>XeL*d{IG37R^PkZuwHW;gdM&4nr$l#e=U7d;YYRYGFobqg|do2UWoj6I#-7o@0 zNUDk>R4lVM8xQsopN?ZgHkzPWnRXqxy>5*iyv(P#bCjWHgdI;A-J{hQut zSaygezG-4i(J|qbH@Ia@mrha>jGKQMQ}pa^;#aEwcEd2AIyLY^+{(ZLi8O1or^jAR zxhQNFgb>P7trvn`I29mE)8MJJ)Z=m=awecjiPf}iwESDz? zFLaWifh(rPBXEbOpAtLx?xBuEAw05wVD1^9h(_U%90n_oaa3M%o3Z}dK>@U!e#{D+sfYoPJbHePq#&0#K;|Hwa z!h1CapCUEc+yVg#3>saJb0VSVuZ|2W^KE>U$g$zjY29r6{TdmC{jjBj?I6hLSd#o@ zU7u88v8{ty%=S1HbZ!Jd!ng5bQp&8C%<^zQFW<#DagZ&igf~+cy1W-BrDBa5?kDEY+r@myE+_X@37{`ncQE>t;wfvr{I%9J)r0h={&Xd{0?Iv$(79+}p)sn@{s zTG)GVXaUPN7h-RYUTjE*nHr$B?_hQ%_y~?OI>^mGh5_Q4Batk$7mPi-CQ_*|TPzKx z%w4W2tX(Mz#H9r6WSVR|5Ak1e-#GEBEDkhX)s0X~51kg2un&FUwo*K>nqFeJ0*`^? zs0wC(I(~JX&e3ZXZ(G7Z<)1}^Tw$JU+wpePQla?E4ucfJ9_R*G6Mpd*9Lq%ZppXZ= z^DDgZsgKDLf1zsuP8e`gXrB)X79o^HoxYTPIk)?MV?-+|%ig(>n$zPFI9fD7?j?)YT?;!=2CurAj&m_jthv)SA?N;Fepw6bS>X?Jb0DIzz?dE@rLhs4yH*R^f+^J zP&y}^p;?@3fwJcln~;U=N!v#D3x;<8;5CVt$T51XyoO;pV-kwKTF=9b%U4X+o|a%VU15$mkf}p;uP2PBCo&{vQp>5DO3N|Vh%=t z*wZFi1UbTkY}-tIELov4+d!)jz_wuGN?cTUCZ?^>3eh_ij%cvojNglEWx8M=kbNL!t_T-tUaVCg z8Rzah!{Dz%)FRbnMmX#--!e|c&8x27*7pBuQ%*v$Vcu&N3ftD!aV#n&qJOHh$ym-c zQC5(;HRB%`qubaEY9&@q-~|ckU{ggrWV4MWwF4C9xW41YgJCE~hwv&3_4NC5Jo3OX z&4lAIb01OJ1vA}@b{D;cX|L#Siw=g=s+nurbSzv8_4iiMuV58s@|v{UJNaqZ#jZ&; zFp`<+8^ALAJV7g1m$w?j92q zTfK%crQWhC4FeanYk*Bkepm~Ak$vpZm7-PCEnf=e$zM!{A4W~{U6Z}>q#LXSdyoY# zd6k~pint7F4J_#CQ%BBWYZpI2iV+-FwhpEI&HG@ai>w;JF(n>Lq}DQ3JRJs|N`ufT z)CrJXV<%&A&#VQW%NM;we~-LdWW^gu(Pkq=-ZIagSwKLWLVAQcdF19>QkN?JI(l=l zAmFa3{O6(uD_u!;Ugxq(D*dd*OCgPOC7`Ud+c-{G{HpdnsWZ$1baQ7vY?k73;2ip- z_N5oOb8k|Oio3MQ)TIC>tsdMq8}MyD}(NYMz+mRtYIfINi&Jek`ht_?29>t))nUHiz!7&&)E z{6J36PeknHS0uVO^rbc!l@(C@O&%;Ek3dSnuO{buCoOc#pZ6Jn%<@+AqkuZZ3HMYgAU~;Yt~5O zP-2dh=$o%K4z}m|IVg|6@)t)p@9x>O>AB6D1VLOb*eI^mlc~Y+zv|)x1Ox%$ae_{Y z>yMA+4svss!AY+3b7aaD2;_4>;J@|t%fJao&NT=C#3$->_*D-3%SZRz`PY9Wr$evq zGQ^Ez7JLv)1uOvyG2Ethsp|gz^#D9;{EW%cG}B?b{&+Z76A9wi82Ot|<4%6j8_P&i z0BiUxfk-(`0Fy8Meg)iw8+ANWhaT0KRO6Mq2*s4ylGt0hT0mv}Mm<`_K=fkM32PR~ zVR7;Ur43Z*L>yNqvYc<57Qr;mMkOJA`Czx3{D)DnTnVk@^j}tH6U8Ky(!p98e1*9^ zKAX^050(CLHM4*kW@i%VZ80N8NzL8#21wjn`UePOsT(5U`*wZv_OmnQC0<3Woqb!f zWI3rxq)}c>C1~Sm*^ioXLfb)DYK$CQ@+i2*)T$UYn_z;iY8RoBw+#*xO2KBhsp~z1 z3!A$tG&-N*+4l)*=~}Gtbr{u% zQ%Xc86?PXtw=Ly}QN}{9@9Xy=RoAXzQ71^O31g&*!Ak|UxP??ygSZisM0zWRXrnpI zyueJ+c?{)I5+LFPgq#NfqHdBclEx@b{4J<~+L-uOJ=W{6pGon`+*4wy#zfy6fdcaHA}R{iyX%=8T`Bj0s?(`QsgJ7hZ>FR#ANNt|iM`Rqr>Fs`WSC3Ivtv3KKkKbpxI3l-ADXsqj3pB&FHFeD_9% z`-ILZQU@V)3*GF^+mh~kxAxh7ZvM~1k(AQR1NZ!WMi_=^DeMr$&0$0{P~LSTQ6>Wt zF7R*w&P59^LK7-pVH{a(I!EqwlG2K67lar zg5v$XqngDG*GA%bT)v=7X1$czGp2@^2%ADnIlla_VmE#Cbdex}CxbqZiDN*SUvR4@ z`IMb6ea0+&bNw!SKwSu*V&ao*aHL-@0aQRv@1L6eM9(Ai%D9BI*cuc0qIs7QsjU{< z{ncw8Y(vj=6A%o34Yw8;Q~A3F-_daL204-*4UuB@4rmca$b}XqpnT{-h z42gwp_mlN>Wc$(99Uq>%Y1z-asMSIuG=6lp$vzL7O4S9|)A|@hkIB(TCfhihqQbcp z6VWX`w*;Ya>tn5ZXJ1D{Z!YR6C1-G)6*}_#_p{hfGFfKDE72K$PUdInt7B2K%VUmOj=g@c8F+j;ts-ql^!bQ_>fg;F-8oM%a ztzvRax)1sgkH}!9yVa35f9Bw9zKN8zF#>j;f;&D(h-%Z={JkF@+w%fHIJO6VhG$V- zKm)^w6YF@UJfOz42INc-=>&@8hRcl=5acaZ7-#}As*W$%nt+monqhSc{;iNzeXL)5 zQV~ufCsmvnKV0qOba%{ks-%Tv!ITiVCLzgvRjSAkEpLWbw=S068;p`nO$69UU9e7+-m(Lux<~SM>^`0vx@@mPcpxq?aU|3VhU4C(+cOZfQ_%Fz z(ma)o7Dao`oH;nM*^N=oq|OF>l*<>5R2QtFBuqZ|-5%jwDQI(Q=wcsK6t#cYg!*k@ zm|+xg6CWSna>Ey%Ci>5oeJzcmFu}y_J}arkI3sZhkHT za}A0Ks&`->(Run<@F`#pV2vIKPh7ejrP?D$Db6=(@A?~cG~cEi*-O**z}OuG zyqlu_Bc_@9>7stwM_EIuiHwCPHbc5FFfrZE^8==h^CQYNq?S~MWJ6B#b7sdIAPKuA z5Ml%3;m6|&eDQ}}G-7;f5`Jme!z*|w&*}Z(sF7*b%po{N>?EnlbSOdIO#p#Ak-#fX zjA4{&Lc_*Wrmo^zhKWklD2}(2Dvtiz=R*&QW&>{Vh%8r8Z#fsdmDkR1`?phz5(a?z z9_(x2sUmD3Gy5g}@U04RlqTbj&*SL;WQPzRQwZuM)>tCKpu4M|jwTmLQj&&AH71~m z0;C3Y!$cECvNLKo9z+sziVnC4=wmM500o!$3rMA;GuRgnB2Vi=*G3B1te|W_Kpv6r zyT>|OTOjW+HH7Qf=>V36do+)tYd521B17Pmqy&N8e8gl#!gg@KYVO&c&4s*qo();4 zhS>6>&8#kxJ+0oCK=5+1)3!|I?&s%m$wpa_F84C^w^a4Tu;ihg7z}GmTiI%GQyo$c z1ijjU5oQ$2&G0#>+JGR{@vQPt|6M&D%ncVeK%K7j(XRlC;q;g&U+RgGbH_j~fBky| zjd`lwbRx0AsxuAdi`orEWt76&m{}Uky!+O?Ioql&&mda;O2f`7cI;TYEHe!0(gGDx zQ)dH2Aln&Y7MQ0z^hxSCs@h|V2MXF}`J~*3TOcVr*9}uR%A29XNOed)G#O%}EDn?8 zB2*`$xohkAC%z5drs0R{&Mt}BmB)fkz*)}UyO|!`@BOOl2HIommi5fakAWQ5U3Wk_ z2i@%)r5Loo+(X6Oc~ooZ`rkKG&c@i*hXhq9{sQQsWZlg--re=RG|BeUUWDZv^7TwJj^RrP=h4!pdc zfIz&d^j&Bi>(^dXqj4E8)X_j{E7$+wkAt%6v`E1}kSFCb`Dfkb|MefO|HY#;&@3pk z5zqR6fPsb@R4Axmu<9Arhd*#fSP4NF0>`_aK(Rqn1vwVE zG4-f$!@B&J$p{=D?_s98ITBeAxRo(6gE4jV-4)FCsvD+y zYsZSo>|3{)w!kifY>#vOVi32{Zn(3a48mI!1^EONGmB6jr}b}NqmPrja>7rg34vP` z%bJ0zDCkFmIwpvMRBm-x@F$2={LbFjSUdP!)%q^RP~$K(+x~T~+Zw)BoSimks5bOUWh%^4CCG1}#inywT`7D%i4&iqp~m*i)BM1Z zG%dGhHRVz);=7HEj?UhLP!BpRrG5I;m?0h3Md}gbdqG*eOL`Ue%0IY^0X*QQx~UM0+zTi-Pe2(li+ zL5Rof*kns6G&uV@SI+7TCBrA+8g$Ne8 zd8MQ(bP@H)j_|qo!-sm-;{_qVn5@n!d}6vKJUf;N$6usSLtg+4>iQnU%G~z~qA7n~ zHlPk8m4v@ldkl4M>5aa1>S`mG)IM%MG0Ub1fwDPo zy$hMORd1*d#mo@1=WEOWcL^32AWF%W!2U=0{}rT}?Ux=>P_f~o;4+>N6*x(f=dPHc zxTDvH4}d>#JM6rjvdvFG=vgp_LUqW@tNnQk^_JRDlp{q3x~0sqtsgihiSxWMjXe_q z;EWM06DH5jdNh?C2ljj0oHTpE}0Q_DGWasOSu4Q!z9zliNOG{ zIx}HX(VI71K~P}l=~_oO0x`hh&xq5`tdAAEIf3(31xyGlVW~UXupE>QHRqiLsBH#h zTqI|byP!go3m&jzjVBU5zG>Mq!I_r>?7xcx}lSMo-k;oECA9KxP>9n8XF&sCRRkfzLQB2KHOXC zty+KNbJlXfLdrTd2BmE7eue&8Nf28d9!uDZkdVTg+ysq zi~~0#^FZCc?|=XP_g=G!rOD>guf{F!>7fw=ZhH6Dy*U{ai18`tfE{Ob-bY(@JhSz~ zn^Y4Y`IEgn2+BMR2|`NwV#-ddIP-OHjl_@BH$2$AB?b9pnoQ%veV` z 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

ZA7zj(x;Y;uW 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�UK7={OO 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 z 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 z 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 z&F$?Q9{session = session; - pvt->tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), put_text_msg, NULL); + pvt->tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, NULL); pvt->head_lead = TDD_LEAD; v18_put(pvt->tdd_state, text, -1); @@ -331,7 +331,7 @@ switch_status_t spandsp_tdd_decode_session(switch_core_session_t *session) } pvt->session = session; - pvt->tdd_state = v18_init(NULL, FALSE, get_v18_mode(session), put_text_msg, pvt); + pvt->tdd_state = v18_init(NULL, FALSE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, pvt); if ((status = switch_core_media_bug_add(session, "spandsp_tdd_decode", NULL, tdd_decode_callback, pvt, 0, SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) { From 40b08d7f89c31f5be6e33679dfd3f38cad625c1f Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 3 Jul 2013 04:22:15 +0000 Subject: [PATCH 031/278] Add mod_rayo to debian packaging --- debian/control-modules | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/control-modules b/debian/control-modules index 188f17ff96..2236c3407b 100644 --- a/debian/control-modules +++ b/debian/control-modules @@ -490,6 +490,10 @@ Module: event_handlers/mod_radius_cdr Description: mod_radius_cdr Adds mod_radius_cdr. +Module: event_handlers/mod_rayo +Description: mod_rayo + Adds mod_rayo. + Module: event_handlers/mod_snmp Description: mod_snmp Adds mod_snmp. From dfd647de224b3dc71d7e8a8c1f0e2509e3e4a8f7 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 3 Jul 2013 04:22:26 +0000 Subject: [PATCH 032/278] Add mod_ssml to Debian packaging --- debian/control-modules | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/control-modules b/debian/control-modules index 2236c3407b..de260c7edb 100644 --- a/debian/control-modules +++ b/debian/control-modules @@ -527,6 +527,10 @@ Description: mod_sndfile Adds mod_sndfile. Build-Depends: libflac-dev, libogg-dev, libvorbis-dev +Module: formats/mod_ssml +Description: mod_ssml + Adds mod_ssml. + Module: formats/mod_tone_stream Description: mod_tone_stream Adds mod_tone_stream. From 87360a384d4a869113826c0e448c9e1c40b09ab9 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 3 Jul 2013 04:25:48 +0000 Subject: [PATCH 033/278] Tweak such that control-modules.gen matches --- debian/control-modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control-modules b/debian/control-modules index de260c7edb..ec291c71b6 100644 --- a/debian/control-modules +++ b/debian/control-modules @@ -227,9 +227,9 @@ Description: Voicemail detection This module detects voicemail beeps at any frequency in O(1) time. Module: applications/mod_voicemail -Depends: mail-transport-agent Description: Voicemail This module provides a voicemail system. +Depends: mail-transport-agent Module: applications/mod_voicemail_ivr Description: Voicemail IVR From 172df304cc8f934f5e3c1251c9490d45a6f32165 Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli Date: Wed, 3 Jul 2013 10:01:17 +0200 Subject: [PATCH 034/278] gsmopen: on Linux, automatically find out and setup audio and data ports associated to dongle model, IMEI and IMSI --- src/mod/endpoints/mod_gsmopen/gsmopen.h | 4 + src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp | 335 +++++++++++++++++- 2 files changed, 331 insertions(+), 8 deletions(-) diff --git a/src/mod/endpoints/mod_gsmopen/gsmopen.h b/src/mod/endpoints/mod_gsmopen/gsmopen.h index 16e532478a..dd6cbea999 100644 --- a/src/mod/endpoints/mod_gsmopen/gsmopen.h +++ b/src/mod/endpoints/mod_gsmopen/gsmopen.h @@ -71,6 +71,7 @@ #include #include #include +#include #endif //WIN32 #ifndef WIN32 @@ -556,3 +557,6 @@ int dump_event_full(private_t *tech_pvt, int is_alarm, int alarm_code, const cha int gsmopen_serial_init_audio_port(private_t *tech_pvt, int controldevice_audio_speed); int serial_audio_init(private_t *tech_pvt); int serial_audio_shutdown(private_t *tech_pvt); +#ifndef WIN32 +void find_ttyusb_devices(private_t *tech_pvt, const char *dirname); +#endif// WIN32 diff --git a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp index 65a9b1db58..2b9f8bf350 100644 --- a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp +++ b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp @@ -1237,6 +1237,8 @@ static switch_status_t load_config(int reload_type) uint32_t controldevprotocol = PROTOCOL_AT; //FIXME TODO uint32_t running = 1; //FIXME TODO const char *gsmopen_serial_sync_period = "300"; //FIXME TODO + const char *imei = ""; + const char *imsi = ""; tech_pvt = NULL; @@ -1394,6 +1396,10 @@ static switch_status_t load_config(int reload_type) playback_boost = val; } else if (!strcasecmp(var, "no_sound")) { no_sound = val; + } else if (!strcasecmp(var, "imsi")) { + imsi = val; + } else if (!strcasecmp(var, "imei")) { + imei = val; } else if (!strcasecmp(var, "gsmopen_serial_sync_period")) { gsmopen_serial_sync_period = val; } @@ -1466,7 +1472,6 @@ static switch_status_t load_config(int reload_type) if (interface_id && interface_id < GSMOPEN_MAX_INTERFACES) { private_t newconf; - switch_threadattr_t *gsmopen_api_thread_attr = NULL; int res = 0; memset(&newconf, '\0', sizeof(newconf)); @@ -1536,6 +1541,8 @@ static switch_status_t load_config(int reload_type) switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callsetupoutgoing_string, at_indicator_callsetupoutgoing_string); switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callsetupremoteringing_string, at_indicator_callsetupremoteringing_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].imsi, imsi); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].imei, imei); globals.GSMOPEN_INTERFACES[interface_id].at_early_audio = atoi(at_early_audio); globals.GSMOPEN_INTERFACES[interface_id].at_after_preinit_pause = atoi(at_after_preinit_pause); globals.GSMOPEN_INTERFACES[interface_id].at_initial_pause = atoi(at_initial_pause); @@ -1550,6 +1557,30 @@ static switch_status_t load_config(int reload_type) globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol = controldevprotocol; //FIXME globals.GSMOPEN_INTERFACES[interface_id].running = running; //FIXME + + gsmopen_store_boost((char *) capture_boost, &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME + gsmopen_store_boost((char *) playback_boost, &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME + + } else { + ERRORA("interface id %u is higher than GSMOPEN_MAX_INTERFACES (%d)\n", GSMOPEN_P_LOG, interface_id, GSMOPEN_MAX_INTERFACES); + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "interface id is higher than GSMOPEN_MAX_INTERFACES"); + continue; + } + + } + +#ifndef WIN32 + find_ttyusb_devices(NULL, "/sys/devices"); +#endif// WIN32 + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + + switch_threadattr_t *gsmopen_api_thread_attr = NULL; + int res = 0; + int interface_id = i; + + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + WARNINGA("STARTING interface_id=%u\n", GSMOPEN_P_LOG, interface_id); DEBUGA_GSMOPEN("id=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].id); DEBUGA_GSMOPEN("name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].name); @@ -1557,6 +1588,8 @@ static switch_status_t load_config(int reload_type) DEBUGA_GSMOPEN("context=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].context); DEBUGA_GSMOPEN("dialplan=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].dialplan); DEBUGA_GSMOPEN("destination=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].destination); + DEBUGA_GSMOPEN("imei=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].imei); + DEBUGA_GSMOPEN("imsi=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].imsi); DEBUGA_GSMOPEN("controldevice_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].controldevice_name); DEBUGA_GSMOPEN("controldevice_audio_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].controldevice_audio_name); DEBUGA_GSMOPEN("gsmopen_serial_sync_period=%d\n", GSMOPEN_P_LOG, @@ -1628,8 +1661,8 @@ static switch_status_t load_config(int reload_type) globals.GSMOPEN_INTERFACES[interface_id].active = 1; - gsmopen_store_boost((char *) capture_boost, &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME - gsmopen_store_boost((char *) playback_boost, &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME + //gsmopen_store_boost((char *) capture_boost, &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME + //gsmopen_store_boost((char *) playback_boost, &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME switch_sleep(100000); switch_threadattr_create(&gsmopen_api_thread_attr, gsmopen_module_pool); @@ -1640,14 +1673,10 @@ static switch_status_t load_config(int reload_type) switch_sleep(100000); WARNINGA("STARTED interface_id=%u\n", GSMOPEN_P_LOG, interface_id); - } else { - ERRORA("interface id %u is higher than GSMOPEN_MAX_INTERFACES (%d)\n", GSMOPEN_P_LOG, interface_id, GSMOPEN_MAX_INTERFACES); - alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "interface id is higher than GSMOPEN_MAX_INTERFACES"); - continue; } - } + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { /* How many real intterfaces */ @@ -1661,6 +1690,8 @@ static switch_status_t load_config(int reload_type) DEBUGA_GSMOPEN("hold-music=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].hold_music); DEBUGA_GSMOPEN("dialplan=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].dialplan); DEBUGA_GSMOPEN("destination=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].destination); + DEBUGA_GSMOPEN("imei=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].imei); + DEBUGA_GSMOPEN("imsi=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].imsi); DEBUGA_GSMOPEN("controldevice_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].controldevice_name); DEBUGA_GSMOPEN("gsmopen_serial_sync_period=%d\n", GSMOPEN_P_LOG, (int) globals.GSMOPEN_INTERFACES[i].gsmopen_serial_sync_period); DEBUGA_GSMOPEN("controldevice_audio_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].controldevice_audio_name); @@ -2773,6 +2804,294 @@ int sms_incoming(private_t *tech_pvt) return 0; } + + +#ifndef WIN32 +#define PATH_MAX_GIOVA PATH_MAX +static struct { +char path_idvendor[PATH_MAX_GIOVA]; +char path_idproduct[PATH_MAX_GIOVA]; +char tty_base[PATH_MAX_GIOVA]; +char idvendor[PATH_MAX_GIOVA]; +char idproduct[PATH_MAX_GIOVA]; +char tty_data_dir[PATH_MAX_GIOVA]; +char tty_audio_dir[PATH_MAX_GIOVA]; +char data_dev_id[256]; +char audio_dev_id[256]; +char delimiter[256]; +char txt_saved[PATH_MAX_GIOVA]; +char tty_base_copied[PATH_MAX_GIOVA]; +char tty_data_device_file[PATH_MAX_GIOVA]; +char tty_data_device[256]; +char tty_audio_device_file[PATH_MAX_GIOVA]; +char tty_audio_device[256]; +char answer[AT_BUFSIZ]; +char imei[256]; +char imsi[256]; +} f; +/* +int times=0; +int conta=0; +*/ +void find_ttyusb_devices(private_t *tech_pvt, const char *dirname) +{ + DIR *dir; + struct dirent *entry; + + memset( f.path_idvendor, 0, sizeof(f.path_idvendor) ); + memset( f.path_idproduct, 0, sizeof(f.path_idproduct) ); + memset( f.tty_base, 0, sizeof(f.tty_base) ); + memset( f.idvendor, 0, sizeof(f.idvendor) ); + memset( f.idproduct, 0, sizeof(f.idproduct) ); + memset( f.tty_data_dir, 0, sizeof(f.tty_data_dir) ); + memset( f.tty_audio_dir, 0, sizeof(f.tty_audio_dir) ); + memset( f.data_dev_id, 0, sizeof(f.data_dev_id) ); + memset( f.audio_dev_id, 0, sizeof(f.audio_dev_id) ); + memset( f.delimiter, 0, sizeof(f.delimiter) ); + memset( f.txt_saved, 0, sizeof(f.txt_saved) ); + memset( f.tty_base_copied, 0, sizeof(f.tty_base_copied) ); + memset( f.tty_data_device_file, 0, sizeof(f.tty_data_device_file) ); + memset( f.tty_data_device, 0, sizeof(f.tty_data_device) ); + memset( f.tty_audio_device_file, 0, sizeof(f.tty_audio_device_file) ); + memset( f.tty_audio_device, 0, sizeof(f.tty_audio_device) ); + + if (!(dir = opendir(dirname))){ + ERRORA("ERRORA! dirname=%s\n", GSMOPEN_P_LOG, dirname); + closedir(dir); + //conta--; + return; + } + if (!(entry = readdir(dir))){ + ERRORA("ERRORA!\n", GSMOPEN_P_LOG); + closedir(dir); + //conta--; + return; + } + + //conta++; + do { + if (entry->d_type == DT_DIR) { + char path[PATH_MAX_GIOVA]; + int len; + + //times++; + len = snprintf(path, sizeof(path)-1, "%s/%s", dirname, entry->d_name); + path[len] = 0; + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + find_ttyusb_devices(tech_pvt, path); + } else{ + int fd=-1; + int len2=-1; + DIR *dir2=NULL; + struct dirent *entry2=NULL; + int counter; + char *scratch=NULL; + char *txt=NULL; + + if (strcmp(entry->d_name, "idVendor") == 0){ + sprintf(f.tty_base, "%s", dirname); + sprintf(f.path_idvendor, "%s/%s", dirname, entry->d_name); + sprintf(f.path_idproduct, "%s/idProduct", dirname); + + fd = open(f.path_idvendor, O_RDONLY); + len2=read(fd, f.idvendor, sizeof(f.idvendor)); + close(fd); + + if(f.idvendor[len2-1]=='\n'){ + f.idvendor[len2-1]='\0'; + } + if (strcmp(f.idvendor, "12d1") == 0){ + + fd = open(f.path_idproduct, O_RDONLY); + len2=read(fd, f.idproduct, sizeof(f.idproduct)); + close(fd); + + if(f.idproduct[len2-1]=='\n'){ + f.idproduct[len2-1]='\0'; + } + if (strcmp(f.idproduct, "1001") == 0 || strcmp(f.idproduct, "140c") == 0 || strcmp(f.idproduct, "1436") == 0 || strcmp(f.idproduct, "1506") == 0 || strcmp(f.idproduct, "14ac") == 0 ){ + if (strcmp(f.idproduct, "1001") == 0){ + strcpy(f.data_dev_id, "2"); + strcpy(f.audio_dev_id, "1"); + }else if (strcmp(f.idproduct, "140c") == 0){ + strcpy(f.data_dev_id, "3"); + strcpy(f.audio_dev_id, "2"); + }else if (strcmp(f.idproduct, "1436") == 0){ + strcpy(f.data_dev_id, "4"); + strcpy(f.audio_dev_id, "3"); + }else if (strcmp(f.idproduct, "1506") == 0){ + strcpy(f.data_dev_id, "1"); + strcpy(f.audio_dev_id, "2"); + }else if (strcmp(f.idproduct, "14ac") == 0){ + strcpy(f.data_dev_id, "4"); + strcpy(f.audio_dev_id, "3"); + }else{ + //not possible + } + strcpy(f.delimiter, "/"); + strcpy(f.tty_base_copied, f.tty_base); + counter=0; + while((txt = strtok_r(!counter ? f.tty_base_copied : NULL, f.delimiter, &scratch))) + { + strcpy(f.txt_saved, txt); + counter++; + } + sprintf(f.tty_data_dir, "%s/%s:1.%s", f.tty_base, f.txt_saved, f.data_dev_id); + sprintf(f.tty_audio_dir, "%s/%s:1.%s", f.tty_base, f.txt_saved, f.audio_dev_id); + + if (!(dir2 = opendir(f.tty_data_dir))) { + ERRORA("ERRORA!\n", GSMOPEN_P_LOG); + } + if (!(entry2 = readdir(dir2))){ + ERRORA("ERRORA!\n", GSMOPEN_P_LOG); + } + do { + if (strncmp(entry2->d_name, "ttyUSB", 6) == 0){ + //char f.answer[AT_BUFSIZ]; + ctb::SerialPort *serialPort_serial_control; + int res; + int read_count; + char at_command[256]; + + sprintf(f.tty_data_device_file, "%s/%s", f.tty_data_dir, entry2->d_name); + sprintf(f.tty_data_device, "/dev/%s", entry2->d_name); + + memset(f.answer,0,sizeof(f.answer)); + memset(f.imei,0,sizeof(f.imei)); + memset(f.imsi,0,sizeof(f.imsi)); + + serialPort_serial_control = new ctb::SerialPort(); + if (serialPort_serial_control->Open(f.tty_data_device, 115200, "8N1", ctb::SerialPort::NoFlowControl) >= 0) { + sprintf(at_command, "AT\r\n"); + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + sprintf(at_command, "ATE1\r\n"); + res = serialPort_serial_control->Write(at_command, 6); + usleep(20000); //0.02 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + sprintf(at_command, "AT\r\n"); + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + memset(f.answer,0,sizeof(f.answer)); + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + memset(f.answer,0,sizeof(f.answer)); + + /* IMEI */ + sprintf(at_command, "AT+GSN\r\n"); + res = serialPort_serial_control->Write(at_command, 8); + if (res != 8) { + ERRORA("writing AT+GSN failed: %d\n", GSMOPEN_P_LOG, res); + }else { + usleep(200000); //0.2 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + if (read_count < 32) { + ERRORA("reading AT+GSN failed: |%s|, read_count=%d\n", GSMOPEN_P_LOG, f.answer, read_count); + } else { + strncpy(f.imei, f.answer+9, 15); + sprintf(at_command, "AT\r\n"); + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + sprintf(at_command, "ATE1\r\n"); + res = serialPort_serial_control->Write(at_command, 6); + usleep(20000); //0.02 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + sprintf(at_command, "AT\r\n"); + res = serialPort_serial_control->Write(at_command, 4); + usleep(20000); //0.02 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + memset(f.answer,0,sizeof(f.answer)); + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + memset(f.answer,0,sizeof(f.answer)); + + /* IMSI */ + sprintf(at_command, "AT+CIMI\r\n"); + res = serialPort_serial_control->Write(at_command, 9); + if (res != 9) { + ERRORA("writing AT+CIMI failed: %d\n", GSMOPEN_P_LOG, res); + }else { + usleep(200000); //0.2 seconds + read_count = serialPort_serial_control->Read(f.answer, AT_BUFSIZ); + if (read_count < 33) { + ERRORA("reading AT+CIMI failed: |%s|, read_count=%d\n", GSMOPEN_P_LOG, f.answer, read_count); + } else { + strncpy(f.imsi, f.answer+10, 15); + } + } + } + } + res = serialPort_serial_control->Close(); + } else { + ERRORA("port %s, NOT open\n", GSMOPEN_P_LOG, f.tty_data_device); + } + } + } while ((entry2 = readdir(dir2))); + closedir(dir2); + + if (!(dir2 = opendir(f.tty_audio_dir))) { + ERRORA("ERRORA!\n", GSMOPEN_P_LOG); + } + if (!(entry2 = readdir(dir2))){ + ERRORA("ERRORA!\n", GSMOPEN_P_LOG); + } + do { + if (strncmp(entry2->d_name, "ttyUSB", 6) == 0){ + int i; + sprintf(f.tty_audio_device_file, "%s/%s", f.tty_audio_dir, entry2->d_name); + sprintf(f.tty_audio_device, "/dev/%s", entry2->d_name); + + NOTICA("************************************************\n", GSMOPEN_P_LOG, f.imei); + NOTICA("f.imei=|%s|\n", GSMOPEN_P_LOG, f.imei); + NOTICA("f.imsi=|%s|\n", GSMOPEN_P_LOG, f.imsi); + NOTICA("f.tty_data_device = |%s|\n", GSMOPEN_P_LOG, f.tty_data_device); + NOTICA("f.tty_audio_device = |%s|\n", GSMOPEN_P_LOG, f.tty_audio_device); + NOTICA("************************************************\n", GSMOPEN_P_LOG, f.imei); + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + switch_threadattr_t *gsmopen_api_thread_attr = NULL; + int res = 0; + int interface_id = i; + + if (strlen(globals.GSMOPEN_INTERFACES[i].name) && (strlen(globals.GSMOPEN_INTERFACES[i].imsi) || strlen(globals.GSMOPEN_INTERFACES[i].imei)) ) { + //NOTICA("globals.GSMOPEN_INTERFACES[i].imei)=%s strlen(globals.GSMOPEN_INTERFACES[i].imei)=%d (strcmp(globals.GSMOPEN_INTERFACES[i].imei, f.imei) == 0)=%d \n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].imei, strlen(globals.GSMOPEN_INTERFACES[i].imei), (strcmp(globals.GSMOPEN_INTERFACES[i].imei, f.imei) == 0) ); + if( ( strlen(globals.GSMOPEN_INTERFACES[i].imei) ? (strcmp(globals.GSMOPEN_INTERFACES[i].imei, f.imei) == 0) : 1) ) { + if ( (strlen(globals.GSMOPEN_INTERFACES[i].imsi) ? (strcmp(globals.GSMOPEN_INTERFACES[i].imsi, f.imsi) == 0) : 1) ){ + strcpy(globals.GSMOPEN_INTERFACES[i].controldevice_audio_name, f.tty_audio_device); + strcpy(globals.GSMOPEN_INTERFACES[i].controldevice_name, f.tty_data_device); + NOTICA("name = |%s|, controldevice_audio_name = |%s|, controldevice_name = |%s|\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].controldevice_audio_name, globals.GSMOPEN_INTERFACES[i].controldevice_name); + break; + } + } + } + } + } + } while ((entry2 = readdir(dir2))); + closedir(dir2); + } + } + } + } + } while ((entry = readdir(dir))); + closedir(dir); +/* + if(f.conta < 0){ + ERRORA("f.conta=%d, dirs=%d, mem=%d\n", GSMOPEN_P_LOG, conta, times, conta*PATH_MAX_GIOVA); + }else{ + NOTICA("f.conta=%d, dirs=%d, mem=%d\n", GSMOPEN_P_LOG, conta, times, conta*PATH_MAX_GIOVA); + } + conta--; +*/ +} +#endif// WIN32 + + + + /* For Emacs: * Local Variables: * mode:c From 4a3d1a074f6c993df36da2fd202177f4a6070049 Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli Date: Wed, 3 Jul 2013 13:56:02 +0200 Subject: [PATCH 035/278] FS-5148 gsmopen: wait for the first audio write() before giving the AT command that moves the audio path to the audio tty (eg: don't accumulate noise by not sending frames on the open tty) --- src/mod/endpoints/mod_gsmopen/gsmopen.h | 1 + src/mod/endpoints/mod_gsmopen/gsmopen_protocol.cpp | 7 +++++-- src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mod/endpoints/mod_gsmopen/gsmopen.h b/src/mod/endpoints/mod_gsmopen/gsmopen.h index dd6cbea999..b37fa77834 100644 --- a/src/mod/endpoints/mod_gsmopen/gsmopen.h +++ b/src/mod/endpoints/mod_gsmopen/gsmopen.h @@ -455,6 +455,7 @@ struct private_object { char buffer2[320]; int buffer2_full; + int serialPort_serial_audio_opened; }; diff --git a/src/mod/endpoints/mod_gsmopen/gsmopen_protocol.cpp b/src/mod/endpoints/mod_gsmopen/gsmopen_protocol.cpp index 6e9e70876b..6af6a0075e 100644 --- a/src/mod/endpoints/mod_gsmopen/gsmopen_protocol.cpp +++ b/src/mod/endpoints/mod_gsmopen/gsmopen_protocol.cpp @@ -2202,7 +2202,7 @@ int gsmopen_serial_answer_AT(private_t *tech_pvt) return -1; } } - res = gsmopen_serial_write_AT_expect(tech_pvt, "AT^DDSETEX=2", tech_pvt->at_dial_expect); + //res = gsmopen_serial_write_AT_expect(tech_pvt, "AT^DDSETEX=2", tech_pvt->at_dial_expect); DEBUGA_GSMOPEN("AT: call answered\n", GSMOPEN_P_LOG); return 0; } @@ -2286,7 +2286,7 @@ int gsmopen_serial_call_AT(private_t *tech_pvt, char *dstr) ERRORA("dial command failed, dial string was: %s\n", GSMOPEN_P_LOG, at_command); return -1; } - res = gsmopen_serial_write_AT_expect(tech_pvt, "AT^DDSETEX=2", tech_pvt->at_dial_expect); + //res = gsmopen_serial_write_AT_expect(tech_pvt, "AT^DDSETEX=2", tech_pvt->at_dial_expect); return 0; } @@ -3007,6 +3007,8 @@ int gsmopen_serial_init_audio_port(private_t *tech_pvt, int controldevice_audio_ if (tech_pvt->serialPort_serial_audio->Open(devname, 115200, "8N1", ctb::SerialPort::NoFlowControl) >= 0) { DEBUGA_GSMOPEN("port %s, SUCCESS open\n", GSMOPEN_P_LOG, tech_pvt->controldevice_audio_name); + tech_pvt->serialPort_serial_audio_opened =1; + gsmopen_serial_write_AT_expect(tech_pvt, "AT^DDSETEX=2", tech_pvt->at_dial_expect); } else { #ifdef WIN32 LPVOID msg; @@ -3054,6 +3056,7 @@ int serial_audio_shutdown(private_t *tech_pvt) res = tech_pvt->serialPort_serial_audio->Close(); DEBUGA_GSMOPEN("serial_audio_shutdown res=%d (controldev_audio_fd is %d)\n", GSMOPEN_P_LOG, res, tech_pvt->controldev_audio_fd); + tech_pvt->serialPort_serial_audio_opened =0; return res; } diff --git a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp index 2b9f8bf350..9e78b8f55b 100644 --- a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp +++ b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp @@ -208,6 +208,7 @@ switch_status_t gsmopen_tech_init(private_t *tech_pvt, switch_core_session_t *se dtmf_rx_init(&tech_pvt->dtmf_state, NULL, NULL); dtmf_rx_parms(&tech_pvt->dtmf_state, 0, 10, 10, -99); +/* if (tech_pvt->no_sound == 0) { if (serial_audio_init(tech_pvt)) { ERRORA("serial_audio_init failed\n", GSMOPEN_P_LOG); @@ -215,6 +216,7 @@ switch_status_t gsmopen_tech_init(private_t *tech_pvt, switch_core_session_t *se } } +*/ if (switch_core_timer_init(&tech_pvt->timer_read, "soft", 20, tech_pvt->read_codec.implementation->samples_per_packet, gsmopen_module_pool) != SWITCH_STATUS_SUCCESS) { @@ -805,6 +807,9 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc gsmopen_sound_boost(frame->data, frame->samples, tech_pvt->playback_boost); if (!tech_pvt->no_sound) { + if (!tech_pvt->serialPort_serial_audio_opened) { + serial_audio_init(tech_pvt); + } sent = tech_pvt->serialPort_serial_audio->Write((char *) frame->data, (int) (frame->datalen)); if (sent && sent != frame->datalen && sent != -1) { From 02046950bebf84b6cf56f27321c3427718037c6b Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 3 Jul 2013 15:58:46 +0000 Subject: [PATCH 036/278] Fix uninitialized use of length variable Bad things must have been happening when this was hit. This was introduced in commit 5cb4cd9d9cef91d172c36e827a6bf1640fdca968 --- src/switch_ivr_async.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 6566670848..18d4a3fcca 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -1132,6 +1132,9 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s switch_time_t diff; rh->wready = 1; + nframe = switch_core_media_bug_get_native_write_frame(bug); + len = nframe->datalen; + if (!rh->rready) { unsigned char fill_data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; switch_size_t fill_len = len; @@ -1140,8 +1143,6 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s } - nframe = switch_core_media_bug_get_native_write_frame(bug); - len = nframe->datalen; if (rh->last_write_time && rh->last_write_time < now) { From fa174ec3d84c4f635c8604529e25d766fd50fb8c Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 3 Jul 2013 10:32:25 +0000 Subject: [PATCH 037/278] Fix spacing --- debian/bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/bootstrap.sh b/debian/bootstrap.sh index 473bae5539..5843872904 100755 --- a/debian/bootstrap.sh +++ b/debian/bootstrap.sh @@ -179,7 +179,7 @@ Build-Depends: # bootstrapping automake (>= 1.9), autoconf, libtool, # core build - dpkg-dev (>= 1.15.8.12), gcc (>= 4:4.4.5) , g++ (>= 4:4.4.5), + dpkg-dev (>= 1.15.8.12), gcc (>= 4:4.4.5), g++ (>= 4:4.4.5), libc6-dev (>= 2.11.3), make (>= 3.81), wget, pkg-config, # configure options From f91bdf12aa1854da04775933a40f444e613775b5 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 10:34:06 -0500 Subject: [PATCH 038/278] --resolve FS-3918 using patch that adds conference_moderator_pin channel variable --- src/mod/applications/mod_conference/mod_conference.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 50bf670aad..7abdd9710c 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -7359,7 +7359,7 @@ SWITCH_STANDARD_APP(conference_function) switch_core_session_message_t msg = { 0 }; uint8_t rl = 0, isbr = 0; char *dpin = ""; - char *mdpin = ""; + const char *mdpin = ""; conf_xml_cfg_t xml_cfg = { 0 }; switch_event_t *params = NULL; int locked = 0; @@ -7601,6 +7601,9 @@ SWITCH_STANDARD_APP(conference_function) rl++; } + /* Moderator PIN as a channel variable */ + mdpin = switch_channel_get_variable(channel, "conference_moderator_pin"); + if (zstr(dpin) && conference->pin) { dpin = conference->pin; } From fdbfbc867feab03ee3262a5ff0e9b45784fec4e1 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Wed, 3 Jul 2013 13:01:23 -0400 Subject: [PATCH 039/278] --resolve FS-3852 --- src/switch_odbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_odbc.c b/src/switch_odbc.c index 9be34079d6..7656231c3e 100644 --- a/src/switch_odbc.c +++ b/src/switch_odbc.c @@ -412,7 +412,7 @@ SWITCH_DECLARE(switch_odbc_status_t) switch_odbc_handle_exec_string(switch_odbc_ SQLRowCount(stmt, &m); handle->affected_rows = (int) m; - if (m <= 0) { + if (m == 0) { goto done; } From 0d32f510f24840de09879f3b65e178eb4b30d6fd Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 3 Jul 2013 14:22:30 -0400 Subject: [PATCH 040/278] mod_rayo: move example config so that make config-rayo works --- .../rayo}/autoload_configs/acl.conf.xml | 0 .../rayo}/autoload_configs/cdr_csv.conf.xml | 0 .../autoload_configs/conference.conf.xml | 0 .../rayo}/autoload_configs/console.conf.xml | 0 .../autoload_configs/distributor.conf.xml | 0 .../autoload_configs/event_socket.conf.xml | 0 .../rayo}/autoload_configs/hash.conf.xml | 0 .../autoload_configs/http_cache.conf.xml | 0 .../autoload_configs/local_stream.conf.xml | 0 .../rayo}/autoload_configs/logfile.conf.xml | 0 .../rayo}/autoload_configs/lua.conf.xml | 0 .../rayo}/autoload_configs/memcache.conf.xml | 0 .../rayo}/autoload_configs/modules.conf.xml | 0 .../autoload_configs/pocketsphinx.conf.xml | 0 .../post_load_modules.conf.xml | 0 .../autoload_configs/presence_map.conf.xml | 0 conf/rayo/autoload_configs/rayo.conf.xml | 43 +++++++++++++++++++ .../rayo}/autoload_configs/shout.conf.xml | 0 .../rayo}/autoload_configs/sofia.conf.xml | 0 .../rayo}/autoload_configs/spandsp.conf.xml | 0 .../rayo}/autoload_configs/ssml.conf.xml | 0 .../rayo}/autoload_configs/switch.conf.xml | 0 .../rayo}/autoload_configs/timezones.conf.xml | 0 .../mod_rayo/conf => conf/rayo}/cacert.pem | 0 .../conf => conf/rayo}/dialplan/public.xml | 0 .../conf => conf/rayo}/directory/default.xml | 0 .../rayo}/directory/default/usera.xml | 0 .../rayo}/directory/default/userb.xml | 0 .../rayo}/directory/default/userc.xml | 0 .../rayo}/directory/default/userd.xml | 0 .../conf => conf/rayo}/freeswitch.xml | 0 .../conf => conf/rayo}/lang/de/de.xml | 0 .../conf => conf/rayo}/lang/de/demo/demo.xml | 0 .../conf => conf/rayo}/lang/de/vm/sounds.xml | 0 .../conf => conf/rayo}/lang/de/vm/tts.xml | 0 .../rayo}/lang/en/demo/demo-ivr.xml | 0 .../conf => conf/rayo}/lang/en/demo/demo.xml | 0 .../rayo}/lang/en/demo/funnies.xml | 0 .../rayo}/lang/en/demo/new-demo-ivr.xml | 0 .../conf => conf/rayo}/lang/en/dir/sounds.xml | 0 .../conf => conf/rayo}/lang/en/dir/tts.xml | 0 .../conf => conf/rayo}/lang/en/en.xml | 0 .../conf => conf/rayo}/lang/en/ivr/sounds.xml | 0 .../conf => conf/rayo}/lang/en/vm/sounds.xml | 0 .../conf => conf/rayo}/lang/en/vm/tts.xml | 0 .../rayo}/lang/en/vm/voicemail_ivr.xml | 0 .../rayo}/lang/es/demo/demo-es-ES.xml | 0 .../rayo}/lang/es/demo/demo-es-MX.xml | 0 .../rayo}/lang/es/demo/demo-ivr-es-ES.xml | 0 .../rayo}/lang/es/demo/demo-ivr-es-MX.xml | 0 .../rayo}/lang/es/dir/sounds-es-ES.xml | 0 .../rayo}/lang/es/dir/sounds-es-MX.xml | 0 .../rayo}/lang/es/dir/tts-es-ES.xml | 0 .../rayo}/lang/es/dir/tts-es-MX.xml | 0 .../conf => conf/rayo}/lang/es/es_ES.xml | 0 .../conf => conf/rayo}/lang/es/es_MX.xml | 0 .../rayo}/lang/es/vm/sounds-es-ES.xml | 0 .../rayo}/lang/es/vm/sounds-es-MX.xml | 0 .../rayo}/lang/es/vm/tts-es-ES.xml | 0 .../rayo}/lang/es/vm/tts-es-MX.xml | 0 .../conf => conf/rayo}/lang/fr/demo/demo.xml | 0 .../conf => conf/rayo}/lang/fr/dir/sounds.xml | 0 .../conf => conf/rayo}/lang/fr/dir/tts.xml | 0 .../conf => conf/rayo}/lang/fr/fr.xml | 0 .../conf => conf/rayo}/lang/fr/vm/sounds.xml | 0 .../rayo}/lang/he/demo/demo-ivr.xml | 0 .../conf => conf/rayo}/lang/he/demo/demo.xml | 0 .../conf => conf/rayo}/lang/he/dir/sounds.xml | 0 .../conf => conf/rayo}/lang/he/he.xml | 0 .../conf => conf/rayo}/lang/he/vm/sounds.xml | 0 .../rayo}/lang/pt/demo/demo-ivr-pt-BR.xml | 0 .../rayo}/lang/pt/demo/demo-ivr-pt-PT.xml | 0 .../rayo}/lang/pt/demo/demo-pt-BR.xml | 0 .../rayo}/lang/pt/demo/demo-pt-PT.xml | 0 .../rayo}/lang/pt/dir/sounds-pt-BR.xml | 0 .../rayo}/lang/pt/dir/sounds-pt-PT.xml | 0 .../rayo}/lang/pt/dir/tts-pt-BR.xml | 0 .../rayo}/lang/pt/dir/tts-pt-PT.xml | 0 .../conf => conf/rayo}/lang/pt/pt_BR.xml | 0 .../conf => conf/rayo}/lang/pt/pt_PT.xml | 0 .../rayo}/lang/pt/vm/sounds-pt-BR.xml | 0 .../rayo}/lang/pt/vm/sounds-pt-PT.xml | 0 .../rayo}/lang/pt/vm/tts-pt-BR.xml | 0 .../rayo}/lang/pt/vm/tts-pt-PT.xml | 0 .../rayo}/lang/ru/demo/demo-ivr.xml | 0 .../conf => conf/rayo}/lang/ru/demo/demo.xml | 0 .../conf => conf/rayo}/lang/ru/dir/sounds.xml | 0 .../conf => conf/rayo}/lang/ru/dir/tts.xml | 0 .../conf => conf/rayo}/lang/ru/ru.xml | 0 .../conf => conf/rayo}/lang/ru/vm/sounds.xml | 0 .../conf => conf/rayo}/lang/ru/vm/tts.xml | 0 .../mod_rayo/conf => conf/rayo}/mime.types | 0 .../rayo}/sip_profiles/external.xml | 0 .../rayo}/sip_profiles/external/example.xml | 0 .../mod_rayo/conf => conf/rayo}/vars.xml | 0 95 files changed, 43 insertions(+) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/acl.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/cdr_csv.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/conference.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/console.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/distributor.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/event_socket.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/hash.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/http_cache.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/local_stream.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/logfile.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/lua.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/memcache.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/modules.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/pocketsphinx.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/post_load_modules.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/presence_map.conf.xml (100%) create mode 100644 conf/rayo/autoload_configs/rayo.conf.xml rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/shout.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/sofia.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/spandsp.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/ssml.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/switch.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/autoload_configs/timezones.conf.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/cacert.pem (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/dialplan/public.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/directory/default.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/directory/default/usera.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/directory/default/userb.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/directory/default/userc.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/directory/default/userd.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/freeswitch.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/de/de.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/de/demo/demo.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/de/vm/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/de/vm/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/demo/demo-ivr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/demo/demo.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/demo/funnies.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/demo/new-demo-ivr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/dir/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/dir/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/en.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/ivr/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/vm/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/vm/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/en/vm/voicemail_ivr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/demo/demo-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/demo/demo-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/demo/demo-ivr-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/demo/demo-ivr-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/dir/sounds-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/dir/sounds-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/dir/tts-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/dir/tts-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/es_ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/es_MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/vm/sounds-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/vm/sounds-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/vm/tts-es-ES.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/es/vm/tts-es-MX.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/fr/demo/demo.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/fr/dir/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/fr/dir/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/fr/fr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/fr/vm/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/he/demo/demo-ivr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/he/demo/demo.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/he/dir/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/he/he.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/he/vm/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/demo/demo-ivr-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/demo/demo-ivr-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/demo/demo-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/demo/demo-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/dir/sounds-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/dir/sounds-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/dir/tts-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/dir/tts-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/pt_BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/pt_PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/vm/sounds-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/vm/sounds-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/vm/tts-pt-BR.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/pt/vm/tts-pt-PT.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/demo/demo-ivr.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/demo/demo.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/dir/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/dir/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/ru.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/vm/sounds.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/lang/ru/vm/tts.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/mime.types (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/sip_profiles/external.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/sip_profiles/external/example.xml (100%) rename {src/mod/event_handlers/mod_rayo/conf => conf/rayo}/vars.xml (100%) diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/acl.conf.xml b/conf/rayo/autoload_configs/acl.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/acl.conf.xml rename to conf/rayo/autoload_configs/acl.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/cdr_csv.conf.xml b/conf/rayo/autoload_configs/cdr_csv.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/cdr_csv.conf.xml rename to conf/rayo/autoload_configs/cdr_csv.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/conference.conf.xml b/conf/rayo/autoload_configs/conference.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/conference.conf.xml rename to conf/rayo/autoload_configs/conference.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/console.conf.xml b/conf/rayo/autoload_configs/console.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/console.conf.xml rename to conf/rayo/autoload_configs/console.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/distributor.conf.xml b/conf/rayo/autoload_configs/distributor.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/distributor.conf.xml rename to conf/rayo/autoload_configs/distributor.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/event_socket.conf.xml b/conf/rayo/autoload_configs/event_socket.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/event_socket.conf.xml rename to conf/rayo/autoload_configs/event_socket.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/hash.conf.xml b/conf/rayo/autoload_configs/hash.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/hash.conf.xml rename to conf/rayo/autoload_configs/hash.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/http_cache.conf.xml b/conf/rayo/autoload_configs/http_cache.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/http_cache.conf.xml rename to conf/rayo/autoload_configs/http_cache.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/local_stream.conf.xml b/conf/rayo/autoload_configs/local_stream.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/local_stream.conf.xml rename to conf/rayo/autoload_configs/local_stream.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/logfile.conf.xml b/conf/rayo/autoload_configs/logfile.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/logfile.conf.xml rename to conf/rayo/autoload_configs/logfile.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/lua.conf.xml b/conf/rayo/autoload_configs/lua.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/lua.conf.xml rename to conf/rayo/autoload_configs/lua.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/memcache.conf.xml b/conf/rayo/autoload_configs/memcache.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/memcache.conf.xml rename to conf/rayo/autoload_configs/memcache.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/modules.conf.xml b/conf/rayo/autoload_configs/modules.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/modules.conf.xml rename to conf/rayo/autoload_configs/modules.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/pocketsphinx.conf.xml b/conf/rayo/autoload_configs/pocketsphinx.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/pocketsphinx.conf.xml rename to conf/rayo/autoload_configs/pocketsphinx.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/post_load_modules.conf.xml b/conf/rayo/autoload_configs/post_load_modules.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/post_load_modules.conf.xml rename to conf/rayo/autoload_configs/post_load_modules.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/presence_map.conf.xml b/conf/rayo/autoload_configs/presence_map.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/presence_map.conf.xml rename to conf/rayo/autoload_configs/presence_map.conf.xml diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml new file mode 100644 index 0000000000..f9ac49b99d --- /dev/null +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/shout.conf.xml b/conf/rayo/autoload_configs/shout.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/shout.conf.xml rename to conf/rayo/autoload_configs/shout.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/sofia.conf.xml b/conf/rayo/autoload_configs/sofia.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/sofia.conf.xml rename to conf/rayo/autoload_configs/sofia.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/spandsp.conf.xml b/conf/rayo/autoload_configs/spandsp.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/spandsp.conf.xml rename to conf/rayo/autoload_configs/spandsp.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/ssml.conf.xml b/conf/rayo/autoload_configs/ssml.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/ssml.conf.xml rename to conf/rayo/autoload_configs/ssml.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/switch.conf.xml b/conf/rayo/autoload_configs/switch.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/switch.conf.xml rename to conf/rayo/autoload_configs/switch.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/timezones.conf.xml b/conf/rayo/autoload_configs/timezones.conf.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/autoload_configs/timezones.conf.xml rename to conf/rayo/autoload_configs/timezones.conf.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/cacert.pem b/conf/rayo/cacert.pem similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/cacert.pem rename to conf/rayo/cacert.pem diff --git a/src/mod/event_handlers/mod_rayo/conf/dialplan/public.xml b/conf/rayo/dialplan/public.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/dialplan/public.xml rename to conf/rayo/dialplan/public.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/directory/default.xml b/conf/rayo/directory/default.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/directory/default.xml rename to conf/rayo/directory/default.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/directory/default/usera.xml b/conf/rayo/directory/default/usera.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/directory/default/usera.xml rename to conf/rayo/directory/default/usera.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/directory/default/userb.xml b/conf/rayo/directory/default/userb.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/directory/default/userb.xml rename to conf/rayo/directory/default/userb.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/directory/default/userc.xml b/conf/rayo/directory/default/userc.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/directory/default/userc.xml rename to conf/rayo/directory/default/userc.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/directory/default/userd.xml b/conf/rayo/directory/default/userd.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/directory/default/userd.xml rename to conf/rayo/directory/default/userd.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/freeswitch.xml b/conf/rayo/freeswitch.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/freeswitch.xml rename to conf/rayo/freeswitch.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/de/de.xml b/conf/rayo/lang/de/de.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/de/de.xml rename to conf/rayo/lang/de/de.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/de/demo/demo.xml b/conf/rayo/lang/de/demo/demo.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/de/demo/demo.xml rename to conf/rayo/lang/de/demo/demo.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/de/vm/sounds.xml b/conf/rayo/lang/de/vm/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/de/vm/sounds.xml rename to conf/rayo/lang/de/vm/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/de/vm/tts.xml b/conf/rayo/lang/de/vm/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/de/vm/tts.xml rename to conf/rayo/lang/de/vm/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/demo/demo-ivr.xml b/conf/rayo/lang/en/demo/demo-ivr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/demo/demo-ivr.xml rename to conf/rayo/lang/en/demo/demo-ivr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/demo/demo.xml b/conf/rayo/lang/en/demo/demo.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/demo/demo.xml rename to conf/rayo/lang/en/demo/demo.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/demo/funnies.xml b/conf/rayo/lang/en/demo/funnies.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/demo/funnies.xml rename to conf/rayo/lang/en/demo/funnies.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/demo/new-demo-ivr.xml b/conf/rayo/lang/en/demo/new-demo-ivr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/demo/new-demo-ivr.xml rename to conf/rayo/lang/en/demo/new-demo-ivr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/dir/sounds.xml b/conf/rayo/lang/en/dir/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/dir/sounds.xml rename to conf/rayo/lang/en/dir/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/dir/tts.xml b/conf/rayo/lang/en/dir/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/dir/tts.xml rename to conf/rayo/lang/en/dir/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/en.xml b/conf/rayo/lang/en/en.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/en.xml rename to conf/rayo/lang/en/en.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/ivr/sounds.xml b/conf/rayo/lang/en/ivr/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/ivr/sounds.xml rename to conf/rayo/lang/en/ivr/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/vm/sounds.xml b/conf/rayo/lang/en/vm/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/vm/sounds.xml rename to conf/rayo/lang/en/vm/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/vm/tts.xml b/conf/rayo/lang/en/vm/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/vm/tts.xml rename to conf/rayo/lang/en/vm/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/en/vm/voicemail_ivr.xml b/conf/rayo/lang/en/vm/voicemail_ivr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/en/vm/voicemail_ivr.xml rename to conf/rayo/lang/en/vm/voicemail_ivr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-es-ES.xml b/conf/rayo/lang/es/demo/demo-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-es-ES.xml rename to conf/rayo/lang/es/demo/demo-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-es-MX.xml b/conf/rayo/lang/es/demo/demo-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-es-MX.xml rename to conf/rayo/lang/es/demo/demo-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-ivr-es-ES.xml b/conf/rayo/lang/es/demo/demo-ivr-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-ivr-es-ES.xml rename to conf/rayo/lang/es/demo/demo-ivr-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-ivr-es-MX.xml b/conf/rayo/lang/es/demo/demo-ivr-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/demo/demo-ivr-es-MX.xml rename to conf/rayo/lang/es/demo/demo-ivr-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/dir/sounds-es-ES.xml b/conf/rayo/lang/es/dir/sounds-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/dir/sounds-es-ES.xml rename to conf/rayo/lang/es/dir/sounds-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/dir/sounds-es-MX.xml b/conf/rayo/lang/es/dir/sounds-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/dir/sounds-es-MX.xml rename to conf/rayo/lang/es/dir/sounds-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/dir/tts-es-ES.xml b/conf/rayo/lang/es/dir/tts-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/dir/tts-es-ES.xml rename to conf/rayo/lang/es/dir/tts-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/dir/tts-es-MX.xml b/conf/rayo/lang/es/dir/tts-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/dir/tts-es-MX.xml rename to conf/rayo/lang/es/dir/tts-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/es_ES.xml b/conf/rayo/lang/es/es_ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/es_ES.xml rename to conf/rayo/lang/es/es_ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/es_MX.xml b/conf/rayo/lang/es/es_MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/es_MX.xml rename to conf/rayo/lang/es/es_MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/vm/sounds-es-ES.xml b/conf/rayo/lang/es/vm/sounds-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/vm/sounds-es-ES.xml rename to conf/rayo/lang/es/vm/sounds-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/vm/sounds-es-MX.xml b/conf/rayo/lang/es/vm/sounds-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/vm/sounds-es-MX.xml rename to conf/rayo/lang/es/vm/sounds-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/vm/tts-es-ES.xml b/conf/rayo/lang/es/vm/tts-es-ES.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/vm/tts-es-ES.xml rename to conf/rayo/lang/es/vm/tts-es-ES.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/es/vm/tts-es-MX.xml b/conf/rayo/lang/es/vm/tts-es-MX.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/es/vm/tts-es-MX.xml rename to conf/rayo/lang/es/vm/tts-es-MX.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/fr/demo/demo.xml b/conf/rayo/lang/fr/demo/demo.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/fr/demo/demo.xml rename to conf/rayo/lang/fr/demo/demo.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/fr/dir/sounds.xml b/conf/rayo/lang/fr/dir/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/fr/dir/sounds.xml rename to conf/rayo/lang/fr/dir/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/fr/dir/tts.xml b/conf/rayo/lang/fr/dir/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/fr/dir/tts.xml rename to conf/rayo/lang/fr/dir/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/fr/fr.xml b/conf/rayo/lang/fr/fr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/fr/fr.xml rename to conf/rayo/lang/fr/fr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/fr/vm/sounds.xml b/conf/rayo/lang/fr/vm/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/fr/vm/sounds.xml rename to conf/rayo/lang/fr/vm/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/he/demo/demo-ivr.xml b/conf/rayo/lang/he/demo/demo-ivr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/he/demo/demo-ivr.xml rename to conf/rayo/lang/he/demo/demo-ivr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/he/demo/demo.xml b/conf/rayo/lang/he/demo/demo.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/he/demo/demo.xml rename to conf/rayo/lang/he/demo/demo.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/he/dir/sounds.xml b/conf/rayo/lang/he/dir/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/he/dir/sounds.xml rename to conf/rayo/lang/he/dir/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/he/he.xml b/conf/rayo/lang/he/he.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/he/he.xml rename to conf/rayo/lang/he/he.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/he/vm/sounds.xml b/conf/rayo/lang/he/vm/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/he/vm/sounds.xml rename to conf/rayo/lang/he/vm/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-ivr-pt-BR.xml b/conf/rayo/lang/pt/demo/demo-ivr-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-ivr-pt-BR.xml rename to conf/rayo/lang/pt/demo/demo-ivr-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-ivr-pt-PT.xml b/conf/rayo/lang/pt/demo/demo-ivr-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-ivr-pt-PT.xml rename to conf/rayo/lang/pt/demo/demo-ivr-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-pt-BR.xml b/conf/rayo/lang/pt/demo/demo-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-pt-BR.xml rename to conf/rayo/lang/pt/demo/demo-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-pt-PT.xml b/conf/rayo/lang/pt/demo/demo-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/demo/demo-pt-PT.xml rename to conf/rayo/lang/pt/demo/demo-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/sounds-pt-BR.xml b/conf/rayo/lang/pt/dir/sounds-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/sounds-pt-BR.xml rename to conf/rayo/lang/pt/dir/sounds-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/sounds-pt-PT.xml b/conf/rayo/lang/pt/dir/sounds-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/sounds-pt-PT.xml rename to conf/rayo/lang/pt/dir/sounds-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/tts-pt-BR.xml b/conf/rayo/lang/pt/dir/tts-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/tts-pt-BR.xml rename to conf/rayo/lang/pt/dir/tts-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/tts-pt-PT.xml b/conf/rayo/lang/pt/dir/tts-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/dir/tts-pt-PT.xml rename to conf/rayo/lang/pt/dir/tts-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/pt_BR.xml b/conf/rayo/lang/pt/pt_BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/pt_BR.xml rename to conf/rayo/lang/pt/pt_BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/pt_PT.xml b/conf/rayo/lang/pt/pt_PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/pt_PT.xml rename to conf/rayo/lang/pt/pt_PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/sounds-pt-BR.xml b/conf/rayo/lang/pt/vm/sounds-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/sounds-pt-BR.xml rename to conf/rayo/lang/pt/vm/sounds-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/sounds-pt-PT.xml b/conf/rayo/lang/pt/vm/sounds-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/sounds-pt-PT.xml rename to conf/rayo/lang/pt/vm/sounds-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/tts-pt-BR.xml b/conf/rayo/lang/pt/vm/tts-pt-BR.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/tts-pt-BR.xml rename to conf/rayo/lang/pt/vm/tts-pt-BR.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/tts-pt-PT.xml b/conf/rayo/lang/pt/vm/tts-pt-PT.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/pt/vm/tts-pt-PT.xml rename to conf/rayo/lang/pt/vm/tts-pt-PT.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/demo/demo-ivr.xml b/conf/rayo/lang/ru/demo/demo-ivr.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/demo/demo-ivr.xml rename to conf/rayo/lang/ru/demo/demo-ivr.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/demo/demo.xml b/conf/rayo/lang/ru/demo/demo.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/demo/demo.xml rename to conf/rayo/lang/ru/demo/demo.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/dir/sounds.xml b/conf/rayo/lang/ru/dir/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/dir/sounds.xml rename to conf/rayo/lang/ru/dir/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/dir/tts.xml b/conf/rayo/lang/ru/dir/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/dir/tts.xml rename to conf/rayo/lang/ru/dir/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/ru.xml b/conf/rayo/lang/ru/ru.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/ru.xml rename to conf/rayo/lang/ru/ru.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/vm/sounds.xml b/conf/rayo/lang/ru/vm/sounds.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/vm/sounds.xml rename to conf/rayo/lang/ru/vm/sounds.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/lang/ru/vm/tts.xml b/conf/rayo/lang/ru/vm/tts.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/lang/ru/vm/tts.xml rename to conf/rayo/lang/ru/vm/tts.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/mime.types b/conf/rayo/mime.types similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/mime.types rename to conf/rayo/mime.types diff --git a/src/mod/event_handlers/mod_rayo/conf/sip_profiles/external.xml b/conf/rayo/sip_profiles/external.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/sip_profiles/external.xml rename to conf/rayo/sip_profiles/external.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/sip_profiles/external/example.xml b/conf/rayo/sip_profiles/external/example.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/sip_profiles/external/example.xml rename to conf/rayo/sip_profiles/external/example.xml diff --git a/src/mod/event_handlers/mod_rayo/conf/vars.xml b/conf/rayo/vars.xml similarity index 100% rename from src/mod/event_handlers/mod_rayo/conf/vars.xml rename to conf/rayo/vars.xml From b93f35ec7630908c135a10fcbdbf52d2470e61a2 Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Wed, 3 Jul 2013 14:55:13 -0400 Subject: [PATCH 041/278] add missing modules --- build/modules.conf.in | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/build/modules.conf.in b/build/modules.conf.in index 81ae32f4c2..d60b1211ef 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -26,10 +26,14 @@ applications/mod_httapi #applications/mod_lcr #applications/mod_memcache #applications/mod_mongo +#applications/mod_mp4 #applications/mod_nibblebill +#applications/mod_oreka #applications/mod_osp +#applications/mod_rad_auth #applications/mod_redis #applications/mod_rss +#applications/mod_sonar applications/mod_sms #applications/mod_snapshot #applications/mod_snipe_hunt @@ -63,17 +67,20 @@ codecs/mod_h26x codecs/mod_vp8 #codecs/mod_ilbc #codecs/mod_isac +#codecs/mod_mp4v #codecs/mod_opus #codecs/mod_sangoma_codec #codecs/mod_silk #codecs/mod_siren codecs/mod_speex +#codecs/mod_theora dialplans/mod_dialplan_asterisk #dialplans/mod_dialplan_directory dialplans/mod_dialplan_xml #directories/mod_ldap #endpoints/mod_alsa #endpoints/mod_dingaling +#endpoints/mod_gsmopen #endpoints/mod_h323 #endpoints/mod_html5 #endpoints/mod_khomp @@ -81,9 +88,10 @@ endpoints/mod_loopback #endpoints/mod_opal #endpoints/mod_portaudio #endpoints/mod_rtmp -#endpoints/mod_skinny +endpoints/mod_skinny #endpoints/mod_skypopen endpoints/mod_sofia +#endpoints/mod_unicall event_handlers/mod_cdr_csv #event_handlers/mod_cdr_mongodb #event_handlers/mod_cdr_pg_csv @@ -92,6 +100,7 @@ event_handlers/mod_cdr_sqlite #event_handlers/mod_event_multicast event_handlers/mod_event_socket #event_handlers/mod_event_zmq +#event_handlers/mod_json_cdr #event_handlers/mod_radius_cdr #event_handlers/mod_rayo #event_handlers/mod_snmp @@ -117,10 +126,13 @@ loggers/mod_syslog #say/mod_say_de say/mod_say_en #say/mod_say_es +#say/mod_say_fa #say/mod_say_fr #say/mod_say_he +#say/mod_say_hr #say/mod_say_hu #say/mod_say_it +#say/mod_say_ja #say/mod_say_nl #say/mod_say_pl #say/mod_say_pt @@ -140,4 +152,4 @@ xml_int/mod_xml_scgi #../../libs/openzap/mod_openzap ## Experimental Modules (don't cry if they're broken) -#../../contrib/mod/xml_int/mod_xml_odbc +#../../contrib/mod/xml_int/mod_xml_odbc \ No newline at end of file From bf89fbd81d50c94857ce6596f3445d3fa2037511 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 11:21:03 -0500 Subject: [PATCH 042/278] --resolve FS-5432 --- conf/vanilla/sip_profiles/internal.xml | 4 ++++ src/mod/endpoints/mod_sofia/conf/sofia.conf.xml | 4 ++++ src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 8 ++++++++ src/mod/endpoints/mod_sofia/sofia_presence.c | 13 +++++++++++++ 5 files changed, 30 insertions(+) diff --git a/conf/vanilla/sip_profiles/internal.xml b/conf/vanilla/sip_profiles/internal.xml index df838406d1..423ec2a60a 100644 --- a/conf/vanilla/sip_profiles/internal.xml +++ b/conf/vanilla/sip_profiles/internal.xml @@ -311,6 +311,10 @@ + + + + diff --git a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml index bf6bee7b28..5bd3a6dc12 100644 --- a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml +++ b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml @@ -353,6 +353,10 @@ + + + + diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 90e8dad19e..365f585787 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -672,6 +672,7 @@ struct sofia_profile { su_strlst_t *tls_verify_in_subjects; uint32_t sip_force_expires; uint32_t sip_expires_max_deviation; + uint32_t sip_subscription_max_deviation; int ireg_seconds; sofia_paid_type_t paid_type; uint32_t rtp_digit_delay; diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 54b683441c..d226b928f4 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -3685,6 +3685,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) profile->rtp_digit_delay = 40; profile->sip_force_expires = 0; profile->sip_expires_max_deviation = 0; + profile->sip_subscription_max_deviation = 0; profile->tls_version = 0; profile->tls_timeout = 300; profile->mflags = MFLAG_REFER | MFLAG_REGISTER; @@ -4635,6 +4636,13 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else { profile->sip_expires_max_deviation = 0; } + } else if (!strcasecmp(var, "sip-subscription-max-deviation")) { + int32_t sip_subscription_max_deviation = atoi(val); + if (sip_subscription_max_deviation >= 0) { + profile->sip_subscription_max_deviation = sip_subscription_max_deviation; + } else { + profile->sip_subscription_max_deviation = 0; + } } else if (!strcasecmp(var, "reuse-connections")) { switch_bool_t value = switch_true(val); if (!value) { diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 91ca35e879..47203d476d 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -3516,6 +3516,7 @@ void sofia_presence_handle_sip_i_subscribe(int status, long exp_delta = 0; char exp_delta_str[30] = ""; + uint32_t sub_max_deviation_var = 0; sip_to_t const *to; const char *from_user = NULL, *from_host = NULL; const char *to_user = NULL, *to_host = NULL; @@ -3615,6 +3616,18 @@ void sofia_presence_handle_sip_i_subscribe(int status, } } + if ((sub_max_deviation_var = profile->sip_subscription_max_deviation)) { + if (sub_max_deviation_var > 0) { + int sub_deviation; + srand( (unsigned) ( (unsigned)(intptr_t)switch_thread_self() + switch_micro_time_now() ) ); + /* random negative number between 0 and negative sub_max_deviation_var: */ + sub_deviation = ( rand() % sub_max_deviation_var ) - sub_max_deviation_var; + if ( (exp_delta + sub_deviation) > 45 ) { + exp_delta += sub_deviation; + } + } + } + if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DELTA %ld\n", exp_delta); } From 51fd5a6cee083b18f467c66da40574e38ba426b3 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 11:21:47 -0500 Subject: [PATCH 043/278] --resolve FS-5426 --- conf/vanilla/sip_profiles/internal.xml | 4 ++++ src/mod/endpoints/mod_sofia/conf/sofia.conf.xml | 4 ++++ src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 5 ++++- src/mod/endpoints/mod_sofia/sofia_reg.c | 8 ++++++++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/conf/vanilla/sip_profiles/internal.xml b/conf/vanilla/sip_profiles/internal.xml index 423ec2a60a..4ef9bde0d2 100644 --- a/conf/vanilla/sip_profiles/internal.xml +++ b/conf/vanilla/sip_profiles/internal.xml @@ -178,6 +178,10 @@ + + + + diff --git a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml index 5bd3a6dc12..411ea3e44e 100644 --- a/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml +++ b/src/mod/endpoints/mod_sofia/conf/sofia.conf.xml @@ -205,6 +205,10 @@ + + + diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 365f585787..ac8b4366f7 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -230,6 +230,7 @@ typedef enum { PFLAG_DISABLE_SRV503, PFLAG_DISABLE_NAPTR, PFLAG_NAT_OPTIONS_PING, + PFLAG_UDP_NAT_OPTIONS_PING, PFLAG_ALL_REG_OPTIONS_PING, PFLAG_MESSAGE_QUERY_ON_REGISTER, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER, diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index d226b928f4..04d17cfd1d 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -4309,10 +4309,13 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else if (!strcasecmp(var, "contact-user")) { profile->contact_user = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "nat-options-ping")) { - if (switch_true(val)) { + if (!strcasecmp(val, "udp-only")) { + sofia_set_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING); + } else if (switch_true(val)) { sofia_set_pflag(profile, PFLAG_NAT_OPTIONS_PING); } else { sofia_clear_pflag(profile, PFLAG_NAT_OPTIONS_PING); + sofia_clear_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING); } } else if (!strcasecmp(var, "all-reg-options-ping")) { if (switch_true(val)) { diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 2bdf11e2d1..aba34a321d 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -822,6 +822,14 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) " from sip_registrations where hostname='%s' and " "profile_name='%s'", mod_sofia_globals.hostname, profile->name); + sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile); + switch_safe_free(sql); + } else if (sofia_test_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING)) { + sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,rpid," + "expires,user_agent,server_user,server_host,profile_name" + " from sip_registrations where status like '%%UDP-NAT%%' " + "and hostname='%s' and profile_name='%s'", mod_sofia_globals.hostname, profile->name); + sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile); switch_safe_free(sql); } else if (sofia_test_pflag(profile, PFLAG_NAT_OPTIONS_PING)) { From 3f84fc99e9645002a582166d363a4538f00e1660 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 11:59:25 -0500 Subject: [PATCH 044/278] --resolve FS-4461 Add record_seconds, record_ms and record_samples variables done recording. original patch not used due to refactor in code. --- src/switch_ivr_async.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 18d4a3fcca..8e5a451e7e 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -1212,6 +1212,12 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s switch_event_fire(&event); } + if (read_impl.actual_samples_per_second) { + switch_channel_set_variable_printf(channel, "record_seconds", "%d", rh->fh->samples_out / read_impl.actual_samples_per_second); + switch_channel_set_variable_printf(channel, "record_ms", "%d", rh->fh->samples_out / (read_impl.actual_samples_per_second / 1000)); + } + switch_channel_set_variable_printf(channel, "record_samples", "%d", rh->fh->samples_out); + switch_channel_execute_on(channel, SWITCH_RECORD_POST_PROCESS_EXEC_APP_VARIABLE); if ((var = switch_channel_get_variable(channel, SWITCH_RECORD_POST_PROCESS_EXEC_API_VARIABLE))) { From 4f71d38e179871b81be57969a8b59c48172ab1ef Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 14:59:53 -0500 Subject: [PATCH 045/278] --resolve FS-5569 --- .../applications/mod_commands/mod_commands.c | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index ef34a985f7..2acead56c8 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -4543,6 +4543,34 @@ SWITCH_STANDARD_API(alias_function) return SWITCH_STATUS_SUCCESS; } +#define COALESCE_SYNTAX "[^^],,..." +SWITCH_STANDARD_API(coalesce_function) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + char *data = (char *) cmd; + char *mydata = NULL, *argv[256] = { 0 }; + int argc = -1; + + if (data && *data && (mydata = strdup(data))) { + argc = switch_separate_string(mydata, ',', argv, + (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc > 0) { + for (int i = 0; i < argc; i++) { + if (argv[i] && *argv[i]) { + stream->write_function(stream, argv[i]); + status = SWITCH_STATUS_SUCCESS; + break; + } + } + } else if (argc <= 0){ + stream->write_function(stream, "-USAGE: %s\n", COALESCE_SYNTAX); + } + + return status; +} + #define SHOW_SYNTAX "codec|endpoint|application|api|dialplan|file|timer|calls [count]|channels [count|like ]|calls|detailed_calls|bridged_calls|detailed_bridged_calls|aliases|complete|chat|management|modules|nat_map|say|interfaces|interface_types|tasks|limits|status" SWITCH_STANDARD_API(show_function) { @@ -5982,7 +6010,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "acl", "Compare an ip to an acl list", acl_function, " "); - SWITCH_ADD_API(commands_api_interface, "alias", "Alias", alias_function, ALIAS_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "alias", "Alias", alias_function, ALIAS_SYNTAX); SWITCH_ADD_API(commands_api_interface, "coalesce", "Return first nonempty parameter", coalesce_function, COALESCE_SYNTAX); SWITCH_ADD_API(commands_api_interface, "banner", "Return the system banner", banner_function, ""); SWITCH_ADD_API(commands_api_interface, "bgapi", "Execute an api command in a thread", bgapi_function, "[ ]"); SWITCH_ADD_API(commands_api_interface, "bg_system", "Execute a system command in the background", bg_system_function, SYSTEM_SYNTAX); @@ -6108,6 +6136,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add alias add"); switch_console_set_complete("add alias del"); + switch_console_set_complete("add coalesce"); switch_console_set_complete("add complete add"); switch_console_set_complete("add complete del"); switch_console_set_complete("add db_cache status"); From e0210d75e3b28d69cf7c30e809bfef72c8450850 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 15:01:03 -0500 Subject: [PATCH 046/278] --resolve FS-5558 --- .../applications/mod_dptools/mod_dptools.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index c02e162b85..fc1806e3c0 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -1519,6 +1519,33 @@ SWITCH_STANDARD_APP(unset_function) } } +SWITCH_STANDARD_APP(multiunset_function) +{ + char delim = ' '; + char *arg = (char *) data; + + if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') { + arg += 2; + delim = *arg++; + } + + if (arg) { + char *array[256] = {0}; + int i, argc; + + arg = switch_core_session_strdup(session, arg); + argc = switch_split(arg, delim, array); + + for(i = 0; i < argc; i++) { + switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL); + } + + } else { + switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL); + } +} + + SWITCH_STANDARD_APP(log_function) { char *level, *log_str; @@ -5506,6 +5533,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) "=", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "unset", "Unset a channel variable", UNSET_LONG_DESC, unset_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "multiunset", "Unset many channel variables", SET_LONG_DESC, multiunset_function, "[^^] ", + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "ring_ready", "Indicate Ring_Ready", "Indicate Ring_Ready on a channel.", ring_ready_function, "", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "remove_bugs", "Remove media bugs", "Remove all media bugs from a channel.", remove_bugs_function, "[]", SAF_NONE); SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA); From 2908e3acd50c54ec3e7f562bd6b7c097359d6670 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Thu, 4 Jul 2013 12:28:35 -0500 Subject: [PATCH 047/278] fix stupid c89 for windows --- src/mod/applications/mod_commands/mod_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 2acead56c8..2d0d02d7fe 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -4557,7 +4557,8 @@ SWITCH_STANDARD_API(coalesce_function) } if (argc > 0) { - for (int i = 0; i < argc; i++) { + int i; + for (i = 0; i < argc; i++) { if (argv[i] && *argv[i]) { stream->write_function(stream, argv[i]); status = SWITCH_STATUS_SUCCESS; From f30e40a80b5d6f03824293be97d7739a221727ee Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 3 Jul 2013 16:07:15 -0500 Subject: [PATCH 048/278] --resolve FS-3922 --- src/include/switch_core.h | 5 +++++ src/switch_xml.c | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 416ed185de..c3583e9555 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -2557,6 +2557,11 @@ SWITCH_DECLARE(int) switch_core_cert_expand_fingerprint(dtls_fingerprint_t *fp, SWITCH_DECLARE(int) switch_core_cert_verify(dtls_fingerprint_t *fp); SWITCH_DECLARE(switch_status_t) switch_core_session_refresh_video(switch_core_session_t *session); +SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait); +SWITCH_DECLARE(int) switch_stream_system_fork(const char *cmd, switch_stream_handle_t *stream); +SWITCH_DECLARE(int) switch_stream_system(const char *cmd, switch_stream_handle_t *stream); + + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/switch_xml.c b/src/switch_xml.c index 018c01dd47..c13e33bea4 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -105,6 +105,42 @@ void globfree(glob_t *); /* Use UTF-8 as the general encoding */ static switch_bool_t USE_UTF_8_ENCODING = SWITCH_TRUE; +static void preprocess_exec_set(char *keyval) +{ + char *key = keyval; + char *val = strchr(key, '='); + + if (val) { + char *ve = val++; + while (*val && *val == ' ') { + val++; + } + *ve-- = '\0'; + while (*ve && *ve == ' ') { + *ve-- = '\0'; + } + } + + if (key && val) { + switch_stream_handle_t exec_result = { 0 }; + SWITCH_STANDARD_STREAM(exec_result); + if (switch_stream_system_fork(val, &exec_result) == 0) { + if (!zstr(exec_result.data)) { + char *tmp = (char *) exec_result.data; + tmp = &tmp[strlen(tmp)-1]; + while (tmp >= (char *) exec_result.data && ( tmp[0] == ' ' || tmp[0] == '\n') ) { + tmp[0] = '\0'; /* remove trailing spaces and newlines */ + tmp--; + } + switch_core_set_variable(key, exec_result.data); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while executing command: %s\n", val); + } + switch_safe_free(exec_result.data); + } +} + static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rlevel); typedef struct switch_xml_root *switch_xml_root_t; @@ -1457,6 +1493,8 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle switch_core_set_variable(name, val); } + } else if (!strcasecmp(tcmd, "exec-set")) { + preprocess_exec_set(targ); } else if (!strcasecmp(tcmd, "include")) { preprocess_glob(cwd, targ, write_fd, rlevel + 1); } else if (!strcasecmp(tcmd, "exec")) { @@ -1515,6 +1553,8 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle switch_core_set_variable(name, val); } + } else if (!strcasecmp(cmd, "exec-set")) { + preprocess_exec_set(arg); } else if (!strcasecmp(cmd, "include")) { preprocess_glob(cwd, arg, write_fd, rlevel + 1); } else if (!strcasecmp(cmd, "exec")) { From f7fb54d0cd22c361298c772ce025a47f7937631d Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Thu, 4 Jul 2013 22:02:47 -0500 Subject: [PATCH 049/278] fix libuuid detection --- acinclude.m4 | 1 + build/config/uuid.m4 | 17 +++++++++++++++++ configure.in | 6 +++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 build/config/uuid.m4 diff --git a/acinclude.m4 b/acinclude.m4 index aeba17b5c3..58b5500a80 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -6,6 +6,7 @@ m4_include([build/config/ac_gcc_archflag.m4]) m4_include([build/config/ac_gcc_x86_cpuid.m4]) m4_include([build/config/ax_lib_mysql.m4]) m4_include([build/config/ax_check_java.m4]) +m4_include([build/config/uuid.m4]) m4_include([build/config/erlang.m4]) m4_include([build/config/odbc.m4]) m4_include([build/config/sched_setaffinity.m4]) diff --git a/build/config/uuid.m4 b/build/config/uuid.m4 new file mode 100644 index 0000000000..ecfa719b5e --- /dev/null +++ b/build/config/uuid.m4 @@ -0,0 +1,17 @@ +AC_DEFUN([CHECK_LIBUUID], + [ + PKG_CHECK_MODULES([LIBUUID], [uuid >= 1.41.2], + [LIBUUID_FOUND=yes], [LIBUUID_FOUND=no]) + if test "$LIBUUID_FOUND" = "no" ; then + PKG_CHECK_MODULES([LIBUUID], [uuid], + [LIBUUID_FOUND=yes], [LIBUUID_FOUND=no]) + if test "$LIBUUID_FOUND" = "no" ; then + AC_MSG_ERROR([libuuid development files required]) + else + LIBUUID_INCLUDEDIR=$(pkg-config --variable=includedir uuid) + LIBUUID_CFLAGS+=" -I$LIBUUID_INCLUDEDIR/uuid " + fi + fi + AC_SUBST([LIBUUID_CFLAGS]) + AC_SUBST([LIBUUID_LIBS]) + ]) diff --git a/configure.in b/configure.in index fd52b959ff..41310491c3 100644 --- a/configure.in +++ b/configure.in @@ -405,6 +405,10 @@ if test "x$enable_core_odbc_support" != "xno"; then AC_CHECK_LIB([odbc], [SQLDisconnect],, AC_MSG_ERROR([no usable libodbc; please install unixodbc devel package or equivalent])) fi +CHECK_LIBUUID +SWITCH_AM_LDFLAGS="$LIBUUID_LIBS $SWITCH_AM_LDFLAGS" +SWITCH_AM_CFLAGS="$LIBUUID_CFLAGS $SWITCH_AM_CFLAGS" + AC_ARG_ENABLE(core-pgsql-support, [AS_HELP_STRING([--enable-core-pgsql-support], [Compile with PGSQL Support])],,[enable_core_pgsql_support="no"]) @@ -414,7 +418,7 @@ AC_PATH_PROG([PG_CONFIG], [pg_config], [no]) if test "$PG_CONFIG" != "no"; then AC_MSG_CHECKING([for PostgreSQL libraries]) POSTGRESQL_CXXFLAGS="`$PG_CONFIG --cppflags` -I`$PG_CONFIG --includedir`" - POSTGRESQL_LDFLAGS="`$PG_CONFIG --ldflags` -L`$PG_CONFIG --libdir` -lpq" + POSTGRESQL_LDFLAGS="`$PG_CONFIG --ldflags|sed 's/ -Wl,--as-needed//g'` -L`$PG_CONFIG --libdir` -lpq" POSTGRESQL_VERSION=`$PG_CONFIG --version | sed -e 's#PostgreSQL ##'` POSTGRESQL_MAJOR_VERSION=`$PG_CONFIG --version | sed -re 's#PostgreSQL ([0-9]+).[0-9]+.[0-9]+#\1#'` POSTGRESQL_MINOR_VERSION=`$PG_CONFIG --version | sed -re 's#PostgreSQL [0-9]+.([0-9]+).[0-9]+#\1#'` From 37a1f2c6334906119d8dbfec9c16f2ee45216f32 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Fri, 5 Jul 2013 09:49:28 -0500 Subject: [PATCH 050/278] fix typo --- conf/vanilla/sip_profiles/internal.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/vanilla/sip_profiles/internal.xml b/conf/vanilla/sip_profiles/internal.xml index 4ef9bde0d2..c813d99c67 100644 --- a/conf/vanilla/sip_profiles/internal.xml +++ b/conf/vanilla/sip_profiles/internal.xml @@ -180,7 +180,7 @@ - + From 9202f0398f09c040923328694a591c98199882bd Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Fri, 5 Jul 2013 10:12:45 -0500 Subject: [PATCH 051/278] Make this a warning not an error. libuuid and its associated dev packages are HIGHLY recommended. --- build/config/uuid.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/config/uuid.m4 b/build/config/uuid.m4 index ecfa719b5e..7f37c23c66 100644 --- a/build/config/uuid.m4 +++ b/build/config/uuid.m4 @@ -6,7 +6,7 @@ AC_DEFUN([CHECK_LIBUUID], PKG_CHECK_MODULES([LIBUUID], [uuid], [LIBUUID_FOUND=yes], [LIBUUID_FOUND=no]) if test "$LIBUUID_FOUND" = "no" ; then - AC_MSG_ERROR([libuuid development files required]) + AC_MSG_WARN([libuuid development package highly recommended!]) else LIBUUID_INCLUDEDIR=$(pkg-config --variable=includedir uuid) LIBUUID_CFLAGS+=" -I$LIBUUID_INCLUDEDIR/uuid " From 9ed53db590ae63ed941a9fe48e56435a903d07a0 Mon Sep 17 00:00:00 2001 From: Brian West Date: Fri, 5 Jul 2013 19:50:43 -0500 Subject: [PATCH 052/278] fix race --- src/mod/applications/mod_fifo/mod_fifo.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index c271cc39d3..0da3c398ff 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -3087,16 +3087,21 @@ SWITCH_STANDARD_APP(fifo_function) switch_channel_set_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(other_session)); switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session)); + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_initiated_bridge", "true"); + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_bridge_role", "caller"); + switch_channel_set_variable(switch_core_session_get_channel(session), "fifo_initiated_bridge", "true"); + switch_channel_set_variable(switch_core_session_get_channel(session), "fifo_bridge_role", "consumer"); + switch_ivr_multi_threaded_bridge(session, other_session, on_dtmf, other_session, session); - if (!switch_channel_test_flag(other_channel, CF_TRANSFER) || !switch_channel_up(other_channel)) { - switch_channel_set_variable(other_channel, "fifo_initiated_bridge", "true"); - switch_channel_set_variable(other_channel, "fifo_bridge_role", "caller"); + if (switch_channel_test_flag(other_channel, CF_TRANSFER) || switch_channel_up(other_channel)) { + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_initiated_bridge", NULL); + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_bridge_role", NULL); } - - if (!switch_channel_test_flag(channel, CF_TRANSFER) || !switch_channel_up(channel)) { - switch_channel_set_variable(channel, "fifo_initiated_bridge", "true"); - switch_channel_set_variable(channel, "fifo_bridge_role", "consumer"); + + if (switch_channel_test_flag(channel, CF_TRANSFER) || switch_channel_up(channel)) { + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_initiated_bridge", NULL); + switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_bridge_role", NULL); } if (outbound_id) { From 82292cced7c342d0eaadec3b0e99c1ac2b859726 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Sat, 6 Jul 2013 12:52:21 -0500 Subject: [PATCH 053/278] FS-3127 --resolve please reopen if not fixed --- .../applications/mod_callcenter/mod_callcenter.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index c758147078..9f74d2f9cc 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -562,27 +562,28 @@ char *cc_execute_sql2str(cc_queue_t *queue, switch_mutex_t *mutex, char *sql, ch switch_cache_db_handle_t *dbh = NULL; - if (!(dbh = cc_get_db_handle())) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n"); - return NULL; - } - if (mutex) { switch_mutex_lock(mutex); } else { switch_mutex_lock(globals.mutex); } + if (!(dbh = cc_get_db_handle())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n"); + goto end; + } + ret = switch_cache_db_execute_sql2str(dbh, sql, resbuf, len, NULL); +end: + switch_cache_db_release_db_handle(&dbh); + if (mutex) { switch_mutex_unlock(mutex); } else { switch_mutex_unlock(globals.mutex); } - switch_cache_db_release_db_handle(&dbh); - return ret; } From 22baa51accd3f0ba5040d1819155da6c397db30b Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Sat, 6 Jul 2013 21:37:11 -0500 Subject: [PATCH 054/278] FS-2816 --resolve --- libs/esl/fs_cli.c | 28 +++++++++++++++++++---- src/mod/loggers/mod_console/mod_console.c | 15 +++++++++++- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 0e6521908c..393fcf80d3 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -57,6 +57,7 @@ typedef struct { int debug; const char *console_fnkeys[12]; char loglevel[128]; + int log_uuid; int quiet; int batch_mode; char prompt_color[12]; @@ -595,6 +596,7 @@ static const char *usage_str = " -i, --interrupt Allow Control-c to interrupt\n" " -x, --execute=command Execute Command and Exit\n" " -l, --loglevel=command Log Level\n" + " -U, --log-uuid Include UUID in log output\n" " -q, --quiet Disable logging\n" " -r, --retry Retry connection on failure\n" " -R, --reconnect Reconnect if disconnected\n" @@ -745,10 +747,14 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) if (aok) { if (feature_level) clear_line(); if(!(global_profile->batch_mode)) { - printf("%s%s", colors[level], handle->last_event->body); + printf("%s", colors[level]); + } + if (global_profile->log_uuid && !esl_strlen_zero(userdata)) { + printf("%s ", userdata); + } + printf("%s", handle->last_event->body); + if(!(global_profile->batch_mode)) { if (!feature_level) printf("%s", ESL_SEQ_DEFAULT_COLOR); - } else { - printf("%s", handle->last_event->body); } if (feature_level) redisplay(); } @@ -757,6 +763,10 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) if(!(global_profile->batch_mode)) { SetConsoleTextAttribute(hStdout, colors[level]); } + if (global_profile->log_uuid && !esl_strlen_zero(userdata)) { + WriteFile(hStdout, userdata, strlen(userdata), &outbytes, NULL); + WriteFile(hStdout, " ", strlen(" "), &outbytes, NULL); + } WriteFile(hStdout, handle->last_event->body, len, &outbytes, NULL); if(!(global_profile->batch_mode)) { SetConsoleTextAttribute(hStdout, wOldColorAttrs); @@ -1221,6 +1231,8 @@ static void read_config(const char *dft_cfile, const char *cfile) { } } else if(!strcasecmp(var, "loglevel")) { esl_set_string(profiles[pcount-1].loglevel, val); + } else if(!strcasecmp(var, "log-uuid")) { + profiles[pcount-1].log_uuid = esl_true(val); } else if(!strcasecmp(var, "quiet")) { profiles[pcount-1].quiet = esl_true(val); } else if(!strcasecmp(var, "prompt-color")) { @@ -1282,6 +1294,7 @@ int main(int argc, char *argv[]) {"debug", 1, 0, 'd'}, {"execute", 1, 0, 'x'}, {"loglevel", 1, 0, 'l'}, + {"log-uuid", 0, 0, 'U'}, {"quiet", 0, 0, 'q'}, {"batchmode", 0, 0, 'b'}, {"retry", 0, 0, 'r'}, @@ -1303,6 +1316,7 @@ int main(int argc, char *argv[]) int argv_exec = 0; char argv_command[1024] = ""; char argv_loglevel[128] = ""; + int argv_log_uuid = 0; int argv_quiet = 0; int argv_batch = 0; int loops = 2, reconnect = 0, timeout = 0; @@ -1340,7 +1354,7 @@ int main(int argc, char *argv[]) esl_global_set_default_logger(6); /* default debug level to 6 (info) */ for(;;) { int option_index = 0; - opt = getopt_long(argc, argv, "H:U:P:S:u:p:d:x:l:t:qrRhib?n", options, &option_index); + opt = getopt_long(argc, argv, "H:P:S:u:p:d:x:l:Ut:qrRhib?n", options, &option_index); if (opt == -1) break; switch (opt) { case 'H': @@ -1383,6 +1397,9 @@ int main(int argc, char *argv[]) case 'l': esl_set_string(argv_loglevel, optarg); break; + case 'U': + argv_log_uuid = 1; + break; case 'q': argv_quiet = 1; break; @@ -1445,6 +1462,9 @@ int main(int argc, char *argv[]) esl_set_string(profile->loglevel, argv_loglevel); profile->quiet = 0; } + if (argv_log_uuid) { + profile->log_uuid = 1; + } esl_log(ESL_LOG_DEBUG, "Using profile %s [%s]\n", profile->name, profile->host); esl_set_string(prompt_color, profile->prompt_color); esl_set_string(input_text_color, profile->input_text_color); diff --git a/src/mod/loggers/mod_console/mod_console.c b/src/mod/loggers/mod_console/mod_console.c index 905029b92b..4b80add5e4 100644 --- a/src/mod/loggers/mod_console/mod_console.c +++ b/src/mod/loggers/mod_console/mod_console.c @@ -60,6 +60,7 @@ static switch_memory_pool_t *module_pool = NULL; static switch_hash_t *log_hash = NULL; static uint32_t all_level = 0; static int32_t hard_log_level = SWITCH_LOG_DEBUG; +static switch_bool_t log_uuid = SWITCH_FALSE; //static int32_t failed_write = 0; static void del_mapping(char *var) { @@ -138,6 +139,8 @@ static switch_status_t config_logger(void) #endif } else if (!strcasecmp(var, "loglevel") && !zstr(val)) { hard_log_level = switch_log_str2level(val); + } else if (!strcasecmp(var, "uuid") && switch_true(val)) { + log_uuid = SWITCH_TRUE; } } } @@ -244,11 +247,21 @@ static switch_status_t switch_console_logger(const switch_log_node_t *node, swit DWORD len = (DWORD) strlen(node->data); DWORD outbytes = 0; SetConsoleTextAttribute(hStdout, COLORS[node->level]); + if (log_uuid && !zstr(node->userdata)) { + WriteFile(hStdout, node->userdata, strlen(node->userdata), &outbytes, NULL); + WriteFile(hStdout, " ", strlen(" "), &outbytes, NULL); + } WriteFile(hStdout, node->data, len, &outbytes, NULL); SetConsoleTextAttribute(hStdout, wOldColorAttrs); #else - fprintf(handle, "%s%s%s", COLORS[node->level], node->data, SWITCH_SEQ_DEFAULT_COLOR); + if (log_uuid && !zstr(node->userdata)) { + fprintf(handle, "%s%s %s%s", COLORS[node->level], node->userdata, node->data, SWITCH_SEQ_DEFAULT_COLOR); + } else { + fprintf(handle, "%s%s%s", COLORS[node->level], node->data, SWITCH_SEQ_DEFAULT_COLOR); + } #endif + } else if (log_uuid && !zstr(node->userdata)) { + fprintf(handle, "%s %s", node->userdata, node->data); } else { fprintf(handle, "%s", node->data); } From f4a66142ce352b2d3ee41ff74026bfa986bd2126 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Mon, 8 Jul 2013 00:03:11 +0800 Subject: [PATCH 055/278] --resolve FS-5072 --- .../applications/mod_dptools/mod_dptools.c | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index fc1806e3c0..8f1057a137 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -31,6 +31,7 @@ * Luke Dashjr (OpenMethods, LLC) * Cesar Cepeda * Christopher M. Rienzo + * Seven Du * * mod_dptools.c -- Raw Audio File Streaming Application Module * @@ -2685,6 +2686,55 @@ SWITCH_STANDARD_APP(endless_playback_function) } +SWITCH_STANDARD_APP(loop_playback_function) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_status_t status = SWITCH_STATUS_SUCCESS; + const char *file = data; + int loop = 1; + + if (*file == '+') { + const char *p = ++file; + while(*file && *file++ != ' ') { } + + if (zstr(p)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing loop in data [%s]\n", data); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } + + loop = atoi(p); + } + + if (zstr(file)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing file arg in data [%s]\n", data); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } + + while (switch_channel_ready(channel) && (loop < 0 || loop-- > 0)) { + status = switch_ivr_play_file(session, NULL, file, NULL); + + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + break; + } + } + + switch (status) { + case SWITCH_STATUS_SUCCESS: + case SWITCH_STATUS_BREAK: + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "FILE PLAYED"); + break; + case SWITCH_STATUS_NOTFOUND: + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "FILE NOT FOUND"); + break; + default: + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "PLAYBACK ERROR"); + break; + } + +} + SWITCH_STANDARD_APP(gentones_function) { char *tone_script = NULL; @@ -5604,6 +5654,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "playback", "Playback File", "Playback a file to the channel", playback_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "endless_playback", "Playback File Endlessly", "Endlessly Playback a file to the channel", endless_playback_function, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "loop_playback", "Playback File looply", "Playback a file to the channel looply for limted times", + loop_playback_function, "[+loops] ", SAF_NONE); SWITCH_ADD_APP(app_interface, "att_xfer", "Attended Transfer", "Attended Transfer", att_xfer_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "read", "Read Digits", "Read Digits", read_function, " ", SAF_NONE); From 5110e28486546c7a5d067a8624551b7a2e3b962f Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Sat, 6 Jul 2013 22:08:00 -0500 Subject: [PATCH 056/278] FS-5576 --resolve fix the vp8 issue, but the other part causes issues with centos5... we\'ll need better detection there and working on a way to handle this --- freeswitch.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freeswitch.spec b/freeswitch.spec index 10f45c08af..c8276c023a 100644 --- a/freeswitch.spec +++ b/freeswitch.spec @@ -1337,7 +1337,7 @@ ASR_TTS_MODULES="asr_tts/mod_flite asr_tts/mod_pocketsphinx asr_tts/mod_tts_comm ###################################################################################################################### CODECS_MODULES="codecs/mod_amr codecs/mod_amrwb codecs/mod_bv codecs/mod_celt codecs/mod_codec2 codecs/mod_g723_1 \ codecs/mod_g729 codecs/mod_h26x codecs/mod_ilbc codecs/mod_isac codecs/mod_mp4v codecs/mod_opus codecs/mod_silk \ - codecs/mod_siren codecs/mod_speex codecs/mod_theora mod_vp8" + codecs/mod_siren codecs/mod_speex codecs/mod_theora codecs/mod_vp8" # %if %{build_sng_tc} CODECS_MODULES+="codecs/mod_sangoma_codec" From c613c654462a6bcf403370d74a5b6163d3c701a9 Mon Sep 17 00:00:00 2001 From: Marc Olivier Chouinard Date: Mon, 8 Jul 2013 00:55:48 -0400 Subject: [PATCH 057/278] --resolve FS-5478 --- src/mod/applications/mod_callcenter/mod_callcenter.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c index 9f74d2f9cc..4e9e852aa9 100644 --- a/src/mod/applications/mod_callcenter/mod_callcenter.c +++ b/src/mod/applications/mod_callcenter/mod_callcenter.c @@ -1525,7 +1525,15 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa /* Switch the agent session */ if (real_uuid) { switch_core_session_rwunlock(agent_session); - agent_session = switch_core_session_locate(real_uuid); + if (!(agent_session = switch_core_session_locate(real_uuid))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member_session), SWITCH_LOG_DEBUG, "Real session is already gone (agent '%s')\n", h->agent_name); + sql = switch_mprintf("UPDATE members SET state = '%q', serving_agent = '', serving_system = ''" + " WHERE serving_agent = '%q' AND serving_system = '%q' AND uuid = '%q' AND system = 'single_box'", + cc_member_state2str(CC_MEMBER_STATE_WAITING), h->agent_name, h->agent_system, h->member_uuid); + cc_execute_sql(NULL, sql, NULL); + switch_safe_free(sql); + goto done; + } agent_uuid = switch_core_session_get_uuid(agent_session); agent_channel = switch_core_session_get_channel(agent_session); From 94f3b90040d1f9a6e8b95a70cfdee3b0ae04e77f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 8 Jul 2013 08:25:34 -0500 Subject: [PATCH 058/278] use static buffer and nonblocking socket in websocket client --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/tport/tport_type_ws.c | 6 +- libs/sofia-sip/libsofia-sip-ua/tport/ws.c | 104 +++++++++++------- libs/sofia-sip/libsofia-sip-ua/tport/ws.h | 9 +- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index 8024eeba38..c36486c61a 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Fri Jun 28 10:39:50 CDT 2013 +Wed Jul 3 11:09:02 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c index 8b7ecfaa7b..22c56ef07f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c @@ -432,7 +432,6 @@ int tport_ws_init_secondary(tport_t *self, int socket, int accepted, int one = 1; tport_ws_primary_t *wspri = (tport_ws_primary_t *)self->tp_pri; tport_ws_t *wstp = (tport_ws_t *)self; - char *buffer, *wbuffer; self->tp_has_connection = 1; @@ -458,10 +457,7 @@ int tport_ws_init_secondary(tport_t *self, int socket, int accepted, memset(&wstp->ws, 0, sizeof(wstp->ws)); - buffer = (char *) su_alloc((su_home_t *)self, 65536); - wbuffer = (char *) su_alloc((su_home_t *)self, 65536); - - if (ws_init(&wstp->ws, socket, buffer, wbuffer, 65336, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { + if (ws_init(&wstp->ws, socket, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { return *return_reason = "WS_INIT", -1; } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index 6ee35e54a3..7e36b6e00d 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -1,5 +1,10 @@ #include "ws.h" #include + +#ifndef _MSC_VER +#include +#endif + #define SHA1_HASH_SIZE 20 struct globals_s globals; @@ -316,16 +321,16 @@ issize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes) do { r = recv(wsh->sock, data, bytes, 0); #ifndef _MSC_VER - if (x++) usleep(10000); + if (x++) usleep(10000); #else - if (x++) Sleep(10); + if (x++) Sleep(10); #endif - } while (r == -1 && (errno == EAGAIN || errno == EINTR) && x < 100); - - //if (r<0) { - // printf("READ FAIL: %s\n", strerror(errno)); - //} - + } while (r == -1 && (errno == EAGAIN || errno == EINTR) && x < 100); + + if (x >= 100) { + r = -1; + } + return r; } @@ -352,7 +357,54 @@ issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) return r; } -int ws_init(wsh_t *wsh, ws_socket_t sock, char *buffer, char *wbuffer, size_t buflen, SSL_CTX *ssl_ctx, int close_sock) +#ifdef _MSC_VER +static int setup_socket(ws_socket_t sock) +{ + unsigned log v = 1; + + if (ioctlsocket(ssock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + + return 0; + +} + +static int restore_socket(ws_socket_t sock) +{ + unsigned log v = 0; + + if (ioctlsocket(ssock, FIONBIO, &v) == SOCKET_ERROR) { + return -1; + } + + return 0; + +} + +#else + +static int setup_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + return fcntl(sock, F_SETFL, flags | O_NONBLOCK); +} + +static int restore_socket(ws_socket_t sock) +{ + int flags = fcntl(sock, F_GETFL, 0); + + flags &= ~O_NONBLOCK; + + return fcntl(sock, F_SETFL, flags); + +} + +#endif + + + +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock) { memset(wsh, 0, sizeof(*wsh)); wsh->sock = sock; @@ -365,28 +417,10 @@ int ws_init(wsh_t *wsh, ws_socket_t sock, char *buffer, char *wbuffer, size_t bu wsh->close_sock = 1; } - if (buflen > MAXLEN) { - buflen = MAXLEN; - } - - wsh->buflen = buflen; + wsh->buflen = sizeof(wsh->buffer); wsh->secure = ssl_ctx ? 1 : 0; - if (buffer) { - wsh->buffer = buffer; - } else if (!wsh->buffer) { - wsh->buffer = malloc(wsh->buflen); - assert(wsh->buffer); - wsh->free_buffer = 1; - } - - if (wbuffer) { - wsh->wbuffer = wbuffer; - } else if (!wsh->wbuffer) { - wsh->wbuffer = malloc(wsh->buflen); - assert(wsh->wbuffer); - wsh->free_wbuffer = 1; - } + setup_socket(sock); if (wsh->secure) { int code; @@ -464,16 +498,6 @@ void ws_destroy(wsh_t *wsh) SSL_free(wsh->ssl); wsh->ssl = NULL; } - - if (wsh->free_buffer && wsh->buffer) { - free(wsh->buffer); - wsh->buffer = NULL; - } - - if (wsh->free_wbuffer && wsh->wbuffer) { - free(wsh->wbuffer); - wsh->wbuffer = NULL; - } } issize_t ws_close(wsh_t *wsh, int16_t reason) @@ -494,6 +518,8 @@ issize_t ws_close(wsh_t *wsh, int16_t reason) ws_raw_write(wsh, fr, 4); } + restore_socket(wsh->sock); + if (wsh->close_sock) { close(wsh->sock); } diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h index 84aa46fceb..81368158b3 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.h @@ -3,7 +3,6 @@ //#define WSS_STANDALONE 1 -#define MAXLEN 0x10000 #define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define B64BUFFLEN 1024 @@ -59,8 +58,8 @@ typedef enum { typedef struct wsh_s { ws_socket_t sock; - char *buffer; - char *wbuffer; + char buffer[65536]; + char wbuffer[65536]; size_t buflen; issize_t datalen; issize_t wdatalen; @@ -71,8 +70,6 @@ typedef struct wsh_s { int handshake; uint8_t down; int secure; - uint8_t free_buffer; - uint8_t free_wbuffer; uint8_t close_sock; } wsh_t; @@ -84,7 +81,7 @@ issize_t ws_raw_read(wsh_t *wsh, void *data, size_t bytes); issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes); issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data); issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes); -int ws_init(wsh_t *wsh, ws_socket_t sock, char *buffer, char *wbuffer, size_t buflen, SSL_CTX *ssl_ctx, int close_sock); +int ws_init(wsh_t *wsh, ws_socket_t sock, SSL_CTX *ssl_ctx, int close_sock); issize_t ws_close(wsh_t *wsh, int16_t reason); void ws_destroy(wsh_t *wsh); void init_ssl(void); From fd1893197e0618aa6355a10e8497e1fa02353c10 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 8 Jul 2013 07:55:50 -0500 Subject: [PATCH 059/278] FS-5586 --resolve --- src/mod/endpoints/mod_sofia/sofia.c | 38 +++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 04d17cfd1d..ba5e1694b3 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -2007,16 +2007,34 @@ void event_handler(switch_event_t *event) if (!strcasecmp(class, "PRESENCE_IN")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nGot Presence IN event via MultiCast\n"); if (switch_event_create(&pevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "proto", switch_event_get_header_nil(event, "orig-proto")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "login", switch_event_get_header_nil(event, "orig-login")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "from", switch_event_get_header_nil(event, "orig-from")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "rpid", switch_event_get_header_nil(event, "orig-rpid")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "status", switch_event_get_header_nil(event, "orig-status")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "answer-state", switch_event_get_header_nil(event, "orig-answer-state")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "alt_event_type", switch_event_get_header_nil(event, "orig-alt_event_type")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "presence-call-direction", switch_event_get_header_nil(event, "orig-presence-call-direction")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "channel-state", switch_event_get_header_nil(event, "orig-channel-state")); - switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "call-direction", switch_event_get_header_nil(event, "orig-call-direction")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "alt_event_type", switch_event_get_header_nil(event, "orig-alt_event_type")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "answer-state", switch_event_get_header_nil(event, "orig-answer-state")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "astate", switch_event_get_header_nil(event, "orig-astate")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "call-direction", switch_event_get_header_nil(event, "orig-call-direction")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Callee-ID-Number", switch_event_get_header_nil(event, "Orig-Caller-Callee-ID-Number")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Caller-ID-Name", switch_event_get_header_nil(event, "Orig-Caller-Caller-ID-Name")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Caller-ID-Number", switch_event_get_header_nil(event, "Orig-Caller-Caller-ID-Number")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Destination-Number", switch_event_get_header_nil(event, "Orig-Caller-Destination-Number")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Direction", switch_event_get_header_nil(event, "Orig-Caller-Direction")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Username", switch_event_get_header_nil(event, "Orig-Caller-Username")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "channel-state", switch_event_get_header_nil(event, "orig-channel-state")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "force-status", switch_event_get_header_nil(event, "orig-force-status")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "from", switch_event_get_header_nil(event, "orig-from")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "login", switch_event_get_header_nil(event, "orig-login")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Other-Leg-Caller-ID-Name", switch_event_get_header_nil(event, "Orig-Other-Leg-Caller-ID-Name")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Other-Leg-Caller-ID-Number", switch_event_get_header_nil(event, "Orig-Other-Leg-Caller-ID-Number")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "presence-call-direction", switch_event_get_header_nil(event, "orig-presence-call-direction")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "presence-call-info-state", switch_event_get_header_nil(event, "orig-presence-call-info-state")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Presence-Privacy", switch_event_get_header_nil(event, "Orig-Presence-Privacy")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "proto", switch_event_get_header_nil(event, "orig-proto")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "register-source", switch_event_get_header_nil(event, "orig-register-source")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "resub", switch_event_get_header_nil(event, "orig-resub")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "rpid", switch_event_get_header_nil(event, "orig-rpid")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "status", switch_event_get_header_nil(event, "orig-status")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Unique-ID", switch_event_get_header_nil(event, "Orig-Unique-ID")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "variable_sip_from_user", switch_event_get_header_nil(event, "Orig-variable_sip_from_user")); + switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "variable_sip_to_user", switch_event_get_header_nil(event, "Orig-variable_sip_to_user")); + /* we cannot use switch_event_fire, or otherwise we'll start an endless loop */ sofia_presence_event_handler(pevent); return; From 8de63a750e0f22d8e144c938c34be71c86e33bae Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 8 Jul 2013 10:02:38 -0500 Subject: [PATCH 060/278] FS-5571 --resolve --- src/switch_rtp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 177fbffcc5..45ab478f96 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -4066,7 +4066,10 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t #endif #ifdef ENABLE_SRTP - if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && rtp_session->recv_msg.header.version == 2 && rtp_session->recv_msg.header.pt == rtp_session->rpayload) { + if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && rtp_session->recv_msg.header.version == 2 && + ((rtp_session->recv_msg.header.pt == rtp_session->rpayload) || + (rtp_session->recv_te && rtp_session->recv_msg.header.pt == rtp_session->recv_te) || + (rtp_session->cng_pt && rtp_session->recv_msg.header.pt == rtp_session->cng_pt))) { //if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] && (!rtp_session->ice.ice_user || rtp_session->recv_msg.header.version == 2)) { int sbytes = (int) *bytes; err_status_t stat = 0; From 6423787da8bd12f5c9cbe8343cf55e837ca619c0 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 8 Jul 2013 13:09:27 -0400 Subject: [PATCH 061/278] mod_rayo: fix - client messages not accepted over s2s connection --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 66f59c9def..24b187d739 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -2884,7 +2884,7 @@ static struct rayo_actor *xmpp_stream_client_locate(struct xmpp_stream *stream, struct rayo_peer_server *rserver = RAYO_PEER_SERVER(xmpp_stream_get_private(stream)); actor = RAYO_ACTOR(rayo_client_create(jid, xmpp_stream_get_jid(stream), PS_UNKNOWN, rayo_client_send, rserver)); RAYO_RDLOCK(actor); - } else if (!strcmp(RAT_CLIENT, actor->type)) { + } else if (strcmp(RAT_CLIENT, actor->type)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, not a client: %s\n", xmpp_stream_get_jid(stream), jid); RAYO_UNLOCK(actor); actor = NULL; From 35fca3d29080ea9937d170c687953e91c98bc41a Mon Sep 17 00:00:00 2001 From: Brian West Date: Mon, 8 Jul 2013 13:19:10 -0500 Subject: [PATCH 062/278] fix logic from race fix --- src/mod/applications/mod_fifo/mod_fifo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index 0da3c398ff..cd1420c058 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -3094,12 +3094,12 @@ SWITCH_STANDARD_APP(fifo_function) switch_ivr_multi_threaded_bridge(session, other_session, on_dtmf, other_session, session); - if (switch_channel_test_flag(other_channel, CF_TRANSFER) || switch_channel_up(other_channel)) { + if (switch_channel_test_flag(other_channel, CF_TRANSFER) && switch_channel_up(other_channel)) { switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_initiated_bridge", NULL); switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_bridge_role", NULL); } - if (switch_channel_test_flag(channel, CF_TRANSFER) || switch_channel_up(channel)) { + if (switch_channel_test_flag(channel, CF_TRANSFER) && switch_channel_up(channel)) { switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_initiated_bridge", NULL); switch_channel_set_variable(switch_core_session_get_channel(other_session), "fifo_bridge_role", NULL); } From e21c57b0993c11d159eb19f7b638b4bef94b6356 Mon Sep 17 00:00:00 2001 From: Michael S Collins Date: Mon, 8 Jul 2013 13:45:27 -0700 Subject: [PATCH 063/278] BNPH-5393: --resolve add vars to affect mod_voicemail behavior: skip_record_check and skip_record_urgent_check --- .../mod_voicemail/mod_voicemail.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 83bb8eaf23..eb552f77b1 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -1186,6 +1186,7 @@ static switch_status_t create_file(switch_core_session_t *session, vm_profile_t cc_t cc = { 0 }; switch_codec_implementation_t read_impl = { 0 }; int got_file = 0; + switch_bool_t skip_record_check = switch_true(switch_channel_get_variable(channel, "skip_record_check")); switch_core_session_get_read_impl(session, &read_impl); @@ -1265,6 +1266,9 @@ static switch_status_t create_file(switch_core_session_t *session, vm_profile_t *(input + 1) = '\0'; status = SWITCH_STATUS_SUCCESS; *cc.buf = '\0'; + } else if (skip_record_check) { + /* Skip the record check and simply return */ + goto end; } else { (void) vm_macro_get(session, VM_RECORD_FILE_CHECK_MACRO, key_buf, input, sizeof(input), 1, "", &term, profile->digit_timeout); } @@ -3311,10 +3315,12 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p int disk_quota = 0; switch_bool_t skip_greeting = switch_true(switch_channel_get_variable(channel, "skip_greeting")); switch_bool_t skip_instructions = switch_true(switch_channel_get_variable(channel, "skip_instructions")); + switch_bool_t skip_record_urgent_check = switch_true(switch_channel_get_variable(channel, "skip_record_urgent_check")); switch_bool_t vm_enabled = SWITCH_TRUE; switch_channel_set_variable(channel, "skip_greeting", NULL); switch_channel_set_variable(channel, "skip_instructions", NULL); + switch_channel_set_variable(channel, "skip_record_urgent_check", NULL); memset(&cbt, 0, sizeof(cbt)); @@ -3589,13 +3595,14 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p char input[10] = "", term = 0; switch_snprintf(key_buf, sizeof(key_buf), "%s:%s", profile->urgent_key, profile->terminator_key); - - (void) vm_macro_get(session, VM_RECORD_URGENT_CHECK_MACRO, key_buf, input, sizeof(input), 1, "", &term, profile->digit_timeout); - if (*profile->urgent_key == *input) { - read_flags = URGENT_FLAG_STRING; - (void) switch_ivr_phrase_macro(session, VM_ACK_MACRO, "marked-urgent", NULL, NULL); - } else { - (void) switch_ivr_phrase_macro(session, VM_ACK_MACRO, "saved", NULL, NULL); + if (!skip_record_urgent_check) { + (void) vm_macro_get(session, VM_RECORD_URGENT_CHECK_MACRO, key_buf, input, sizeof(input), 1, "", &term, profile->digit_timeout); + if (*profile->urgent_key == *input) { + read_flags = URGENT_FLAG_STRING; + (void) switch_ivr_phrase_macro(session, VM_ACK_MACRO, "marked-urgent", NULL, NULL); + } else { + (void) switch_ivr_phrase_macro(session, VM_ACK_MACRO, "saved", NULL, NULL); + } } } From 2985d38f52d2d29691bcc45cdfc7c4a3c79ca39a Mon Sep 17 00:00:00 2001 From: Seven Du Date: Tue, 9 Jul 2013 13:45:40 +0800 Subject: [PATCH 064/278] debug audio on h323 channels --- src/mod/endpoints/mod_h323/mod_h323.cpp | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/mod/endpoints/mod_h323/mod_h323.cpp b/src/mod/endpoints/mod_h323/mod_h323.cpp index 90afd02c4e..17619129cf 100644 --- a/src/mod/endpoints/mod_h323/mod_h323.cpp +++ b/src/mod/endpoints/mod_h323/mod_h323.cpp @@ -1591,7 +1591,36 @@ switch_status_t FSH323Connection::receive_message(switch_core_session_message_t } break; } + case SWITCH_MESSAGE_INDICATE_DEBUG_MEDIA:{ + if (switch_rtp_ready(tech_pvt->rtp_session) && !zstr(msg->string_array_arg[0]) && !zstr(msg->string_array_arg[1])) { + switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID] = {(switch_rtp_flag_t)0}; + int x = 0; + if (!strcasecmp(msg->string_array_arg[0], "read")) { + x++; flags[SWITCH_RTP_FLAG_DEBUG_RTP_READ] = (switch_rtp_flag_t)1; + } else if (!strcasecmp(msg->string_array_arg[0], "write")) { + x++; flags[SWITCH_RTP_FLAG_DEBUG_RTP_WRITE] = (switch_rtp_flag_t)1; + } else if (!strcasecmp(msg->string_array_arg[0], "both")) { + x++; + flags[SWITCH_RTP_FLAG_DEBUG_RTP_READ] = (switch_rtp_flag_t)1; + flags[SWITCH_RTP_FLAG_DEBUG_RTP_WRITE] = (switch_rtp_flag_t)1; + } else if (*msg->string_array_arg[0] == 'v') { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Video is not supported yet\n"); + break; + } + + if (x) { + if (switch_true(msg->string_array_arg[1])) { + switch_rtp_set_flags(tech_pvt->rtp_session, flags); + } else { + switch_rtp_clear_flags(tech_pvt->rtp_session, flags); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Invalid Options\n"); + } + } + break; + } default:{ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"Received message id = %d [%p]\n", msg->message_id,this); } From 5fc2bc9993cebbce6648083b146a87ea7d41f70c Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Tue, 9 Jul 2013 07:53:56 -0500 Subject: [PATCH 065/278] FS-5588 --resolve windows compiler fixes --- libs/esl/fs_cli.c | 4 ++-- libs/sofia-sip/libsofia-sip-ua/tport/ws.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 393fcf80d3..5d87d521d3 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -764,8 +764,8 @@ static void *msg_thread_run(esl_thread_t *me, void *obj) SetConsoleTextAttribute(hStdout, colors[level]); } if (global_profile->log_uuid && !esl_strlen_zero(userdata)) { - WriteFile(hStdout, userdata, strlen(userdata), &outbytes, NULL); - WriteFile(hStdout, " ", strlen(" "), &outbytes, NULL); + WriteFile(hStdout, userdata, (DWORD)strlen(userdata), &outbytes, NULL); + WriteFile(hStdout, " ", (DWORD)strlen(" "), &outbytes, NULL); } WriteFile(hStdout, handle->last_event->body, len, &outbytes, NULL); if(!(global_profile->batch_mode)) { diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c index 7e36b6e00d..33d984a9d5 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/ws.c @@ -360,9 +360,9 @@ issize_t ws_raw_write(wsh_t *wsh, void *data, size_t bytes) #ifdef _MSC_VER static int setup_socket(ws_socket_t sock) { - unsigned log v = 1; + unsigned long v = 1; - if (ioctlsocket(ssock, FIONBIO, &v) == SOCKET_ERROR) { + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { return -1; } @@ -372,9 +372,9 @@ static int setup_socket(ws_socket_t sock) static int restore_socket(ws_socket_t sock) { - unsigned log v = 0; + unsigned long v = 0; - if (ioctlsocket(ssock, FIONBIO, &v) == SOCKET_ERROR) { + if (ioctlsocket(sock, FIONBIO, &v) == SOCKET_ERROR) { return -1; } From 267ef728e155566bfdafc0a933fc9b549777b288 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Tue, 9 Jul 2013 08:53:36 -0500 Subject: [PATCH 066/278] FS-5275 --resolve --- src/mod/say/mod_say_de/mod_say_de.c | 89 ++++++++++++++++++----------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/src/mod/say/mod_say_de/mod_say_de.c b/src/mod/say/mod_say_de/mod_say_de.c index af970430c5..a461bebe1f 100644 --- a/src/mod/say/mod_say_de/mod_say_de.c +++ b/src/mod/say/mod_say_de/mod_say_de.c @@ -91,7 +91,7 @@ static switch_status_t play_group(switch_say_method_t method, switch_say_gender_ } else { say_file("digits/%d.wav", a); } - say_file("digits/hundred.wav"); + say_file("digits/hundred.wav"); } if (b) { @@ -106,13 +106,25 @@ static switch_status_t play_group(switch_say_method_t method, switch_say_gender_ say_file("currency/and.wav"); } if (method == SSM_COUNTED) { - say_file("digits/h-%d0.wav", b); + if ( gender == SSG_MASCULINE ) { + say_file("digits/h-%d0_m.wav", b); + } else if ( gender == SSG_NEUTER ) { + say_file("digits/h-%d0_n.wav", b); + } else { + say_file("digits/h-%d0.wav", b); + } } else { say_file("digits/%d0.wav", b); } } else { if (method == SSM_COUNTED) { - say_file("digits/h-%d%d.wav", b, c); + if ( gender == SSG_MASCULINE ) { + say_file("digits/h-%d%d_m.wav", b,c); + } else if ( gender == SSG_NEUTER ) { + say_file("digits/h-%d%d_n.wav", b,c); + } else { + say_file("digits/h-%d%d.wav", b,c); + } } else { say_file("digits/%d%d.wav", b, c); } @@ -122,12 +134,18 @@ static switch_status_t play_group(switch_say_method_t method, switch_say_gender_ if (c) { if (method == SSM_COUNTED) { - say_file("digits/h-%d.wav", c); + if ( gender == SSG_MASCULINE ) { + say_file("digits/h-%d_m.wav", c); + } else if ( gender == SSG_NEUTER ) { + say_file("digits/h-%d_n.wav", c); + } else { + say_file("digits/h-%d.wav", c); + } } else { /*"one" used as an article is feminine or masculine in german, e.g. voicemail-message is feminine only applies to the likes of 1, 101, 1001 etc.*/ if ( b == 0 && c == 1 && gender == SSG_FEMININE ) { - say_file("digits/%d_f.wav", c); + say_file("digits/1_f.wav"); } else if ( b == 0 && c == 1 && what ) { say_file("digits/s-1.wav"); } else { @@ -251,10 +269,11 @@ static switch_status_t de_say_time(switch_core_session_t *session, char *tosay, } if (hours) { - say_num(hours, SSM_PRONOUNCED); if (hours == 1) { + say_file("digits/1_f.wav"); say_file("time/hour.wav"); } else { + say_num(hours, SSM_PRONOUNCED); say_file("time/hours.wav"); } } else { @@ -263,10 +282,11 @@ static switch_status_t de_say_time(switch_core_session_t *session, char *tosay, } if (minutes) { - say_num(minutes, SSM_PRONOUNCED); if (minutes == 1) { + say_file("digits/1_f.wav"); say_file("time/minute.wav"); } else { + say_num(minutes, SSM_PRONOUNCED); say_file("time/minutes.wav"); } } else { @@ -274,11 +294,13 @@ static switch_status_t de_say_time(switch_core_session_t *session, char *tosay, say_file("time/minutes.wav"); } + say_file("currency/and.wav"); if (seconds) { - say_num(seconds, SSM_PRONOUNCED); if (seconds == 1) { + say_file("digits/1_f.wav"); say_file("time/second.wav"); } else { + say_num(seconds, SSM_PRONOUNCED); say_file("time/seconds.wav"); } } else { @@ -311,6 +333,7 @@ static switch_status_t de_say_time(switch_core_session_t *session, char *tosay, } if (say_date) { + say_args->gender = SSG_MASCULINE; say_file("time/day-%d.wav", tm.tm_wday); say_num(tm.tm_mday, SSM_COUNTED); say_file("time/mon-%d.wav", tm.tm_mon); @@ -318,29 +341,27 @@ static switch_status_t de_say_time(switch_core_session_t *session, char *tosay, } if (say_time) { - int32_t hour = tm.tm_hour, pm = 0; - - if (hour > 12) { - hour -= 12; - pm = 1; - } else if (hour == 12) { - pm = 1; - } else if (hour == 0) { - hour = 12; - pm = 0; + if (say_date) { + say_file("time/at.wav"); } - say_num(hour, SSM_PRONOUNCED); - say_file("time/oclock.wav"); - - if (tm.tm_min > 9) { - say_num(tm.tm_min, SSM_PRONOUNCED); - } else if (tm.tm_min) { - say_file("time/oh.wav"); - say_num(tm.tm_min, SSM_PRONOUNCED); + if (tm.tm_hour == 1) { + say_file("digits/s-1.wav"); + } else { + say_num(tm.tm_hour, SSM_PRONOUNCED); + } + say_file("time/oclock.wav"); + + if (tm.tm_min > 0) { + say_file("currency/and.wav"); + if (tm.tm_min == 1) { + say_file("digits/1_f.wav") + say_file("time/minute.wav"); + } else { + say_num(tm.tm_min, SSM_PRONOUNCED); + say_file("time/minutes.wav"); + } } - - say_file("time/%s.wav", pm ? "p-m" : "a-m"); } return SWITCH_STATUS_SUCCESS; @@ -378,23 +399,25 @@ static switch_status_t de_say_money(switch_core_session_t *session, char *tosay, dollars++; } + /* Say dollar amount */ - de_say_general_count(session, dollars, say_args, args); if (atoi(dollars) == 1) { + say_file("digits/s-1.wav"); say_file("currency/dollar.wav"); } else { + de_say_general_count(session, dollars, say_args, args); say_file("currency/dollars.wav"); } - /* Say "and" */ - say_file("currency/and.wav"); - /* Say cents */ if (cents) { - de_say_general_count(session, cents, say_args, args); + /* Say "and" */ + say_file("currency/and.wav"); if (atoi(cents) == 1) { + say_file("digits/s-1.wav"); say_file("currency/cent.wav"); } else { + de_say_general_count(session, cents, say_args, args); say_file("currency/cents.wav"); } } else { From 41880353f9afc3ad8cd6682e5903b864e5c536fc Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 10 Jul 2013 09:43:33 -0500 Subject: [PATCH 067/278] FS-5589 --resolve --- src/switch_ivr_play_say.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index 1f2f076a06..7222c43533 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -1705,7 +1705,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess if (read_impl.samples_per_second) { switch_channel_set_variable_printf(channel, "playback_seconds", "%d", fh->samples_in / fh->native_rate); - switch_channel_set_variable_printf(channel, "playback_ms", "%d", fh->samples_in / fh->native_rate); + switch_channel_set_variable_printf(channel, "playback_ms", "%d", fh->samples_in / (fh->native_rate / 1000)); } switch_channel_set_variable_printf(channel, "playback_samples", "%d", fh->samples_in); From de9bd1c3679c7eda8d4db90b9a92ce4a1d83837b Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Wed, 10 Jul 2013 10:52:40 -0400 Subject: [PATCH 068/278] FS-3911 --resolve patch applied --- .../applications/mod_commands/mod_commands.c | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 2d0d02d7fe..1837328356 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -4390,7 +4390,11 @@ static int show_as_json_callback(void *pArg, int argc, char **argv, char **colum } if (holder->justcount) { - holder->count++; + if (zstr(argv[0])) { + holder->count = 0; + } else { + holder->count = (uint32_t) atoi(argv[0]); + } return 0; } @@ -4430,7 +4434,11 @@ static int show_as_xml_callback(void *pArg, int argc, char **argv, char **column } if (holder->justcount) { - holder->count++; + if (zstr(argv[0])) { + holder->count = 0; + } else { + holder->count = (uint32_t) atoi(argv[0]); + } return 0; } @@ -4468,7 +4476,11 @@ static int show_callback(void *pArg, int argc, char **argv, char **columnNames) int x; if (holder->justcount) { - holder->count++; + if (zstr(argv[0])) { + holder->count = 0; + } else { + holder->count = (uint32_t) atoi(argv[0]); + } return 0; } @@ -4704,6 +4716,7 @@ SWITCH_STANDARD_API(show_function) if (!strcasecmp(command, "calls")) { sprintf(sql, "select * from basic_calls where hostname='%s' order by call_created_epoch", hostname); if (argv[1] && !strcasecmp(argv[1], "count")) { + sprintf(sql, "select count(*) from basic_calls where hostname='%s'", hostname); holder.justcount = 1; if (argv[3] && !strcasecmp(argv[2], "as")) { as = argv[3]; @@ -4712,6 +4725,7 @@ SWITCH_STANDARD_API(show_function) } else if (!strcasecmp(command, "registrations")) { sprintf(sql, "select * from registrations where hostname='%s'", hostname); if (argv[1] && !strcasecmp(argv[1], "count")) { + sprintf(sql, "select count(*) from registrations where hostname='%s'", hostname); holder.justcount = 1; if (argv[3] && !strcasecmp(argv[2], "as")) { as = argv[3]; @@ -4743,6 +4757,7 @@ SWITCH_STANDARD_API(show_function) } else if (!strcasecmp(command, "channels")) { sprintf(sql, "select * from channels where hostname='%s' order by created_epoch", hostname); if (argv[1] && !strcasecmp(argv[1], "count")) { + sprintf(sql, "select count(*) from channels where hostname='%s'", hostname); holder.justcount = 1; if (argv[3] && !strcasecmp(argv[2], "as")) { as = argv[3]; From 30d5ecf9f7ec7344f0a82869c170f4ec489b12cf Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 10 Jul 2013 10:26:08 -0500 Subject: [PATCH 069/278] FS-5445 --resolve this was actually just missing one word from the code as seen in the similar code in the core, the same mistake was made there where the compiler is not smart enough to see the mutually exclusive logical paths this code is specific to spontaneously creating an array in a single set like set foo[12]=val which would be totally broken if that line were commented --- libs/esl/src/esl_event.c | 2 +- src/switch_event.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 19cb6708a5..910180f47e 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -445,7 +445,7 @@ static esl_status_t esl_event_base_add_header(esl_event_t *event, esl_stack_t st fly++; } - if ((header = esl_event_get_header_ptr(event, header_name))) { + if (header || (header = esl_event_get_header_ptr(event, header_name))) { if (index_ptr) { if (index > -1 && index <= 4000) { diff --git a/src/switch_event.c b/src/switch_event.c index de875b6337..20262677e1 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -916,11 +916,9 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc if (index_ptr || (stack & SWITCH_STACK_PUSH) || (stack & SWITCH_STACK_UNSHIFT)) { if (!(header = switch_event_get_header_ptr(event, header_name)) && index_ptr) { - /* - * Removing a possible leak. But it doesn't appear this is used anywhere, and even if it were then it wouldn't be working. - header = new_header(header_name); - */ - + + header = new_header(header_name); + if (switch_test_flag(event, EF_UNIQ_HEADERS)) { switch_event_del_header(event, header_name); } From 91392fb18d44751ae7aaa3665d3b8e25a1f04982 Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Wed, 10 Jul 2013 12:16:34 -0400 Subject: [PATCH 070/278] FS-5467 --resolve patch applied --- src/mod/applications/mod_db/mod_db.c | 44 +++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/mod/applications/mod_db/mod_db.c b/src/mod/applications/mod_db/mod_db.c index 5cb6dd7cfa..ed9a8cf877 100644 --- a/src/mod/applications/mod_db/mod_db.c +++ b/src/mod/applications/mod_db/mod_db.c @@ -342,6 +342,8 @@ static switch_status_t do_config() /* CORE DB STUFF */ +static int group_callback(void *pArg, int argc, char **argv, char **columnNames); + SWITCH_STANDARD_API(db_api_function) { int argc = 0; @@ -409,6 +411,44 @@ SWITCH_STANDARD_API(db_api_function) else { stream->write_function(stream, "true"); } + goto done; + } else if (!strcasecmp(argv[0], "count")) { + char buf[256] = ""; + if (argc < 2) { + sql = switch_mprintf("select count(distinct realm) from db_data"); + } else if (argc < 3) { + sql = switch_mprintf("select count(data_key) from db_data where realm='%q'", argv[1]); + } else { + goto error; + } + limit_execute_sql2str(sql, buf, sizeof(buf)); + switch_safe_free(sql); + stream->write_function(stream, "%s", buf); + goto done; + } else if (!strcasecmp(argv[0], "list")) { + char buf[4096] = ""; + callback_t cbt = { 0 }; + cbt.buf = buf; + cbt.len = sizeof(buf); + + if (argc < 2) { + sql = switch_mprintf("select distinct realm,',' from db_data"); + } else if (argc < 3) { + sql = switch_mprintf("select distinct data_key,',' from db_data where realm='%q'", argv[1]); + } else { + goto error; + } + switch_assert(sql); + + limit_execute_sql_callback(sql, group_callback, &cbt); + switch_safe_free(sql); + + if (!zstr(buf)) { + *(buf + (strlen(buf) - 1)) = '\0'; + } + + stream->write_function(stream, "%s", buf); + goto done; } @@ -628,11 +668,13 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_db_load) SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC); - SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]///"); + SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select|exists|count|list]///"); switch_console_set_complete("add db insert"); switch_console_set_complete("add db delete"); switch_console_set_complete("add db select"); switch_console_set_complete("add db exists"); + switch_console_set_complete("add db count"); + switch_console_set_complete("add db list"); SWITCH_ADD_API(commands_api_interface, "group", "group [insert|delete|call]", group_api_function, "[insert|delete|call]::"); switch_console_set_complete("add group insert"); switch_console_set_complete("add group delete"); From 745ab02e2b4546ac9ac518553cfd38cc1246a519 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 10 Jul 2013 10:29:41 -0500 Subject: [PATCH 071/278] render arrays properly in xml_cdr --- src/switch_ivr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index ba4ad8bead..e98be1349e 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2234,8 +2234,10 @@ static int switch_ivr_set_xml_chan_var(switch_xml_t xml, const char *var, const char *data; switch_size_t dlen = strlen(val) * 3 + 1; switch_xml_t variable; + + if (!val) val = ""; - if (!zstr(var) && !zstr(val) && ((variable = switch_xml_add_child_d(xml, var, off++)))) { + if (!zstr(var) && ((variable = switch_xml_add_child_d(xml, var, off++)))) { if ((data = malloc(dlen))) { memset(data, 0, dlen); switch_url_encode(val, data, dlen); From a64af629385bb2e0d9ab326394a4df5e6b638e1c Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Wed, 10 Jul 2013 12:46:34 -0400 Subject: [PATCH 072/278] add chatplan info app --- src/mod/applications/mod_sms/mod_sms.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/mod/applications/mod_sms/mod_sms.c b/src/mod/applications/mod_sms/mod_sms.c index 102acb3db8..49037f24d9 100644 --- a/src/mod/applications/mod_sms/mod_sms.c +++ b/src/mod/applications/mod_sms/mod_sms.c @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Raymond Chandler * * mod_sms.c -- Abstract SMS * @@ -448,6 +449,23 @@ static switch_status_t chat_send(switch_event_t *message_event) } +SWITCH_STANDARD_CHAT_APP(info_function) +{ + char *buf; + int level = SWITCH_LOG_INFO; + + if (!zstr(data)) { + level = switch_log_str2level(data); + } + + switch_event_serialize(message, &buf, SWITCH_FALSE); + switch_assert(buf); + switch_log_printf(SWITCH_CHANNEL_LOG, level, "CHANNEL_DATA:\n%s\n", buf); + free(buf); + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_STANDARD_CHAT_APP(system_function) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Executing command: %s\n", data); @@ -546,6 +564,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load) SWITCH_ADD_CHAT(chat_interface, SMS_CHAT_PROTO, chat_send); + SWITCH_ADD_CHAT_APP(chat_app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SCAF_NONE); SWITCH_ADD_CHAT_APP(chat_app_interface, "reply", "reply to a message", "reply to a message", reply_function, "", SCAF_NONE); SWITCH_ADD_CHAT_APP(chat_app_interface, "stop", "stop execution", "stop execution", stop_function, "", SCAF_NONE); SWITCH_ADD_CHAT_APP(chat_app_interface, "set", "set a variable", "set a variable", set_function, "", SCAF_NONE); From be98eb34abe3e7e9ecb310aedee73aef5a80cd6c Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 10 Jul 2013 11:25:30 -0500 Subject: [PATCH 073/278] FS-5559 --resolve --- src/mod/endpoints/mod_sofia/sofia_presence.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 47203d476d..3b4d1aa557 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -260,9 +260,22 @@ switch_status_t sofia_presence_chat_send(switch_event_t *message_event) } if (!list) { + switch_event_t *event; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nNobody to send to: Profile %s\n", proto, from, to, body ? body : "[no body]", prof ? prof : "NULL"); + // emit no recipient event + if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Error-Type", "chat"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Error-Reason", "no recipient"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-To", to); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-From", from); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-Profile", prof ? prof : "NULL"); + switch_event_add_body(event, body); + switch_event_fire(&event); + } + goto end; } From eb329ad5aea1e64b748572af87a93c3582dc2710 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 10 Jul 2013 11:26:41 -0500 Subject: [PATCH 074/278] FS-5524 --resolve --- conf/vanilla/autoload_configs/lua.conf.xml | 2 + src/mod/languages/mod_lua/mod_lua.cpp | 50 +++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/conf/vanilla/autoload_configs/lua.conf.xml b/conf/vanilla/autoload_configs/lua.conf.xml index 1eb594f0b5..f50307ea89 100644 --- a/conf/vanilla/autoload_configs/lua.conf.xml +++ b/conf/vanilla/autoload_configs/lua.conf.xml @@ -26,5 +26,7 @@ --> + + diff --git a/src/mod/languages/mod_lua/mod_lua.cpp b/src/mod/languages/mod_lua/mod_lua.cpp index 642ad3fbf7..3d79dd8d0e 100644 --- a/src/mod/languages/mod_lua/mod_lua.cpp +++ b/src/mod/languages/mod_lua/mod_lua.cpp @@ -32,6 +32,7 @@ #include +#include SWITCH_BEGIN_EXTERN_C #include "lua.h" #include @@ -44,6 +45,7 @@ SWITCH_MODULE_DEFINITION_EX(mod_lua, mod_lua_load, mod_lua_shutdown, NULL, SMODF static struct { switch_memory_pool_t *pool; char *xml_handler; + switch_event_node_t *node; } globals; int luaopen_freeswitch(lua_State * L); @@ -287,10 +289,12 @@ static switch_xml_t lua_fetch(const char *section, } +static void lua_event_handler(switch_event_t *event); + static switch_status_t do_config(void) { const char *cf = "lua.conf"; - switch_xml_t cfg, xml, settings, param; + switch_xml_t cfg, xml, settings, param, hook; switch_stream_handle_t path_stream = {0}; switch_stream_handle_t cpath_stream = {0}; @@ -327,6 +331,31 @@ static switch_status_t do_config(void) path_stream.write_function(&path_stream, "%s", val); } } + + for (hook = switch_xml_child(settings, "hook"); hook; hook = hook->next) { + char *event = (char *) switch_xml_attr_soft(hook, "event"); + char *subclass = (char *) switch_xml_attr_soft(hook, "subclass"); + //char *script = strdup( (char *) switch_xml_attr_soft(hook, "script")); + char *script = (char *) switch_xml_attr_soft(hook, "script"); + switch_event_types_t evtype; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "hook params: '%s' | '%s' | '%s'\n", event, subclass, script); + + if (switch_name_event(event,&evtype) == SWITCH_STATUS_SUCCESS) { + if (!zstr(script)) { + if (switch_event_bind_removable(modname, evtype, !zstr(subclass) ? subclass : SWITCH_EVENT_SUBCLASS_ANY, + lua_event_handler, script, &globals.node) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "event handler for '%s' set to '%s'\n", switch_event_name(evtype), script); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: unsuccessful bind\n"); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: no script name\n", event); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: unknown event type '%s'\n", event); + } + } } if (cpath_stream.data_len) { @@ -405,6 +434,23 @@ int lua_thread(const char *text) return 0; } +static void lua_event_handler(switch_event_t *event) +{ + lua_State *L = lua_init(); + char *script = NULL; + + if (event->bind_user_data) { + script = strdup((char *)event->bind_user_data); + } + + mod_lua_conjure_event(L, event, "event", 1); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "lua event hook: execute '%s'\n", (char *)script); + lua_parse_and_execute(L, (char *)script); + lua_uninit(L); + + switch_safe_free(script); +} + SWITCH_STANDARD_APP(lua_function) { lua_State *L = lua_init(); @@ -645,6 +691,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_lua_load) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_lua_shutdown) { + switch_event_unbind(&globals.node); + return SWITCH_STATUS_SUCCESS; } From 6240d5213f3acb5db56dbeb53312341689a8ab00 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 10 Jul 2013 12:08:54 -0500 Subject: [PATCH 075/278] FS-5555 --resolve --- src/include/switch_utils.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 923919bc6a..755ac91d27 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -607,12 +607,15 @@ static inline char *switch_sanitize_number(char *number) switch_assert(number); - if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@'))) { + if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@') || strchr(p, '%'))) { return number; } while ((q = strrchr(p, '@'))) *q = '\0'; + + while ((q = strrchr(p, '%'))) + *q = '\0'; for (i = 0; i < (int) strlen(warp); i++) { while (p && (q = strchr(p, warp[i]))) From df7a4fc958284fa5f096f31f2d93f6d4756db3a6 Mon Sep 17 00:00:00 2001 From: Raymond Chandler Date: Wed, 10 Jul 2013 17:53:42 -0400 Subject: [PATCH 076/278] FS-4123 --resolve slightly modified patch applied --- .../mod_conference/mod_conference.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 7abdd9710c..a841f2d371 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -7543,6 +7543,8 @@ SWITCH_STANDARD_APP(conference_function) /* if the conference exists, get the pointer to it */ if (!conference) { const char *max_members_str; + const char *endconf_grace_time_str; + const char *auto_record_str; /* couldn't find the conference, create one */ conference = conference_new(conf_name, xml_cfg, session, NULL); @@ -7562,11 +7564,19 @@ SWITCH_STANDARD_APP(conference_function) if (zstr(conference->moh_sound)) { conference->moh_sound = switch_core_strdup(conference->pool, switch_channel_get_variable(channel, "conference_moh_sound")); } + /* Set perpetual-sound from variable if not set */ if (zstr(conference->perpetual_sound)) { conference->perpetual_sound = switch_core_strdup(conference->pool, switch_channel_get_variable(channel, "conference_perpetual_sound")); } + /* Override auto-record profile parameter from variable */ + if (!zstr(auto_record_str = switch_channel_get_variable(channel, "conference_auto_record"))) { + conference->auto_record = switch_core_strdup(conference->pool, auto_record_str); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "conference_auto_record set from variable to %s\n", auto_record_str); + } + /* Set the minimum number of members (once you go above it you cannot go below it) */ conference->min = 1; @@ -7583,6 +7593,21 @@ SWITCH_STANDARD_APP(conference_function) } } + /* check for variable to override endconf_grace_time profile value */ + if (!zstr(endconf_grace_time_str = switch_channel_get_variable(channel, "conference_endconf_grace_time"))) { + uint32_t grace_time_val; + errno = 0; /* sanity first */ + grace_time_val = strtol(endconf_grace_time_str, NULL, 0); /* base 0 lets 0x... for hex 0... for octal and base 10 otherwise through */ + if (errno == ERANGE || errno == EINVAL || (int32_t) grace_time_val < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "conference_endconf_grace_time variable %s is invalid, not setting a time limit\n", endconf_grace_time_str); + } else { + conference->endconf_grace_time = grace_time_val; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "conference endconf_grace_time set from variable to %d\n", grace_time_val); + } + } + /* Indicate the conference is dynamic */ switch_set_flag_locked(conference, CFLAG_DYNAMIC); From 7f3792234b2e22ca18e3bb937f9c0058233ca954 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 10 Jul 2013 17:31:00 -0500 Subject: [PATCH 077/278] FS-5003 --resolve --- src/switch_ivr_bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index 9f903c6ba8..f9b22f8a4f 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -874,6 +874,8 @@ static switch_status_t uuid_bridge_on_soft_execute(switch_core_session_t *sessio if (switch_ivr_wait_for_answer(session, other_session) != SWITCH_STATUS_SUCCESS) { if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_continue_on_cancel"))) { switch_channel_set_state(channel, CS_EXECUTE); + } else if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_park_on_cancel"))) { + switch_ivr_park_session(session); } else if (!switch_channel_test_flag(channel, CF_TRANSFER)) { switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL); } From 90021b29537f12161048d978485de931eb45cc8e Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Thu, 11 Jul 2013 01:15:16 +0200 Subject: [PATCH 078/278] mod_sofia: Add missing format string to switch_event_add_body() call Fixes GCC format string warning/error. Signed-off-by: Stefan Knoblich --- src/mod/endpoints/mod_sofia/sofia_presence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 3b4d1aa557..8ac0629ae1 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -272,7 +272,7 @@ switch_status_t sofia_presence_chat_send(switch_event_t *message_event) switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-To", to); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-From", from); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Chat-Send-Profile", prof ? prof : "NULL"); - switch_event_add_body(event, body); + switch_event_add_body(event, "%s", body); switch_event_fire(&event); } From cb554fb02364ac50f4a85be93056069d23c2a0de Mon Sep 17 00:00:00 2001 From: Stefan Knoblich Date: Thu, 11 Jul 2013 03:12:25 +0200 Subject: [PATCH 079/278] mod_shout: Rework our_mpg123_new() to handle error cases correctly and to clean up the code Keeping parameter handling quirks for backwards compatibility reasons. Signed-off-by: Stefan Knoblich --- src/mod/formats/mod_shout/mod_shout.c | 61 ++++++++++++--------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/mod/formats/mod_shout/mod_shout.c b/src/mod/formats/mod_shout/mod_shout.c index eb081a226f..0b64ba7e5b 100644 --- a/src/mod/formats/mod_shout/mod_shout.c +++ b/src/mod/formats/mod_shout/mod_shout.c @@ -60,52 +60,43 @@ static struct { mpg123_handle *our_mpg123_new(const char *decoder, int *error) { - mpg123_handle *mh; const char *arch = "auto"; + const char *err = NULL; + mpg123_handle *mh; int x64 = 0; int rc = 0; - const char *err = NULL; - if (*globals.decoder || globals.outscale || globals.vol) { - if (*globals.decoder) { - arch = globals.decoder; - } - if ((mh = mpg123_new(arch, &rc))) { - if (rc) { - err = mpg123_plain_strerror(rc); - } - if (globals.outscale) { - mpg123_param(mh, MPG123_OUTSCALE, globals.outscale, 0); - } - if (globals.vol) { - mpg123_volume(mh, globals.vol); - } - } + if (*globals.decoder) { + arch = globals.decoder; + } +#ifndef WIN32 + else if (sizeof(void *) == 4) { + arch = "i586"; } else { - -#ifdef WIN32 - x64++; + x64 = 1; + } #else - if (sizeof(void *) == 4) { - arch = "i586"; - } else { - x64++; - } + x64 = 1; #endif + mh = mpg123_new(arch, &rc); + if (!mh) { + err = mpg123_plain_strerror(rc); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating mpg123 handle! %s\n", switch_str_nil(err)); + return NULL; + } - if ((mh = mpg123_new(arch, &rc))) { - if (rc) { - err = mpg123_plain_strerror(rc); - } - if (x64) { - mpg123_param(mh, MPG123_OUTSCALE, 8192, 0); - } + /* NOTE: keeping the globals.decoder check here for behaviour backwards compat - stkn */ + if (*globals.decoder || globals.outscale || globals.vol) { + if (globals.outscale) { + mpg123_param(mh, MPG123_OUTSCALE, globals.outscale, 0); } + if (globals.vol) { + mpg123_volume(mh, globals.vol); + } + } else if (x64) { + mpg123_param(mh, MPG123_OUTSCALE, 8192, 0); } - if (err) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating mpg123 handle! %s\n", err); - } return mh; } From bb5e55ac6a2cde79934f29e38c1c9d9453e342ed Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 11 Jul 2013 15:06:15 +0000 Subject: [PATCH 080/278] Ignore .deb files from e.g. mk-build-deps --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 766adfb371..87492e50ea 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ *.xz *.bz2 *.tbz2 +*.deb *.swp aclocal.m4 autom4te.cache From a4123ab89657ab2f70850c9b8c104850f9fc96e7 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 11 Jul 2013 15:06:30 +0000 Subject: [PATCH 081/278] Ignore test-driver files from autotools --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 87492e50ea..69f8cb476e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ config.cache configure.lineno config.log config.status +test-driver core.* TAGS *.2010.log From 518b5a802efccee46837bc9334511a14d4e689b7 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Thu, 11 Jul 2013 11:09:28 -0500 Subject: [PATCH 082/278] FS-5567 --resolve --- src/switch_xml.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/switch_xml.c b/src/switch_xml.c index c13e33bea4..076244939f 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -1328,20 +1328,24 @@ static FILE *preprocess_exec(const char *cwd, const char *command, FILE *write_f } -static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_fd, int rlevel) +static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_fd, int rlevel, switch_bool_t ignore_nomatch) { char *full_path = NULL; char *dir_path = NULL, *e = NULL; glob_t glob_data; size_t n; + int globres; if (!switch_is_file_path(pattern)) { full_path = switch_mprintf("%s%s%s", cwd, SWITCH_PATH_SEPARATOR, pattern); pattern = full_path; } - if (glob(pattern, GLOB_NOCHECK, NULL, &glob_data) != 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); + globres = glob(pattern, ignore_nomatch ? GLOB_NOMATCH : GLOB_NOCHECK, NULL, &glob_data); + if ( globres != 0) { + if ( !ignore_nomatch || globres != GLOB_NOMATCH ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); + } goto end; } @@ -1496,7 +1500,9 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle } else if (!strcasecmp(tcmd, "exec-set")) { preprocess_exec_set(targ); } else if (!strcasecmp(tcmd, "include")) { - preprocess_glob(cwd, targ, write_fd, rlevel + 1); + preprocess_glob(cwd, targ, write_fd, rlevel + 1, SWITCH_FALSE); + } else if (!strcasecmp(tcmd, "include_silent")) { + preprocess_glob(cwd, targ, write_fd, rlevel + 1, SWITCH_TRUE); } else if (!strcasecmp(tcmd, "exec")) { preprocess_exec(cwd, targ, write_fd, rlevel + 1); } @@ -1556,7 +1562,9 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle } else if (!strcasecmp(cmd, "exec-set")) { preprocess_exec_set(arg); } else if (!strcasecmp(cmd, "include")) { - preprocess_glob(cwd, arg, write_fd, rlevel + 1); + preprocess_glob(cwd, arg, write_fd, rlevel + 1, SWITCH_FALSE); + } else if (!strcasecmp(cmd, "include_silent")) { + preprocess_glob(cwd, arg, write_fd, rlevel + 1, SWITCH_TRUE); } else if (!strcasecmp(cmd, "exec")) { preprocess_exec(cwd, arg, write_fd, rlevel + 1); } From 2f48c63107fd076b477eb8e1c9ec40e1b903ec91 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Thu, 11 Jul 2013 12:04:12 -0500 Subject: [PATCH 083/278] Revert "FS-5567 --resolve" pulling this one for a bit... This reverts commit 518b5a802efccee46837bc9334511a14d4e689b7. --- src/switch_xml.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/switch_xml.c b/src/switch_xml.c index 076244939f..c13e33bea4 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -1328,24 +1328,20 @@ static FILE *preprocess_exec(const char *cwd, const char *command, FILE *write_f } -static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_fd, int rlevel, switch_bool_t ignore_nomatch) +static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_fd, int rlevel) { char *full_path = NULL; char *dir_path = NULL, *e = NULL; glob_t glob_data; size_t n; - int globres; if (!switch_is_file_path(pattern)) { full_path = switch_mprintf("%s%s%s", cwd, SWITCH_PATH_SEPARATOR, pattern); pattern = full_path; } - globres = glob(pattern, ignore_nomatch ? GLOB_NOMATCH : GLOB_NOCHECK, NULL, &glob_data); - if ( globres != 0) { - if ( !ignore_nomatch || globres != GLOB_NOMATCH ) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); - } + if (glob(pattern, GLOB_NOCHECK, NULL, &glob_data) != 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); goto end; } @@ -1500,9 +1496,7 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle } else if (!strcasecmp(tcmd, "exec-set")) { preprocess_exec_set(targ); } else if (!strcasecmp(tcmd, "include")) { - preprocess_glob(cwd, targ, write_fd, rlevel + 1, SWITCH_FALSE); - } else if (!strcasecmp(tcmd, "include_silent")) { - preprocess_glob(cwd, targ, write_fd, rlevel + 1, SWITCH_TRUE); + preprocess_glob(cwd, targ, write_fd, rlevel + 1); } else if (!strcasecmp(tcmd, "exec")) { preprocess_exec(cwd, targ, write_fd, rlevel + 1); } @@ -1562,9 +1556,7 @@ static int preprocess(const char *cwd, const char *file, FILE *write_fd, int rle } else if (!strcasecmp(cmd, "exec-set")) { preprocess_exec_set(arg); } else if (!strcasecmp(cmd, "include")) { - preprocess_glob(cwd, arg, write_fd, rlevel + 1, SWITCH_FALSE); - } else if (!strcasecmp(cmd, "include_silent")) { - preprocess_glob(cwd, arg, write_fd, rlevel + 1, SWITCH_TRUE); + preprocess_glob(cwd, arg, write_fd, rlevel + 1); } else if (!strcasecmp(cmd, "exec")) { preprocess_exec(cwd, arg, write_fd, rlevel + 1); } From 79d23ec0da582884c502e9859f218733c060c4a7 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Thu, 11 Jul 2013 12:23:04 -0500 Subject: [PATCH 084/278] FS-5567 --resolve add a non-error level informational log message when no files match pattern --- src/switch_xml.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/switch_xml.c b/src/switch_xml.c index 076244939f..ed92e05b63 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -1345,6 +1345,8 @@ static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_f if ( globres != 0) { if ( !ignore_nomatch || globres != GLOB_NOMATCH ) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); + } else if ( ignore_nomatch && globres == GLOB_NOMATCH ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Include pattern %s did not match any files.\n", pattern); } goto end; } From 65dcf18d5ffd0a17fa101adcf7ccfe62f0a7913b Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Thu, 11 Jul 2013 12:24:32 -0500 Subject: [PATCH 085/278] Revert "FS-5567 --resolve add a non-error level informational log message when no files match pattern" This reverts commit 79d23ec0da582884c502e9859f218733c060c4a7. --- src/switch_xml.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/switch_xml.c b/src/switch_xml.c index ed92e05b63..076244939f 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -1345,8 +1345,6 @@ static FILE *preprocess_glob(const char *cwd, const char *pattern, FILE *write_f if ( globres != 0) { if ( !ignore_nomatch || globres != GLOB_NOMATCH ) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error including %s\n", pattern); - } else if ( ignore_nomatch && globres == GLOB_NOMATCH ) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Include pattern %s did not match any files.\n", pattern); } goto end; } From bab6ebc54949133dfd46ed79def9dd6ff455f75d Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Thu, 11 Jul 2013 13:14:44 -0500 Subject: [PATCH 086/278] skinny: improve cleanup of devices during registration and unregistration, resolves some weird state issues when devices get disconnected --- src/mod/endpoints/mod_skinny/mod_skinny.c | 47 ++++++++++++++------ src/mod/endpoints/mod_skinny/mod_skinny.h | 1 + src/mod/endpoints/mod_skinny/skinny_server.c | 10 ++++- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 6845ab2070..790eb7836c 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1315,25 +1315,12 @@ static int flush_listener_callback(void *pArg, int argc, char **argv, char **col return 0; } -static void flush_listener(listener_t *listener) +void skinny_clean_listener_from_db(listener_t *listener) { - if(!zstr(listener->device_name)) { skinny_profile_t *profile = listener->profile; char *sql; - if ((sql = switch_mprintf( - "SELECT '%q', value, '%q', '%q', '%d' " - "FROM skinny_lines " - "WHERE device_name='%s' AND device_instance=%d " - "ORDER BY position", - profile->name, profile->domain, listener->device_name, listener->device_instance, - listener->device_name, listener->device_instance - ))) { - skinny_execute_sql_callback(profile, profile->sql_mutex, sql, flush_listener_callback, NULL); - switch_safe_free(sql); - } - if ((sql = switch_mprintf( "DELETE FROM skinny_devices " "WHERE name='%s' and instance=%d", @@ -1358,6 +1345,38 @@ static void flush_listener(listener_t *listener) switch_safe_free(sql); } + if ((sql = switch_mprintf( + "DELETE FROM skinny_active_lines " + "WHERE device_name='%s' and device_instance=%d", + listener->device_name, listener->device_instance))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + } +} + +static void flush_listener(listener_t *listener) +{ + + if(!zstr(listener->device_name)) { + skinny_profile_t *profile = listener->profile; + char *sql; + + if ((sql = switch_mprintf( + "SELECT '%q', value, '%q', '%q', '%d' " + "FROM skinny_lines " + "WHERE device_name='%s' AND device_instance=%d " + "ORDER BY position", + profile->name, profile->domain, listener->device_name, listener->device_instance, + listener->device_name, listener->device_instance + ))) { + skinny_execute_sql_callback(profile, profile->sql_mutex, sql, flush_listener_callback, NULL); + switch_safe_free(sql); + } + + skinny_clean_listener_from_db(listener); + strcpy(listener->device_name, ""); } } diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index e0e3515da4..717bcd0378 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -271,6 +271,7 @@ switch_bool_t skinny_execute_sql_callback(skinny_profile_t *profile, uint8_t listener_is_ready(listener_t *listener); switch_status_t kill_listener(listener_t *listener, void *pvt); switch_status_t keepalive_listener(listener_t *listener, void *pvt); +void skinny_clean_listener_from_db(listener_t *listener); /*****************************************************************************/ /* CHANNEL FUNCTIONS */ diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index c3c8f9029b..2b7031186d 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -964,8 +964,8 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r if (!zstr(listener->device_name)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "A device is already registred on this listener.\n"); - send_register_reject(listener, "A device is already registred on this listener"); + "A device is already registered on this listener.\n"); + send_register_reject(listener, "A device is already registered on this listener"); return SWITCH_STATUS_FALSE; } @@ -993,6 +993,9 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r goto end; } + /* clean up all traces before adding to database */ + skinny_clean_listener_from_db(listener); + if ((sql = switch_mprintf( "INSERT INTO skinny_devices " "(name, user_id, instance, ip, type, max_streams, codec_string) " @@ -1952,6 +1955,9 @@ switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t /* Close socket */ switch_clear_flag_locked(listener, LFLAG_RUNNING); + /* Clear this device from database and any active lines/etc. */ + skinny_clean_listener_from_db(listener); + return SWITCH_STATUS_SUCCESS; } From 5dbf2b3cf7541a7163eb19ca665b79c93c5f0fc9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 11 Jul 2013 17:38:24 -0500 Subject: [PATCH 087/278] refactor some video code --- libs/sofia-sip/.update | 2 +- .../sofia-sip/libsofia-sip-ua/sdp/sdp_print.c | 2 +- src/include/switch_rtp.h | 1 + src/include/switch_types.h | 1 + .../mod_conference/mod_conference.c | 83 +++-- src/switch_core_media.c | 55 +++- src/switch_core_session.c | 4 + src/switch_rtp.c | 291 +++++++++++++++--- 8 files changed, 360 insertions(+), 79 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index c36486c61a..17253549d1 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Jul 3 11:09:02 CDT 2013 +Thu Jul 11 17:38:06 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c index c912dd3b59..b587aa5cc1 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c @@ -590,7 +590,7 @@ static void print_media(sdp_printer_t *p, case sdp_proto_udp: proto = "udp"; break; case sdp_proto_rtp: proto = "RTP/AVP"; break; case sdp_proto_srtp: proto = "RTP/SAVP"; break; - case sdp_proto_extended_srtp: proto = "RTP/SAVPF"; break; + //case sdp_proto_extended_srtp: proto = "RTP/SAVPF"; break; case sdp_proto_udptl: proto = "udptl"; break; case sdp_proto_msrp: proto = "TCP/MSRP"; break; case sdp_proto_msrps: proto = "TCP/TLS/MSRP"; break; diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index ff7079b833..f704d943d1 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -139,6 +139,7 @@ SWITCH_DECLARE(void) switch_rtp_shutdown(void); SWITCH_DECLARE(switch_port_t) switch_rtp_set_start_port(switch_port_t port); SWITCH_DECLARE(switch_status_t) switch_rtp_set_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc); +SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc); /*! \brief Set/Get RTP end port diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 2e469de3de..315c0b2fdf 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1326,6 +1326,7 @@ typedef enum { CF_DTLS_OK, CF_VIDEO_PASSIVE, CF_NOVIDEO, + CF_VIDEO_ECHO, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ CF_FLAG_MAX diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index a841f2d371..bb572f4e78 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -201,7 +201,8 @@ typedef enum { CFLAG_VIDEO_BRIDGE = (1 << 14), CFLAG_AUDIO_ALWAYS = (1 << 15), CFLAG_ENDCONF_FORCED = (1 << 16), - CFLAG_RFC4579 = (1 << 17) + CFLAG_RFC4579 = (1 << 17), + CFLAG_FLOOR_CHANGE = (1 << 18) } conf_flag_t; typedef enum { @@ -394,6 +395,7 @@ typedef struct conference_relationship { struct conference_member { uint32_t id; switch_core_session_t *session; + switch_channel_t *channel; conference_obj_t *conference; switch_memory_pool_t *pool; switch_buffer_t *audio_buffer; @@ -1443,15 +1445,20 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe conference_send_presence(conference); - - channel = switch_core_session_get_channel(member->session); - switch_channel_set_flag(channel, CF_VIDEO_PASSIVE); + switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id); switch_channel_set_variable_printf(channel, "conference_moderator", "%s", switch_test_flag(member, MFLAG_MOD) ? "true" : "false"); switch_channel_set_variable(channel, "conference_recording", conference->record_filename); switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str); - + + + + if (switch_channel_test_flag(channel, CF_VIDEO)) { + switch_channel_clear_flag(channel, CF_VIDEO_ECHO); + /* Tell the channel to request a fresh vid frame */ + switch_core_session_refresh_video(member->session); + } if (!switch_channel_get_variable(channel, "conference_call_key")) { char *key = switch_core_session_sprintf(member->session, "conf_%s_%s_%s", @@ -1565,6 +1572,20 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe return status; } +static void conference_set_floor_holder(conference_obj_t *conference, conference_member_t *member) +{ + + if (conference->floor_holder && conference->floor_holder != member) { + switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE); + } + + if ((conference->floor_holder = member)) { + switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->floor_holder->session); + switch_set_flag(conference, CFLAG_FLOOR_CHANGE); + } +} + /* Gain exclusive access and remove the member from the list */ static switch_status_t conference_del_member(conference_obj_t *conference, conference_member_t *member) { @@ -1648,7 +1669,9 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe } if (member == member->conference->floor_holder) { - member->conference->floor_holder = NULL; + //member->conference->floor_holder = NULL; + conference_set_floor_holder(member->conference, NULL); + if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); @@ -1673,7 +1696,10 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe } } - switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); + if (switch_channel_test_flag(channel, CF_VIDEO)) { + switch_channel_set_flag(channel, CF_VIDEO_ECHO); + switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); + } conference_send_presence(conference); switch_channel_set_variable(channel, "conference_call_key", NULL); @@ -1839,19 +1865,15 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr conference_member_t *imember; switch_frame_t *vid_frame; switch_status_t status; - int has_vid = 1, want_refresh = 0; + int want_refresh = 0; int yield = 0; switch_core_session_t *session; - switch_core_session_message_t msg = { 0 }; + char buf[65536]; conference->video_running = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video thread started for conference %s\n", conference->name); - /* Tell the channel to request a fresh vid frame */ - msg.from = __FILE__; - msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ; - - while (has_vid && conference->video_running == 1 && globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { + while (conference->video_running == 1 && globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { if (yield) { switch_yield(yield); yield = 0; @@ -1887,12 +1909,21 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr goto do_continue; } + if (vid_frame && switch_test_flag(vid_frame, SFF_CNG)) { + yield = 10000; + goto do_continue; + } + memcpy(buf, vid_frame->packet, vid_frame->packetlen); + switch_mutex_unlock(conference->mutex); switch_mutex_lock(conference->mutex); - has_vid = 0; want_refresh = 0; + if (switch_test_flag(conference, CFLAG_FLOOR_CHANGE)) { + switch_clear_flag(conference, CFLAG_FLOOR_CHANGE); + } + for (imember = conference->members; imember; imember = imember->next) { switch_core_session_t *isession = imember->session; switch_channel_t *ichannel; @@ -1908,21 +1939,20 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr switch_channel_clear_flag(ichannel, CF_VIDEO_REFRESH_REQ); } - if (imember->session && switch_channel_test_flag(ichannel, CF_VIDEO)) { - has_vid++; + if (isession && switch_channel_test_flag(ichannel, CF_VIDEO)) { + memcpy(vid_frame->packet, buf, vid_frame->packetlen); switch_core_session_write_video_frame(imember->session, vid_frame, SWITCH_IO_FLAG_NONE, 0); } switch_core_session_rwunlock(isession); } - if (want_refresh) { - switch_core_session_receive_message(session, &msg); + if (want_refresh && session) { + switch_core_session_refresh_video(session); want_refresh = 0; } do_continue: - switch_mutex_unlock(conference->mutex); } @@ -2078,7 +2108,8 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } } - conference->floor_holder = floor_holder; + //conference->floor_holder = floor_holder; + conference_set_floor_holder(conference, floor_holder); } @@ -5219,7 +5250,8 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st switch_mutex_lock(member->conference->mutex); if (member->conference->floor_holder == member) { - member->conference->floor_holder = NULL; + //member->conference->floor_holder = NULL; + conference_set_floor_holder(member->conference, NULL); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -5232,7 +5264,8 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st } } } else if (member->conference->floor_holder == NULL) { - member->conference->floor_holder = member; + //member->conference->floor_holder = member; + conference_set_floor_holder(member->conference, member); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -5272,7 +5305,8 @@ static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, s if (member->conference->floor_holder != member) { conference_member_t *old_member = member->conference->floor_holder; - member->conference->floor_holder = member; + //member->conference->floor_holder = member; + conference_set_floor_holder(member->conference, member); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -7781,6 +7815,7 @@ SWITCH_STANDARD_APP(conference_function) } member.session = session; + member.channel = switch_core_session_get_channel(session); member.pool = switch_core_session_get_pool(session); if (setup_media(&member, conference)) { diff --git a/src/switch_core_media.c b/src/switch_core_media.c index a52c9e08e7..9e21e458eb 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -126,6 +126,7 @@ typedef struct switch_rtp_engine_s { uint32_t max_missed_packets; uint32_t max_missed_hold_packets; uint32_t ssrc; + uint32_t remote_ssrc; switch_port_t remote_rtcp_port; switch_rtp_bug_flag_t rtp_bugs; @@ -1985,6 +1986,8 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_ generate_local_fingerprint(smh, type); switch_channel_set_flag(smh->session->channel, CF_DTLS); + } else if (!engine->remote_ssrc && !strcasecmp(attr->a_name, "ssrc") && attr->a_value) { + engine->remote_ssrc = (uint32_t) atol(attr->a_value); #ifdef RTCP_MUX } else if (!strcasecmp(attr->a_name, "rtcp-mux")) { engine->rtcp_mux = SWITCH_TRUE; @@ -2294,7 +2297,6 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s codec_array = smh->codecs; total_codecs = smh->mparams->num_codecs; - if (!(parser = sdp_parse(NULL, r_sdp, (int) strlen(r_sdp), 0))) { return 0; } @@ -2459,9 +2461,12 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s got_webrtc++; switch_core_session_set_ice(session); } - + if (m->m_proto_name && !strcasecmp(m->m_proto_name, "UDP/TLS/RTP/SAVPF")) { switch_channel_set_flag(session->channel, CF_WEBRTC_MOZ); + printf("PRICK FACE 1\n"); + } else { + printf("PRICK FACE 2 [%s]\n", m->m_proto_name); } if (m->m_proto == sdp_proto_srtp || m->m_proto == sdp_proto_extended_srtp) { @@ -3790,7 +3795,8 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread started\n", switch_channel_get_name(session->channel)); switch_core_session_refresh_video(session); - + switch_channel_set_flag(channel, CF_VIDEO_ECHO); + while (switch_channel_up_nosig(channel)) { if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) { @@ -3812,7 +3818,6 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); - if (!SWITCH_READ_ACCEPTABLE(status)) { switch_cond_next(); continue; @@ -3828,7 +3833,9 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi continue; } - switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); + if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) { + switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); + } } @@ -4092,6 +4099,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi switch_rtp_set_ssrc(a_engine->rtp_session, a_engine->ssrc); } + if (a_engine->remote_ssrc) { + switch_rtp_set_remote_ssrc(a_engine->rtp_session, a_engine->remote_ssrc); + } switch_channel_set_flag(session->channel, CF_FS_RTP); @@ -4552,6 +4562,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi switch_rtp_set_ssrc(v_engine->rtp_session, v_engine->ssrc); } + if (v_engine->remote_ssrc) { + switch_rtp_set_remote_ssrc(v_engine->rtp_session, v_engine->remote_ssrc); + } + if (v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].ready) { gen_ice(session, SWITCH_MEDIA_TYPE_VIDEO, NULL, 0); @@ -5125,7 +5139,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess switch_rtp_engine_t *a_engine, *v_engine; switch_media_handle_t *smh; ice_t *ice_out; - + int vp8 = 0; switch_assert(session); @@ -5629,6 +5643,10 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess if (v_engine->codec_params.rm_encoding) { const char *of; + + if (!strcasecmp(v_engine->codec_params.rm_encoding, "VP8")) { + vp8 = v_engine->codec_params.pt; + } rate = v_engine->codec_params.rm_rate; switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%ld\n", @@ -5694,6 +5712,10 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess channels = get_channels(imp); + if (!strcasecmp(imp->iananame, "VP8")) { + vp8 = ianacode; + } + if (channels > 1) { switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", ianacode, imp->iananame, imp->samples_per_second, channels); @@ -5733,7 +5755,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess } - if (smh->mparams->rtcp_audio_interval_msec) { + if (smh->mparams->rtcp_video_interval_msec) { if (v_engine->rtcp_mux > 0) { switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp-mux\n"); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp:%d IN %s %s\n", v_port, family, ip); @@ -5751,7 +5773,9 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess uint32_t c2 = (2^24)*126 + (2^8)*65535 + (2^0)*(256 - 2); uint32_t c3 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 1); uint32_t c4 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 2); - + const char *vbw; + int bw = 256; + tmp1[10] = '\0'; tmp2[10] = '\0'; switch_stun_random_string(tmp1, 10, "0123456789"); @@ -5760,6 +5784,21 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ice_out = &v_engine->ice_out; + if ((vbw = switch_channel_get_variable(smh->session->channel, "rtp_video_max_bandwidth"))) { + int v = atoi(vbw); + bw = v; + } + + if (bw > 0) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "b=AS:%d\n", bw); + } + + + if (vp8) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), + "a=rtcp-fb:%d ccm fir\n", vp8); + } + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u cname:%s\n", v_engine->ssrc, smh->cname); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u msid:%s v0\n", v_engine->ssrc, smh->msid); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u mslabel:%s\n", v_engine->ssrc, smh->msid); diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 6e97391f21..8fc40f7d9d 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -2567,6 +2567,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_execute_application_get_flag *flags = application_interface->flags; } + if (!switch_test_flag(application_interface, SAF_SUPPORT_NOMEDIA) && (switch_channel_test_flag(session->channel, CF_VIDEO))) { + switch_core_session_refresh_video(session); + } + if (switch_channel_test_flag(session->channel, CF_PROXY_MODE) && !switch_test_flag(application_interface, SAF_SUPPORT_NOMEDIA)) { switch_ivr_media(session->uuid_str, SMF_NONE); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Application %s Requires media on channel %s!\n", diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 45ab478f96..f1ceb1d5c3 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -34,6 +34,8 @@ //#define RTP_DEBUG_WRITE_DELTA //#define DEBUG_MISSED_SEQ +#define FIR_COUNTDOWN 100 + #include #ifndef _MSC_VER #include @@ -116,7 +118,6 @@ typedef struct { uint8_t r3; } rtcp_fir_t; - #ifdef _MSC_VER #pragma pack(push, r1, 1) #endif @@ -264,6 +265,13 @@ static int dtls_state_dummy(switch_rtp_t *rtp_session, switch_dtls_t *dtls); dtls_state_handler_t dtls_states[DS_INVALID] = {dtls_state_handshake, dtls_state_setup, dtls_state_ready, dtls_state_dummy}; +typedef struct ts_normalize_s { + uint32_t last_ssrc; + uint32_t last_frame; + uint32_t ts; + uint32_t delta; + uint8_t m; +} ts_normalize_t; struct switch_rtp { /* @@ -281,6 +289,8 @@ struct switch_rtp { rtcp_msg_t rtcp_send_msg; rtcp_ext_msg_t rtcp_ext_send_msg; uint8_t fir_seq; + uint16_t fir_countdown; + ts_normalize_t ts_norm; switch_sockaddr_t *remote_addr, *rtcp_remote_addr; rtp_msg_t recv_msg; rtcp_msg_t rtcp_recv_msg; @@ -307,6 +317,7 @@ struct switch_rtp { uint16_t seq; uint32_t ssrc; + uint32_t remote_ssrc; int8_t sending_dtmf; uint8_t need_mark; switch_payload_t payload; @@ -698,10 +709,10 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice) elapsed = (unsigned int) ((switch_micro_time_now() - rtp_session->last_stun) / 1000); if (elapsed > 30000) { - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No stun for a long time (PUNT!)\n"); - status = SWITCH_STATUS_GENERR; - goto end; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No stun for a long time!\n"); + rtp_session->last_stun = switch_micro_time_now(); + //status = SWITCH_STATUS_GENERR; + //goto end; } } @@ -743,7 +754,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice) ice->sending = 3; - end: + // end: READ_DEC(rtp_session); return status; @@ -1329,20 +1340,31 @@ static void send_fir(switch_rtp_t *rtp_session) return; } + if (rtp_session->remote_ssrc == 0) { + rtp_session->remote_ssrc = rtp_session->stats.rtcp.peer_ssrc; + } + + if (rtp_session->remote_ssrc == 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, "Peer ssrc not known yet for FIR\n"); + return; + } + if (rtp_session->rtcp_sock_output && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) { rtcp_fir_t *fir = (rtcp_fir_t *) rtp_session->rtcp_ext_send_msg.body; switch_size_t rtcp_bytes; - + rtp_session->rtcp_ext_send_msg.header.version = 2; rtp_session->rtcp_ext_send_msg.header.p = 0; rtp_session->rtcp_ext_send_msg.header.fmt = 4; rtp_session->rtcp_ext_send_msg.header.pt = 206; rtp_session->rtcp_ext_send_msg.header.send_ssrc = htonl(rtp_session->ssrc); - rtp_session->rtcp_ext_send_msg.header.recv_ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + rtp_session->rtcp_ext_send_msg.header.recv_ssrc = 0;//htonl(rtp_session->stats.rtcp.peer_ssrc); - fir->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); - fir->seq = (uint8_t) htonl(rtp_session->fir_seq++); + //fir->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + fir->ssrc = htonl(rtp_session->remote_ssrc); + fir->seq = ++rtp_session->fir_seq; + fir->r1 = fir->r2 = fir->r3 = 0; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP FIR %d\n", rtp_session->fir_seq); @@ -1409,6 +1431,92 @@ static void send_fir(switch_rtp_t *rtp_session) return; } + +#if 0 +static void send_pli(switch_rtp_t *rtp_session) +{ + + if (!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->ice.ice_user) { + return; + } + + if (rtp_session->rtcp_sock_output && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) { + switch_size_t rtcp_bytes; + + rtp_session->rtcp_ext_send_msg.header.version = 2; + rtp_session->rtcp_ext_send_msg.header.p = 0; + rtp_session->rtcp_ext_send_msg.header.fmt = 1; + rtp_session->rtcp_ext_send_msg.header.pt = 206; + + rtp_session->rtcp_ext_send_msg.header.send_ssrc = htonl(rtp_session->ssrc); + rtp_session->rtcp_ext_send_msg.header.recv_ssrc = 0; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP PLI\n"); + + rtcp_bytes = sizeof(switch_rtcp_ext_hdr_t); + rtp_session->rtcp_ext_send_msg.header.length = htons((u_short)(rtcp_bytes / 4) - 1); + + +#ifdef ENABLE_SRTP + if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND]) { + int sbytes = (int) rtcp_bytes; + int stat = srtp_protect_rtcp(rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_ext_send_msg.header, &sbytes); + + if (stat) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error: SRTP RTCP protection failed with code %d\n", stat); + goto end; + } else { + rtcp_bytes = sbytes; + } + + } +#endif + +#ifdef ENABLE_ZRTP + /* ZRTP Send */ + if (zrtp_on && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA]) { + unsigned int sbytes = (int) rtcp_bytes; + zrtp_status_t stat = zrtp_status_fail; + + stat = zrtp_process_rtcp(rtp_session->zrtp_stream, (void *) &rtp_session->rtcp_ext_send_msg, &sbytes); + + switch (stat) { + case zrtp_status_ok: + break; + case zrtp_status_drop: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection drop with code %d\n", stat); + goto end; + break; + case zrtp_status_fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection fail with code %d\n", stat); + break; + default: + break; + } + + rtcp_bytes = sbytes; + } +#endif + +#ifdef DEBUG_EXTRA + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "%s SEND %s RTCP %ld\n", + switch_core_session_get_name(rtp_session->session), + rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "video" : "audio", rtcp_bytes); +#endif + if (switch_socket_sendto(rtp_session->rtcp_sock_output, rtp_session->rtcp_remote_addr, 0, (void *)&rtp_session->rtcp_ext_send_msg, &rtcp_bytes ) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG,"RTCP packet not written\n"); + } else { + rtp_session->stats.inbound.period_packet_count = 0; + } + } + + + end: + + return; +} +#endif + static int check_rtcp_and_ice(switch_rtp_t *rtp_session) { int ret = 0; @@ -1442,10 +1550,6 @@ static int check_rtcp_and_ice(switch_rtp_t *rtp_session) rtcp_ok = 0; } - //if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { - // rtcp_ok = 0; - //} - if (rtp_session->rtcp_sock_output && rtcp_ok && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP] && !rtp_session->flags[SWITCH_RTP_FLAG_RTCP_PASSTHRU]) { struct switch_rtcp_senderinfo *sr = (struct switch_rtcp_senderinfo*) rtp_session->rtcp_send_msg.body; const char* str_cname=NULL; @@ -2123,6 +2227,10 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ status = enable_remote_rtcp_socket(rtp_session, err); } + if (rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP] && rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { + rtp_session->rtcp_remote_addr = rtp_session->remote_addr; + } + switch_mutex_unlock(rtp_session->write_mutex); return status; @@ -2224,7 +2332,7 @@ static int dtls_state_ready(switch_rtp_t *rtp_session, switch_dtls_t *dtls) if (dtls->new_state) { if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { switch_core_session_t *other_session; - send_fir(rtp_session); + rtp_session->fir_countdown = FIR_COUNTDOWN; if (rtp_session->session && switch_core_session_get_partner(rtp_session->session, &other_session) == SWITCH_STATUS_SUCCESS) { switch_core_session_refresh_video(other_session); @@ -2682,6 +2790,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_ssrc(switch_rtp_t *rtp_session, u return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc) +{ + rtp_session->remote_ssrc = ssrc; + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session, switch_payload_t payload, uint32_t samples_per_interval, @@ -2781,6 +2896,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); } + if (channel) { switch_channel_set_private(channel, "__rtcp_audio_rtp_session", rtp_session); } @@ -3221,7 +3337,10 @@ SWITCH_DECLARE(void) switch_rtp_flush(switch_rtp_t *rtp_session) SWITCH_DECLARE(void) switch_rtp_video_refresh(switch_rtp_t *rtp_session) { if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->ice.ice_user) { - send_fir(rtp_session); + if (!rtp_session->fir_countdown) { + //send_fir(rtp_session); + rtp_session->fir_countdown = FIR_COUNTDOWN; + } } } @@ -3827,6 +3946,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t uint32_t ts = 0; unsigned char *b = NULL; int sync = 0; + switch_time_t now; switch_assert(bytes); more: @@ -3896,15 +4016,14 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } } - if (status == SWITCH_STATUS_SUCCESS && *bytes) { if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { *flags &= ~SFF_RTCP; - if (rtp_session->recv_msg.header.pt != rtp_session->rpayload && (!rtp_session->recv_te || - rtp_session->recv_msg.header.pt != rtp_session->recv_te) && + if (rtp_session->recv_msg.header.pt != rtp_session->rpayload && + (!rtp_session->recv_te || rtp_session->recv_msg.header.pt != rtp_session->recv_te) && (!rtp_session->cng_pt || rtp_session->recv_msg.header.pt != rtp_session->cng_pt) && - rtp_session->rtcp_recv_msg_p->header.version == 2 && rtp_session->rtcp_recv_msg_p->header.type > 199 && - rtp_session->rtcp_recv_msg_p->header.type < 208) { //rtcp muxed + rtp_session->rtcp_recv_msg_p->header.version == 2 && + rtp_session->rtcp_recv_msg_p->header.type > 199 && rtp_session->rtcp_recv_msg_p->header.type < 208) { //rtcp muxed *flags |= SFF_RTCP; return SWITCH_STATUS_SUCCESS; } @@ -3952,11 +4071,13 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t udptl: - ts = ntohl(rtp_session->recv_msg.header.ts); + ts = 0; rtp_session->recv_msg.ebody = NULL; + now = switch_micro_time_now(); if (*bytes) { uint16_t seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq); + ts = ntohl(rtp_session->recv_msg.header.ts); if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && rtp_session->recv_msg.header.version == 2 && rtp_session->recv_msg.header.x) { /* header extensions */ @@ -3981,40 +4102,47 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (num_missed == 1) { /* We missed one packet */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed one RTP frame with sequence [%d]%s. Time since last read [%ld]\n", rtp_session->last_seq+1, (flushed_packets_diff == 1) ? " (flushed by FS)" : " (missed)", - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else { /* We missed multiple packets */ if (flushed_packets_diff == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (missed). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else if (flushed_packets_diff == num_missed) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (flushed by FS). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else if (num_missed > flushed_packets_diff) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (%ld packets flushed by FS, %ld packets missed)." " Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, flushed_packets_diff, num_missed-flushed_packets_diff, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (%ld packets flushed by FS). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - flushed_packets_diff, rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + flushed_packets_diff, rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } } } #endif rtp_session->last_seq = seq; - } + - rtp_session->last_flush_packet_count = rtp_session->stats.inbound.flush_packet_count; - rtp_session->last_read_time = switch_micro_time_now(); + rtp_session->last_flush_packet_count = rtp_session->stats.inbound.flush_packet_count; + + + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && now - rtp_session->last_read_time > 500000) { + switch_rtp_video_refresh(rtp_session); + } + + rtp_session->last_read_time = now; + } if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && *bytes && (!rtp_session->recv_te || rtp_session->recv_msg.header.pt != rtp_session->recv_te) && @@ -4121,7 +4249,17 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } - rtp_session->last_read_ts = ts; + if (*bytes && rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + unsigned int diff = ts - rtp_session->last_read_ts; + + if (abs(diff) > 10000) { + switch_rtp_video_refresh(rtp_session); + } + } + + if (ts) { + rtp_session->last_read_ts = ts; + } if (rtp_session->flags[SWITCH_RTP_FLAG_BYTESWAP] && rtp_session->recv_msg.header.pt == rtp_session->rpayload) { switch_swap_linear((int16_t *)RTP_BODY(rtp_session), (int) *bytes - rtp_header_len); @@ -4251,16 +4389,13 @@ static switch_status_t read_rtcp_packet(switch_rtp_t *rtp_session, switch_size_t if (rtp_session->rtcp_dtls) { char *b = (char *) &rtp_session->rtcp_recv_msg; - //printf("RECV2 %d %ld\n", *b, *bytes); - if (*b == 0 || *b == 1) { if (rtp_session->rtcp_ice.ice_user) { handle_ice(rtp_session, &rtp_session->rtcp_ice, (void *) &rtp_session->rtcp_recv_msg, *bytes); } *bytes = 0; } - - + if (*bytes && (*b >= 20) && (*b <= 64)) { rtp_session->rtcp_dtls->bytes = *bytes; rtp_session->rtcp_dtls->data = (void *) &rtp_session->rtcp_recv_msg; @@ -4474,14 +4609,15 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ pt = 20000; } - if ((rtp_session->ice.ice_user && rtp_session->flags[SWITCH_RTP_FLAG_VIDEO])) { - pt = 10000; - } if ((io_flags & SWITCH_IO_FLAG_NOBLOCK)) { pt = 0; } + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + pt = 100000; + } + poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, pt); if (rtp_session->dtmf_data.out_digit_dur > 0) { @@ -4499,6 +4635,22 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; goto end; } + + + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "Read bytes (%i) %ld\n", status, bytes); + + if (bytes == 0) { + if (check_rtcp_and_ice(rtp_session) == -1) { + ret = -1; + goto end; + } + // This is dumb + switch_rtp_video_refresh(rtp_session); + goto rtcp; + } + } + if ((*flags & SFF_PROXY_PACKET)) { ret = (int) bytes; goto end; @@ -4509,8 +4661,8 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ has_rtcp = 1; goto rtcp; } - - //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, "Read bytes (%i) %ld\n", status, bytes); + + } poll_loop = 0; } else { @@ -4539,9 +4691,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; goto end; } - - } else if ((!(io_flags & SWITCH_IO_FLAG_NOBLOCK)) && - (rtp_session->dtmf_data.out_digit_dur == 0)) { + goto recvfrom; + } + + if ((!(io_flags & SWITCH_IO_FLAG_NOBLOCK)) && + (rtp_session->dtmf_data.out_digit_dur == 0)) { return_cng_frame(); } } @@ -4572,7 +4726,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (rtcp_status == SWITCH_STATUS_SUCCESS) { switch_rtp_reset_media_timer(rtp_session); - + if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_PASSTHRU] || rtp_session->rtcp_recv_msg_p->header.type == 206) { switch_channel_t *channel = switch_core_session_get_channel(rtp_session->session); const char *uuid = switch_channel_get_partner_uuid(channel); @@ -5118,6 +5272,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp return SWITCH_STATUS_FALSE; } + if (rtp_session->fir_countdown) { + if (--rtp_session->fir_countdown == 0) { + send_fir(rtp_session); + //send_pli(rtp_session); + } + } + bytes = rtp_common_read(rtp_session, &frame->payload, &frame->flags, io_flags); frame->data = RTP_BODY(rtp_session); @@ -5311,6 +5472,45 @@ static int rtp_common_write(switch_rtp_t *rtp_session, } } + + if (switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { + /* Normalize the timestamps to our own base by generating a made up starting point then adding the measured deltas to that base + so if the timestamps and ssrc of the source change, it will not break the other end's jitter bufffer / decoder etc *cough* CHROME *cough* + */ + + if (!rtp_session->ts_norm.ts) { + rtp_session->ts_norm.ts = (uint32_t) rand() % 1000000 + 1; + } + + if (!rtp_session->ts_norm.last_ssrc || send_msg->header.ssrc != rtp_session->ts_norm.last_ssrc) { + if (rtp_session->ts_norm.last_ssrc) { + rtp_session->ts_norm.m = 1; + if (rtp_session->ts_norm.delta) { + rtp_session->ts_norm.ts += rtp_session->ts_norm.delta; + } + } + rtp_session->ts_norm.last_ssrc = send_msg->header.ssrc; + rtp_session->ts_norm.last_frame = ntohl(send_msg->header.ts); + } + + + if (ntohl(send_msg->header.ts) != rtp_session->ts_norm.last_frame) { + rtp_session->ts_norm.delta = ntohl(send_msg->header.ts) - rtp_session->ts_norm.last_frame; + rtp_session->ts_norm.ts += rtp_session->ts_norm.delta; + } + + rtp_session->ts_norm.last_frame = ntohl(send_msg->header.ts); + send_msg->header.ts = htonl(rtp_session->ts_norm.ts); + + if (rtp_session->ts_norm.m) { + if (send_msg->header.m) { + rtp_session->ts_norm.m = 0; + } else { + send_msg->header.m = 1; + } + } + } + send_msg->header.ssrc = htonl(rtp_session->ssrc); if (rtp_session->flags[SWITCH_RTP_FLAG_GOOGLEHACK] && rtp_session->send_msg.header.pt == 97) { @@ -5447,6 +5647,8 @@ static int rtp_common_write(switch_rtp_t *rtp_session, send = 0; } + + if (send) { send_msg->header.seq = htons(++rtp_session->seq); @@ -5549,7 +5751,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, } } - if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) send_msg, &bytes) != SWITCH_STATUS_SUCCESS) { rtp_session->seq--; ret = -1; From 719e0329bc208b4380227764a196c41bcf16b619 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 10:16:32 -0500 Subject: [PATCH 088/278] add files to spandsp ignore list --- libs/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/.gitignore b/libs/.gitignore index 093c5bdf10..9bf6e4bab6 100644 --- a/libs/.gitignore +++ b/libs/.gitignore @@ -558,11 +558,13 @@ opal /spandsp/src/Makefile.in /spandsp/src/make_math_fixed_tables /spandsp/src/make_modem_filter +/spandsp/src/make_t43_gray_code_tables /spandsp/src/math_fixed_tables.h /spandsp/src/msvc/All/BuildLog make_at_dictionary.htm /spandsp/src/msvc/All/BuildLog make_modem_filter.htm /spandsp/src/spandsp.h /spandsp/src/stamp-h1 +/spandsp/src/t43_gray_code_tables.h /spandsp/src/v17_v32bis_rx_fixed_rrc.h /spandsp/src/v17_v32bis_rx_floating_rrc.h /spandsp/src/v17_v32bis_tx_fixed_rrc.h From 11cd5262f7cfa99d9f03cd25f8cec7b8a5797cec Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 11:04:32 -0500 Subject: [PATCH 089/278] update device types list with more known types --- src/mod/endpoints/mod_skinny/skinny_tables.c | 61 ++++++++++++++++---- src/mod/endpoints/mod_skinny/skinny_tables.h | 2 +- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index 62c97aacf5..e896245955 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -115,18 +115,59 @@ SKINNY_DECLARE_STR2ID(skinny_str2message_type, SKINNY_MESSAGE_TYPES, -1) {1, "Cisco 30 SP+"}, {2, "Cisco 12 SP+"}, {3, "Cisco 12 SP"}, - {4, "Cisco 12"}, + {4, "Cisco 12 S"}, {5, "Cisco 30 VIP"}, - {6, "Cisco IP Phone 7910"}, - {7, "Cisco IP Phone 7960"}, - {8, "Cisco IP Phone 7940"}, - {9, "Cisco IP Phone 7935"}, + {6, "Cisco 7910"}, + {7, "Cisco 7960"}, + {8, "Cisco 7940"}, + {9, "Cisco 7935"}, + {10, "Cisco VGC Phone"}, + {11, "Cisco VGC Virtual Phone"}, {12, "Cisco ATA 186"}, - {365, "Cisco IP Phone CP-7921G"}, - {404, "Cisco IP Phone CP-7962G"}, - {436, "Cisco IP Phone CP-7965G"}, - {30018, "Cisco IP Phone CP-7961G"}, - {30019, "Cisco IP Phone 7936"}, + {30, "Cisco Analog Access"}, + {40, "Cisco Digital Access"}, + {42, "Cisco Digital Access+"}, + {43, "Cisco Digital Access WS-X6608"}, + {47, "Cisco Analog Access WS-X6624"}, + {51, "Cisco Conference Bridge WS-X6608"}, + {61, "Cisco H.323 Phone"}, + {100, "Cisco Load Simulator"}, + {111, "Cisco Media Termination Point Hardware"}, + {115, "Cisco 7941"}, + {115, "Cisco CP-7941G"}, + {119, "Cisco 7971"}, + {120, "Cisco MGCP Station"}, + {121, "Cisco MGCP Trunk"}, + {124, "Cisco 7914 14-Button Line Expansion Module"}, + {302, "Cisco 7985"}, + {307, "Cisco 7911"}, + {308, "Cisco 7961G-GE"}, + {309, "Cisco 7941G-GE"}, + {335, "Cisco Motorola CN622"}, + {348, "Cisco 7931"}, + {358, "Cisco Unified Personal Communicator"}, + {365, "Cisco 7921"}, + {369, "Cisco 7906"}, + {375, "Cisco TelePresence"}, + {404, "Cisco 7962"}, + {412, "Cisco 3951"}, + {431, "Cisco 7937"}, + {434, "Cisco 7942"}, + {435, "Cisco 7945"}, + {436, "Cisco 7965"}, + {437, "Cisco 7975"}, + {446, "Cisco 3911"}, + {20000, "Cisco 7905"}, + {30002, "Cisco 7920"}, + {30006, "Cisco 7970"}, + {30007, "Cisco 7912"}, + {30008, "Cisco 7902"}, + {30018, "Cisco 7961"}, + {30019, "Cisco 7936"}, + {30027, "Cisco Analog Phone"}, + {30028, "Cisco ISDN BRI Phone"}, + {30032, "Cisco SCCP gateway virtual phone"}, + {30035, "Cisco IP-STE"}, {0, NULL} }; SKINNY_DECLARE_ID2STR(skinny_device_type2str, SKINNY_DEVICE_TYPES, "UnknownDeviceType") diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.h b/src/mod/endpoints/mod_skinny/skinny_tables.h index a35e4be18d..b4b1e1bb66 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.h +++ b/src/mod/endpoints/mod_skinny/skinny_tables.h @@ -92,7 +92,7 @@ const char *skinny_message_type2str(uint32_t id); uint32_t skinny_str2message_type(const char *str); #define SKINNY_PUSH_MESSAGE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_MESSAGE_TYPES) -extern struct skinny_table SKINNY_DEVICE_TYPES[16]; +extern struct skinny_table SKINNY_DEVICE_TYPES[75]; const char *skinny_device_type2str(uint32_t id); uint32_t skinny_str2device_type(const char *str); #define SKINNY_PUSH_DEVICE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_DEVICE_TYPES) From 9bdfb187150c7b4bde31f0291e6ff881a16e08a7 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 11:05:18 -0500 Subject: [PATCH 090/278] skinny correct field name --- src/mod/endpoints/mod_skinny/skinny_protocol.c | 4 ++-- src/mod/endpoints/mod_skinny/skinny_protocol.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index eb472167ee..fa27bd1de3 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -155,8 +155,8 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) memcpy(request, mbuf, bytes); #ifdef SKINNY_MEGA_DEBUG switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, - "Got request: length=%d,reserved=%x,type=%x\n", - request->length,request->reserved,request->type); + "Got request: length=%d,version=%x,type=%x\n", + request->length,request->version,request->type); #endif if(request->length < SKINNY_MESSAGE_FIELD_SIZE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index dcea93f891..d3b84ffabc 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -702,7 +702,7 @@ union skinny_data { #endif /* - * header is length+reserved + * header is length+version * body is type+data * length is length of body */ From 76c3f4be9851db5bf80090071ed8ed804d93fdac Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 11:05:52 -0500 Subject: [PATCH 091/278] add a few additional skinny message types --- src/mod/endpoints/mod_skinny/skinny_protocol.h | 11 +++++++++++ src/mod/endpoints/mod_skinny/skinny_tables.c | 3 +++ src/mod/endpoints/mod_skinny/skinny_tables.h | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index d3b84ffabc..f4f89f4c6b 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -145,6 +145,8 @@ struct PACKED capabilities_res_message { struct station_capabilities caps[SWITCH_MAX_CODECS]; }; +#define SERVER_REQ_MESSAGE 0x0012 + /* AlarmMessage */ #define ALARM_MESSAGE 0x0020 struct PACKED alarm_message { @@ -207,6 +209,8 @@ struct PACKED data_message { #define DEVICE_TO_USER_DATA_RESPONSE_MESSAGE 0x002F /* See struct PACKED data_message */ +#define DEVICE_UPDATECAPABILITIES 0x0030 + /* ServiceUrlStatReqMessage */ #define SERVICE_URL_STAT_REQ_MESSAGE 0x0033 struct PACKED service_url_stat_req_message { @@ -442,6 +446,8 @@ struct PACKED register_reject_message { char error[33]; }; +#define SERVER_RESPONSE_MESSAGE 0x009E + /* ResetMessage */ #define RESET_MESSAGE 0x009F struct PACKED reset_message { @@ -723,6 +729,8 @@ typedef struct skinny_message skinny_message_t; /* SKINNY TYPES */ /*****************************************************************************/ enum skinny_codecs { + SKINNY_CODEC_NONE = 0, + SKINNY_CODEC_NONSTANDARD = 1, SKINNY_CODEC_ALAW_64K = 2, SKINNY_CODEC_ALAW_56K = 3, SKINNY_CODEC_ULAW_64K = 4, @@ -744,6 +752,8 @@ enum skinny_codecs { SKINNY_CODEC_WIDEBAND_256K = 25, SKINNY_CODEC_DATA_64K = 32, SKINNY_CODEC_DATA_56K = 33, + SKINNY_CODEC_G722_1_32K = 40, + SKINNY_CODEC_G722_1_24K = 41, SKINNY_CODEC_GSM = 80, SKINNY_CODEC_ACTIVEVOICE = 81, SKINNY_CODEC_G726_32K = 82, @@ -754,6 +764,7 @@ enum skinny_codecs { SKINNY_CODEC_H261 = 100, SKINNY_CODEC_H263 = 101, SKINNY_CODEC_VIDEO = 102, + SKINNY_CODEC_H264 = 103, SKINNY_CODEC_T120 = 105, SKINNY_CODEC_H224 = 106, SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index e896245955..7a7027b049 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -61,6 +61,7 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = { {REGISTER_AVAILABLE_LINES_MESSAGE, "RegisterAvailableLinesMessage"}, {DEVICE_TO_USER_DATA_MESSAGE, "DeviceToUserDataMessage"}, {DEVICE_TO_USER_DATA_RESPONSE_MESSAGE, "DeviceToUserDataResponseMessage"}, + {DEVICE_UPDATECAPABILITIES, "DeviceUpdateCapabilities"}, {SERVICE_URL_STAT_REQ_MESSAGE, "ServiceUrlStatReqMessage"}, {FEATURE_STAT_REQ_MESSAGE, "FeatureStatReqMessage"}, {DEVICE_TO_USER_DATA_VERSION1_MESSAGE, "DeviceToUserDataVersion1Message"}, @@ -84,7 +85,9 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = { {BUTTON_TEMPLATE_RES_MESSAGE, "ButtonTemplateResMessage"}, {VERSION_MESSAGE, "VersionMessage"}, {CAPABILITIES_REQ_MESSAGE, "CapabilitiesReqMessage"}, + {SERVER_REQ_MESSAGE, "Server Request Message"}, {REGISTER_REJECT_MESSAGE, "RegisterRejectMessage"}, + {SERVER_RESPONSE_MESSAGE, "ServerResponseMessage"}, {RESET_MESSAGE, "ResetMessage"}, {KEEP_ALIVE_ACK_MESSAGE, "KeepAliveAckMessage"}, {OPEN_RECEIVE_CHANNEL_MESSAGE, "OpenReceiveChannelMessage"}, diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.h b/src/mod/endpoints/mod_skinny/skinny_tables.h index b4b1e1bb66..1e35a7d208 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.h +++ b/src/mod/endpoints/mod_skinny/skinny_tables.h @@ -87,7 +87,7 @@ if (my_matches) {\ } -extern struct skinny_table SKINNY_MESSAGE_TYPES[72]; +extern struct skinny_table SKINNY_MESSAGE_TYPES[75]; const char *skinny_message_type2str(uint32_t id); uint32_t skinny_str2message_type(const char *str); #define SKINNY_PUSH_MESSAGE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_MESSAGE_TYPES) From 802aa379fc851260371aad3ff83af7b0d44c08a2 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 11:15:46 -0500 Subject: [PATCH 092/278] skinny typedef --- src/mod/endpoints/mod_skinny/skinny_protocol.c | 2 +- src/mod/endpoints/mod_skinny/skinny_protocol.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index fa27bd1de3..3e1748d6e2 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -37,7 +37,7 @@ /*****************************************************************************/ /* SKINNY FUNCTIONS */ /*****************************************************************************/ -char* skinny_codec2string(enum skinny_codecs skinnycodec) +char* skinny_codec2string(skinny_codecs skinnycodec) { switch (skinnycodec) { case SKINNY_CODEC_ALAW_64K: diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index f4f89f4c6b..5127b1b54b 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -728,7 +728,7 @@ typedef struct skinny_message skinny_message_t; /*****************************************************************************/ /* SKINNY TYPES */ /*****************************************************************************/ -enum skinny_codecs { +typedef enum { SKINNY_CODEC_NONE = 0, SKINNY_CODEC_NONSTANDARD = 1, SKINNY_CODEC_ALAW_64K = 2, @@ -768,9 +768,9 @@ enum skinny_codecs { SKINNY_CODEC_T120 = 105, SKINNY_CODEC_H224 = 106, SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 -}; +} skinny_codecs; -char* skinny_codec2string(enum skinny_codecs skinnycodec); +char* skinny_codec2string(skinny_codecs skinnycodec); /*****************************************************************************/ /* SKINNY FUNCTIONS */ From 655325ab059c70d9e29430fadc408bb10f7bc148 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 11:39:23 -0500 Subject: [PATCH 093/278] fixup and merge in FS-4027 enhancements for additional device support --- .../endpoints/mod_skinny/skinny_protocol.c | 20 ++ .../endpoints/mod_skinny/skinny_protocol.h | 253 ++++++++++++++---- src/mod/endpoints/mod_skinny/skinny_server.c | 79 +++++- 3 files changed, 306 insertions(+), 46 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index 3e1748d6e2..677c703b39 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -623,6 +623,26 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, return skinny_send_reply_quiet(listener, message); } +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port) +{ + skinny_message_t *message; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.serv_res_mess)); + message->type = SERVER_RESPONSE_MESSAGE; + message->length = 4 + sizeof(message->data.serv_res_mess); + + message->data.serv_res_mess.serverListenPort[0] = port; + switch_inet_pton(AF_INET,ip, &message->data.serv_res_mess.serverIpAddr[0]); + switch_copy_string(message->data.serv_res_mess.server[0].serverName,ip,sizeof(message->data.serv_res_mess.server[0].serverName)); + + skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, + "Sending Server Request Response with IP (%s) and Port (%d)\n", ip, port); + + return skinny_send_reply(listener, message); +} + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index 5127b1b54b..9bdd584b69 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -36,6 +36,53 @@ /* mod_skinny.h should be loaded first */ #include "mod_skinny.h" +/*****************************************************************************/ +/* SKINNY TYPES */ +/*****************************************************************************/ +typedef enum { + SKINNY_CODEC_NONE = 0, + SKINNY_CODEC_NONSTANDARD = 1, + SKINNY_CODEC_ALAW_64K = 2, + SKINNY_CODEC_ALAW_56K = 3, + SKINNY_CODEC_ULAW_64K = 4, + SKINNY_CODEC_ULAW_56K = 5, + SKINNY_CODEC_G722_64K = 6, + SKINNY_CODEC_G722_56K = 7, + SKINNY_CODEC_G722_48K = 8, + SKINNY_CODEC_G723_1 = 9, + SKINNY_CODEC_G728 = 10, + SKINNY_CODEC_G729 = 11, + SKINNY_CODEC_G729A = 12, + SKINNY_CODEC_IS11172 = 13, + SKINNY_CODEC_IS13818 = 14, + SKINNY_CODEC_G729B = 15, + SKINNY_CODEC_G729AB = 16, + SKINNY_CODEC_GSM_FULL = 18, + SKINNY_CODEC_GSM_HALF = 19, + SKINNY_CODEC_GSM_EFULL = 20, + SKINNY_CODEC_WIDEBAND_256K = 25, + SKINNY_CODEC_DATA_64K = 32, + SKINNY_CODEC_DATA_56K = 33, + SKINNY_CODEC_G722_1_32K = 40, + SKINNY_CODEC_G722_1_24K = 41, + SKINNY_CODEC_GSM = 80, + SKINNY_CODEC_ACTIVEVOICE = 81, + SKINNY_CODEC_G726_32K = 82, + SKINNY_CODEC_G726_24K = 83, + SKINNY_CODEC_G726_16K = 84, + SKINNY_CODEC_G729B_BIS = 85, + SKINNY_CODEC_G729B_LOW = 86, + SKINNY_CODEC_H261 = 100, + SKINNY_CODEC_H263 = 101, + SKINNY_CODEC_VIDEO = 102, + SKINNY_CODEC_H264 = 103, + SKINNY_CODEC_T120 = 105, + SKINNY_CODEC_H224 = 106, + SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 +} skinny_codecs; + +char* skinny_codec2string(skinny_codecs skinnycodec); + /*****************************************************************************/ /* SKINNY MESSAGE DATA */ /*****************************************************************************/ @@ -211,6 +258,145 @@ struct PACKED data_message { #define DEVICE_UPDATECAPABILITIES 0x0030 +#define MAX_CUSTOM_PICTURES 6 +#define MAX_LAYOUT_WITH_SAME_SERVICE 5 +#define MAX_SERVICE_TYPE 4 +#define SKINNY_MAX_CAPABILITIES 18 /*!< max capabilities allowed in Cap response message */ +#define SKINNY_MAX_VIDEO_CAPABILITIES 10 +#define SKINNY_MAX_DATA_CAPABILITIES 5 +#define MAX_LEVEL_PREFERENCE 4 + +/*! + * \brief Picture Format Structure + */ +typedef struct { + uint32_t customPictureFormatWidth; /*!< Picture Width */ + uint32_t customPictureFormatHeight; /*!< Picture Height */ + uint32_t customPictureFormatpixelAspectRatio; /*!< Picture Pixel Aspect Ratio */ + uint32_t customPictureFormatpixelclockConversionCode; /*!< Picture Pixel Conversion Code */ + uint32_t customPictureFormatpixelclockDivisor; /*!< Picture Pixel Divisor */ +} customPictureFormat_t; + + +/*! + * \brief Video Level Preference Structure + */ +typedef struct { + uint32_t transmitPreference; /*!< Transmit Preference */ + uint32_t format; /*!< Format / Codec */ + uint32_t maxBitRate; /*!< Maximum BitRate */ + uint32_t minBitRate; /*!< Minimum BitRate */ + uint32_t MPI; /*!< */ + uint32_t serviceNumber; /*!< Service Number */ +} levelPreference_t; /*!< Level Preference Structure */ + +/*! + * \brief Layout Config Structure (Update Capabilities Message Struct) + * \since 20080111 + */ +typedef struct { + uint32_t layout; /*!< Layout \todo what is layout? */ +} layoutConfig_t; /*!< Layout Config Structure */ + + +/*! + * \brief Service Resource Structure + */ +typedef struct { + uint32_t layoutCount; /*!< Layout Count */ + layoutConfig_t layout[MAX_LAYOUT_WITH_SAME_SERVICE]; /*!< Layout */ + uint32_t serviceNum; /*!< Service Number */ + uint32_t maxStreams; /*!< Maximum number of Streams */ + uint32_t maxConferences; /*!< Maximum number of Conferences */ + uint32_t activeConferenceOnRegistration; /*!< Active Conference On Registration */ +} serviceResource_t; + + + +/*! + * \brief Audio Capabilities Structure + */ +typedef struct { + skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ + uint32_t lel_maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */ + uint32_t lel_unknown[2]; /*!< this are related to G.723 */ +} audioCap_t; + +/*! + * \brief Video Capabilities Structure + */ +typedef struct { + skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ + uint32_t lel_transmitOreceive; /*!< Transmit of Receive */ + uint32_t lel_levelPreferenceCount; /*!< Level of Preference Count */ + + levelPreference_t levelPreference[MAX_LEVEL_PREFERENCE]; /*!< Level Preference */ + +// uint32_t lel_codec_options[2]; /*!< Codec Options */ + + union { + struct { + uint32_t unknown1; + uint32_t unknown2; + } h263; + struct { + uint32_t profile; /*!< H264 profile */ + uint32_t level; /*!< H264 level */ + } h264; + } codec_options; + + /** + * Codec options contains data specific for every codec + * + * Here is a list of known parameters per codec + // H.261 + uint32_t lel_temporalSpatialTradeOffCapability; + uint32_t lel_stillImageTransmission; + + // H.263 + uint32_t lel_h263_capability_bitfield; + uint32_t lel_annexNandWFutureUse; + + // Video + uint32_t lel_modelNumber; + uint32_t lel_bandwidth; + */ +} videoCap_t; /*!< Video Capabilities Structure */ + +/*! + * \brief Data Capabilities Structure + */ +typedef struct { + uint32_t payloadCapability; /*!< Payload Capability */ + uint32_t transmitOrReceive; /*!< Transmit or Receive */ + uint32_t protocolDependentData; /*!< Protocol Dependent Data */ + uint32_t maxBitRate; /*!< Maximum BitRate */ +} dataCap_t; /*!< Data Capabilities Structure */ + + +struct PACKED update_capabilities_message { + uint32_t lel_audioCapCount; /*!< Audio Capability Count */ + uint32_t lel_videoCapCount; /*!< Video Capability Count */ + uint32_t lel_dataCapCount; /*!< Data Capability Count */ + uint32_t RTPPayloadFormat; /*!< RTP Payload Format */ + uint32_t customPictureFormatCount; /*!< Custom Picture Format Count */ + + customPictureFormat_t customPictureFormat[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */ + + uint32_t activeStreamsOnRegistration; /*!< Active Streams on Registration */ + uint32_t maxBW; /*!< Max BW ?? */ + + uint32_t serviceResourceCount; /*!< Service Resource Count */ + serviceResource_t serviceResource[MAX_SERVICE_TYPE]; /*!< Service Resource */ + + audioCap_t audioCaps[SKINNY_MAX_CAPABILITIES]; /*!< Audio Capabilities */ + videoCap_t videoCaps[SKINNY_MAX_VIDEO_CAPABILITIES]; /*!< Video Capabilities */ + dataCap_t dataCaps[SKINNY_MAX_DATA_CAPABILITIES]; /*!< Data Capabilities */ + + uint32_t unknown; /*!< Unknown */ +}; + + /* ServiceUrlStatReqMessage */ #define SERVICE_URL_STAT_REQ_MESSAGE 0x0033 struct PACKED service_url_stat_req_message { @@ -447,6 +633,21 @@ struct PACKED register_reject_message { }; #define SERVER_RESPONSE_MESSAGE 0x009E +#define ServerMaxNameSize 48 +#define StationMaxServers 5 +/*! + * \brief Station Identifier Structure + */ +typedef struct { + char serverName[ServerMaxNameSize]; /*!< Server Name */ +} ServerIdentifier; + +struct PACKED server_response_message { + ServerIdentifier server[StationMaxServers]; /*!< Server Identifier */ + uint32_t serverListenPort[StationMaxServers]; /*!< Server is Listening on Port */ + uint32_t serverIpAddr[StationMaxServers]; /*!< Server IP Port */ +}; /*!< Server Result Message Structure */ + /* ResetMessage */ #define RESET_MESSAGE 0x009F @@ -676,6 +877,7 @@ union skinny_data { /* no data for CAPABILITIES_REQ_MESSAGE */ struct register_reject_message reg_rej; struct reset_message reset; + struct server_response_message serv_res_mess; /* no data for KEEP_ALIVE_ACK_MESSAGE */ struct open_receive_channel_message open_receive_channel; struct close_receive_channel_message close_receive_channel; @@ -696,6 +898,7 @@ union skinny_data { /* see field "extended_data" for USER_TO_DEVICE_DATA_VERSION1_MESSAGE */ struct dialed_phone_book_ack_message dialed_phone_book_ack; + struct update_capabilities_message upd_cap; struct data_message data; struct extended_data_message extended_data; @@ -725,52 +928,7 @@ struct PACKED skinny_message { typedef struct skinny_message skinny_message_t; -/*****************************************************************************/ -/* SKINNY TYPES */ -/*****************************************************************************/ -typedef enum { - SKINNY_CODEC_NONE = 0, - SKINNY_CODEC_NONSTANDARD = 1, - SKINNY_CODEC_ALAW_64K = 2, - SKINNY_CODEC_ALAW_56K = 3, - SKINNY_CODEC_ULAW_64K = 4, - SKINNY_CODEC_ULAW_56K = 5, - SKINNY_CODEC_G722_64K = 6, - SKINNY_CODEC_G722_56K = 7, - SKINNY_CODEC_G722_48K = 8, - SKINNY_CODEC_G723_1 = 9, - SKINNY_CODEC_G728 = 10, - SKINNY_CODEC_G729 = 11, - SKINNY_CODEC_G729A = 12, - SKINNY_CODEC_IS11172 = 13, - SKINNY_CODEC_IS13818 = 14, - SKINNY_CODEC_G729B = 15, - SKINNY_CODEC_G729AB = 16, - SKINNY_CODEC_GSM_FULL = 18, - SKINNY_CODEC_GSM_HALF = 19, - SKINNY_CODEC_GSM_EFULL = 20, - SKINNY_CODEC_WIDEBAND_256K = 25, - SKINNY_CODEC_DATA_64K = 32, - SKINNY_CODEC_DATA_56K = 33, - SKINNY_CODEC_G722_1_32K = 40, - SKINNY_CODEC_G722_1_24K = 41, - SKINNY_CODEC_GSM = 80, - SKINNY_CODEC_ACTIVEVOICE = 81, - SKINNY_CODEC_G726_32K = 82, - SKINNY_CODEC_G726_24K = 83, - SKINNY_CODEC_G726_16K = 84, - SKINNY_CODEC_G729B_BIS = 85, - SKINNY_CODEC_G729B_LOW = 86, - SKINNY_CODEC_H261 = 100, - SKINNY_CODEC_H263 = 101, - SKINNY_CODEC_VIDEO = 102, - SKINNY_CODEC_H264 = 103, - SKINNY_CODEC_T120 = 105, - SKINNY_CODEC_H224 = 106, - SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 -} skinny_codecs; -char* skinny_codec2string(skinny_codecs skinnycodec); /*****************************************************************************/ /* SKINNY FUNCTIONS */ @@ -862,6 +1020,11 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, uint32_t mode); #define send_set_speaker_mode(listener, ...) perform_send_set_speaker_mode(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port); +#define send_srvreq_response(listener, ...) perform_send_srvreq_response(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 2b7031186d..450584b554 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -2184,6 +2184,79 @@ switch_status_t skinny_handle_accessory_status_message(listener_t *listener, ski return SWITCH_STATUS_SUCCESS; } +switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_profile_t *profile; + + uint32_t i = 0; + uint32_t n = 0; + char *codec_order[SWITCH_MAX_CODECS]; + char *codec_string; + + size_t string_len, string_pos, pos; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount)); + + n = request->data.upd_cap.lel_audioCapCount; + if (n > SWITCH_MAX_CODECS) { + n = SWITCH_MAX_CODECS; + } + string_len = -1; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount) + n * sizeof(request->data.upd_cap.audioCaps[0])); + + for (i = 0; i < n; i++) { + char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].lel_payloadCapability); + codec_order[i] = codec; + string_len += strlen(codec)+1; + } + i = 0; + pos = 0; + codec_string = switch_core_alloc(listener->pool, string_len+1); + for (string_pos = 0; string_pos < string_len; string_pos++) { + char *codec = codec_order[i]; + switch_assert(i < n); + if(pos == strlen(codec)) { + codec_string[string_pos] = ','; + i++; + pos = 0; + } else { + codec_string[string_pos] = codec[pos++]; + } + } + codec_string[string_len] = '\0'; + if ((sql = switch_mprintf( + "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'", + codec_string, + listener->device_name + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Codecs %s supported.\n", codec_string); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t skinny_handle_server_req_message(listener_t *listener, skinny_message_t *request) +{ + skinny_profile_t *profile; + + profile = listener->profile; + + skinny_log_l(listener, SWITCH_LOG_INFO, "Received Server Request Message (length=%d).\n", request->length); + + send_srvreq_response(listener, profile->ip, profile->port); + return SWITCH_STATUS_SUCCESS; +} + switch_status_t skinny_handle_xml_alarm(listener_t *listener, skinny_message_t *request) { switch_event_t *event = NULL; @@ -2209,7 +2282,7 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re skinny_log_l(listener, SWITCH_LOG_DEBUG, "Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); } - if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE) { + if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE && request->type != KEEP_ALIVE_MESSAGE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Device should send a register message first. Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); return SWITCH_STATUS_FALSE; @@ -2281,6 +2354,10 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re return skinny_handle_accessory_status_message(listener, request); case XML_ALARM_MESSAGE: return skinny_handle_xml_alarm(listener, request); + case DEVICE_UPDATECAPABILITIES: + return skinny_handle_updatecapabilities(listener, request); + case SERVER_REQ_MESSAGE: + return skinny_handle_server_req_message(listener, request); default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); From a8b4ccf7fb78cb935eada6a30661e8634c6f5d82 Mon Sep 17 00:00:00 2001 From: Brian West Date: Fri, 12 Jul 2013 11:50:03 -0500 Subject: [PATCH 094/278] FS-5555 improvement --resolve --- src/include/switch_utils.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 755ac91d27..daceb854a6 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -598,31 +598,39 @@ switch_mutex_unlock(obj->flag_mutex); #define switch_set_string(_dst, _src) switch_copy_string(_dst, _src, sizeof(_dst)) +SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); +SWITCH_DECLARE(char *) switch_url_decode(char *s); static inline char *switch_sanitize_number(char *number) { - char *p = number, *q; + char *p, *q; char warp[] = "/:"; int i; + char *val; switch_assert(number); - if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@') || strchr(p, '%'))) { + p = strdup(number); + val = p; + switch_url_decode(val); + + if (!(strchr(val, '/') || strchr(val, ':') || strchr(val, '@') || strchr(val, '%'))) { return number; } - while ((q = strrchr(p, '@'))) + while ((q = strrchr(val, '@'))) *q = '\0'; - while ((q = strrchr(p, '%'))) + while ((q = strrchr(val, '%'))) *q = '\0'; for (i = 0; i < (int) strlen(warp); i++) { - while (p && (q = strchr(p, warp[i]))) - p = q + 1; + while (val && (q = strchr(val, warp[i]))) + val = q + 1; } + free(p); - return p; + return val; } static inline switch_bool_t switch_string_var_check(char *s, switch_bool_t disable) @@ -930,8 +938,6 @@ SWITCH_DECLARE(char *) switch_util_quote_shell_arg_pool(const char *string, swit #define SWITCH_READ_ACCEPTABLE(status) (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) -SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); -SWITCH_DECLARE(char *) switch_url_decode(char *s); SWITCH_DECLARE(switch_bool_t) switch_simple_email(const char *to, const char *from, const char *headers, From af9a18c63ce426d7b01c7d9c07fd043551c241ee Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 12 Jul 2013 12:00:57 -0500 Subject: [PATCH 095/278] FS-4027 - improve adherence to code conventions --- .../endpoints/mod_skinny/skinny_protocol.h | 52 +++++++++---------- src/mod/endpoints/mod_skinny/skinny_server.c | 10 ++-- src/mod/endpoints/mod_skinny/skinny_tables.c | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index 9bdd584b69..2fe1336aa6 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -256,7 +256,7 @@ struct PACKED data_message { #define DEVICE_TO_USER_DATA_RESPONSE_MESSAGE 0x002F /* See struct PACKED data_message */ -#define DEVICE_UPDATECAPABILITIES 0x0030 +#define UPDATE_CAPABILITIES_MESSAGE 0x0030 #define MAX_CUSTOM_PICTURES 6 #define MAX_LAYOUT_WITH_SAME_SERVICE 5 @@ -270,12 +270,12 @@ struct PACKED data_message { * \brief Picture Format Structure */ typedef struct { - uint32_t customPictureFormatWidth; /*!< Picture Width */ - uint32_t customPictureFormatHeight; /*!< Picture Height */ - uint32_t customPictureFormatpixelAspectRatio; /*!< Picture Pixel Aspect Ratio */ - uint32_t customPictureFormatpixelclockConversionCode; /*!< Picture Pixel Conversion Code */ - uint32_t customPictureFormatpixelclockDivisor; /*!< Picture Pixel Divisor */ -} customPictureFormat_t; + uint32_t custom_picture_format_width; /*!< Picture Width */ + uint32_t custom_picture_format_height; /*!< Picture Height */ + uint32_t custom_picture_format_pixelAspectRatio; /*!< Picture Pixel Aspect Ratio */ + uint32_t custom_picture_format_pixelclockConversionCode; /*!< Picture Pixel Conversion Code */ + uint32_t custom_picture_format_pixelclockDivisor; /*!< Picture Pixel Divisor */ +} custom_picture_format_t; /*! @@ -317,22 +317,22 @@ typedef struct { * \brief Audio Capabilities Structure */ typedef struct { - skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ - uint32_t lel_maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */ - uint32_t lel_unknown[2]; /*!< this are related to G.723 */ + skinny_codecs payload_capability; /*!< PayLoad Capability */ + uint32_t maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */ + uint32_t unknown[2]; /*!< this are related to G.723 */ } audioCap_t; /*! * \brief Video Capabilities Structure */ typedef struct { - skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ - uint32_t lel_transmitOreceive; /*!< Transmit of Receive */ - uint32_t lel_levelPreferenceCount; /*!< Level of Preference Count */ + skinny_codecs payload_capability; /*!< PayLoad Capability */ + uint32_t transmitOreceive; /*!< Transmit of Receive */ + uint32_t levelPreferenceCount; /*!< Level of Preference Count */ levelPreference_t levelPreference[MAX_LEVEL_PREFERENCE]; /*!< Level Preference */ -// uint32_t lel_codec_options[2]; /*!< Codec Options */ +// uint32_t codec_options[2]; /*!< Codec Options */ union { struct { @@ -350,16 +350,16 @@ typedef struct { * * Here is a list of known parameters per codec // H.261 - uint32_t lel_temporalSpatialTradeOffCapability; - uint32_t lel_stillImageTransmission; + uint32_t temporalSpatialTradeOffCapability; + uint32_t stillImageTransmission; // H.263 - uint32_t lel_h263_capability_bitfield; - uint32_t lel_annexNandWFutureUse; + uint32_t h263_capability_bitfield; + uint32_t annexNandWFutureUse; // Video - uint32_t lel_modelNumber; - uint32_t lel_bandwidth; + uint32_t modelNumber; + uint32_t bandwidth; */ } videoCap_t; /*!< Video Capabilities Structure */ @@ -367,7 +367,7 @@ typedef struct { * \brief Data Capabilities Structure */ typedef struct { - uint32_t payloadCapability; /*!< Payload Capability */ + uint32_t payload_capability; /*!< Payload Capability */ uint32_t transmitOrReceive; /*!< Transmit or Receive */ uint32_t protocolDependentData; /*!< Protocol Dependent Data */ uint32_t maxBitRate; /*!< Maximum BitRate */ @@ -375,13 +375,13 @@ typedef struct { struct PACKED update_capabilities_message { - uint32_t lel_audioCapCount; /*!< Audio Capability Count */ - uint32_t lel_videoCapCount; /*!< Video Capability Count */ - uint32_t lel_dataCapCount; /*!< Data Capability Count */ + uint32_t audio_cap_count; /*!< Audio Capability Count */ + uint32_t videoCapCount; /*!< Video Capability Count */ + uint32_t dataCapCount; /*!< Data Capability Count */ uint32_t RTPPayloadFormat; /*!< RTP Payload Format */ - uint32_t customPictureFormatCount; /*!< Custom Picture Format Count */ + uint32_t custom_picture_formatCount; /*!< Custom Picture Format Count */ - customPictureFormat_t customPictureFormat[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */ + custom_picture_format_t custom_picture_format[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */ uint32_t activeStreamsOnRegistration; /*!< Active Streams on Registration */ uint32_t maxBW; /*!< Max BW ?? */ diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 450584b554..bc06b9bdb3 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -2201,18 +2201,18 @@ switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_me profile = listener->profile; - skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount)); + skinny_check_data_length(request, sizeof(request->data.upd_cap.audio_cap_count)); - n = request->data.upd_cap.lel_audioCapCount; + n = request->data.upd_cap.audio_cap_count; if (n > SWITCH_MAX_CODECS) { n = SWITCH_MAX_CODECS; } string_len = -1; - skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount) + n * sizeof(request->data.upd_cap.audioCaps[0])); + skinny_check_data_length(request, sizeof(request->data.upd_cap.audio_cap_count) + n * sizeof(request->data.upd_cap.audioCaps[0])); for (i = 0; i < n; i++) { - char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].lel_payloadCapability); + char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].payload_capability); codec_order[i] = codec; string_len += strlen(codec)+1; } @@ -2354,7 +2354,7 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re return skinny_handle_accessory_status_message(listener, request); case XML_ALARM_MESSAGE: return skinny_handle_xml_alarm(listener, request); - case DEVICE_UPDATECAPABILITIES: + case UPDATE_CAPABILITIES_MESSAGE: return skinny_handle_updatecapabilities(listener, request); case SERVER_REQ_MESSAGE: return skinny_handle_server_req_message(listener, request); diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index 7a7027b049..9f04f8cd63 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -61,7 +61,7 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = { {REGISTER_AVAILABLE_LINES_MESSAGE, "RegisterAvailableLinesMessage"}, {DEVICE_TO_USER_DATA_MESSAGE, "DeviceToUserDataMessage"}, {DEVICE_TO_USER_DATA_RESPONSE_MESSAGE, "DeviceToUserDataResponseMessage"}, - {DEVICE_UPDATECAPABILITIES, "DeviceUpdateCapabilities"}, + {UPDATE_CAPABILITIES_MESSAGE, "DeviceUpdateCapabilities"}, {SERVICE_URL_STAT_REQ_MESSAGE, "ServiceUrlStatReqMessage"}, {FEATURE_STAT_REQ_MESSAGE, "FeatureStatReqMessage"}, {DEVICE_TO_USER_DATA_VERSION1_MESSAGE, "DeviceToUserDataVersion1Message"}, From f9c5f8544400ff9e19cbe52b7882794367b425b5 Mon Sep 17 00:00:00 2001 From: Brian West Date: Fri, 12 Jul 2013 13:03:28 -0500 Subject: [PATCH 096/278] REVERT FS-5555 --- src/include/switch_utils.h | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index daceb854a6..40630d8c9f 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -598,39 +598,28 @@ switch_mutex_unlock(obj->flag_mutex); #define switch_set_string(_dst, _src) switch_copy_string(_dst, _src, sizeof(_dst)) -SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); -SWITCH_DECLARE(char *) switch_url_decode(char *s); static inline char *switch_sanitize_number(char *number) { - char *p, *q; + char *p = number, *q; char warp[] = "/:"; int i; - char *val; switch_assert(number); - p = strdup(number); - val = p; - switch_url_decode(val); - - if (!(strchr(val, '/') || strchr(val, ':') || strchr(val, '@') || strchr(val, '%'))) { + if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@'))) { return number; } - while ((q = strrchr(val, '@'))) + while ((q = strrchr(p, '@'))) *q = '\0'; - while ((q = strrchr(val, '%'))) - *q = '\0'; - for (i = 0; i < (int) strlen(warp); i++) { - while (val && (q = strchr(val, warp[i]))) - val = q + 1; + while (p && (q = strchr(p, warp[i]))) + p = q + 1; } - free(p); - return val; + return p; } static inline switch_bool_t switch_string_var_check(char *s, switch_bool_t disable) @@ -938,6 +927,8 @@ SWITCH_DECLARE(char *) switch_util_quote_shell_arg_pool(const char *string, swit #define SWITCH_READ_ACCEPTABLE(status) (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) +SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); +SWITCH_DECLARE(char *) switch_url_decode(char *s); SWITCH_DECLARE(switch_bool_t) switch_simple_email(const char *to, const char *from, const char *headers, From 647f3fd3fb4260dc88bbe6c01fe97a372878c1a7 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 12 Jul 2013 13:35:59 -0500 Subject: [PATCH 097/278] FS-5396 --resolve --- src/mod/endpoints/mod_sofia/mod_sofia.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 98d2228e96..b8b00ac758 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -4275,6 +4275,24 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session } } else { host++; + + if (!strchr(host, '.')) { + struct sockaddr_in sa; + struct hostent *he = gethostbyname(host); + char *ip, *tmp; + + if (he) { + memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); + ip = inet_ntoa(sa.sin_addr); + + tmp = switch_string_replace(dest, host, ip); + //host = switch_core_session_strdup(nsession, ip); + //dest = switch_core_session_strdup(nsession, tmp); + switch_channel_set_variable_printf(nchannel, "sip_route_uri", "sip:%s", tmp); + free(tmp); + } + } + tech_pvt->dest = switch_core_session_alloc(nsession, strlen(dest) + 5); tech_pvt->e_dest = switch_core_session_strdup(nsession, dest); switch_snprintf(tech_pvt->dest, strlen(dest) + 5, "sip:%s", dest); From 57a13694f727680dc9e3b03ba987dd121dd34bfb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 12 Jul 2013 13:54:50 -0500 Subject: [PATCH 098/278] use this and send {sip_gethostbyname=true} in the dial string to force this behaviour --- src/mod/endpoints/mod_sofia/mod_sofia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index b8b00ac758..d8cd0219f8 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -4276,7 +4276,7 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session } else { host++; - if (!strchr(host, '.')) { + if (!strchr(host, '.') || switch_true(switch_event_get_header(var_event, "sip_gethostbyname"))) { struct sockaddr_in sa; struct hostent *he = gethostbyname(host); char *ip, *tmp; From 82ffd425c7f0598ae26080c81326b400ea05c922 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 12 Jul 2013 14:27:10 -0500 Subject: [PATCH 099/278] cleanup --- src/switch_core_media.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 9e21e458eb..6f2fa533a1 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -2464,9 +2464,6 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s if (m->m_proto_name && !strcasecmp(m->m_proto_name, "UDP/TLS/RTP/SAVPF")) { switch_channel_set_flag(session->channel, CF_WEBRTC_MOZ); - printf("PRICK FACE 1\n"); - } else { - printf("PRICK FACE 2 [%s]\n", m->m_proto_name); } if (m->m_proto == sdp_proto_srtp || m->m_proto == sdp_proto_extended_srtp) { From 92452ad30990d69fc9649a66aaf2404f99e287ea Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Fri, 12 Jul 2013 14:27:21 -0500 Subject: [PATCH 100/278] fix windows compiler error --- src/switch_rtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index f1ceb1d5c3..661ce3f7d4 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -34,8 +34,6 @@ //#define RTP_DEBUG_WRITE_DELTA //#define DEBUG_MISSED_SEQ -#define FIR_COUNTDOWN 100 - #include #ifndef _MSC_VER #include @@ -56,6 +54,8 @@ #include #include +#define FIR_COUNTDOWN 100 + #define READ_INC(rtp_session) switch_mutex_lock(rtp_session->read_mutex); rtp_session->reading++ #define READ_DEC(rtp_session) switch_mutex_unlock(rtp_session->read_mutex); rtp_session->reading-- #define WRITE_INC(rtp_session) switch_mutex_lock(rtp_session->write_mutex); rtp_session->writing++ From c14d9c436eaf621d064be6940a1e3b8c4a8f4e5b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 12 Jul 2013 14:06:29 -0500 Subject: [PATCH 101/278] FS-5575 --resolve --- libs/esl/java/esl_wrap.cpp | 69 +++++- .../org/freeswitch/esl/ESLconnection.java | 14 +- libs/esl/java/org/freeswitch/esl/eslJNI.java | 8 +- libs/esl/lua/esl_wrap.cpp | 110 ++++++++- libs/esl/managed/ESLPINVOKE.cs | 12 +- libs/esl/managed/ESLconnection.cs | 12 +- libs/esl/managed/esl_wrap.cpp | 40 ++- libs/esl/perl/esl_wrap.cpp | 227 ++++++++++++++++-- libs/esl/php/esl_wrap.cpp | 126 +++++++++- libs/esl/python/esl_wrap.cpp | 172 ++++++++++++- libs/esl/ruby/esl_wrap.cpp | 153 +++++++++++- libs/esl/src/esl_oop.cpp | 14 ++ libs/esl/src/include/esl_oop.h | 2 + 13 files changed, 907 insertions(+), 52 deletions(-) diff --git a/libs/esl/java/esl_wrap.cpp b/libs/esl/java/esl_wrap.cpp index 0550231dde..c5c4fa60c2 100644 --- a/libs/esl/java/esl_wrap.cpp +++ b/libs/esl/java/esl_wrap.cpp @@ -634,7 +634,70 @@ SWIGEXPORT jstring JNICALL Java_org_freeswitch_esl_eslJNI_ESLevent_1nextHeader(J } -SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2, jstring jarg3, jstring jarg4) { +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jstring jarg1, jint jarg2, jstring jarg3, jstring jarg4) { + jlong jresult = 0 ; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + + (void)jenv; + (void)jcls; + arg1 = 0; + if (jarg1) { + arg1 = (char *)jenv->GetStringUTFChars(jarg1, 0); + if (!arg1) return 0; + } + arg2 = (int)jarg2; + arg3 = 0; + if (jarg3) { + arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); + if (!arg3) return 0; + } + arg4 = 0; + if (jarg4) { + arg4 = (char *)jenv->GetStringUTFChars(jarg4, 0); + if (!arg4) return 0; + } + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + *(ESLconnection **)&jresult = result; + if (arg1) jenv->ReleaseStringUTFChars(jarg1, (const char *)arg1); + if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + if (arg4) jenv->ReleaseStringUTFChars(jarg4, (const char *)arg4); + return jresult; +} + + +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jstring jarg1, jint jarg2, jstring jarg3) { + jlong jresult = 0 ; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + + (void)jenv; + (void)jcls; + arg1 = 0; + if (jarg1) { + arg1 = (char *)jenv->GetStringUTFChars(jarg1, 0); + if (!arg1) return 0; + } + arg2 = (int)jarg2; + arg3 = 0; + if (jarg3) { + arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); + if (!arg3) return 0; + } + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + *(ESLconnection **)&jresult = result; + if (arg1) jenv->ReleaseStringUTFChars(jarg1, (const char *)arg1); + if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + return jresult; +} + + +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2, jstring jarg3, jstring jarg4) { jlong jresult = 0 ; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -674,7 +737,7 @@ SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SW } -SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2, jstring jarg3) { +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_13(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2, jstring jarg3) { jlong jresult = 0 ; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -707,7 +770,7 @@ SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SW } -SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jint jarg1) { +SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_new_1ESLconnection_1_1SWIG_14(JNIEnv *jenv, jclass jcls, jint jarg1) { jlong jresult = 0 ; int arg1 ; ESLconnection *result = 0 ; diff --git a/libs/esl/java/org/freeswitch/esl/ESLconnection.java b/libs/esl/java/org/freeswitch/esl/ESLconnection.java index 588cefcc99..a45613473d 100644 --- a/libs/esl/java/org/freeswitch/esl/ESLconnection.java +++ b/libs/esl/java/org/freeswitch/esl/ESLconnection.java @@ -33,16 +33,24 @@ public class ESLconnection { swigCPtr = 0; } - public ESLconnection(String host, String port, String user, String password) { + public ESLconnection(String host, int port, String user, String password) { this(eslJNI.new_ESLconnection__SWIG_0(host, port, user, password), true); } - public ESLconnection(String host, String port, String password) { + public ESLconnection(String host, int port, String password) { this(eslJNI.new_ESLconnection__SWIG_1(host, port, password), true); } + public ESLconnection(String host, String port, String user, String password) { + this(eslJNI.new_ESLconnection__SWIG_2(host, port, user, password), true); + } + + public ESLconnection(String host, String port, String password) { + this(eslJNI.new_ESLconnection__SWIG_3(host, port, password), true); + } + public ESLconnection(int socket) { - this(eslJNI.new_ESLconnection__SWIG_2(socket), true); + this(eslJNI.new_ESLconnection__SWIG_4(socket), true); } public int socketDescriptor() { diff --git a/libs/esl/java/org/freeswitch/esl/eslJNI.java b/libs/esl/java/org/freeswitch/esl/eslJNI.java index ff3744e9df..e7da6f5fdf 100644 --- a/libs/esl/java/org/freeswitch/esl/eslJNI.java +++ b/libs/esl/java/org/freeswitch/esl/eslJNI.java @@ -31,9 +31,11 @@ class eslJNI { public final static native boolean ESLevent_delHeader(long jarg1, ESLevent jarg1_, String jarg2); public final static native String ESLevent_firstHeader(long jarg1, ESLevent jarg1_); public final static native String ESLevent_nextHeader(long jarg1, ESLevent jarg1_); - public final static native long new_ESLconnection__SWIG_0(String jarg1, String jarg2, String jarg3, String jarg4); - public final static native long new_ESLconnection__SWIG_1(String jarg1, String jarg2, String jarg3); - public final static native long new_ESLconnection__SWIG_2(int jarg1); + public final static native long new_ESLconnection__SWIG_0(String jarg1, int jarg2, String jarg3, String jarg4); + public final static native long new_ESLconnection__SWIG_1(String jarg1, int jarg2, String jarg3); + public final static native long new_ESLconnection__SWIG_2(String jarg1, String jarg2, String jarg3, String jarg4); + public final static native long new_ESLconnection__SWIG_3(String jarg1, String jarg2, String jarg3); + public final static native long new_ESLconnection__SWIG_4(int jarg1); public final static native void delete_ESLconnection(long jarg1); public final static native int ESLconnection_socketDescriptor(long jarg1, ESLconnection jarg1_); public final static native int ESLconnection_connected(long jarg1, ESLconnection jarg1_); diff --git a/libs/esl/lua/esl_wrap.cpp b/libs/esl/lua/esl_wrap.cpp index 9420f853bb..9dac86544d 100644 --- a/libs/esl/lua/esl_wrap.cpp +++ b/libs/esl/lua/esl_wrap.cpp @@ -2231,6 +2231,63 @@ static const char *swig_ESLevent_base_names[] = {0}; static swig_lua_class _wrap_class_ESLevent = { "ESLevent", &SWIGTYPE_p_ESLevent,_wrap_new_ESLevent, swig_delete_ESLevent, swig_ESLevent_methods, swig_ESLevent_attributes, swig_ESLevent_bases, swig_ESLevent_base_names }; static int _wrap_new_ESLconnection__SWIG_0(lua_State* L) { + int SWIG_arg = -1; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + + SWIG_check_num_args("ESLconnection",4,4) + if(!lua_isstring(L,1)) SWIG_fail_arg("ESLconnection",1,"char const *"); + if(!lua_isnumber(L,2)) SWIG_fail_arg("ESLconnection",2,"int const"); + if(!lua_isstring(L,3)) SWIG_fail_arg("ESLconnection",3,"char const *"); + if(!lua_isstring(L,4)) SWIG_fail_arg("ESLconnection",4,"char const *"); + arg1 = (char *)lua_tostring(L, 1); + arg2 = (int const)lua_tonumber(L, 2); + arg3 = (char *)lua_tostring(L, 3); + arg4 = (char *)lua_tostring(L, 4); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + SWIG_arg=0; + SWIG_NewPointerObj(L,result,SWIGTYPE_p_ESLconnection,1); SWIG_arg++; + return SWIG_arg; + + if(0) SWIG_fail; + +fail: + lua_error(L); + return SWIG_arg; +} + + +static int _wrap_new_ESLconnection__SWIG_1(lua_State* L) { + int SWIG_arg = -1; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + + SWIG_check_num_args("ESLconnection",3,3) + if(!lua_isstring(L,1)) SWIG_fail_arg("ESLconnection",1,"char const *"); + if(!lua_isnumber(L,2)) SWIG_fail_arg("ESLconnection",2,"int const"); + if(!lua_isstring(L,3)) SWIG_fail_arg("ESLconnection",3,"char const *"); + arg1 = (char *)lua_tostring(L, 1); + arg2 = (int const)lua_tonumber(L, 2); + arg3 = (char *)lua_tostring(L, 3); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + SWIG_arg=0; + SWIG_NewPointerObj(L,result,SWIGTYPE_p_ESLconnection,1); SWIG_arg++; + return SWIG_arg; + + if(0) SWIG_fail; + +fail: + lua_error(L); + return SWIG_arg; +} + + +static int _wrap_new_ESLconnection__SWIG_2(lua_State* L) { int SWIG_arg = -1; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -2260,7 +2317,7 @@ fail: } -static int _wrap_new_ESLconnection__SWIG_1(lua_State* L) { +static int _wrap_new_ESLconnection__SWIG_3(lua_State* L) { int SWIG_arg = -1; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -2287,7 +2344,7 @@ fail: } -static int _wrap_new_ESLconnection__SWIG_2(lua_State* L) { +static int _wrap_new_ESLconnection__SWIG_4(lua_State* L) { int SWIG_arg = -1; int arg1 ; ESLconnection *result = 0 ; @@ -2321,7 +2378,26 @@ static int _wrap_new_ESLconnection(lua_State* L) { _v = lua_isnumber(L,argv[0]); } if (_v) { - return _wrap_new_ESLconnection__SWIG_2(L); + return _wrap_new_ESLconnection__SWIG_4(L); + } + } + if (argc == 3) { + int _v; + { + _v = lua_isstring(L,argv[0]); + } + if (_v) { + { + _v = lua_isnumber(L,argv[1]); + } + if (_v) { + { + _v = lua_isstring(L,argv[2]); + } + if (_v) { + return _wrap_new_ESLconnection__SWIG_1(L); + } + } } } if (argc == 3) { @@ -2338,7 +2414,31 @@ static int _wrap_new_ESLconnection(lua_State* L) { _v = lua_isstring(L,argv[2]); } if (_v) { - return _wrap_new_ESLconnection__SWIG_1(L); + return _wrap_new_ESLconnection__SWIG_3(L); + } + } + } + } + if (argc == 4) { + int _v; + { + _v = lua_isstring(L,argv[0]); + } + if (_v) { + { + _v = lua_isnumber(L,argv[1]); + } + if (_v) { + { + _v = lua_isstring(L,argv[2]); + } + if (_v) { + { + _v = lua_isstring(L,argv[3]); + } + if (_v) { + return _wrap_new_ESLconnection__SWIG_0(L); + } } } } @@ -2361,7 +2461,7 @@ static int _wrap_new_ESLconnection(lua_State* L) { _v = lua_isstring(L,argv[3]); } if (_v) { - return _wrap_new_ESLconnection__SWIG_0(L); + return _wrap_new_ESLconnection__SWIG_2(L); } } } diff --git a/libs/esl/managed/ESLPINVOKE.cs b/libs/esl/managed/ESLPINVOKE.cs index 5ada2c148d..749ebeabfe 100644 --- a/libs/esl/managed/ESLPINVOKE.cs +++ b/libs/esl/managed/ESLPINVOKE.cs @@ -251,13 +251,19 @@ class ESLPINVOKE { public static extern string ESLevent_NextHeader(HandleRef jarg1); [DllImport("ESL", EntryPoint="CSharp_new_ESLconnection__SWIG_0")] - public static extern IntPtr new_ESLconnection__SWIG_0(string jarg1, string jarg2, string jarg3, string jarg4); + public static extern IntPtr new_ESLconnection__SWIG_0(string jarg1, int jarg2, string jarg3, string jarg4); [DllImport("ESL", EntryPoint="CSharp_new_ESLconnection__SWIG_1")] - public static extern IntPtr new_ESLconnection__SWIG_1(string jarg1, string jarg2, string jarg3); + public static extern IntPtr new_ESLconnection__SWIG_1(string jarg1, int jarg2, string jarg3); [DllImport("ESL", EntryPoint="CSharp_new_ESLconnection__SWIG_2")] - public static extern IntPtr new_ESLconnection__SWIG_2(int jarg1); + public static extern IntPtr new_ESLconnection__SWIG_2(string jarg1, string jarg2, string jarg3, string jarg4); + + [DllImport("ESL", EntryPoint="CSharp_new_ESLconnection__SWIG_3")] + public static extern IntPtr new_ESLconnection__SWIG_3(string jarg1, string jarg2, string jarg3); + + [DllImport("ESL", EntryPoint="CSharp_new_ESLconnection__SWIG_4")] + public static extern IntPtr new_ESLconnection__SWIG_4(int jarg1); [DllImport("ESL", EntryPoint="CSharp_delete_ESLconnection")] public static extern void delete_ESLconnection(HandleRef jarg1); diff --git a/libs/esl/managed/ESLconnection.cs b/libs/esl/managed/ESLconnection.cs index 2d1e6d4dc3..82efb907ed 100644 --- a/libs/esl/managed/ESLconnection.cs +++ b/libs/esl/managed/ESLconnection.cs @@ -38,13 +38,19 @@ public class ESLconnection : IDisposable { } } - public ESLconnection(string host, string port, string user, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_0(host, port, user, password), true) { + public ESLconnection(string host, int port, string user, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_0(host, port, user, password), true) { } - public ESLconnection(string host, string port, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_1(host, port, password), true) { + public ESLconnection(string host, int port, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_1(host, port, password), true) { } - public ESLconnection(int socket) : this(ESLPINVOKE.new_ESLconnection__SWIG_2(socket), true) { + public ESLconnection(string host, string port, string user, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_2(host, port, user, password), true) { + } + + public ESLconnection(string host, string port, string password) : this(ESLPINVOKE.new_ESLconnection__SWIG_3(host, port, password), true) { + } + + public ESLconnection(int socket) : this(ESLPINVOKE.new_ESLconnection__SWIG_4(socket), true) { } public int SocketDescriptor() { diff --git a/libs/esl/managed/esl_wrap.cpp b/libs/esl/managed/esl_wrap.cpp index 65da98a15a..010ece83e2 100644 --- a/libs/esl/managed/esl_wrap.cpp +++ b/libs/esl/managed/esl_wrap.cpp @@ -576,7 +576,41 @@ SWIGEXPORT char * SWIGSTDCALL CSharp_ESLevent_NextHeader(void * jarg1) { } -SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_0(char * jarg1, char * jarg2, char * jarg3, char * jarg4) { +SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_0(char * jarg1, int jarg2, char * jarg3, char * jarg4) { + void * jresult ; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + + arg1 = (char *)jarg1; + arg2 = (int)jarg2; + arg3 = (char *)jarg3; + arg4 = (char *)jarg4; + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_1(char * jarg1, int jarg2, char * jarg3) { + void * jresult ; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + + arg1 = (char *)jarg1; + arg2 = (int)jarg2; + arg3 = (char *)jarg3; + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_2(char * jarg1, char * jarg2, char * jarg3, char * jarg4) { void * jresult ; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -594,7 +628,7 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_0(char * jarg1, cha } -SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_1(char * jarg1, char * jarg2, char * jarg3) { +SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_3(char * jarg1, char * jarg2, char * jarg3) { void * jresult ; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -610,7 +644,7 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_1(char * jarg1, cha } -SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_2(int jarg1) { +SWIGEXPORT void * SWIGSTDCALL CSharp_new_ESLconnection__SWIG_4(int jarg1) { void * jresult ; int arg1 ; ESLconnection *result = 0 ; diff --git a/libs/esl/perl/esl_wrap.cpp b/libs/esl/perl/esl_wrap.cpp index 832289f156..e2b7eec88c 100644 --- a/libs/esl/perl/esl_wrap.cpp +++ b/libs/esl/perl/esl_wrap.cpp @@ -2673,6 +2673,117 @@ XS(_wrap_ESLevent_nextHeader) { XS(_wrap_new_ESLconnection__SWIG_0) { + { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 4) || (items > 4)) { + SWIG_croak("Usage: new_ESLconnection(host,port,user,password);"); + } + res1 = SWIG_AsCharPtrAndSize(ST(0), &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_ESLconnection" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_ESLconnection" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(ST(2), &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "new_ESLconnection" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + res4 = SWIG_AsCharPtrAndSize(ST(3), &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "new_ESLconnection" "', argument " "4"" of type '" "char const *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + ST(argvi) = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLconnection, SWIG_OWNER | SWIG_SHADOW); argvi++ ; + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + XSRETURN(argvi); + fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + SWIG_croak_null(); + } +} + + +XS(_wrap_new_ESLconnection__SWIG_1) { + { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 3) || (items > 3)) { + SWIG_croak("Usage: new_ESLconnection(host,port,password);"); + } + res1 = SWIG_AsCharPtrAndSize(ST(0), &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_ESLconnection" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_ESLconnection" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(ST(2), &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "new_ESLconnection" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + ST(argvi) = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLconnection, SWIG_OWNER | SWIG_SHADOW); argvi++ ; + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + XSRETURN(argvi); + fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_croak_null(); + } +} + + +XS(_wrap_new_ESLconnection__SWIG_2) { { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -2734,7 +2845,7 @@ XS(_wrap_new_ESLconnection__SWIG_0) { } -XS(_wrap_new_ESLconnection__SWIG_1) { +XS(_wrap_new_ESLconnection__SWIG_3) { { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -2785,7 +2896,7 @@ XS(_wrap_new_ESLconnection__SWIG_1) { } -XS(_wrap_new_ESLconnection__SWIG_2) { +XS(_wrap_new_ESLconnection__SWIG_4) { { int arg1 ; ESLconnection *result = 0 ; @@ -2855,8 +2966,10 @@ XS(_wrap_new_ESLconnection) { _rankm += _pi; _pi *= SWIG_MAXCASTRANK; { - int res = SWIG_AsCharPtrAndSize(ST(1), 0, NULL, 0); - _v = SWIG_CheckState(res); + { + int res = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), NULL); + _v = SWIG_CheckState(res); + } } if (!_v) goto check_2; _ranki += _v*_pi; @@ -2877,7 +2990,7 @@ XS(_wrap_new_ESLconnection) { } check_2: - if (items == 4) { + if (items == 3) { SWIG_TypeRank _ranki = 0; SWIG_TypeRank _rankm = 0; SWIG_TypeRank _pi = 1; @@ -2906,14 +3019,6 @@ XS(_wrap_new_ESLconnection) { _ranki += _v*_pi; _rankm += _pi; _pi *= SWIG_MAXCASTRANK; - { - int res = SWIG_AsCharPtrAndSize(ST(3), 0, NULL, 0); - _v = SWIG_CheckState(res); - } - if (!_v) goto check_3; - _ranki += _v*_pi; - _rankm += _pi; - _pi *= SWIG_MAXCASTRANK; if (!_index || (_ranki < _rank)) { _rank = _ranki; _index = 3; if (_rank == _rankm) goto dispatch; @@ -2921,14 +3026,108 @@ XS(_wrap_new_ESLconnection) { } check_3: + if (items == 4) { + SWIG_TypeRank _ranki = 0; + SWIG_TypeRank _rankm = 0; + SWIG_TypeRank _pi = 1; + int _v = 0; + { + int res = SWIG_AsCharPtrAndSize(ST(0), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_4; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + { + int res = SWIG_AsVal_int SWIG_PERL_CALL_ARGS_2(ST(1), NULL); + _v = SWIG_CheckState(res); + } + } + if (!_v) goto check_4; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + int res = SWIG_AsCharPtrAndSize(ST(2), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_4; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + int res = SWIG_AsCharPtrAndSize(ST(3), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_4; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + if (!_index || (_ranki < _rank)) { + _rank = _ranki; _index = 4; + if (_rank == _rankm) goto dispatch; + } + } + check_4: + + if (items == 4) { + SWIG_TypeRank _ranki = 0; + SWIG_TypeRank _rankm = 0; + SWIG_TypeRank _pi = 1; + int _v = 0; + { + int res = SWIG_AsCharPtrAndSize(ST(0), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_5; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + int res = SWIG_AsCharPtrAndSize(ST(1), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_5; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + int res = SWIG_AsCharPtrAndSize(ST(2), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_5; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + { + int res = SWIG_AsCharPtrAndSize(ST(3), 0, NULL, 0); + _v = SWIG_CheckState(res); + } + if (!_v) goto check_5; + _ranki += _v*_pi; + _rankm += _pi; + _pi *= SWIG_MAXCASTRANK; + if (!_index || (_ranki < _rank)) { + _rank = _ranki; _index = 5; + if (_rank == _rankm) goto dispatch; + } + } + check_5: + dispatch: switch(_index) { case 1: - ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_2); return; + ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_4); return; case 2: ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_1); return; case 3: + ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_3); return; + case 4: ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_0); return; + case 5: + ++PL_markstack_ptr; SWIG_CALLXS(_wrap_new_ESLconnection__SWIG_2); return; } } diff --git a/libs/esl/php/esl_wrap.cpp b/libs/esl/php/esl_wrap.cpp index b5fd7a7d52..8c91f25452 100644 --- a/libs/esl/php/esl_wrap.cpp +++ b/libs/esl/php/esl_wrap.cpp @@ -1796,6 +1796,93 @@ fail: ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_0) { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + zval **args[4]; + + SWIG_ResetError(); + if(ZEND_NUM_ARGS() != 4 || zend_get_parameters_array_ex(4, args) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[0]); + arg1 = (char *) Z_STRVAL_PP(args[0]); + /*@SWIG@*/; + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,7,CONVERT_INT_IN@*/ + convert_to_long_ex(args[1]); + arg2 = (int) Z_LVAL_PP(args[1]); + /*@SWIG@*/; + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[2]); + arg3 = (char *) Z_STRVAL_PP(args[2]); + /*@SWIG@*/; + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[3]); + arg4 = (char *) Z_STRVAL_PP(args[3]); + /*@SWIG@*/; + + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + { + SWIG_SetPointerZval(return_value, (void *)result, SWIGTYPE_p_ESLconnection, 1); + } + return; +fail: + zend_error(SWIG_ErrorCode(),SWIG_ErrorMsg()); +} + + +ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_1) { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + zval **args[3]; + + SWIG_ResetError(); + if(ZEND_NUM_ARGS() != 3 || zend_get_parameters_array_ex(3, args) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[0]); + arg1 = (char *) Z_STRVAL_PP(args[0]); + /*@SWIG@*/; + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,7,CONVERT_INT_IN@*/ + convert_to_long_ex(args[1]); + arg2 = (int) Z_LVAL_PP(args[1]); + /*@SWIG@*/; + + + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[2]); + arg3 = (char *) Z_STRVAL_PP(args[2]); + /*@SWIG@*/; + + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + { + SWIG_SetPointerZval(return_value, (void *)result, SWIGTYPE_p_ESLconnection, 1); + } + return; +fail: + zend_error(SWIG_ErrorCode(),SWIG_ErrorMsg()); +} + + +ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_2) { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) 0 ; @@ -1842,7 +1929,7 @@ fail: } -ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_1) { +ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_3) { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) 0 ; @@ -1882,7 +1969,7 @@ fail: } -ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_2) { +ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection__SWIG_4) { int arg1 ; ESLconnection *result = 0 ; zval **args[1]; @@ -1918,7 +2005,20 @@ ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection) { int _v; _v = (Z_TYPE_PP(argv[0]) == IS_LONG); if (_v) { - return _wrap_new_ESLconnection__SWIG_2(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return _wrap_new_ESLconnection__SWIG_4(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + } + if (argc == 3) { + int _v; + _v = (Z_TYPE_PP(argv[0]) == IS_STRING); + if (_v) { + _v = (Z_TYPE_PP(argv[1]) == IS_LONG); + if (_v) { + _v = (Z_TYPE_PP(argv[2]) == IS_STRING); + if (_v) { + return _wrap_new_ESLconnection__SWIG_1(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + } } } if (argc == 3) { @@ -1929,7 +2029,23 @@ ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection) { if (_v) { _v = (Z_TYPE_PP(argv[2]) == IS_STRING); if (_v) { - return _wrap_new_ESLconnection__SWIG_1(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return _wrap_new_ESLconnection__SWIG_3(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + } + } + } + if (argc == 4) { + int _v; + _v = (Z_TYPE_PP(argv[0]) == IS_STRING); + if (_v) { + _v = (Z_TYPE_PP(argv[1]) == IS_LONG); + if (_v) { + _v = (Z_TYPE_PP(argv[2]) == IS_STRING); + if (_v) { + _v = (Z_TYPE_PP(argv[3]) == IS_STRING); + if (_v) { + return _wrap_new_ESLconnection__SWIG_0(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } } } } @@ -1944,7 +2060,7 @@ ZEND_NAMED_FUNCTION(_wrap_new_ESLconnection) { if (_v) { _v = (Z_TYPE_PP(argv[3]) == IS_STRING); if (_v) { - return _wrap_new_ESLconnection__SWIG_0(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return _wrap_new_ESLconnection__SWIG_2(INTERNAL_FUNCTION_PARAM_PASSTHRU); } } } diff --git a/libs/esl/python/esl_wrap.cpp b/libs/esl/python/esl_wrap.cpp index 35d600e0b0..b12f21cab0 100644 --- a/libs/esl/python/esl_wrap.cpp +++ b/libs/esl/python/esl_wrap.cpp @@ -3786,6 +3786,124 @@ SWIGINTERN PyObject *ESLevent_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObj } SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_0(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + PyObject * obj3 = 0 ; + + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + if (!PyArg_ParseTuple(args,(char *)"OOOO:new_ESLconnection",&obj0,&obj1,&obj2,&obj3)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_ESLconnection" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_ESLconnection" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "new_ESLconnection" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + res4 = SWIG_AsCharPtrAndSize(obj3, &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "new_ESLconnection" "', argument " "4"" of type '" "char const *""'"); + } + arg4 = reinterpret_cast< char * >(buf4); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLconnection, SWIG_POINTER_NEW | 0 ); + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + SWIG_PYTHON_THREAD_END_BLOCK; + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + SWIG_PYTHON_THREAD_END_BLOCK; + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_1(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + if (!PyArg_ParseTuple(args,(char *)"OOO:new_ESLconnection",&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_ESLconnection" "', argument " "1"" of type '" "char const *""'"); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int(obj1, &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "new_ESLconnection" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "new_ESLconnection" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ESLconnection, SWIG_POINTER_NEW | 0 ); + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_PYTHON_THREAD_END_BLOCK; + return resultobj; +fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_PYTHON_THREAD_END_BLOCK; + return NULL; +} + + +SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_2(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -3853,7 +3971,7 @@ fail: } -SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_1(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { +SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_3(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; @@ -3909,7 +4027,7 @@ fail: } -SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_2(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { +SWIGINTERN PyObject *_wrap_new_ESLconnection__SWIG_4(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; int arg1 ; ESLconnection *result = 0 ; @@ -3957,7 +4075,26 @@ SWIGINTERN PyObject *_wrap_new_ESLconnection(PyObject *self, PyObject *args) { } if (_v) { SWIG_PYTHON_THREAD_END_BLOCK; - return _wrap_new_ESLconnection__SWIG_2(self, args); + return _wrap_new_ESLconnection__SWIG_4(self, args); + } + } + if (argc == 3) { + int _v; + int res = SWIG_AsCharPtrAndSize(argv[0], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_int(argv[1], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[2], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + SWIG_PYTHON_THREAD_END_BLOCK; + return _wrap_new_ESLconnection__SWIG_1(self, args); + } + } } } if (argc == 3) { @@ -3972,7 +4109,30 @@ SWIGINTERN PyObject *_wrap_new_ESLconnection(PyObject *self, PyObject *args) { _v = SWIG_CheckState(res); if (_v) { SWIG_PYTHON_THREAD_END_BLOCK; - return _wrap_new_ESLconnection__SWIG_1(self, args); + return _wrap_new_ESLconnection__SWIG_3(self, args); + } + } + } + } + if (argc == 4) { + int _v; + int res = SWIG_AsCharPtrAndSize(argv[0], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_int(argv[1], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[2], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[3], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + SWIG_PYTHON_THREAD_END_BLOCK; + return _wrap_new_ESLconnection__SWIG_0(self, args); + } } } } @@ -3992,7 +4152,7 @@ SWIGINTERN PyObject *_wrap_new_ESLconnection(PyObject *self, PyObject *args) { _v = SWIG_CheckState(res); if (_v) { SWIG_PYTHON_THREAD_END_BLOCK; - return _wrap_new_ESLconnection__SWIG_0(self, args); + return _wrap_new_ESLconnection__SWIG_2(self, args); } } } @@ -4003,6 +4163,8 @@ SWIGINTERN PyObject *_wrap_new_ESLconnection(PyObject *self, PyObject *args) { fail: SWIG_SetErrorMsg(PyExc_NotImplementedError,"Wrong number of arguments for overloaded function 'new_ESLconnection'.\n" " Possible C/C++ prototypes are:\n" + " ESLconnection(char const *,int const,char const *,char const *)\n" + " ESLconnection(char const *,int const,char const *)\n" " ESLconnection(char const *,char const *,char const *,char const *)\n" " ESLconnection(char const *,char const *,char const *)\n" " ESLconnection(int)\n"); diff --git a/libs/esl/ruby/esl_wrap.cpp b/libs/esl/ruby/esl_wrap.cpp index 223ba3ea34..2d7e906402 100644 --- a/libs/esl/ruby/esl_wrap.cpp +++ b/libs/esl/ruby/esl_wrap.cpp @@ -2745,6 +2745,107 @@ swig_class cESLconnection; SWIGINTERN VALUE _wrap_new_ESLconnection__SWIG_0(int argc, VALUE *argv, VALUE self) { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + char *arg4 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int res4 ; + char *buf4 = 0 ; + int alloc4 = 0 ; + + if ((argc < 4) || (argc > 4)) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 4)",argc); SWIG_fail; + } + res1 = SWIG_AsCharPtrAndSize(argv[0], &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "char const *","ESLconnection", 1, argv[0] )); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int(argv[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), Ruby_Format_TypeError( "", "int","ESLconnection", 2, argv[1] )); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(argv[2], &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), Ruby_Format_TypeError( "", "char const *","ESLconnection", 3, argv[2] )); + } + arg3 = reinterpret_cast< char * >(buf3); + res4 = SWIG_AsCharPtrAndSize(argv[3], &buf4, NULL, &alloc4); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), Ruby_Format_TypeError( "", "char const *","ESLconnection", 4, argv[3] )); + } + arg4 = reinterpret_cast< char * >(buf4); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3,(char const *)arg4);DATA_PTR(self) = result; + + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + return self; +fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + if (alloc4 == SWIG_NEWOBJ) delete[] buf4; + return Qnil; +} + + +SWIGINTERN VALUE +_wrap_new_ESLconnection__SWIG_1(int argc, VALUE *argv, VALUE self) { + char *arg1 = (char *) 0 ; + int arg2 ; + char *arg3 = (char *) 0 ; + ESLconnection *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + + if ((argc < 3) || (argc > 3)) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 3)",argc); SWIG_fail; + } + res1 = SWIG_AsCharPtrAndSize(argv[0], &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "char const *","ESLconnection", 1, argv[0] )); + } + arg1 = reinterpret_cast< char * >(buf1); + ecode2 = SWIG_AsVal_int(argv[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), Ruby_Format_TypeError( "", "int","ESLconnection", 2, argv[1] )); + } + arg2 = static_cast< int >(val2); + res3 = SWIG_AsCharPtrAndSize(argv[2], &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), Ruby_Format_TypeError( "", "char const *","ESLconnection", 3, argv[2] )); + } + arg3 = reinterpret_cast< char * >(buf3); + result = (ESLconnection *)new ESLconnection((char const *)arg1,arg2,(char const *)arg3);DATA_PTR(self) = result; + + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + return self; +fail: + if (alloc1 == SWIG_NEWOBJ) delete[] buf1; + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + return Qnil; +} + + +SWIGINTERN VALUE +_wrap_new_ESLconnection__SWIG_2(int argc, VALUE *argv, VALUE self) { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) 0 ; @@ -2803,7 +2904,7 @@ fail: SWIGINTERN VALUE -_wrap_new_ESLconnection__SWIG_1(int argc, VALUE *argv, VALUE self) { +_wrap_new_ESLconnection__SWIG_3(int argc, VALUE *argv, VALUE self) { char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; char *arg3 = (char *) 0 ; @@ -2868,7 +2969,7 @@ _wrap_ESLconnection_allocate(VALUE self) { SWIGINTERN VALUE -_wrap_new_ESLconnection__SWIG_2(int argc, VALUE *argv, VALUE self) { +_wrap_new_ESLconnection__SWIG_4(int argc, VALUE *argv, VALUE self) { int arg1 ; ESLconnection *result = 0 ; int val1 ; @@ -2907,7 +3008,25 @@ SWIGINTERN VALUE _wrap_new_ESLconnection(int nargs, VALUE *args, VALUE self) { _v = SWIG_CheckState(res); } if (_v) { - return _wrap_new_ESLconnection__SWIG_2(nargs, args, self); + return _wrap_new_ESLconnection__SWIG_4(nargs, args, self); + } + } + if (argc == 3) { + int _v; + int res = SWIG_AsCharPtrAndSize(argv[0], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_int(argv[1], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[2], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + return _wrap_new_ESLconnection__SWIG_1(nargs, args, self); + } + } } } if (argc == 3) { @@ -2921,7 +3040,29 @@ SWIGINTERN VALUE _wrap_new_ESLconnection(int nargs, VALUE *args, VALUE self) { int res = SWIG_AsCharPtrAndSize(argv[2], 0, NULL, 0); _v = SWIG_CheckState(res); if (_v) { - return _wrap_new_ESLconnection__SWIG_1(nargs, args, self); + return _wrap_new_ESLconnection__SWIG_3(nargs, args, self); + } + } + } + } + if (argc == 4) { + int _v; + int res = SWIG_AsCharPtrAndSize(argv[0], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_int(argv[1], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[2], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[3], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + return _wrap_new_ESLconnection__SWIG_0(nargs, args, self); + } } } } @@ -2940,7 +3081,7 @@ SWIGINTERN VALUE _wrap_new_ESLconnection(int nargs, VALUE *args, VALUE self) { int res = SWIG_AsCharPtrAndSize(argv[3], 0, NULL, 0); _v = SWIG_CheckState(res); if (_v) { - return _wrap_new_ESLconnection__SWIG_0(nargs, args, self); + return _wrap_new_ESLconnection__SWIG_2(nargs, args, self); } } } @@ -2949,6 +3090,8 @@ SWIGINTERN VALUE _wrap_new_ESLconnection(int nargs, VALUE *args, VALUE self) { fail: Ruby_Format_OverloadedError( argc, 4, "ESLconnection.new", + " ESLconnection.new(char const *host, int const port, char const *user, char const *password)\n" + " ESLconnection.new(char const *host, int const port, char const *password)\n" " ESLconnection.new(char const *host, char const *port, char const *user, char const *password)\n" " ESLconnection.new(char const *host, char const *port, char const *password)\n" " ESLconnection.new(int socket)\n"); diff --git a/libs/esl/src/esl_oop.cpp b/libs/esl/src/esl_oop.cpp index 3fa4dcce56..9c202f2160 100644 --- a/libs/esl/src/esl_oop.cpp +++ b/libs/esl/src/esl_oop.cpp @@ -9,6 +9,20 @@ void eslSetLogLevel(int level) esl_global_set_default_logger(level); } +ESLconnection::ESLconnection(const char *host, const int port, const char *password) +{ + connection_construct_common(); + + esl_connect(&handle, host, port, NULL, password); +} + +ESLconnection::ESLconnection(const char *host, const int port, const char *user, const char *password) +{ + connection_construct_common(); + + esl_connect(&handle, host, port, user, password); +} + ESLconnection::ESLconnection(const char *host, const char *port, const char *password) { connection_construct_common(); diff --git a/libs/esl/src/include/esl_oop.h b/libs/esl/src/include/esl_oop.h index 450b047afc..93ce487965 100644 --- a/libs/esl/src/include/esl_oop.h +++ b/libs/esl/src/include/esl_oop.h @@ -74,6 +74,8 @@ class ESLconnection { private: esl_handle_t handle; public: + ESLconnection(const char *host, const int port, const char *user, const char *password); + ESLconnection(const char *host, const int port, const char *password); ESLconnection(const char *host, const char *port, const char *user, const char *password); ESLconnection(const char *host, const char *port, const char *password); ESLconnection(int socket); From 4bf4bf83a850c1b0afa7b17cd5c687e7b71ed3bf Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 12 Jul 2013 16:59:35 -0500 Subject: [PATCH 102/278] tweak --- src/switch_rtp.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 661ce3f7d4..6bdcbc5c0e 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -270,7 +270,6 @@ typedef struct ts_normalize_s { uint32_t last_frame; uint32_t ts; uint32_t delta; - uint8_t m; } ts_normalize_t; struct switch_rtp { @@ -5484,7 +5483,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, if (!rtp_session->ts_norm.last_ssrc || send_msg->header.ssrc != rtp_session->ts_norm.last_ssrc) { if (rtp_session->ts_norm.last_ssrc) { - rtp_session->ts_norm.m = 1; if (rtp_session->ts_norm.delta) { rtp_session->ts_norm.ts += rtp_session->ts_norm.delta; } @@ -5502,13 +5500,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_session->ts_norm.last_frame = ntohl(send_msg->header.ts); send_msg->header.ts = htonl(rtp_session->ts_norm.ts); - if (rtp_session->ts_norm.m) { - if (send_msg->header.m) { - rtp_session->ts_norm.m = 0; - } else { - send_msg->header.m = 1; - } - } } send_msg->header.ssrc = htonl(rtp_session->ssrc); From d134f717477f6bf5606c9b9081469f03a300626d Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Sat, 13 Jul 2013 11:49:07 -0500 Subject: [PATCH 103/278] FS-5598 --resolve --- libs/win32/apr/libapr.2010.vcxproj | 4 ++-- libs/win32/apr/libapr.2012.vcxproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/win32/apr/libapr.2010.vcxproj b/libs/win32/apr/libapr.2010.vcxproj index 5c6b686ee0..1bce164a1d 100644 --- a/libs/win32/apr/libapr.2010.vcxproj +++ b/libs/win32/apr/libapr.2010.vcxproj @@ -112,7 +112,7 @@ xcopy "$(ProjectDir)..\..\apr\include\*.h" "$(ProjectDir)..\..\include\" /C /D / /EHsc %(AdditionalOptions) Disabled ..\..\apr\include;..\..\apr\include/arch;..\..\apr\include/arch/win32;..\..\apr\include/arch/unix;%(AdditionalIncludeDirectories) - _DEBUG;APR_DECLARE_EXPORT;WIN32;_WINDOWS;%(PreprocessorDefinitions) + _DEBUG;APR_DECLARE_EXPORT;APR_VOID_P_IS_QUAD;WIN32;_WINDOWS;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level3 true @@ -181,7 +181,7 @@ xcopy "$(ProjectDir)..\..\apr\include\*.h" "$(ProjectDir)..\..\include\" /C /D / MaxSpeed OnlyExplicitInline ..\..\apr\include;..\..\apr\include/arch;..\..\apr\include/arch/win32;..\..\apr\include/arch/unix;%(AdditionalIncludeDirectories) - NDEBUG;APR_DECLARE_EXPORT;WIN32;_WINDOWS;%(PreprocessorDefinitions) + NDEBUG;APR_DECLARE_EXPORT;APR_VOID_P_IS_QUAD;WIN32;_WINDOWS;%(PreprocessorDefinitions) true MultiThreadedDLL true diff --git a/libs/win32/apr/libapr.2012.vcxproj b/libs/win32/apr/libapr.2012.vcxproj index c491751b00..39cc98d863 100644 --- a/libs/win32/apr/libapr.2012.vcxproj +++ b/libs/win32/apr/libapr.2012.vcxproj @@ -116,7 +116,7 @@ xcopy "$(ProjectDir)..\..\apr\include\*.h" "$(ProjectDir)..\..\include\" /C /D / /EHsc %(AdditionalOptions) Disabled ..\..\apr\include;..\..\apr\include/arch;..\..\apr\include/arch/win32;..\..\apr\include/arch/unix;%(AdditionalIncludeDirectories) - _DEBUG;APR_DECLARE_EXPORT;WIN32;_WINDOWS;%(PreprocessorDefinitions) + _DEBUG;APR_DECLARE_EXPORT;APR_VOID_P_IS_QUAD;WIN32;_WINDOWS;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level3 true @@ -185,7 +185,7 @@ xcopy "$(ProjectDir)..\..\apr\include\*.h" "$(ProjectDir)..\..\include\" /C /D / MaxSpeed OnlyExplicitInline ..\..\apr\include;..\..\apr\include/arch;..\..\apr\include/arch/win32;..\..\apr\include/arch/unix;%(AdditionalIncludeDirectories) - NDEBUG;APR_DECLARE_EXPORT;WIN32;_WINDOWS;%(PreprocessorDefinitions) + NDEBUG;APR_DECLARE_EXPORT;APR_VOID_P_IS_QUAD;WIN32;_WINDOWS;%(PreprocessorDefinitions) true MultiThreadedDLL true From 51d3282d06314fa7fcb7e9dad7e8206e7a3bab56 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Sun, 14 Jul 2013 10:34:35 +0800 Subject: [PATCH 104/278] FS-2809 --resolved --- src/mod/say/mod_say_zh/mod_say_zh.c | 98 ++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/src/mod/say/mod_say_zh/mod_say_zh.c b/src/mod/say/mod_say_zh/mod_say_zh.c index 7dab05e57c..87ab0042b8 100644 --- a/src/mod/say/mod_say_zh/mod_say_zh.c +++ b/src/mod/say/mod_say_zh/mod_say_zh.c @@ -40,7 +40,7 @@ * Anthony Minessale II * PeteDao * Steve Underwood 0.0.1 - * + * Seven Du * * mod_say_zh.c -- Say for Mandarin, Cantonese, and probably any other Chinese * dialect. @@ -430,6 +430,58 @@ static switch_status_t zh_say_money(switch_core_session_t *session, char *tosay, return SWITCH_STATUS_SUCCESS; } +static switch_status_t zh_CN_say_money(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + char sbuf[16] = ""; /* enough for 999,999,999,999.99 (w/o the commas or leading $) */ + char dbuf[16] = ""; /* enough for digits/x.wav */ + char *yuan = NULL; + char *rest = NULL; + int i; + + if (strlen(tosay) > 15 || !(tosay = switch_strip_nonnumerics(tosay, sbuf, sizeof(sbuf)-1))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + return SWITCH_STATUS_GENERR; + } + + yuan = sbuf; + + if ((rest = strchr(sbuf, '.'))) { + *rest++ = '\0'; + } + + /* If positive sign - skip over" */ + if (sbuf[0] == '+') { + yuan++; + } + + /* If negative say "negative" */ + if (sbuf[0] == '-') { + say_file("currency/negative.wav"); + yuan++; + } + + /* Say dollar amount */ + zh_say_general_count(session, yuan, say_args, args); + say_file("currency/yuan.wav"); + + if (!rest) return SWITCH_STATUS_SUCCESS; + + /* Say cents */ + for (i=0; *rest; i++, rest++) { + sprintf(dbuf, "digits/%c.wav", *rest); + say_file(dbuf); + if (i == 0) { + say_file("currency/jiao.wav"); + } else if (i == 1) { + say_file("currency/fen.wav"); + } else if (i == 2) { + say_file("currency/li.wav"); + } /* else just say the rest of digits */ + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t zh_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) { switch_say_callback_t say_cb = NULL; @@ -469,15 +521,59 @@ static switch_status_t zh_say(switch_core_session_t *session, char *tosay, switc return SWITCH_STATUS_FALSE; } +static switch_status_t zh_CN_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + switch_say_callback_t say_cb = NULL; + + switch (say_args->type) { + case SST_NUMBER: + case SST_ITEMS: + case SST_PERSONS: + case SST_MESSAGES: + say_cb = zh_say_general_count; + break; + case SST_TIME_MEASUREMENT: + case SST_CURRENT_DATE: + case SST_CURRENT_TIME: + case SST_CURRENT_DATE_TIME: + say_cb = zh_say_time; + break; + case SST_IP_ADDRESS: + return switch_ivr_say_ip(session, tosay, zh_say_general_count, say_args, args); + break; + case SST_NAME_SPELLED: + case SST_NAME_PHONETIC: + return switch_ivr_say_spell(session, tosay, say_args, args); + break; + case SST_CURRENCY: + say_cb = zh_CN_say_money; + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown Say type=[%d]\n", say_args->type); + break; + } + + if (say_cb) { + return say_cb(session, tosay, say_args, args); + } + + return SWITCH_STATUS_FALSE; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_say_zh_load) { switch_say_interface_t *say_interface; + switch_say_interface_t *say_zh_CN_interface; /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); say_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE); say_interface->interface_name = "zh"; say_interface->say_function = zh_say; + say_zh_CN_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE); + say_zh_CN_interface->interface_name = "zh_CN"; + say_zh_CN_interface->say_function = zh_CN_say; + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } From e46050d4872b295f1f599168435a882d8e4d2762 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 15 Jul 2013 11:07:56 -0500 Subject: [PATCH 105/278] mod_conference add vid-floor command to branch off the floor for video on demand and force it to a particular member vid-floor can be api or bound to dtmf but who gets the floor must toggle it off before someone else can have it. vid-floor-force (vid-floor force)can be clobbered at any time --- .../mod_conference/mod_conference.c | 382 +++++++++--------- 1 file changed, 194 insertions(+), 188 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index bb572f4e78..23f08f69b2 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -334,6 +334,7 @@ typedef struct conference_obj { switch_mutex_t *mutex; conference_member_t *members; conference_member_t *floor_holder; + conference_member_t *video_floor_holder; switch_mutex_t *member_mutex; conference_file_node_t *fnode; conference_file_node_t *async_fnode; @@ -527,7 +528,6 @@ static switch_status_t chat_send(switch_event_t *message_event); static void launch_conference_record_thread(conference_obj_t *conference, char *path); static int launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b); -static void launch_conference_video_mirror_thread(conference_member_t *member_a); typedef switch_status_t (*conf_api_args_cmd_t) (conference_obj_t *, switch_stream_handle_t *, int, char **); typedef switch_status_t (*conf_api_member_cmd_t) (conference_member_t *, switch_stream_handle_t *, void *); @@ -542,6 +542,8 @@ static switch_status_t conf_api_sub_undeaf(conference_member_t *member, switch_s static switch_status_t conference_add_event_data(conference_obj_t *conference, switch_event_t *event); static switch_status_t conference_add_event_member_data(conference_member_t *member, switch_event_t *event); static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data); +static switch_status_t conf_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data); +static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data); static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data); @@ -1455,7 +1457,9 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe if (switch_channel_test_flag(channel, CF_VIDEO)) { - switch_channel_clear_flag(channel, CF_VIDEO_ECHO); + if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + switch_channel_clear_flag(channel, CF_VIDEO_ECHO); + } /* Tell the channel to request a fresh vid frame */ switch_core_session_refresh_video(member->session); } @@ -1572,18 +1576,94 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe return status; } +static void conference_set_video_floor_holder(conference_obj_t *conference, conference_member_t *member) +{ + switch_event_t *event; + int old_id = 0; + + if (switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + return; + } + + if (conference->video_floor_holder) { + if (conference->video_floor_holder == member) { + return; + } else { + old_id = conference->video_floor_holder->id; + switch_channel_clear_flag(conference->video_floor_holder->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->video_floor_holder->session); + } + } + + if ((conference->video_floor_holder = member)) { + switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->video_floor_holder->session); + switch_set_flag(conference, CFLAG_FLOOR_CHANGE); + } + + if (old_id > -1 && test_eflag(conference, EFLAG_FLOOR_CHANGE)) { + switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); + conference_add_event_data(conference, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "video-floor-change"); + if (old_id) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_id); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none"); + } + if (conference->video_floor_holder) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->video_floor_holder->id); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); + } + switch_event_fire(&event); + } + +} + static void conference_set_floor_holder(conference_obj_t *conference, conference_member_t *member) { + switch_event_t *event; + int old_id = 0; - if (conference->floor_holder && conference->floor_holder != member) { - switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE); + if (conference->floor_holder) { + if (conference->floor_holder == member) { + return; + } else { + old_id = conference->floor_holder->id; + if (!conference->video_floor_holder && !switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->floor_holder->session); + } + } } if ((conference->floor_holder = member)) { - switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE); - switch_core_session_refresh_video(conference->floor_holder->session); + if (!conference->video_floor_holder && !switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->floor_holder->session); + } switch_set_flag(conference, CFLAG_FLOOR_CHANGE); } + + if (old_id > -1 && test_eflag(conference, EFLAG_FLOOR_CHANGE)) { + switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); + conference_add_event_data(conference, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); + if (old_id) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_id); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none"); + } + + if (conference->floor_holder) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->floor_holder->id); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); + } + + switch_event_fire(&event); + } + } /* Gain exclusive access and remove the member from the list */ @@ -1669,18 +1749,12 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe } if (member == member->conference->floor_holder) { - //member->conference->floor_holder = NULL; conference_set_floor_holder(member->conference, NULL); + } - if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) { - switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); - conference_add_event_data(conference, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", member->id); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); - switch_event_fire(&event); - } + if (member == member->conference->video_floor_holder) { + conference_set_video_floor_holder(member->conference, NULL); } member->conference = NULL; @@ -1758,6 +1832,8 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread switch_thread_rwlock_rdlock(vh->member_b->rwlock); + switch_channel_set_flag(channel_a, CF_VIDEO_PASSIVE); + /* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */ switch_core_session_read_lock(session_a); switch_core_session_read_lock(session_b); @@ -1786,6 +1862,7 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread } } } + switch_channel_clear_flag(channel_a, CF_VIDEO_PASSIVE); switch_thread_rwlock_unlock(vh->member_b->rwlock); switch_thread_rwlock_unlock(vh->member_a->rwlock); @@ -1802,62 +1879,6 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread } - -static void *SWITCH_THREAD_FUNC conference_video_mirror_thread_run(switch_thread_t *thread, void *obj) -{ - struct vid_helper *vh = obj; - switch_core_session_t *session_a = vh->member_a->session; - switch_channel_t *channel_a = switch_core_session_get_channel(session_a); - switch_status_t status; - switch_frame_t *read_frame; - conference_obj_t *conference = vh->member_a->conference; - switch_core_session_message_t msg = { 0 }; - - switch_thread_rwlock_rdlock(conference->rwlock); - switch_thread_rwlock_rdlock(vh->member_a->rwlock); - - /* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */ - switch_core_session_read_lock(session_a); - - /* Tell the channel to request a fresh vid frame */ - msg.from = __FILE__; - msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ; - switch_core_session_receive_message(session_a, &msg); - - vh->up = 1; - while (vh->up > 0 && switch_test_flag(vh->member_a, MFLAG_RUNNING) && - switch_channel_ready(channel_a)) { - - if (vh->up == 1) { - status = switch_core_session_read_video_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0); - if (!SWITCH_READ_ACCEPTABLE(status)) { - break; - } - - if (!switch_test_flag(read_frame, SFF_CNG)) { - if (switch_core_session_write_video_frame(session_a, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - break; - } - } - } else { - switch_yield(100000); - } - } - - - switch_thread_rwlock_unlock(vh->member_a->rwlock); - - switch_core_session_rwunlock(session_a); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s video mirror thread ended.\n", switch_channel_get_name(channel_a)); - - switch_thread_rwlock_unlock(conference->rwlock); - - vh->up = 0; - return NULL; -} - - /* Main video monitor thread (1 per distinct conference room) */ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thread, void *obj) { @@ -1869,6 +1890,7 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr int yield = 0; switch_core_session_t *session; char buf[65536]; + conference_member_t *floor_holder = NULL; conference->video_running = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video thread started for conference %s\n", conference->name); @@ -1881,17 +1903,24 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr switch_mutex_lock(conference->mutex); - if (!conference->floor_holder) { + if (conference->video_floor_holder) { + floor_holder = conference->video_floor_holder; + } else { + floor_holder = conference->floor_holder; + } + + + if (!floor_holder) { yield = 100000; goto do_continue; } - if (!switch_channel_test_flag(switch_core_session_get_channel(conference->floor_holder->session), CF_VIDEO)) { + if (!switch_channel_test_flag(switch_core_session_get_channel(floor_holder->session), CF_VIDEO)) { yield = 100000; goto do_continue; } - session = conference->floor_holder->session; + session = floor_holder->session; if ((status = switch_core_session_read_lock(session)) == SWITCH_STATUS_SUCCESS) { switch_mutex_unlock(conference->mutex); @@ -2070,48 +2099,9 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_mutex_unlock(imember->audio_in_mutex); } - - if (floor_holder != conference->floor_holder) { - switch_event_t *event = NULL; - - if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) { - switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); - - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); - - if (floor_holder) { - conference_add_event_member_data(floor_holder, event); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", floor_holder->id); - } else { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); - } - - if (conference->floor_holder) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", conference->floor_holder->id); - } else { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none"); - } - - switch_event_fire(&event); - } - - if (floor_holder) { - switch_channel_t *floor_channel = switch_core_session_get_channel(floor_holder->session); - if (switch_channel_test_flag(floor_channel, CF_VIDEO)) { - switch_core_session_message_t msg = { 0 }; - - msg.from = __FILE__; - msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ; - - switch_core_session_receive_message(floor_holder->session, &msg); - } - } - - //conference->floor_holder = floor_holder; conference_set_floor_holder(conference, floor_holder); } - if (conference->perpetual_sound && !conference->async_fnode) { conference_play_file(conference, conference->perpetual_sound, CONF_DEFAULT_LEADIN, NULL, 1); @@ -2187,16 +2177,6 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } else { conference->mh.up = -1; } - } else if (conference->vh[0].up == 0 && - conference->vh[1].up == 0 && - conference->mh.up == 0 && - video_bridge_members[0] && - !video_bridge_members[1] && - switch_test_flag(video_bridge_members[0], MFLAG_RUNNING) && - switch_channel_ready(switch_core_session_get_channel(video_bridge_members[0]->session)) - ) { - - launch_conference_video_mirror_thread(video_bridge_members[0]); } } @@ -2573,6 +2553,20 @@ static void conference_loop_fn_floor_toggle(conference_member_t *member, caller_ conf_api_sub_floor(member, NULL, NULL); } +static void conference_loop_fn_vid_floor_toggle(conference_member_t *member, caller_control_action_t *action) +{ + if (member == NULL) return; + + conf_api_sub_vid_floor(member, NULL, NULL); +} + +static void conference_loop_fn_vid_floor_force(conference_member_t *member, caller_control_action_t *action) +{ + if (member == NULL) return; + + conf_api_sub_vid_floor(member, NULL, "force"); +} + static void conference_loop_fn_enforce_floor(conference_member_t *member, caller_control_action_t *action) { if (member == NULL) return; @@ -5242,7 +5236,6 @@ static switch_status_t conf_api_sub_list(conference_obj_t *conference, switch_st static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data) { - switch_event_t *event; if (member == NULL) return SWITCH_STATUS_GENERR; @@ -5250,38 +5243,14 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st switch_mutex_lock(member->conference->mutex); if (member->conference->floor_holder == member) { - //member->conference->floor_holder = NULL; conference_set_floor_holder(member->conference, NULL); - if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { - switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); - conference_add_event_data(member->conference, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", member->id); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); - switch_event_fire(&event); - if (stream != NULL) { - stream->write_function(stream, "OK floor none\n"); - } + if (stream != NULL) { + stream->write_function(stream, "OK floor none\n"); } } else if (member->conference->floor_holder == NULL) { - //member->conference->floor_holder = member; conference_set_floor_holder(member->conference, member); - if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { - switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); - conference_add_event_data(member->conference, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "none"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", member->id); - switch_event_fire(&event); - if (stream != NULL) { - stream->write_function(stream, "OK floor %u\n", member->id); - } - - if (switch_core_session_read_lock(member->session) == SWITCH_STATUS_SUCCESS) { - /* Tell the channel to request a fresh vid frame */ - switch_channel_set_flag(switch_core_session_get_channel(member->session), CF_VIDEO_REFRESH_REQ); - switch_core_session_rwunlock(member->session); - } + if (stream != NULL) { + stream->write_function(stream, "OK floor %u\n", member->id); } } else { if (stream != NULL) { @@ -5294,39 +5263,85 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st return SWITCH_STATUS_SUCCESS; } +static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data) +{ + + if (switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "conference %s is in video bridge mode, this functionality is not compatible\n", conference->name); + return SWITCH_STATUS_FALSE; + } + + switch_mutex_lock(conference->mutex); + conference_set_video_floor_holder(conference, NULL); + switch_mutex_unlock(conference->mutex); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t conf_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data) +{ + int force = 0; + + if (member == NULL) + return SWITCH_STATUS_GENERR; + + if (switch_test_flag(member->conference, CFLAG_VIDEO_BRIDGE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "conference %s is in video bridge mode, this functionality is not compatible\n", member->conference->name); + return SWITCH_STATUS_FALSE; + } + + switch_mutex_lock(member->conference->mutex); + + if (data && switch_stristr("force", (char *) data)) { + force = 1; + } + + if (!force && member->conference->video_floor_holder == member) { + conference_set_video_floor_holder(member->conference, NULL); + if (stream == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK floor none\n", member->conference->name); + } else { + stream->write_function(stream, "OK floor none\n"); + } + + } else if (force || member->conference->video_floor_holder == NULL) { + conference_set_video_floor_holder(member->conference, member); + if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { + if (stream == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK floor %d %s\n", + member->conference->name, member->id, switch_channel_get_name(member->channel)); + } else { + stream->write_function(stream, "OK floor %u\n", member->id); + } + } + } else { + if (stream == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s floor already held by %d %s\n", + member->conference->name, member->id, switch_channel_get_name(member->channel)); + } else { + stream->write_function(stream, "ERR floor is held by %u\n", member->conference->video_floor_holder->id); + } + } + + switch_mutex_unlock(member->conference->mutex); + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data) { - switch_event_t *event; - if (member == NULL) return SWITCH_STATUS_GENERR; switch_mutex_lock(member->conference->mutex); if (member->conference->floor_holder != member) { - conference_member_t *old_member = member->conference->floor_holder; - //member->conference->floor_holder = member; conference_set_floor_holder(member->conference, member); - if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { - switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); - conference_add_event_data(member->conference, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change"); - if (old_member == NULL) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "none"); - } else { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_member->id); - } - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", member->id); - switch_event_fire(&event); - if (stream != NULL) { - stream->write_function(stream, "OK floor %u\n", member->id); - } - } - if (switch_core_session_read_lock(member->session) == SWITCH_STATUS_SUCCESS) { - /* Tell the channel to request a fresh vid frame */ - switch_channel_set_flag(switch_core_session_get_channel(member->session), CF_VIDEO_REFRESH_REQ); - switch_core_session_rwunlock(member->session); + if (stream != NULL) { + stream->write_function(stream, "OK floor %u\n", member->id); } } @@ -6528,6 +6543,8 @@ static api_command_t conf_api_sub_commands[] = { {"get", (void_fn_t) & conf_api_sub_get, CONF_API_SUB_ARGS_SPLIT, "get", ""}, {"set", (void_fn_t) & conf_api_sub_set, CONF_API_SUB_ARGS_SPLIT, "set", " "}, {"floor", (void_fn_t) & conf_api_sub_floor, CONF_API_SUB_MEMBER_TARGET, "floor", ""}, + {"vid-floor", (void_fn_t) & conf_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", " [force]"}, + {"clear-vid-floor", (void_fn_t) & conf_api_sub_clear_vid_floor, CONF_API_SUB_ARGS_AS_ONE, "clear-vid-floor", ""}, {"enforce_floor", (void_fn_t) & conf_api_sub_enforce_floor, CONF_API_SUB_MEMBER_TARGET, "enforce_floor", ""}, }; @@ -8035,19 +8052,6 @@ static int launch_conference_video_bridge_thread(conference_member_t *member_a, -/* Create a video thread for the conference and launch it */ -static void launch_conference_video_mirror_thread(conference_member_t *member_a) -{ - conference_obj_t *conference = member_a->conference; - switch_memory_pool_t *pool = conference->pool; - - memset(&conference->mh, 0, sizeof(conference->mh)); - - conference->mh.member_a = member_a; - - launch_thread_detached(conference_video_mirror_thread_run, pool, &conference->mh); -} - static void launch_conference_record_thread(conference_obj_t *conference, char *path) { switch_thread_t *thread; @@ -9116,6 +9120,8 @@ static struct _mapping control_mappings[] = { {"transfer", conference_loop_fn_transfer}, {"execute_application", conference_loop_fn_exec_app}, {"floor", conference_loop_fn_floor_toggle}, + {"vid-floor", conference_loop_fn_vid_floor_toggle}, + {"vid-floor-force", conference_loop_fn_vid_floor_force}, {"enforce_floor", conference_loop_fn_enforce_floor}, }; #define MAPPING_LEN (sizeof(control_mappings)/sizeof(control_mappings[0])) From acb33edc3a627ca6fc77cecfeaab4d24e4058191 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Mon, 15 Jul 2013 15:19:18 -0500 Subject: [PATCH 106/278] skinny: improve hangup handling --- src/mod/endpoints/mod_skinny/mod_skinny.c | 17 ++++++++++++++--- src/mod/endpoints/mod_skinny/mod_skinny.h | 4 ++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 790eb7836c..407aa65499 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -848,11 +848,22 @@ int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnN skinny_line_set_state(listener, line_instance, call_id, SKINNY_ON_HOOK); send_select_soft_keys(listener, line_instance, call_id, SKINNY_KEY_SET_ON_HOOK, 0xffff); send_define_current_time_date(listener); - if((call_state == SKINNY_PROCEED) || (call_state == SKINNY_RING_OUT) || (call_state == SKINNY_CONNECTED)) { /* calling parties */ + + skinny_log_ls(listener, helper->tech_pvt->session, SWITCH_LOG_DEBUG, + "channel_on_hangup_callback - cause=%s [%d], call_state = %s [%d]\n", + switch_channel_cause2str(helper->cause), helper->cause, + skinny_call_state2str(call_state), call_state); + + if ( call_state == SKINNY_RING_OUT && helper->cause == SWITCH_CAUSE_USER_BUSY ) + { + // don't hang up speaker here + } + else if((call_state == SKINNY_PROCEED) || (call_state == SKINNY_RING_OUT) || (call_state == SKINNY_CONNECTED)) { /* calling parties */ // This is NOT correct, but results in slightly better behavior than before // leaving note here to revisit. - //send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF); + /* re-enabling for testing to bring back bad behavior */ + send_set_speaker_mode(listener, SKINNY_SPEAKER_OFF); } send_set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0, call_id); } @@ -869,7 +880,7 @@ switch_status_t channel_on_hangup(switch_core_session_t *session) switch_clear_flag_locked(tech_pvt, TFLAG_IO); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP [%s]\n", + skinny_log_s(session, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP [%s]\n", switch_channel_get_name(channel), switch_channel_cause2str(cause)); helper.tech_pvt= tech_pvt; diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index 717bcd0378..76ca8017ac 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -61,6 +61,10 @@ "[%s:%d @ %s:%d] " _fmt, skinny_undef_str(listener->device_name), listener->device_instance, skinny_undef_str(listener->remote_ip), \ listener->remote_port) +#define skinny_log_s(session, level, _fmt, ...) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), level, \ + _fmt, __VA_ARGS__) + + /*****************************************************************************/ /* MODULE TYPES */ /*****************************************************************************/ From e201bb01ea3402e024cbdab4ef3458a51347b8f8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 15 Jul 2013 16:57:55 -0500 Subject: [PATCH 107/278] FS-5610 --resolve --- libs/iksemel/.update | 2 +- libs/iksemel/include/iksemel.h | 1 + libs/iksemel/src/stream.c | 74 +++++++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/libs/iksemel/.update b/libs/iksemel/.update index b391edc878..a3a56a0ec9 100644 --- a/libs/iksemel/.update +++ b/libs/iksemel/.update @@ -1 +1 @@ -Tue May 14 07:44:21 CDT 2013 +Mon Jul 15 16:57:11 CDT 2013 diff --git a/libs/iksemel/include/iksemel.h b/libs/iksemel/include/iksemel.h index 9ef479a9ee..2b11070671 100644 --- a/libs/iksemel/include/iksemel.h +++ b/libs/iksemel/include/iksemel.h @@ -226,6 +226,7 @@ void iks_disconnect (iksparser *prs); int iks_has_tls (void); int iks_is_secure (iksparser *prs); int iks_start_tls (iksparser *prs); +int iks_proceed_tls (iksparser *prs, const char *cert_file, const char *key_file, int use_ssl); int iks_start_sasl (iksparser *prs, enum ikssasltype type, char *username, char *pass); /***** jabber *****/ diff --git a/libs/iksemel/src/stream.c b/libs/iksemel/src/stream.c index 559335b16e..658c40207c 100644 --- a/libs/iksemel/src/stream.c +++ b/libs/iksemel/src/stream.c @@ -35,6 +35,8 @@ typedef unsigned __int32 uint32_t; #define SF_FOREIGN 1 #define SF_TRY_SECURE 2 #define SF_SECURE 4 +#define SF_SERVER 8 +#define SF_SSLv23 16 struct stream_data { iksparser *prs; @@ -51,6 +53,8 @@ struct stream_data { unsigned int flags; char *auth_username; char *auth_pass; + char *cert_file; + char *key_file; #ifdef HAVE_GNUTLS gnutls_session sess; gnutls_certificate_credentials cred; @@ -201,6 +205,7 @@ handshake (struct stream_data *data) gnutls_certificate_free_credentials (data->cred); return IKS_NOMEM; } + gnutls_protocol_set_priority (data->sess, protocol_priority); gnutls_cipher_set_priority(data->sess, cipher_priority); gnutls_compression_set_priority(data->sess, comp_priority); @@ -224,7 +229,9 @@ handshake (struct stream_data *data) data->flags &= (~SF_TRY_SECURE); data->flags |= SF_SECURE; - iks_send_header (data->prs, data->server); + if (!(data->flags & SF_SERVER)) { + iks_send_header (data->prs, data->server); + } return IKS_OK; } // HAVE_GNUTLS @@ -311,8 +318,25 @@ handshake (struct stream_data *data) SSL_library_init(); SSL_load_error_strings(); - data->ssl_ctx = SSL_CTX_new(TLSv1_method()); - if(!data->ssl_ctx) return IKS_NOMEM; + if (data->flags & SF_SERVER) { + if (data->flags & SF_SSLv23) { + data->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + } else { + data->ssl_ctx = SSL_CTX_new(TLSv1_server_method()); + } + if(!data->ssl_ctx) return IKS_NOMEM; + + if (SSL_CTX_use_certificate_file(data->ssl_ctx, data->cert_file, SSL_FILETYPE_PEM) <= 0) { + return IKS_NET_TLSFAIL; + } + if (SSL_CTX_use_PrivateKey_file(data->ssl_ctx, data->key_file, SSL_FILETYPE_PEM) <= 0) { + return IKS_NET_TLSFAIL; + } + SSL_CTX_set_verify(data->ssl_ctx, SSL_VERIFY_NONE, NULL); + } else { + data->ssl_ctx = SSL_CTX_new(TLSv1_method()); + if(!data->ssl_ctx) return IKS_NOMEM; + } data->ssl = SSL_new(data->ssl_ctx); if(!data->ssl) return IKS_NOMEM; @@ -329,7 +353,11 @@ handshake (struct stream_data *data) do { - ret = SSL_connect(data->ssl); + if (data->flags & SF_SERVER) { + ret = SSL_accept(data->ssl); + } else { + ret = SSL_connect(data->ssl); + } if( ret != 1 ) { @@ -346,7 +374,9 @@ handshake (struct stream_data *data) data->flags &= (~SF_TRY_SECURE); data->flags |= SF_SECURE; - iks_send_header (data->prs, data->server); + if (!(data->flags & SF_SERVER)) { + iks_send_header (data->prs, data->server); + } } return ret == 1 ? IKS_OK : IKS_NET_TLSFAIL; @@ -954,6 +984,40 @@ iks_start_tls (iksparser *prs) #endif } +int +iks_proceed_tls (iksparser *prs, const char *cert_file, const char *key_file, int use_ssl) +{ +#ifdef HAVE_GNUTLS + int ret; + struct stream_data *data = iks_user_data (prs); + + ret = iks_send_raw (prs, ""); + if (ret) return ret; + data->cert_file = iks_stack_strdup(data->s, cert_file, 0); + data->key_file = iks_stack_strdup(data->s, key_file, 0); + data->flags |= SF_TRY_SECURE | SF_SERVER; + if (use_ssl) { + data->flags |= SF_SSLv23; + } + return handshake (data); +#elif HAVE_SSL + int ret; + struct stream_data *data = iks_user_data (prs); + + ret = iks_send_raw (prs, ""); + if (ret) return ret; + data->cert_file = iks_stack_strdup(data->s, cert_file, 0); + data->key_file = iks_stack_strdup(data->s, key_file, 0); + data->flags |= SF_TRY_SECURE | SF_SERVER; + if (use_ssl) { + data->flags |= SF_SSLv23; + } + return handshake (data); +#else + return IKS_NET_NOTSUPP; +#endif +} + /***** sasl *****/ int From 07e0de32295ae20076bda153ee11915bfbc51243 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 15 Jul 2013 18:52:09 -0400 Subject: [PATCH 108/278] mod_rayo: support secure client-to-server connections --- conf/rayo/autoload_configs/rayo.conf.xml | 4 +- .../conf/autoload_configs/rayo.conf.xml | 4 +- src/mod/event_handlers/mod_rayo/iks_helpers.h | 3 +- src/mod/event_handlers/mod_rayo/mod_rayo.c | 26 ++-- .../event_handlers/mod_rayo/xmpp_streams.c | 129 +++++++++++------- .../event_handlers/mod_rayo/xmpp_streams.h | 2 + 6 files changed, 103 insertions(+), 65 deletions(-) diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index f9ac49b99d..0cb46d7992 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -11,8 +11,10 @@ - + + + diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index f9ac49b99d..0cb46d7992 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -11,8 +11,10 @@ - + + + diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 4e21113452..442722a267 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -39,8 +39,7 @@ #define IKS_NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" #define IKS_NS_XMPP_STREAMS "http://etherx.jabber.org/streams" #define IKS_NS_XMPP_DIALBACK "jabber:server:dialback" -#define IKS_NS_BIDI_FEATURE "urn:xmpp:features:bidi" -#define IKS_NS_BIDI "urn:xmpp:bidi" +#define IKS_NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls" struct xmpp_error { const char *name; diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 24b187d739..a110cdb703 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -3079,6 +3079,8 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_ switch_xml_t l; const char *shared_secret = switch_xml_attr_soft(domain, "shared-secret"); const char *name = switch_xml_attr_soft(domain, "name"); + const char *cert = switch_xml_attr_soft(domain, "cert"); + const char *key = switch_xml_attr_soft(domain, "key"); if (zstr(name)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing #include +#include + #include "xmpp_streams.h" #include "iks_helpers.h" #include "sasl.h" @@ -63,6 +65,10 @@ struct xmpp_stream_context { int shutdown; /** prevents context shutdown until all threads are finished */ switch_thread_rwlock_t *shutdown_rwlock; + /** path to cert PEM file */ + const char *cert_pem_file; + /** path to key PEM file */ + const char *key_pem_file; }; /** @@ -71,8 +77,8 @@ struct xmpp_stream_context { enum xmpp_stream_state { /** new connection */ XSS_CONNECT, - /** bidirectional comms established */ - XSS_BIDI, + /** encrypted comms established */ + XSS_SECURE, /** remote party authenticated */ XSS_AUTHENTICATED, /** client resource bound */ @@ -159,7 +165,7 @@ static const char *xmpp_stream_state_to_string(enum xmpp_stream_state state) { switch(state) { case XSS_CONNECT: return "CONNECT"; - case XSS_BIDI: return "BIDI"; + case XSS_SECURE: return "SECURE"; case XSS_AUTHENTICATED: return "AUTHENTICATED"; case XSS_RESOURCE_BOUND: return "RESOURCE_BOUND"; case XSS_READY: return "READY"; @@ -358,6 +364,31 @@ static void xmpp_send_client_header_auth(struct xmpp_stream *stream) free(header); } +/** + * Send sasl + starttls reply to xmpp + * @param stream the xmpp stream + */ +static void xmpp_send_client_header_tls(struct xmpp_stream *stream) +{ + if (stream->context->key_pem_file && stream->context->cert_pem_file) { + struct xmpp_stream_context *context = stream->context; + char *header = switch_mprintf( + "" + "" + "" + "PLAIN" + "", context->domain, stream->id); + iks_send_raw(stream->parser, header); + free(header); + } else { + /* not set up for TLS, skip it */ + stream->state = XSS_SECURE; + xmpp_send_client_header_auth(stream); + } +} + /** * Send sasl reply to xmpp * @param stream the xmpp stream @@ -370,12 +401,6 @@ static void xmpp_send_server_header_auth(struct xmpp_stream *stream) " from='%s' id='%s' xml:lang='en' version='1.0'" " xmlns:stream='"IKS_NS_XMPP_STREAMS"'>" "" -#if 0 - "" - "" - "PLAIN" - "" -#endif "", context->domain, stream->id); iks_send_raw(stream->parser, header); @@ -417,6 +442,21 @@ static void xmpp_send_outbound_server_header(struct xmpp_stream *stream) free(header); } +/** + * Handle message. + * @param the xmpp stream + * @param node the packet + */ +static void on_stream_starttls(struct xmpp_stream *stream, iks *node) +{ + /* wait for handshake to start */ + if (iks_proceed_tls(stream->parser, stream->context->cert_pem_file, stream->context->key_pem_file, 1) == IKS_OK) { + stream->state = XSS_SECURE; + } else { + stream->state = XSS_ERROR; + } +} + /** * Handle message. Only PLAIN supported. * @param stream the xmpp stream @@ -431,7 +471,7 @@ static void on_stream_auth(struct xmpp_stream *stream, iks *node) switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, auth, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state)); /* wrong state for authentication */ - if (stream->state != XSS_BIDI) { + if (stream->state != XSS_SECURE) { switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth UNEXPECTED, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state)); /* on_auth unexpected error */ stream->state = XSS_ERROR; @@ -486,38 +526,6 @@ static void on_stream_auth(struct xmpp_stream *stream, iks *node) } } -/** - * Handle message. - * @param stream the xmpp stream - * @param node the packet - */ -static void on_stream_bidi(struct xmpp_stream *stream, iks *node) -{ - /* only allow bidi on s2s connections before auth */ - if (stream->s2s) { - switch(stream->state) { - case XSS_CONNECT: - stream->state = XSS_BIDI; - break; - case XSS_BIDI: - case XSS_AUTHENTICATED: - case XSS_RESOURCE_BOUND: - case XSS_READY: - case XSS_SHUTDOWN: - case XSS_ERROR: - case XSS_DESTROY: - /* error */ - stream->state = XSS_ERROR; - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bad state: %s\n", stream->jid, xmpp_stream_state_to_string(stream->state)); - break; - } - } else { - /* error */ - stream->state = XSS_ERROR; - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bidi not allowed from client\n", stream->jid); - } -} - /** * Handle request * @param stream the xmpp stream @@ -606,7 +614,7 @@ static void on_stream_iq(struct xmpp_stream *stream, iks *iq) struct xmpp_stream_context *context = stream->context; switch(stream->state) { case XSS_CONNECT: - case XSS_BIDI: { + case XSS_SECURE: { iks *error = iks_new_error(iq, STANZA_ERROR_NOT_AUTHORIZED); xmpp_stream_stanza_send(stream, error); break; @@ -689,7 +697,9 @@ static void on_client_stream_start(struct xmpp_stream *stream, iks *node) switch (stream->state) { case XSS_CONNECT: - case XSS_BIDI: + xmpp_send_client_header_tls(stream); + break; + case XSS_SECURE: xmpp_send_client_header_auth(stream); break; case XSS_AUTHENTICATED: @@ -960,7 +970,7 @@ static void on_outbound_server_stream_start(struct xmpp_stream *stream, iks *nod /* strange... I expect IKS_NODE_STOP, this is a workaround. */ stream->state = XSS_DESTROY; break; - case XSS_BIDI: + case XSS_SECURE: case XSS_AUTHENTICATED: case XSS_RESOURCE_BOUND: case XSS_READY: @@ -1001,7 +1011,7 @@ static void on_inbound_server_stream_start(struct xmpp_stream *stream, iks *node case XSS_CONNECT: xmpp_send_server_header_auth(stream); break; - case XSS_BIDI: + case XSS_SECURE: break; case XSS_AUTHENTICATED: { /* all set */ @@ -1075,8 +1085,8 @@ static int on_stream(void *user_data, int type, iks *node) on_stream_presence(stream, node); } else if (!strcmp("auth", name)) { on_stream_auth(stream, node); - } else if (!strcmp("bidi", name)) { - on_stream_bidi(stream, node); + } else if (!strcmp("starttls", name)) { + on_stream_starttls(stream, node); } else if (!strcmp("db:result", name)) { on_stream_dialback_result(stream, node); } else if (!strcmp("db:verify", name)) { @@ -1194,6 +1204,7 @@ static void *SWITCH_THREAD_FUNC xmpp_stream_thread(switch_thread_t *thread, void case IKS_OK: err_count = 0; break; + case IKS_NET_TLSFAIL: case IKS_NET_RWERR: case IKS_NET_NOCONN: case IKS_NET_NOSOCK: @@ -1291,11 +1302,6 @@ static struct xmpp_stream *xmpp_stream_init(struct xmpp_stream_context *context, stream->incoming = incoming; switch_queue_create(&stream->msg_queue, MAX_QUEUE_LEN, pool); - if (!stream->s2s) { - /* client is already bi-directional */ - stream->state = XSS_BIDI; - } - /* set up XMPP stream parser */ stream->parser = iks_stream_new(stream->s2s ? IKS_NS_SERVER : IKS_NS_CLIENT, stream, on_stream); @@ -1829,6 +1835,23 @@ void *xmpp_stream_get_private(struct xmpp_stream *stream) return stream->user_private; } +/** + * Add PEM cert file to stream for new SSL connections + */ +void xmpp_stream_context_add_cert(struct xmpp_stream_context *context, const char *cert_pem_file) +{ + context->cert_pem_file = switch_core_strdup(context->pool, cert_pem_file); +} + +/** + * Add PEM key file to stream for new SSL connections + */ +void xmpp_stream_context_add_key(struct xmpp_stream_context *context, const char *key_pem_file) +{ + context->key_pem_file = switch_core_strdup(context->pool, key_pem_file); +} + + /* For Emacs: * Local Variables: * mode:c diff --git a/src/mod/event_handlers/mod_rayo/xmpp_streams.h b/src/mod/event_handlers/mod_rayo/xmpp_streams.h index d4ba663246..a07c75d025 100644 --- a/src/mod/event_handlers/mod_rayo/xmpp_streams.h +++ b/src/mod/event_handlers/mod_rayo/xmpp_streams.h @@ -37,6 +37,8 @@ typedef void (* xmpp_stream_recv_callback)(struct xmpp_stream *stream, iks *stan typedef void (* xmpp_stream_destroy_callback)(struct xmpp_stream *stream); extern struct xmpp_stream_context *xmpp_stream_context_create(const char *domain, const char *domain_secret, xmpp_stream_ready_callback ready, xmpp_stream_recv_callback recv, xmpp_stream_destroy_callback destroy); +extern void xmpp_stream_context_add_cert(struct xmpp_stream_context *context, const char *cert_pem_file); +extern void xmpp_stream_context_add_key(struct xmpp_stream_context *context, const char *key_pem_file); extern void xmpp_stream_context_add_user(struct xmpp_stream_context *context, const char *user, const char *password); extern void xmpp_stream_context_dump(struct xmpp_stream_context *context, switch_stream_handle_t *stream); extern void xmpp_stream_context_destroy(struct xmpp_stream_context *context); From d6ac3b92504d6b1c067f24e88871dcb9ac1c54c2 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Tue, 16 Jul 2013 10:45:45 +0800 Subject: [PATCH 109/278] finally found a router supporting uPnP --- htdocs/portal/assets/js/fsportal.js | 24 ++++++++++++++++++++++++ htdocs/portal/index.html | 24 +++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/htdocs/portal/assets/js/fsportal.js b/htdocs/portal/assets/js/fsportal.js index 32c3b24703..618b3d5004 100644 --- a/htdocs/portal/assets/js/fsportal.js +++ b/htdocs/portal/assets/js/fsportal.js @@ -154,6 +154,12 @@ App.ShowSaysRoute = Ember.Route.extend({ } }); +App.ShowNatMapsRoute = Ember.Route.extend({ + setupController: function(controller) { + App.showNatMapsController.load(); + } +}); + App.ShowChatsRoute = Ember.Route.extend({ setupController: function(controller) { App.showChatsController.load(); @@ -203,6 +209,7 @@ App.Router.map(function(){ this.route("showAliases"); this.route("showCompletes"); this.route("showManagements"); + this.route("showNatMaps"); this.route("showSays"); this.route("showChats"); this.route("showInterfaces"); @@ -550,6 +557,23 @@ App.showManagementsController = Ember.ArrayController.create({ } }); +App.showNatMapsController = Ember.ArrayController.create({ + content: [], + init: function(){ + }, + load: function() { + var me = this; + $.getJSON("/txtapi/show?nat_map%20as%20json", function(data){ + me.set('total', data.row_count); + me.content.clear(); + if (data.row_count == 0) return; + + me.pushObjects(data.rows); + + }); + } +}); + App.showSaysController = Ember.ArrayController.create({ content: [], init: function(){ diff --git a/htdocs/portal/index.html b/htdocs/portal/index.html index 47270e6814..8771a8f09e 100644 --- a/htdocs/portal/index.html +++ b/htdocs/portal/index.html @@ -357,6 +357,28 @@ + + From 878c232b06a4c09cf507b2df481ab8dc3d7003d4 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Tue, 30 Jul 2013 14:35:39 -0500 Subject: [PATCH 207/278] mod_skinny: initial reg wasn't able to clean up due to missing device name, add new function to clean up given explicit device name, also add more logging --- src/mod/endpoints/mod_skinny/mod_skinny.c | 55 ++++++++++++++++++++ src/mod/endpoints/mod_skinny/mod_skinny.h | 1 + src/mod/endpoints/mod_skinny/skinny_server.c | 11 ++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 407aa65499..72cc82f9fa 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1326,12 +1326,64 @@ static int flush_listener_callback(void *pArg, int argc, char **argv, char **col return 0; } +void skinny_clean_device_from_db(listener_t *listener, char *device_name) +{ + if(!zstr(device_name)) { + skinny_profile_t *profile = listener->profile; + char *sql; + + skinny_log_l(listener, SWITCH_LOG_DEBUG, + "Clean device from DB with name '%s'\n", + device_name); + + if ((sql = switch_mprintf( + "DELETE FROM skinny_devices " + "WHERE name='%s'", + device_name))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + if ((sql = switch_mprintf( + "DELETE FROM skinny_lines " + "WHERE device_name='%s'", + device_name))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + if ((sql = switch_mprintf( + "DELETE FROM skinny_buttons " + "WHERE device_name='%s'", + device_name))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + if ((sql = switch_mprintf( + "DELETE FROM skinny_active_lines " + "WHERE device_name='%s'", + device_name))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + + } else { + skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, + "Clean device from DB, missing device name.\n"); + } +} + void skinny_clean_listener_from_db(listener_t *listener) { if(!zstr(listener->device_name)) { skinny_profile_t *profile = listener->profile; char *sql; + skinny_log_l(listener, SWITCH_LOG_DEBUG, + "Clean listener from DB with name '%s' and instance '%d'\n", + listener->device_name, listener->device_instance); + if ((sql = switch_mprintf( "DELETE FROM skinny_devices " "WHERE name='%s' and instance=%d", @@ -1364,6 +1416,9 @@ void skinny_clean_listener_from_db(listener_t *listener) switch_safe_free(sql); } + } else { + skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, + "Clean listener from DB, missing device name.\n"); } } diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index 76ca8017ac..d8ed1b03bf 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -276,6 +276,7 @@ uint8_t listener_is_ready(listener_t *listener); switch_status_t kill_listener(listener_t *listener, void *pvt); switch_status_t keepalive_listener(listener_t *listener, void *pvt); void skinny_clean_listener_from_db(listener_t *listener); +void skinny_clean_device_from_db(listener_t *listener, char *device_name); /*****************************************************************************/ /* CHANNEL FUNCTIONS */ diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index feac761cc3..95d486d3e0 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -975,6 +975,9 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r skinny_device_event(listener, ¶ms, SWITCH_EVENT_REQUEST_PARAMS, SWITCH_EVENT_SUBCLASS_ANY); switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "action", "skinny-auth"); + /* clean up all traces before adding to database */ + skinny_clean_device_from_db(listener, request->data.reg.device_name); + if (switch_xml_locate_user("id", request->data.reg.device_name, profile->domain, "", &xroot, &xdomain, &xuser, &xgroup, params) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't find device [%s@%s]\n" "You must define a domain called '%s' in your directory and add a user with id=\"%s\".\n" @@ -984,6 +987,11 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r goto end; } + /* we clean up device above, so this below block will never trigger. I don't + know the full details of why there would be multiple listeners with + the same device - maybe a VGC or similar? Not really high priority for + support at the moment, but may need to revisit this later */ + skinny_profile_find_listener_by_device_name_and_instance(listener->profile, request->data.reg.device_name, request->data.reg.instance, &listener2); if (listener2) { @@ -995,9 +1003,6 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r goto end; } - /* clean up all traces before adding to database */ - skinny_clean_listener_from_db(listener); - if ((sql = switch_mprintf( "INSERT INTO skinny_devices " "(name, user_id, instance, ip, type, max_streams, codec_string) " From da29531c08074ee55cd31005959033915becfd5a Mon Sep 17 00:00:00 2001 From: Seven Du Date: Wed, 31 Jul 2013 15:34:20 +0800 Subject: [PATCH 208/278] cleanup code, less dependency --- src/mod/xml_int/mod_xml_rpc/Makefile | 2 +- src/mod/xml_int/mod_xml_rpc/ws.c | 1 - src/mod/xml_int/mod_xml_rpc/ws.h | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mod/xml_int/mod_xml_rpc/Makefile b/src/mod/xml_int/mod_xml_rpc/Makefile index f0585dc324..bc7e4c1de4 100644 --- a/src/mod/xml_int/mod_xml_rpc/Makefile +++ b/src/mod/xml_int/mod_xml_rpc/Makefile @@ -65,7 +65,7 @@ ws.o LOCAL_CFLAGS = -w -I$(XMLRPC_DIR)/lib/expat/xmlparse -I$(XMLRPC_DIR)/lib/expat/xmltok -I$(XMLRPC_DIR) -I$(XMLRPC_DIR)/include LOCAL_CFLAGS+= -I$(XMLRPC_DIR)/lib/abyss/src -I$(XMLRPC_DIR)/lib/util/include -D_THREAD -D__EXTENSIONS__ -LOCAL_CFLAGS+= -I. -I../../../../libs/sofia-sip/libsofia-sip-ua/su +LOCAL_CFLAGS+= -I. include $(BASE)/build/modmake.rules diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c index 1ef76e3cc9..40829ffe40 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.c +++ b/src/mod/xml_int/mod_xml_rpc/ws.c @@ -1,5 +1,4 @@ #include "ws.h" -#include #ifndef _MSC_VER #include diff --git a/src/mod/xml_int/mod_xml_rpc/ws.h b/src/mod/xml_int/mod_xml_rpc/ws.h index 06fd3b2594..f5cb5e820a 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.h +++ b/src/mod/xml_int/mod_xml_rpc/ws.h @@ -11,24 +11,24 @@ #include #include #include +#include #else #pragma warning(disable:4996) +#define snprintf _snprintf #endif #include -#include #include #include #include #include #include #include -//#include "sha1.h" #include -#include #include <../lib/abyss/src/session.h> #include <../lib/abyss/src/conn.h> typedef TSession ws_tsession_t; +typedef int issize_t; struct globals_s { const SSL_METHOD *ssl_method; From 89eb8c4624c1055c85bec106573b81a072e60558 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Wed, 31 Jul 2013 16:19:58 +0800 Subject: [PATCH 209/278] trying to fix windows build --- .../mod_xml_rpc/mod_xml_rpc.2010.vcxproj | 14 +++++++++----- .../mod_xml_rpc/mod_xml_rpc.2012.vcxproj | 18 ++++++++++++++---- src/mod/xml_int/mod_xml_rpc/ws.h | 1 - 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj index f95e975efe..6420eb41e5 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj @@ -66,7 +66,7 @@ - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -83,7 +83,7 @@ X64 - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -98,7 +98,7 @@ - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -115,7 +115,7 @@ X64 - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -130,6 +130,7 @@ + @@ -149,7 +150,10 @@ false + + + - \ No newline at end of file + diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj index 1a65b391e0..af3305a50c 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj @@ -70,7 +70,7 @@ - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -87,7 +87,7 @@ X64 - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -102,7 +102,7 @@ - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -119,7 +119,7 @@ X64 - $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) ABYSS_WIN32;%(PreprocessorDefinitions) @@ -134,8 +134,15 @@ + + + {d331904d-a00a-4694-a5a3-fcff64ab5dbe} + + + {b4b62169-5ad4-4559-8707-3d933ac5db39} + {d2396dd7-7d38-473a-abb7-6f96d65ae1b9} @@ -153,6 +160,9 @@ false + + + diff --git a/src/mod/xml_int/mod_xml_rpc/ws.h b/src/mod/xml_int/mod_xml_rpc/ws.h index f5cb5e820a..09dfd282da 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.h +++ b/src/mod/xml_int/mod_xml_rpc/ws.h @@ -11,7 +11,6 @@ #include #include #include -#include #else #pragma warning(disable:4996) #define snprintf _snprintf From cbe7612ed5f65e50413a046388b425b3f1a8f0ca Mon Sep 17 00:00:00 2001 From: Seven Du Date: Wed, 31 Jul 2013 16:27:56 +0800 Subject: [PATCH 210/278] more cleanup --- src/mod/xml_int/mod_xml_rpc/ws.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c index 40829ffe40..a9a31c2b8b 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.c +++ b/src/mod/xml_int/mod_xml_rpc/ws.c @@ -219,13 +219,10 @@ static void sha1_digest(unsigned char *digest, char *in) int ws_handshake_kvp(wsh_t *wsh, char *key, char *version, char *proto) { - char uri[256] = ""; char input[256] = ""; unsigned char output[SHA1_HASH_SIZE] = ""; char b64[256] = ""; char respond[512] = ""; - issize_t bytes; - char *p, *e = 0; if (!wsh->tsession) { return -3; @@ -258,9 +255,6 @@ int ws_handshake_kvp(wsh_t *wsh, char *key, char *version, char *proto) snprintf(respond, sizeof(respond), "HTTP/1.1 400 Bad Request\r\n" "Sec-WebSocket-Version: 13\r\n\r\n"); - //printf("ERR:\n%s\n", respond); - - ws_raw_write(wsh, respond, strlen(respond)); ws_close(wsh, WS_NONE); From bb2832eb115d9c1937e79e99abbbc5b7a82d6a72 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 31 Jul 2013 07:16:23 -0400 Subject: [PATCH 211/278] mod_spandsp: added UUID command completion to start/stop_tone_detect commands --- src/mod/applications/mod_spandsp/mod_spandsp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index 91c907426a..51e2505758 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -648,7 +648,7 @@ switch_status_t load_configuration(switch_bool_t reload) "Unable to add tone_descriptor: %s, tone: %s. (too many tones)\n", name, tone_name); switch_goto_status(SWITCH_STATUS_FALSE, done); } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Adding tone_descriptor: %s, tone: %s(%d)\n", name, tone_name, id); /* add elements to tone */ for (element = switch_xml_child(tone, "element"); element; element = switch_xml_next(element)) { @@ -683,7 +683,7 @@ switch_status_t load_configuration(switch_bool_t reload) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid element param.\n"); switch_goto_status(SWITCH_STATUS_FALSE, done); } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Adding tone_descriptor: %s, tone: %s(%d), element (%d, %d, %d, %d)\n", name, tone_name, id, freq1, freq2, min, max); tone_descriptor_add_tone_element(descriptor, id, freq1, freq2, min, max); } @@ -713,7 +713,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) *module_interface = switch_loadable_module_create_module_interface(pool, modname); switch_mutex_init(&spandsp_globals.mutex, SWITCH_MUTEX_NESTED, pool); - SWITCH_ADD_APP(app_interface, "t38_gateway", "Convert to T38 Gateway if tones are heard", "Convert to T38 Gateway if tones are heard", + SWITCH_ADD_APP(app_interface, "t38_gateway", "Convert to T38 Gateway if tones are heard", "Convert to T38 Gateway if tones are heard", t38_gateway_function, "", SAF_MEDIA_TAP); SWITCH_ADD_APP(app_interface, "rxfax", "FAX Receive Application", "FAX Receive Application", spanfax_rx_function, SPANFAX_RX_USAGE, @@ -734,7 +734,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_ADD_APP(app_interface, "spandsp_send_tdd", "Send TDD data", "Send TDD data", tdd_send_function, "", SAF_NONE); - SWITCH_ADD_APP(app_interface, "spandsp_start_fax_detect", "start fax detect", "start fax detect", spandsp_fax_detect_session_function, + SWITCH_ADD_APP(app_interface, "spandsp_start_fax_detect", "start fax detect", "start fax detect", spandsp_fax_detect_session_function, "[ ][ ][ ]", SAF_NONE); SWITCH_ADD_APP(app_interface, "spandsp_stop_fax_detect", "stop fax detect", "stop fax detect", spandsp_stop_fax_detect_session_function, "", SAF_NONE); @@ -752,9 +752,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init) SWITCH_ADD_APP(app_interface, "stop_tone_detect", "Stop background tone detection with cadence", "", stop_tone_detect_app, "", SAF_NONE); SWITCH_ADD_API(api_interface, "start_tone_detect", "Start background tone detection with cadence", start_tone_detect_api, " "); SWITCH_ADD_API(api_interface, "stop_tone_detect", "Stop background tone detection with cadence", stop_tone_detect_api, ""); + switch_console_set_complete("add start_tone_detect ::console::list_uuid"); + switch_console_set_complete("add stop_tone_detect ::console::list_uuid"); } - SWITCH_ADD_API(api_interface, "start_tdd_detect", "Start background tdd detection", start_tdd_detect_api, ""); SWITCH_ADD_API(api_interface, "stop_tdd_detect", "Stop background tdd detection", stop_tdd_detect_api, ""); From 358b6e08353a0db69e3ebe60c85d43e0193eb30a Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Wed, 31 Jul 2013 17:16:43 -0500 Subject: [PATCH 212/278] mod_skinny: filter sip mwi events --- src/mod/endpoints/mod_skinny/mod_skinny.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 72cc82f9fa..165624a303 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -2253,6 +2253,10 @@ static void skinny_message_waiting_event_handler(switch_event_t *event) return; } + if (!strncmp("sip:", account, 4)) { + return; + } + if (!(yn = switch_event_get_header(event, "mwi-messages-waiting"))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required Header 'MWI-Messages-Waiting'\n"); return; From 6c8eb054ad06771d92e86703ea0b55896febcf93 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Wed, 31 Jul 2013 17:17:07 -0500 Subject: [PATCH 213/278] mod_skinny: allow to work with native pgsql --- src/mod/endpoints/mod_skinny/mod_skinny.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 165624a303..dc83c47294 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1850,14 +1850,8 @@ switch_status_t skinny_profile_set(skinny_profile_t *profile, const char *var, c profile->keep_alive = atoi(val); } else if (!strcasecmp(var, "date-format")) { strncpy(profile->date_format, val, 6); - } else if (!strcasecmp(var, "odbc-dsn")) { - if (!zstr(val)) { - if (switch_odbc_available()) { - profile->odbc_dsn = switch_core_strdup(profile->pool, val); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC IS NOT AVAILABLE!\n"); - } - } + } else if (!strcasecmp(var, "odbc-dsn") && !zstr(val)) { + profile->odbc_dsn = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "debug")) { profile->debug = atoi(val); } else if (!strcasecmp(var, "auto-restart")) { From f255f65a82e2cfe1aed1f44aa2459e5faaed150e Mon Sep 17 00:00:00 2001 From: Seven Du Date: Thu, 1 Aug 2013 09:50:51 +0800 Subject: [PATCH 214/278] add SHORT_DATE_TIME support --- src/mod/say/mod_say_zh/mod_say_zh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/say/mod_say_zh/mod_say_zh.c b/src/mod/say/mod_say_zh/mod_say_zh.c index 87ab0042b8..ac5ba32e42 100644 --- a/src/mod/say/mod_say_zh/mod_say_zh.c +++ b/src/mod/say/mod_say_zh/mod_say_zh.c @@ -497,6 +497,7 @@ static switch_status_t zh_say(switch_core_session_t *session, char *tosay, switc case SST_CURRENT_DATE: case SST_CURRENT_TIME: case SST_CURRENT_DATE_TIME: + case SST_SHORT_DATE_TIME: say_cb = zh_say_time; break; case SST_IP_ADDRESS: From b71fe0031dd618bea21763bbf79af4e3c9015031 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Wed, 31 Jul 2013 21:45:18 -0500 Subject: [PATCH 215/278] FS-5655 try this --- .../src/msvc/make_cielab_luts.2010.vcxproj | 2 +- .../src/msvc/make_cielab_luts.2012.vcxproj | 2 +- .../msvc/make_math_fixed_tables.2010.vcxproj | 2 +- .../msvc/make_math_fixed_tables.2012.vcxproj | 2 +- .../mod_xml_rpc/mod_xml_rpc.2010.vcxproj | 324 +++++++++--------- .../mod_xml_rpc/mod_xml_rpc.2012.vcxproj | 6 + 6 files changed, 175 insertions(+), 163 deletions(-) diff --git a/libs/spandsp/src/msvc/make_cielab_luts.2010.vcxproj b/libs/spandsp/src/msvc/make_cielab_luts.2010.vcxproj index 282cc186cf..de83ac7306 100644 --- a/libs/spandsp/src/msvc/make_cielab_luts.2010.vcxproj +++ b/libs/spandsp/src/msvc/make_cielab_luts.2010.vcxproj @@ -27,7 +27,7 @@ <_ProjectFileVersion>10.0.30319.1 $(PlatformName)\$(Configuration)\ - $(PlatformName)\make_at_dictionary\$(Configuration)\ + $(PlatformName)\make_cielab_luts\$(Configuration)\ false diff --git a/libs/spandsp/src/msvc/make_cielab_luts.2012.vcxproj b/libs/spandsp/src/msvc/make_cielab_luts.2012.vcxproj index 881c035554..d7c54ca97f 100644 --- a/libs/spandsp/src/msvc/make_cielab_luts.2012.vcxproj +++ b/libs/spandsp/src/msvc/make_cielab_luts.2012.vcxproj @@ -28,7 +28,7 @@ <_ProjectFileVersion>10.0.30319.1 $(PlatformName)\$(Configuration)\ - $(PlatformName)\make_at_dictionary\$(Configuration)\ + $(PlatformName)\make_cielab_luts\$(Configuration)\ false diff --git a/libs/spandsp/src/msvc/make_math_fixed_tables.2010.vcxproj b/libs/spandsp/src/msvc/make_math_fixed_tables.2010.vcxproj index a71f70e556..61465917f1 100644 --- a/libs/spandsp/src/msvc/make_math_fixed_tables.2010.vcxproj +++ b/libs/spandsp/src/msvc/make_math_fixed_tables.2010.vcxproj @@ -27,7 +27,7 @@ <_ProjectFileVersion>10.0.30319.1 $(PlatformName)\$(Configuration)\ - $(PlatformName)\make_at_dictionary\$(Configuration)\ + $(PlatformName)\make_math_fixed_tables\$(Configuration)\ false diff --git a/libs/spandsp/src/msvc/make_math_fixed_tables.2012.vcxproj b/libs/spandsp/src/msvc/make_math_fixed_tables.2012.vcxproj index 610bb28625..ab6b648bcb 100644 --- a/libs/spandsp/src/msvc/make_math_fixed_tables.2012.vcxproj +++ b/libs/spandsp/src/msvc/make_math_fixed_tables.2012.vcxproj @@ -28,7 +28,7 @@ <_ProjectFileVersion>10.0.30319.1 $(PlatformName)\$(Configuration)\ - $(PlatformName)\make_at_dictionary\$(Configuration)\ + $(PlatformName)\make_math_fixed_tables\$(Configuration)\ false diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj index 6420eb41e5..76e7eb0b29 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj @@ -1,159 +1,165 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - mod_xml_rpc - {CBEC7225-0C21-4DA8-978E-1F158F8AD950} - mod_xml_rpc - Win32Proj - - - - DynamicLibrary - MultiByte - - - DynamicLibrary - MultiByte - - - DynamicLibrary - MultiByte - - - DynamicLibrary - MultiByte - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - - - - $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) - ABYSS_WIN32;%(PreprocessorDefinitions) - - - - - ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibD;%(AdditionalLibraryDirectories) - false - - - - - - - X64 - - - $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) - ABYSS_WIN32;%(PreprocessorDefinitions) - - - - - %(AdditionalLibraryDirectories) - false - - - MachineX64 - - - - - $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) - ABYSS_WIN32;%(PreprocessorDefinitions) - - - - - ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibR;%(AdditionalLibraryDirectories) - false - - - - - - - X64 - - - $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) - ABYSS_WIN32;%(PreprocessorDefinitions) - - - - - %(AdditionalLibraryDirectories) - false - - - MachineX64 - - - - - - - - - {d2396dd7-7d38-473a-abb7-6f96d65ae1b9} - - - {0d108721-eae8-4baf-8102-d8960ec93647} - - - {cee544a9-0303-44c2-8ece-efa7d7bcbbba} - - - {b535402e-38d2-4d54-8360-423acbd17192} - - - {202d7a4e-760d-4d0e-afa1-d7459ced30ff} - false - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + mod_xml_rpc + {CBEC7225-0C21-4DA8-978E-1F158F8AD950} + mod_xml_rpc + Win32Proj + + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + ABYSS_WIN32;%(PreprocessorDefinitions) + + + + + ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibD;%(AdditionalLibraryDirectories) + false + + + + + + + X64 + + + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + ABYSS_WIN32;%(PreprocessorDefinitions) + + + + + %(AdditionalLibraryDirectories) + false + + + MachineX64 + + + + + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + ABYSS_WIN32;%(PreprocessorDefinitions) + + + + + ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibR;%(AdditionalLibraryDirectories) + false + + + + + + + X64 + + + $(SolutionDir)libs\openssl-1.0.1c\include;$(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories) + ABYSS_WIN32;%(PreprocessorDefinitions) + + + + + %(AdditionalLibraryDirectories) + false + + + MachineX64 + + + + + + + + + {d331904d-a00a-4694-a5a3-fcff64ab5dbe} + + + {b4b62169-5ad4-4559-8707-3d933ac5db39} + + + {d2396dd7-7d38-473a-abb7-6f96d65ae1b9} + + + {0d108721-eae8-4baf-8102-d8960ec93647} + + + {cee544a9-0303-44c2-8ece-efa7d7bcbbba} + + + {b535402e-38d2-4d54-8360-423acbd17192} + + + {202d7a4e-760d-4d0e-afa1-d7459ced30ff} + false + + + + + + + + + \ No newline at end of file diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj index af3305a50c..afd6d9f0c5 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj @@ -137,6 +137,12 @@ + + {d331904d-a00a-4694-a5a3-fcff64ab5dbe} + + + {b4b62169-5ad4-4559-8707-3d933ac5db39} + {d331904d-a00a-4694-a5a3-fcff64ab5dbe} From 8d2805fc4291a2c93078098fe084071072354290 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Thu, 1 Aug 2013 13:56:30 -0400 Subject: [PATCH 216/278] FS-5659 --resolve Add logging for spandsp dtmf detector when dtmf_verbose channel variable is set to true --- .../applications/mod_spandsp/mod_spandsp.h | 4 +- .../mod_spandsp/mod_spandsp_dsp.c | 21 +++-- .../mod_spandsp/mod_spandsp_fax.c | 88 +++++++++---------- .../mod_spandsp/mod_spandsp_modem.c | 10 +-- 4 files changed, 64 insertions(+), 59 deletions(-) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h index fafccae693..323ff07a45 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.h +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * @@ -141,7 +141,7 @@ switch_status_t spandsp_fax_detect_session(switch_core_session_t *session, int hits, const char *app, const char *data, switch_tone_detect_callback_t callback); switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session); -void spanfax_log_message(void *user_data, int level, const char *msg); +void mod_spandsp_log_message(void *session, int level, const char *msg); switch_status_t load_configuration(switch_bool_t reload); void mod_spandsp_indicate_data(switch_core_session_t *session, switch_bool_t self, switch_bool_t on); diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c index 03d7297edd..42e31d1ef5 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c @@ -349,6 +349,7 @@ switch_status_t spandsp_tdd_decode_session(switch_core_session_t *session) typedef struct { switch_core_session_t *session; dtmf_rx_state_t *dtmf_detect; + int verbose; char last_digit; uint32_t samples; uint32_t last_digit_end; @@ -394,6 +395,10 @@ static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_da switch (type) { case SWITCH_ABC_TYPE_INIT: { pvt->dtmf_detect = dtmf_rx_init(NULL, NULL, NULL); + span_log_set_message_handler(dtmf_rx_get_logging_state(pvt->dtmf_detect), mod_spandsp_log_message, pvt->session); + if (pvt->verbose) { + span_log_set_level(dtmf_rx_get_logging_state(pvt->dtmf_detect), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + } dtmf_rx_parms(pvt->dtmf_detect, pvt->filter_dialtone, pvt->twist, pvt->reverse_twist, pvt->threshold); dtmf_rx_set_realtime_callback(pvt->dtmf_detect, spandsp_dtmf_rx_realtime_callback, pvt); break; @@ -500,6 +505,10 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session) pvt->filter_dialtone = 0; } + if ((value = switch_channel_get_variable(channel, "dtmf_verbose"))) { + pvt->verbose = switch_true(value); + } + if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } @@ -554,7 +563,7 @@ static switch_bool_t callprogress_detector_process_buffer(switch_media_bug_t *bu * Allocate the tone descriptor * * @param descriptor the descriptor to create - * @param name the descriptor name + * @param name the descriptor name * @param memory_pool the pool to use * @return SWITCH_STATUS_SUCCESS if successful */ @@ -632,7 +641,7 @@ static void tone_report_callback(void *user_data, int code, int level, int delay /** * Process tone segment report from spandsp (for debugging) - * + * * @param user_data the tone_detector * @param f1 the first frequency of the segment * @param f2 the second frequency of the segment @@ -705,7 +714,7 @@ static switch_bool_t tone_detector_process_buffer(tone_detector_t *detector, voi * Destroy the tone detector * @param detector the detector to destroy */ -static void tone_detector_destroy(tone_detector_t *detector) +static void tone_detector_destroy(tone_detector_t *detector) { if (detector) { if (detector->spandsp_detector) { @@ -720,7 +729,7 @@ static void tone_detector_destroy(tone_detector_t *detector) * Start call progress detection * * @param session the session to detect - * @param name of the descriptor to use + * @param name of the descriptor to use * @return SWITCH_STATUS_SUCCESS if successful */ switch_status_t callprogress_detector_start(switch_core_session_t *session, const char *name) @@ -759,7 +768,7 @@ switch_status_t callprogress_detector_start(switch_core_session_t *session, cons /** * Process a buffer of audio data for call progress tones * - * @param bug the session's media bug + * @param bug the session's media bug * @param user_data the detector * @param type the type of data available from the bug * @return SWITCH_TRUE @@ -817,7 +826,7 @@ static switch_bool_t callprogress_detector_process_buffer(switch_media_bug_t *bu /** * Stop call progress detection - * @param session the session to stop + * @param session the session to stop * @return SWITCH_STATUS_SUCCESS if successful */ switch_status_t callprogress_detector_stop(switch_core_session_t *session) diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 02623fd20e..21a9cc51dc 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * @@ -167,7 +167,7 @@ static int del_pvt(pvt_t *del_pvt) r = 1; break; } - + l = p; } @@ -259,15 +259,11 @@ static void counter_increment(void) switch_mutex_unlock(spandsp_globals.mutex); } -void spanfax_log_message(void *user_data, int level, const char *msg) +void mod_spandsp_log_message(void *user_data, int level, const char *msg) { int fs_log_level; - switch_core_session_t *session; - pvt_t *pvt; - - pvt = (pvt_t *) user_data; - session = pvt->session; - + switch_core_session_t *session = (switch_core_session_t *)user_data; + switch (level) { case SPAN_LOG_NONE: return; @@ -329,7 +325,7 @@ static int phase_b_handler(t30_state_t *s, void *user_data, int result) switch_channel_set_variable(channel, "fax_remote_model", switch_str_nil(t30_get_rx_model(s))); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "=== Negotiation Result =======================================================\n"); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "=== Negotiation Result =======================================================\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote station id: %s\n", far_ident); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Local station id: %s\n", local_ident); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Transfer Rate: %i\n", t30_stats.bit_rate); @@ -339,7 +335,7 @@ static int phase_b_handler(t30_state_t *s, void *user_data, int result) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote vendor: %s\n", switch_str_nil(t30_get_rx_vendor(s))); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "remote model: %s\n", switch_str_nil(t30_get_rx_model(s))); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "==============================================================================\n"); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "==============================================================================\n"); switch_channel_execute_on(channel, "execute_on_fax_phase_b"); @@ -673,9 +669,9 @@ static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uin if (r < 0) { t30_state_t *t30; - + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "TERMINATING T30 STATE\n"); - + if (pvt->t38_state && (t30 = t38_terminal_get_t30_state(pvt->t38_state))) { t30_terminate(t30); } @@ -733,8 +729,8 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) fax_set_transmit_on_idle(fax, TRUE); - span_log_set_message_handler(fax_get_logging_state(fax), spanfax_log_message, pvt); - span_log_set_message_handler(t30_get_logging_state(t30), spanfax_log_message, pvt); + span_log_set_message_handler(fax_get_logging_state(fax), mod_spandsp_log_message, pvt->session); + span_log_set_message_handler(t30_get_logging_state(t30), mod_spandsp_log_message, pvt->session); if (pvt->verbose) { span_log_set_level(fax_get_logging_state(fax), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); @@ -772,7 +768,7 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) pvt->t38_core = t38_terminal_get_t38_core_state(pvt->t38_state); - if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, fec_span, fec_entries, + if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, fec_span, fec_entries, (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n"); return SWITCH_STATUS_FALSE; @@ -789,8 +785,8 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) } } - span_log_set_message_handler(t38_terminal_get_logging_state(t38), spanfax_log_message, pvt); - span_log_set_message_handler(t30_get_logging_state(t30), spanfax_log_message, pvt); + span_log_set_message_handler(t38_terminal_get_logging_state(t38), mod_spandsp_log_message, pvt->session); + span_log_set_message_handler(t30_get_logging_state(t30), mod_spandsp_log_message, pvt->session); if (pvt->verbose) { span_log_set_level(t38_terminal_get_logging_state(t38), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); @@ -811,13 +807,13 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my T.38 structs\n"); t38_gateway_free(pvt->t38_gateway_state); pvt->t38_gateway_state = NULL; - + return SWITCH_STATUS_FALSE; } pvt->t38_core = t38_gateway_get_t38_core_state(pvt->t38_gateway_state); - if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, fec_span, fec_entries, + if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, fec_span, fec_entries, (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n"); t38_gateway_free(pvt->t38_gateway_state); @@ -843,8 +839,8 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) t38_gateway_set_ecm_capability(pvt->t38_gateway_state, TRUE); } - span_log_set_message_handler(t38_gateway_get_logging_state(pvt->t38_gateway_state), spanfax_log_message, pvt); - span_log_set_message_handler(t38_core_get_logging_state(pvt->t38_core), spanfax_log_message, pvt); + span_log_set_message_handler(t38_gateway_get_logging_state(pvt->t38_gateway_state), mod_spandsp_log_message, pvt->session); + span_log_set_message_handler(t38_core_get_logging_state(pvt->t38_core), mod_spandsp_log_message, pvt->session); if (pvt->verbose) { span_log_set_level(t38_gateway_get_logging_state(pvt->t38_gateway_state), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); @@ -988,7 +984,7 @@ static t38_mode_t configure_t38(pvt_t *pvt) t38_set_jbig_transcoding(pvt->t38_core, t38_options->T38FaxTranscodingJBIG); t38_set_max_datagram_size(pvt->t38_core, t38_options->T38FaxMaxDatagram); - if (t38_options->T38FaxRateManagement) { + if (t38_options->T38FaxRateManagement) { if (!strcasecmp(t38_options->T38FaxRateManagement, "transferredTCF")) { method = 2; } else { @@ -1043,7 +1039,7 @@ static t38_mode_t negotiate_t38(pvt_t *pvt) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxMaxDatagram = %d\n", t38_options->T38FaxMaxDatagram); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxUdpEC = '%s'\n", t38_options->T38FaxUdpEC); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38VendorInfo = '%s'\n", switch_str_nil(t38_options->T38VendorInfo)); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ip = '%s'\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ip = '%s'\n", t38_options->remote_ip ? t38_options->remote_ip : "Not specified"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "port = %d\n", t38_options->remote_port); @@ -1128,7 +1124,7 @@ static t38_mode_t request_t38(pvt_t *pvt) } if ((t38_options = switch_channel_get_private(channel, "t38_options"))) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s already has T.38 data\n", switch_channel_get_name(channel)); enabled = 0; } @@ -1427,11 +1423,11 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat int tx = 0; switch_status_t status; - /* + /* if we are in T.38 mode, we should: 1- initialize the ptv->t38_state stuff, if not done and then set some callbacks when reading frames. The only thing we need, then, in this loop, is: - - read a frame without blocking + - read a frame without blocking - eventually feed that frame in spandsp, - call t38_terminal_send_timeout(), sleep for a while @@ -1458,7 +1454,7 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED); spanfax_init(pvt, T38_MODE); configure_t38(pvt); - + /* This will change the rtp stack to udptl mode */ msg.from = __FILE__; msg.message_id = SWITCH_MESSAGE_INDICATE_UDPTL_MODE; @@ -1475,7 +1471,7 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat request_t38(pvt); } } - + if (switch_channel_test_app_flag_key("T38", channel, CF_APP_T38)) { if (negotiate_t38(pvt) == T38_MODE_NEGOTIATED) { /* is is safe to call this again, it was already called above in AUDIO_MODE */ @@ -1493,11 +1489,11 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat /* dunno what to do, most likely you will not get too many of these since we turn off the timer in udptl mode */ continue; } - + if (switch_test_flag(read_frame, SFF_UDPTL_PACKET) && read_frame->packet && read_frame->packetlen) { /* now we know we can cast frame->packet to a udptl structure */ //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "READ %d udptl bytes\n", read_frame->packetlen); - + udptl_rx_packet(pvt->udptl_state, read_frame->packet, read_frame->packetlen); } } @@ -1602,7 +1598,7 @@ static switch_status_t t38_gateway_on_soft_execute(switch_core_session_t *sessio if (!(other_session = switch_core_session_locate(peer_uuid))) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Cannot locate channel with uuid %s", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Cannot locate channel with uuid %s", switch_channel_get_name(channel), peer_uuid); goto end; } @@ -1829,13 +1825,13 @@ static switch_status_t t38_gateway_on_consume_media(switch_core_session_t *sessi switch_ivr_sleep(session, 0, SWITCH_TRUE, NULL); if (switch_true(t38_trace)) { - trace_read = switch_core_session_sprintf(session, "%s%s%s_read.raw", SWITCH_GLOBAL_dirs.temp_dir, + trace_read = switch_core_session_sprintf(session, "%s%s%s_read.raw", SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session)); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Tracing inbound audio to %s\n", trace_read); switch_channel_set_variable(channel, "t38_trace_read", trace_read); - trace_write = switch_core_session_sprintf(session, "%s%s%s_write.raw", SWITCH_GLOBAL_dirs.temp_dir, + trace_write = switch_core_session_sprintf(session, "%s%s%s_write.raw", SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session)); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Tracing outbound audio to %s\n", trace_write); @@ -1865,7 +1861,7 @@ static switch_status_t t38_gateway_on_consume_media(switch_core_session_t *sessi } else { if (read_fd != FAX_INVALID_SOCKET) { switch_ssize_t rv; - do { rv = write(read_fd, read_frame->data, read_frame->datalen); } while (rv == -1 && errno == EINTR); + do { rv = write(read_fd, read_frame->data, read_frame->datalen); } while (rv == -1 && errno == EINTR); } if (t38_gateway_rx(pvt->t38_gateway_state, (int16_t *) read_frame->data, read_frame->samples)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "fax_rx reported an error\n"); @@ -1994,7 +1990,7 @@ switch_bool_t t38_gateway_start(switch_core_session_t *session, const char *app, switch_channel_set_variable(peer ? channel : other_channel, "t38_gateway_format", "audio"); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s starting gateway mode to %s\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s starting gateway mode to %s\n", switch_channel_get_name(peer ? channel : other_channel), switch_channel_get_name(peer ? other_channel : channel)); @@ -2033,20 +2029,20 @@ typedef struct { int expires; int default_sleep; int default_expires; - switch_tone_detect_callback_t callback; + switch_tone_detect_callback_t callback; modem_connect_tones_rx_state_t rx_tones; switch_media_bug_t *bug; switch_core_session_t *session; int bug_running; - + } spandsp_fax_tone_container_t; static switch_status_t tone_on_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf, switch_dtmf_direction_t direction) { switch_channel_t *channel = switch_core_session_get_channel(session); spandsp_fax_tone_container_t *cont = switch_channel_get_private(channel, "_fax_tone_detect_"); - + if (!cont || dtmf->digit != 'f') { return SWITCH_STATUS_SUCCESS; @@ -2060,7 +2056,7 @@ static switch_status_t tone_on_dtmf(switch_core_session_t *session, const switch switch_core_session_execute_application_async(cont->session, cont->app, cont->data); } } - + return SWITCH_STATUS_SUCCESS; } @@ -2085,13 +2081,13 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da case SWITCH_ABC_TYPE_WRITE_REPLACE: { int skip = 0; - + if (type == SWITCH_ABC_TYPE_READ_REPLACE) { frame = switch_core_media_bug_get_read_replace_frame(bug); } else { frame = switch_core_media_bug_get_write_replace_frame(bug); } - + if (cont->sleep) { cont->sleep--; if (cont->sleep) { @@ -2122,7 +2118,7 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da if (cont->hits) { switch_event_t *event; - + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, "Fax Tone Detected. [%s][%s]\n", cont->app, switch_str_nil(cont->data)); @@ -2134,7 +2130,7 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da switch_core_session_execute_application_async(cont->session, cont->app, cont->data); } } - + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_TONE) == SWITCH_STATUS_SUCCESS) { switch_event_t *dup; @@ -2148,7 +2144,7 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da channel = switch_core_session_get_channel(session); if (channel) switch_channel_event_set_data(channel, event); } - + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { switch_event_fire(&dup); } @@ -2163,7 +2159,7 @@ static switch_bool_t tone_detect_callback(switch_media_bug_t *bug, void *user_da rval = SWITCH_FALSE; } - + } break; case SWITCH_ABC_TYPE_WRITE: diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_modem.c b/src/mod/applications/mod_spandsp/mod_spandsp_modem.c index b1a794a387..dec93970a0 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_modem.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_modem.c @@ -346,23 +346,23 @@ switch_status_t modem_init(modem_t *modem, modem_control_handler_t control_handl if (spandsp_globals.modem_verbose) { logging = t31_get_logging_state(modem->t31_state); - span_log_set_message_handler(logging, spanfax_log_message, NULL); + span_log_set_message_handler(logging, mod_spandsp_log_message, NULL); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); logging = v17_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v17_rx); - span_log_set_message_handler(logging, spanfax_log_message, NULL); + span_log_set_message_handler(logging, mod_spandsp_log_message, NULL); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); logging = v29_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v29_rx); - span_log_set_message_handler(logging, spanfax_log_message, NULL); + span_log_set_message_handler(logging, mod_spandsp_log_message, NULL); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); logging = v27ter_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v27ter_rx); - span_log_set_message_handler(logging, spanfax_log_message, NULL); + span_log_set_message_handler(logging, mod_spandsp_log_message, NULL); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); logging = t38_core_get_logging_state(modem->t38_core); - span_log_set_message_handler(logging, spanfax_log_message, NULL); + span_log_set_message_handler(logging, mod_spandsp_log_message, NULL); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); } From fed93fd49ecf242cb79e9397686768f298b70c25 Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Thu, 1 Aug 2013 13:29:51 -0500 Subject: [PATCH 217/278] mod_xml_rpc revert extra stuff --- src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj index afd6d9f0c5..af3305a50c 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2012.vcxproj @@ -137,12 +137,6 @@ - - {d331904d-a00a-4694-a5a3-fcff64ab5dbe} - - - {b4b62169-5ad4-4559-8707-3d933ac5db39} - {d331904d-a00a-4694-a5a3-fcff64ab5dbe} From 40c983beeb525ebca68568d97071234557a4cc09 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Thu, 1 Aug 2013 14:37:28 -0400 Subject: [PATCH 218/278] FS-5657 --resolve fix mod_rayo build errors in CentOS 5 --- src/mod/event_handlers/mod_rayo/Makefile | 2 + src/mod/event_handlers/mod_rayo/iks_helpers.c | 87 +++++++++++++ src/mod/event_handlers/mod_rayo/iks_helpers.h | 97 ++------------- .../event_handlers/mod_rayo/rayo_elements.c | 117 ++++++++++++++++++ .../event_handlers/mod_rayo/rayo_elements.h | 82 ++---------- 5 files changed, 221 insertions(+), 164 deletions(-) create mode 100644 src/mod/event_handlers/mod_rayo/rayo_elements.c diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile index d15ae1cbb1..6fccfdf318 100644 --- a/src/mod/event_handlers/mod_rayo/Makefile +++ b/src/mod/event_handlers/mod_rayo/Makefile @@ -10,6 +10,7 @@ LOCAL_OBJS= $(IKS_LA) \ iks_helpers.o \ nlsml.o \ rayo_components.o \ + rayo_elements.o \ rayo_input_component.o \ rayo_output_component.o \ rayo_prompt_component.o \ @@ -21,6 +22,7 @@ LOCAL_SOURCES= \ iks_helpers.c \ nlsml.c \ rayo_components.c \ + rayo_elements.c \ rayo_input_component.c \ rayo_output_component.c \ rayo_prompt_component.c \ diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c index 46fd1c4cea..0b5616a1f3 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.c +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c @@ -305,6 +305,93 @@ int value_matches(const char *value, const char *rule) return 0; } +/** + * Validate boolean + * @param value + * @return SWTICH_TRUE if boolean + */ +int iks_attrib_is_bool(const char *value) +{ + if (value && *value && (!strcasecmp("true", value) || !strcasecmp("false", value))) { + return SWITCH_TRUE; + } + return SWITCH_FALSE; +} + +/** + * Validate integer + * @param value + * @return SWTICH_TRUE if not negative + */ +int iks_attrib_is_not_negative(const char *value) +{ + if (value && *value && switch_is_number(value)) { + int value_i = atoi(value); + if (value_i >= 0) { + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/** + * Validate integer + * @param value + * @return SWTICH_TRUE if positive + */ +int iks_attrib_is_positive(const char *value) +{ + if (value && *value && switch_is_number(value)) { + int value_i = atoi(value); + if (value_i > 0) { + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/** + * Validate integer + * @param value + * @return SWTICH_TRUE if positive or -1 + */ +int iks_attrib_is_positive_or_neg_one(const char *value) +{ + if (value && *value && switch_is_number(value)) { + int value_i = atoi(value); + if (value_i == -1 || value_i > 0) { + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/** + * Validate string + * @param value + * @return SWTICH_TRUE + */ +int iks_attrib_is_any(const char *value) +{ + return SWITCH_TRUE; +} + +/** + * Validate decimal + * @param value + * @return SWTICH_TRUE if 0.0 <= x <= 1.0 + */ +int iks_attrib_is_decimal_between_zero_and_one(const char *value) +{ + if (value && *value && switch_is_number(value)) { + double value_d = atof(value); + if (value_d >= 0.0 || value_d <= 1.0) { + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + #define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1) /** diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 442722a267..90a5ca688d 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -73,101 +73,20 @@ extern char *iks_server_dialback_key(const char *secret, const char *receiving_s /** A function to validate attribute value */ typedef int (*iks_attrib_validation_function)(const char *); -#define ELEMENT(name) inline int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0; +#define ELEMENT_DECL(name) extern int VALIDATE_##name(iks *node); +#define ELEMENT(name) int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0; #define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def)); #define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule); #define ELEMENT_END return result; } extern int value_matches(const char *value, const char *rule); -#define ATTRIB_RULE(rule) inline int iks_attrib_is_ ## rule (const char *value) - -/** - * Validate boolean - * @param value - * @return SWTICH_TRUE if boolean - */ -ATTRIB_RULE(bool) -{ - if (value && *value && (!strcasecmp("true", value) || !strcasecmp("false", value))) { - return SWITCH_TRUE; - } - return SWITCH_FALSE; -} - -/** - * Validate integer - * @param value - * @return SWTICH_TRUE if not negative - */ -ATTRIB_RULE(not_negative) -{ - if (value && *value && switch_is_number(value)) { - int value_i = atoi(value); - if (value_i >= 0) { - return SWITCH_TRUE; - } - } - return SWITCH_FALSE; -} - -/** - * Validate integer - * @param value - * @return SWTICH_TRUE if positive - */ -ATTRIB_RULE(positive) -{ - if (value && *value && switch_is_number(value)) { - int value_i = atoi(value); - if (value_i > 0) { - return SWITCH_TRUE; - } - } - return SWITCH_FALSE; -} - -/** - * Validate integer - * @param value - * @return SWTICH_TRUE if positive or -1 - */ -ATTRIB_RULE(positive_or_neg_one) -{ - if (value && *value && switch_is_number(value)) { - int value_i = atoi(value); - if (value_i == -1 || value_i > 0) { - return SWITCH_TRUE; - } - } - return SWITCH_FALSE; -} - -/** - * Validate string - * @param value - * @return SWTICH_TRUE - */ -ATTRIB_RULE(any) -{ - return SWITCH_TRUE; -} - -/** - * Validate decimal - * @param value - * @return SWTICH_TRUE if 0.0 <= x <= 1.0 - */ -ATTRIB_RULE(decimal_between_zero_and_one) -{ - if (value && *value && switch_is_number(value)) { - double value_d = atof(value); - if (value_d >= 0.0 || value_d <= 1.0) { - return SWITCH_TRUE; - } - } - return SWITCH_FALSE; -} +extern int iks_attrib_is_bool(const char *value); +extern int iks_attrib_is_not_negative(const char *value); +extern int iks_attrib_is_positive(const char *value); +extern int iks_attrib_is_positive_or_neg_one(const char *value); +extern int iks_attrib_is_any(const char *value); +extern int iks_attrib_is_decimal_between_zero_and_one(const char *value); #endif diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c new file mode 100644 index 0000000000..07051c1474 --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c @@ -0,0 +1,117 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2013, Grasshopper + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla 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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_elements.c -- Rayo XML element definition + * + */ +#include "rayo_elements.h" + +/** + * component validation + */ +ELEMENT(RAYO_INPUT) + STRING_ATTRIB(mode, any, "any,dtmf,voice") + ATTRIB(terminator,, any) + ATTRIB(recognizer,, any) + ATTRIB(language, en-US, any) + ATTRIB(initial-timeout, -1, positive_or_neg_one) + ATTRIB(inter-digit-timeout, -1, positive_or_neg_one) + ATTRIB(sensitivity, 0.5, decimal_between_zero_and_one) + ATTRIB(min-confidence, 0, decimal_between_zero_and_one) + ATTRIB(max-silence, -1, positive_or_neg_one) + /* for now, only NLSML */ + STRING_ATTRIB(match-content-type, application/nlsml+xml, "application/nlsml+xml") + /* internal attribs for prompt support */ + ATTRIB(barge-event, false, bool) + ATTRIB(start-timers, true, bool) +ELEMENT_END + +/** + * component validation + */ +ELEMENT(RAYO_OUTPUT) + ATTRIB(start-offset, 0, not_negative) + ATTRIB(start-paused, false, bool) + ATTRIB(repeat-interval, 0, not_negative) + ATTRIB(repeat-times, 1, not_negative) + ATTRIB(max-time, -1, positive_or_neg_one) + ATTRIB(renderer,, any) + ATTRIB(voice,, any) +ELEMENT_END + +/** + * validation + */ +ELEMENT(RAYO_OUTPUT_SEEK) + STRING_ATTRIB(direction,, "forward,back") + ATTRIB(amount,-1, positive) +ELEMENT_END + +/** + * component validation + */ +ELEMENT(RAYO_PROMPT) + ATTRIB(barge-in, true, bool) +ELEMENT_END + +/** + * component validation + */ +ELEMENT(RAYO_RECORD) + ATTRIB(format, mp3, any) + ATTRIB(start-beep, false, bool) + ATTRIB(stop-beep, false, bool) + ATTRIB(start-paused, false, bool) + ATTRIB(max-duration, -1, positive_or_neg_one) + ATTRIB(initial-timeout, -1, positive_or_neg_one) + ATTRIB(final-timeout, -1, positive_or_neg_one) + STRING_ATTRIB(direction, duplex, "duplex,send,recv") + ATTRIB(mix, false, bool) +ELEMENT_END + +/** + * command validation + */ +ELEMENT(RAYO_JOIN) + /* for now, only allow duplex + STRING_ATTRIB(direction, duplex, "send,recv,duplex"); */ + STRING_ATTRIB(direction, duplex, "duplex") + STRING_ATTRIB(media, bridge, "bridge,direct") + ATTRIB(call-uri,, any) + ATTRIB(mixer-name,, any) +ELEMENT_END + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ + diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.h b/src/mod/event_handlers/mod_rayo/rayo_elements.h index be2d68d054..c780f160cf 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_elements.h +++ b/src/mod/event_handlers/mod_rayo/rayo_elements.h @@ -23,7 +23,7 @@ * Contributor(s): * Chris Rienzo * - * rayo_elements.h -- Rayo XML element definition + * rayo_elements.h -- Rayo XML elements * */ #ifndef RAYO_ELEMENTS_H @@ -31,80 +31,12 @@ #include "iks_helpers.h" -/** - * component validation - */ -ELEMENT(RAYO_INPUT) - STRING_ATTRIB(mode, any, "any,dtmf,voice") - ATTRIB(terminator,, any) - ATTRIB(recognizer,, any) - ATTRIB(language, en-US, any) - ATTRIB(initial-timeout, -1, positive_or_neg_one) - ATTRIB(inter-digit-timeout, -1, positive_or_neg_one) - ATTRIB(sensitivity, 0.5, decimal_between_zero_and_one) - ATTRIB(min-confidence, 0, decimal_between_zero_and_one) - ATTRIB(max-silence, -1, positive_or_neg_one) - /* for now, only NLSML */ - STRING_ATTRIB(match-content-type, application/nlsml+xml, "application/nlsml+xml") - /* internal attribs for prompt support */ - ATTRIB(barge-event, false, bool) - ATTRIB(start-timers, true, bool) -ELEMENT_END - -/** - * component validation - */ -ELEMENT(RAYO_OUTPUT) - ATTRIB(start-offset, 0, not_negative) - ATTRIB(start-paused, false, bool) - ATTRIB(repeat-interval, 0, not_negative) - ATTRIB(repeat-times, 1, not_negative) - ATTRIB(max-time, -1, positive_or_neg_one) - ATTRIB(renderer,, any) - ATTRIB(voice,, any) -ELEMENT_END - -/** - * validation - */ -ELEMENT(RAYO_OUTPUT_SEEK) - STRING_ATTRIB(direction,, "forward,back") - ATTRIB(amount,-1, positive) -ELEMENT_END - -/** - * component validation - */ -ELEMENT(RAYO_PROMPT) - ATTRIB(barge-in, true, bool) -ELEMENT_END - -/** - * component validation - */ -ELEMENT(RAYO_RECORD) - ATTRIB(format, mp3, any) - ATTRIB(start-beep, false, bool) - ATTRIB(stop-beep, false, bool) - ATTRIB(start-paused, false, bool) - ATTRIB(max-duration, -1, positive_or_neg_one) - ATTRIB(initial-timeout, -1, positive_or_neg_one) - ATTRIB(final-timeout, -1, positive_or_neg_one) - STRING_ATTRIB(direction, duplex, "duplex,send,recv") - ATTRIB(mix, false, bool) -ELEMENT_END - -/** - * command validation - */ -ELEMENT(RAYO_JOIN) - /* for now, only allow duplex - STRING_ATTRIB(direction, duplex, "send,recv,duplex"); */ - STRING_ATTRIB(direction, duplex, "duplex") - STRING_ATTRIB(media, bridge, "bridge,direct") - ATTRIB(call-uri,, any) - ATTRIB(mixer-name,, any) -ELEMENT_END +ELEMENT_DECL(RAYO_INPUT) +ELEMENT_DECL(RAYO_OUTPUT) +ELEMENT_DECL(RAYO_OUTPUT_SEEK) +ELEMENT_DECL(RAYO_PROMPT) +ELEMENT_DECL(RAYO_RECORD) +ELEMENT_DECL(RAYO_JOIN) #endif From 76f3cb5b468154d9657cf90535db645cc72a0908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Czu=C5=82ada?= Date: Fri, 2 Aug 2013 10:23:38 +0200 Subject: [PATCH 219/278] Describe patches related to JIRA issues --- docs/SubmittingPatches | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/SubmittingPatches b/docs/SubmittingPatches index 4be7eaefca..d65e62bbb8 100644 --- a/docs/SubmittingPatches +++ b/docs/SubmittingPatches @@ -77,6 +77,21 @@ message might look like this: > After appropriate amounts of frobbing have been done, a new variable > `frobbing_done` is set in the caller's channel. +Patches related to JIRA issues +------------------------------ + +If your patch is related to an JIRA issue, you should add its name +(i.e. FS-555) to your commit message. You can also use some JIRA +directives, like '--resolve'. For example, if your patch fixes some +error reported by case FS-555, just add the following line somewhere +in your commit message: + +> FS-555 --resolve + +or modify the subject: + +> FS-555 --resolve Add frobinator support to mod_sofia + Where to Go for Help -------------------- From 8fd514df6bcfb32c035b5632d71dc3ef3098ea3f Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 2 Aug 2013 14:37:54 +0000 Subject: [PATCH 220/278] Tweak style and content of last commit The directions here anticipate planned improvements to our JIRA bot to allow issues to be resolved by placing the JIRA directives in the body of the commit message. --- docs/SubmittingPatches | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/SubmittingPatches b/docs/SubmittingPatches index d65e62bbb8..e839ff128d 100644 --- a/docs/SubmittingPatches +++ b/docs/SubmittingPatches @@ -76,21 +76,21 @@ message might look like this: > > After appropriate amounts of frobbing have been done, a new variable > `frobbing_done` is set in the caller's channel. +> +> FS-XXXX --resolve -Patches related to JIRA issues +Patches Related to JIRA Issues ------------------------------ -If your patch is related to an JIRA issue, you should add its name -(i.e. FS-555) to your commit message. You can also use some JIRA -directives, like '--resolve'. For example, if your patch fixes some -error reported by case FS-555, just add the following line somewhere -in your commit message: +When your patch is related to an issue logged in JIRA, add the +identifier for the issue (e.g. FS-XXXX) to the body of your commit +message at the beginning of a line, typically the last line or just +before "Signed-off-by:" and "Thanks-to:" lines. This helps our JIRA +bot do useful things by relating the commit to the issue. -> FS-555 --resolve - -or modify the subject: - -> FS-555 --resolve Add frobinator support to mod_sofia +If you believe your patch resolves the issue in question, follow the +issue number with a space and the "--resolve" directive as in the +example above. Where to Go for Help -------------------- From f184953570bc4d3013ce2b45997f81ae01768b94 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 10:54:46 -0500 Subject: [PATCH 221/278] mod_skinny: add minimal handling of MeetMe button to send to a conference creation extension --- .../mod_skinny/conf/skinny_profiles/internal.xml | 1 + src/mod/endpoints/mod_skinny/mod_skinny.c | 9 +++++++++ src/mod/endpoints/mod_skinny/mod_skinny.h | 1 + src/mod/endpoints/mod_skinny/skinny_api.c | 1 + src/mod/endpoints/mod_skinny/skinny_server.c | 6 +++++- src/mod/endpoints/mod_skinny/skinny_tables.c | 2 +- src/mod/endpoints/mod_skinny/skinny_tables.h | 2 +- 7 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/conf/skinny_profiles/internal.xml b/src/mod/endpoints/mod_skinny/conf/skinny_profiles/internal.xml index 39ce60fe4a..74cb86701b 100644 --- a/src/mod/endpoints/mod_skinny/conf/skinny_profiles/internal.xml +++ b/src/mod/endpoints/mod_skinny/conf/skinny_profiles/internal.xml @@ -14,6 +14,7 @@ + diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index dc83c47294..18b0f3c3f5 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -171,6 +171,7 @@ switch_status_t skinny_profile_dump(const skinny_profile_t *profile, switch_stre stream->write_function(stream, "Listener-Threads \t%d\n", profile->listener_threads); stream->write_function(stream, "Ext-Voicemail \t%s\n", profile->ext_voicemail); stream->write_function(stream, "Ext-Redial \t%s\n", profile->ext_redial); + stream->write_function(stream, "Ext-MeetMe \t%s\n", profile->ext_meetme); stream->write_function(stream, "%s\n", line); return SWITCH_STATUS_SUCCESS; @@ -1864,6 +1865,10 @@ switch_status_t skinny_profile_set(skinny_profile_t *profile, const char *var, c if (!profile->ext_redial || strcmp(val, profile->ext_redial)) { profile->ext_redial = switch_core_strdup(profile->pool, val); } + } else if (!strcasecmp(var, "ext-meetme")) { + if (!profile->ext_meetme || strcmp(val, profile->ext_meetme)) { + profile->ext_meetme = switch_core_strdup(profile->pool, val); + } } else { return SWITCH_STATUS_FALSE; } @@ -1956,6 +1961,10 @@ static switch_status_t load_skinny_config(void) skinny_profile_set(profile, "ext-redial", "redial"); } + if (!profile->ext_meetme) { + skinny_profile_set(profile, "ext-meetme", "conference"); + } + if (profile->port == 0) { profile->port = 2000; } diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index d8ed1b03bf..39faed4388 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -117,6 +117,7 @@ struct skinny_profile { /* extensions */ char *ext_voicemail; char *ext_redial; + char *ext_meetme; /* db */ char *dbname; char *odbc_dsn; diff --git a/src/mod/endpoints/mod_skinny/skinny_api.c b/src/mod/endpoints/mod_skinny/skinny_api.c index 604b8509f7..94318c8de0 100644 --- a/src/mod/endpoints/mod_skinny/skinny_api.c +++ b/src/mod/endpoints/mod_skinny/skinny_api.c @@ -233,6 +233,7 @@ static switch_status_t skinny_api_list_settings(const char *line, const char *cu switch_console_push_match(&my_matches, "auto-restart"); switch_console_push_match(&my_matches, "ext-voicemail"); switch_console_push_match(&my_matches, "ext-redial"); + switch_console_push_match(&my_matches, "ext-meetme"); if (my_matches) { *matches = my_matches; diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 95d486d3e0..f2d8eb8abe 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -76,7 +76,7 @@ uint32_t soft_key_template_default_events[] = { SOFTKEY_CONF, SOFTKEY_PARK, SOFTKEY_JOIN, - SOFTKEY_MEETMECONF, + SOFTKEY_MEETME, SOFTKEY_CALLPICKUP, SOFTKEY_GRPCALLPICKUP, SOFTKEY_DND, @@ -1929,6 +1929,10 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn } } break; + case SOFTKEY_MEETME: + skinny_create_incoming_session(listener, &line_instance, &session); + skinny_session_process_dest(session, listener, line_instance, listener->profile->ext_meetme, '\0', 0); + break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown SoftKeyEvent type: %d.\n", request->data.soft_key_event.event); diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index 9f04f8cd63..ea29918cbb 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -239,7 +239,7 @@ SKINNY_DECLARE_STR2ID(skinny_str2button, SKINNY_BUTTONS, -1) {SOFTKEY_CONF, "SoftkeyConf"}, {SOFTKEY_PARK, "SoftkeyPark"}, {SOFTKEY_JOIN, "SoftkeyJoin"}, - {SOFTKEY_MEETMECONF, "SoftkeyMeetmeconfrm"}, + {SOFTKEY_MEETME, "SoftkeyMeetme"}, {SOFTKEY_CALLPICKUP, "SoftkeyCallpickup"}, {SOFTKEY_GRPCALLPICKUP, "SoftkeyGrpcallpickup"}, {SOFTKEY_DND, "SoftkeyDnd"}, diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.h b/src/mod/endpoints/mod_skinny/skinny_tables.h index 1e35a7d208..647837f212 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.h +++ b/src/mod/endpoints/mod_skinny/skinny_tables.h @@ -192,7 +192,7 @@ enum skinny_soft_key_event { SOFTKEY_CONF = 0x0D, SOFTKEY_PARK = 0x0E, SOFTKEY_JOIN = 0x0F, - SOFTKEY_MEETMECONF = 0x10, + SOFTKEY_MEETME = 0x10, SOFTKEY_CALLPICKUP = 0x11, SOFTKEY_GRPCALLPICKUP = 0x12, SOFTKEY_DND = 0x13, From d1268e81036ce8ce00de8ee22f387cdbf43a7203 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 11:21:40 -0500 Subject: [PATCH 222/278] make default conf example work properly based on FS-5335 feedback/testing --- src/mod/endpoints/mod_skinny/conf/dialplan/skinny-patterns.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/endpoints/mod_skinny/conf/dialplan/skinny-patterns.xml b/src/mod/endpoints/mod_skinny/conf/dialplan/skinny-patterns.xml index b37d8eed82..69419f36c2 100644 --- a/src/mod/endpoints/mod_skinny/conf/dialplan/skinny-patterns.xml +++ b/src/mod/endpoints/mod_skinny/conf/dialplan/skinny-patterns.xml @@ -15,7 +15,7 @@ - + From c2d5d47078b9a2b0698469a06d2058147402890e Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 13:53:29 -0500 Subject: [PATCH 223/278] mod_skinny: implement per device setting of ext- extensions --- .../conf/directory/default/skinny-example.xml | 7 ++++-- src/mod/endpoints/mod_skinny/mod_skinny.h | 9 +++++++ src/mod/endpoints/mod_skinny/skinny_server.c | 24 +++++++++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml b/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml index 357eb72f12..8b8fad5812 100644 --- a/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml +++ b/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml @@ -2,8 +2,11 @@ diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index 39faed4388..7c9dcdb314 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -35,6 +35,12 @@ #include +/*****************************************************************************/ +/* UTILITY MACROS */ +/*****************************************************************************/ +#define empty_null(a) ((a)?(a):NULL) +#define empty_null2(a,b) ((a)?(a):empty_null(b)) + /*****************************************************************************/ /* LOGGING FUNCTIONS */ /*****************************************************************************/ @@ -183,6 +189,9 @@ struct listener { uint32_t flags; time_t expire_time; struct listener *next; + char *ext_voicemail; + char *ext_redial; + char *ext_meetme; }; typedef struct listener listener_t; diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index f2d8eb8abe..d648cac7e5 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -1034,6 +1034,18 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r strncpy(listener->firmware_version, value, 16); } else if (!strcasecmp(name, "skinny-soft-key-set-set")) { listener->soft_key_set_set = switch_core_strdup(profile->pool, value); + } else if (!strcasecmp(name, "ext-voicemail")) { + if (!listener->ext_voicemail || strcmp(value,listener->ext_voicemail)) { + listener->ext_voicemail = switch_core_strdup(profile->pool, value); + } + } else if (!strcasecmp(name, "ext-redial")) { + if (!listener->ext_redial || strcmp(value,listener->ext_redial)) { + listener->ext_redial = switch_core_strdup(profile->pool, value); + } + } else if (!strcasecmp(name, "ext-meetme")) { + if (!listener->ext_meetme || strcmp(value,listener->ext_meetme)) { + listener->ext_meetme = switch_core_strdup(profile->pool, value); + } } } } @@ -1267,7 +1279,8 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess switch(request->data.stimulus.instance_type) { case SKINNY_BUTTON_LAST_NUMBER_REDIAL: skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, listener->profile->ext_redial, '\0', 0); + skinny_session_process_dest(session, listener, line_instance, + empty_null2(listener->ext_redial,listener->profile->ext_redial), '\0', 0); break; case SKINNY_BUTTON_SPEED_DIAL: skinny_speed_dial_get(listener, request->data.stimulus.instance, &button_speed_dial); @@ -1296,7 +1309,8 @@ switch_status_t skinny_handle_stimulus_message(listener_t *listener, skinny_mess break; case SKINNY_BUTTON_VOICEMAIL: skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, listener->profile->ext_voicemail, '\0', 0); + skinny_session_process_dest(session, listener, line_instance, + empty_null2(listener->ext_voicemail, listener->profile->ext_voicemail), '\0', 0); break; case SKINNY_BUTTON_LINE: @@ -1871,7 +1885,8 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn switch(request->data.soft_key_event.event) { case SOFTKEY_REDIAL: status = skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, listener->profile->ext_redial, '\0', 0); + skinny_session_process_dest(session, listener, line_instance, + empty_null2(listener->ext_redial,listener->profile->ext_redial), '\0', 0); break; case SOFTKEY_NEWCALL: status = skinny_create_incoming_session(listener, &line_instance, &session); @@ -1931,7 +1946,8 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn break; case SOFTKEY_MEETME: skinny_create_incoming_session(listener, &line_instance, &session); - skinny_session_process_dest(session, listener, line_instance, listener->profile->ext_meetme, '\0', 0); + skinny_session_process_dest(session, listener, line_instance, + empty_null2(listener->ext_meetme, listener->profile->ext_meetme), '\0', 0); break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, From 7dbf80ed8b20be01f26a91f19f0eec79bba6bba2 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 14:12:03 -0500 Subject: [PATCH 224/278] FS-5662 --resolve implement call pickup group functionality in mod_skinny. Currently treats callPickup and grpCallPickup identically, may revisit when I find out more about what's different between the two --- .../conf/directory/default/skinny-example.xml | 8 ++++++++ src/mod/endpoints/mod_skinny/mod_skinny.c | 9 +++++++++ src/mod/endpoints/mod_skinny/mod_skinny.h | 2 ++ src/mod/endpoints/mod_skinny/skinny_api.c | 1 + src/mod/endpoints/mod_skinny/skinny_server.c | 10 ++++++++++ 5 files changed, 30 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml b/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml index 8b8fad5812..c51b5ce9cf 100644 --- a/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml +++ b/src/mod/endpoints/mod_skinny/conf/directory/default/skinny-example.xml @@ -8,6 +8,14 @@ --> + + + + diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index 18b0f3c3f5..ab4398a898 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -172,6 +172,7 @@ switch_status_t skinny_profile_dump(const skinny_profile_t *profile, switch_stre stream->write_function(stream, "Ext-Voicemail \t%s\n", profile->ext_voicemail); stream->write_function(stream, "Ext-Redial \t%s\n", profile->ext_redial); stream->write_function(stream, "Ext-MeetMe \t%s\n", profile->ext_meetme); + stream->write_function(stream, "Ext-PickUp \t%s\n", profile->ext_pickup); stream->write_function(stream, "%s\n", line); return SWITCH_STATUS_SUCCESS; @@ -1869,6 +1870,10 @@ switch_status_t skinny_profile_set(skinny_profile_t *profile, const char *var, c if (!profile->ext_meetme || strcmp(val, profile->ext_meetme)) { profile->ext_meetme = switch_core_strdup(profile->pool, val); } + } else if (!strcasecmp(var, "ext-pickup")) { + if (!profile->ext_pickup || strcmp(val, profile->ext_pickup)) { + profile->ext_pickup = switch_core_strdup(profile->pool, val); + } } else { return SWITCH_STATUS_FALSE; } @@ -1965,6 +1970,10 @@ static switch_status_t load_skinny_config(void) skinny_profile_set(profile, "ext-meetme", "conference"); } + if (!profile->ext_pickup) { + skinny_profile_set(profile, "ext-pickup", "pickup"); + } + if (profile->port == 0) { profile->port = 2000; } diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.h b/src/mod/endpoints/mod_skinny/mod_skinny.h index 7c9dcdb314..df876d0894 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.h +++ b/src/mod/endpoints/mod_skinny/mod_skinny.h @@ -124,6 +124,7 @@ struct skinny_profile { char *ext_voicemail; char *ext_redial; char *ext_meetme; + char *ext_pickup; /* db */ char *dbname; char *odbc_dsn; @@ -192,6 +193,7 @@ struct listener { char *ext_voicemail; char *ext_redial; char *ext_meetme; + char *ext_pickup; }; typedef struct listener listener_t; diff --git a/src/mod/endpoints/mod_skinny/skinny_api.c b/src/mod/endpoints/mod_skinny/skinny_api.c index 94318c8de0..db9338b000 100644 --- a/src/mod/endpoints/mod_skinny/skinny_api.c +++ b/src/mod/endpoints/mod_skinny/skinny_api.c @@ -234,6 +234,7 @@ static switch_status_t skinny_api_list_settings(const char *line, const char *cu switch_console_push_match(&my_matches, "ext-voicemail"); switch_console_push_match(&my_matches, "ext-redial"); switch_console_push_match(&my_matches, "ext-meetme"); + switch_console_push_match(&my_matches, "ext-pickup"); if (my_matches) { *matches = my_matches; diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index d648cac7e5..b30a0004d6 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -1046,6 +1046,10 @@ switch_status_t skinny_handle_register(listener_t *listener, skinny_message_t *r if (!listener->ext_meetme || strcmp(value,listener->ext_meetme)) { listener->ext_meetme = switch_core_strdup(profile->pool, value); } + } else if (!strcasecmp(name, "ext-pickup")) { + if (!listener->ext_pickup || strcmp(value,listener->ext_pickup)) { + listener->ext_pickup = switch_core_strdup(profile->pool, value); + } } } } @@ -1949,6 +1953,12 @@ switch_status_t skinny_handle_soft_key_event_message(listener_t *listener, skinn skinny_session_process_dest(session, listener, line_instance, empty_null2(listener->ext_meetme, listener->profile->ext_meetme), '\0', 0); break; + case SOFTKEY_CALLPICKUP: + case SOFTKEY_GRPCALLPICKUP: + skinny_create_incoming_session(listener, &line_instance, &session); + skinny_session_process_dest(session, listener, line_instance, + empty_null2(listener->ext_pickup, listener->profile->ext_pickup), '\0', 0); + break; default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown SoftKeyEvent type: %d.\n", request->data.soft_key_event.event); From 118614e44772569daf7d70f12b9c273cb1ec169e Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 21:11:56 -0500 Subject: [PATCH 225/278] mod_skinny work on FS-5632 - first pass --- .../endpoints/mod_skinny/skinny_protocol.c | 260 ++++++++++-------- .../endpoints/mod_skinny/skinny_protocol.h | 19 +- src/mod/endpoints/mod_skinny/skinny_server.c | 88 +++--- 3 files changed, 198 insertions(+), 169 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index 677c703b39..2de3b87e27 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -463,16 +463,15 @@ switch_status_t perform_send_keep_alive_ack(listener_t *listener, const char *file, const char *func, int line) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12); - message->type = KEEP_ALIVE_ACK_MESSAGE; - message->length = 4; + + skinny_create_empty_message(message, KEEP_ALIVE_ACK_MESSAGE); if ( listener->profile->debug >= 10 ) { skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Sending Keep Alive Ack%s\n", ""); } - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_register_ack(listener_t *listener, @@ -484,9 +483,9 @@ switch_status_t perform_send_register_ack(listener_t *listener, char *reserved2) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.reg_ack)); - message->type = REGISTER_ACK_MESSAGE; - message->length = 4 + sizeof(message->data.reg_ack); + + skinny_create_message(message, REGISTER_ACK_MESSAGE, reg_ack); + message->data.reg_ack.keep_alive = keep_alive; strncpy(message->data.reg_ack.date_format, date_format, 6); strncpy(message->data.reg_ack.reserved, reserved, 2); @@ -497,7 +496,7 @@ switch_status_t perform_send_register_ack(listener_t *listener, "Sending Register Ack with Keep Alive (%d), Date Format (%s), Secondary Keep Alive (%d)\n", keep_alive, date_format, secondary_keep_alive); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_speed_dial_stat_res(listener_t *listener, @@ -507,9 +506,8 @@ switch_status_t perform_send_speed_dial_stat_res(listener_t *listener, char *speed_label) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.speed_dial_res)); - message->type = SPEED_DIAL_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.speed_dial_res); + + skinny_create_message(message, SPEED_DIAL_STAT_RES_MESSAGE, speed_dial_res); message->data.speed_dial_res.number = number; strncpy(message->data.speed_dial_res.line, speed_line, 24); @@ -519,7 +517,7 @@ switch_status_t perform_send_speed_dial_stat_res(listener_t *listener, "Sending Speed Dial Stat Res with Number (%d), Line (%s), Label (%s)\n", number, speed_line, speed_label); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_start_tone(listener_t *listener, @@ -530,9 +528,9 @@ switch_status_t perform_send_start_tone(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.start_tone)); - message->type = START_TONE_MESSAGE; - message->length = 4 + sizeof(message->data.start_tone); + + skinny_create_message(message, START_TONE_MESSAGE, start_tone); + message->data.start_tone.tone = tone; message->data.start_tone.reserved = reserved; message->data.start_tone.line_instance = line_instance; @@ -542,7 +540,7 @@ switch_status_t perform_send_start_tone(listener_t *listener, "Sending Start Tone with Tone (%s), Line Instance (%d), Call ID (%d)\n", skinny_tone2str(tone), line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_stop_tone(listener_t *listener, @@ -551,16 +549,16 @@ switch_status_t perform_send_stop_tone(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.stop_tone)); - message->type = STOP_TONE_MESSAGE; - message->length = 4 + sizeof(message->data.stop_tone); + + skinny_create_message(message, STOP_TONE_MESSAGE, stop_tone); + message->data.stop_tone.line_instance = line_instance; message->data.stop_tone.call_id = call_id; skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Sending Stop Tone with Line Instance (%d), Call ID (%d)\n", line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_set_ringer(listener_t *listener, @@ -571,9 +569,9 @@ switch_status_t perform_send_set_ringer(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.ringer)); - message->type = SET_RINGER_MESSAGE; - message->length = 4 + sizeof(message->data.ringer); + + skinny_create_message(message, SET_RINGER_MESSAGE, ringer); + message->data.ringer.ring_type = ring_type; message->data.ringer.ring_mode = ring_mode; message->data.ringer.line_instance = line_instance; @@ -583,7 +581,7 @@ switch_status_t perform_send_set_ringer(listener_t *listener, "Sending SetRinger with Ring Type (%s), Mode (%s), Line Instance (%d), Call ID (%d)\n", skinny_ring_type2str(ring_type), skinny_ring_mode2str(ring_mode), line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_set_lamp(listener_t *listener, @@ -593,9 +591,9 @@ switch_status_t perform_send_set_lamp(listener_t *listener, uint32_t mode) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.lamp)); - message->type = SET_LAMP_MESSAGE; - message->length = 4 + sizeof(message->data.lamp); + + skinny_create_message(message, SET_LAMP_MESSAGE, lamp); + message->data.lamp.stimulus = stimulus; message->data.lamp.stimulus_instance = stimulus_instance; message->data.lamp.mode = mode; @@ -604,7 +602,7 @@ switch_status_t perform_send_set_lamp(listener_t *listener, "Sending Set Lamp with Stimulus (%s), Stimulus Instance (%d), Mode (%s)\n", skinny_button2str(stimulus), stimulus_instance, skinny_lamp_mode2str(mode)); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_set_speaker_mode(listener_t *listener, @@ -612,15 +610,15 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, uint32_t mode) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.speaker_mode)); - message->type = SET_SPEAKER_MODE_MESSAGE; - message->length = 4 + sizeof(message->data.speaker_mode); + + skinny_create_message(message, SET_SPEAKER_MODE_MESSAGE, speaker_mode); + message->data.speaker_mode.mode = mode; skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Sending Set Speaker Mode with Mode (%s)\n", skinny_speaker_mode2str(mode)); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_srvreq_response(listener_t *listener, @@ -629,9 +627,7 @@ switch_status_t perform_send_srvreq_response(listener_t *listener, { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.serv_res_mess)); - message->type = SERVER_RESPONSE_MESSAGE; - message->length = 4 + sizeof(message->data.serv_res_mess); + skinny_create_message(message, SERVER_RESPONSE_MESSAGE, serv_res_mess); message->data.serv_res_mess.serverListenPort[0] = port; switch_inet_pton(AF_INET,ip, &message->data.serv_res_mess.serverIpAddr[0]); @@ -640,7 +636,7 @@ switch_status_t perform_send_srvreq_response(listener_t *listener, skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Sending Server Request Response with IP (%s) and Port (%d)\n", ip, port); - return skinny_send_reply(listener, message); + return skinny_send_reply(listener, message, SWITCH_TRUE); } switch_status_t perform_send_start_media_transmission(listener_t *listener, @@ -657,9 +653,9 @@ switch_status_t perform_send_start_media_transmission(listener_t *listener, uint32_t g723_bitrate) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.start_media)); - message->type = START_MEDIA_TRANSMISSION_MESSAGE; - message->length = 4 + sizeof(message->data.start_media); + + skinny_create_message(message, START_MEDIA_TRANSMISSION_MESSAGE, start_media); + message->data.start_media.conference_id = conference_id; message->data.start_media.pass_thru_party_id = pass_thru_party_id; message->data.start_media.remote_ip = remote_ip; @@ -676,7 +672,7 @@ switch_status_t perform_send_start_media_transmission(listener_t *listener, "Send Start Media Transmission with Conf ID(%d), Passthrough Party ID (%d), ...\n", conference_id, pass_thru_party_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_stop_media_transmission(listener_t *listener, @@ -686,9 +682,9 @@ switch_status_t perform_send_stop_media_transmission(listener_t *listener, uint32_t conference_id2) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.stop_media)); - message->type = STOP_MEDIA_TRANSMISSION_MESSAGE; - message->length = 4 + sizeof(message->data.stop_media); + + skinny_create_message(message, STOP_MEDIA_TRANSMISSION_MESSAGE, stop_media); + message->data.stop_media.conference_id = conference_id; message->data.stop_media.pass_thru_party_id = pass_thru_party_id; message->data.stop_media.conference_id2 = conference_id2; @@ -698,7 +694,7 @@ switch_status_t perform_send_stop_media_transmission(listener_t *listener, "Send Stop Media Transmission with Conf ID (%d), Passthrough Party ID (%d), Conf ID2 (%d)\n", conference_id, pass_thru_party_id, conference_id2); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_call_info(listener_t *listener, @@ -725,9 +721,9 @@ switch_status_t perform_send_call_info(listener_t *listener, uint32_t party_pi_restriction_bits) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.call_info)); - message->type = CALL_INFO_MESSAGE; - message->length = 4 + sizeof(message->data.call_info); + + skinny_create_message(message, CALL_INFO_MESSAGE, call_info); + strncpy(message->data.call_info.calling_party_name, calling_party_name, 40); strncpy(message->data.call_info.calling_party, calling_party, 24); strncpy(message->data.call_info.called_party_name, called_party_name, 40); @@ -752,7 +748,7 @@ switch_status_t perform_send_call_info(listener_t *listener, skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Call Info with Line Instance (%d)...\n", line_instance); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_define_time_date(listener_t *listener, @@ -768,9 +764,9 @@ switch_status_t perform_send_define_time_date(listener_t *listener, uint32_t timestamp) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.define_time_date)); - message->type = DEFINE_TIME_DATE_MESSAGE; - message->length = 4+sizeof(message->data.define_time_date); + + skinny_create_message(message, DEFINE_TIME_DATE_MESSAGE, define_time_date); + message->data.define_time_date.year = year; message->data.define_time_date.month = month; message->data.define_time_date.day_of_week = day_of_week; @@ -785,7 +781,7 @@ switch_status_t perform_send_define_time_date(listener_t *listener, "Send Define Time Date with %.4d-%.2d-%.2d %.2d:%.2d:%.2d.%d, Timestamp (%d), DOW (%d)\n", year, month, day, hour, minute, seconds, milliseconds, timestamp, day_of_week); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_define_current_time_date(listener_t *listener, @@ -812,14 +808,13 @@ switch_status_t perform_send_capabilities_req(listener_t *listener, const char *file, const char *func, int line) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12); - message->type = CAPABILITIES_REQ_MESSAGE; - message->length = 4; + + skinny_create_empty_message(message, CAPABILITIES_REQ_MESSAGE); skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Capabilities Req%s\n", ""); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_version(listener_t *listener, @@ -827,15 +822,15 @@ switch_status_t perform_send_version(listener_t *listener, char *version) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.version)); - message->type = VERSION_MESSAGE; - message->length = 4+ sizeof(message->data.version); + + skinny_create_message(message, VERSION_MESSAGE, version); + strncpy(message->data.version.version, version, 16); skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Version with Version(%s)\n", version); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_register_reject(listener_t *listener, @@ -843,15 +838,15 @@ switch_status_t perform_send_register_reject(listener_t *listener, char *error) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.reg_rej)); - message->type = REGISTER_REJECT_MESSAGE; - message->length = 4 + sizeof(message->data.reg_rej); + + skinny_create_message(message, REGISTER_REJECT_MESSAGE, reg_rej); + strncpy(message->data.reg_rej.error, error, 33); skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Register Reject with Error (%s)\n", error); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_open_receive_channel(listener_t *listener, @@ -866,9 +861,9 @@ switch_status_t perform_send_open_receive_channel(listener_t *listener, uint32_t reserved[10]) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.open_receive_channel)); - message->type = OPEN_RECEIVE_CHANNEL_MESSAGE; - message->length = 4 + sizeof(message->data.open_receive_channel); + + skinny_create_message(message, OPEN_RECEIVE_CHANNEL_MESSAGE, open_receive_channel); + message->data.open_receive_channel.conference_id = conference_id; message->data.open_receive_channel.pass_thru_party_id = pass_thru_party_id; message->data.open_receive_channel.ms_per_packet = ms_per_packet; @@ -892,7 +887,7 @@ switch_status_t perform_send_open_receive_channel(listener_t *listener, skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Open Receive Channel with Conf ID (%d), ...\n", conference_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_close_receive_channel(listener_t *listener, @@ -902,9 +897,9 @@ switch_status_t perform_send_close_receive_channel(listener_t *listener, uint32_t conference_id2) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.close_receive_channel)); - message->type = CLOSE_RECEIVE_CHANNEL_MESSAGE; - message->length = 4 + sizeof(message->data.close_receive_channel); + + skinny_create_message(message, CLOSE_RECEIVE_CHANNEL_MESSAGE, close_receive_channel); + message->data.close_receive_channel.conference_id = conference_id; message->data.close_receive_channel.pass_thru_party_id = pass_thru_party_id; message->data.close_receive_channel.conference_id2 = conference_id2; @@ -912,7 +907,7 @@ switch_status_t perform_send_close_receive_channel(listener_t *listener, skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Close Receive Channel with Conf ID (%d), ...\n", conference_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_select_soft_keys(listener_t *listener, @@ -923,9 +918,9 @@ switch_status_t perform_send_select_soft_keys(listener_t *listener, uint32_t valid_key_mask) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.select_soft_keys)); - message->type = SELECT_SOFT_KEYS_MESSAGE; - message->length = 4 + sizeof(message->data.select_soft_keys); + + skinny_create_message(message, SELECT_SOFT_KEYS_MESSAGE, select_soft_keys); + message->data.select_soft_keys.line_instance = line_instance; message->data.select_soft_keys.call_id = call_id; message->data.select_soft_keys.soft_key_set = soft_key_set; @@ -935,7 +930,7 @@ switch_status_t perform_send_select_soft_keys(listener_t *listener, "Send Select Soft Keys with Line Instance (%d), Call ID (%d), Soft Key Set (%d), Valid Key Mask (%x)\n", line_instance, call_id, soft_key_set, valid_key_mask); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_call_state(listener_t *listener, @@ -945,9 +940,9 @@ switch_status_t perform_send_call_state(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.call_state)); - message->type = CALL_STATE_MESSAGE; - message->length = 4 + sizeof(message->data.call_state); + + skinny_create_message(message, CALL_STATE_MESSAGE, call_state); + message->data.call_state.call_state = call_state; message->data.call_state.line_instance = line_instance; message->data.call_state.call_id = call_id; @@ -956,7 +951,7 @@ switch_status_t perform_send_call_state(listener_t *listener, "Send Call State with State (%s), Line Instance (%d), Call ID (%d)\n", skinny_call_state2str(call_state), line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_display_prompt_status(listener_t *listener, @@ -969,9 +964,8 @@ switch_status_t perform_send_display_prompt_status(listener_t *listener, skinny_message_t *message; char *tmp; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.display_prompt_status)); - message->type = DISPLAY_PROMPT_STATUS_MESSAGE; - message->length = 4 + sizeof(message->data.display_prompt_status); + skinny_create_message(message, DISPLAY_PROMPT_STATUS_MESSAGE, display_prompt_status); + message->data.display_prompt_status.timeout = timeout; strncpy(message->data.display_prompt_status.display, display, 32); message->data.display_prompt_status.line_instance = line_instance; @@ -985,7 +979,7 @@ switch_status_t perform_send_display_prompt_status(listener_t *listener, switch_safe_free(tmp); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_display_prompt_status_textid(listener_t *listener, @@ -997,9 +991,9 @@ switch_status_t perform_send_display_prompt_status_textid(listener_t *listener, { skinny_message_t *message; char *label; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.display_prompt_status)); - message->type = DISPLAY_PROMPT_STATUS_MESSAGE; - message->length = 4 + sizeof(message->data.display_prompt_status); + + skinny_create_message(message, DISPLAY_PROMPT_STATUS_MESSAGE, display_prompt_status); + message->data.display_prompt_status.timeout = timeout; label = skinny_textid2raw(display_textid); @@ -1013,7 +1007,7 @@ switch_status_t perform_send_display_prompt_status_textid(listener_t *listener, "Send Display Prompt Status with Timeout (%d), Display (%s), Line Instance (%d), Call ID (%d)\n", timeout, skinny_textid2str(display_textid), line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_clear_prompt_status(listener_t *listener, @@ -1022,9 +1016,9 @@ switch_status_t perform_send_clear_prompt_status(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.clear_prompt_status)); - message->type = CLEAR_PROMPT_STATUS_MESSAGE; - message->length = 4 + sizeof(message->data.clear_prompt_status); + + skinny_create_message(message, CLEAR_PROMPT_STATUS_MESSAGE, clear_prompt_status); + message->data.clear_prompt_status.line_instance = line_instance; message->data.clear_prompt_status.call_id = call_id; @@ -1032,7 +1026,7 @@ switch_status_t perform_send_clear_prompt_status(listener_t *listener, "Send Clear Prompt Status with Line Instance (%d), Call ID (%d)\n", line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_activate_call_plane(listener_t *listener, @@ -1040,15 +1034,15 @@ switch_status_t perform_send_activate_call_plane(listener_t *listener, uint32_t line_instance) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.activate_call_plane)); - message->type = ACTIVATE_CALL_PLANE_MESSAGE; - message->length = 4 + sizeof(message->data.activate_call_plane); + + skinny_create_message(message, ACTIVATE_CALL_PLANE_MESSAGE, activate_call_plane); + message->data.activate_call_plane.line_instance = line_instance; skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Activate Call Plane with Line Instance (%d)\n", line_instance); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_back_space_request(listener_t *listener, @@ -1057,9 +1051,9 @@ switch_status_t perform_send_back_space_request(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.back_space_req)); - message->type = BACK_SPACE_REQ_MESSAGE; - message->length = 4 + sizeof(message->data.back_space_req); + + skinny_create_message(message, BACK_SPACE_REQ_MESSAGE, back_space_req); + message->data.back_space_req.line_instance = line_instance; message->data.back_space_req.call_id = call_id; @@ -1067,7 +1061,7 @@ switch_status_t perform_send_back_space_request(listener_t *listener, "Send Back Space Request with Line Instance (%d), Call ID (%d)\n", line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } @@ -1078,9 +1072,9 @@ switch_status_t perform_send_dialed_number(listener_t *listener, uint32_t call_id) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.dialed_number)); - message->type = DIALED_NUMBER_MESSAGE; - message->length = 4 + sizeof(message->data.dialed_number); + + skinny_create_message(message, DIALED_NUMBER_MESSAGE, dialed_number); + strncpy(message->data.dialed_number.called_party, called_party, 24); message->data.dialed_number.line_instance = line_instance; message->data.dialed_number.call_id = call_id; @@ -1089,7 +1083,7 @@ switch_status_t perform_send_dialed_number(listener_t *listener, "Send Dialed Number with Number (%s), Line Instance (%d), Call ID (%d)\n", called_party, line_instance, call_id); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_display_pri_notify(listener_t *listener, @@ -1099,9 +1093,9 @@ switch_status_t perform_send_display_pri_notify(listener_t *listener, char *notify) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.display_pri_notify)); - message->type = DISPLAY_PRI_NOTIFY_MESSAGE; - message->length = 4 + sizeof(message->data.display_pri_notify); + + skinny_create_message(message, DISPLAY_PRI_NOTIFY_MESSAGE, display_pri_notify); + message->data.display_pri_notify.message_timeout = message_timeout; message->data.display_pri_notify.priority = priority; strncpy(message->data.display_pri_notify.notify, notify, 32); @@ -1110,7 +1104,7 @@ switch_status_t perform_send_display_pri_notify(listener_t *listener, "Send Display Pri Notify with Timeout (%d), Priority (%d), Message (%s)\n", message_timeout, priority, notify); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } @@ -1119,15 +1113,15 @@ switch_status_t perform_send_reset(listener_t *listener, uint32_t reset_type) { skinny_message_t *message; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.reset)); - message->type = RESET_MESSAGE; - message->length = 4 + sizeof(message->data.reset); + + skinny_create_message(message, RESET_MESSAGE, reset); + message->data.reset.reset_type = reset_type; skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Reset with Type (%s)\n", skinny_device_reset_type2str(reset_type)); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_data(listener_t *listener, @@ -1142,13 +1136,17 @@ switch_status_t perform_send_data(listener_t *listener, { skinny_message_t *message; switch_assert(data_length == strlen(data)); + /* data_length should be a multiple of 4 */ if ((data_length % 4) != 0) { data_length = (data_length / 4 + 1) * 4; } - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.data)+data_length-1); + + /* This one needs explicit allocation */ + message = calloc(12+sizeof(message->data.data)+data_length-1, 1); message->type = message_type; message->length = 4 + sizeof(message->data.data)+data_length-1; + message->data.data.application_id = application_id; message->data.data.line_instance = line_instance; message->data.data.call_id = call_id; @@ -1159,7 +1157,7 @@ switch_status_t perform_send_data(listener_t *listener, skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, "Send Data with Data Length (%d)\n", data_length); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } switch_status_t perform_send_extended_data(listener_t *listener, @@ -1179,13 +1177,17 @@ switch_status_t perform_send_extended_data(listener_t *listener, { skinny_message_t *message; switch_assert(data_length == strlen(data)); + /* data_length should be a multiple of 4 */ if ((data_length % 4) != 0) { data_length = (data_length / 4 + 1) * 4; } - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.extended_data)+data_length-1); + + /* This one needs explicit allocation */ + message = calloc(12+sizeof(message->data.extended_data)+data_length-1, 1); message->type = message_type; message->length = 4 + sizeof(message->data.extended_data)+data_length-1; + message->data.extended_data.application_id = application_id; message->data.extended_data.line_instance = line_instance; message->data.extended_data.call_id = call_id; @@ -1202,32 +1204,45 @@ switch_status_t perform_send_extended_data(listener_t *listener, "Send Extended Data with Application ID (%d), Line Instance (%d), Call ID (%d), ...\n", application_id, line_instance, call_id ); - return skinny_send_reply_quiet(listener, message); + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); } -switch_status_t skinny_perform_send_reply_quiet(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply) +switch_status_t skinny_perform_send_reply_quiet(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply, + switch_bool_t discard) { char *ptr; switch_size_t len; + switch_status_t res; + switch_assert(reply != NULL); + len = reply->length+8; ptr = (char *) reply; if (listener_is_ready(listener)) { - return switch_socket_send(listener->sock, ptr, &len); + res = switch_socket_send(listener->sock, ptr, &len); + + if ( discard ) { switch_safe_free(reply); } + return res; } else { skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_WARNING, "Not sending %s (type=%x,length=%d) while not ready.\n", skinny_message_type2str(reply->type), reply->type, reply->length); + + if ( discard ) { switch_safe_free(reply); } return SWITCH_STATUS_FALSE; } } -switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply) +switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply, + switch_bool_t discard) { char *ptr; switch_size_t len; + switch_status_t res; + switch_assert(reply != NULL); + len = reply->length+8; ptr = (char *) reply; @@ -1237,11 +1252,16 @@ switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file "Sending %s (type=%x,length=%d).\n", skinny_message_type2str(reply->type), reply->type, reply->length); } - return switch_socket_send(listener->sock, ptr, &len); + res = switch_socket_send(listener->sock, ptr, &len); + + if ( discard ) { switch_safe_free(reply); } + return res; } else { skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_WARNING, "Not sending %s (type=%x,length=%d) while not ready.\n", skinny_message_type2str(reply->type), reply->type, reply->length); + + if ( discard ) { switch_safe_free(reply); } return SWITCH_STATUS_FALSE; } } diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index 2fe1336aa6..43b2502e81 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -87,6 +87,17 @@ char* skinny_codec2string(skinny_codecs skinnycodec); /* SKINNY MESSAGE DATA */ /*****************************************************************************/ +#define skinny_create_message(message,msgtype,field) \ + message = calloc(12 + sizeof(message->data.field), 1); \ + message->type = msgtype; \ + message->length = 4 + sizeof(message->data.field) + +#define skinny_create_empty_message(message,msgtype) \ + message = calloc(12, 1); \ + message->type = msgtype; \ + message->length = 4 + + /* KeepAliveMessage */ #define KEEP_ALIVE_MESSAGE 0x0000 @@ -955,11 +966,11 @@ void skinny_speed_dial_get(listener_t *listener, uint32_t instance, struct speed void skinny_service_url_get(listener_t *listener, uint32_t instance, struct service_url_stat_res_message **button); void skinny_feature_get(listener_t *listener, uint32_t instance, struct feature_stat_res_message **button); -switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply); -#define skinny_send_reply(listener, reply) skinny_perform_send_reply(listener, __FILE__, __SWITCH_FUNC__, __LINE__, reply) +switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply, switch_bool_t discard); +#define skinny_send_reply(listener, reply, discard) skinny_perform_send_reply(listener, __FILE__, __SWITCH_FUNC__, __LINE__, reply, discard) -switch_status_t skinny_perform_send_reply_quiet(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply); -#define skinny_send_reply_quiet(listener, reply) skinny_perform_send_reply_quiet(listener, __FILE__, __SWITCH_FUNC__, __LINE__, reply) +switch_status_t skinny_perform_send_reply_quiet(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply, switch_bool_t discard); +#define skinny_send_reply_quiet(listener, reply, discard) skinny_perform_send_reply_quiet(listener, __FILE__, __SWITCH_FUNC__, __LINE__, reply, discard) switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *request); diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index b30a0004d6..36e3f07d04 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -58,8 +58,6 @@ uint32_t soft_key_template_default_textids[] = { SKINNY_TEXTID_IDIVERT }; -#define TEXT_ID_LEN 20 - uint32_t soft_key_template_default_events[] = { SOFTKEY_REDIAL, SOFTKEY_NEWCALL, @@ -1440,15 +1438,13 @@ switch_status_t skinny_handle_forward_stat_req_message(listener_t *listener, ski skinny_check_data_length(request, sizeof(request->data.forward_stat_req)); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.forward_stat)); - message->type = FORWARD_STAT_MESSAGE; - message->length = 4 + sizeof(message->data.forward_stat); + skinny_create_message(message, FORWARD_STAT_MESSAGE, forward_stat); message->data.forward_stat.line_instance = request->data.forward_stat_req.line_instance; skinny_log_l(listener, SWITCH_LOG_DEBUG, "Handle Forward Stat Req Message with Line Instance (%d)\n", request->data.forward_stat_req.line_instance); - skinny_send_reply_quiet(listener, message); + skinny_send_reply_quiet(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -1475,15 +1471,13 @@ switch_status_t skinny_handle_line_stat_request(listener_t *listener, skinny_mes skinny_check_data_length(request, sizeof(request->data.line_req)); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.line_res)); - message->type = LINE_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.line_res); + skinny_create_message(message, LINE_STAT_RES_MESSAGE, line_res); skinny_line_get(listener, request->data.line_req.number, &button); memcpy(&message->data.line_res, button, sizeof(struct line_stat_res_message)); - skinny_send_reply(listener, message); + skinny_send_reply(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -1521,9 +1515,7 @@ switch_status_t skinny_handle_config_stat_request(listener_t *listener, skinny_m profile = listener->profile; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.config_res)); - message->type = CONFIG_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.config_res); + skinny_create_message(message, CONFIG_STAT_RES_MESSAGE, config_res); if ((sql = switch_mprintf( "SELECT name, user_id, instance, '' AS user_name, '' AS server_name, " @@ -1540,7 +1532,7 @@ switch_status_t skinny_handle_config_stat_request(listener_t *listener, skinny_m skinny_execute_sql_callback(profile, profile->sql_mutex, sql, skinny_config_stat_res_callback, message); switch_safe_free(sql); } - skinny_send_reply(listener, message); + skinny_send_reply(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -1591,9 +1583,7 @@ switch_status_t skinny_handle_button_template_request(listener_t *listener, skin profile = listener->profile; - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.button_template)); - message->type = BUTTON_TEMPLATE_RES_MESSAGE; - message->length = 4 + sizeof(message->data.button_template); + skinny_create_message(message, BUTTON_TEMPLATE_RES_MESSAGE, button_template); message->data.button_template.button_offset = 0; message->data.button_template.button_count = 0; @@ -1636,9 +1626,7 @@ switch_status_t skinny_handle_button_template_request(listener_t *listener, skin } } - - - return skinny_send_reply(listener, message);; + return skinny_send_reply(listener, message, SWITCH_TRUE); } switch_status_t skinny_handle_version_request(listener_t *listener, skinny_message_t *request) @@ -1856,7 +1844,7 @@ switch_status_t skinny_handle_soft_key_set_request(listener_t *listener, skinny_ skinny_log_l(listener, SWITCH_LOG_DEBUG, "Handle Soft Key Set Request with Set (%s)\n", "default"); } if (message) { - skinny_send_reply(listener, message); + skinny_send_reply_quiet(listener, message, SWITCH_FALSE); } else { skinny_log_l(listener, SWITCH_LOG_ERROR, "Profile %s doesn't have a default .\n", listener->profile->name); @@ -1980,14 +1968,13 @@ switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_UNREGISTER); switch_event_fire(&event); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.unregister_ack)); - message->type = UNREGISTER_ACK_MESSAGE; - message->length = 4 + sizeof(message->data.unregister_ack); + skinny_create_message(message, UNREGISTER_ACK_MESSAGE, unregister_ack); + message->data.unregister_ack.unregister_status = 0; /* OK */ skinny_log_l(listener, SWITCH_LOG_DEBUG, "Handle Unregister with Status (%d)\n", message->data.unregister_ack.unregister_status); - skinny_send_reply_quiet(listener, message); + skinny_send_reply_quiet(listener, message, SWITCH_TRUE); /* Close socket */ switch_clear_flag_locked(listener, LFLAG_RUNNING); @@ -2006,18 +1993,15 @@ switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, sk switch_assert(listener->profile); switch_assert(listener->device_name); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_template)); - message->type = SOFT_KEY_TEMPLATE_RES_MESSAGE; - message->length = 4 + sizeof(message->data.soft_key_template); + skinny_create_message(message, SOFT_KEY_TEMPLATE_RES_MESSAGE, soft_key_template); message->data.soft_key_template.soft_key_offset = 0; message->data.soft_key_template.soft_key_count = 21; message->data.soft_key_template.total_soft_key_count = 21; - memset(message->data.soft_key_template.soft_key, 0, sizeof(message->data.soft_key_template)); - for (i=0; i< TEXT_ID_LEN; i++) { + for (i=0; i < sizeof(soft_key_template_default_textids)/4; i++) { char *label = skinny_textid2raw(soft_key_template_default_textids[i]); - strcpy(message->data.soft_key_template.soft_key[i].soft_key_label, skinny_textid2raw(soft_key_template_default_textids[i])); + strncpy(message->data.soft_key_template.soft_key[i].soft_key_label, label, sizeof(message->data.soft_key_template.soft_key[i].soft_key_label)); switch_safe_free(label); message->data.soft_key_template.soft_key[i].soft_key_event = soft_key_template_default_events[i]; @@ -2025,7 +2009,7 @@ switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, sk skinny_log_l_msg(listener, SWITCH_LOG_DEBUG, "Handle Soft Key Template Request with Default Template\n"); - skinny_send_reply_quiet(listener, message); + skinny_send_reply_quiet(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -2078,12 +2062,16 @@ switch_status_t skinny_handle_data_message(listener_t *listener, skinny_message_ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Call-Id", "%d", request->data.data.call_id); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Transaction-Id", "%d", request->data.data.transaction_id); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Data-Length", "%d", request->data.data.data_length); - /* Ensure that the body is null-terminated */ + tmp = malloc(request->data.data.data_length + 1); memcpy(tmp, request->data.data.data, request->data.data.data_length); + + /* Ensure that the body is null-terminated */ tmp[request->data.data.data_length] = '\0'; switch_event_add_body(event, "%s", tmp); + switch_safe_free(tmp); + switch_event_fire(&event); return SWITCH_STATUS_SUCCESS; @@ -2096,15 +2084,13 @@ switch_status_t skinny_handle_service_url_stat_request(listener_t *listener, ski skinny_check_data_length(request, sizeof(request->data.service_url_req)); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.service_url_res)); - message->type = SERVICE_URL_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.service_url_res); + skinny_create_message(message, SERVICE_URL_STAT_RES_MESSAGE, service_url_res); skinny_service_url_get(listener, request->data.service_url_req.service_url_index, &button); memcpy(&message->data.service_url_res, button, sizeof(struct service_url_stat_res_message)); - skinny_send_reply(listener, message); + skinny_send_reply(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -2116,15 +2102,13 @@ switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_ skinny_check_data_length(request, sizeof(request->data.feature_req)); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.feature_res)); - message->type = FEATURE_STAT_RES_MESSAGE; - message->length = 4 + sizeof(message->data.feature_res); + skinny_create_message(message, FEATURE_STAT_RES_MESSAGE, feature_res); skinny_feature_get(listener, request->data.feature_req.feature_index, &button); memcpy(&message->data.feature_res, button, sizeof(struct feature_stat_res_message)); - skinny_send_reply(listener, message); + skinny_send_reply(listener, message, SWITCH_TRUE); return SWITCH_STATUS_SUCCESS; } @@ -2150,32 +2134,46 @@ switch_status_t skinny_handle_extended_data_message(listener_t *listener, skinny switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Conference-Id", "%d", request->data.extended_data.conference_id); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-App-Instance-Id", "%d", request->data.extended_data.app_instance_id); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Routing-Id", "%d", request->data.extended_data.routing_id); - /* Ensure that the body is null-terminated */ + tmp = malloc(request->data.data.data_length + 1); memcpy(tmp, request->data.data.data, request->data.data.data_length); + + /* Ensure that the body is null-terminated */ tmp[request->data.data.data_length] = '\0'; switch_event_add_body(event, "%s", tmp); + switch_safe_free(tmp); switch_event_fire(&event); return SWITCH_STATUS_SUCCESS; } + switch_status_t skinny_handle_dialed_phone_book_message(listener_t *listener, skinny_message_t *request) { skinny_message_t *message; skinny_check_data_length(request, sizeof(request->data.dialed_phone_book)); - message = switch_core_alloc(listener->pool, 12+sizeof(message->data.dialed_phone_book_ack)); - message->type = DIALED_PHONE_BOOK_ACK_MESSAGE; - message->length = 4 + sizeof(message->data.dialed_phone_book_ack); + skinny_create_message(message, DIALED_PHONE_BOOK_ACK_MESSAGE, dialed_phone_book_ack); + message->data.dialed_phone_book_ack.number_index = request->data.dialed_phone_book.number_index; message->data.dialed_phone_book_ack.line_instance = request->data.dialed_phone_book.line_instance; message->data.dialed_phone_book_ack.unknown = request->data.dialed_phone_book.unknown; message->data.dialed_phone_book_ack.unknown2 = 0; +#if 0 + /* Not sure why this isn't being sent at this point, need to investigate */ + skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, + "Sending Handle Dialed Phone Book Ack Message with Number Index (%d), Line Instance (%d)\n", + request->data.dialed_phone_book.number_index, request->data.dialed_phone_book.line_instance); + + return skinny_send_reply_quiet(listener, message, SWITCH_TRUE); +#else + switch_safe_free(message); return SWITCH_STATUS_SUCCESS; +#endif } + switch_status_t skinny_handle_accessory_status_message(listener_t *listener, skinny_message_t *request) { char *sql; From 4d9ff80a0168593401ba0bf5001f31525119342d Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Fri, 2 Aug 2013 21:25:51 -0500 Subject: [PATCH 226/278] mod_skinny work on FS-5632 - few more cases --- src/mod/endpoints/mod_skinny/mod_skinny.c | 4 +++- src/mod/endpoints/mod_skinny/skinny_protocol.c | 10 +++++++++- src/mod/endpoints/mod_skinny/skinny_server.c | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/mod/endpoints/mod_skinny/mod_skinny.c b/src/mod/endpoints/mod_skinny/mod_skinny.c index ab4398a898..13a94810f8 100644 --- a/src/mod/endpoints/mod_skinny/mod_skinny.c +++ b/src/mod/endpoints/mod_skinny/mod_skinny.c @@ -1604,9 +1604,11 @@ static void *SWITCH_THREAD_FUNC listener_run(switch_thread_t *thread, void *obj) if (skinny_handle_request(listener, request) != SWITCH_STATUS_SUCCESS) { switch_clear_flag_locked(listener, LFLAG_RUNNING); + switch_safe_free(request); break; + } else { + switch_safe_free(request); } - } remove_listener(listener); diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index 2de3b87e27..ce756da34a 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -110,7 +110,7 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) char *ptr; switch_status_t status = SWITCH_STATUS_SUCCESS; - request = switch_core_alloc(listener->pool, SKINNY_MESSAGE_MAXSIZE); + request = calloc(SKINNY_MESSAGE_MAXSIZE,1); if (!request) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate memory.\n"); @@ -122,6 +122,7 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) while (listener_is_ready(listener)) { uint8_t do_sleep = 1; if (listener->expire_time && listener->expire_time < switch_epoch_time_now(NULL)) { + switch_safe_free(request); return SWITCH_STATUS_TIMEOUT; } if(bytes < SKINNY_MESSAGE_FIELD_SIZE) { @@ -135,6 +136,7 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) status = switch_socket_recv(listener->sock, ptr, &mlen); if (listener->expire_time && listener->expire_time < switch_epoch_time_now(NULL)) { + switch_safe_free(request); return SWITCH_STATUS_TIMEOUT; } @@ -143,6 +145,7 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) } if (!switch_status_is_timeup(status) && !SWITCH_STATUS_IS_BREAK(status) && (status != SWITCH_STATUS_SUCCESS)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Socket break with status=%d.\n", status); + switch_safe_free(request); return SWITCH_STATUS_FALSE; } @@ -162,17 +165,20 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Skinny client sent invalid data. Length should be greater than 4 but got %d.\n", request->length); + switch_safe_free(request); return SWITCH_STATUS_FALSE; } if(request->length + 2*SKINNY_MESSAGE_FIELD_SIZE > SKINNY_MESSAGE_MAXSIZE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Skinny client sent too huge data. Got %d which is above threshold %d.\n", request->length, SKINNY_MESSAGE_MAXSIZE - 2*SKINNY_MESSAGE_FIELD_SIZE); + switch_safe_free(request); return SWITCH_STATUS_FALSE; } if(bytes >= request->length + 2*SKINNY_MESSAGE_FIELD_SIZE) { /* Message body */ *req = request; + /* Do not free here, caller needs to do it */ return SWITCH_STATUS_SUCCESS; } } @@ -181,6 +187,8 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) switch_cond_next(); } } + + switch_safe_free(request); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 36e3f07d04..f3dda49f1b 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -1687,7 +1687,11 @@ switch_status_t skinny_handle_capabilities_response(listener_t *listener, skinny } i = 0; pos = 0; - codec_string = switch_core_alloc(listener->pool, string_len+1); + codec_string = calloc(string_len+1,1); + if ( !codec_string ) { + skinny_log_l_msg(listener, SWITCH_LOG_ERROR, "Unable to allocate memory for codec string.\n"); + return SWITCH_STATUS_FALSE; + } for (string_pos = 0; string_pos < string_len; string_pos++) { char *codec = codec_order[i]; switch_assert(i < n); @@ -1709,6 +1713,7 @@ switch_status_t skinny_handle_capabilities_response(listener_t *listener, skinny switch_safe_free(sql); } skinny_log_l(listener, SWITCH_LOG_DEBUG, "Codecs %s supported.\n", codec_string); + switch_safe_free(codec_string); return SWITCH_STATUS_SUCCESS; } From 80f91682269291fd63ca841fc31b1a9d57ced198 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 3 Aug 2013 18:49:55 +0500 Subject: [PATCH 227/278] FS-5664 --resolve --- src/switch_core_sqldb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/switch_core_sqldb.c b/src/switch_core_sqldb.c index b793121a77..e270a0c29c 100644 --- a/src/switch_core_sqldb.c +++ b/src/switch_core_sqldb.c @@ -393,6 +393,7 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h const char *db_name = NULL; const char *odbc_user = NULL; const char *odbc_pass = NULL; + const char *db_type = NULL; while(runtime.max_db_handles && sql_manager.total_handles >= runtime.max_db_handles && sql_manager.total_used_handles >= sql_manager.total_handles) { if (!waiting++) { @@ -416,12 +417,14 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h db_name = connection_options->pgsql_options.dsn; odbc_user = NULL; odbc_pass = NULL; + db_type = "pgsql"; } case SCDB_TYPE_ODBC: { db_name = connection_options->odbc_options.dsn; odbc_user = connection_options->odbc_options.user; odbc_pass = connection_options->odbc_options.pass; + db_type = "odbc"; } break; case SCDB_TYPE_CORE_DB: @@ -429,6 +432,7 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h db_name = connection_options->core_db_options.db_path; odbc_user = NULL; odbc_pass = NULL; + db_type = "core_db"; } break; } @@ -438,9 +442,9 @@ SWITCH_DECLARE(switch_status_t) _switch_cache_db_get_db_handle(switch_cache_db_h } if (odbc_user || odbc_pass) { - snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\";user=\"%s\";pass=\"%s\"", db_name, odbc_user, odbc_pass); + snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\";type=\"%s\"user=\"%s\";pass=\"%s\"", db_name, db_type, odbc_user, odbc_pass); } else { - snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\"", db_name); + snprintf(db_str, sizeof(db_str) - 1, "db=\"%s\",type=\"%s\"", db_name, db_type); } snprintf(db_callsite_str, sizeof(db_callsite_str) - 1, "%s:%d", file, line); snprintf(thread_str, sizeof(thread_str) - 1, "thread=\"%lu\"", (unsigned long) (intptr_t) self); From 95ae3732a8f40ed04f0e4e545b2d05b0af4684b6 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Mon, 5 Aug 2013 23:27:16 +0800 Subject: [PATCH 228/278] FS-5655 --resolve msvc has so many versions, makes it complicated --- Freeswitch.2010.express.sln | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Freeswitch.2010.express.sln b/Freeswitch.2010.express.sln index 4749470674..6b7c5fde3b 100644 --- a/Freeswitch.2010.express.sln +++ b/Freeswitch.2010.express.sln @@ -38,6 +38,11 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_console", "src\mod\loggers\mod_console\mod_console.2010.vcxproj", "{1C453396-D912-4213-89FD-9B489162B7B5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_xml_rpc", "src\mod\xml_int\mod_xml_rpc\mod_xml_rpc.2010.vcxproj", "{CBEC7225-0C21-4DA8-978E-1F158F8AD950}" + ProjectSection(ProjectDependencies) = postProject + {9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6} = {9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6} + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981} = {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981} + {329A6FA0-0FCC-4435-A950-E670AEFA9838} = {329A6FA0-0FCC-4435-A950-E670AEFA9838} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_rss", "src\mod\applications\mod_rss\mod_rss.2010.vcxproj", "{B69247FA-ECD6-40ED-8E44-5CA6C3BAF9A4}" EndProject @@ -221,6 +226,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspandsp", "libs\spandsp\src\libspandsp.2010.vcxproj", "{1CBB0077-18C5-455F-801C-0A0CE7B0BBF5}" ProjectSection(ProjectDependencies) = postProject {019DBD2A-273D-4BA4-BF86-B5EFE2ED76B1} = {019DBD2A-273D-4BA4-BF86-B5EFE2ED76B1} + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981} = {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspeex", "libs\speex\win32\VS2008\libspeex\libspeex.vcxproj", "{E972C52F-9E85-4D65-B19C-031E511E9DB4}" @@ -338,6 +344,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gennmtab", "libs\win32\xmlr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "make_math_fixed_tables", "libs\spandsp\src\msvc\make_math_fixed_tables.2010.vcxproj", "{9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "make_cielab_luts", "libs\spandsp\src\msvc\make_cielab_luts.2010.vcxproj", "{85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution All|Win32 = All|Win32 @@ -2067,6 +2075,15 @@ Global {9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6}.Release|Win32.Build.0 = All|Win32 {9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6}.Release|x64.ActiveCfg = All|Win32 {9D04A840-CE18-4FF5-A6D3-0A2BB92FF2E6}.Release|x64.Build.0 = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.All|Win32.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.All|Win32.Build.0 = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.All|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.Build.0 = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.Build.0 = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.ActiveCfg = All|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 7c744ce1d840e29c5c6001c080f40caa682c7daf Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Mon, 5 Aug 2013 23:50:28 +0800 Subject: [PATCH 229/278] Added custom allocation functions to spandsp --- libs/spandsp/configure.ac | 3 + libs/spandsp/src/Makefile.am | 2 + libs/spandsp/src/alloc.c | 128 +++++++++++++++++++++++++++++++ libs/spandsp/src/spandsp.h.in | 1 + libs/spandsp/src/spandsp/alloc.h | 63 +++++++++++++++ libs/spandsp/tests/Makefile.am | 4 + libs/spandsp/tests/alloc_tests.c | 78 +++++++++++++++++++ libs/spandsp/tests/v18_tests.c | 16 +++- 8 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 libs/spandsp/src/alloc.c create mode 100644 libs/spandsp/src/spandsp/alloc.h create mode 100644 libs/spandsp/tests/alloc_tests.c diff --git a/libs/spandsp/configure.ac b/libs/spandsp/configure.ac index dacc3ae4ed..8a6158fc80 100644 --- a/libs/spandsp/configure.ac +++ b/libs/spandsp/configure.ac @@ -138,6 +138,7 @@ AC_ARG_ENABLE(avx, [ --enable-avx Enable AVX support]) AC_ARG_ENABLE(avx2, [ --enable-avx2 Enable AVX2 support]) AC_ARG_ENABLE(neon, [ --enable-neon Enable NEON support]) AC_ARG_ENABLE(fixed_point, [ --enable-fixed-point Enable fixed point support]) + # The following is for MSVC, where we may be using a local copy of libtiff, built alongside spandsp AC_ARG_ENABLE(builtin_tiff, [AC_HELP_STRING([--enable-builtin-tiff],[build with builtin libtiff])],[enable_builtin_tiff="$enableval"],[enable_builtin_tiff="no"]) @@ -164,6 +165,8 @@ fi AX_C99_FLEXIBLE_ARRAY AC_CHECK_FUNCS([aligned_alloc]) +AC_CHECK_FUNCS([memalign]) +AC_CHECK_FUNCS([posix_memalign]) AC_CHECK_FUNCS([memmove]) AC_CHECK_FUNCS([memset]) AC_CHECK_FUNCS([select]) diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index 3d63ae7274..a794e37fb5 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -84,6 +84,7 @@ lib_LTLIBRARIES = libspandsp.la libspandsp_la_SOURCES = ademco_contactid.c \ adsi.c \ + alloc.c \ async.c \ at_interpreter.c \ awgn.c \ @@ -178,6 +179,7 @@ libspandsp_la_LDFLAGS = -version-info @SPANDSP_LT_CURRENT@:@SPANDSP_LT_REVISION@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ spandsp/adsi.h \ + spandsp/alloc.h \ spandsp/async.h \ spandsp/arctan2.h \ spandsp/at_interpreter.h \ diff --git a/libs/spandsp/src/alloc.c b/libs/spandsp/src/alloc.c new file mode 100644 index 0000000000..e75c603c9f --- /dev/null +++ b/libs/spandsp/src/alloc.c @@ -0,0 +1,128 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * alloc.c - memory allocation handling. + * + * Written by Steve Underwood + * + * Copyright (C) 2013 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1, + * as published by the Free Software Foundation. + * + * This program 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 program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include +#define __USE_ISOC11 +#include +#if defined(HAVE_MALLOC_H) +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "spandsp/telephony.h" +#include "spandsp/alloc.h" + +#if defined(HAVE_POSIX_MEMALIGN) +static void *fake_posix_memalign(size_t alignment, size_t size); +#endif +static void *fake_aligned_alloc(size_t alignment, size_t size); + +span_alloc_t __span_alloc = malloc; +#if defined(HAVE_ALIGNED_ALLOC) +span_aligned_alloc_t __span_aligned_alloc = aligned_alloc; +#elif defined(HAVE_MEMALIGN) +span_aligned_alloc_t __span_aligned_alloc = memalign; +#elif defined(HAVE_POSIX_MEMALIGN) +span_aligned_alloc_t __span_aligned_alloc = fake_posix_memalign; +#else +span_aligned_alloc_t __span_aligned_alloc = fake_aligned_alloc; +#endif +span_realloc_t __span_realloc = realloc; +span_free_t __span_free = free; + +#if defined(HAVE_POSIX_MEMALIGN) +static void *fake_posix_memalign(size_t alignment, size_t size) +{ + void *ptr; + + /* Make posix_memalign look like the more modern aligned_alloc */ + posix_memalign(&ptr, alignment, size); + return ptr; +} +/*- End of function --------------------------------------------------------*/ +#endif + +static void *fake_aligned_alloc(size_t alignment, size_t size) +{ + return NULL; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void *) span_alloc(size_t size) +{ + return __span_alloc(size); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void *) span_aligned_alloc(size_t alignment, size_t size) +{ + return __span_aligned_alloc(alignment, size); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void *) span_realloc(void *ptr, size_t size) +{ + return __span_realloc(ptr, size); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) span_free(void *ptr) +{ + __span_free(ptr); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) span_mem_allocators(span_alloc_t custom_alloc, + span_aligned_alloc_t custom_aligned_alloc, + span_realloc_t custom_realloc, + span_free_t custom_free) +{ + if (custom_alloc == NULL || custom_realloc == NULL || custom_free == NULL) + return -1; + __span_alloc = custom_alloc; + if (custom_aligned_alloc) + __span_aligned_alloc = custom_aligned_alloc; + else + __span_aligned_alloc = fake_aligned_alloc; + __span_realloc = custom_realloc; + __span_free = custom_free; + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/src/spandsp.h.in b/libs/spandsp/src/spandsp.h.in index 2764aa4289..d5a7b44581 100644 --- a/libs/spandsp/src/spandsp.h.in +++ b/libs/spandsp/src/spandsp.h.in @@ -48,6 +48,7 @@ #include #include +#include #include #include #include diff --git a/libs/spandsp/src/spandsp/alloc.h b/libs/spandsp/src/spandsp/alloc.h new file mode 100644 index 0000000000..8511897604 --- /dev/null +++ b/libs/spandsp/src/spandsp/alloc.h @@ -0,0 +1,63 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * alloc.h - memory allocation handling. + * + * Written by Steve Underwood + * + * Copyright (C) 2013 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1, + * as published by the Free Software Foundation. + * + * This program 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 program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*! \file */ + +#if !defined(_SPANDSP_ALLOC_H_) +#define _SPANDSP_ALLOC_H_ + +typedef void *(*span_alloc_t)(size_t size); +typedef void *(*span_aligned_alloc_t)(size_t alignment, size_t size); +typedef void *(*span_realloc_t)(void *ptr, size_t size); +typedef void (*span_free_t)(void *ptr); + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* Allocate size bytes of memory. */ +SPAN_DECLARE(void *) span_alloc(size_t size); + +/* Allocate size bytes allocated to ALIGNMENT bytes. */ +SPAN_DECLARE(void *) span_aligned_alloc(size_t alignment, size_t size); + +/* Re-allocate the previously allocated block in ptr, making the new block size bytes long. */ +SPAN_DECLARE(void *) span_realloc(void *ptr, size_t size); + +/* Free a block allocated by span_alloc, span_aligned_alloc, or span_realloc. */ +SPAN_DECLARE(void) span_free(void *ptr); + +SPAN_DECLARE(int) span_mem_allocators(span_alloc_t custom_alloc, + span_aligned_alloc_t custom_aligned_alloc, + span_realloc_t custom_realloc, + span_free_t custom_free); + +#if defined(__cplusplus) +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index e35408c3b7..8eda2bb201 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -52,6 +52,7 @@ LIBDIR = -L$(top_builddir)/src noinst_PROGRAMS = ademco_contactid_tests \ adsi_tests \ + alloc_tests \ async_tests \ at_interpreter_tests \ awgn_tests \ @@ -146,6 +147,9 @@ ademco_contactid_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIB adsi_tests_SOURCES = adsi_tests.c adsi_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp +alloc_tests_SOURCES = alloc_tests.c +alloc_tests_LDADD = $(LIBDIR) -lspandsp + async_tests_SOURCES = async_tests.c async_tests_LDADD = $(LIBDIR) -lspandsp diff --git a/libs/spandsp/tests/alloc_tests.c b/libs/spandsp/tests/alloc_tests.c new file mode 100644 index 0000000000..54dfb1e96d --- /dev/null +++ b/libs/spandsp/tests/alloc_tests.c @@ -0,0 +1,78 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * alloc_tests.c - memory allocation handling tests. + * + * Written by Steve Underwood + * + * Copyright (C) 2013 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*! \file */ + +/*! \page alloc_tests_page Memory allocation tests +\section alloc_tests_page_sec_1 What does it do? +???. + +\section alloc_tests_page_sec_2 How does it work? +???. +*/ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spandsp.h" + +int main(int argc, char *argv[]) +{ + void *a; + void *b; + void *c; + + if (span_mem_allocators(malloc, + memalign, + realloc, + free)) + { + printf("Failed\n"); + exit(2); + } + a = span_alloc(42); + b = span_aligned_alloc(8, 42); + c = span_realloc(NULL, 42); + printf("%p %p %p\n", a, b, c); + span_free(a); + span_free(b); + span_free(c); +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/libs/spandsp/tests/v18_tests.c b/libs/spandsp/tests/v18_tests.c index 0c12678c80..64a47a1593 100644 --- a/libs/spandsp/tests/v18_tests.c +++ b/libs/spandsp/tests/v18_tests.c @@ -7473,7 +7473,7 @@ static int test_x_01(void) result[0][0] = result[1][0] = '\0'; - v18_put(v18[0], "z", 1); + v18_put(v18[0], "zabcdefghijklmnopq", -1); for (i = 0; i < 10000; i++) { for (j = 0; j < 2; j++) @@ -7518,7 +7518,7 @@ static int test_x_01(void) v18_free(v18[0]); v18_free(v18[1]); ref = "cdefghij"; - printf("Result:\n%s\n", result[0]); + printf("Result:\n%s\n", result[1]); printf("Reference result:\n%s\n", ref); if (unexpected_echo || strcmp(result[1], ref) != 0) return -1; @@ -7864,9 +7864,18 @@ static int test_x_04(void) static void x_05_put_text_msg(void *user_data, const uint8_t *msg, int len) { if (user_data == NULL) + { + /* Gather the received characters, which should be like the transmitted characters, + but with the first three characters missing. */ strcat(result[0], (const char *) msg); + } else + { + /* Receiving a character from the far end should block out its receiver + for a while. If we send a stream of DTMF back, the first few characters + (actually 3 for this particular text string) should be lost. */ v18_put(v18[1], "behknqtwz", 9); + } } /*- End of function --------------------------------------------------------*/ @@ -7920,7 +7929,8 @@ static int test_x_05(void) result[0][0] = result[1][0] = '\0'; - v18_put(v18[0], "e", 1); + /* Sending a character should block out the receiver for a while */ + v18_put(v18[0], "z", 1); for (i = 0; i < 1000; i++) { From d5e4089c22a86ccd08b367d6f43fc28c564e1608 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 6 Aug 2013 00:49:15 +0800 Subject: [PATCH 230/278] Moved a lot of spandsp to the use of custom allocation functions --- libs/spandsp/src/ademco_contactid.c | 9 +++++---- libs/spandsp/src/adsi.c | 9 +++++---- libs/spandsp/src/alloc.c | 12 ++++++------ libs/spandsp/src/async.c | 9 +++++---- libs/spandsp/src/at_interpreter.c | 15 ++++++++------- libs/spandsp/src/awgn.c | 5 +++-- libs/spandsp/src/bell_r2_mf.c | 17 +++++++++-------- libs/spandsp/src/bert.c | 5 +++-- libs/spandsp/src/bitstream.c | 5 +++-- libs/spandsp/src/complex_filters.c | 13 +++++++------ libs/spandsp/src/dtmf.c | 9 +++++---- libs/spandsp/src/echo.c | 21 +++++++++++---------- libs/spandsp/src/fax.c | 5 +++-- libs/spandsp/src/fax_modems.c | 5 +++-- libs/spandsp/src/g711.c | 5 +++-- libs/spandsp/src/g722.c | 9 +++++---- libs/spandsp/src/g726.c | 5 +++-- libs/spandsp/src/gsm0610_encode.c | 5 +++-- libs/spandsp/src/logging.c | 5 +++-- libs/spandsp/src/lpc10_decode.c | 5 +++-- libs/spandsp/src/lpc10_encode.c | 5 +++-- libs/spandsp/src/modem_connect_tones.c | 11 ++++++----- libs/spandsp/src/modem_echo.c | 17 +++++++++-------- libs/spandsp/src/noise.c | 5 +++-- libs/spandsp/src/oki_adpcm.c | 5 +++-- libs/spandsp/src/playout.c | 13 +++++++------ libs/spandsp/src/plc.c | 5 +++-- libs/spandsp/src/power_meter.c | 9 +++++---- libs/spandsp/src/queue.c | 5 +++-- libs/spandsp/src/schedule.c | 7 ++++--- libs/spandsp/src/sig_tone.c | 9 +++++---- libs/spandsp/src/silence_gen.c | 5 +++-- libs/spandsp/src/super_tone_rx.c | 25 +++++++++++++------------ libs/spandsp/src/super_tone_tx.c | 9 +++++---- libs/spandsp/src/swept_tone.c | 5 +++-- libs/spandsp/src/t30_api.c | 25 +++++++++++++------------ libs/spandsp/src/t81_t82_arith_coding.c | 9 +++++---- libs/spandsp/src/t85_decode.c | 23 ++++++++++++----------- libs/spandsp/src/t85_encode.c | 15 ++++++++------- libs/spandsp/src/time_scale.c | 7 ++++--- libs/spandsp/src/timezone.c | 5 +++-- libs/spandsp/src/tone_detect.c | 5 +++-- libs/spandsp/src/tone_generate.c | 9 +++++---- libs/spandsp/src/v42.c | 5 +++-- libs/spandsp/src/v42bis.c | 4 +++- libs/spandsp/src/v8.c | 5 +++-- 46 files changed, 233 insertions(+), 187 deletions(-) diff --git a/libs/spandsp/src/ademco_contactid.c b/libs/spandsp/src/ademco_contactid.c index 6aaf99c382..29d1130b99 100644 --- a/libs/spandsp/src/ademco_contactid.c +++ b/libs/spandsp/src/ademco_contactid.c @@ -46,6 +46,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/queue.h" @@ -749,7 +750,7 @@ SPAN_DECLARE(ademco_contactid_receiver_state_t *) ademco_contactid_receiver_init { if (s == NULL) { - if ((s = (ademco_contactid_receiver_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (ademco_contactid_receiver_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -776,7 +777,7 @@ SPAN_DECLARE(int) ademco_contactid_receiver_release(ademco_contactid_receiver_st SPAN_DECLARE(int) ademco_contactid_receiver_free(ademco_contactid_receiver_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -1084,7 +1085,7 @@ SPAN_DECLARE(ademco_contactid_sender_state_t *) ademco_contactid_sender_init(ade { if (s == NULL) { - if ((s = (ademco_contactid_sender_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (ademco_contactid_sender_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1121,7 +1122,7 @@ SPAN_DECLARE(int) ademco_contactid_sender_release(ademco_contactid_sender_state_ SPAN_DECLARE(int) ademco_contactid_sender_free(ademco_contactid_sender_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/adsi.c b/libs/spandsp/src/adsi.c index df327bf3c5..24e87d65ec 100644 --- a/libs/spandsp/src/adsi.c +++ b/libs/spandsp/src/adsi.c @@ -44,6 +44,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/queue.h" @@ -426,7 +427,7 @@ SPAN_DECLARE(adsi_rx_state_t *) adsi_rx_init(adsi_rx_state_t *s, { if (s == NULL) { - if ((s = (adsi_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (adsi_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -465,7 +466,7 @@ SPAN_DECLARE(int) adsi_rx_release(adsi_rx_state_t *s) SPAN_DECLARE(int) adsi_rx_free(adsi_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -658,7 +659,7 @@ SPAN_DECLARE(adsi_tx_state_t *) adsi_tx_init(adsi_tx_state_t *s, int standard) { if (s == NULL) { - if ((s = (adsi_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (adsi_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -688,7 +689,7 @@ SPAN_DECLARE(int) adsi_tx_release(adsi_tx_state_t *s) SPAN_DECLARE(int) adsi_tx_free(adsi_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/alloc.c b/libs/spandsp/src/alloc.c index e75c603c9f..d02291248c 100644 --- a/libs/spandsp/src/alloc.c +++ b/libs/spandsp/src/alloc.c @@ -48,11 +48,6 @@ #include "spandsp/telephony.h" #include "spandsp/alloc.h" -#if defined(HAVE_POSIX_MEMALIGN) -static void *fake_posix_memalign(size_t alignment, size_t size); -#endif -static void *fake_aligned_alloc(size_t alignment, size_t size); - span_alloc_t __span_alloc = malloc; #if defined(HAVE_ALIGNED_ALLOC) span_aligned_alloc_t __span_aligned_alloc = aligned_alloc; @@ -60,13 +55,18 @@ span_aligned_alloc_t __span_aligned_alloc = aligned_alloc; span_aligned_alloc_t __span_aligned_alloc = memalign; #elif defined(HAVE_POSIX_MEMALIGN) span_aligned_alloc_t __span_aligned_alloc = fake_posix_memalign; +static void *fake_posix_memalign(size_t alignment, size_t size); #else span_aligned_alloc_t __span_aligned_alloc = fake_aligned_alloc; #endif span_realloc_t __span_realloc = realloc; span_free_t __span_free = free; -#if defined(HAVE_POSIX_MEMALIGN) +static void *fake_aligned_alloc(size_t alignment, size_t size); + +#if defined(HAVE_ALIGNED_ALLOC) +#elif defined(HAVE_MEMALIGN) +#elif defined(HAVE_POSIX_MEMALIGN) static void *fake_posix_memalign(size_t alignment, size_t size) { void *ptr; diff --git a/libs/spandsp/src/async.c b/libs/spandsp/src/async.c index fb381bb7bc..15c73adcc0 100644 --- a/libs/spandsp/src/async.c +++ b/libs/spandsp/src/async.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -174,7 +175,7 @@ SPAN_DECLARE(async_rx_state_t *) async_rx_init(async_rx_state_t *s, { if (s == NULL) { - if ((s = (async_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (async_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->data_bits = data_bits; @@ -203,7 +204,7 @@ SPAN_DECLARE(int) async_rx_release(async_rx_state_t *s) SPAN_DECLARE(int) async_rx_free(async_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -274,7 +275,7 @@ SPAN_DECLARE(async_tx_state_t *) async_tx_init(async_tx_state_t *s, { if (s == NULL) { - if ((s = (async_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (async_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } /* We have a use_v14 parameter for completeness, but right now V.14 only @@ -304,7 +305,7 @@ SPAN_DECLARE(int) async_tx_release(async_tx_state_t *s) SPAN_DECLARE(int) async_tx_free(async_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/at_interpreter.c b/libs/spandsp/src/at_interpreter.c index 2ecdbb866d..d36532afbd 100644 --- a/libs/spandsp/src/at_interpreter.c +++ b/libs/spandsp/src/at_interpreter.c @@ -46,6 +46,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/power_meter.h" @@ -379,7 +380,7 @@ SPAN_DECLARE(void) at_reset_call_info(at_state_t *s) for (call_id = s->call_id; call_id; call_id = next) { next = call_id->next; - free(call_id); + span_free(call_id); } s->call_id = NULL; s->rings_indicated = 0; @@ -392,8 +393,8 @@ SPAN_DECLARE(void) at_set_call_info(at_state_t *s, char const *id, char const *v at_call_id_t *new_call_id; at_call_id_t *call_id; - /* TODO: We should really not merely ignore a failure to malloc */ - if ((new_call_id = (at_call_id_t *) malloc(sizeof(*new_call_id))) == NULL) + /* TODO: We should really not merely ignore a failure to allocate */ + if ((new_call_id = (at_call_id_t *) span_alloc(sizeof(*new_call_id))) == NULL) return; call_id = s->call_id; /* If these strdups fail its pretty harmless. We just appear to not @@ -752,7 +753,7 @@ static int parse_string_out(at_state_t *s, const char **t, char **target, const default: /* Set value */ if (*target) - free(*target); + span_free(*target); /* If this strdup fails, it should be harmless */ *target = strdup(*t); break; @@ -5590,7 +5591,7 @@ SPAN_DECLARE(at_state_t *) at_init(at_state_t *s, { if (s == NULL) { - if ((s = (at_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (at_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, '\0', sizeof(*s)); @@ -5615,7 +5616,7 @@ SPAN_DECLARE(int) at_release(at_state_t *s) { at_reset_call_info(s); if (s->local_id) - free(s->local_id); + span_free(s->local_id); return 0; } /*- End of function --------------------------------------------------------*/ @@ -5625,7 +5626,7 @@ SPAN_DECLARE(int) at_free(at_state_t *s) int ret; ret = at_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/awgn.c b/libs/spandsp/src/awgn.c index 582959cc93..fa33b1b4c5 100644 --- a/libs/spandsp/src/awgn.c +++ b/libs/spandsp/src/awgn.c @@ -55,6 +55,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/awgn.h" @@ -100,7 +101,7 @@ SPAN_DECLARE(awgn_state_t *) awgn_init_dbov(awgn_state_t *s, int idum, float lev if (s == NULL) { - if ((s = (awgn_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (awgn_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } if (idum < 0) @@ -140,7 +141,7 @@ SPAN_DECLARE(int) awgn_release(awgn_state_t *s) SPAN_DECLARE(int) awgn_free(awgn_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/bell_r2_mf.c b/libs/spandsp/src/bell_r2_mf.c index dba407cddd..493f766f9c 100644 --- a/libs/spandsp/src/bell_r2_mf.c +++ b/libs/spandsp/src/bell_r2_mf.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/fast_convert.h" #include "spandsp/queue.h" @@ -325,7 +326,7 @@ SPAN_DECLARE(bell_mf_tx_state_t *) bell_mf_tx_init(bell_mf_tx_state_t *s) { if (s == NULL) { - if ((s = (bell_mf_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (bell_mf_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -348,7 +349,7 @@ SPAN_DECLARE(int) bell_mf_tx_release(bell_mf_tx_state_t *s) SPAN_DECLARE(int) bell_mf_tx_free(bell_mf_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -397,7 +398,7 @@ SPAN_DECLARE(r2_mf_tx_state_t *) r2_mf_tx_init(r2_mf_tx_state_t *s, int fwd) if (s == NULL) { - if ((s = (r2_mf_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (r2_mf_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -451,7 +452,7 @@ SPAN_DECLARE(int) r2_mf_tx_release(r2_mf_tx_state_t *s) SPAN_DECLARE(int) r2_mf_tx_free(r2_mf_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -633,7 +634,7 @@ SPAN_DECLARE(bell_mf_rx_state_t *) bell_mf_rx_init(bell_mf_rx_state_t *s, if (s == NULL) { - if ((s = (bell_mf_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (bell_mf_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -671,7 +672,7 @@ SPAN_DECLARE(int) bell_mf_rx_release(bell_mf_rx_state_t *s) SPAN_DECLARE(int) bell_mf_rx_free(bell_mf_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -813,7 +814,7 @@ SPAN_DECLARE(r2_mf_rx_state_t *) r2_mf_rx_init(r2_mf_rx_state_t *s, if (s == NULL) { - if ((s = (r2_mf_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (r2_mf_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -855,7 +856,7 @@ SPAN_DECLARE(int) r2_mf_rx_release(r2_mf_rx_state_t *s) SPAN_DECLARE(int) r2_mf_rx_free(r2_mf_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/bert.c b/libs/spandsp/src/bert.c index a8ad5ad6b7..a4081f66d1 100644 --- a/libs/spandsp/src/bert.c +++ b/libs/spandsp/src/bert.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/async.h" #include "spandsp/bert.h" @@ -350,7 +351,7 @@ SPAN_DECLARE(bert_state_t *) bert_init(bert_state_t *s, int limit, int pattern, if (s == NULL) { - if ((s = (bert_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (bert_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -503,7 +504,7 @@ SPAN_DECLARE(int) bert_release(bert_state_t *s) SPAN_DECLARE(int) bert_free(bert_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/bitstream.c b/libs/spandsp/src/bitstream.c index 9a8c0bc9dd..f3da9c4d0b 100644 --- a/libs/spandsp/src/bitstream.c +++ b/libs/spandsp/src/bitstream.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/bitstream.h" #include "spandsp/private/bitstream.h" @@ -132,7 +133,7 @@ SPAN_DECLARE(bitstream_state_t *) bitstream_init(bitstream_state_t *s, int lsb_f { if (s == NULL) { - if ((s = (bitstream_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (bitstream_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->bitstream = 0; @@ -151,7 +152,7 @@ SPAN_DECLARE(int) bitstream_release(bitstream_state_t *s) SPAN_DECLARE(int) bitstream_free(bitstream_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/complex_filters.c b/libs/spandsp/src/complex_filters.c index d54e55818a..22cf4ac9b8 100644 --- a/libs/spandsp/src/complex_filters.c +++ b/libs/spandsp/src/complex_filters.c @@ -32,6 +32,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/complex.h" #include "spandsp/complex_filters.h" @@ -40,7 +41,7 @@ SPAN_DECLARE(filter_t *) filter_create(fspec_t *fs) int i; filter_t *fi; - if ((fi = (filter_t *) malloc(sizeof(*fi) + sizeof(float)*(fs->np + 1)))) + if ((fi = (filter_t *) span_alloc(sizeof(*fi) + sizeof(float)*(fs->np + 1)))) { fi->fs = fs; fi->sum = 0.0; @@ -56,7 +57,7 @@ SPAN_DECLARE(filter_t *) filter_create(fspec_t *fs) SPAN_DECLARE(void) filter_delete(filter_t *fi) { if (fi) - free(fi); + span_free(fi); } /*- End of function --------------------------------------------------------*/ @@ -70,17 +71,17 @@ SPAN_DECLARE(cfilter_t *) cfilter_create(fspec_t *fs) { cfilter_t *cfi; - if ((cfi = (cfilter_t *) malloc(sizeof(*cfi)))) + if ((cfi = (cfilter_t *) span_alloc(sizeof(*cfi)))) { if ((cfi->ref = filter_create(fs)) == NULL) { - free(cfi); + span_free(cfi); return NULL; } if ((cfi->imf = filter_create(fs)) == NULL) { - free(cfi->ref); - free(cfi); + span_free(cfi->ref); + span_free(cfi); return NULL; } } diff --git a/libs/spandsp/src/dtmf.c b/libs/spandsp/src/dtmf.c index 991f3d6a85..e58552de58 100644 --- a/libs/spandsp/src/dtmf.c +++ b/libs/spandsp/src/dtmf.c @@ -43,6 +43,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/fast_convert.h" #include "spandsp/queue.h" @@ -413,7 +414,7 @@ SPAN_DECLARE(dtmf_rx_state_t *) dtmf_rx_init(dtmf_rx_state_t *s, if (s == NULL) { - if ((s = (dtmf_rx_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (dtmf_rx_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -466,7 +467,7 @@ SPAN_DECLARE(int) dtmf_rx_release(dtmf_rx_state_t *s) SPAN_DECLARE(int) dtmf_rx_free(dtmf_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -578,7 +579,7 @@ SPAN_DECLARE(dtmf_tx_state_t *) dtmf_tx_init(dtmf_tx_state_t *s, { if (s == NULL) { - if ((s = (dtmf_tx_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (dtmf_tx_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -603,7 +604,7 @@ SPAN_DECLARE(int) dtmf_tx_release(dtmf_tx_state_t *s) SPAN_DECLARE(int) dtmf_tx_free(dtmf_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/echo.c b/libs/spandsp/src/echo.c index 84926fa3c4..18c4ba4c1f 100644 --- a/libs/spandsp/src/echo.c +++ b/libs/spandsp/src/echo.c @@ -93,6 +93,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/saturated.h" @@ -244,26 +245,26 @@ SPAN_DECLARE(echo_can_state_t *) echo_can_init(int len, int adaption_mode) int i; int j; - if ((ec = (echo_can_state_t *) malloc(sizeof(*ec))) == NULL) + if ((ec = (echo_can_state_t *) span_alloc(sizeof(*ec))) == NULL) return NULL; memset(ec, 0, sizeof(*ec)); ec->taps = len; ec->curr_pos = ec->taps - 1; ec->tap_mask = ec->taps - 1; - if ((ec->fir_taps32 = (int32_t *) malloc(ec->taps*sizeof(int32_t))) == NULL) + if ((ec->fir_taps32 = (int32_t *) span_alloc(ec->taps*sizeof(int32_t))) == NULL) { - free(ec); + span_free(ec); return NULL; } memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t)); for (i = 0; i < 4; i++) { - if ((ec->fir_taps16[i] = (int16_t *) malloc(ec->taps*sizeof(int16_t))) == NULL) + if ((ec->fir_taps16[i] = (int16_t *) span_alloc(ec->taps*sizeof(int16_t))) == NULL) { for (j = 0; j < i; j++) - free(ec->fir_taps16[j]); - free(ec->fir_taps32); - free(ec); + span_free(ec->fir_taps16[j]); + span_free(ec->fir_taps32); + span_free(ec); return NULL; } memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t)); @@ -294,10 +295,10 @@ SPAN_DECLARE(int) echo_can_free(echo_can_state_t *ec) int i; fir16_free(&ec->fir_state); - free(ec->fir_taps32); + span_free(ec->fir_taps32); for (i = 0; i < 4; i++) - free(ec->fir_taps16[i]); - free(ec); + span_free(ec->fir_taps16[i]); + span_free(ec); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/fax.c b/libs/spandsp/src/fax.c index ef7e33433a..b07aa002c5 100644 --- a/libs/spandsp/src/fax.c +++ b/libs/spandsp/src/fax.c @@ -50,6 +50,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/dc_restore.h" @@ -470,7 +471,7 @@ SPAN_DECLARE(fax_state_t *) fax_init(fax_state_t *s, int calling_party) if (s == NULL) { - if ((s = (fax_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (fax_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -525,7 +526,7 @@ SPAN_DECLARE(int) fax_release(fax_state_t *s) SPAN_DECLARE(int) fax_free(fax_state_t *s) { t30_release(&s->t30); - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/fax_modems.c b/libs/spandsp/src/fax_modems.c index 3cf658e4df..69698bcaeb 100644 --- a/libs/spandsp/src/fax_modems.c +++ b/libs/spandsp/src/fax_modems.c @@ -48,6 +48,7 @@ #endif #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/dc_restore.h" @@ -503,7 +504,7 @@ SPAN_DECLARE(fax_modems_state_t *) fax_modems_init(fax_modems_state_t *s, { if (s == NULL) { - if ((s = (fax_modems_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (fax_modems_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } /*endif*/ @@ -562,7 +563,7 @@ SPAN_DECLARE(int) fax_modems_release(fax_modems_state_t *s) SPAN_DECLARE(int) fax_modems_free(fax_modems_state_t *s) { if (s) - free(s); + span_free(s); /*endif*/ return 0; } diff --git a/libs/spandsp/src/g711.c b/libs/spandsp/src/g711.c index 173bd2ae53..edccd27bb1 100644 --- a/libs/spandsp/src/g711.c +++ b/libs/spandsp/src/g711.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/bit_operations.h" #include "spandsp/g711.h" #include "spandsp/private/g711.h" @@ -171,7 +172,7 @@ SPAN_DECLARE(g711_state_t *) g711_init(g711_state_t *s, int mode) { if (s == NULL) { - if ((s = (g711_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (g711_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->mode = mode; @@ -187,7 +188,7 @@ SPAN_DECLARE(int) g711_release(g711_state_t *s) SPAN_DECLARE(int) g711_free(g711_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/g722.c b/libs/spandsp/src/g722.c index 22715cfa4a..bbf3bbfc0f 100644 --- a/libs/spandsp/src/g722.c +++ b/libs/spandsp/src/g722.c @@ -41,6 +41,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/vector_int.h" @@ -251,7 +252,7 @@ SPAN_DECLARE(g722_decode_state_t *) g722_decode_init(g722_decode_state_t *s, int { if (s == NULL) { - if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (g722_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -281,7 +282,7 @@ SPAN_DECLARE(int) g722_decode_release(g722_decode_state_t *s) SPAN_DECLARE(int) g722_decode_free(g722_decode_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -432,7 +433,7 @@ SPAN_DECLARE(g722_encode_state_t *) g722_encode_init(g722_encode_state_t *s, int { if (s == NULL) { - if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (g722_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -462,7 +463,7 @@ SPAN_DECLARE(int) g722_encode_release(g722_encode_state_t *s) SPAN_DECLARE(int) g722_encode_free(g722_encode_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/g726.c b/libs/spandsp/src/g726.c index 6be385fc5f..ba1c5b913e 100644 --- a/libs/spandsp/src/g726.c +++ b/libs/spandsp/src/g726.c @@ -67,6 +67,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/bitstream.h" #include "spandsp/bit_operations.h" #include "spandsp/g711.h" @@ -1002,7 +1003,7 @@ SPAN_DECLARE(g726_state_t *) g726_init(g726_state_t *s, int bit_rate, int ext_co return NULL; if (s == NULL) { - if ((s = (g726_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (g726_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->yl = 34816; @@ -1062,7 +1063,7 @@ SPAN_DECLARE(int) g726_release(g726_state_t *s) SPAN_DECLARE(int) g726_free(g726_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/gsm0610_encode.c b/libs/spandsp/src/gsm0610_encode.c index 2c359688fe..eeac7d2881 100644 --- a/libs/spandsp/src/gsm0610_encode.c +++ b/libs/spandsp/src/gsm0610_encode.c @@ -45,6 +45,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/bitstream.h" #include "spandsp/saturated.h" @@ -115,7 +116,7 @@ SPAN_DECLARE(gsm0610_state_t *) gsm0610_init(gsm0610_state_t *s, int packing) { if (s == NULL) { - if ((s = (gsm0610_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (gsm0610_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; /*endif*/ } @@ -136,7 +137,7 @@ SPAN_DECLARE(int) gsm0610_release(gsm0610_state_t *s) SPAN_DECLARE(int) gsm0610_free(gsm0610_state_t *s) { if (s) - free(s); + span_free(s); /*endif*/ return 0; } diff --git a/libs/spandsp/src/logging.c b/libs/spandsp/src/logging.c index 3f9aa58f17..68975b0e04 100644 --- a/libs/spandsp/src/logging.c +++ b/libs/spandsp/src/logging.c @@ -42,6 +42,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/private/logging.h" @@ -229,7 +230,7 @@ SPAN_DECLARE(logging_state_t *) span_log_init(logging_state_t *s, int level, con { if (s == NULL) { - if ((s = (logging_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (logging_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->span_message = __span_message; @@ -252,7 +253,7 @@ SPAN_DECLARE(int) span_log_release(logging_state_t *s) SPAN_DECLARE(int) span_log_free(logging_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/lpc10_decode.c b/libs/spandsp/src/lpc10_decode.c index 19860e149d..eb6d9968cb 100644 --- a/libs/spandsp/src/lpc10_decode.c +++ b/libs/spandsp/src/lpc10_decode.c @@ -44,6 +44,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/lpc10.h" #include "spandsp/private/lpc10.h" @@ -1016,7 +1017,7 @@ SPAN_DECLARE(lpc10_decode_state_t *) lpc10_decode_init(lpc10_decode_state_t *s, if (s == NULL) { - if ((s = (lpc10_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (lpc10_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } @@ -1084,7 +1085,7 @@ SPAN_DECLARE(int) lpc10_decode_release(lpc10_decode_state_t *s) SPAN_DECLARE(int) lpc10_decode_free(lpc10_decode_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/lpc10_encode.c b/libs/spandsp/src/lpc10_encode.c index 739734f866..976fc602d0 100644 --- a/libs/spandsp/src/lpc10_encode.c +++ b/libs/spandsp/src/lpc10_encode.c @@ -44,6 +44,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/lpc10.h" #include "spandsp/private/lpc10.h" @@ -269,7 +270,7 @@ SPAN_DECLARE(lpc10_encode_state_t *) lpc10_encode_init(lpc10_encode_state_t *s, if (s == NULL) { - if ((s = (lpc10_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (lpc10_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } @@ -367,7 +368,7 @@ SPAN_DECLARE(int) lpc10_encode_release(lpc10_encode_state_t *s) SPAN_DECLARE(int) lpc10_encode_free(lpc10_encode_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/modem_connect_tones.c b/libs/spandsp/src/modem_connect_tones.c index 76b42a23fc..6a08781f66 100644 --- a/libs/spandsp/src/modem_connect_tones.c +++ b/libs/spandsp/src/modem_connect_tones.c @@ -57,6 +57,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/complex.h" @@ -258,7 +259,7 @@ SPAN_DECLARE(modem_connect_tones_tx_state_t *) modem_connect_tones_tx_init(modem alloced = FALSE; if (s == NULL) { - if ((s = (modem_connect_tones_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (modem_connect_tones_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; alloced = TRUE; } @@ -341,7 +342,7 @@ SPAN_DECLARE(modem_connect_tones_tx_state_t *) modem_connect_tones_tx_init(modem break; default: if (alloced) - free(s); + span_free(s); return NULL; } return s; @@ -356,7 +357,7 @@ SPAN_DECLARE(int) modem_connect_tones_tx_release(modem_connect_tones_tx_state_t SPAN_DECLARE(int) modem_connect_tones_tx_free(modem_connect_tones_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -716,7 +717,7 @@ SPAN_DECLARE(modem_connect_tones_rx_state_t *) modem_connect_tones_rx_init(modem { if (s == NULL) { - if ((s = (modem_connect_tones_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (modem_connect_tones_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } @@ -765,7 +766,7 @@ SPAN_DECLARE(int) modem_connect_tones_rx_release(modem_connect_tones_rx_state_t SPAN_DECLARE(int) modem_connect_tones_rx_free(modem_connect_tones_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/modem_echo.c b/libs/spandsp/src/modem_echo.c index 14257c7eb7..59bf57b544 100644 --- a/libs/spandsp/src/modem_echo.c +++ b/libs/spandsp/src/modem_echo.c @@ -47,6 +47,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/bit_operations.h" #include "spandsp/dc_restore.h" #include "spandsp/modem_echo.h" @@ -66,29 +67,29 @@ SPAN_DECLARE(modem_echo_can_state_t *) modem_echo_can_init(int len) { modem_echo_can_state_t *ec; - if ((ec = (modem_echo_can_state_t *) malloc(sizeof(*ec))) == NULL) + if ((ec = (modem_echo_can_state_t *) span_alloc(sizeof(*ec))) == NULL) return NULL; memset(ec, 0, sizeof(*ec)); ec->taps = len; ec->curr_pos = ec->taps - 1; - if ((ec->fir_taps32 = (int32_t *) malloc(ec->taps*sizeof(int32_t))) == NULL) + if ((ec->fir_taps32 = (int32_t *) span_alloc(ec->taps*sizeof(int32_t))) == NULL) { free(ec); return NULL; } memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t)); - if ((ec->fir_taps16 = (int16_t *) malloc(ec->taps*sizeof(int16_t))) == NULL) + if ((ec->fir_taps16 = (int16_t *) span_alloc(ec->taps*sizeof(int16_t))) == NULL) { - free(ec->fir_taps32); - free(ec); + span_free(ec->fir_taps32); + span_free(ec); return NULL; } memset(ec->fir_taps16, 0, ec->taps*sizeof(int16_t)); if (fir16_create(&ec->fir_state, ec->fir_taps16, ec->taps) == NULL) { - free(ec->fir_taps16); - free(ec->fir_taps32); - free(ec); + span_free(ec->fir_taps16); + span_free(ec->fir_taps32); + span_free(ec); return NULL; } return ec; diff --git a/libs/spandsp/src/noise.c b/libs/spandsp/src/noise.c index 5d7fe23aad..3ad164cf7f 100644 --- a/libs/spandsp/src/noise.c +++ b/libs/spandsp/src/noise.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/noise.h" @@ -84,7 +85,7 @@ SPAN_DECLARE(noise_state_t *) noise_init_dbov(noise_state_t *s, int seed, float if (s == NULL) { - if ((s = (noise_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (noise_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -121,7 +122,7 @@ SPAN_DECLARE(int) noise_release(noise_state_t *s) SPAN_DECLARE(int) noise_free(noise_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/oki_adpcm.c b/libs/spandsp/src/oki_adpcm.c index f6d54b804c..0e5e8f6659 100644 --- a/libs/spandsp/src/oki_adpcm.c +++ b/libs/spandsp/src/oki_adpcm.c @@ -39,6 +39,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/oki_adpcm.h" #include "spandsp/private/oki_adpcm.h" @@ -246,7 +247,7 @@ SPAN_DECLARE(oki_adpcm_state_t *) oki_adpcm_init(oki_adpcm_state_t *s, int bit_r return NULL; if (s == NULL) { - if ((s = (oki_adpcm_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (oki_adpcm_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -264,7 +265,7 @@ SPAN_DECLARE(int) oki_adpcm_release(oki_adpcm_state_t *s) SPAN_DECLARE(int) oki_adpcm_free(oki_adpcm_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/playout.c b/libs/spandsp/src/playout.c index e98e48d70c..60a8d97674 100644 --- a/libs/spandsp/src/playout.c +++ b/libs/spandsp/src/playout.c @@ -41,6 +41,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/playout.h" static playout_frame_t *queue_get(playout_state_t *s, timestamp_t sender_stamp) @@ -236,7 +237,7 @@ SPAN_DECLARE(int) playout_put(playout_state_t *s, void *data, int type, timestam } else { - if ((frame = (playout_frame_t *) malloc(sizeof(*frame))) == NULL) + if ((frame = (playout_frame_t *) span_alloc(sizeof(*frame))) == NULL) return PLAYOUT_ERROR; } @@ -312,7 +313,7 @@ SPAN_DECLARE(void) playout_restart(playout_state_t *s, int min_length, int max_l for (frame = s->free_frames; frame; frame = next) { next = frame->later; - free(frame); + span_free(frame); } memset(s, 0, sizeof(*s)); @@ -332,7 +333,7 @@ SPAN_DECLARE(playout_state_t *) playout_init(int min_length, int max_length) { playout_state_t *s; - if ((s = (playout_state_t *) malloc(sizeof(playout_state_t))) == NULL) + if ((s = (playout_state_t *) span_alloc(sizeof(playout_state_t))) == NULL) return NULL; memset(s, 0, sizeof(*s)); playout_restart(s, min_length, max_length); @@ -350,13 +351,13 @@ SPAN_DECLARE(int) playout_release(playout_state_t *s) for (frame = s->first_frame; frame; frame = next) { next = frame->later; - free(frame); + span_free(frame); } /* Free all the frames on the free list */ for (frame = s->free_frames; frame; frame = next) { next = frame->later; - free(frame); + span_free(frame); } return 0; } @@ -368,7 +369,7 @@ SPAN_DECLARE(int) playout_free(playout_state_t *s) { playout_release(s); /* Finally, free ourselves! */ - free(s); + span_free(s); } return 0; } diff --git a/libs/spandsp/src/plc.c b/libs/spandsp/src/plc.c index dcb07120e1..840682a8fc 100644 --- a/libs/spandsp/src/plc.c +++ b/libs/spandsp/src/plc.c @@ -43,6 +43,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/plc.h" @@ -234,7 +235,7 @@ SPAN_DECLARE(plc_state_t *) plc_init(plc_state_t *s) { if (s == NULL) { - if ((s = (plc_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (plc_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -251,7 +252,7 @@ SPAN_DECLARE(int) plc_release(plc_state_t *s) SPAN_DECLARE(int) plc_free(plc_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/power_meter.c b/libs/spandsp/src/power_meter.c index f4fb2afa8f..9843930215 100644 --- a/libs/spandsp/src/power_meter.c +++ b/libs/spandsp/src/power_meter.c @@ -45,13 +45,14 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/power_meter.h" SPAN_DECLARE(power_meter_t *) power_meter_init(power_meter_t *s, int shift) { if (s == NULL) { - if ((s = (power_meter_t *) malloc(sizeof(*s))) == NULL) + if ((s = (power_meter_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } s->shift = shift; @@ -69,7 +70,7 @@ SPAN_DECLARE(int) power_meter_release(power_meter_t *s) SPAN_DECLARE(int) power_meter_free(power_meter_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -181,7 +182,7 @@ SPAN_DECLARE(power_surge_detector_state_t *) power_surge_detector_init(power_sur if (s == NULL) { - if ((s = (power_surge_detector_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (power_surge_detector_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -205,7 +206,7 @@ SPAN_DECLARE(int) power_surge_detector_release(power_surge_detector_state_t *s) SPAN_DECLARE(int) power_surge_detector_free(power_surge_detector_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/queue.c b/libs/spandsp/src/queue.c index 264cee0c87..3f55e833ea 100644 --- a/libs/spandsp/src/queue.c +++ b/libs/spandsp/src/queue.c @@ -39,6 +39,7 @@ #define SPANDSP_FULLY_DEFINE_QUEUE_STATE_T #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/queue.h" #include "spandsp/private/queue.h" @@ -395,7 +396,7 @@ SPAN_DECLARE(queue_state_t *) queue_init(queue_state_t *s, int len, int flags) { if (s == NULL) { - if ((s = (queue_state_t *) malloc(sizeof(*s) + len + 1)) == NULL) + if ((s = (queue_state_t *) span_alloc(sizeof(*s) + len + 1)) == NULL) return NULL; } s->iptr = @@ -414,7 +415,7 @@ SPAN_DECLARE(int) queue_release(queue_state_t *s) SPAN_DECLARE(int) queue_free(queue_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/schedule.c b/libs/spandsp/src/schedule.c index 60ba12bc1f..f65b5d7764 100644 --- a/libs/spandsp/src/schedule.c +++ b/libs/spandsp/src/schedule.c @@ -33,6 +33,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/schedule.h" @@ -53,7 +54,7 @@ SPAN_DECLARE(int) span_schedule_event(span_sched_state_t *s, int us, span_sched_ if (i >= s->allocated) { s->allocated += 5; - s->sched = (span_sched_t *) realloc(s->sched, sizeof(span_sched_t)*s->allocated); + s->sched = (span_sched_t *) span_realloc(s->sched, sizeof(span_sched_t)*s->allocated); } /*endif*/ if (i >= s->max_to_date) @@ -141,7 +142,7 @@ SPAN_DECLARE(int) span_schedule_release(span_sched_state_t *s) { if (s->sched) { - free(s->sched); + span_free(s->sched); s->sched = NULL; } return 0; @@ -152,7 +153,7 @@ SPAN_DECLARE(int) span_schedule_free(span_sched_state_t *s) { span_schedule_release(s); if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/sig_tone.c b/libs/spandsp/src/sig_tone.c index 5e4e180178..698c07180e 100644 --- a/libs/spandsp/src/sig_tone.c +++ b/libs/spandsp/src/sig_tone.c @@ -44,6 +44,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/vector_int.h" @@ -325,7 +326,7 @@ SPAN_DECLARE(sig_tone_tx_state_t *) sig_tone_tx_init(sig_tone_tx_state_t *s, int if (s == NULL) { - if ((s = (sig_tone_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (sig_tone_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -357,7 +358,7 @@ SPAN_DECLARE(int) sig_tone_tx_release(sig_tone_tx_state_t *s) SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -641,7 +642,7 @@ SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int if (s == NULL) { - if ((s = (sig_tone_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (sig_tone_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -685,7 +686,7 @@ SPAN_DECLARE(int) sig_tone_rx_release(sig_tone_rx_state_t *s) SPAN_DECLARE(int) sig_tone_rx_free(sig_tone_rx_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/silence_gen.c b/libs/spandsp/src/silence_gen.c index ad7d7caed0..d89f9c8e42 100644 --- a/libs/spandsp/src/silence_gen.c +++ b/libs/spandsp/src/silence_gen.c @@ -45,6 +45,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/async.h" #include "spandsp/silence_gen.h" @@ -119,7 +120,7 @@ SPAN_DECLARE(silence_gen_state_t *) silence_gen_init(silence_gen_state_t *s, int { if (s == NULL) { - if ((s = (silence_gen_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (silence_gen_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -137,7 +138,7 @@ SPAN_DECLARE(int) silence_gen_release(silence_gen_state_t *s) SPAN_DECLARE(int) silence_gen_free(silence_gen_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/super_tone_rx.c b/libs/spandsp/src/super_tone_rx.c index e46faa8148..eabebe6a30 100644 --- a/libs/spandsp/src/super_tone_rx.c +++ b/libs/spandsp/src/super_tone_rx.c @@ -45,6 +45,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/complex.h" #include "spandsp/vector_float.h" @@ -95,7 +96,7 @@ static int add_super_tone_freq(super_tone_rx_descriptor_t *desc, int freq) desc->pitches[i][1] = desc->monitored_frequencies; if (desc->monitored_frequencies%5 == 0) { - desc->desc = (goertzel_descriptor_t *) realloc(desc->desc, (desc->monitored_frequencies + 5)*sizeof(goertzel_descriptor_t)); + desc->desc = (goertzel_descriptor_t *) span_realloc(desc->desc, (desc->monitored_frequencies + 5)*sizeof(goertzel_descriptor_t)); } make_goertzel_descriptor(&desc->desc[desc->monitored_frequencies++], (float) freq, SUPER_TONE_BINS); desc->used_frequencies++; @@ -107,8 +108,8 @@ SPAN_DECLARE(int) super_tone_rx_add_tone(super_tone_rx_descriptor_t *desc) { if (desc->tones%5 == 0) { - desc->tone_list = (super_tone_rx_segment_t **) realloc(desc->tone_list, (desc->tones + 5)*sizeof(super_tone_rx_segment_t *)); - desc->tone_segs = (int *) realloc(desc->tone_segs, (desc->tones + 5)*sizeof(int)); + desc->tone_list = (super_tone_rx_segment_t **) span_realloc(desc->tone_list, (desc->tones + 5)*sizeof(super_tone_rx_segment_t *)); + desc->tone_segs = (int *) span_realloc(desc->tone_segs, (desc->tones + 5)*sizeof(int)); } desc->tone_list[desc->tones] = NULL; desc->tone_segs[desc->tones] = 0; @@ -129,7 +130,7 @@ SPAN_DECLARE(int) super_tone_rx_add_element(super_tone_rx_descriptor_t *desc, step = desc->tone_segs[tone]; if (step%5 == 0) { - desc->tone_list[tone] = (super_tone_rx_segment_t *) realloc(desc->tone_list[tone], (step + 5)*sizeof(super_tone_rx_segment_t)); + desc->tone_list[tone] = (super_tone_rx_segment_t *) span_realloc(desc->tone_list[tone], (step + 5)*sizeof(super_tone_rx_segment_t)); } desc->tone_list[tone][step].f1 = add_super_tone_freq(desc, f1); desc->tone_list[tone][step].f2 = add_super_tone_freq(desc, f2); @@ -199,7 +200,7 @@ SPAN_DECLARE(super_tone_rx_descriptor_t *) super_tone_rx_make_descriptor(super_t { if (desc == NULL) { - if ((desc = (super_tone_rx_descriptor_t *) malloc(sizeof(*desc))) == NULL) + if ((desc = (super_tone_rx_descriptor_t *) span_alloc(sizeof(*desc))) == NULL) return NULL; } desc->tone_list = NULL; @@ -222,15 +223,15 @@ SPAN_DECLARE(int) super_tone_rx_free_descriptor(super_tone_rx_descriptor_t *desc for (i = 0; i < desc->tones; i++) { if (desc->tone_list[i]) - free(desc->tone_list[i]); + span_free(desc->tone_list[i]); } if (desc->tone_list) - free(desc->tone_list); + span_free(desc->tone_list); if (desc->tone_segs) - free(desc->tone_segs); + span_free(desc->tone_segs); if (desc->desc) - free(desc->desc); - free(desc); + span_free(desc->desc); + span_free(desc); } return 0; } @@ -265,7 +266,7 @@ SPAN_DECLARE(super_tone_rx_state_t *) super_tone_rx_init(super_tone_rx_state_t * return NULL; if (s == NULL) { - if ((s = (super_tone_rx_state_t *) malloc(sizeof(*s) + desc->monitored_frequencies*sizeof(goertzel_state_t))) == NULL) + if ((s = (super_tone_rx_state_t *) span_alloc(sizeof(*s) + desc->monitored_frequencies*sizeof(goertzel_state_t))) == NULL) return NULL; } @@ -301,7 +302,7 @@ SPAN_DECLARE(int) super_tone_rx_release(super_tone_rx_state_t *s) SPAN_DECLARE(int) super_tone_rx_free(super_tone_rx_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/super_tone_tx.c b/libs/spandsp/src/super_tone_tx.c index 3809bb4bf8..5b8ab4303d 100644 --- a/libs/spandsp/src/super_tone_tx.c +++ b/libs/spandsp/src/super_tone_tx.c @@ -45,6 +45,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/complex.h" #include "spandsp/dds.h" @@ -78,7 +79,7 @@ SPAN_DECLARE(super_tone_tx_step_t *) super_tone_tx_make_step(super_tone_tx_step_ { if (s == NULL) { - if ((s = (super_tone_tx_step_t *) malloc(sizeof(*s))) == NULL) + if ((s = (super_tone_tx_step_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } if (f1 >= 1.0f) @@ -121,7 +122,7 @@ SPAN_DECLARE(int) super_tone_tx_free_tone(super_tone_tx_step_t *s) super_tone_tx_free_tone(s->nest); t = s; s = s->next; - free(t); + span_free(t); } return 0; } @@ -133,7 +134,7 @@ SPAN_DECLARE(super_tone_tx_state_t *) super_tone_tx_init(super_tone_tx_state_t * return NULL; if (s == NULL) { - if ((s = (super_tone_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (super_tone_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -155,7 +156,7 @@ SPAN_DECLARE(int) super_tone_tx_release(super_tone_tx_state_t *s) SPAN_DECLARE(int) super_tone_tx_free(super_tone_tx_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/swept_tone.c b/libs/spandsp/src/swept_tone.c index f5ccc9bed8..20eb7d5de9 100644 --- a/libs/spandsp/src/swept_tone.c +++ b/libs/spandsp/src/swept_tone.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/complex.h" #include "spandsp/vector_float.h" @@ -59,7 +60,7 @@ SPAN_DECLARE(swept_tone_state_t *) swept_tone_init(swept_tone_state_t *s, float { if (s == NULL) { - if ((s = (swept_tone_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (swept_tone_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -119,7 +120,7 @@ SPAN_DECLARE(int) swept_tone_release(swept_tone_state_t *s) SPAN_DECLARE(int) swept_tone_free(swept_tone_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t30_api.c b/libs/spandsp/src/t30_api.c index 7ab68677c3..d00ede33d2 100644 --- a/libs/spandsp/src/t30_api.c +++ b/libs/spandsp/src/t30_api.c @@ -45,6 +45,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/queue.h" @@ -277,8 +278,8 @@ SPAN_DECLARE(const char *) t30_get_rx_password(t30_state_t *s) SPAN_DECLARE(int) t30_set_tx_nsf(t30_state_t *s, const uint8_t *nsf, int len) { if (s->tx_info.nsf) - free(s->tx_info.nsf); - if (nsf && len > 0 && (s->tx_info.nsf = malloc(len + 3))) + span_free(s->tx_info.nsf); + if (nsf && len > 0 && (s->tx_info.nsf = span_alloc(len + 3))) { memcpy(s->tx_info.nsf + 3, nsf, len); s->tx_info.nsf_len = len; @@ -311,8 +312,8 @@ SPAN_DECLARE(size_t) t30_get_rx_nsf(t30_state_t *s, const uint8_t *nsf[]) SPAN_DECLARE(int) t30_set_tx_nsc(t30_state_t *s, const uint8_t *nsc, int len) { if (s->tx_info.nsc) - free(s->tx_info.nsc); - if (nsc && len > 0 && (s->tx_info.nsc = malloc(len + 3))) + span_free(s->tx_info.nsc); + if (nsc && len > 0 && (s->tx_info.nsc = span_alloc(len + 3))) { memcpy(s->tx_info.nsc + 3, nsc, len); s->tx_info.nsc_len = len; @@ -345,8 +346,8 @@ SPAN_DECLARE(size_t) t30_get_rx_nsc(t30_state_t *s, const uint8_t *nsc[]) SPAN_DECLARE(int) t30_set_tx_nss(t30_state_t *s, const uint8_t *nss, int len) { if (s->tx_info.nss) - free(s->tx_info.nss); - if (nss && len > 0 && (s->tx_info.nss = malloc(len + 3))) + span_free(s->tx_info.nss); + if (nss && len > 0 && (s->tx_info.nss = span_alloc(len + 3))) { memcpy(s->tx_info.nss + 3, nss, len); s->tx_info.nss_len = len; @@ -379,7 +380,7 @@ SPAN_DECLARE(size_t) t30_get_rx_nss(t30_state_t *s, const uint8_t *nss[]) SPAN_DECLARE(int) t30_set_tx_tsa(t30_state_t *s, int type, const char *address, int len) { if (s->tx_info.tsa) - free(s->tx_info.tsa); + span_free(s->tx_info.tsa); if (address == NULL || len == 0) { s->tx_info.tsa = NULL; @@ -389,7 +390,7 @@ SPAN_DECLARE(int) t30_set_tx_tsa(t30_state_t *s, int type, const char *address, s->tx_info.tsa_type = type; if (len < 0) len = strlen(address); - if ((s->tx_info.tsa = malloc(len))) + if ((s->tx_info.tsa = span_alloc(len))) { memcpy(s->tx_info.tsa, address, len); s->tx_info.tsa_len = len; @@ -421,7 +422,7 @@ SPAN_DECLARE(size_t) t30_get_rx_tsa(t30_state_t *s, int *type, const char *addre SPAN_DECLARE(int) t30_set_tx_ira(t30_state_t *s, int type, const char *address, int len) { if (s->tx_info.ira) - free(s->tx_info.ira); + span_free(s->tx_info.ira); if (address == NULL) { s->tx_info.ira = NULL; @@ -455,7 +456,7 @@ SPAN_DECLARE(size_t) t30_get_rx_ira(t30_state_t *s, int *type, const char *addre SPAN_DECLARE(int) t30_set_tx_cia(t30_state_t *s, int type, const char *address, int len) { if (s->tx_info.cia) - free(s->tx_info.cia); + span_free(s->tx_info.cia); if (address == NULL) { s->tx_info.cia = NULL; @@ -489,7 +490,7 @@ SPAN_DECLARE(size_t) t30_get_rx_cia(t30_state_t *s, int *type, const char *addre SPAN_DECLARE(int) t30_set_tx_isp(t30_state_t *s, int type, const char *address, int len) { if (s->tx_info.isp) - free(s->tx_info.isp); + span_free(s->tx_info.isp); if (address == NULL) { s->tx_info.isp = NULL; @@ -523,7 +524,7 @@ SPAN_DECLARE(size_t) t30_get_rx_isp(t30_state_t *s, int *type, const char *addre SPAN_DECLARE(int) t30_set_tx_csa(t30_state_t *s, int type, const char *address, int len) { if (s->tx_info.csa) - free(s->tx_info.csa); + span_free(s->tx_info.csa); if (address == NULL) { s->tx_info.csa = NULL; diff --git a/libs/spandsp/src/t81_t82_arith_coding.c b/libs/spandsp/src/t81_t82_arith_coding.c index 86e7e0271f..5d51bbc25d 100644 --- a/libs/spandsp/src/t81_t82_arith_coding.c +++ b/libs/spandsp/src/t81_t82_arith_coding.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/t81_t82_arith_coding.h" #include "spandsp/private/t81_t82_arith_coding.h" @@ -341,7 +342,7 @@ SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_a { if (s == NULL) { - if ((s = (t81_t82_arith_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t81_t82_arith_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -364,7 +365,7 @@ SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s) int ret; ret = t81_t82_arith_encode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ @@ -481,7 +482,7 @@ SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_a { if (s == NULL) { - if ((s = (t81_t82_arith_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t81_t82_arith_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -502,7 +503,7 @@ SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s) int ret; ret = t81_t82_arith_decode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t85_decode.c b/libs/spandsp/src/t85_decode.c index 93efc06e81..f48502c9d9 100644 --- a/libs/spandsp/src/t85_decode.c +++ b/libs/spandsp/src/t85_decode.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/async.h" #include "spandsp/timezone.h" @@ -399,7 +400,7 @@ SPAN_DECLARE(int) t85_decode_put(t85_decode_state_t *s, const uint8_t data[], si if (min_len > s->row_buf_len) { /* We need to expand the 3 row buffer */ - if ((buf = (uint8_t *) realloc(s->row_buf, min_len)) == NULL) + if ((buf = (uint8_t *) span_realloc(s->row_buf, min_len)) == NULL) return T4_DECODE_NOMEM; s->row_buf = buf; s->row_buf_len = min_len; @@ -409,7 +410,7 @@ SPAN_DECLARE(int) t85_decode_put(t85_decode_state_t *s, const uint8_t data[], si s->s.nopadding = s->options & T85_VLENGTH; if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } s->comment_len = 0; @@ -455,7 +456,7 @@ SPAN_DECLARE(int) t85_decode_put(t85_decode_state_t *s, const uint8_t data[], si s->interrupt = s->comment_handler(s->comment_user_data, s->comment, s->comment_len); if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } s->comment_len = 0; @@ -506,12 +507,12 @@ SPAN_DECLARE(int) t85_decode_put(t85_decode_state_t *s, const uint8_t data[], si s->comment_len = pack_32(&s->buffer[2]); /* Only try to buffer and process the comment's contents if we have a defined callback routine to do something with it. */ - /* If this malloc fails we carry on working just fine, and don't try to + /* If this allocate fails we carry on working just fine, and don't try to process the contents of the comment. That is fairly benign, as the comments are not generally of critical importance, so let's not worry. */ if (s->comment_handler && s->comment_len > 0 && s->comment_len <= s->max_comment_len) - s->comment = malloc(s->comment_len); + s->comment = span_alloc(s->comment_len); s->comment_progress = 0; continue; case T82_ATMOVE: @@ -738,7 +739,7 @@ SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s) s->end_of_data = 0; if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } s->comment_len = 0; @@ -786,7 +787,7 @@ SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s) s->end_of_data = 0; if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } s->comment_len = 0; @@ -811,7 +812,7 @@ SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s, { if (s == NULL) { - if ((s = (t85_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t85_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -837,12 +838,12 @@ SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s) { if (s->row_buf) { - free(s->row_buf); + span_free(s->row_buf); s->row_buf = NULL; } if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } return 0; @@ -854,7 +855,7 @@ SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s) int ret; ret = t85_decode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t85_encode.c b/libs/spandsp/src/t85_encode.c index ed8e47364f..9b417a39fd 100644 --- a/libs/spandsp/src/t85_encode.c +++ b/libs/spandsp/src/t85_encode.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/async.h" #include "spandsp/timezone.h" @@ -78,7 +79,7 @@ static void put_stuff(t85_encode_state_t *s, const uint8_t buf[], int len) /* The number of uncompressed bytes per row seems like a reasonable measure of what to expect as a poor case for a compressed row. */ bytes_per_row = (s->xd + 7) >> 3; - if ((new_buf = realloc(s->bitstream, s->bitstream_len + len + bytes_per_row)) == NULL) + if ((new_buf = span_realloc(s->bitstream, s->bitstream_len + len + bytes_per_row)) == NULL) return; s->bitstream = new_buf; s->bitstream_len += (len + bytes_per_row); @@ -518,7 +519,7 @@ SPAN_DECLARE(int) t85_encode_set_image_width(t85_encode_state_t *s, uint32_t ima return -1; s->xd = image_width; bytes_per_row = (s->xd + 7) >> 3; - if ((t = (uint8_t *) realloc(s->row_buf, 3*bytes_per_row)) == NULL) + if ((t = (uint8_t *) span_realloc(s->row_buf, 3*bytes_per_row)) == NULL) return -1; s->row_buf = t; memset(s->row_buf, 0, 3*bytes_per_row); @@ -663,7 +664,7 @@ SPAN_DECLARE(int) t85_encode_restart(t85_encode_state_t *s, uint32_t image_width s->bitstream_optr = 0; if (s->bitstream) { - free(s->bitstream); + span_free(s->bitstream); s->bitstream = NULL; } s->bitstream_len = 0; @@ -689,7 +690,7 @@ SPAN_DECLARE(t85_encode_state_t *) t85_encode_init(t85_encode_state_t *s, { if (s == NULL) { - if ((s = (t85_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t85_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -722,12 +723,12 @@ SPAN_DECLARE(int) t85_encode_release(t85_encode_state_t *s) { if (s->row_buf) { - free(s->row_buf); + span_free(s->row_buf); s->row_buf = NULL; } if (s->bitstream) { - free(s->bitstream); + span_free(s->bitstream); s->bitstream = NULL; s->bitstream_len = 0; } @@ -740,7 +741,7 @@ SPAN_DECLARE(int) t85_encode_free(t85_encode_state_t *s) int ret; ret = t85_encode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/time_scale.c b/libs/spandsp/src/time_scale.c index 5b2a64a66b..a817834d22 100644 --- a/libs/spandsp/src/time_scale.c +++ b/libs/spandsp/src/time_scale.c @@ -45,6 +45,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/time_scale.h" #include "spandsp/saturated.h" @@ -132,7 +133,7 @@ SPAN_DECLARE(time_scale_state_t *) time_scale_init(time_scale_state_t *s, int sa alloced = FALSE; if (s == NULL) { - if ((s = (time_scale_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (time_scale_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; /*endif*/ alloced = TRUE; @@ -145,7 +146,7 @@ SPAN_DECLARE(time_scale_state_t *) time_scale_init(time_scale_state_t *s, int sa if (time_scale_rate(s, playout_rate)) { if (alloced) - free(s); + span_free(s); return NULL; } /*endif*/ @@ -164,7 +165,7 @@ SPAN_DECLARE(int) time_scale_release(time_scale_state_t *s) SPAN_DECLARE(int) time_scale_free(time_scale_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/timezone.c b/libs/spandsp/src/timezone.c index 79966f1d4c..158b0100da 100644 --- a/libs/spandsp/src/timezone.c +++ b/libs/spandsp/src/timezone.c @@ -47,6 +47,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/timezone.h" #include "spandsp/private/timezone.h" @@ -796,7 +797,7 @@ SPAN_DECLARE(tz_t *) tz_init(tz_t *tz, const char *tzstring) { if (tz == NULL) { - if ((tz = (tz_t *) malloc(sizeof(*tz))) == NULL) + if ((tz = (tz_t *) span_alloc(sizeof(*tz))) == NULL) return NULL; } memset(tz, 0, sizeof(*tz)); @@ -816,7 +817,7 @@ SPAN_DECLARE(int) tz_release(tz_t *tz) SPAN_DECLARE(int) tz_free(tz_t *tz) { if (tz) - free(tz); + span_free(tz); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/tone_detect.c b/libs/spandsp/src/tone_detect.c index 4dfea5d5bf..2865ba40d8 100644 --- a/libs/spandsp/src/tone_detect.c +++ b/libs/spandsp/src/tone_detect.c @@ -44,6 +44,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/complex.h" #include "spandsp/complex_vector_float.h" #include "spandsp/tone_detect.h" @@ -72,7 +73,7 @@ SPAN_DECLARE(goertzel_state_t *) goertzel_init(goertzel_state_t *s, { if (s == NULL) { - if ((s = (goertzel_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (goertzel_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } #if defined(SPANDSP_USE_FIXED_POINT) @@ -98,7 +99,7 @@ SPAN_DECLARE(int) goertzel_release(goertzel_state_t *s) SPAN_DECLARE(int) goertzel_free(goertzel_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/tone_generate.c b/libs/spandsp/src/tone_generate.c index 21285f6a96..be3f6854a4 100644 --- a/libs/spandsp/src/tone_generate.c +++ b/libs/spandsp/src/tone_generate.c @@ -44,6 +44,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/complex.h" #include "spandsp/dds.h" @@ -69,7 +70,7 @@ SPAN_DECLARE(tone_gen_descriptor_t *) tone_gen_descriptor_init(tone_gen_descript { if (s == NULL) { - if ((s = (tone_gen_descriptor_t *) malloc(sizeof(*s))) == NULL) + if ((s = (tone_gen_descriptor_t *) span_alloc(sizeof(*s))) == NULL) { return NULL; } @@ -114,7 +115,7 @@ SPAN_DECLARE(tone_gen_descriptor_t *) tone_gen_descriptor_init(tone_gen_descript SPAN_DECLARE(void) tone_gen_descriptor_free(tone_gen_descriptor_t *s) { - free(s); + span_free(s); } /*- End of function --------------------------------------------------------*/ @@ -220,7 +221,7 @@ SPAN_DECLARE(tone_gen_state_t *) tone_gen_init(tone_gen_state_t *s, tone_gen_des if (s == NULL) { - if ((s = (tone_gen_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (tone_gen_state_t *) span_alloc(sizeof(*s))) == NULL) { return NULL; } @@ -252,7 +253,7 @@ SPAN_DECLARE(int) tone_gen_release(tone_gen_state_t *s) SPAN_DECLARE(int) tone_gen_free(tone_gen_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v42.c b/libs/spandsp/src/v42.c index 29255eea07..33a4573052 100644 --- a/libs/spandsp/src/v42.c +++ b/libs/spandsp/src/v42.c @@ -41,6 +41,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -1509,7 +1510,7 @@ SPAN_DECLARE(v42_state_t *) v42_init(v42_state_t *ss, if (ss == NULL) { - if ((ss = (v42_state_t *) malloc(sizeof(*ss))) == NULL) + if ((ss = (v42_state_t *) span_alloc(sizeof(*ss))) == NULL) return NULL; } memset(ss, 0, sizeof(*ss)); @@ -1564,7 +1565,7 @@ SPAN_DECLARE(int) v42_release(v42_state_t *s) SPAN_DECLARE(int) v42_free(v42_state_t *s) { v42_release(s); - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v42bis.c b/libs/spandsp/src/v42bis.c index fec129a6f2..cc34c54be9 100644 --- a/libs/spandsp/src/v42bis.c +++ b/libs/spandsp/src/v42bis.c @@ -39,6 +39,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -729,7 +730,7 @@ SPAN_DECLARE(v42bis_state_t *) v42bis_init(v42bis_state_t *s, return NULL; if (s == NULL) { - if ((s = (v42bis_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v42bis_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -760,6 +761,7 @@ SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s) { comp_exit(&s->compress); comp_exit(&s->decompress); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v8.c b/libs/spandsp/src/v8.c index 3cf63ceacb..fb8436651c 100644 --- a/libs/spandsp/src/v8.c +++ b/libs/spandsp/src/v8.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/async.h" @@ -1078,7 +1079,7 @@ SPAN_DECLARE(v8_state_t *) v8_init(v8_state_t *s, { if (s == NULL) { - if ((s = (v8_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v8_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1103,7 +1104,7 @@ SPAN_DECLARE(int) v8_free(v8_state_t *s) int ret; ret = queue_free(s->tx_queue); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ From 799402dd5b83853379e70c6ca42d50bb019f67a7 Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 6 Aug 2013 01:10:48 +0800 Subject: [PATCH 231/278] More movements of spandsp to the sue of custom allocation routines --- libs/spandsp/src/fsk.c | 9 +++--- libs/spandsp/src/hdlc.c | 9 +++--- libs/spandsp/src/ima_adpcm.c | 5 ++-- libs/spandsp/src/image_translate.c | 13 +++++---- libs/spandsp/src/modem_echo.c | 8 +++--- libs/spandsp/src/t31.c | 7 +++-- libs/spandsp/src/t38_core.c | 5 ++-- libs/spandsp/src/t38_gateway.c | 5 ++-- libs/spandsp/src/t38_non_ecm_buffer.c | 5 ++-- libs/spandsp/src/t38_terminal.c | 5 ++-- libs/spandsp/src/t42.c | 41 ++++++++++++++------------- libs/spandsp/src/t43.c | 14 +++++---- libs/spandsp/src/t4_t6_decode.c | 17 +++++------ libs/spandsp/src/t4_t6_encode.c | 17 +++++------ libs/spandsp/src/v17tx.c | 5 ++-- libs/spandsp/src/v18.c | 5 ++-- libs/spandsp/src/v22bis_tx.c | 5 ++-- libs/spandsp/src/v27ter_rx.c | 5 ++-- libs/spandsp/src/v27ter_tx.c | 5 ++-- libs/spandsp/src/v29rx.c | 5 ++-- libs/spandsp/src/v29tx.c | 5 ++-- 21 files changed, 109 insertions(+), 86 deletions(-) diff --git a/libs/spandsp/src/fsk.c b/libs/spandsp/src/fsk.c index 2c1d326d84..7e6619a58f 100644 --- a/libs/spandsp/src/fsk.c +++ b/libs/spandsp/src/fsk.c @@ -42,6 +42,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/complex.h" #include "spandsp/dds.h" #include "spandsp/power_meter.h" @@ -166,7 +167,7 @@ SPAN_DECLARE(fsk_tx_state_t *) fsk_tx_init(fsk_tx_state_t *s, { if (s == NULL) { - if ((s = (fsk_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (fsk_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -186,7 +187,7 @@ SPAN_DECLARE(int) fsk_tx_release(fsk_tx_state_t *s) SPAN_DECLARE(int) fsk_tx_free(fsk_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -328,7 +329,7 @@ SPAN_DECLARE(fsk_rx_state_t *) fsk_rx_init(fsk_rx_state_t *s, { if (s == NULL) { - if ((s = (fsk_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (fsk_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -348,7 +349,7 @@ SPAN_DECLARE(int) fsk_rx_release(fsk_rx_state_t *s) SPAN_DECLARE(int) fsk_rx_free(fsk_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/hdlc.c b/libs/spandsp/src/hdlc.c index 7733aa8280..3efaf3810e 100644 --- a/libs/spandsp/src/hdlc.c +++ b/libs/spandsp/src/hdlc.c @@ -35,6 +35,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/async.h" #include "spandsp/crc.h" #include "spandsp/bit_operations.h" @@ -314,7 +315,7 @@ SPAN_DECLARE(hdlc_rx_state_t *) hdlc_rx_init(hdlc_rx_state_t *s, { if (s == NULL) { - if ((s = (hdlc_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (hdlc_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -350,7 +351,7 @@ SPAN_DECLARE(int) hdlc_rx_release(hdlc_rx_state_t *s) SPAN_DECLARE(int) hdlc_rx_free(hdlc_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ @@ -592,7 +593,7 @@ SPAN_DECLARE(hdlc_tx_state_t *) hdlc_tx_init(hdlc_tx_state_t *s, { if (s == NULL) { - if ((s = (hdlc_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (hdlc_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -624,7 +625,7 @@ SPAN_DECLARE(int) hdlc_tx_release(hdlc_tx_state_t *s) SPAN_DECLARE(int) hdlc_tx_free(hdlc_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/ima_adpcm.c b/libs/spandsp/src/ima_adpcm.c index 6c4cc16370..fbd59df621 100644 --- a/libs/spandsp/src/ima_adpcm.c +++ b/libs/spandsp/src/ima_adpcm.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/saturated.h" #include "spandsp/ima_adpcm.h" @@ -282,7 +283,7 @@ SPAN_DECLARE(ima_adpcm_state_t *) ima_adpcm_init(ima_adpcm_state_t *s, { if (s == NULL) { - if ((s = (ima_adpcm_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (ima_adpcm_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } /*endif*/ @@ -301,7 +302,7 @@ SPAN_DECLARE(int) ima_adpcm_release(ima_adpcm_state_t *s) SPAN_DECLARE(int) ima_adpcm_free(ima_adpcm_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c index b504ca9074..ef30f6f4d4 100644 --- a/libs/spandsp/src/image_translate.c +++ b/libs/spandsp/src/image_translate.c @@ -52,6 +52,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/saturated.h" @@ -712,7 +713,7 @@ SPAN_DECLARE(int) image_translate_restart(image_translate_state_t *s, int input_ { if (s->raw_pixel_row[i] == NULL) { - if ((s->raw_pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) + if ((s->raw_pixel_row[i] = (uint8_t *) span_alloc(raw_row_size)) == NULL) return -1; } memset(s->raw_pixel_row[i], 0, raw_row_size); @@ -729,7 +730,7 @@ SPAN_DECLARE(int) image_translate_restart(image_translate_state_t *s, int input_ { if (s->pixel_row[i] == NULL) { - if ((s->pixel_row[i] = (uint8_t *) malloc(raw_row_size)) == NULL) + if ((s->pixel_row[i] = (uint8_t *) span_alloc(raw_row_size)) == NULL) return -1; } memset(s->pixel_row[i], 0, raw_row_size); @@ -757,7 +758,7 @@ SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_sta { if (s == NULL) { - if ((s = (image_translate_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (image_translate_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -794,12 +795,12 @@ SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s) { if (s->raw_pixel_row[i]) { - free(s->raw_pixel_row[i]); + span_free(s->raw_pixel_row[i]); s->raw_pixel_row[i] = NULL; } if (s->pixel_row[i]) { - free(s->pixel_row[i]); + span_free(s->pixel_row[i]); s->pixel_row[i] = NULL; } } @@ -812,7 +813,7 @@ SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s) int res; res = image_translate_release(s); - free(s); + span_free(s); return res; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/modem_echo.c b/libs/spandsp/src/modem_echo.c index 59bf57b544..622c201279 100644 --- a/libs/spandsp/src/modem_echo.c +++ b/libs/spandsp/src/modem_echo.c @@ -57,9 +57,9 @@ SPAN_DECLARE(void) modem_echo_can_free(modem_echo_can_state_t *ec) { fir16_free(&ec->fir_state); - free(ec->fir_taps32); - free(ec->fir_taps16); - free(ec); + span_free(ec->fir_taps32); + span_free(ec->fir_taps16); + span_free(ec); } /*- End of function --------------------------------------------------------*/ @@ -74,7 +74,7 @@ SPAN_DECLARE(modem_echo_can_state_t *) modem_echo_can_init(int len) ec->curr_pos = ec->taps - 1; if ((ec->fir_taps32 = (int32_t *) span_alloc(ec->taps*sizeof(int32_t))) == NULL) { - free(ec); + span_free(ec); return NULL; } memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t)); diff --git a/libs/spandsp/src/t31.c b/libs/spandsp/src/t31.c index 6a9f08d382..9993a7a521 100644 --- a/libs/spandsp/src/t31.c +++ b/libs/spandsp/src/t31.c @@ -51,6 +51,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/bitstream.h" @@ -3016,7 +3017,7 @@ SPAN_DECLARE(t31_state_t *) t31_init(t31_state_t *s, alloced = FALSE; if (s == NULL) { - if ((s = (t31_state_t *) malloc(sizeof (*s))) == NULL) + if ((s = (t31_state_t *) span_alloc(sizeof (*s))) == NULL) return NULL; /*endif*/ alloced = TRUE; @@ -3071,7 +3072,7 @@ SPAN_DECLARE(t31_state_t *) t31_init(t31_state_t *s, if ((s->rx_queue = queue_init(NULL, 4096, QUEUE_WRITE_ATOMIC | QUEUE_READ_ATOMIC)) == NULL) { if (alloced) - free(s); + span_free(s); /*endif*/ return NULL; } @@ -3100,7 +3101,7 @@ SPAN_DECLARE(int) t31_release(t31_state_t *s) SPAN_DECLARE(int) t31_free(t31_state_t *s) { t31_release(s); - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t38_core.c b/libs/spandsp/src/t38_core.c index ae6526f7e8..922e8a372a 100644 --- a/libs/spandsp/src/t38_core.c +++ b/libs/spandsp/src/t38_core.c @@ -47,6 +47,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/t38_core.h" @@ -1109,7 +1110,7 @@ SPAN_DECLARE(t38_core_state_t *) t38_core_init(t38_core_state_t *s, { if (s == NULL) { - if ((s = (t38_core_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t38_core_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1157,7 +1158,7 @@ SPAN_DECLARE(int) t38_core_release(t38_core_state_t *s) SPAN_DECLARE(int) t38_core_free(t38_core_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t38_gateway.c b/libs/spandsp/src/t38_gateway.c index 7656332e80..7078a37908 100644 --- a/libs/spandsp/src/t38_gateway.c +++ b/libs/spandsp/src/t38_gateway.c @@ -50,6 +50,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/dc_restore.h" @@ -2267,7 +2268,7 @@ SPAN_DECLARE(t38_gateway_state_t *) t38_gateway_init(t38_gateway_state_t *s, /*endif*/ if (s == NULL) { - if ((s = (t38_gateway_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t38_gateway_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; /*endif*/ } @@ -2332,7 +2333,7 @@ SPAN_DECLARE(int) t38_gateway_release(t38_gateway_state_t *s) SPAN_DECLARE(int) t38_gateway_free(t38_gateway_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t38_non_ecm_buffer.c b/libs/spandsp/src/t38_non_ecm_buffer.c index cc339a0581..bd63c419f2 100644 --- a/libs/spandsp/src/t38_non_ecm_buffer.c +++ b/libs/spandsp/src/t38_non_ecm_buffer.c @@ -46,6 +46,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/dc_restore.h" @@ -351,7 +352,7 @@ SPAN_DECLARE(t38_non_ecm_buffer_state_t *) t38_non_ecm_buffer_init(t38_non_ecm_b { if (s == NULL) { - if ((s = (t38_non_ecm_buffer_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t38_non_ecm_buffer_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -371,7 +372,7 @@ SPAN_DECLARE(int) t38_non_ecm_buffer_release(t38_non_ecm_buffer_state_t *s) SPAN_DECLARE(int) t38_non_ecm_buffer_free(t38_non_ecm_buffer_state_t *s) { if (s) - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t38_terminal.c b/libs/spandsp/src/t38_terminal.c index 66938d8d24..02a9369f35 100644 --- a/libs/spandsp/src/t38_terminal.c +++ b/libs/spandsp/src/t38_terminal.c @@ -46,6 +46,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/queue.h" @@ -1525,7 +1526,7 @@ SPAN_DECLARE(t38_terminal_state_t *) t38_terminal_init(t38_terminal_state_t *s, if (s == NULL) { - if ((s = (t38_terminal_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t38_terminal_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; /*endif*/ } @@ -1564,7 +1565,7 @@ SPAN_DECLARE(int) t38_terminal_release(t38_terminal_state_t *s) SPAN_DECLARE(int) t38_terminal_free(t38_terminal_state_t *s) { t38_terminal_release(s); - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t42.c b/libs/spandsp/src/t42.c index 8d0023f7d7..5a00a4121e 100644 --- a/libs/spandsp/src/t42.c +++ b/libs/spandsp/src/t42.c @@ -50,6 +50,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/saturated.h" @@ -742,7 +743,7 @@ static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); if (s->scan_line_out) { - free(s->scan_line_out); + span_free(s->scan_line_out); s->scan_line_out = NULL; } if (s->out) @@ -802,12 +803,12 @@ static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) set_itu_fax(s); - if ((s->scan_line_in = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL) + if ((s->scan_line_in = (JSAMPROW) span_alloc(s->samples_per_pixel*s->image_width)) == NULL) return -1; if (s->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) { - if ((s->scan_line_out = (JSAMPROW) malloc(s->samples_per_pixel*s->image_width)) == NULL) + if ((s->scan_line_out = (JSAMPROW) span_alloc(s->samples_per_pixel*s->image_width)) == NULL) return -1; for (i = 0; i < s->compressor.image_height; i++) @@ -828,7 +829,7 @@ static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) if (s->scan_line_out) { - free(s->scan_line_out); + span_free(s->scan_line_out); s->scan_line_out = NULL; } jpeg_finish_compress(&s->compressor); @@ -841,13 +842,13 @@ static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) #else s->buf_size = s->compressed_image_size = ftell(s->out); - if ((s->compressed_buf = malloc(s->compressed_image_size)) == NULL) + if ((s->compressed_buf = span_alloc(s->compressed_image_size)) == NULL) return -1; if (fseek(s->out, 0, SEEK_SET) != 0) { fclose(s->out); s->out = NULL; - free(s->compressed_buf); + span_free(s->compressed_buf); s->compressed_buf = NULL; return -1; } @@ -855,7 +856,7 @@ static int t42_srgb_to_itulab_jpeg(t42_encode_state_t *s) { fclose(s->out); s->out = NULL; - free(s->compressed_buf); + span_free(s->compressed_buf); s->compressed_buf = NULL; return -1; } @@ -986,7 +987,7 @@ SPAN_DECLARE(t42_encode_state_t *) t42_encode_init(t42_encode_state_t *s, { if (s == NULL) { - if ((s = (t42_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t42_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1016,7 +1017,7 @@ SPAN_DECLARE(int) t42_encode_free(t42_encode_state_t *s) int ret; ret = t42_encode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ @@ -1087,7 +1088,7 @@ static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s) span_log(&s->logging, SPAN_LOG_FLOW, "Unspecified libjpeg error.\n"); if (s->scan_line_out) { - free(s->scan_line_out); + span_free(s->scan_line_out); s->scan_line_out = NULL; } if (s->in) @@ -1155,12 +1156,12 @@ static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s) jpeg_start_decompress(&s->decompressor); - if ((s->scan_line_in = malloc(s->samples_per_pixel*s->image_width)) == NULL) + if ((s->scan_line_in = span_alloc(s->samples_per_pixel*s->image_width)) == NULL) return -1; if (s->samples_per_pixel == 3) { - if ((s->scan_line_out = malloc(s->samples_per_pixel*s->image_width)) == NULL) + if ((s->scan_line_out = span_alloc(s->samples_per_pixel*s->image_width)) == NULL) return -1; while (s->decompressor.output_scanline < s->image_length) @@ -1181,12 +1182,12 @@ static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s) if (s->scan_line_in) { - free(s->scan_line_in); + span_free(s->scan_line_in); s->scan_line_in = NULL; } if (s->scan_line_out) { - free(s->scan_line_out); + span_free(s->scan_line_out); s->scan_line_out = NULL; } jpeg_finish_decompress(&s->decompressor); @@ -1243,7 +1244,7 @@ SPAN_DECLARE(int) t42_decode_put(t42_decode_state_t *s, const uint8_t data[], si if (s->compressed_image_size + len > s->buf_size) { - if ((buf = (uint8_t *) realloc(s->compressed_buf, s->compressed_image_size + len + 10000)) == NULL) + if ((buf = (uint8_t *) span_realloc(s->compressed_buf, s->compressed_image_size + len + 10000)) == NULL) return -1; s->buf_size = s->compressed_image_size + len + 10000; s->compressed_buf = buf; @@ -1340,7 +1341,7 @@ SPAN_DECLARE(t42_decode_state_t *) t42_decode_init(t42_decode_state_t *s, { if (s == NULL) { - if ((s = (t42_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t42_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1363,12 +1364,12 @@ SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s) { if (s->scan_line_in) { - free(s->scan_line_in); + span_free(s->scan_line_in); s->scan_line_in = NULL; } if (s->scan_line_out) { - free(s->scan_line_out); + span_free(s->scan_line_out); s->scan_line_out = NULL; } jpeg_destroy_decompress(&s->decompressor); @@ -1379,7 +1380,7 @@ SPAN_DECLARE(int) t42_decode_release(t42_decode_state_t *s) } if (s->comment) { - free(s->comment); + span_free(s->comment); s->comment = NULL; } return 0; @@ -1391,7 +1392,7 @@ SPAN_DECLARE(int) t42_decode_free(t42_decode_state_t *s) int ret; ret = t42_decode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t43.c b/libs/spandsp/src/t43.c index b0e1abffb9..9e2ee76be2 100644 --- a/libs/spandsp/src/t43.c +++ b/libs/spandsp/src/t43.c @@ -44,6 +44,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/async.h" #include "spandsp/timezone.h" @@ -133,7 +134,9 @@ static int t43_create_header(t43_encode_state_t *s, uint8_t data[], size_t len) { int pos; int val[6]; +#if 0 int bytes_per_entry; +#endif pos = 0; unpack_16(data, 0xFFA8); @@ -345,7 +348,7 @@ SPAN_DECLARE(t43_encode_state_t *) t43_encode_init(t43_encode_state_t *s, { if (s == NULL) { - if ((s = (t43_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t43_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -380,7 +383,7 @@ SPAN_DECLARE(int) t43_encode_free(t43_encode_state_t *s) t85_encode_free(&s->t85); ret = t43_encode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ @@ -700,7 +703,7 @@ static int t85_row_write_handler(void *user_data, const uint8_t buf[], size_t le if (s->buf == NULL) { image_size = s->samples_per_pixel*s->t85.xd*s->t85.yd; - if ((s->buf = malloc(image_size)) == NULL) + if ((s->buf = span_alloc(image_size)) == NULL) return -1; memset(s->buf, 0, image_size); } @@ -761,6 +764,7 @@ SPAN_DECLARE(int) t43_decode_put(t43_decode_state_t *s, const uint8_t data[], si } /* Now deal the bit-planes, one after another. */ + total_len = 0; while (s->current_bit_plane < s->t85.bit_planes) { j = s->current_bit_plane; @@ -882,7 +886,7 @@ SPAN_DECLARE(t43_decode_state_t *) t43_decode_init(t43_decode_state_t *s, { if (s == NULL) { - if ((s = (t43_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t43_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -922,7 +926,7 @@ SPAN_DECLARE(int) t43_decode_free(t43_decode_state_t *s) ret = t43_decode_release(s); t85_decode_free(&s->t85); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t4_t6_decode.c b/libs/spandsp/src/t4_t6_decode.c index c347dfd19a..219a0bafb8 100644 --- a/libs/spandsp/src/t4_t6_decode.c +++ b/libs/spandsp/src/t4_t6_decode.c @@ -79,6 +79,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -138,17 +139,17 @@ static int free_buffers(t4_t6_decode_state_t *s) { if (s->cur_runs) { - free(s->cur_runs); + span_free(s->cur_runs); s->cur_runs = NULL; } if (s->ref_runs) { - free(s->ref_runs); + span_free(s->ref_runs); s->ref_runs = NULL; } if (s->row_buf) { - free(s->row_buf); + span_free(s->row_buf); s->row_buf = NULL; } s->bytes_per_row = 0; @@ -824,10 +825,10 @@ SPAN_DECLARE(int) t4_t6_decode_restart(t4_t6_decode_state_t *s, int image_width) if (s->bytes_per_row == 0 || image_width != s->image_width) { /* Allocate the space required for decoding the new row length. */ - if ((bufptr = (uint32_t *) realloc(s->cur_runs, run_space)) == NULL) + if ((bufptr = (uint32_t *) span_realloc(s->cur_runs, run_space)) == NULL) return -1; s->cur_runs = bufptr; - if ((bufptr = (uint32_t *) realloc(s->ref_runs, run_space)) == NULL) + if ((bufptr = (uint32_t *) span_realloc(s->ref_runs, run_space)) == NULL) return -1; s->ref_runs = bufptr; s->image_width = image_width; @@ -835,7 +836,7 @@ SPAN_DECLARE(int) t4_t6_decode_restart(t4_t6_decode_state_t *s, int image_width) bytes_per_row = (image_width + 7)/8; if (bytes_per_row != s->bytes_per_row) { - if ((bufptr8 = (uint8_t *) realloc(s->row_buf, bytes_per_row)) == NULL) + if ((bufptr8 = (uint8_t *) span_realloc(s->row_buf, bytes_per_row)) == NULL) return -1; s->row_buf = bufptr8; s->bytes_per_row = bytes_per_row; @@ -892,7 +893,7 @@ SPAN_DECLARE(t4_t6_decode_state_t *) t4_t6_decode_init(t4_t6_decode_state_t *s, { if (s == NULL) { - if ((s = (t4_t6_decode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t4_t6_decode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -919,7 +920,7 @@ SPAN_DECLARE(int) t4_t6_decode_free(t4_t6_decode_state_t *s) int ret; ret = t4_t6_decode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t4_t6_encode.c b/libs/spandsp/src/t4_t6_encode.c index 4ed063e063..fde78f4827 100644 --- a/libs/spandsp/src/t4_t6_encode.c +++ b/libs/spandsp/src/t4_t6_encode.c @@ -76,6 +76,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -373,17 +374,17 @@ static int free_buffers(t4_t6_encode_state_t *s) { if (s->cur_runs) { - free(s->cur_runs); + span_free(s->cur_runs); s->cur_runs = NULL; } if (s->ref_runs) { - free(s->ref_runs); + span_free(s->ref_runs); s->ref_runs = NULL; } if (s->bitstream) { - free(s->bitstream); + span_free(s->bitstream); s->bitstream = NULL; } s->bytes_per_row = 0; @@ -1026,13 +1027,13 @@ SPAN_DECLARE(int) t4_t6_encode_set_image_width(t4_t6_encode_state_t *s, int imag s->bytes_per_row = (s->image_width + 7)/8; run_space = (s->image_width + 4)*sizeof(uint32_t); - if ((bufptr = (uint32_t *) realloc(s->cur_runs, run_space)) == NULL) + if ((bufptr = (uint32_t *) span_realloc(s->cur_runs, run_space)) == NULL) return -1; s->cur_runs = bufptr; - if ((bufptr = (uint32_t *) realloc(s->ref_runs, run_space)) == NULL) + if ((bufptr = (uint32_t *) span_realloc(s->ref_runs, run_space)) == NULL) return -1; s->ref_runs = bufptr; - if ((bufptr8 = (uint8_t *) realloc(s->bitstream, (s->image_width + 1)*sizeof(uint16_t))) == NULL) + if ((bufptr8 = (uint8_t *) span_realloc(s->bitstream, (s->image_width + 1)*sizeof(uint16_t))) == NULL) return -1; s->bitstream = bufptr8; } @@ -1151,7 +1152,7 @@ SPAN_DECLARE(t4_t6_encode_state_t *) t4_t6_encode_init(t4_t6_encode_state_t *s, { if (s == NULL) { - if ((s = (t4_t6_encode_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t4_t6_encode_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1181,7 +1182,7 @@ SPAN_DECLARE(int) t4_t6_encode_free(t4_t6_encode_state_t *s) int ret; ret = t4_t6_encode_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v17tx.c b/libs/spandsp/src/v17tx.c index fe361d7744..b4f9f3e80a 100644 --- a/libs/spandsp/src/v17tx.c +++ b/libs/spandsp/src/v17tx.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/complex.h" @@ -449,7 +450,7 @@ SPAN_DECLARE(v17_tx_state_t *) v17_tx_init(v17_tx_state_t *s, int bit_rate, int } if (s == NULL) { - if ((s = (v17_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v17_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -473,7 +474,7 @@ SPAN_DECLARE(int) v17_tx_release(v17_tx_state_t *s) SPAN_DECLARE(int) v17_tx_free(v17_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v18.c b/libs/spandsp/src/v18.c index 65783eb73b..177ca88229 100644 --- a/libs/spandsp/src/v18.c +++ b/libs/spandsp/src/v18.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/queue.h" #include "spandsp/async.h" @@ -1113,7 +1114,7 @@ SPAN_DECLARE(v18_state_t *) v18_init(v18_state_t *s, if (s == NULL) { - if ((s = (v18_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v18_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1204,7 +1205,7 @@ SPAN_DECLARE(int) v18_release(v18_state_t *s) SPAN_DECLARE(int) v18_free(v18_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v22bis_tx.c b/libs/spandsp/src/v22bis_tx.c index bb18b77b26..d7ecbe269a 100644 --- a/libs/spandsp/src/v22bis_tx.c +++ b/libs/spandsp/src/v22bis_tx.c @@ -45,6 +45,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/complex.h" @@ -700,7 +701,7 @@ SPAN_DECLARE(v22bis_state_t *) v22bis_init(v22bis_state_t *s, } if (s == NULL) { - if ((s = (v22bis_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v22bis_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -748,7 +749,7 @@ SPAN_DECLARE(int) v22bis_release(v22bis_state_t *s) SPAN_DECLARE(int) v22bis_free(v22bis_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v27ter_rx.c b/libs/spandsp/src/v27ter_rx.c index 5dabe1c6f0..0e49cf901b 100644 --- a/libs/spandsp/src/v27ter_rx.c +++ b/libs/spandsp/src/v27ter_rx.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/fast_convert.h" #include "spandsp/math_fixed.h" @@ -1087,7 +1088,7 @@ SPAN_DECLARE(v27ter_rx_state_t *) v27ter_rx_init(v27ter_rx_state_t *s, int bit_r } if (s == NULL) { - if ((s = (v27ter_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v27ter_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1110,7 +1111,7 @@ SPAN_DECLARE(int) v27ter_rx_release(v27ter_rx_state_t *s) SPAN_DECLARE(int) v27ter_rx_free(v27ter_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v27ter_tx.c b/libs/spandsp/src/v27ter_tx.c index f1d7ce263d..1195b96d8f 100644 --- a/libs/spandsp/src/v27ter_tx.c +++ b/libs/spandsp/src/v27ter_tx.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/complex.h" @@ -404,7 +405,7 @@ SPAN_DECLARE(v27ter_tx_state_t *) v27ter_tx_init(v27ter_tx_state_t *s, int bit_r } if (s == NULL) { - if ((s = (v27ter_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v27ter_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -427,7 +428,7 @@ SPAN_DECLARE(int) v27ter_tx_release(v27ter_tx_state_t *s) SPAN_DECLARE(int) v27ter_tx_free(v27ter_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v29rx.c b/libs/spandsp/src/v29rx.c index 3bb476726d..a5520e92cd 100644 --- a/libs/spandsp/src/v29rx.c +++ b/libs/spandsp/src/v29rx.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/fast_convert.h" #include "spandsp/math_fixed.h" @@ -1148,7 +1149,7 @@ SPAN_DECLARE(v29_rx_state_t *) v29_rx_init(v29_rx_state_t *s, int bit_rate, put_ } if (s == NULL) { - if ((s = (v29_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v29_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1176,7 +1177,7 @@ SPAN_DECLARE(int) v29_rx_release(v29_rx_state_t *s) SPAN_DECLARE(int) v29_rx_free(v29_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/v29tx.c b/libs/spandsp/src/v29tx.c index 3eac97e1bc..d6dbedf9ce 100644 --- a/libs/spandsp/src/v29tx.c +++ b/libs/spandsp/src/v29tx.c @@ -42,6 +42,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/complex.h" @@ -388,7 +389,7 @@ SPAN_DECLARE(v29_tx_state_t *) v29_tx_init(v29_tx_state_t *s, int bit_rate, int } if (s == NULL) { - if ((s = (v29_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v29_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -411,7 +412,7 @@ SPAN_DECLARE(int) v29_tx_release(v29_tx_state_t *s) SPAN_DECLARE(int) v29_tx_free(v29_tx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ From 13e8532398350866d1d57b943d0cb605e8df632e Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 6 Aug 2013 01:17:05 +0800 Subject: [PATCH 232/278] Hopefully all the spandsp functions now use custom allocation routines --- libs/spandsp/src/t30.c | 39 ++++++++++++++++++++------------------- libs/spandsp/src/t4_rx.c | 3 ++- libs/spandsp/src/t4_tx.c | 13 +++++++------ libs/spandsp/src/v17rx.c | 5 +++-- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index 1e3107b0b2..484b627a2b 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -45,6 +45,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/queue.h" @@ -636,89 +637,89 @@ static void release_resources(t30_state_t *s) { if (s->tx_info.nsf) { - free(s->tx_info.nsf); + span_free(s->tx_info.nsf); s->tx_info.nsf = NULL; } s->tx_info.nsf_len = 0; if (s->tx_info.nsc) { - free(s->tx_info.nsc); + span_free(s->tx_info.nsc); s->tx_info.nsc = NULL; } s->tx_info.nsc_len = 0; if (s->tx_info.nss) { - free(s->tx_info.nss); + span_free(s->tx_info.nss); s->tx_info.nss = NULL; } s->tx_info.nss_len = 0; if (s->tx_info.tsa) { - free(s->tx_info.tsa); + span_free(s->tx_info.tsa); s->tx_info.tsa = NULL; } if (s->tx_info.ira) { - free(s->tx_info.ira); + span_free(s->tx_info.ira); s->tx_info.ira = NULL; } if (s->tx_info.cia) { - free(s->tx_info.cia); + span_free(s->tx_info.cia); s->tx_info.cia = NULL; } if (s->tx_info.isp) { - free(s->tx_info.isp); + span_free(s->tx_info.isp); s->tx_info.isp = NULL; } if (s->tx_info.csa) { - free(s->tx_info.csa); + span_free(s->tx_info.csa); s->tx_info.csa = NULL; } if (s->rx_info.nsf) { - free(s->rx_info.nsf); + span_free(s->rx_info.nsf); s->rx_info.nsf = NULL; } s->rx_info.nsf_len = 0; if (s->rx_info.nsc) { - free(s->rx_info.nsc); + span_free(s->rx_info.nsc); s->rx_info.nsc = NULL; } s->rx_info.nsc_len = 0; if (s->rx_info.nss) { - free(s->rx_info.nss); + span_free(s->rx_info.nss); s->rx_info.nss = NULL; } s->rx_info.nss_len = 0; if (s->rx_info.tsa) { - free(s->rx_info.tsa); + span_free(s->rx_info.tsa); s->rx_info.tsa = NULL; } if (s->rx_info.ira) { - free(s->rx_info.ira); + span_free(s->rx_info.ira); s->rx_info.ira = NULL; } if (s->rx_info.cia) { - free(s->rx_info.cia); + span_free(s->rx_info.cia); s->rx_info.cia = NULL; } if (s->rx_info.isp) { - free(s->rx_info.isp); + span_free(s->rx_info.isp); s->rx_info.isp = NULL; } if (s->rx_info.csa) { - free(s->rx_info.csa); + span_free(s->rx_info.csa); s->rx_info.csa = NULL; } } @@ -5829,7 +5830,7 @@ static int decode_nsf_nss_nsc(t30_state_t *s, uint8_t *msg[], const uint8_t *pkt { uint8_t *t; - if ((t = malloc(len - 1)) == NULL) + if ((t = span_alloc(len - 1)) == NULL) return 0; memcpy(t, pkt + 1, len - 1); *msg = t; @@ -6795,7 +6796,7 @@ SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s, { if (s == NULL) { - if ((s = (t30_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t30_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -6845,7 +6846,7 @@ SPAN_DECLARE(int) t30_release(t30_state_t *s) SPAN_DECLARE(int) t30_free(t30_state_t *s) { t30_release(s); - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 616452a406..5a7bb6bc45 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -48,6 +48,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -1121,7 +1122,7 @@ SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int allocated = FALSE; if (s == NULL) { - if ((s = (t4_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t4_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; allocated = TRUE; } diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 3419894c64..9e8f3e678c 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -48,6 +48,7 @@ #include #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/bit_operations.h" #include "spandsp/async.h" @@ -621,7 +622,7 @@ static int read_tiff_t85_image(t4_tx_state_t *s) if (len > biggest) biggest = len; } - if ((raw_data = malloc(biggest)) == NULL) + if ((raw_data = span_alloc(biggest)) == NULL) return -1; s->tiff.image_size = s->tiff.image_length*((s->tiff.image_width + 7)/8); @@ -685,7 +686,7 @@ static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) total_image_len = 0; for (i = 0; i < num_strips; i++) total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i); - if ((raw_data = malloc(total_image_len)) == NULL) + if ((raw_data = span_alloc(total_image_len)) == NULL) return -1; total_len = 0; @@ -704,7 +705,7 @@ static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); image_size = 3*s->metadata.image_length*s->metadata.image_width; - if ((*buf = malloc(image_size)) == NULL) + if ((*buf = span_alloc(image_size)) == NULL) return -1; pack.buf = *buf; @@ -751,7 +752,7 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) for (i = 0; i < num_strips; i++) total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i); - if ((raw_data = malloc(total_image_len)) == NULL) + if ((raw_data = span_alloc(total_image_len)) == NULL) return -1; total_len = 0; @@ -985,7 +986,7 @@ static int make_header(t4_tx_state_t *s) if (s->header_text == NULL) { - if ((s->header_text = malloc(132 + 1)) == NULL) + if ((s->header_text = span_alloc(132 + 1)) == NULL) return -1; } /* This is very English oriented, but then most FAX machines are, too. Some @@ -1604,7 +1605,7 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int allocated = FALSE; if (s == NULL) { - if ((s = (t4_tx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (t4_tx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; allocated = TRUE; } diff --git a/libs/spandsp/src/v17rx.c b/libs/spandsp/src/v17rx.c index 256d71a14c..a1d9585983 100644 --- a/libs/spandsp/src/v17rx.c +++ b/libs/spandsp/src/v17rx.c @@ -43,6 +43,7 @@ #include "floating_fudge.h" #include "spandsp/telephony.h" +#include "spandsp/alloc.h" #include "spandsp/logging.h" #include "spandsp/fast_convert.h" #include "spandsp/math_fixed.h" @@ -1526,7 +1527,7 @@ SPAN_DECLARE(v17_rx_state_t *) v17_rx_init(v17_rx_state_t *s, int bit_rate, put_ } if (s == NULL) { - if ((s = (v17_rx_state_t *) malloc(sizeof(*s))) == NULL) + if ((s = (v17_rx_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); @@ -1551,7 +1552,7 @@ SPAN_DECLARE(int) v17_rx_release(v17_rx_state_t *s) SPAN_DECLARE(int) v17_rx_free(v17_rx_state_t *s) { - free(s); + span_free(s); return 0; } /*- End of function --------------------------------------------------------*/ From df669f700ae685f19cd8a569882066139898513e Mon Sep 17 00:00:00 2001 From: Steve Underwood Date: Tue, 6 Aug 2013 02:38:06 +0800 Subject: [PATCH 233/278] A cleanup of FAX image size/resolution matching accuracy. Squashing of bi-level images should now be correct. This is now close to supporting grey scale and colour. --- libs/spandsp/INSTALL | 397 ++++--- libs/spandsp/spandsp-sim/Makefile.am | 2 +- libs/spandsp/src/Makefile.am | 2 +- libs/spandsp/src/spandsp/t4_tx.h | 17 - libs/spandsp/src/t30.c | 414 +++---- libs/spandsp/src/t4_rx.c | 254 +++-- libs/spandsp/src/t4_tx.c | 1457 ++++++++++++++++++++---- libs/spandsp/tests/Makefile.am | 2 +- libs/spandsp/tests/regression_tests.sh | 9 + libs/spandsp/tests/t31_tests.c | 4 +- libs/spandsp/tests/t4_tests.c | 3 - libs/spandsp/tests/tsb85_tests.c | 65 +- 12 files changed, 1860 insertions(+), 766 deletions(-) diff --git a/libs/spandsp/INSTALL b/libs/spandsp/INSTALL index 2abc70492a..007e9396d0 100644 --- a/libs/spandsp/INSTALL +++ b/libs/spandsp/INSTALL @@ -1,121 +1,80 @@ -Building and installing spandsp -=============================== - -A number of distributions include spandsp, but they usually use older versions -of the library, which lack a lot of the features of the current version. Before -installing spandsp, make sure there are no older versions already on your -machine. Make sure libtiff is installed on your machine. Versions 3.5.7, -3.6.0, 3.7.1 and 3.8.2 seem to work OK. There have been several bugs related -to FAX document handling in some versions of libtiff. Also, some people have -had trouble using spandsp because they had more than one version of libtiff -on their machine. Take care with this. If you are using an RPM based system, -such as RedHat or Fedora, you will need the libtiff and libtiff-devel RPMs -installed to be able to build spandsp. - -You can use the usual: - - ./configure - make - make install - -process to build the spandsp library. Note that if you use configure in this -way, the software will be installed in /usr/local. In this case make sure your -/etc/ld.so.conf file has an entry for /usr/local/lib. If you wish the software -to be installed in /usr, you should build it with the commands. - - ./configure --prefix=/usr - make - make install - - -Building the programming documentation -====================================== - -If you wish to build the programming documentation for spandsp, configure -spandsp with: - - ./configure --enable-doc - -You need doxygen installed on your machine. - - -Building the test suite -======================= - -Most sections of the spandsp library have an accompanying test program in the -test directory. If you wish to build these test programs, configure spandsp -with: - - ./configure --enable-tests - -To build these tests you will need libaudiofile installed on your machine. To -build the modem tests, with the GUI monitoring feature you will need Fltk 1.1.4 -or later, an audio meter module and a cartesian plotting module. Fltk may be -obtained from http://www.fltk.org. The audio meter module may be obtained from -http://www.soft-switch.org/downloads/Fl_Audio_Meter.tgz . The cartesian plotting -module may be obtained from http://134.226.68.29/fltk. However, there is no -suitable makefile supplied with that. You can find a version at -http://www.soft-switch.org/downloads/Fl_Cartesian.tgz which will build as a -Linux library. The actual code in both these versions is identical. -You need to have Fltk 1.1.4 or later installed before building the plotting -library. - - -Applications -============ - -Applications support for spandsp is built into packages such as Callweaver, -FreeSwitch and iaxmodem. Code to add spandsp based FAX support to Asterisk may -be found at http://sourceforge.net/projects/agx-ast-addons. - +Installation Instructions +************************* +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. Basic Installation ================== - These are generic installation instructions. + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, a file -`config.cache' that saves the results of its tests to speed up -reconfiguring, and a file `config.log' containing compiler output -(useful mainly for debugging `configure'). +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can -be considered for the next release. If at some point `config.cache' -contains results you don't want to keep, you may remove or edit it. +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. - The file `configure.ac' is used to create `configure' by a program -called `autoconf'. You only need `configure.ac' if you want to change -it or regenerate `configure' using a newer version of `autoconf'. + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. -The simplest way to compile this package is: + The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. + `./configure' to configure the package for your system. - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with - the package. + the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and - documentation. + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. - 5. You can remove the program binaries and object files from the + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is @@ -124,62 +83,119 @@ The simplest way to compile this package is: all sorts of other programs in order to regenerate files that came with the distribution. + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + Compilers and Options ===================== Some systems require unusual options for compilation or linking that -the `configure' script does not know about. You can give `configure' -initial values for variables by setting them in the environment. Using -a Bourne-compatible shell, you can do that on the command line like -this: - CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. -Or on systems that have the `env' program, you can do it like this: - env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the +own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. - If you have to use a `make' that does not supports the `VPATH' -variable, you have to compile the package for one architecture at a time -in the source code directory. After you have installed the package for -one architecture, use `make distclean' before reconfiguring for another -architecture. + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. Installation Names ================== - By default, `make install' will install the package's files in -`/usr/local/bin', `/usr/local/man', etc. You can specify an -installation prefix other than `/usr/local' by giving `configure' the -option `--prefix=PATH'. + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you -give `configure' the option `--exec-prefix=PATH', the package will use -PATH as the prefix for installing programs and libraries. -Documentation and other data files will still use the regular prefix. +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give -options like `--bindir=PATH' to specify different values for particular +options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. -Optional Features -================= - Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE @@ -192,25 +208,80 @@ find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + Specifying the System Type ========================== - There may be some features `configure' can not figure out -automatically, but needs to determine by the type of host the package -will run on. Usually `configure' can figure that out, but if it prints -a message saying it can not guess the host type, give it the -`--host=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name with three fields: + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + CPU-COMPANY-SYSTEM -See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the host type. +where SYSTEM can have one of these forms: - If you are building compiler tools for cross-compiling, you can also -use the `--target=TYPE' option to select the type of system they will -produce code for and the `--build=TYPE' option to select the type of -system on which you are compiling the package. + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. Sharing Defaults ================ @@ -223,19 +294,56 @@ default values for variables like `CC', `cache_file', and `prefix'. `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. -Operation Controls +Defining Variables ================== + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + `configure' recognizes the following options to control how it operates. -`--cache-file=FILE' - Use and save the results of the tests in FILE instead of - `./config.cache'. Set FILE to `/dev/null' to disable caching, for - debugging `configure'. - `--help' - Print a summary of the options to `configure', and exit. +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. `--quiet' `--silent' @@ -248,8 +356,15 @@ operates. Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. -`--version' - Print the version of Autoconf used to generate the `configure' - script, and exit. +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. -`configure' also accepts some other, not widely useful, options. +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/libs/spandsp/spandsp-sim/Makefile.am b/libs/spandsp/spandsp-sim/Makefile.am index d33575a55c..4eb86c4b49 100644 --- a/libs/spandsp/spandsp-sim/Makefile.am +++ b/libs/spandsp/spandsp-sim/Makefile.am @@ -34,7 +34,7 @@ EXTRA_DIST = libspandsp_sim.dsp \ msvc/vc9proj.head \ msvc/vc9proj.foot -INCLUDES = -I$(top_builddir) -I$(top_builddir)/src -DDATADIR="\"$(pkgdatadir)\"" +AM_CPPFLAGS = -I$(top_builddir) -I$(top_builddir)/src -DDATADIR="\"$(pkgdatadir)\"" noinst_PROGRAMS = make_line_models diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index a794e37fb5..7a28c27aad 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -78,7 +78,7 @@ EXTRA_DIST = floating_fudge.h \ spandsp/private/README \ spandsp/version.h.in -INCLUDES = -I$(top_builddir) +AM_CPPFLAGS = -I$(top_builddir) lib_LTLIBRARIES = libspandsp.la diff --git a/libs/spandsp/src/spandsp/t4_tx.h b/libs/spandsp/src/spandsp/t4_tx.h index b411892c78..41974c9fcf 100644 --- a/libs/spandsp/src/spandsp/t4_tx.h +++ b/libs/spandsp/src/spandsp/t4_tx.h @@ -323,12 +323,6 @@ SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, int supported_bilevel_resolutions, int supported_colour_resolutions); -/*! \brief Set the compression for the encoded data. - \param s The T.4 context. - \param encoding The encoding. - \return 0 for success, otherwise -1. */ -SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding); - /*! \brief Set the minimum number of encoded bits per row. This allows the makes the encoding process to be set to comply with the minimum row time specified by a remote receiving machine. @@ -336,11 +330,6 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int encoding); \param bits The minimum number of bits per row. */ SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits); -/*! \brief Set the width of the image. - \param s The T.4 context. - \param image_width The image width, in pixels. */ -SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width); - /*! \brief Set the maximum number of 2D encoded rows between 1D encoded rows. This is only valid for T.4 2D encoding. \param s The T.4 context. @@ -381,12 +370,6 @@ SPAN_DECLARE(void) t4_tx_set_header_overlays_image(t4_tx_state_t *s, int header_ \return 0 for success, otherwise -1. */ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handler_t handler, void *user_data); -/*! \brief Set the row squashing ratio, for adjusting row-to-row (y) resolution of bi-level - images for a T.4 transmit context. - \param s The T.4 transmit context. - \param row_squashing_ratio Vertical squashing ratio. */ -SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio); - /*! \brief Get the number of pages in the file. \param s The T.4 context. \return The number of pages, or -1 if there is an error. */ diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index 484b627a2b..3a4a12acf5 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -1224,7 +1224,7 @@ int t30_build_dis_or_dtc(t30_state_t *s) // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_T88_CAPABILITY_3); //} - //if ((s->supported_compressions & (T4_COMPRESSION_COLOUR | T4_COMPRESSION_GRAYSCALE))) + if ((s->supported_compressions & (T4_COMPRESSION_COLOUR | T4_COMPRESSION_GRAYSCALE))) { if ((s->supported_compressions & T4_COMPRESSION_COLOUR)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_FULL_COLOUR_CAPABLE); @@ -1249,8 +1249,8 @@ int t30_build_dis_or_dtc(t30_state_t *s) if ((s->supported_compressions & T4_COMPRESSION_12BIT)) set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE); - //if ((s->supported_compressions & T4_COMPRESSION_NO_SUBSAMPLING)) - // set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING); + if ((s->supported_compressions & T4_COMPRESSION_NO_SUBSAMPLING)) + set_ctrl_bit(s->local_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING); /* No custom illuminant */ /* No custom gamut range */ @@ -1423,8 +1423,6 @@ static int prune_dis_dtc(t30_state_t *s) static int build_dcs(t30_state_t *s) { int i; - int bad; - int row_squashing_ratio; int use_bilevel; int image_type; @@ -1523,229 +1521,21 @@ static int build_dcs(t30_state_t *s) break; } - /* Set the Y resolution bits */ - row_squashing_ratio = 1; - bad = T30_ERR_NORESSUPPORT; - if ((use_bilevel && (s->current_page_resolution & s->mutual_bilevel_resolutions)) - || - (!use_bilevel && (s->current_page_resolution & s->mutual_colour_resolutions))) - { - /* The resolution is supported by both parties */ - switch (s->current_page_resolution) - { - case T4_RESOLUTION_1200_1200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_1200_1200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200); - break; - case T4_RESOLUTION_600_1200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_1200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_600_600: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_600); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600); - break; - case T4_RESOLUTION_400_800: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_800); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_400_400: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); - break; - case T4_RESOLUTION_300_600: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_600); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_300_300: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); - break; - case T4_RESOLUTION_200_400: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_200_200: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); - break; - case T4_RESOLUTION_200_100: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - break; - case T4_RESOLUTION_100_100: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); - if (!use_bilevel) - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100); - break; - case T4_RESOLUTION_R16_SUPERFINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); - break; - case T4_RESOLUTION_R8_SUPERFINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); - break; - case T4_RESOLUTION_R8_FINE: - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - break; - case T4_RESOLUTION_R8_STANDARD: - /* Nothing special to set */ - break; - } - bad = T30_ERR_OK; - } - else - { -#if 0 - /* Deal with resolution fudging */ - if ((s->current_page_resolution & (T4_RESOLUTION_R16_SUPERFINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_STANDARD))) - { - if ((s->mutual_bilevel_resolutions & (T4_RESOLUTION_400_400 | T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_100))) - { - /* Fudge between imperial and metric */ - } - } - else if ((s->current_page_resolution & (T4_RESOLUTION_400_400 | T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_100)) - { - if ((s->mutual_bilevel_resolutions & (T4_RESOLUTION_R16_SUPERFINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_STANDARD))) - { - /* Fudge between imperial and metric */ - } - } -#endif - /* Deal with squashing options */ - if ((s->current_page_resolution & T4_RESOLUTION_R8_SUPERFINE)) - { - if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_FINE)) - { - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_STANDARD)) - { - row_squashing_ratio = 4; - bad = T30_ERR_OK; - } - } - else if ((s->current_page_resolution & T4_RESOLUTION_R8_FINE) && (s->mutual_bilevel_resolutions & T4_RESOLUTION_R8_STANDARD)) - { - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->current_page_resolution & T4_RESOLUTION_200_400)) - { - if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_200_200)) - { - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - else if ((s->mutual_bilevel_resolutions & T4_RESOLUTION_200_100)) - { - row_squashing_ratio = 4; - bad = T30_ERR_OK; - } - } - else if ((s->current_page_resolution & T4_RESOLUTION_200_200) && (s->mutual_bilevel_resolutions & T4_RESOLUTION_200_100)) - { - row_squashing_ratio = 2; - bad = T30_ERR_OK; - } - } - - t4_tx_set_row_squashing_ratio(&s->t4.tx, row_squashing_ratio); - if (bad != T30_ERR_OK) - { - t30_set_status(s, bad); - span_log(&s->logging, SPAN_LOG_FLOW, "Image resolution (%d x %d) not acceptable\n", s->x_resolution, s->y_resolution); - return -1; - } - - /* Deal with the image width. */ - /* Low (R4) res widths are not supported in recent versions of T.30 */ - bad = T30_ERR_OK; - /* The following treats a width field of 11 like 10, which does what note 6 of Table 2/T.30 - says we should do with the invalid value 11. */ - if (((s->image_width == T4_WIDTH_200_A4) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) - || - ((s->image_width == T4_WIDTH_300_A4) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_A4) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_A4) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_A4) && (s->x_resolution == T4_X_RESOLUTION_1200))) + /* Set the image width */ + switch (s->line_width_code) { + case T4_SUPPORT_WIDTH_215MM: span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A4 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); /* No width related bits need to be set. */ - } - else if (((s->image_width == T4_WIDTH_200_B4) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) - || - ((s->image_width == T4_WIDTH_300_B4) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_B4) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_B4) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_B4) && (s->x_resolution == T4_X_RESOLUTION_1200))) - { - if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_255MM)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH); - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - } - else if (((s->image_width == T4_WIDTH_200_A3) && (s->x_resolution == T4_X_RESOLUTION_200 || s->x_resolution == T4_X_RESOLUTION_R8)) - || - ((s->image_width == T4_WIDTH_300_A3) && (s->x_resolution == T4_X_RESOLUTION_300)) - || - ((s->image_width == T4_WIDTH_400_A3) && (s->x_resolution == T4_X_RESOLUTION_400 || s->x_resolution == T4_X_RESOLUTION_R16)) - || - ((s->image_width == T4_WIDTH_600_A3) && (s->x_resolution == T4_X_RESOLUTION_600)) - || - ((s->image_width == T4_WIDTH_1200_A3) && (s->x_resolution == T4_X_RESOLUTION_1200))) - { - if ((s->mutual_image_sizes & T4_SUPPORT_WIDTH_303MM)) - { - span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); - set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH); - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - } - else - { - /* We do not support this width and resolution combination */ - bad = T30_ERR_NOSIZESUPPORT; - } - - if (bad != T30_ERR_OK) - { - t30_set_status(s, bad); - span_log(&s->logging, - SPAN_LOG_FLOW, - "Image width (%d pixels) and resolution (%d x %d) is not an acceptable\n", - s->image_width, - s->x_resolution, - s->y_resolution); - return -1; + break; + case T4_SUPPORT_WIDTH_255MM: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_255MM_WIDTH); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is B4 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); + break; + case T4_SUPPORT_WIDTH_303MM: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_303MM_WIDTH); + span_log(&s->logging, SPAN_LOG_FLOW, "Image width is A3 at %ddpm x %ddpm\n", s->x_resolution, s->y_resolution); + break; } /* Set the image length */ @@ -1760,6 +1550,77 @@ static int build_dcs(t30_state_t *s) else if ((s->mutual_image_sizes & T4_SUPPORT_LENGTH_US_LEGAL)) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_NORTH_AMERICAN_LEGAL); + /* Set the Y resolution bits */ + switch (s->current_page_resolution) + { + case T4_RESOLUTION_1200_1200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_1200_1200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_1200_1200); + break; + case T4_RESOLUTION_600_1200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_1200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_600_600: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_600_600); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_600_600); + break; + case T4_RESOLUTION_400_800: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_800); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_400_400: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); + break; + case T4_RESOLUTION_300_600: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_600); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_300_300: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_300_300); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_300_300_400_400); + break; + case T4_RESOLUTION_200_400: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_200_200: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_FULL_COLOUR_MODE); + break; + case T4_RESOLUTION_200_100: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + break; + case T4_RESOLUTION_100_100: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_INCH_RESOLUTION); + if (!use_bilevel) + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_COLOUR_GRAY_100_100); + break; + case T4_RESOLUTION_R16_SUPERFINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_400_400); + break; + case T4_RESOLUTION_R8_SUPERFINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_400); + break; + case T4_RESOLUTION_R8_FINE: + set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_200_200); + break; + case T4_RESOLUTION_R8_STANDARD: + /* Nothing special to set */ + break; + } + if (s->error_correcting_mode) set_ctrl_bit(s->dcs_frame, T30_DCS_BIT_ECM_MODE); @@ -1832,7 +1693,7 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) if (!s->error_correcting_mode) { /* Remove any compression schemes which need error correction to work. */ - s->mutual_compressions &= (0xF0000000 | T4_COMPRESSION_NONE | T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D); + s->mutual_compressions &= (0xFF800000 | T4_COMPRESSION_NONE | T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D); if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_2D_CAPABLE)) s->mutual_compressions &= ~T4_COMPRESSION_T4_2D; } @@ -1866,8 +1727,8 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_12BIT_CAPABLE)) s->mutual_compressions &= ~T4_COMPRESSION_12BIT; - //if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING)) - // ???? = T4_COMPRESSION_T42_T81_SUBSAMPLING; + if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_NO_SUBSAMPLING)) + s->mutual_compressions &= ~T4_COMPRESSION_NO_SUBSAMPLING; /* bit74 custom illuminant */ /* bit75 custom gamut range */ @@ -2087,7 +1948,7 @@ static int analyze_rx_dcs(t30_state_t *s, const uint8_t *msg, int len) if (test_ctrl_bit(dcs_frame, T30_DCS_BIT_NO_SUBSAMPLING)) { - //???? = T4_COMPRESSION_T42_T81_SUBSAMPLING; + //???? = T4_COMPRESSION_NO_SUBSAMPLING; } if (!test_ctrl_bit(dcs_frame, T30_DCS_BIT_PREFERRED_HUFFMAN_TABLES)) @@ -2529,7 +2390,6 @@ static int send_dcs_sequence(t30_state_t *s, int start) /* Schedule training after the messages */ if (start) { - prune_dcs(s); set_state(s, T30_STATE_D); s->step = 0; } @@ -2654,22 +2514,12 @@ static void set_min_scan_time(t30_state_t *s) { case T4_Y_RESOLUTION_SUPERFINE: case T4_Y_RESOLUTION_400: - if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_400_CAPABLE)) - { - s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; - break; - } - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support super-fine resolution. Squashing image.\n"); - /* Fall through */ + s->min_scan_time_code = translate_min_scan_time[(test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_MIN_SCAN_TIME_HALVES)) ? 2 : 1][min_bits_field]; + break; case T4_Y_RESOLUTION_FINE: case T4_Y_RESOLUTION_200: - if (test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_200_200_CAPABLE)) - { - s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; - break; - } - span_log(&s->logging, SPAN_LOG_FLOW, "Remote FAX does not support fine resolution. Squashing image.\n"); - /* Fall through */ + s->min_scan_time_code = translate_min_scan_time[1][min_bits_field]; + break; case T4_Y_RESOLUTION_STANDARD: case T4_Y_RESOLUTION_100: s->min_scan_time_code = translate_min_scan_time[0][min_bits_field]; @@ -2689,6 +2539,8 @@ static void set_min_scan_time(t30_state_t *s) static int start_sending_document(t30_state_t *s) { + int res; + if (s->tx_file[0] == '\0') { /* There is nothing to send */ @@ -2703,27 +2555,68 @@ static int start_sending_document(t30_state_t *s) return -1; } s->operation_in_progress = OPERATION_IN_PROGRESS_T4_TX; - t4_tx_get_pages_in_file(&s->t4.tx); - t4_tx_set_tx_encoding(&s->t4.tx, s->line_compression); + t4_tx_set_local_ident(&s->t4.tx, s->tx_info.ident); t4_tx_set_header_info(&s->t4.tx, s->header_info); if (s->use_own_tz) t4_tx_set_header_tz(&s->t4.tx, &s->tz); - if (tx_start_page(s)) + t4_tx_get_pages_in_file(&s->t4.tx); + + if ((res = t4_tx_set_tx_image_format(&s->t4.tx, + s->mutual_compressions, + s->mutual_image_sizes, + s->mutual_bilevel_resolutions, + s->mutual_colour_resolutions)) < 0) { - span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n"); - t30_set_status(s, T30_ERR_FILEERROR); + switch (res) + { + case T4_IMAGE_FORMAT_INCOMPATIBLE: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image format\n"); + t30_set_status(s, T30_ERR_BADTIFFHDR); + break; + case T4_IMAGE_FORMAT_NOSIZESUPPORT: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image size\n"); + t30_set_status(s, T30_ERR_NOSIZESUPPORT); + break; + case T4_IMAGE_FORMAT_NORESSUPPORT: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image resolution\n"); + t30_set_status(s, T30_ERR_NORESSUPPORT); + break; + default: + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image format\n"); + t30_set_status(s, T30_ERR_BADTIFF); + break; + } return -1; } + s->line_image_type = t4_tx_get_tx_image_type(&s->t4.tx); + s->line_compression = t4_tx_get_tx_compression(&s->t4.tx); + s->image_width = t4_tx_get_tx_image_width(&s->t4.tx); + s->line_width_code = t4_tx_get_tx_image_width_code(&s->t4.tx); s->x_resolution = t4_tx_get_tx_x_resolution(&s->t4.tx); s->y_resolution = t4_tx_get_tx_y_resolution(&s->t4.tx); - s->image_width = t4_tx_get_tx_image_width(&s->t4.tx); - /* The minimum scan time to be used can't be evaluated until we know the Y resolution, and - must be evaluated before the minimum scan row bits can be evaluated. */ + s->current_page_resolution = t4_tx_get_tx_resolution(&s->t4.tx); + + span_log(&s->logging, + SPAN_LOG_FLOW, + "Choose image type %s (%d), compression %s (%d)\n", + t4_image_type_to_str(s->line_image_type), + s->line_image_type, + t4_compression_to_str(s->line_compression), + s->line_compression); + + /* The minimum scan time to be used can't be evaluated until we know the Y resolution. */ set_min_scan_time(s); + if (tx_start_page(s)) + { + span_log(&s->logging, SPAN_LOG_WARNING, "Something seems to be wrong in the file\n"); + t30_set_status(s, T30_ERR_BADTIFFHDR); + return -1; + } + if (s->error_correcting_mode) { if (get_partial_ecm_page(s) == 0) @@ -2795,21 +2688,6 @@ static int process_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) send_dcn(s); return -1; } - - /* Choose a compression scheme from amongst those mutually available */ - if ((s->mutual_compressions & T4_COMPRESSION_T85_L0)) - s->line_compression = T4_COMPRESSION_T85_L0; - else if ((s->mutual_compressions & T4_COMPRESSION_T85)) - s->line_compression = T4_COMPRESSION_T85; - else if ((s->mutual_compressions & T4_COMPRESSION_T6)) - s->line_compression = T4_COMPRESSION_T6; - else if ((s->mutual_compressions & T4_COMPRESSION_T4_2D)) - s->line_compression = T4_COMPRESSION_T4_2D; - else - s->line_compression = T4_COMPRESSION_T4_1D; - - span_log(&s->logging, SPAN_LOG_FLOW, "Choose compression %s (%d)\n", t4_compression_to_str(s->line_compression), s->line_compression); - if (s->phase_b_handler) { new_status = s->phase_b_handler(s, s->phase_b_user_data, msg[2]); @@ -6825,7 +6703,7 @@ SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s, | T4_SUPPORT_LENGTH_UNLIMITED; /* Set the output encoding to something safe. Most things get 1D and 2D encoding right. Quite a lot get other things wrong. */ - s->supported_output_compressions = T4_COMPRESSION_T4_2D; + s->supported_output_compressions = T4_COMPRESSION_T4_2D | T4_COMPRESSION_T42_T81; s->local_min_scan_time_code = T30_MIN_SCAN_0MS; span_log_init(&s->logging, SPAN_LOG_NONE, NULL); span_log_set_protocol(&s->logging, "T.30"); diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 5a7bb6bc45..b7bed5dd18 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -234,11 +234,24 @@ static int set_tiff_directory_info(t4_rx_state_t *s) #endif #if defined(SPANDSP_SUPPORT_T42) case T4_COMPRESSION_T42_T81: + output_compression = COMPRESSION_JPEG; + bits_per_sample = 8; + if (t->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) + { + samples_per_pixel = 3; + photometric = PHOTOMETRIC_YCBCR; //PHOTOMETRIC_ITULAB; + } + else + { + samples_per_pixel = 1; + photometric = PHOTOMETRIC_MINISBLACK; + } + break; case T4_COMPRESSION_SYCC_T81: output_compression = COMPRESSION_JPEG; bits_per_sample = 8; samples_per_pixel = 3; - photometric = PHOTOMETRIC_ITULAB; + photometric = PHOTOMETRIC_YCBCR; break; #endif #if defined(SPANDSP_SUPPORT_T43) @@ -288,6 +301,13 @@ static int set_tiff_directory_info(t4_rx_state_t *s) TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB); + if (t->compression == T4_COMPRESSION_T42_T81) + { + TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 2, 2); + //TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 1, 1); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGQUALITY, 75); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } /* TIFFTAG_STRIPBYTECOUNTS and TIFFTAG_STRIPOFFSETS are added automatically */ x_resolution = s->metadata.x_resolution/100.0f; @@ -344,30 +364,32 @@ static int set_tiff_directory_info(t4_rx_state_t *s) is always one greater than the highest page number in the file. */ s->tiff.pages_in_file = s->current_page + 1; s->metadata.image_length = 0; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - /* We only get bad row info from pages received in non-ECM mode. */ - if (output_compression == COMPRESSION_CCITT_T4) + case 0: + s->metadata.image_length = 1024; + break; + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: + if ((s->metadata.compression & (T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D))) { - if (s->decoder.t4_t6.bad_rows) + /* We only get bad row info from pages received in non-ECM mode. */ + if (output_compression == COMPRESSION_CCITT_T4) { - TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->decoder.t4_t6.bad_rows); - TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->decoder.t4_t6.longest_bad_row_run); - TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED); - } - else - { - TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN); + if (s->decoder.t4_t6.bad_rows) + { + TIFFSetField(t->tiff_file, TIFFTAG_BADFAXLINES, s->decoder.t4_t6.bad_rows); + TIFFSetField(t->tiff_file, TIFFTAG_CONSECUTIVEBADFAXLINES, s->decoder.t4_t6.longest_bad_row_run); + TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_REGENERATED); + } + else + { + TIFFSetField(t->tiff_file, TIFFTAG_CLEANFAXDATA, CLEANFAXDATA_CLEAN); + } } } - /* Fall through */ - case T4_COMPRESSION_T6: s->metadata.image_length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: s->metadata.image_length = t85_decode_get_image_length(&s->decoder.t85); break; #if defined(SPANDSP_SUPPORT_T88) @@ -401,6 +423,34 @@ static int set_tiff_directory_info(t4_rx_state_t *s) /* Create a placeholder for the global parameters IFD, to be filled in later */ TIFFSetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, 0); } + +#if 0 + /* Paletised image? */ + TIFFSetField(t->tiff_file, TIFFTAG_INDEXED, 1); + /* T.44 mode */ + TIFFSetField(t->tiff_file, TIFFTAG_MODENUMBER, 0); + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 2\n"); + { + float xxx[] = {20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0}; + TIFFSetField(t->tiff_file, TIFFTAG_DECODE, (uint16) 2*samples_per_pixel, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 3\n"); + { + uint16_t xxx[] = {12, 34, 45, 67}; + TIFFSetField(t->tiff_file, TIFFTAG_IMAGEBASECOLOR, (uint16_t) samples_per_pixel, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 4\n"); + TIFFSetField(t->tiff_file, TIFFTAG_T82OPTIONS, 0); + { + uint32_t xxx[] = {34, 56, 78, 90}; + TIFFSetField(t->tiff_file, TIFFTAG_STRIPROWCOUNTS, (uint16_t) 5, xxx); + } + span_log(&s->logging, SPAN_LOG_FLOW, "TIFF/FX stuff 5\n"); + { + uint32_t xxx[] = {2, 3}; + TIFFSetField(t->tiff_file, TIFFTAG_IMAGELAYER, xxx); + } +#endif #endif return 0; } @@ -450,10 +500,10 @@ static int write_tiff_t85_image(t4_rx_state_t *s) if (buf_len < image_len + 65536) { buf_len += 65536; - if ((buf2 = realloc(buf, buf_len)) == NULL) + if ((buf2 = span_realloc(buf, buf_len)) == NULL) { if (buf) - free(buf); + span_free(buf); return -1; } buf = buf2; @@ -468,7 +518,7 @@ static int write_tiff_t85_image(t4_rx_state_t *s) return -1; } t85_encode_release(&t85); - free(buf); + span_free(buf); return 0; } /*- End of function --------------------------------------------------------*/ @@ -496,10 +546,10 @@ static int write_tiff_t43_image(t4_rx_state_t *s) if (buf_len < image_len + 65536) { buf_len += 65536; - if ((buf2 = realloc(buf, buf_len)) == NULL) + if ((buf2 = span_realloc(buf, buf_len)) == NULL) { if (buf) - free(buf); + span_free(buf); return -1; } buf = buf2; @@ -511,7 +561,7 @@ static int write_tiff_t43_image(t4_rx_state_t *s) if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, buf, image_len) < 0) span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file); t43_encode_release(&t43); - free(buf); + span_free(buf); return 0; } /*- End of function --------------------------------------------------------*/ @@ -525,49 +575,57 @@ static int write_tiff_image(t4_rx_state_t *s) #endif t = &s->tiff; - if (t->image_buffer == NULL || t->image_size <= 0) + if (s->pre_encoded_ptr <= 0 && (t->image_buffer == NULL || t->image_size <= 0)) return -1; /* Set up the TIFF directory info... */ set_tiff_directory_info(s); /* ...Put the directory in the file before the image data, to get them in the order specified for TIFF/F files... */ - if (!TIFFCheckpointDirectory(t->tiff_file)) - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page); + //if (!TIFFCheckpointDirectory(t->tiff_file)) + // span_log(&s->logging, SPAN_LOG_WARNING, "%s: Failed to checkpoint directory for page %d.\n", t->file, s->current_page); /* ...and write out the image... */ - switch (t->compression) + if (s->pre_encoded_ptr > 0) { - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t85_image(s) < 0) - return -1; - break; + if (TIFFWriteRawStrip(s->tiff.tiff_file, 0, s->pre_encoded_buf, s->pre_encoded_ptr) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", s->tiff.file); + } + else + { + switch (t->compression) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t85_image(s) < 0) + return -1; + break; #if defined(SPANDSP_SUPPORT_T88) - case T4_COMPRESSION_T88: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t88_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T88: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t88_image(s) < 0) + return -1; + break; #endif #if defined(SPANDSP_SUPPORT_T43) - case T4_COMPRESSION_T43: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t43_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T43: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t43_image(s) < 0) + return -1; + break; #endif #if defined(SPANDSP_SUPPORT_T45) - case T4_COMPRESSION_T45: - /* We need to perform this compression here, as libtiff does not understand it. */ - if (write_tiff_t45_image(s) < 0) - return -1; - break; + case T4_COMPRESSION_T45: + /* We need to perform this compression here, as libtiff does not understand it. */ + if (write_tiff_t45_image(s) < 0) + return -1; + break; #endif - default: - /* Let libtiff do the compression */ - if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) - span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); - break; + default: + /* Let libtiff do the compression */ + if (TIFFWriteEncodedStrip(t->tiff_file, 0, t->image_buffer, t->image_size) < 0) + span_log(&s->logging, SPAN_LOG_WARNING, "%s: Error writing TIFF strip.\n", t->file); + break; + } } /* ...then finalise the directory entry, and libtiff is happy. */ if (!TIFFWriteDirectory(t->tiff_file)) @@ -633,7 +691,7 @@ static int close_tiff_output_file(t4_rx_state_t *s) put in it. */ if (s->current_page == 0) remove(s->tiff.file); - free((char *) s->tiff.file); + span_free((char *) s->tiff.file); } s->tiff.file = NULL; return 0; @@ -646,7 +704,7 @@ static void tiff_rx_release(t4_rx_state_t *s) close_tiff_output_file(s); if (s->tiff.image_buffer) { - free(s->tiff.image_buffer); + span_free(s->tiff.image_buffer); s->tiff.image_buffer = NULL; s->tiff.image_size = 0; s->tiff.image_buffer_size = 0; @@ -673,10 +731,10 @@ SPAN_DECLARE(int) t4_rx_put(t4_rx_state_t *s, const uint8_t buf[], size_t len) if (s->pre_encoded_len < s->pre_encoded_ptr + 65536) { s->pre_encoded_len += 65536; - if ((buf2 = realloc(s->pre_encoded_buf, s->pre_encoded_len)) == NULL) + if ((buf2 = span_realloc(s->pre_encoded_buf, s->pre_encoded_len)) == NULL) { if (s->pre_encoded_buf) - free(s->pre_encoded_buf); + span_free(s->pre_encoded_buf); return -1; } s->pre_encoded_buf = buf2; @@ -757,11 +815,19 @@ static void select_tiff_compression(t4_rx_state_t *s, int output_image_type) else if ((s->supported_tiff_compressions & T4_COMPRESSION_T43)) s->tiff.compression = T4_COMPRESSION_T43; } + s->tiff.image_type = output_image_type; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) { +#if 0 + output_image_type = T4_IMAGE_TYPE_BILEVEL; + s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); + s->current_decoder = 0; + return 0; +#else switch (compression) { case T4_COMPRESSION_T4_1D: @@ -775,6 +841,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t4_t6_decode_init(&s->decoder.t4_t6, compression, s->metadata.image_width, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6; break; } s->metadata.compression = compression; @@ -789,14 +856,15 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t85_decode_init(&s->decoder.t85, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ t85_decode_set_image_size_constraints(&s->decoder.t85, T4_WIDTH_1200_A3, 0); break; } - select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); return 0; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -805,10 +873,12 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) case T4_COMPRESSION_T88: break; default: + t88_decode_init(&s->decoder.t88, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T88; break; } - select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); s->metadata.compression = compression; + select_tiff_compression(s, T4_IMAGE_TYPE_BILEVEL); return 0; #endif case T4_COMPRESSION_T42_T81: @@ -820,6 +890,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t42_decode_init(&s->decoder.t42, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T42_T81; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ @@ -837,6 +908,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) break; default: t43_decode_init(&s->decoder.t43, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T43; /* Constrain received images to the maximum width of any FAX. This will avoid one potential cause of trouble, where a bad received image has a gigantic dimension that sucks our memory dry. */ @@ -854,6 +926,8 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) case T4_COMPRESSION_T45: break; default: + t45_decode_init(&s->decoder.t45, s->row_handler, s->row_handler_user_data); + s->current_decoder = T4_COMPRESSION_T45; break; } s->metadata.compression = compression; @@ -863,6 +937,7 @@ SPAN_DECLARE(int) t4_rx_set_rx_encoding(t4_rx_state_t *s, int compression) } return -1; +#endif } /*- End of function --------------------------------------------------------*/ @@ -876,14 +951,11 @@ SPAN_DECLARE(int) t4_rx_set_row_write_handler(t4_rx_state_t *s, t4_row_write_han { s->row_handler = handler; s->row_handler_user_data = user_data; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: return t4_t6_decode_set_row_write_handler(&s->decoder.t4_t6, handler, user_data); - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: return t85_decode_set_row_write_handler(&s->decoder.t85, handler, user_data); #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -914,12 +986,11 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t t->image_y_resolution = s->metadata.y_resolution; t->x_resolution = s->metadata.x_resolution; t->y_resolution = s->metadata.y_resolution; + t->compression = s->metadata.compression; - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t4_t6_decode_get_image_width(&s->decoder.t4_t6); t->length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); @@ -930,8 +1001,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t t->bad_rows = s->decoder.t4_t6.bad_rows; t->longest_bad_row_run = s->decoder.t4_t6.longest_bad_row_run; break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t85_decode_get_image_width(&s->decoder.t85); t->length = t85_decode_get_image_length(&s->decoder.t85); @@ -945,7 +1015,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t break; #endif case T4_COMPRESSION_T42_T81: - t->type = T4_IMAGE_TYPE_COLOUR_8BIT; //T4_IMAGE_TYPE_GRAY_8BIT; + t->type = T4_IMAGE_TYPE_GRAY_8BIT; //T4_IMAGE_TYPE_COLOUR_8BIT; t->width = t42_decode_get_image_width(&s->decoder.t42); t->length = t42_decode_get_image_length(&s->decoder.t42); t->image_type = t->type; @@ -976,21 +1046,18 @@ SPAN_DECLARE(int) t4_rx_start_page(t4_rx_state_t *s) { span_log(&s->logging, SPAN_LOG_FLOW, "Start rx page %d - compression %s\n", s->current_page, t4_compression_to_str(s->metadata.compression)); - switch (s->metadata.compression) + switch (s->current_decoder) { case 0: s->pre_encoded_ptr = 0; s->pre_encoded_len = 0; s->image_put_handler = NULL; break; - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: t4_t6_decode_restart(&s->decoder.t4_t6, s->metadata.image_width); s->image_put_handler = (t4_image_put_handler_t) t4_t6_decode_put; break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: t85_decode_restart(&s->decoder.t85); s->image_put_handler = (t4_image_put_handler_t) t85_decode_put; break; @@ -1036,7 +1103,7 @@ static int tiff_row_write_handler(void *user_data, const uint8_t buf[], size_t l { if (s->tiff.image_size + len >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_buffer_size + 100*len)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_buffer_size + 100*len)) == NULL) return -1; s->tiff.image_buffer_size += 100*len; s->tiff.image_buffer = t; @@ -1057,15 +1124,15 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s) if (s->image_put_handler) s->image_put_handler((void *) &s->decoder, NULL, 0); - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case 0: + length = s->pre_encoded_ptr; + break; + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: length = t4_t6_decode_get_image_length(&s->decoder.t4_t6); break; - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: length = t85_decode_get_image_length(&s->decoder.t85); break; #if defined(SPANDSP_SUPPORT_T88) @@ -1100,6 +1167,7 @@ SPAN_DECLARE(int) t4_rx_end_page(t4_rx_state_t *s) if (write_tiff_image(s) == 0) s->current_page++; s->tiff.image_size = 0; + s->pre_encoded_ptr = 0; } else { @@ -1142,6 +1210,7 @@ SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int s->metadata.y_resolution = T4_Y_RESOLUTION_FINE; s->current_page = 0; + s->current_decoder = 0; /* Default handler */ s->row_handler = tiff_row_write_handler; @@ -1153,7 +1222,7 @@ SPAN_DECLARE(t4_rx_state_t *) t4_rx_init(t4_rx_state_t *s, const char *file, int if (open_tiff_output_file(s, file) < 0) { if (allocated) - free(s); + span_free(s); return NULL; } /* Save the file name for logging reports. */ @@ -1167,14 +1236,11 @@ SPAN_DECLARE(int) t4_rx_release(t4_rx_state_t *s) { if (s->tiff.file) tiff_rx_release(s); - switch (s->metadata.compression) + switch (s->current_decoder) { - case T4_COMPRESSION_T4_1D: - case T4_COMPRESSION_T4_2D: - case T4_COMPRESSION_T6: + case T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6: return t4_t6_decode_release(&s->decoder.t4_t6); - case T4_COMPRESSION_T85: - case T4_COMPRESSION_T85_L0: + case T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0: return t85_decode_release(&s->decoder.t85); #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: @@ -1200,7 +1266,7 @@ SPAN_DECLARE(int) t4_rx_free(t4_rx_state_t *s) int ret; ret = t4_rx_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 9e8f3e678c..35d3632f60 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -96,41 +96,37 @@ typedef struct int bit_mask; } packer_t; -typedef struct -{ - float resolution; - int code; -} res_table_t; +static void t4_tx_set_image_type(t4_tx_state_t *s, int image_type); +static void set_image_width(t4_tx_state_t *s, uint32_t image_width); +static void set_image_length(t4_tx_state_t *s, uint32_t image_length); -static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length); - -static const res_table_t x_res_table[] = +static const float x_res_table[] = { - { 100.0f/CM_PER_INCH, T4_X_RESOLUTION_100}, - { 102.0f/CM_PER_INCH, T4_X_RESOLUTION_R4}, - { 200.0f/CM_PER_INCH, T4_X_RESOLUTION_200}, - { 204.0f/CM_PER_INCH, T4_X_RESOLUTION_R8}, - { 300.0f/CM_PER_INCH, T4_X_RESOLUTION_300}, - { 400.0f/CM_PER_INCH, T4_X_RESOLUTION_400}, - { 408.0f/CM_PER_INCH, T4_X_RESOLUTION_R16}, - { 600.0f/CM_PER_INCH, T4_X_RESOLUTION_600}, - {1200.0f/CM_PER_INCH, T4_X_RESOLUTION_1200}, - { -1.00f, -1} + 100.0f*100.0f/CM_PER_INCH, + 102.0f*100.0f/CM_PER_INCH, + 200.0f*100.0f/CM_PER_INCH, + 204.0f*100.0f/CM_PER_INCH, + 300.0f*100.0f/CM_PER_INCH, + 400.0f*100.0f/CM_PER_INCH, + 408.0f*100.0f/CM_PER_INCH, + 600.0f*100.0f/CM_PER_INCH, + 1200.0f*100.0f/CM_PER_INCH, + -1.00f }; -static const res_table_t y_res_table[] = +static const float y_res_table[] = { - { 38.50f, T4_Y_RESOLUTION_STANDARD}, - { 100.0f/CM_PER_INCH, T4_Y_RESOLUTION_100}, - { 77.00f, T4_Y_RESOLUTION_FINE}, - { 200.0f/CM_PER_INCH, T4_Y_RESOLUTION_200}, - { 300.0f/CM_PER_INCH, T4_Y_RESOLUTION_300}, - { 154.00f, T4_Y_RESOLUTION_SUPERFINE}, - { 400.0f/CM_PER_INCH, T4_Y_RESOLUTION_400}, - { 600.0f/CM_PER_INCH, T4_Y_RESOLUTION_600}, - { 800.0f/CM_PER_INCH, T4_Y_RESOLUTION_800}, - {1200.0f/CM_PER_INCH, T4_Y_RESOLUTION_1200}, - { -1.00f, -1} + 38.50f*100.0f, + 100.0f*100.0f/CM_PER_INCH, + 77.00f*100.0f, + 200.0f*100.0f/CM_PER_INCH, + 300.0f*100.0f/CM_PER_INCH, + 154.00f*100.0f, + 400.0f*100.0f/CM_PER_INCH, + 600.0f*100.0f/CM_PER_INCH, + 800.0f*100.0f/CM_PER_INCH, + 1200.0f*100.0f/CM_PER_INCH, + -1.00f }; static const int resolution_map[10][9] = @@ -215,7 +211,65 @@ SPAN_DECLARE(void) TIFF_FX_init(void) /*- End of function --------------------------------------------------------*/ #endif -static int match_resolution(int res_unit, float actual, const res_table_t table[]) +static int code_to_x_resolution(int code) +{ + static const int xxx[] = + { + T4_X_RESOLUTION_R8, /* R8 x standard */ + T4_X_RESOLUTION_R8, /* R8 x fine */ + T4_X_RESOLUTION_R8, /* R8 x superfine */ + T4_X_RESOLUTION_R16, /* R16 x superfine */ + T4_X_RESOLUTION_100, /* 100x100 */ + T4_X_RESOLUTION_200, /* 200x100 */ + T4_X_RESOLUTION_200, /* 200x200 */ + T4_X_RESOLUTION_200, /* 200x400 */ + T4_X_RESOLUTION_300, /* 300x300 */ + T4_X_RESOLUTION_300, /* 300x600 */ + T4_X_RESOLUTION_400, /* 400x400 */ + T4_X_RESOLUTION_400, /* 400x800 */ + T4_X_RESOLUTION_600, /* 600x600 */ + T4_X_RESOLUTION_600, /* 600x1200 */ + T4_X_RESOLUTION_1200 /* 1200x1200 */ + }; + int entry; + + entry = top_bit(code); + if (entry < 0 || entry > 14) + return 0; + return xxx[entry]; +} +/*- End of function --------------------------------------------------------*/ + +static int code_to_y_resolution(int code) +{ + static const int yyy[] = + { + T4_Y_RESOLUTION_STANDARD, /* R8 x standard */ + T4_Y_RESOLUTION_FINE, /* R8 x fine */ + T4_Y_RESOLUTION_SUPERFINE, /* R8 x superfine */ + T4_Y_RESOLUTION_SUPERFINE, /* R16 x superfine */ + T4_Y_RESOLUTION_100, /* 100x100 */ + T4_Y_RESOLUTION_100, /* 200x100 */ + T4_Y_RESOLUTION_200, /* 200x200 */ + T4_Y_RESOLUTION_400, /* 200x400 */ + T4_Y_RESOLUTION_300, /* 300x300 */ + T4_Y_RESOLUTION_600, /* 300x600 */ + T4_Y_RESOLUTION_400, /* 400x400 */ + T4_Y_RESOLUTION_800, /* 400x800 */ + T4_Y_RESOLUTION_600, /* 600x600 */ + T4_Y_RESOLUTION_1200, /* 600x1200 */ + T4_Y_RESOLUTION_1200 /* 1200x1200 */ + }; + int entry; + + entry = top_bit(code); + if (entry < 0 || entry > 14) + return 0; + return yyy[entry]; +} +/*- End of function --------------------------------------------------------*/ + +static int match_resolution(float actual, const float table[]) { int i; int best_entry; @@ -225,16 +279,14 @@ static int match_resolution(int res_unit, float actual, const res_table_t table[ if (actual == 0.0f) return -1; - if (res_unit == RESUNIT_INCH) - actual /= CM_PER_INCH; best_ratio = 0.0f; best_entry = -1; - for (i = 0; table[i].code > 0; i++) + for (i = 0; table[i] > 0.0f; i++) { - if (actual > table[i].resolution) - ratio = table[i].resolution/actual; + if (actual > table[i]) + ratio = table[i]/actual; else - ratio = actual/table[i].resolution; + ratio = actual/table[i]; if (ratio > best_ratio) { best_entry = i; @@ -247,7 +299,51 @@ static int match_resolution(int res_unit, float actual, const res_table_t table[ } /*- End of function --------------------------------------------------------*/ -#if 0 //defined(SPANDSP_SUPPORT_TIFF_FX) +static int best_colour_resolution(float actual, int allowed_resolutions) +{ + static const struct + { + float resolution; + int resolution_code; + } x_res_table[] = + { + { 100.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_100_100}, + { 200.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_200_200}, + { 300.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_300_300}, + { 400.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_400_400}, + { 600.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_600_600}, + {1200.0f*100.0f/CM_PER_INCH, T4_RESOLUTION_1200_1200}, + { -1.00f, -1} + }; + int i; + int best_entry; + float best_ratio; + float ratio; + + if (actual == 0.0f) + return -1; + + best_ratio = 0.0f; + best_entry = -1; + for (i = 0; x_res_table[i].resolution > 0.0f; i++) + { + if (!(allowed_resolutions & x_res_table[i].resolution_code)) + continue; + if (actual > x_res_table[i].resolution) + ratio = x_res_table[i].resolution/actual; + else + ratio = actual/x_res_table[i].resolution; + if (ratio > best_ratio) + { + best_entry = i; + best_ratio = ratio; + } + } + return x_res_table[best_entry].resolution_code; +} +/*- End of function --------------------------------------------------------*/ + +#if defined(SPANDSP_SUPPORT_TIFF_FX) static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) { int i; @@ -266,7 +362,7 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) /* TODO: This only allows for 8 bit deep maps */ span_log(&s->logging, SPAN_LOG_FLOW, "Got a colour map\n"); s->colour_map_entries = 1 << bits_per_sample; - if ((s->colour_map = realloc(s->colour_map, 3*s->colour_map_entries)) == NULL) + if ((s->colour_map = span_realloc(s->colour_map, 3*s->colour_map_entries)) == NULL) return -1; #if 0 /* Sweep the colormap in the proper order */ @@ -286,7 +382,7 @@ static int read_colour_map(t4_tx_state_t *s, int bits_per_sample) s->colour_map[2*s->colour_map_entries + i] = (map_b[i] >> 8) & 0xFF; } #endif - lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, 256); + lab_to_srgb(&s->lab_params, s->colour_map, s->colour_map, s->colour_map_entries); for (i = 0; i < s->colour_map_entries; i++) span_log(&s->logging, SPAN_LOG_FLOW, "Map %3d - %5d %5d %5d\n", i, s->colour_map[3*i], s->colour_map[3*i + 1], s->colour_map[3*i + 2]); return 0; @@ -309,8 +405,18 @@ static int get_tiff_directory_info(t4_tx_state_t *s) }; char *u; char uu[10]; + float *fl_parms; uint64_t diroff; + float lmin; + float lmax; + float amin; + float amax; + float bmin; + float bmax; uint8_t parm8; +#endif +#if defined(TIFFTAG_INDEXED) + uint16_t parm16; #endif uint32_t parm32; int best_x_entry; @@ -321,6 +427,8 @@ static int get_tiff_directory_info(t4_tx_state_t *s) uint16_t bits_per_sample; uint16_t samples_per_pixel; uint16_t res_unit; + uint16_t YCbCrSubsample_horiz; + uint16_t YCbCrSubsample_vert; t = &s->tiff; bits_per_sample = 1; @@ -331,6 +439,8 @@ static int get_tiff_directory_info(t4_tx_state_t *s) t->image_type = T4_IMAGE_TYPE_BILEVEL; else if (samples_per_pixel == 3 && bits_per_sample == 1) t->image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; + else if (samples_per_pixel == 4 && bits_per_sample == 1) + t->image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; else if (samples_per_pixel == 1 && bits_per_sample == 8) t->image_type = T4_IMAGE_TYPE_GRAY_8BIT; else if (samples_per_pixel == 1 && bits_per_sample > 8) @@ -341,62 +451,111 @@ static int get_tiff_directory_info(t4_tx_state_t *s) t->image_type = T4_IMAGE_TYPE_COLOUR_12BIT; else return -1; -#if 0 - /* Limit ourselves to plain black and white pages */ - if (t->image_type != T4_IMAGE_TYPE_BILEVEL) - return -1; + +#if defined(TIFFTAG_INDEXED) + parm16 = 0; + if (TIFFGetField(t->tiff_file, TIFFTAG_INDEXED, &parm16)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Indexed %s (%u)\n", (parm16) ? "palette image" : "non-palette image", parm16); + if (parm16 == 1) + { + /* Its an indexed image, so its really a colour image, even though it may have only one sample per pixel */ + if (samples_per_pixel == 1 && bits_per_sample == 8) + t->image_type = T4_IMAGE_TYPE_COLOUR_8BIT; + else if (samples_per_pixel == 1 && bits_per_sample > 8) + t->image_type = T4_IMAGE_TYPE_COLOUR_12BIT; + } + } #endif + parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32); - t->image_width = - s->metadata.image_width = parm32; + t->image_width = parm32; parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGELENGTH, &parm32); - t->image_length = - s->metadata.image_length = parm32; + t->image_length = parm32; + x_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution); y_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_YRESOLUTION, &y_resolution); res_unit = RESUNIT_INCH; TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit); + + t->x_resolution = x_resolution*100.0f; + t->y_resolution = y_resolution*100.0f; + if (res_unit == RESUNIT_INCH) + { + t->x_resolution /= CM_PER_INCH; + t->y_resolution /= CM_PER_INCH; + } + + if (((best_x_entry = match_resolution(t->x_resolution, x_res_table)) >= 0) + && + ((best_y_entry = match_resolution(t->y_resolution, y_res_table)) >= 0)) + { + t->resolution_code = resolution_map[best_y_entry][best_x_entry]; + } + else + { + t->resolution_code = 0; + } + t->photo_metric = PHOTOMETRIC_MINISWHITE; TIFFGetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, &t->photo_metric); - set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f); + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); t->compression = -1; TIFFGetField(t->tiff_file, TIFFTAG_COMPRESSION, &t->compression); + switch (t->compression) + { + case COMPRESSION_CCITT_T4: + span_log(&s->logging, SPAN_LOG_FLOW, "T.4\n"); + break; + case COMPRESSION_CCITT_T6: + span_log(&s->logging, SPAN_LOG_FLOW, "T.6\n"); + break; + case COMPRESSION_T85: + span_log(&s->logging, SPAN_LOG_FLOW, "T.85\n"); + break; +#if defined(SPANDSP_SUPPORT_T43) + case COMPRESSION_T43: + span_log(&s->logging, SPAN_LOG_FLOW, "T.43\n"); + break; +#endif + case COMPRESSION_JPEG: + span_log(&s->logging, SPAN_LOG_FLOW, "JPEG\n"); + if (t->photo_metric == PHOTOMETRIC_ITULAB) + span_log(&s->logging, SPAN_LOG_FLOW, "ITULAB\n"); + break; + case COMPRESSION_NONE: + span_log(&s->logging, SPAN_LOG_FLOW, "No compression\n"); + break; + default: + span_log(&s->logging, SPAN_LOG_FLOW, "Unexpected compression %d\n", t->compression); + break; + } + +#if defined(SPANDSP_SUPPORT_TIFF_FX) + read_colour_map(s, bits_per_sample); +#endif + + YCbCrSubsample_horiz = 0; + YCbCrSubsample_vert = 0; + if (TIFFGetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, &YCbCrSubsample_horiz, &YCbCrSubsample_vert)) + span_log(&s->logging, SPAN_LOG_FLOW, "Subsampling %d %d\n", YCbCrSubsample_horiz, YCbCrSubsample_vert); + t->fill_order = FILLORDER_LSB2MSB; - if (res_unit == RESUNIT_INCH) - t->x_resolution = x_resolution*100.0f/CM_PER_INCH; - else - t->x_resolution = x_resolution*100.0f; - /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */ - if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0) - best_x_entry = 3; - s->metadata.x_resolution = x_res_table[best_x_entry].code; - - if (res_unit == RESUNIT_INCH) - t->y_resolution = y_resolution*100.0f/CM_PER_INCH; - else - t->y_resolution = y_resolution*100.0f; - if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0) - best_y_entry = 0; - s->metadata.y_resolution = y_res_table[best_y_entry].code; - - s->metadata.resolution_code = resolution_map[best_y_entry][best_x_entry]; - - t4_tx_set_image_width(s, s->metadata.image_width); - t4_tx_set_image_length(s, s->metadata.image_length); - t4_tx_set_max_2d_rows_per_1d_row(s, -s->metadata.y_resolution); #if defined(SPANDSP_SUPPORT_TIFF_FX) if (TIFFGetField(t->tiff_file, TIFFTAG_PROFILETYPE, &parm32)) span_log(&s->logging, SPAN_LOG_FLOW, "Profile type %u\n", parm32); if (TIFFGetField(t->tiff_file, TIFFTAG_FAXPROFILE, &parm8)) span_log(&s->logging, SPAN_LOG_FLOW, "FAX profile %s (%u)\n", tiff_fx_fax_profiles[parm8], parm8); + if (TIFFGetField(t->tiff_file, TIFFTAG_CODINGMETHODS, &parm32)) span_log(&s->logging, SPAN_LOG_FLOW, "Coding methods 0x%x\n", parm32); if (TIFFGetField(t->tiff_file, TIFFTAG_VERSIONYEAR, &u)) @@ -408,11 +567,60 @@ static int get_tiff_directory_info(t4_tx_state_t *s) if (TIFFGetField(t->tiff_file, TIFFTAG_MODENUMBER, &parm8)) span_log(&s->logging, SPAN_LOG_FLOW, "Mode number %u\n", parm8); + switch (t->photo_metric) + { + case PHOTOMETRIC_ITULAB: +#if 1 + /* 8 bit version */ + lmin = 0.0f; + lmax = 100.0f; + amin = -21760.0f/255.0f; + amax = 21590.0f/255.0f; + bmin = -19200.0f/255.0f; + bmax = 31800.0f/255.0f; +#else + /* 12 bit version */ + lmin = 0.0f; + lmax = 100.0f; + amin = -348160.0f/4095.0f + amax = 347990.0f/4095.0f + bmin = -307200.0f/4095.0f + bmax = 511800.0f/4095.0f +#endif + break; + default: + lmin = 0.0f; + lmax = 0.0f; + amin = 0.0f; + amax = 0.0f; + bmin = 0.0f; + bmax = 0.0f; + break; + } + + if (TIFFGetField(t->tiff_file, TIFFTAG_DECODE, &parm16, &fl_parms)) + { + lmin = fl_parms[0]; + lmax = fl_parms[1]; + amin = fl_parms[2]; + amax = fl_parms[3]; + bmin = fl_parms[4]; + bmax = fl_parms[5]; + span_log(&s->logging, SPAN_LOG_FLOW, "Got decode tag %f %f %f %f %f %f\n", lmin, lmax, amin, amax, bmin, bmax); + } + + /* TIFFTAG_IMAGEBASECOLOR */ + + if (TIFFGetField(t->tiff_file, TIFFTAG_T82OPTIONS, &parm32)) + span_log(&s->logging, SPAN_LOG_FLOW, "T.82 options 0x%x\n", parm32); + + /* TIFFTAG_STRIPROWCOUNTS */ + /* TIFFTAG_IMAGELAYER */ + /* If global parameters are present they should only be on the first page of the file. However, as we scan the file we might as well look for them on any page. */ if (TIFFGetField(t->tiff_file, TIFFTAG_GLOBALPARAMETERSIFD, &diroff)) { - span_log(&s->logging, SPAN_LOG_FLOW, "Global parameters IFD at %" PRIu64 "\n", diroff); if (!TIFFReadCustomDirectory(t->tiff_file, diroff, &tiff_fx_field_array)) { span_log(&s->logging, SPAN_LOG_FLOW, "Global parameter read failed\n"); @@ -451,8 +659,6 @@ static int test_tiff_directory_info(t4_tx_state_t *s) uint16_t bits_per_sample; uint16_t samples_per_pixel; int image_type; - int best_x_entry; - int best_y_entry; float x_resolution; float y_resolution; t4_tx_tiff_state_t *t; @@ -466,6 +672,8 @@ static int test_tiff_directory_info(t4_tx_state_t *s) image_type = T4_IMAGE_TYPE_BILEVEL; else if (samples_per_pixel == 3 && bits_per_sample == 1) image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; + else if (samples_per_pixel == 4 && bits_per_sample == 1) + image_type = T4_IMAGE_TYPE_COLOUR_BILEVEL; else if (samples_per_pixel == 1 && bits_per_sample == 8) image_type = T4_IMAGE_TYPE_GRAY_8BIT; else if (samples_per_pixel == 1 && bits_per_sample > 8) @@ -476,18 +684,14 @@ static int test_tiff_directory_info(t4_tx_state_t *s) image_type = T4_IMAGE_TYPE_COLOUR_12BIT; else image_type = -1; -#if 0 - /* Limit ourselves to plain black and white pages */ - if (t->image_type != T4_IMAGE_TYPE_BILEVEL) - return -1; -#endif if (t->image_type != image_type) return 1; parm32 = 0; TIFFGetField(t->tiff_file, TIFFTAG_IMAGEWIDTH, &parm32); if (s->tiff.image_width != (int) parm32) - return 1; + return 2; + x_resolution = 0.0f; TIFFGetField(t->tiff_file, TIFFTAG_XRESOLUTION, &x_resolution); y_resolution = 0.0f; @@ -495,16 +699,17 @@ static int test_tiff_directory_info(t4_tx_state_t *s) res_unit = RESUNIT_INCH; TIFFGetField(t->tiff_file, TIFFTAG_RESOLUTIONUNIT, &res_unit); - /* Treat everything we can't match as R8. Most FAXes are this resolution anyway. */ - if ((best_x_entry = match_resolution(res_unit, x_resolution, x_res_table)) < 0) - return 1; - if (s->metadata.x_resolution != x_res_table[best_x_entry].code) - return 1; - - if ((best_y_entry = match_resolution(res_unit, y_resolution, y_res_table)) < 0) - return 1; - if (s->metadata.y_resolution != y_res_table[best_y_entry].code) - return 1; + x_resolution *= 100.0f; + y_resolution *= 100.0f; + if (res_unit == RESUNIT_INCH) + { + x_resolution /= CM_PER_INCH; + y_resolution /= CM_PER_INCH; + } + if (s->tiff.x_resolution != (int) x_resolution) + return 3; + if (s->tiff.y_resolution != (int) y_resolution) + return 4; return 0; } @@ -535,6 +740,19 @@ static int open_tiff_input_file(t4_tx_state_t *s, const char *file) } /*- End of function --------------------------------------------------------*/ +static int metadata_row_read_handler(void *user_data, uint8_t buf[], size_t len) +{ + t4_tx_state_t *s; + + s = (t4_tx_state_t *) user_data; + if (s->tiff.row >= s->metadata.image_length) + return 0; + memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); + s->tiff.row++; + return len; +} +/*- End of function --------------------------------------------------------*/ + static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; @@ -544,6 +762,11 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) s = (t4_tx_state_t *) user_data; if (s->tiff.row >= s->tiff.image_length) return 0; + if (s->tiff.image_buffer == NULL) + { + exit(2); + return 0; + } memcpy(buf, &s->tiff.image_buffer[s->tiff.row*len], len); s->tiff.row++; @@ -551,7 +774,7 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) far end will accept, we need to squash it down to size. */ for (i = 1; i < s->row_squashing_ratio && s->tiff.row < s->tiff.image_length; i++) { - for (j = 0; j < s->tiff.image_width/8; j++) + for (j = 0; j < len; j++) buf[j] |= s->tiff.image_buffer[s->tiff.row*len + j]; s->tiff.row++; } @@ -559,19 +782,52 @@ static int tiff_row_read_handler(void *user_data, uint8_t buf[], size_t len) } /*- End of function --------------------------------------------------------*/ -static int row_read(void *user_data, uint8_t buf[], size_t len) +static int translate_row_read2(void *user_data, uint8_t buf[], size_t len) { t4_tx_state_t *s; + s = (t4_tx_state_t *) user_data; + memcpy(buf, &s->pack_buf[s->pack_ptr], len); + s->pack_ptr += len; + s->pack_row++; + return len; +} +/*- End of function --------------------------------------------------------*/ + +static int translate_row_read(void *user_data, uint8_t buf[], size_t len) +{ + t4_tx_state_t *s; + int i; + int j; + s = (t4_tx_state_t *) user_data; if (s->tiff.raw_row >= s->tiff.image_length) return 0; + if (TIFFReadScanline(s->tiff.tiff_file, buf, s->tiff.raw_row, 0) < 0) return 0; + s->tiff.raw_row++; + + /* If this is a bi-level image which is stretched more vertically than we are able + to send we need to squash it down to size. */ + for (i = 1; i < s->row_squashing_ratio; i++) + { +#if defined(_MSC_VER) + uint8_t *extra_buf = (uint8_t *) _alloca(len); +#else + uint8_t extra_buf[len]; +#endif + + if (TIFFReadScanline(s->tiff.tiff_file, extra_buf, s->tiff.raw_row, 0) < 0) + return 0; + s->tiff.raw_row++; + /* We know this is a bi-level image if we are squashing */ + for (j = 0; j < s->tiff.image_width/8; j++) + buf[j] |= extra_buf[s->tiff.image_width/8 + j]; + } if (s->apply_lab) lab_to_srgb(&s->lab_params, buf, buf, len/3); - s->tiff.raw_row++; return len; } /*- End of function --------------------------------------------------------*/ @@ -601,6 +857,35 @@ static int embedded_comment_handler(void *user_data, const uint8_t buf[], size_t } /*- End of function --------------------------------------------------------*/ +static int read_tiff_raw_image(t4_tx_state_t *s) +{ + int num_strips; + int total_len; + int len; + int i; + + num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); + total_len = 0; + for (i = 0; i < num_strips; i++) + total_len += TIFFRawStripSize(s->tiff.tiff_file, i); + if ((s->pre_encoded_buf = span_realloc(s->pre_encoded_buf, total_len)) == NULL) + return -1; + total_len = 0; + for (i = 0; i < num_strips; i++, total_len += len) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &s->pre_encoded_buf[total_len], len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); + return -1; + } + } + s->pre_encoded_len = total_len; + s->pre_encoded_ptr = 0; + return 0; +} +/*- End of function --------------------------------------------------------*/ + static int read_tiff_t85_image(t4_tx_state_t *s) { int biggest; @@ -628,9 +913,9 @@ static int read_tiff_t85_image(t4_tx_state_t *s) s->tiff.image_size = s->tiff.image_length*((s->tiff.image_width + 7)/8); if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - free(raw_data); + span_free(raw_data); return -1; } s->tiff.image_buffer_size = s->tiff.image_size; @@ -639,6 +924,7 @@ static int read_tiff_t85_image(t4_tx_state_t *s) pack.buf = s->tiff.image_buffer; pack.ptr = 0; + pack.size = s->tiff.image_size; pack.row = 0; t85_decode_init(&t85, packing_row_write_handler, &pack); t85_decode_set_comment_handler(&t85, 1000, embedded_comment_handler, s); @@ -650,7 +936,7 @@ static int read_tiff_t85_image(t4_tx_state_t *s) if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0) { span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); - free(raw_data); + span_free(raw_data); return -1; } result = t85_decode_put(&t85, raw_data, len); @@ -663,40 +949,55 @@ static int read_tiff_t85_image(t4_tx_state_t *s) len = t85_decode_get_compressed_image_size(&t85); span_log(&s->logging, SPAN_LOG_WARNING, "Compressed image is %d bytes, %d rows\n", len/8, s->tiff.image_length); t85_decode_release(&t85); - free(raw_data); + span_free(raw_data); return 0; } /*- End of function --------------------------------------------------------*/ #if defined(SPANDSP_SUPPORT_T43) -static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) +static int read_tiff_t43_image(t4_tx_state_t *s) { + int biggest; int num_strips; - int total_len; int len; int i; - int total_image_len; - int image_size; - logging_state_t *logging; + int result; + uint8_t *t; uint8_t *raw_data; + logging_state_t *logging; t43_decode_state_t t43; packer_t pack; + uint16_t bits_per_sample; + uint16_t samples_per_pixel; + + bits_per_sample = 1; + TIFFGetField(s->tiff.tiff_file, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + samples_per_pixel = 3; + TIFFGetField(s->tiff.tiff_file, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); + + samples_per_pixel = 3; num_strips = TIFFNumberOfStrips(s->tiff.tiff_file); - total_image_len = 0; + biggest = 0; for (i = 0; i < num_strips; i++) - total_image_len += TIFFRawStripSize(s->tiff.tiff_file, i); - if ((raw_data = span_alloc(total_image_len)) == NULL) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if (len > biggest) + biggest = len; + } + if ((raw_data = span_alloc(biggest)) == NULL) return -1; - total_len = 0; - for (i = 0; i < num_strips; i++, total_len += len) + s->tiff.image_size = samples_per_pixel*s->tiff.image_width*s->tiff.image_length; + if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - span_log(&s->logging, SPAN_LOG_FLOW, "TIFF read error.\n"); + span_free(raw_data); return -1; } + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; } t43_decode_init(&t43, packing_row_write_handler, &pack); @@ -704,22 +1005,35 @@ static int read_tiff_t43_image(t4_tx_state_t *s, uint8_t **buf) logging = t43_decode_get_logging_state(&t43); span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); - image_size = 3*s->metadata.image_length*s->metadata.image_width; - if ((*buf = span_alloc(image_size)) == NULL) - return -1; - - pack.buf = *buf; + pack.buf = s->tiff.image_buffer; pack.ptr = 0; + pack.size = s->tiff.image_size; pack.row = 0; - t43_decode_put(&t43, raw_data, total_len); + + result = -1; + for (i = 0; i < num_strips; i++) + { + len = TIFFRawStripSize(s->tiff.tiff_file, i); + if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, raw_data, len)) < 0) + { + span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); + span_free(raw_data); + return -1; + } + result = t43_decode_put(&t43, raw_data, len); + if (result != T4_DECODE_MORE_DATA) + break; + } + if (result == T4_DECODE_MORE_DATA) + result = t43_decode_put(&t43, NULL, 0); + t43_decode_release(&t43); - free(raw_data); - return image_size; + span_free(raw_data); + return s->tiff.image_size; } /*- End of function --------------------------------------------------------*/ #endif -#if 0 static int read_tiff_t42_t81_image(t4_tx_state_t *s) { int total_len; @@ -763,7 +1077,7 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) if ((len = TIFFReadRawStrip(s->tiff.tiff_file, i, &raw_data[total_len], total_image_len - total_len)) < 0) { span_log(&s->logging, SPAN_LOG_WARNING, "%s: TIFFReadRawStrip error.\n", s->tiff.file); - free(raw_data); + span_free(raw_data); return -1; } } @@ -776,9 +1090,9 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) s->tiff.image_size = samples_per_pixel*s->tiff.image_width*s->tiff.image_length; if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) { - free(raw_data); + span_free(raw_data); return -1; } s->tiff.image_buffer_size = s->tiff.image_size; @@ -795,11 +1109,10 @@ static int read_tiff_t42_t81_image(t4_tx_state_t *s) t42_decode_put(&t42, NULL, 0); t42_decode_release(&t42); - free(raw_data); + span_free(raw_data); return s->tiff.image_size; } /*- End of function --------------------------------------------------------*/ -#endif static int read_tiff_decompressed_image(t4_tx_state_t *s) { @@ -814,7 +1127,7 @@ static int read_tiff_decompressed_image(t4_tx_state_t *s) s->tiff.image_size = s->tiff.image_length*TIFFScanlineSize(s->tiff.tiff_file); if (s->tiff.image_size >= s->tiff.image_buffer_size) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) return -1; s->tiff.image_buffer_size = s->tiff.image_size; s->tiff.image_buffer = t; @@ -831,7 +1144,7 @@ static int read_tiff_decompressed_image(t4_tx_state_t *s) } } /* We might need to flip all the bits, so 1 = black and 0 = white. */ - if (s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL && s->tiff.photo_metric != PHOTOMETRIC_MINISWHITE) { span_log(&s->logging, SPAN_LOG_FLOW, "%s: Photometric needs swapping.\n", s->tiff.file); for (i = 0; i < s->tiff.image_size; i++) @@ -849,50 +1162,160 @@ static int read_tiff_image(t4_tx_state_t *s) { int total_len; int i; + int len; uint8_t *t; - image_translate_state_t *translator; + + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + image_translate_restart(&s->translator, s->tiff.image_length); + s->metadata.image_length = image_translate_get_output_length(&s->translator); + } + else + { + s->metadata.image_length = s->tiff.image_length; + } + s->pack_buf = NULL; + s->pack_ptr = 0; + s->pack_row = 0; if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL) { - /* We need to dither this image down to pure black and white, possibly resizing it - along the way. */ - if ((translator = image_translate_init(NULL, T4_IMAGE_TYPE_BILEVEL, 1728, -1, s->tiff.image_type, s->metadata.image_width, s->metadata.image_length, row_read, s)) == NULL) - return -1; - s->metadata.image_width = image_translate_get_output_width(translator); - s->metadata.image_length = image_translate_get_output_length(translator); - s->metadata.x_resolution = T4_X_RESOLUTION_R8; - s->metadata.y_resolution = T4_Y_RESOLUTION_FINE; - s->metadata.resolution_code = T4_RESOLUTION_R8_FINE; - s->tiff.image_size = (s->metadata.image_width*s->metadata.image_length + 7)/8; - if (s->tiff.image_size >= s->tiff.image_buffer_size) + /* If colour/gray scale is supported we may be able to send the image as it is, perhaps after + a resizing. Otherwise we need to resize it, and squash it to a bilevel image. */ + if (s->tiff.compression == COMPRESSION_JPEG && s->tiff.photo_metric == PHOTOMETRIC_ITULAB) { - if ((t = realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) - return -1; - s->tiff.image_buffer_size = s->tiff.image_size; - s->tiff.image_buffer = t; + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (read_tiff_t42_t81_image(s) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } } - s->tiff.raw_row = 0; - switch (s->tiff.photo_metric) +#if defined(SPANDSP_SUPPORT_T43) + else if (s->tiff.compression == COMPRESSION_T43) { - case PHOTOMETRIC_CIELAB: - /* The default luminant is D50 */ - set_lab_illuminant(&s->lab_params, 0.96422f, 1.0f, 0.82521f); - set_lab_gamut(&s->lab_params, 0, 100, -128, 127, -128, 127, TRUE); - s->apply_lab = TRUE; - break; - case PHOTOMETRIC_ITULAB: - set_lab_illuminant(&s->lab_params, 0.9638f, 1.0f, 0.8245f); - set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); - s->apply_lab = TRUE; - break; - default: - s->apply_lab = FALSE; - break; + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if ((len = read_tiff_t43_image(s)) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } + } +#endif +#if defined(SPANDSP_SUPPORT_T45) + else if (s->tiff.compression == COMPRESSION_T45) + { + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (read_tiff_t45_image(s) < 0) + return -1; + + s->pack_buf = s->tiff.image_buffer; + s->pack_ptr = 0; + s->pack_row = 0; + image_translate_set_row_read_handler(&s->translator, translate_row_read2, s); + } + else + { + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + } + } +#endif + else + { + /* Let libtiff handle the decompression */ + TIFFSetField(s->tiff.tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + image_translate_set_row_read_handler(&s->translator, translate_row_read, s); + } + else + { + if (read_tiff_decompressed_image(s) < 0) + return -1; + } + } + set_image_width(s, s->metadata.image_width); + set_image_length(s, s->metadata.image_length); + t4_tx_set_image_type(s, s->metadata.image_type); + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + /* We need to dither this image down to pure black and white, possibly resizing it + along the way. */ + s->tiff.image_size = (s->metadata.image_width*s->metadata.image_length + 7)/8; + if (s->tiff.image_size >= s->tiff.image_buffer_size) + { + if ((t = span_realloc(s->tiff.image_buffer, s->tiff.image_size)) == NULL) + return -1; + s->tiff.image_buffer_size = s->tiff.image_size; + s->tiff.image_buffer = t; + } + s->tiff.raw_row = 0; + switch (s->tiff.photo_metric) + { + case PHOTOMETRIC_CIELAB: + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab_params, 0, 100, -128, 127, -128, 127, TRUE); + s->apply_lab = TRUE; + break; + case PHOTOMETRIC_ITULAB: + /* The default luminant is D50 */ + set_lab_illuminant(&s->lab_params, 96.422f, 100.000f, 82.521f); + set_lab_gamut(&s->lab_params, 0, 100, -85, 85, -75, 125, FALSE); + s->apply_lab = TRUE; + break; + default: + s->apply_lab = FALSE; + break; + } + total_len = 0; + for (i = 0; i < s->metadata.image_length; i++) + total_len += image_translate_row(&s->translator, &s->tiff.image_buffer[total_len], s->metadata.image_width/8); + image_translate_release(&s->translator); + s->row_handler = metadata_row_read_handler; + s->row_handler_user_data = (void *) s; + } + else + { + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + total_len = 0; + s->tiff.image_buffer = span_realloc(s->tiff.image_buffer, s->metadata.image_width*s->metadata.image_length*3); + for (i = 0; i < s->metadata.image_length; i++) + total_len += image_translate_row(&s->translator, &s->tiff.image_buffer[total_len], s->metadata.image_width); + image_translate_release(&s->translator); + s->row_handler = metadata_row_read_handler; + s->row_handler_user_data = (void *) s; + } + else + { + s->row_handler = tiff_row_read_handler; + s->row_handler_user_data = (void *) s; + } } - total_len = 0; - for (i = 0; i < s->metadata.image_length; i++) - total_len += image_translate_row(translator, &s->tiff.image_buffer[total_len], s->metadata.image_width/8); - image_translate_free(translator); } else { @@ -903,13 +1326,59 @@ static int read_tiff_image(t4_tx_state_t *s) slightly long one, but lets not bother. */ switch (s->tiff.compression) { - case COMPRESSION_T85: - /* Decode the whole image into a buffer */ - /* libtiff probably cannot decompress T.85, so we must handle it ourselves */ - /* Decode the whole image into a buffer */ - if (read_tiff_t85_image(s) < 0) - return -1; +#if defined(SPANDSP_SUPPORT_T88) + case COMPRESSION_T88: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T88: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* libtiff probably cannot decompress T.88, so we must handle it ourselves */ + /* Decode the whole image into a buffer */ + if (read_tiff_t88_image(s) < 0) + return -1; + break; + } break; +#endif + case COMPRESSION_T85: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T85: + case T4_COMPRESSION_T85_L0: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* libtiff probably cannot decompress T.85, so we must handle it ourselves */ + /* Decode the whole image into a buffer */ + if (read_tiff_t85_image(s) < 0) + return -1; + break; + } + break; +#if 0 + case COMPRESSION_CCITT_T6: + switch (s->metadata.compression) + { + case T4_COMPRESSION_T6: + /* Read the raw image, and send it as is */ + if (read_tiff_raw_image(s) < 0) + return -1; + break; + default: + /* Decode the whole image into a buffer */ + /* Let libtiff handle the decompression */ + if (read_tiff_decompressed_image(s) < 0) + return -1; + break; + } + break; +#endif default: /* Decode the whole image into a buffer */ /* Let libtiff handle the decompression */ @@ -930,12 +1399,12 @@ static void tiff_tx_release(t4_tx_state_t *s) TIFFClose(s->tiff.tiff_file); s->tiff.tiff_file = NULL; if (s->tiff.file) - free((char *) s->tiff.file); + span_free((char *) s->tiff.file); s->tiff.file = NULL; } if (s->tiff.image_buffer) { - free(s->tiff.image_buffer); + span_free(s->tiff.image_buffer); s->tiff.image_buffer = NULL; s->tiff.image_size = 0; s->tiff.image_buffer_size = 0; @@ -1023,37 +1492,74 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) int pattern; int pos; int row; + int i; char *t; t4_tx_state_t *s; s = (t4_tx_state_t *) user_data; - switch (s->metadata.y_resolution) + switch (s->metadata.resolution_code) { - case T4_Y_RESOLUTION_1200: - y_repeats = 12; - break; - case T4_Y_RESOLUTION_800: - y_repeats = 8; - break; - case T4_Y_RESOLUTION_600: - y_repeats = 6; - break; - case T4_Y_RESOLUTION_SUPERFINE: - case T4_Y_RESOLUTION_400: - y_repeats = 4; - break; - case T4_Y_RESOLUTION_300: - y_repeats = 3; - break; - case T4_Y_RESOLUTION_FINE: - case T4_Y_RESOLUTION_200: - y_repeats = 2; - break; default: + case T4_RESOLUTION_100_100: + x_repeats = 1; y_repeats = 1; break; + case T4_RESOLUTION_R8_STANDARD: + case T4_RESOLUTION_200_100: + x_repeats = 2; + y_repeats = 1; + break; + case T4_RESOLUTION_R8_FINE: + case T4_RESOLUTION_200_200: + x_repeats = 2; + y_repeats = 2; + break; + case T4_RESOLUTION_300_300: + x_repeats = 3; + y_repeats = 3; + break; + case T4_RESOLUTION_R8_SUPERFINE: + case T4_RESOLUTION_200_400: + x_repeats = 2; + y_repeats = 4; + break; + case T4_RESOLUTION_R16_SUPERFINE: + case T4_RESOLUTION_400_400: + x_repeats = 4; + y_repeats = 4; + break; + case T4_RESOLUTION_400_800: + x_repeats = 4; + y_repeats = 8; + break; + case T4_RESOLUTION_300_600: + x_repeats = 3; + y_repeats = 6; + break; + case T4_RESOLUTION_600_600: + x_repeats = 6; + y_repeats = 6; + break; + case T4_RESOLUTION_600_1200: + x_repeats = 6; + y_repeats = 12; + break; + case T4_RESOLUTION_1200_1200: + x_repeats = 12; + y_repeats = 12; + break; + } + switch (s->metadata.width_code) + { + case T4_SUPPORT_WIDTH_215MM: + break; + case T4_SUPPORT_WIDTH_255MM: + x_repeats *= 2; + break; + case T4_SUPPORT_WIDTH_303MM: + x_repeats *= 3; + break; } - y_repeats /= s->row_squashing_ratio; if (s->header_overlays_image) { /* Read and dump a row of the real image, allowing for the possibility @@ -1064,16 +1570,60 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) return len; } } + t = s->header_text; row = s->header_row/y_repeats; pos = 0; - for (t = s->header_text; *t && pos <= len - 2; t++) + switch (s->metadata.image_type) { - pattern = header_font[(uint8_t) *t][row]; - buf[pos++] = (uint8_t) (pattern >> 8); - buf[pos++] = (uint8_t) (pattern & 0xFF); + case T4_IMAGE_TYPE_BILEVEL: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + buf[pos++] = (uint8_t) (pattern >> 8); + buf[pos++] = (uint8_t) (pattern & 0xFF); + } + if (pos < len) + memset(&buf[pos], 0, len - pos); + break; + case T4_IMAGE_TYPE_GRAY_8BIT: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + for (i = 0; i < 16; i++) + { + buf[pos + i] = (pattern & 0x8000) ? 0 : 0xFF; + pattern <<= 1; + } + pos += 16; + } + if (pos < len) + memset(&buf[pos], 0xFF, len - pos); + break; + case T4_IMAGE_TYPE_COLOUR_8BIT: + for ( ; *t && pos <= len - 2; t++) + { + pattern = header_font[(uint8_t) *t][row]; + for (i = 0; i < 16; i++) + { + buf[pos + 3*i + 0] = + buf[pos + 3*i + 1] = + buf[pos + 3*i + 2] = (pattern & 0x8000) ? 0 : 0xFF; + pattern <<= 1; + } + pos += 3*16; + } + if (pos < len) + memset(&buf[pos], 0xFF, len - pos); + break; + case T4_IMAGE_TYPE_COLOUR_BILEVEL: + case T4_IMAGE_TYPE_4COLOUR_BILEVEL: + case T4_IMAGE_TYPE_GRAY_12BIT: + case T4_IMAGE_TYPE_4COLOUR_8BIT: + case T4_IMAGE_TYPE_COLOUR_12BIT: + case T4_IMAGE_TYPE_4COLOUR_12BIT: + default: + memset(buf, 0xFF, len); } - while (pos < len) - buf[pos++] = 0; s->header_row++; if (s->header_row >= 16*y_repeats) { @@ -1084,12 +1634,6 @@ static int header_row_read_handler(void *user_data, uint8_t buf[], size_t len) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_row_squashing_ratio(t4_tx_state_t *s, int row_squashing_ratio) -{ - s->row_squashing_ratio = row_squashing_ratio; -} -/*- End of function --------------------------------------------------------*/ - SPAN_DECLARE(int) t4_tx_next_page_has_different_format(t4_tx_state_t *s) { span_log(&s->logging, SPAN_LOG_FLOW, "Checking for the existence of page %d\n", s->current_page + 1); @@ -1113,8 +1657,372 @@ SPAN_DECLARE(int) t4_tx_set_row_read_handler(t4_tx_state_t *s, t4_row_read_handl } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) +SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, + int supported_compressions, + int supported_image_sizes, + int supported_bilevel_resolutions, + int supported_colour_resolutions) { + static const struct + { + int width; + int width_code; + int res_code; /* Correct resolution code */ + int alt_res_code; /* Fallback resolution code, where a metric/inch swap is possible */ + } width_info[] = + { + { T4_WIDTH_100_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_100_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_100_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_100_100, 0}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_100, T4_RESOLUTION_R8_STANDARD}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_200, T4_RESOLUTION_R8_FINE}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_200_400, T4_RESOLUTION_R8_SUPERFINE}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_STANDARD, T4_RESOLUTION_200_100}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_FINE, T4_RESOLUTION_200_200}, + { T4_WIDTH_200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R8_SUPERFINE, T4_RESOLUTION_200_400}, + { T4_WIDTH_300_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_300_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_300_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_300_300, 0}, + { T4_WIDTH_300_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_300_600, 0}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_400_400, T4_RESOLUTION_R16_SUPERFINE}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_400_800, 0}, + { T4_WIDTH_400_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_R16_SUPERFINE, T4_RESOLUTION_400_400}, + { T4_WIDTH_600_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_600_1200, 0}, + { T4_WIDTH_600_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_600_1200, 0}, + { T4_WIDTH_600_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_600_600, 0}, + { T4_WIDTH_600_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_600_1200, 0}, + {T4_WIDTH_1200_A4, T4_SUPPORT_WIDTH_215MM, T4_RESOLUTION_1200_1200, 0}, + {T4_WIDTH_1200_B4, T4_SUPPORT_WIDTH_255MM, T4_RESOLUTION_1200_1200, 0}, + {T4_WIDTH_1200_A3, T4_SUPPORT_WIDTH_303MM, T4_RESOLUTION_1200_1200, 0}, + {0x7FFFFFFF, -1, -1, -1} + }; + + static const struct + { + int resolution; + struct + { + int resolution; + int squashing_factor; + } fallback[4]; + } squashable[4] = + { + { + T4_RESOLUTION_200_400, + { + {T4_RESOLUTION_200_200, 2}, + {T4_RESOLUTION_R8_FINE, 2}, + {T4_RESOLUTION_200_100, 4}, + {T4_RESOLUTION_R8_STANDARD, 4} + } + }, + { + T4_RESOLUTION_200_200, + { + {T4_RESOLUTION_200_100, 2}, + {T4_RESOLUTION_R8_STANDARD, 2}, + {0, 0}, + {0, 0} + } + }, + { + T4_RESOLUTION_R8_SUPERFINE, + { + {T4_RESOLUTION_R8_FINE, 2}, + {T4_RESOLUTION_200_200, 2}, + {T4_RESOLUTION_R8_STANDARD, 4}, + {T4_RESOLUTION_200_100, 4} + } + }, + { + T4_RESOLUTION_R8_FINE, + { + {T4_RESOLUTION_R8_STANDARD, 2}, + {T4_RESOLUTION_200_100, 2}, + {0, 0}, + {0, 0} + } + } + }; + + int i; + int j; + int entry; + int compression; + int res; + + compression = -1; + s->metadata.image_type = s->tiff.image_type; + if (s->tiff.image_type != T4_IMAGE_TYPE_BILEVEL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Non-bi-level image\n"); + /* Can we send this page as it is? */ + if (supported_colour_resolutions + && + (supported_compressions & (T4_COMPRESSION_T42_T81 | T4_COMPRESSION_T43 | T4_COMPRESSION_T45 | T4_COMPRESSION_SYCC_T81)) + && + (((s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL || s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT || s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + && + (supported_compressions & T4_COMPRESSION_COLOUR)) + || + ((s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT || s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + && + (supported_compressions & T4_COMPRESSION_GRAYSCALE)))) + { + /* Gray-scale/colour is possible */ + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale/colour is allowed\n"); + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_COLOUR)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Colour is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + } + else if (s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_GRAYSCALE)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + } + /* Choose the best gray-scale/colour encoding available to us */ + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL && (supported_compressions & T4_COMPRESSION_T43)) + compression = T4_COMPRESSION_T43; + else if ((supported_compressions & T4_COMPRESSION_T42_T81)) + compression = T4_COMPRESSION_T42_T81; + else if ((supported_compressions & T4_COMPRESSION_T43)) + compression = T4_COMPRESSION_T43; + else if ((supported_compressions & T4_COMPRESSION_T45)) + compression = T4_COMPRESSION_T45; + else if ((supported_compressions & T4_COMPRESSION_SYCC_T81)) + compression = T4_COMPRESSION_SYCC_T81; + + //best_colour_resolution(s->tiff.x_resolution, supported_colour_resolutions); + } + else + { + /* Gray-scale/colour is not possible. Can we flatten the image to send it? */ + span_log(&s->logging, SPAN_LOG_FLOW, "Gray-scale/colour is not allowed\n"); + if (s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_BILEVEL + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_COLOUR_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_COLOUR_TO_BILEVEL)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Flattening is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + s->metadata.image_type = T4_IMAGE_TYPE_BILEVEL; + } + else if (s->tiff.image_type == T4_IMAGE_TYPE_GRAY_8BIT + || + s->tiff.image_type == T4_IMAGE_TYPE_GRAY_12BIT) + { + if (!(supported_compressions & T4_COMPRESSION_GRAY_TO_BILEVEL)) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Flattening is not allowed\n"); + return T4_IMAGE_FORMAT_INCOMPATIBLE; + } + s->metadata.image_type = T4_IMAGE_TYPE_BILEVEL; + } + /* Squashing to a bi-level image is possible */ + span_log(&s->logging, SPAN_LOG_FLOW, "The image may be flattened to %d\n", s->metadata.image_type); + } + } + + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + /* Choose the best bi-level encoding available to us */ + if ((supported_compressions & T4_COMPRESSION_T85_L0)) + compression = T4_COMPRESSION_T85_L0; + else if ((supported_compressions & T4_COMPRESSION_T85)) + compression = T4_COMPRESSION_T85; + else if ((supported_compressions & T4_COMPRESSION_T6)) + compression = T4_COMPRESSION_T6; + else if ((supported_compressions & T4_COMPRESSION_T4_2D)) + compression = T4_COMPRESSION_T4_2D; + else + compression = T4_COMPRESSION_T4_1D; + } + + /* Deal with the image width/resolution combination. */ + /* Look for a pattern that matches the image */ + res = T4_IMAGE_FORMAT_NOSIZESUPPORT; + for (entry = 0; s->tiff.image_width >= width_info[entry].width; entry++) + { + if (s->tiff.image_width == width_info[entry].width && s->tiff.resolution_code == width_info[entry].res_code) + { + res = T4_IMAGE_FORMAT_OK; + break; + } + } + + s->metadata.width_code = width_info[entry].width_code; + + s->row_squashing_ratio = 1; + if (res == T4_IMAGE_FORMAT_OK) + { + /* We have a valid width/resolution combination */ + + /* Check if this width/resolution combination is supported */ + if (!(supported_image_sizes & width_info[entry].width_code)) + return T4_IMAGE_FORMAT_NOSIZESUPPORT; + + /* No resize necessary */ + s->metadata.image_width = s->tiff.image_width; + s->metadata.image_length = s->tiff.image_length; + + res = T4_IMAGE_FORMAT_NORESSUPPORT; + if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL) + { + if ((width_info[entry].res_code & supported_bilevel_resolutions)) + { + /* We can use the resolution of the original image */ + s->metadata.resolution_code = s->tiff.resolution_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + else + { + /* The resolution is not supported, but there might be an approximation, or a length + squashing that might work. */ + if ((width_info[entry].alt_res_code & supported_bilevel_resolutions)) + { + /* We can do a metric/imperial swap, and have a usable resolution */ + span_log(&s->logging, + SPAN_LOG_FLOW, + "Image resolution %s falls back to %s\n", + t4_image_resolution_to_str(s->tiff.resolution_code), + t4_image_resolution_to_str(width_info[entry].alt_res_code)); + s->metadata.resolution_code = width_info[entry].alt_res_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + else + { + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + { + if ((s->tiff.resolution_code & (T4_RESOLUTION_200_400 | T4_RESOLUTION_200_200 | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R8_FINE))) + { + /* This might be a resolution we can squash down to something which is supported */ + for (i = 0; i < 4; i++) + { + if ((s->tiff.resolution_code & squashable[i].resolution)) + break; + } + if (i < 4) + { + /* This is a squashable resolution, so let's see if there is a valid + fallback we can squash the image to, scanning through the entries + in their order of preference. */ + for (j = 0; j < 4; j++) + { + if ((supported_bilevel_resolutions & squashable[i].fallback[j].resolution)) + { + span_log(&s->logging, + SPAN_LOG_FLOW, + "Image resolution %s falls back to %s\n", + t4_image_resolution_to_str(s->tiff.resolution_code), + t4_image_resolution_to_str(squashable[i].fallback[j].resolution)); + s->row_squashing_ratio = squashable[i].fallback[j].squashing_factor; + s->metadata.resolution_code = squashable[i].fallback[j].resolution; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + break; + } + } + } + } + } + } + } + /* If we have not succeeded in matching up the size and resolution, the next step will + depend on whether the original was a bi-level image. If it was we are stuck, as you can't + really resize those. If it was not, a resize might be possible */ + if (res != T4_IMAGE_FORMAT_OK) + { + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + return T4_IMAGE_FORMAT_NORESSUPPORT; + if (!(supported_compressions & T4_COMPRESSION_RESCALING)) + return T4_IMAGE_FORMAT_NORESSUPPORT; + } + /* TODO */ + } + else + { + if ((width_info[entry].res_code & supported_bilevel_resolutions)) + { + if ((s->tiff.resolution_code & supported_colour_resolutions)) + { + /* We can use the resolution of the original image */ + s->metadata.resolution_code = width_info[entry].res_code; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + res = T4_IMAGE_FORMAT_OK; + } + } + } + } + else + { + /* Can we rework the image to fit? */ + /* We can't rework a bilevel image that fits none of the patterns */ + if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) + return T4_IMAGE_FORMAT_NORESSUPPORT; + res = T4_IMAGE_FORMAT_OK; + /* Any other kind of image might be resizable */ + s->metadata.image_width = T4_WIDTH_200_A4; + s->metadata.resolution_code = T4_RESOLUTION_200_200; + s->metadata.x_resolution = code_to_x_resolution(s->metadata.resolution_code); + s->metadata.y_resolution = code_to_y_resolution(s->metadata.resolution_code); + } + + if (res != T4_IMAGE_FORMAT_OK) + return res; + + if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) + { + if (image_translate_init(&s->translator, s->metadata.image_type, s->metadata.image_width, -1, s->tiff.image_type, s->tiff.image_width, s->tiff.image_length, translate_row_read2, s) == NULL) + return T4_IMAGE_FORMAT_INCOMPATIBLE; + s->metadata.image_length = image_translate_get_output_length(&s->translator); + } + + if (compression != s->metadata.compression) { switch (compression) { @@ -1129,13 +2037,13 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; default: t4_t6_encode_init(&s->encoder.t4_t6, compression, s->metadata.image_width, s->metadata.image_length, s->row_handler, s->row_handler_user_data); - t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); break; } s->metadata.compression = compression; + res = T4_IMAGE_FORMAT_OK; if (t4_t6_encode_set_encoding(&s->encoder.t4_t6, compression)) - return -1; - return s->metadata.compression; + res = -1; + break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: switch (s->metadata.compression) @@ -1148,7 +2056,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #if defined(SPANDSP_SUPPORT_T88) case T4_COMPRESSION_T88: switch (s->metadata.compression) @@ -1160,7 +2069,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif case T4_COMPRESSION_T42_T81: case T4_COMPRESSION_SYCC_T81: @@ -1174,7 +2084,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: switch (s->metadata.compression) @@ -1186,7 +2097,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif #if defined(SPANDSP_SUPPORT_T45) case T4_COMPRESSION_T45: @@ -1199,24 +2111,25 @@ SPAN_DECLARE(int) t4_tx_set_tx_encoding(t4_tx_state_t *s, int compression) break; } s->metadata.compression = compression; - return s->metadata.compression; + res = T4_IMAGE_FORMAT_OK; + break; #endif } } - return -1; -} -/*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits) -{ switch (s->metadata.compression) { case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - t4_t6_encode_set_min_bits_per_row(&s->encoder.t4_t6, bits); + t4_t6_encode_set_max_2d_rows_per_1d_row(&s->encoder.t4_t6, -s->metadata.y_resolution); break; } + + set_image_width(s, s->metadata.image_width); + set_image_length(s, s->metadata.image_length); + t4_tx_set_image_type(s, s->metadata.image_type); + return res; } /*- End of function --------------------------------------------------------*/ @@ -1256,7 +2169,13 @@ SPAN_DECLARE(int) t4_tx_get_tx_image_width(t4_tx_state_t *s) } /*- End of function --------------------------------------------------------*/ -SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) +SPAN_DECLARE(int) t4_tx_get_tx_image_width_code(t4_tx_state_t *s) +{ + return s->metadata.width_code; +} +/*- End of function --------------------------------------------------------*/ + +static void set_image_width(t4_tx_state_t *s, uint32_t image_width) { s->metadata.image_width = image_width; switch (s->metadata.compression) @@ -1293,11 +2212,16 @@ SPAN_DECLARE(void) t4_tx_set_image_width(t4_tx_state_t *s, int image_width) } /*- End of function --------------------------------------------------------*/ -static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length) +static void set_image_length(t4_tx_state_t *s, uint32_t image_length) { s->metadata.image_length = image_length; switch (s->metadata.compression) { + case T4_COMPRESSION_T4_1D: + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T6: + t4_t6_encode_set_image_length(&s->encoder.t4_t6, image_length); + break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: t85_encode_set_image_length(&s->encoder.t85, image_length); @@ -1325,6 +2249,47 @@ static void t4_tx_set_image_length(t4_tx_state_t *s, uint32_t image_length) } /*- End of function --------------------------------------------------------*/ +static void t4_tx_set_image_type(t4_tx_state_t *s, int image_type) +{ + s->metadata.image_type = image_type; + switch (s->metadata.compression) + { +#if defined(SPANDSP_SUPPORT_T88) + case T4_COMPRESSION_T88: + t88_encode_set_image_type(&s->encoder.t88, image_type); + break; +#endif + case T4_COMPRESSION_T42_T81: + case T4_COMPRESSION_SYCC_T81: + t42_encode_set_image_type(&s->encoder.t42, image_type); + break; +#if defined(SPANDSP_SUPPORT_T43) + case T4_COMPRESSION_T43: + t43_encode_set_image_type(&s->encoder.t43, image_type); + break; +#endif +#if defined(SPANDSP_SUPPORT_T45) + case T4_COMPRESSION_T45: + t45_encode_set_image_type(&s->encoder.t45, image_type); + break; +#endif + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) t4_tx_set_min_bits_per_row(t4_tx_state_t *s, int bits) +{ + switch (s->metadata.compression) + { + case T4_COMPRESSION_T4_1D: + case T4_COMPRESSION_T4_2D: + case T4_COMPRESSION_T6: + t4_t6_encode_set_min_bits_per_row(&s->encoder.t4_t6, bits); + break; + } +} +/*- End of function --------------------------------------------------------*/ + SPAN_DECLARE(void) t4_tx_set_max_2d_rows_per_1d_row(t4_tx_state_t *s, int max) { switch (s->metadata.compression) @@ -1395,8 +2360,9 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t t->image_x_resolution = s->tiff.x_resolution; t->image_y_resolution = s->tiff.y_resolution; t->x_resolution = s->metadata.x_resolution; - t->y_resolution = s->metadata.y_resolution/s->row_squashing_ratio; + t->y_resolution = s->metadata.y_resolution; + t->type = s->metadata.image_type; t->compression = s->metadata.compression; switch (s->metadata.compression) @@ -1404,16 +2370,14 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t case T4_COMPRESSION_T4_1D: case T4_COMPRESSION_T4_2D: case T4_COMPRESSION_T6: - t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t4_t6_encode_get_image_width(&s->encoder.t4_t6); - t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6)/s->row_squashing_ratio; + t->length = t4_t6_encode_get_image_length(&s->encoder.t4_t6); t->line_image_size = t4_t6_encode_get_compressed_image_size(&s->encoder.t4_t6)/8; break; case T4_COMPRESSION_T85: case T4_COMPRESSION_T85_L0: - t->type = T4_IMAGE_TYPE_BILEVEL; t->width = t85_encode_get_image_width(&s->encoder.t85); - t->length = t85_encode_get_image_length(&s->encoder.t85)/s->row_squashing_ratio; + t->length = t85_encode_get_image_length(&s->encoder.t85); t->line_image_size = t85_encode_get_compressed_image_size(&s->encoder.t85)/8; break; #if defined(SPANDSP_SUPPORT_T88) @@ -1425,16 +2389,14 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t #endif case T4_COMPRESSION_T42_T81: case T4_COMPRESSION_SYCC_T81: - t->type = 0; t->width = t42_encode_get_image_width(&s->encoder.t42); - t->length = t42_encode_get_image_length(&s->encoder.t42)/s->row_squashing_ratio; + t->length = t42_encode_get_image_length(&s->encoder.t42); t->line_image_size = t42_encode_get_compressed_image_size(&s->encoder.t42)/8; break; #if defined(SPANDSP_SUPPORT_T43) case T4_COMPRESSION_T43: - t->type = 0; t->width = t43_encode_get_image_width(&s->encoder.t43); - t->length = t43_encode_get_image_length(&s->encoder.t43)/s->row_squashing_ratio; + t->length = t43_encode_get_image_length(&s->encoder.t43); t->line_image_size = t43_encode_get_compressed_image_size(&s->encoder.t43)/8; break; #endif @@ -1451,6 +2413,13 @@ SPAN_DECLARE(void) t4_tx_get_transfer_statistics(t4_tx_state_t *s, t4_stats_t *t SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s) { + if (s->pre_encoded_len > 0) + { + if (s->pre_encoded_ptr >= s->pre_encoded_len) + return SIG_STATUS_END_OF_DATA; + return 0; + } + switch (s->metadata.compression) { case T4_COMPRESSION_T4_1D: @@ -1482,7 +2451,21 @@ SPAN_DECLARE(int) t4_tx_image_complete(t4_tx_state_t *s) SPAN_DECLARE(int) t4_tx_get_bit(t4_tx_state_t *s) { + int bit; + /* We only get bit by bit for T.4 1D and T.4 2-D. */ + if (s->pre_encoded_len > 0) + { + if (s->pre_encoded_ptr >= s->pre_encoded_len) + return SIG_STATUS_END_OF_DATA; + bit = (s->pre_encoded_buf[s->pre_encoded_ptr] >> s->pre_encoded_bit) & 1; + if (++s->pre_encoded_bit >= 8) + { + s->pre_encoded_bit = 0; + s->pre_encoded_ptr++; + } + return bit; + } return t4_t6_encode_get_bit(&s->encoder.t4_t6); } /*- End of function --------------------------------------------------------*/ @@ -1500,6 +2483,7 @@ SPAN_DECLARE(int) t4_tx_get(t4_tx_state_t *s, uint8_t buf[], size_t max_len) if (s->image_get_handler) return s->image_get_handler((void *) &s->encoder, buf, max_len); + return 0; } /*- End of function --------------------------------------------------------*/ @@ -1562,9 +2546,10 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_tx_state_t *s) s->image_get_handler = NULL; break; } + /* If there is a page header, create that first */ - if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0) - //if (s->header_info && s->header_info[0] && make_header(s) == 0) + //if (s->metadata.image_type == T4_IMAGE_TYPE_BILEVEL && s->header_info && s->header_info[0] && make_header(s) == 0) + if (s->header_info && s->header_info[0] && make_header(s) == 0) { s->header_row = 0; set_row_read_handler(s, header_row_read_handler, (void *) s); @@ -1633,7 +2618,7 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int if (open_tiff_input_file(s, file) < 0) { if (allocated) - free(s); + span_free(s); return NULL; } s->tiff.file = strdup(file); @@ -1644,7 +2629,7 @@ SPAN_DECLARE(t4_tx_state_t *) t4_tx_init(t4_tx_state_t *s, const char *file, int { tiff_tx_release(s); if (allocated) - free(s); + span_free(s); return NULL; } } @@ -1658,12 +2643,12 @@ SPAN_DECLARE(int) t4_tx_release(t4_tx_state_t *s) tiff_tx_release(s); if (s->header_text) { - free(s->header_text); + span_free(s->header_text); s->header_text = NULL; } if (s->colour_map) { - free(s->colour_map); + span_free(s->colour_map); s->colour_map = NULL; } switch (s->metadata.compression) @@ -1700,7 +2685,7 @@ SPAN_DECLARE(int) t4_tx_free(t4_tx_state_t *s) int ret; ret = t4_tx_release(s); - free(s); + span_free(s); return ret; } /*- End of function --------------------------------------------------------*/ diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am index 8eda2bb201..881a4839b5 100644 --- a/libs/spandsp/tests/Makefile.am +++ b/libs/spandsp/tests/Makefile.am @@ -46,7 +46,7 @@ EXTRA_DIST = fax_tests.sh \ MAINTAINERCLEANFILES = Makefile.in -INCLUDES = -I$(top_builddir)/src -I$(top_builddir)/spandsp-sim -DDATADIR="\"$(pkgdatadir)\"" +AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_builddir)/spandsp-sim -DDATADIR="\"$(pkgdatadir)\"" LIBDIR = -L$(top_builddir)/src diff --git a/libs/spandsp/tests/regression_tests.sh b/libs/spandsp/tests/regression_tests.sh index 31aeb5a952..949eca3fcf 100755 --- a/libs/spandsp/tests/regression_tests.sh +++ b/libs/spandsp/tests/regression_tests.sh @@ -43,6 +43,15 @@ then fi echo adsi_tests completed OK +./alloc_tests >$STDOUT_DEST 2>$STDERR_DEST +RETVAL=$? +if [ $RETVAL != 0 ] +then + echo alloc_tests failed! + exit $RETVAL +fi +echo alloc_tests completed OK + ./async_tests >$STDOUT_DEST 2>$STDERR_DEST RETVAL=$? if [ $RETVAL != 0 ] diff --git a/libs/spandsp/tests/t31_tests.c b/libs/spandsp/tests/t31_tests.c index a002aa73bc..c7d941384f 100644 --- a/libs/spandsp/tests/t31_tests.c +++ b/libs/spandsp/tests/t31_tests.c @@ -106,9 +106,9 @@ static const struct command_response_s fax_send_test_seq[] = EXCHANGE("AT+FRH=3\r", "\r\nCONNECT\r\n"), // #if 1 - RESPONSE("\xFF\x13\x80\x00\xEE\xF8\x80\x80\x89\x80\x80\x80\x18\x18\xB9\x10\x03"), // For audio FAXing + RESPONSE("\xFF\x13\x80\x00\xEE\xF8\x80\x80\x99\x80\x80\x80\x18\x58\x0D\x10\x03"), // For audio FAXing #else - RESPONSE("\xFF\x13\x80\x04\xEE\xF8\x80\x80\x89\x80\x80\x80\x18\x84\x09\x10\x03"), // For T.38 FAXing + RESPONSE("\xFF\x13\x80\x04\xEE\xF8\x80\x80\x99\x80\x80\x80\x18\xC4\xBD\x10\x03"), // For T.38 FAXing #endif RESPONSE("\r\nOK\r\n"), //EXCHANGE("AT+FRH=3\r", "\r\nNO CARRIER\r\n"), diff --git a/libs/spandsp/tests/t4_tests.c b/libs/spandsp/tests/t4_tests.c index ac682bd620..f01b1d3db7 100644 --- a/libs/spandsp/tests/t4_tests.c +++ b/libs/spandsp/tests/t4_tests.c @@ -518,7 +518,6 @@ int main(int argc, char *argv[]) compression = compression_sequence[compression_step++]; } } -#if 0 if (t4_tx_set_tx_image_format(send_state, compression, T4_SUPPORT_WIDTH_215MM @@ -548,8 +547,6 @@ int main(int argc, char *argv[]) { break; } -#endif - t4_tx_set_tx_encoding(send_state, compression); t4_rx_set_rx_encoding(receive_state, compression); if (t4_tx_start_page(send_state)) diff --git a/libs/spandsp/tests/tsb85_tests.c b/libs/spandsp/tests/tsb85_tests.c index d8f626520c..e8064b3d45 100644 --- a/libs/spandsp/tests/tsb85_tests.c +++ b/libs/spandsp/tests/tsb85_tests.c @@ -397,6 +397,7 @@ static void fax_prepare(void) | T4_RESOLUTION_R8_FINE | T4_RESOLUTION_R8_SUPERFINE | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_100_100 | T4_RESOLUTION_200_100 | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_400 @@ -980,7 +981,37 @@ static int next_step(faxtester_state_t *s) else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_T6; } - t4_tx_set_tx_encoding(&t4_tx_state, compression_type); + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); if (t4_tx_start_page(&t4_tx_state)) { @@ -1020,7 +1051,37 @@ static int next_step(faxtester_state_t *s) else if (strcasecmp((const char *) compression, "T.6") == 0) compression_type = T4_COMPRESSION_T6; } - t4_tx_set_tx_encoding(&t4_tx_state, compression_type); + if (t4_tx_set_tx_image_format(&t4_tx_state, + compression_type, + T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED, + T4_RESOLUTION_R8_STANDARD + | T4_RESOLUTION_R8_FINE + | T4_RESOLUTION_R8_SUPERFINE + | T4_RESOLUTION_R16_SUPERFINE + | T4_RESOLUTION_200_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_200_400 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_300_600 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_400_800 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_600_1200 + | T4_RESOLUTION_1200_1200, + T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400 + | T4_RESOLUTION_600_600 + | T4_RESOLUTION_1200_1200) < 0) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Failed to set T.4 compression\n"); + printf("Test failed\n"); + exit(2); + } t4_tx_set_min_bits_per_row(&t4_tx_state, min_row_bits); if (t4_tx_start_page(&t4_tx_state)) { From f53bde459ba71aa2552459055d5efc8d17d49874 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 5 Aug 2013 23:49:59 +0500 Subject: [PATCH 234/278] add new file --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 145733e636..91d949b359 100644 --- a/Makefile.am +++ b/Makefile.am @@ -291,6 +291,7 @@ libfreeswitch_la_SOURCES = \ libs/libnatpmp/natpmp.c \ libs/libnatpmp/getgateway.c\ libs/spandsp/src/plc.c \ + libs/spandsp/src/alloc.c \ libs/spandsp/src/bit_operations.c if ENABLE_CPP From 9ad14d1d3304385096a7a99dcefa1dd18db2a1dd Mon Sep 17 00:00:00 2001 From: Ben Langfeld Date: Tue, 6 Aug 2013 11:03:01 -0300 Subject: [PATCH 235/278] mod_rayo: Commands executed against a dead call should return item-not-found --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index a110cdb703..7baaf5f46f 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -1424,7 +1424,7 @@ void rayo_call_send(struct rayo_actor *call, struct rayo_message *msg) if (!session) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, session not found\n", RAYO_JID(call)); if (!msg->is_reply) { - RAYO_SEND_REPLY(call, msg->from_jid, iks_new_error(iq, STANZA_ERROR_SERVICE_UNAVAILABLE)); + RAYO_SEND_REPLY(call, msg->from_jid, iks_new_error(iq, STANZA_ERROR_ITEM_NOT_FOUND)); } return; } From 25b068714d4259c0f5540f956dc0c82d35668192 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 6 Aug 2013 11:05:35 -0400 Subject: [PATCH 236/278] FS-5669 --resolve mod_rayo: return error if is missing in --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 3 ++- .../mod_rayo/rayo_output_component.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 7baaf5f46f..6370d88048 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -3688,7 +3688,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) "" "0123456789*#]]>" ""); - + rayo_add_cmd_alias("output_bad", + ""); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/event_handlers/mod_rayo/rayo_output_component.c b/src/mod/event_handlers/mod_rayo/rayo_output_component.c index 82fd0aa8b2..f92d994a64 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_output_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_output_component.c @@ -126,12 +126,19 @@ static iks *start_call_output_component(struct rayo_actor *call, struct rayo_mes switch_core_session_t *session = (switch_core_session_t *)session_data; struct rayo_component *output_component = NULL; iks *output = iks_find(iq, "output"); + iks *document = NULL; /* validate output attributes */ if (!VALIDATE_RAYO_OUTPUT(output)) { return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } + /* check if exists */ + document = iks_find(output, "document"); + if (!document) { + return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); + } + output_component = create_output_component(call, RAT_CALL_COMPONENT, output, iks_find_attrib(iq, "from")); return start_call_output(output_component, session, output, iq); } @@ -144,6 +151,7 @@ static iks *start_mixer_output_component(struct rayo_actor *mixer, struct rayo_m iks *iq = msg->payload; struct rayo_component *component = NULL; iks *output = iks_find(iq, "output"); + iks *document = NULL; switch_stream_handle_t stream = { 0 }; /* validate output attributes */ @@ -151,6 +159,12 @@ static iks *start_mixer_output_component(struct rayo_actor *mixer, struct rayo_m return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } + /* check if exists */ + document = iks_find(output, "document"); + if (!document) { + return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); + } + component = create_output_component(mixer, RAT_MIXER_COMPONENT, output, iks_find_attrib(iq, "from")); /* build conference command */ From d285e798676e7a376c05c8de54abf031946d549a Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 6 Aug 2013 14:52:56 -0400 Subject: [PATCH 237/278] mod_rayo: fix crash on originate error --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 6370d88048..3aa255f833 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -1907,6 +1907,7 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * char *dial_to_dup = NULL; const char *dial_from = iks_find_attrib(dial, "from"); const char *dial_timeout_ms = iks_find_attrib(dial, "timeout"); + const char *uuid = NULL; struct dial_gateway *gateway = NULL; struct rayo_call *call = NULL; switch_stream_handle_t stream = { 0 }; @@ -1917,6 +1918,7 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * call->dcp_jid = switch_core_strdup(RAYO_POOL(call), dcp_jid); call->dial_id = iks_find_attrib(iq, "id"); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s has control of call\n", dcp_jid); + uuid = switch_core_strdup(dtdata->pool, rayo_call_get_uuid(call)); /* set rayo channel variables so channel originate event can be identified as coming from Rayo */ stream.write_function(&stream, "{origination_uuid=%s,rayo_dcp_jid=%s,rayo_call_jid=%s", @@ -2016,19 +2018,17 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * /* response will be sent when originate event is received- otherwise error is returned */ if (switch_api_execute("originate", stream.data, NULL, &api_stream) == SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Got originate result: %s\n", (char *)api_stream.data); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got originate result: %s\n", (char *)api_stream.data); /* check for failure */ if (strncmp("+OK", api_stream.data, strlen("+OK"))) { - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "Failed to originate call\n"); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_INFO, "Failed to originate call\n"); if (call->dial_id) { /* map failure reason to iq error */ if (!strncmp("-ERR DESTINATION_OUT_OF_ORDER", api_stream.data, strlen("-ERR DESTINATION_OUT_OF_ORDER"))) { /* this -ERR is received when out of sessions */ response = iks_new_error(iq, STANZA_ERROR_RESOURCE_CONSTRAINT); - } else { - response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, api_stream.data); } } } From 68253e37488a3d62af8d1887789efd6dbcc6b900 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 6 Aug 2013 15:30:22 -0400 Subject: [PATCH 238/278] FS-5668 --resolve mod_rayo: offer from/to do not match SIP --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 3aa255f833..79d2179053 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -2728,12 +2728,35 @@ static iks *rayo_create_offer(struct rayo_call *call, switch_core_session_t *ses switch_caller_profile_t *profile = switch_channel_get_caller_profile(channel); iks *presence = iks_new("presence"); iks *offer = iks_insert(presence, "offer"); + const char *val; iks_insert_attrib(presence, "from", RAYO_JID(call)); - iks_insert_attrib(offer, "from", profile->caller_id_number); - iks_insert_attrib(offer, "to", profile->destination_number); iks_insert_attrib(offer, "xmlns", RAYO_NS); + if ((val = switch_channel_get_variable(channel, "sip_from_uri"))) { + /* is a SIP call - pass the URI */ + if (!strchr(val, ':')) { + iks_insert_attrib_printf(offer, "from", "sip:%s", val); + } else { + iks_insert_attrib(offer, "from", val); + } + } else { + /* pass caller ID */ + iks_insert_attrib(offer, "from", profile->caller_id_number); + } + + if ((val = switch_channel_get_variable(channel, "sip_to_uri"))) { + /* is a SIP call - pass the URI */ + if (!strchr(val, ':')) { + iks_insert_attrib_printf(offer, "to", "sip:%s", val); + } else { + iks_insert_attrib(offer, "to", val); + } + } else { + /* pass dialed number */ + iks_insert_attrib(offer, "to", profile->destination_number); + } + /* add signaling headers */ { switch_event_header_t *var; From e94c931102cf056d0d1d6dcfb023d5621d829534 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 6 Aug 2013 16:21:22 -0400 Subject: [PATCH 239/278] FS-5679 --resolve mod_rayo: SIP 180 doesn't result in ringing event --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 79d2179053..6798f70f06 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -129,6 +129,8 @@ struct rayo_call { const char *dial_id; /** channel destroy event */ switch_event_t *end_event; + /** True if ringing event sent to client */ + int ringing_sent; }; /** @@ -1031,6 +1033,7 @@ static struct rayo_call *rayo_call_init(struct rayo_call *call, switch_memory_po call->dcp_jid = ""; call->idle_start_time = switch_micro_time_now(); call->joined = 0; + call->ringing_sent = 0; switch_core_hash_init(&call->pcps, pool); switch_safe_free(call_jid); @@ -2550,13 +2553,21 @@ static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *ev */ static void on_call_ringing_event(struct rayo_client *rclient, switch_event_t *event) { - struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); - if (call) { - iks *revent = iks_new_presence("ringing", RAYO_NS, - switch_event_get_header(event, "variable_rayo_call_jid"), - switch_event_get_header(event, "variable_rayo_dcp_jid")); - RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); - RAYO_UNLOCK(call); + const char *call_direction = switch_event_get_header(event, "Call-Direction"); + if (call_direction && !strcmp(call_direction, "outbound")) { + struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); + if (call) { + switch_mutex_lock(RAYO_ACTOR(call)->mutex); + if (!call->ringing_sent) { + iks *revent = iks_new_presence("ringing", RAYO_NS, + switch_event_get_header(event, "variable_rayo_call_jid"), + switch_event_get_header(event, "variable_rayo_dcp_jid")); + call->ringing_sent = 1; + RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + RAYO_UNLOCK(call); + } + switch_mutex_unlock(RAYO_ACTOR(call)->mutex); + } } } @@ -2651,6 +2662,7 @@ static void rayo_client_handle_event(struct rayo_client *rclient, switch_event_t case SWITCH_EVENT_CHANNEL_ORIGINATE: on_call_originate_event(rclient, event); break; + case SWITCH_EVENT_CHANNEL_PROGRESS: case SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA: on_call_ringing_event(rclient, event); break; @@ -3598,6 +3610,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) switch_event_bind(modname, SWITCH_EVENT_CHANNEL_ORIGINATE, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA, NULL, route_call_event, NULL); + switch_event_bind(modname, SWITCH_EVENT_CHANNEL_PROGRESS, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_ANSWER, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_BRIDGE, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_UNBRIDGE, NULL, route_call_event, NULL); From 14ca0b54987dd37c93864c9a37f7f3c6747c435f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 7 Aug 2013 20:40:16 +0500 Subject: [PATCH 240/278] FS-5655 --resolve --- Freeswitch.2010.express.sln | 2 ++ Freeswitch.2010.sln | 2 ++ Freeswitch.2012.sln | 2 ++ libs/spandsp/src/alloc.c | 11 ++++++++++- libs/spandsp/src/libspandsp.2010.vcxproj | 1 + libs/spandsp/src/libspandsp.2010.vcxproj.filters | 3 +++ libs/spandsp/src/libspandsp.2012.vcxproj | 1 + libs/spandsp/src/libspandsp.2012.vcxproj.filters | 3 +++ 8 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Freeswitch.2010.express.sln b/Freeswitch.2010.express.sln index 6b7c5fde3b..e554a13076 100644 --- a/Freeswitch.2010.express.sln +++ b/Freeswitch.2010.express.sln @@ -2081,9 +2081,11 @@ Global {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.Build.0 = All|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Freeswitch.2010.sln b/Freeswitch.2010.sln index 65ade0314f..4148d2ddc5 100644 --- a/Freeswitch.2010.sln +++ b/Freeswitch.2010.sln @@ -3801,11 +3801,13 @@ Global {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x86 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x86 Setup.ActiveCfg = All|Win32 EndGlobalSection diff --git a/Freeswitch.2012.sln b/Freeswitch.2012.sln index 242ebc85b6..346914e690 100644 --- a/Freeswitch.2012.sln +++ b/Freeswitch.2012.sln @@ -3811,11 +3811,13 @@ Global {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x64 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Debug|x86 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|Win32.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.ActiveCfg = All|Win32 + {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64.Build.0 = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x64 Setup.ActiveCfg = All|Win32 {85F0CF8C-C7AB-48F6-BA19-CC94CF87F981}.Release|x86 Setup.ActiveCfg = All|Win32 EndGlobalSection diff --git a/libs/spandsp/src/alloc.c b/libs/spandsp/src/alloc.c index d02291248c..eee961c615 100644 --- a/libs/spandsp/src/alloc.c +++ b/libs/spandsp/src/alloc.c @@ -48,6 +48,13 @@ #include "spandsp/telephony.h" #include "spandsp/alloc.h" +static void *fake_aligned_alloc(size_t alignment, size_t size); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4232) /* address of dllimport is not static, identity not guaranteed */ +#endif + span_alloc_t __span_alloc = malloc; #if defined(HAVE_ALIGNED_ALLOC) span_aligned_alloc_t __span_aligned_alloc = aligned_alloc; @@ -62,7 +69,9 @@ span_aligned_alloc_t __span_aligned_alloc = fake_aligned_alloc; span_realloc_t __span_realloc = realloc; span_free_t __span_free = free; -static void *fake_aligned_alloc(size_t alignment, size_t size); +#ifdef _MSC_VER +#pragma warning(pop) +#endif #if defined(HAVE_ALIGNED_ALLOC) #elif defined(HAVE_MEMALIGN) diff --git a/libs/spandsp/src/libspandsp.2010.vcxproj b/libs/spandsp/src/libspandsp.2010.vcxproj index 1697ba19a5..8bb87b58c6 100644 --- a/libs/spandsp/src/libspandsp.2010.vcxproj +++ b/libs/spandsp/src/libspandsp.2010.vcxproj @@ -157,6 +157,7 @@ + diff --git a/libs/spandsp/src/libspandsp.2010.vcxproj.filters b/libs/spandsp/src/libspandsp.2010.vcxproj.filters index 05763b180c..cc34e6c278 100644 --- a/libs/spandsp/src/libspandsp.2010.vcxproj.filters +++ b/libs/spandsp/src/libspandsp.2010.vcxproj.filters @@ -283,6 +283,9 @@ Source Files + + Source Files + diff --git a/libs/spandsp/src/libspandsp.2012.vcxproj b/libs/spandsp/src/libspandsp.2012.vcxproj index 92ddbbe0c9..dd99dbc2d9 100644 --- a/libs/spandsp/src/libspandsp.2012.vcxproj +++ b/libs/spandsp/src/libspandsp.2012.vcxproj @@ -161,6 +161,7 @@ + diff --git a/libs/spandsp/src/libspandsp.2012.vcxproj.filters b/libs/spandsp/src/libspandsp.2012.vcxproj.filters index 05763b180c..cc34e6c278 100644 --- a/libs/spandsp/src/libspandsp.2012.vcxproj.filters +++ b/libs/spandsp/src/libspandsp.2012.vcxproj.filters @@ -283,6 +283,9 @@ Source Files + + Source Files + From 11d6c0877aed8d56557c068e5c17435f873e75e0 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 7 Aug 2013 20:59:08 +0500 Subject: [PATCH 241/278] FS-5665 --resolve this should push the extra ones into the variable and convert it to an array --- src/mod/endpoints/mod_sofia/sofia.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index c9c09b233e..da0cd3deac 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -8496,7 +8496,18 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia if (call_info->ci_params && (msg_params_find(call_info->ci_params, "answer-after=0"))) { switch_channel_set_variable(channel, "sip_auto_answer_detected", "true"); } + switch_channel_set_variable(channel, "sip_call_info", call_info_str); + + call_info = call_info->ci_next; + + while (call_info) { + call_info_str = sip_header_as_string(nh->nh_home, (void *) call_info); + switch_channel_add_variable_var_check(channel, "sip_call_info", call_info_str, SWITCH_FALSE, SWITCH_STACK_PUSH); + call_info = call_info->ci_next; + } + + } else if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) { char buf[128] = ""; char *sql; From 116ebc02442e69b7470ac96fd4280635f64b6ca8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 7 Aug 2013 21:22:26 +0500 Subject: [PATCH 242/278] FS-5661 --resolve --- src/include/switch_scheduler.h | 1 + src/mod/applications/mod_commands/mod_commands.c | 6 ++++-- src/mod/applications/mod_dptools/mod_dptools.c | 4 +++- src/switch_scheduler.c | 11 +++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/include/switch_scheduler.h b/src/include/switch_scheduler.h index 8d9bd9ca1a..59997a006d 100644 --- a/src/include/switch_scheduler.h +++ b/src/include/switch_scheduler.h @@ -43,6 +43,7 @@ SWITCH_BEGIN_EXTERN_C int64_t created; int64_t runtime; uint32_t cmd_id; + uint32_t repeat; char *group; void *cmd_arg; uint32_t task_id; diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index ef91f36a8d..fc74c3dd65 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -3164,7 +3164,7 @@ SWITCH_STANDARD_API(uuid_broadcast_function) return SWITCH_STATUS_SUCCESS; } -#define SCHED_BROADCAST_SYNTAX "[+] + + + + + diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 0cb46d7992..248fd47ccf 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -11,6 +11,11 @@ + + + + + diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c index 0b5616a1f3..30d9d215a5 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.c +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c @@ -216,6 +216,17 @@ double iks_find_decimal_attrib(iks *xml, const char *attrib) return atof(iks_find_attrib_soft(xml, attrib)); } +/** + * Get attribute character value of node + * @param xml the XML node to search + * @param attrib the Attribute name + * @return the attribute value + */ +char iks_find_char_attrib(iks *xml, const char *attrib) +{ + return iks_find_attrib_soft(xml, attrib)[0]; +} + /** * Convert iksemel XML node type to string * @param type the XML node type @@ -392,6 +403,54 @@ int iks_attrib_is_decimal_between_zero_and_one(const char *value) return SWITCH_FALSE; } +/** + * Validate dtmf digit + * @param value + * @return SWITCH_TRUE if 0-9,a,b,c,d,A,B,C,D,*,# + */ +int iks_attrib_is_dtmf_digit(const char *value) +{ + if (value && *value && strlen(value) == 1) { + switch (*value) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'a': + case 'B': + case 'b': + case 'C': + case 'c': + case 'D': + case 'd': + case '*': + case '#': + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/** + * @param fn to evaluate attribute + * @param attrib to evaluate + * @return true if not set or is valid + */ +int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib) +{ + if (!attrib || !*attrib) { + return SWITCH_TRUE; + } + return fn(attrib); +} + #define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1) /** diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 90a5ca688d..3a7bae02e7 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -63,6 +63,7 @@ extern const char *iks_find_attrib_soft(iks *xml, const char *attrib); extern const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def); extern int iks_find_bool_attrib(iks *xml, const char *attrib); extern int iks_find_int_attrib(iks *xml, const char *attrib); +extern char iks_find_char_attrib(iks *xml, const char *attrib); extern double iks_find_decimal_attrib(iks *xml, const char *attrib); extern const char *iks_node_type_to_string(int type); extern const char *iks_net_error_to_string(int err); @@ -73,9 +74,12 @@ extern char *iks_server_dialback_key(const char *secret, const char *receiving_s /** A function to validate attribute value */ typedef int (*iks_attrib_validation_function)(const char *); +extern int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib); + #define ELEMENT_DECL(name) extern int VALIDATE_##name(iks *node); #define ELEMENT(name) int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0; #define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def)); +#define OPTIONAL_ATTRIB(name, def, rule) result &= validate_optional_attrib(iks_attrib_is_##rule, iks_find_attrib_default(node, #name, #def)); #define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule); #define ELEMENT_END return result; } @@ -87,6 +91,7 @@ extern int iks_attrib_is_positive(const char *value); extern int iks_attrib_is_positive_or_neg_one(const char *value); extern int iks_attrib_is_any(const char *value); extern int iks_attrib_is_decimal_between_zero_and_one(const char *value); +extern int iks_attrib_is_dtmf_digit(const char *value); #endif diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index fffffe31eb..69f8b2271b 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -3737,6 +3737,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) "" ""); + rayo_add_cmd_alias("prompt_barge_mrcp", "" + "

Please press a digit.

]]>" + "" + "" + "0123456789]]>" + "" + ""); + rayo_add_cmd_alias("prompt_no_barge", "" "

Please press a digit.

]]>
" "" @@ -3800,6 +3808,34 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) ""); rayo_add_cmd_alias("unjoin", ""); + rayo_add_cmd_alias("input_voice_yesno_unimrcp", + "" + "" + "yesno]]>"); +rayo_add_cmd_alias("input_voice_yesno_unimrcp_timeout", + "" + "" + "yesno]]>"); + rayo_add_cmd_alias("input_voice_yesno_pocketsphinx", + "" + "" + "yesno]]>"); + rayo_add_cmd_alias("input_voice_yesno_default", + "" + "" + "yesno]]>"); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.c b/src/mod/event_handlers/mod_rayo/rayo_components.c index 54e241d036..d8a8854f52 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.c +++ b/src/mod/event_handlers/mod_rayo/rayo_components.c @@ -227,15 +227,10 @@ void rayo_component_api_execute_async(struct rayo_component *component, const ch */ switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { - rayo_input_component_load(); - rayo_output_component_load(module_interface, pool); - rayo_prompt_component_load(); - rayo_record_component_load(pool, config_file); - - if (rayo_input_component_load() != SWITCH_STATUS_SUCCESS || - rayo_output_component_load(module_interface, pool) != SWITCH_STATUS_SUCCESS || - rayo_prompt_component_load() != SWITCH_STATUS_SUCCESS || - rayo_record_component_load(pool, config_file) != SWITCH_STATUS_SUCCESS) { + if (rayo_input_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; } return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.h b/src/mod/event_handlers/mod_rayo/rayo_components.h index 71891ea3d6..6e93dfbc43 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.h +++ b/src/mod/event_handlers/mod_rayo/rayo_components.h @@ -54,10 +54,10 @@ #define COMPONENT_COMPLETE_HANGUP "hangup", RAYO_EXT_COMPLETE_NS extern switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); -extern switch_status_t rayo_input_component_load(void); -extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool); -extern switch_status_t rayo_prompt_component_load(void); -extern switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); extern switch_status_t rayo_components_shutdown(void); extern switch_status_t rayo_input_component_shutdown(void); diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c index 89e31c7d66..34577a9492 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_elements.c +++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c @@ -33,7 +33,7 @@ */ ELEMENT(RAYO_INPUT) STRING_ATTRIB(mode, any, "any,dtmf,voice") - ATTRIB(terminator,, any) + OPTIONAL_ATTRIB(terminator,, dtmf_digit) ATTRIB(recognizer,, any) ATTRIB(language, en-US, any) ATTRIB(initial-timeout, -1, positive_or_neg_one) diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 9e10d1b7a5..3bf3b6dfb6 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -45,6 +45,8 @@ struct input_handler; static struct { /** grammar parser */ struct srgs_parser *parser; + /** default recognizer to use if none specified */ + const char *default_recognizer; } globals; /** @@ -57,8 +59,8 @@ struct input_component { int speech_mode; /** Number of collected digits */ int num_digits; - /** Terminating digits */ - int term_digit_mask; + /** Terminating digit */ + char term_digit; /** The collected digits */ char digits[MAX_DTMF + 1]; /** grammar to match */ @@ -70,7 +72,9 @@ struct input_component { /** maximum silence allowed */ int max_silence; /** minimum speech detection confidence */ - int min_confidence; + double min_confidence; + /** sensitivity to background noise */ + double sensitivity; /** timeout after first digit is received */ int inter_digit_timeout; /** stop flag */ @@ -79,6 +83,10 @@ struct input_component { int start_timers; /** true if event fired for first digit / start of speech */ int barge_event; + /** optional language to use */ + const char *language; + /** optional recognizer to use */ + const char *recognizer; /** global data */ struct input_handler *handler; }; @@ -91,77 +99,24 @@ struct input_component { struct input_handler { /** media bug to monitor frames / control input lifecycle */ switch_media_bug_t *bug; - /** active input component - TODO multiple inputs */ - struct input_component *component; + /** active voice input component */ + struct input_component *voice_component; + /** active dtmf input component */ + struct input_component *dtmf_component; /** synchronizes media bug and dtmf callbacks */ switch_mutex_t *mutex; + /** last recognizer used */ + const char *last_recognizer; }; /** - * @return digit mask + * @param digit1 to match + * @param digit2 to match + * @return true if matching */ -static int get_digit_mask(char digit) +static int digit_test(char digit1, char digit2) { - switch(digit) { - case '0': return 1; - case '1': return 1 << 1; - case '2': return 1 << 2; - case '3': return 1 << 3; - case '4': return 1 << 4; - case '5': return 1 << 5; - case '6': return 1 << 6; - case '7': return 1 << 7; - case '8': return 1 << 8; - case '9': return 1 << 9; - case 'A': - case 'a': return 1 << 10; - case 'B': - case 'b': return 1 << 11; - case 'C': - case 'c': return 1 << 12; - case 'D': - case 'd': return 1 << 13; - case '#': return 1 << 14; - case '*': return 1 << 15; - } - return 0; -} - -/** - * @param digit_mask to check - * @param digit to look for - * @return true if set - */ -static int digit_mask_test(int digit_mask, char digit) -{ - return digit_mask & get_digit_mask(digit); -} - -/** - * @param digit_mask to set digit in - * @param digit to set - * @return the digit mask with the set digit - */ -static int digit_mask_set(int digit_mask, char digit) -{ - return digit_mask | get_digit_mask(digit); -} - -/** - * @param digit_mask to set digits in - * @param digits to add to mask - * @return the digit mask with the set digits - */ -static int digit_mask_set_from_digits(int digit_mask, const char *digits) -{ - if (!zstr(digits)) { - int digits_len = strlen(digits); - int i; - for (i = 0; i < digits_len; i++) { - digit_mask = digit_mask_set(digit_mask, digits[i]); - } - } - return digit_mask; + return digit1 && digit2 && tolower(digit1) == tolower(digit2); } /** @@ -205,15 +160,14 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c switch_mutex_lock(handler->mutex); - component = handler->component; + component = handler->dtmf_component; /* additional paranoia check */ if (!component) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Received DTMF without active input component\n"); switch_mutex_unlock(handler->mutex); return SWITCH_STATUS_SUCCESS; } - is_term_digit = digit_mask_test(component->term_digit_mask, dtmf->digit); + is_term_digit = digit_test(component->term_digit, dtmf->digit); if (!is_term_digit) { component->digits[component->num_digits] = dtmf->digit; @@ -247,7 +201,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c } case SMT_NO_MATCH: { /* notify of no-match and remove input component */ - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_remove(session, &handler->bug); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NO MATCH = %s\n", component->digits); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); @@ -256,7 +210,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c case SMT_MATCH_END: { iks *result = nlsml_create_dtmf_match(component->digits); /* notify of match and remove input component */ - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_remove(session, &handler->bug); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits); send_match_event(RAYO_COMPONENT(component), result); @@ -279,7 +233,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void struct input_component *component; switch_mutex_lock(handler->mutex); - component = handler->component; + component = handler->dtmf_component; switch(type) { case SWITCH_ABC_TYPE_INIT: { @@ -294,7 +248,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000; if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) { enum srgs_match_type match; - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_set_flag(bug, SMBF_PRUNE); /* we got some input, check for match */ @@ -310,7 +264,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); } } else if (!component->num_digits && component->initial_timeout > 0 && elapsed_ms > component->initial_timeout) { - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_set_flag(bug, SMBF_PRUNE); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n"); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOINPUT); @@ -323,10 +277,10 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void /* check for hangup */ if (component) { if (component->stop) { - handler->component = NULL; + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); } else { - handler->component = NULL; + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP); } } @@ -396,34 +350,53 @@ static iks *start_call_input(struct input_component *component, switch_core_sess handler = switch_core_session_alloc(session, sizeof(*handler)); switch_mutex_init(&handler->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); switch_channel_set_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR, handler); + handler->last_recognizer = ""; } - handler->component = component; + + /* TODO break up this function by mode... dtmf/voice/fax/etc */ + component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf"); + if (component->speech_mode && handler->voice_component) { + /* don't allow multi voice input */ + return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple voice input is not allowed"); + } + if (!component->speech_mode && handler->dtmf_component) { + /* don't allow multi dtmf input */ + return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple dtmf input is not allowed"); + } + + if (component->speech_mode) { + handler->voice_component = component; + } else { + handler->dtmf_component = component; + } + + component->grammar = NULL; component->num_digits = 0; component->digits[0] = '\0'; component->stop = 0; - component->speech_mode = 0; component->initial_timeout = iks_find_int_attrib(input, "initial-timeout"); component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout"); component->max_silence = iks_find_int_attrib(input, "max-silence"); - component->min_confidence = (int)ceil(iks_find_decimal_attrib(input, "min-confidence") * 100.0); + component->min_confidence = iks_find_decimal_attrib(input, "min-confidence"); + component->sensitivity = iks_find_decimal_attrib(input, "sensitivity"); component->barge_event = iks_find_bool_attrib(input, "barge-event"); component->start_timers = iks_find_bool_attrib(input, "start-timers"); - /* TODO this should just be a single digit terminator? */ - component->term_digit_mask = digit_mask_set_from_digits(0, iks_find_attrib_soft(input, "terminator")); - /* TODO recognizer ignored */ - /* TODO language ignored */ + component->term_digit = iks_find_char_attrib(input, "terminator"); + component->recognizer = iks_find_attrib(input, "recognizer"); + component->language = iks_find_attrib(input, "language"); component->handler = handler; - /* parse the grammar */ - if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); - RAYO_UNLOCK(component); - RAYO_DESTROY(component); - return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); - } - /* is this voice or dtmf srgs grammar? */ - if (!strcasecmp("dtmf", iks_find_attrib_soft(input, "mode"))) { + if (!component->speech_mode) { + + /* parse the grammar */ + if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); + } + component->last_digit_time = switch_micro_time_now(); /* acknowledge command */ @@ -431,38 +404,124 @@ static iks *start_call_input(struct input_component *component, switch_core_sess /* start dtmf input detection */ if (switch_core_media_bug_add(session, "rayo_input_component", NULL, input_component_bug_callback, handler, 0, SMBF_READ_REPLACE, &handler->bug) != SWITCH_STATUS_SUCCESS) { + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); } } else { - char *grammar = NULL; - const char *jsgf_path; - component->speech_mode = 1; - jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram"); - if (!jsgf_path) { + switch_stream_handle_t grammar = { 0 }; + SWITCH_STANDARD_STREAM(grammar); + + if (zstr(component->recognizer)) { + component->recognizer = globals.default_recognizer; + } + + /* if recognition engine is different, we can't handle this request */ + if (!zstr(handler->last_recognizer) && strcmp(component->recognizer, handler->last_recognizer)) { + handler->voice_component = NULL; RAYO_UNLOCK(component); RAYO_DESTROY(component); - return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar error"); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Must use the same recognizer for the entire call"); + } + handler->last_recognizer = switch_core_session_strdup(session, component->recognizer); + + if (!strcmp(component->recognizer, "pocketsphinx")) { + const char *jsgf_path; + + /* transform SRGS grammar to JSGF */ + if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); + handler->voice_component = NULL; + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); + } + jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram"); + if (!jsgf_path) { + handler->voice_component = NULL; + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar conversion to JSGF error"); + } + + /* build pocketsphinx grammar string */ + grammar.write_function(&grammar, + "{start-input-timers=%s,no-input-timeout=%d,speech-timeout=%d,confidence-threshold=%d}%s", + component->start_timers ? "true" : "false", + component->initial_timeout, + component->max_silence, + (int)ceil(component->min_confidence * 100.0), + jsgf_path); + } else if (!strncmp(component->recognizer, "unimrcp", strlen("unimrcp"))) { + /* send inline grammar to unimrcp */ + grammar.write_function(&grammar, "{start-input-timers=%s,confidence-threshold=%f,sensitivity-level=%f", + component->start_timers ? "true" : "false", + component->min_confidence, + component->sensitivity); + + if (component->initial_timeout > 0) { + grammar.write_function(&grammar, ",no-input-timeout=%d", + component->initial_timeout); + } + + if (component->max_silence > 0) { + grammar.write_function(&grammar, ",speech-complete-timeout=%d,speech-incomplete-timeout=%d", + component->max_silence, + component->max_silence); + } + + if (!zstr(component->language)) { + grammar.write_function(&grammar, ",speech-language=%s", component->language); + } + + if (!strcmp(iks_find_attrib_soft(input, "mode"), "any")) { + /* set dtmf params */ + if (component->inter_digit_timeout > 0) { + grammar.write_function(&grammar, ",dtmf-interdigit-timeout=%d", component->inter_digit_timeout); + } + if (component->term_digit) { + grammar.write_function(&grammar, ",dtmf-term-char=%c", component->term_digit); + } + } + + grammar.write_function(&grammar, "}inline:%s", iks_find_cdata(input, "grammar")); + } else { + /* passthrough to unknown ASR module */ + grammar.write_function(&grammar, "%s", iks_find_cdata(input, "grammar")); } /* acknowledge command */ rayo_component_send_start(RAYO_COMPONENT(component), iq); - /* TODO configurable speech detection - different engines, grammar passthrough, dtmf handled by recognizer */ - grammar = switch_mprintf("{no-input-timeout=%s,speech-timeout=%s,start-input-timers=%s,confidence-threshold=%d}%s", - component->initial_timeout, component->max_silence, - component->start_timers ? "true" : "false", - component->min_confidence, jsgf_path); /* start speech detection */ switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true"); - if (switch_ivr_detect_speech(session, "pocketsphinx", grammar, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_detect_speech(session, component->recognizer, grammar.data, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) { + handler->voice_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); } - switch_safe_free(grammar); + switch_safe_free(grammar.data); } return NULL; } +/** + * Create input component id for session. + * @param session requesting component + * @param input request + * @return the ID + */ +static char *create_input_component_id(switch_core_session_t *session, iks *input) +{ + const char *mode = "unk"; + if (input) { + mode = iks_find_attrib_soft(input, "mode"); + if (!strcmp(mode, "any")) { + mode = "voice"; + } + } + return switch_core_session_sprintf(session, "%s-input-%s", switch_core_session_get_uuid(session), mode); +} + /** * Start execution of input component */ @@ -470,10 +529,10 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; - char *component_id = switch_mprintf("%s-input", switch_core_session_get_uuid(session)); + iks *input = iks_find(iq, "input"); + char *component_id = create_input_component_id(session, input); switch_memory_pool_t *pool = NULL; struct input_component *input_component = NULL; - iks *input = iks_find(iq, "input"); const char *error = NULL; if (!validate_call_input(input, &error)) { @@ -484,7 +543,6 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess switch_core_new_memory_pool(&pool); input_component = switch_core_alloc(pool, sizeof(*input_component)); rayo_component_init(RAYO_COMPONENT(input_component), pool, RAT_CALL_COMPONENT, "input", component_id, call, iks_find_attrib(iq, "from")); - switch_safe_free(component_id); /* start input */ return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0); @@ -530,7 +588,6 @@ static iks *start_timers_call_input_component(struct rayo_actor *component, stru switch_mutex_lock(input_component->handler->mutex); if (input_component->speech_mode) { switch_ivr_detect_speech_start_input_timers(session); - rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); } else { input_component->last_digit_time = switch_micro_time_now(); input_component->start_timers = 1; @@ -549,53 +606,63 @@ static void on_detected_speech_event(switch_event_t *event) { const char *speech_type = switch_event_get_header(event, "Speech-Type"); char *event_str = NULL; + const char *uuid = switch_event_get_header(event, "Unique-ID"); switch_event_serialize(event, &event_str, SWITCH_FALSE); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str); - if (!speech_type) { + if (!speech_type || !uuid) { return; } + if (!strcasecmp("detected-speech", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); + switch_safe_free(component_id); if (component) { const char *result = switch_event_get_body(event); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); - INPUT_COMPONENT(component)->handler->component = NULL; + INPUT_COMPONENT(component)->handler->voice_component = NULL; switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); if (zstr(result)) { rayo_component_send_complete(component, INPUT_NOMATCH); } else { - enum nlsml_match_type match_type = nlsml_parse(result, uuid); - switch (match_type) { - case NMT_NOINPUT: + if (strchr(result, '<')) { + /* got an XML result */ + enum nlsml_match_type match_type = nlsml_parse(result, uuid); + switch (match_type) { + case NMT_NOINPUT: + rayo_component_send_complete(component, INPUT_NOINPUT); + break; + case NMT_MATCH: { + iks *result_xml = nlsml_normalize(result); + send_match_event(RAYO_COMPONENT(component), result_xml); + iks_delete(result_xml); + break; + } + case NMT_BAD_XML: + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result); + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + case NMT_NOMATCH: + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + default: + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result); + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + } + } else if (strstr(result, "002")) { + /* Completion-Cause: 002 no-input-timeout */ rayo_component_send_complete(component, INPUT_NOINPUT); - break; - case NMT_MATCH: { - iks *result_xml = nlsml_normalize(result); - send_match_event(RAYO_COMPONENT(component), result_xml); - iks_delete(result_xml); - break; - } - case NMT_BAD_XML: - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result); + } else { + /* assume no match */ rayo_component_send_complete(component, INPUT_NOMATCH); - break; - case NMT_NOMATCH: - rayo_component_send_complete(component, INPUT_NOMATCH); - break; - default: - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result); - rayo_component_send_complete(component, INPUT_NOMATCH); - break; } } RAYO_UNLOCK(component); } } else if (!strcasecmp("begin-speaking", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); switch_safe_free(component_id); if (component && INPUT_COMPONENT(component)->barge_event) { @@ -603,14 +670,13 @@ static void on_detected_speech_event(switch_event_t *event) } RAYO_UNLOCK(component); } else if (!strcasecmp("closed", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); switch_safe_free(component_id); if (component) { char *channel_state = switch_event_get_header(event, "Channel-State"); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); - INPUT_COMPONENT(component)->handler->component = NULL; + INPUT_COMPONENT(component)->handler->voice_component = NULL; switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n"); if (channel_state && !strcmp("CS_HANGUP", channel_state)) { @@ -625,12 +691,63 @@ static void on_detected_speech_event(switch_event_t *event) switch_safe_free(event_str); } +/** + * Process module XML configuration + * @param pool memory pool to allocate from + * @param config_file to use + * @return SWITCH_STATUS_SUCCESS on successful configuration + */ +static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file) +{ + switch_xml_t cfg, xml; + + /* set defaults */ + globals.default_recognizer = "pocketsphinx"; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring module\n"); + if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file); + return SWITCH_STATUS_TERM; + } + + /* get params */ + { + switch_xml_t settings = switch_xml_child(cfg, "input"); + if (settings) { + switch_xml_t param; + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + const char *var = switch_xml_attr_soft(param, "name"); + const char *val = switch_xml_attr_soft(param, "value"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val); + if (!strcasecmp(var, "default-recognizer")) { + if (!zstr(val)) { + globals.default_recognizer = switch_core_strdup(pool, val); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var); + } + } + } + } + + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + /** * Initialize input component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_input_component_load(void) +switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { + if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } + srgs_init(); nlsml_init(); diff --git a/src/mod/event_handlers/mod_rayo/rayo_output_component.c b/src/mod/event_handlers/mod_rayo/rayo_output_component.c index f92d994a64..e85a12cece 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_output_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_output_component.c @@ -1092,9 +1092,12 @@ static char *fileman_supported_formats[] = { "fileman", NULL }; /** * Initialize output component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) +switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { switch_api_interface_t *api_interface; switch_file_interface_t *file_interface; diff --git a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c index 47dbc46df3..f48e7d02d2 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c @@ -620,9 +620,12 @@ static iks *forward_output_component_request(struct rayo_actor *prompt, struct r /** * Initialize prompt component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_prompt_component_load(void) +switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { /* Prompt is a convenience component that wraps and */ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component); diff --git a/src/mod/event_handlers/mod_rayo/rayo_record_component.c b/src/mod/event_handlers/mod_rayo/rayo_record_component.c index 2db5b30771..601d8cb3aa 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_record_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_record_component.c @@ -479,11 +479,12 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_ /** * Initialize record component + * @param module_interface * @param pool memory pool to allocate from * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file) +switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; diff --git a/src/mod/event_handlers/mod_rayo/test_iks/main.c b/src/mod/event_handlers/mod_rayo/test_iks/main.c index 699f40267e..09a368dc4b 100644 --- a/src/mod/event_handlers/mod_rayo/test_iks/main.c +++ b/src/mod/event_handlers/mod_rayo/test_iks/main.c @@ -145,6 +145,27 @@ static void test_dialback_key(void) ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL)); } +static void test_validate_dtmf(void) +{ + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("1")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("A")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("a")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("D")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("d")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("*")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("#")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("E")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit(NULL)); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "A")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "1")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "Z")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, NULL)); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "")); +} + /** * main program */ @@ -159,5 +180,6 @@ int main(int argc, char **argv) TEST(test_rayo_test_srgs); TEST(test_iks_helper_value_matches); TEST(test_dialback_key); + TEST(test_validate_dtmf); return 0; } From 451aece80391bf08bc0e8b25fce2e2cb4f88bc41 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 14 Aug 2013 10:06:18 -0400 Subject: [PATCH 275/278] mod_unimrcp: add example config for Vestec --- conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml diff --git a/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml new file mode 100644 index 0000000000..cbde87ca5a --- /dev/null +++ b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From cae7d029abe8ebe7a2b79bdfeae8129b83c61e16 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 14 Aug 2013 09:05:14 -0500 Subject: [PATCH 276/278] FS-5694 --resolve --- src/mod/applications/mod_voicemail/mod_voicemail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 41f7cef5d6..e7d4da79ca 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -2876,7 +2876,7 @@ static switch_status_t deliver_vm(vm_profile_t *profile, update_mwi(profile, myid, domain_name, myfolder, MWI_REASON_NEW); } - if (send_mail && !zstr(vm_email) && switch_file_exists(file_path, pool) == SWITCH_STATUS_SUCCESS) { + if (send_mail && (!zstr(vm_email) || !zstr(vm_notify_email)) && switch_file_exists(file_path, pool) == SWITCH_STATUS_SUCCESS) { switch_event_t *event; char *from; char *body; From 129a509cfbd1def1c76c2ccacfeb78c696d52a42 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 14 Aug 2013 09:48:05 -0500 Subject: [PATCH 277/278] FS-5648 --resolve --- src/mod/endpoints/mod_sofia/sofia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index db11faa40e..40c8bc370d 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -8669,6 +8669,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia profile_dup_clean(displayname, tech_pvt->caller_profile->caller_id_name, tech_pvt->caller_profile->pool); profile_dup_clean(from_user, tech_pvt->caller_profile->caller_id_number, tech_pvt->caller_profile->pool); profile_dup_clean(network_ip, tech_pvt->caller_profile->network_addr, tech_pvt->caller_profile->pool); + profile_dup_clean(from_user, tech_pvt->caller_profile->ani, tech_pvt->caller_profile->pool); profile_dup_clean(aniii, tech_pvt->caller_profile->aniii, tech_pvt->caller_profile->pool); profile_dup_clean(context, tech_pvt->caller_profile->context, tech_pvt->caller_profile->pool); profile_dup_clean(destination_number, tech_pvt->caller_profile->destination_number, tech_pvt->caller_profile->pool); From 624189be16a1d3828025d82ae092bbb1da3d489d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Aug 2013 21:00:08 +0500 Subject: [PATCH 278/278] FS-5701 --resolve --- src/switch_ivr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 232f0a6dd2..1f91ed9625 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2907,6 +2907,12 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3 interval = read_impl.microseconds_per_packet / 1000; //samples = switch_samples_per_packet(read_impl.samples_per_second, interval); + if (delay_ms < interval * 2) { + delay_ms = interval * 2; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Minimum possible delay for this codec (%d) has been chosen\n", delay_ms); + } + + qlen = delay_ms / (interval) / 2; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0);