From f155cb4d073c60c57a8f2aca833c11fc04317c43 Mon Sep 17 00:00:00 2001 From: rcollier Date: Thu, 16 Jan 2014 05:14:39 +0000 Subject: [PATCH 02/22] SMACK-286 Made ProviderManager much more configurable. Separated the reading of provider files from the ProviderManager. Manager now only manages. Added ability to add collections of providers to the manager via a ProviderLoader, of which there is one default implementation which loads from the default file format. Now provider files can be programmatically added at any time. Also updated the configuration abilities so that a provider file can also be set via VM arg, as well as the smack configuration itself. Introduced Java Util Logging as well. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13861 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 45 +- build/release.xml | 17 +- build/resources/META-INF/core.providers | 19 + .../{smack.providers => extension.providers} | 1312 ++++++++--------- build/resources/META-INF/jul.properties | 3 + build/resources/META-INF/sample.providers | 17 + build/resources/META-INF/smack-config.xml | 6 +- documentation/gettingstarted.html | 31 + documentation/providers.html | 283 +++- .../smack/LoggingInitializer.java | 28 + .../smack/SmackConfiguration.java | 253 ++-- .../jivesoftware/smack/SmackInitializer.java | 14 + .../smack/provider/AbstractProviderInfo.java | 26 + .../smack/provider/CoreInitializer.java | 15 + .../smack/provider/ExtensionProviderInfo.java | 33 + .../smack/provider/IQProviderInfo.java | 35 + .../smack/provider/ProviderFileLoader.java | 146 ++ .../smack/provider/ProviderLoader.java | 23 + .../smack/provider/ProviderManager.java | 185 +-- .../provider/UrlProviderFileInitializer.java | 54 + .../smack/provider/VmArgInitializer.java | 23 + .../jivesoftware/smack/util/FileUtils.java | 59 + .../smackx/provider/ExtensionInitializer.java | 16 + .../smackx/provider/ProviderConfigTest.java | 55 + .../smackx/provider/test.providers | 11 + 25 files changed, 1709 insertions(+), 1000 deletions(-) create mode 100644 build/resources/META-INF/core.providers rename build/resources/META-INF/{smack.providers => extension.providers} (97%) create mode 100644 build/resources/META-INF/jul.properties create mode 100644 build/resources/META-INF/sample.providers create mode 100644 source/org/jivesoftware/smack/LoggingInitializer.java create mode 100644 source/org/jivesoftware/smack/SmackInitializer.java create mode 100644 source/org/jivesoftware/smack/provider/AbstractProviderInfo.java create mode 100644 source/org/jivesoftware/smack/provider/CoreInitializer.java create mode 100644 source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java create mode 100644 source/org/jivesoftware/smack/provider/IQProviderInfo.java create mode 100644 source/org/jivesoftware/smack/provider/ProviderFileLoader.java create mode 100644 source/org/jivesoftware/smack/provider/ProviderLoader.java create mode 100644 source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java create mode 100644 source/org/jivesoftware/smack/provider/VmArgInitializer.java create mode 100644 source/org/jivesoftware/smack/util/FileUtils.java create mode 100644 source/org/jivesoftware/smackx/provider/ExtensionInitializer.java create mode 100644 test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java create mode 100644 test-unit/org/jivesoftware/smackx/provider/test.providers diff --git a/build/build.xml b/build/build.xml index a93e74c18..7d5d73acd 100644 --- a/build/build.xml +++ b/build/build.xml @@ -29,7 +29,7 @@ - + @@ -170,54 +170,63 @@ + - + + + includes="org/jivesoftware/smack/**/*.class, **/smack-config.xml, **/core.providers, **/jul.properties"> - - + + - - + + - - + + - + + + - - - + + - + @@ -225,14 +234,14 @@ - - + + - + diff --git a/build/release.xml b/build/release.xml index b4a0eef4b..667b71d2f 100644 --- a/build/release.xml +++ b/build/release.xml @@ -43,10 +43,14 @@ + + + + @@ -54,12 +58,20 @@ + + + + + + + + @@ -110,7 +122,10 @@ - + + + + diff --git a/build/resources/META-INF/core.providers b/build/resources/META-INF/core.providers new file mode 100644 index 000000000..614a2d8d7 --- /dev/null +++ b/build/resources/META-INF/core.providers @@ -0,0 +1,19 @@ + + + + + + + query + jabber:iq:privacy + org.jivesoftware.smack.provider.PrivacyProvider + + + + + ping + urn:xmpp:ping + org.jivesoftware.smack.ping.provider.PingProvider + + + diff --git a/build/resources/META-INF/smack.providers b/build/resources/META-INF/extension.providers similarity index 97% rename from build/resources/META-INF/smack.providers rename to build/resources/META-INF/extension.providers index 452e3df55..a0e420c20 100644 --- a/build/resources/META-INF/smack.providers +++ b/build/resources/META-INF/extension.providers @@ -1,663 +1,649 @@ - - - - - - - query - jabber:iq:private - org.jivesoftware.smackx.PrivateDataManager$PrivateDataIQProvider - - - - - query - jabber:iq:time - org.jivesoftware.smackx.packet.Time - - - - - x - jabber:x:roster - org.jivesoftware.smackx.provider.RosterExchangeProvider - - - - - x - jabber:x:event - org.jivesoftware.smackx.provider.MessageEventProvider - - - - - active - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - composing - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - paused - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - inactive - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - gone - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - - html - http://jabber.org/protocol/xhtml-im - org.jivesoftware.smackx.provider.XHTMLExtensionProvider - - - - - x - jabber:x:conference - org.jivesoftware.smackx.GroupChatInvitation$Provider - - - - - query - http://jabber.org/protocol/disco#items - org.jivesoftware.smackx.provider.DiscoverItemsProvider - - - - - query - http://jabber.org/protocol/disco#info - org.jivesoftware.smackx.provider.DiscoverInfoProvider - - - - - x - jabber:x:data - org.jivesoftware.smackx.provider.DataFormProvider - - - - - x - http://jabber.org/protocol/muc#user - org.jivesoftware.smackx.provider.MUCUserProvider - - - - - query - http://jabber.org/protocol/muc#admin - org.jivesoftware.smackx.provider.MUCAdminProvider - - - - - query - http://jabber.org/protocol/muc#owner - org.jivesoftware.smackx.provider.MUCOwnerProvider - - - - - x - jabber:x:delay - org.jivesoftware.smackx.provider.DelayInformationProvider - - - - delay - urn:xmpp:delay - org.jivesoftware.smackx.provider.DelayInfoProvider - - - - - query - jabber:iq:version - org.jivesoftware.smackx.packet.Version - - - - - vCard - vcard-temp - org.jivesoftware.smackx.provider.VCardProvider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.packet.OfflineMessageRequest$Provider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.packet.OfflineMessageInfo$Provider - - - - - query - jabber:iq:last - org.jivesoftware.smackx.packet.LastActivity$Provider - - - - - query - jabber:iq:search - org.jivesoftware.smackx.search.UserSearch$Provider - - - - - sharedgroup - http://www.jivesoftware.org/protocol/sharedgroup - org.jivesoftware.smackx.packet.SharedGroupsInfo$Provider - - - - - addresses - http://jabber.org/protocol/address - org.jivesoftware.smackx.provider.MultipleAddressesProvider - - - - - si - http://jabber.org/protocol/si - org.jivesoftware.smackx.provider.StreamInitiationProvider - - - - query - http://jabber.org/protocol/bytestreams - org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider - - - - open - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - close - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - - query - jabber:iq:privacy - org.jivesoftware.smack.provider.PrivacyProvider - - - - - command - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider - - - - bad-action - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadActionError - - - - malformed-actionn - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$MalformedActionError - - - - bad-locale - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadLocaleError - - - - bad-payload - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadPayloadError - - - - bad-sessionid - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadSessionIDError - - - - session-expired - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$SessionExpiredError - - - - - - offer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider - - - - offer-revoke - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider - - - - agent-status-request - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider - - - - transcripts - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider - - - - transcript - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptProvider - - - - workgroups - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider - - - - agent-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider - - - - transcript-search - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider - - - - occupants-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider - - - - chat-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider - - - - chat-notes - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider - - - - chat-sessions - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider - - - - offline-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider - - - - sound-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider - - - - workgroup-properties - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider - - - - - search-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider - - - - workgroup-form - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider - - - - macros - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider - - - - chat-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider - - - - generic-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider - - - - monitor - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider - - - - - queue-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider - - - - workgroup - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider - - - - metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MetaDataProvider - - - - session - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.SessionID$Provider - - - - user - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.UserID$Provider - - - - agent-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider - - - - notify-queue-details - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider - - - - notify-queue - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider - - - - invite - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider - - - - transfer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider - - - - - headers - http://jabber.org/protocol/shim - org.jivesoftware.smackx.provider.HeadersProvider - - - - header - http://jabber.org/protocol/shim - org.jivesoftware.smackx.provider.HeaderProvider - - - - - pubsub - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - create - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - items - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - subscriptions - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider - - - - subscription - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider - - - - affiliations - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider - - - - affiliation - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationProvider - - - - options - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - pubsub - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - configure - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - default - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - event - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.EventProvider - - - - configuration - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider - - - - delete - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - options - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - items - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - retract - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.RetractEventProvider - - - - purge - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - - nick - http://jabber.org/protocol/nick - org.jivesoftware.smackx.packet.Nick$Provider - - - - - attention - urn:xmpp:attention:0 - org.jivesoftware.smackx.packet.AttentionExtension$Provider - - - - - received - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider - - - request - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider - - - - - c - http://jabber.org/protocol/caps - org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider - - - - - ping - urn:xmpp:ping - org.jivesoftware.smack.ping.provider.PingProvider - - - + + + + + + + query + jabber:iq:private + org.jivesoftware.smackx.PrivateDataManager$PrivateDataIQProvider + + + + + query + jabber:iq:time + org.jivesoftware.smackx.packet.Time + + + + + x + jabber:x:roster + org.jivesoftware.smackx.provider.RosterExchangeProvider + + + + + x + jabber:x:event + org.jivesoftware.smackx.provider.MessageEventProvider + + + + + active + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + composing + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + paused + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + inactive + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + gone + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + + html + http://jabber.org/protocol/xhtml-im + org.jivesoftware.smackx.provider.XHTMLExtensionProvider + + + + + x + jabber:x:conference + org.jivesoftware.smackx.GroupChatInvitation$Provider + + + + + query + http://jabber.org/protocol/disco#items + org.jivesoftware.smackx.provider.DiscoverItemsProvider + + + + + query + http://jabber.org/protocol/disco#info + org.jivesoftware.smackx.provider.DiscoverInfoProvider + + + + + x + jabber:x:data + org.jivesoftware.smackx.provider.DataFormProvider + + + + + x + http://jabber.org/protocol/muc#user + org.jivesoftware.smackx.provider.MUCUserProvider + + + + + query + http://jabber.org/protocol/muc#admin + org.jivesoftware.smackx.provider.MUCAdminProvider + + + + + query + http://jabber.org/protocol/muc#owner + org.jivesoftware.smackx.provider.MUCOwnerProvider + + + + + x + jabber:x:delay + org.jivesoftware.smackx.provider.DelayInformationProvider + + + + delay + urn:xmpp:delay + org.jivesoftware.smackx.provider.DelayInfoProvider + + + + + query + jabber:iq:version + org.jivesoftware.smackx.packet.Version + + + + + vCard + vcard-temp + org.jivesoftware.smackx.provider.VCardProvider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.packet.OfflineMessageRequest$Provider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.packet.OfflineMessageInfo$Provider + + + + + query + jabber:iq:last + org.jivesoftware.smackx.packet.LastActivity$Provider + + + + + query + jabber:iq:search + org.jivesoftware.smackx.search.UserSearch$Provider + + + + + sharedgroup + http://www.jivesoftware.org/protocol/sharedgroup + org.jivesoftware.smackx.packet.SharedGroupsInfo$Provider + + + + + addresses + http://jabber.org/protocol/address + org.jivesoftware.smackx.provider.MultipleAddressesProvider + + + + + si + http://jabber.org/protocol/si + org.jivesoftware.smackx.provider.StreamInitiationProvider + + + + query + http://jabber.org/protocol/bytestreams + org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider + + + + open + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + close + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + + command + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider + + + + bad-action + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadActionError + + + + malformed-actionn + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$MalformedActionError + + + + bad-locale + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadLocaleError + + + + bad-payload + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadPayloadError + + + + bad-sessionid + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadSessionIDError + + + + session-expired + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$SessionExpiredError + + + + + + offer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider + + + + offer-revoke + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider + + + + agent-status-request + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider + + + + transcripts + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider + + + + transcript + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptProvider + + + + workgroups + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider + + + + agent-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider + + + + transcript-search + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider + + + + occupants-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider + + + + chat-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider + + + + chat-notes + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider + + + + chat-sessions + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider + + + + offline-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider + + + + sound-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider + + + + workgroup-properties + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider + + + + + search-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider + + + + workgroup-form + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider + + + + macros + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider + + + + chat-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider + + + + generic-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider + + + + monitor + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider + + + + + queue-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider + + + + workgroup + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider + + + + metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MetaDataProvider + + + + session + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.SessionID$Provider + + + + user + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.UserID$Provider + + + + agent-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider + + + + notify-queue-details + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider + + + + notify-queue + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider + + + + invite + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider + + + + transfer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider + + + + + headers + http://jabber.org/protocol/shim + org.jivesoftware.smackx.provider.HeadersProvider + + + + header + http://jabber.org/protocol/shim + org.jivesoftware.smackx.provider.HeaderProvider + + + + + pubsub + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + create + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + items + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + subscriptions + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider + + + + subscription + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider + + + + affiliations + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider + + + + affiliation + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationProvider + + + + options + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + pubsub + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + configure + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + default + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + event + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.EventProvider + + + + configuration + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider + + + + delete + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + options + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + items + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + retract + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.RetractEventProvider + + + + purge + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + + nick + http://jabber.org/protocol/nick + org.jivesoftware.smackx.packet.Nick$Provider + + + + + attention + urn:xmpp:attention:0 + org.jivesoftware.smackx.packet.AttentionExtension$Provider + + + + + received + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider + + + request + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider + + + + + c + http://jabber.org/protocol/caps + org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider + + + diff --git a/build/resources/META-INF/jul.properties b/build/resources/META-INF/jul.properties new file mode 100644 index 000000000..fe90575ec --- /dev/null +++ b/build/resources/META-INF/jul.properties @@ -0,0 +1,3 @@ +# Java Util Logging configuration for Smack. +handlers = java.util.logging.ConsoleHandler +.level = WARNING \ No newline at end of file diff --git a/build/resources/META-INF/sample.providers b/build/resources/META-INF/sample.providers new file mode 100644 index 000000000..b13ddbdaa --- /dev/null +++ b/build/resources/META-INF/sample.providers @@ -0,0 +1,17 @@ + + + + + + element + ns + com.myco.MyIQProvider + + + + elem + http://jabber.org/protocol/whoknows + com.myco.MyExtProvider + + + diff --git a/build/resources/META-INF/smack-config.xml b/build/resources/META-INF/smack-config.xml index a4df4764c..f3cad304e 100644 --- a/build/resources/META-INF/smack-config.xml +++ b/build/resources/META-INF/smack-config.xml @@ -22,9 +22,13 @@ - org.jivesoftware.smackx.ServiceDiscoveryManager + org.jivesoftware.smack.LoggingInitializer + org.jivesoftware.smack.provider.CoreInitializer + org.jivesoftware.smack.provider.VmArgInitializer org.jivesoftware.smack.PrivacyListManager org.jivesoftware.smack.keepalive.KeepAliveManager + org.jivesoftware.smackx.provider.ExtensionInitializer + org.jivesoftware.smackx.ServiceDiscoveryManager org.jivesoftware.smackx.XHTMLManager org.jivesoftware.smackx.muc.MultiUserChat org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager diff --git a/documentation/gettingstarted.html b/documentation/gettingstarted.html index c6dd7d670..7c6d5725d 100644 --- a/documentation/gettingstarted.html +++ b/documentation/gettingstarted.html @@ -40,6 +40,37 @@ over which features applications require: +

Configuration

+Smack has an initialization process that involves 2 phases. +
    +
  • Initializing system properties - Initializing all the system properties accessible through the class +SmackConfiguration. These properties are retrieve by the getXXX methods on that class. +
  • Initializing startup classes - Initializing any classes meant to be active at startup by instantiating +the class, and then calling the initialize method on that class if it extends SmackInitializer. +If it does not extend this interface, then initialization will have to take place in a static block of code +which is automatically executed when the class is loaded. +
+

+Initialization is accomplished via a configuration file. By default, Smack will load the one embedded in +the Smack jar at META-INF/smack-config.xml. This particular configuration contains all the default +property values as well as a list of initializer classes to load. All manager type classes are contained +in this list of initializers. If your application does not use all the features provided by Smack via the +aforementioned managers, you may want to 'turn them off' by providing a custom config file that does not +include that feature. +

+If you want to change the configuration file used, you have two options: +

    +
  • Programmatically - Call the setConfigFileUrl method of SmackConfiguration with the location +of a new config file. +
    SmackConfiguration.setConfigFileUrl("classpath:test/smack-config.xml", null)
    +
  • VM Argument - Set the VM argument smack.config.file to the location of a new config file. +
    -Dsmack.config.file=file:///c:/com/myco/provider/myco_custom_config.xml
    +
+ +

+Please note, there is a copy of the smack-config.xml in the samples directory of the deployment +archive file (zip or tar). +

Establishing a Connection

diff --git a/documentation/providers.html b/documentation/providers.html index 229df7f8b..21f441b64 100644 --- a/documentation/providers.html +++ b/documentation/providers.html @@ -15,44 +15,68 @@ Provider Architecture: Packet Extensions and Custom IQ's

+

Introduction

The Smack provider architecture is a system for plugging in custom XML parsing of packet extensions and IQ packets. The standard Smack Extensions -are built using the provider architecture. Two types of -providers exist:
    +are built using the provider architecture. There are two types of +providers:
    • IQProvider -- parses IQ requests into Java objects. -
    • PacketExtension -- parses XML sub-documents attached to +
    • Extension Provider -- parses XML sub-documents attached to packets into PacketExtension instances.
    -

    IQProvider

    - -By default, Smack only knows how to process IQ packets with sub-packets that -are in a few namespaces such as:
      +By default, Smack only knows how to process a few standard packets and sub-packets +that are in a few namespaces such as:
      • jabber:iq:auth
      • jabber:iq:roster
      • jabber:iq:register
      -Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing -mechanism is provided. IQ providers are registered programatically or by creating a -smack.providers file in the META-INF directory of your JAR file. The file is an XML -document that contains one or more iqProvider entries, as in the following example: +There are many more IQ types and extensions that are part of XMPP standards, and of +course an endless number that can be added as custom extensions. To support this, an +extensible parsing mechanism is provided via Smack and user build providers. +

      +Whenever a packet extension is found in a packet, parsing will +be passed to the correct provider. Each provider can either implement the +PacketExtensionProvider interface or be a standard Java Bean. In the +former case, each extension provider is responsible for parsing the raw +XML stream, via the XML Pull Parser, to contruct an object. In the latter case, bean introspection +is used to try to automatically set the properties of the class using +the values in the packet extension sub-element. +

      +When no extension provider is registered for an element name and +namespace combination, Smack will store all top-level elements of the +sub-packet in the DefaultPacketExtension object and then attach it to the packet. + +

      +Management of these providers is accomplished via the ProviderManager +class. There are multiple ways to add providers to the manager.

        +
      • Call addXXProvider methods - You can call the appropriate add methods directly. +
        +    ProviderManager.getInstance().addIQProvider("element", "namespace", new MyIQProvider());
        +    ProviderManager.getInstance().addExtensionProvider("element", "namespace", new MyExtProvider());
        +
        +
      • Add a loader - You can add a ProviderLoader which will inject a means of loading multiple +providers (both types) into the manager. This is the mechanism used by Smack to load from the +Smack specific file format (via ProviderFileLoader). Implementers can provide the means to load +providers from any source they wish, or simply reuse the ProviderFileLoader to load from +their own provider files. +
        +    ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:com/myco/provider/myco_custom.providers", null)));
        +
        +
      • VM Argument - You can add a provider file via the VM argument smack.provider.file. +This will load the file at the specified URL during startup when Smack initializes. +This also assumes the default configuration, since it requires that the VmArgInitializer was +part of the startup configuration.
        - <?xml version="1.0"?>
        - <smackProviders>
        -     <iqProvider>
        -         <elementName>query</elementName>
        -         <namespace>jabber:iq:time</namespace>
        -         <className>org.jivesoftware.smack.packet.Time</className>
        -     </iqProvider>
        - </smackProviders>
        + -Dsmack.provider.file=classpath:com/myco/provider/myco_custom.providers + or + -Dsmack.provider.file=file:///c:/myco/provider/myco_custom.providers + +
      -Each IQ provider is associated with an element name and a namespace. In the -example above, the element name is query and the namespace is -abber:iq:time. If multiple provider entries attempt to register to -handle the same namespace, the first entry loaded from the classpath will -take precedence.

      +

      IQ Providers

      The IQ provider class can either implement the IQProvider interface, or extend the IQ class. In the former case, each IQProvider is @@ -61,54 +85,191 @@ the latter case, bean introspection is used to try to automatically set properties of the IQ instance using the values found in the IQ packet XML. For example, an XMPP time packet resembles the following: +

      +Introspection +

      +Time Packet

      -<iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
      -    <query xmlns='jabber:iq:time'>
      -        <utc>20020910T17:58:35</utc>
      -        <tz>MDT</tz>
      -        <display>Tue Sep 10 12:58:35 2002</display>
      -    </query>
      -</iq>
      + <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'> + <query xmlns='jabber:iq:time'> + <utc>20020910T17:58:35</utc> + <tz>MDT</tz> + <display>Tue Sep 10 12:58:35 2002</display> + </query> + </iq> + -In order for this packet to be automatically mapped to the Time object listed in the -providers file above, it must have the methods setUtc(String), setTz(String), and -setDisplay(String). The introspection service will automatically try to convert the String +

      +Time IQ Class +

      +    class Time extends IQ {
      +        private Date utc;
      +        private TimeZone timeZone;
      +        private String display;
      +
      +        @Override
      +        public String getChildElementXML() {
      +            return null;
      +        }
      +
      +        public void setUtc(String utcString) {
      +            try {
      +                utc = StringUtils.parseDate(utcString);
      +            } catch (ParseException e) {
      +            }
      +        }
      +
      +        public void setTimeZone(String zone) {
      +            timeZone = TimeZone.getTimeZone(zone);
      +        }
      +
      +        public void setDisplay(String timeDisplay) {
      +            display = timeDisplay;
      +        }
      +    }
      +
      + +The introspection service will automatically try to convert the String value from the XML into a boolean, int, long, float, double, or Class depending on the -type the IQ instance expects.

      +type the IQ instance expects. -

      PacketExtensionProvider

      +

      +IQProvider Implementation +

      +Disco Items Packet +

      +    <iq type='result' from='shakespeare.lit' to='romeo@montague.net/orchard' id='items1'>
      +       <query xmlns='http://jabber.org/protocol/disco#items'>
      +           <item jid='people.shakespeare.lit' name='Directory of Characters'/>
      +           <item jid='plays.shakespeare.lit' name='Play-Specific Chatrooms'/>
      +           <item jid='mim.shakespeare.lit' name='Gateway to Marlowe IM'/>
      +           <item jid='words.shakespeare.lit' name='Shakespearean Lexicon'/>
      +           <item jid='globe.shakespeare.lit' name='Calendar of Performances'/>
      +           <item jid='headlines.shakespeare.lit' name='Latest Shakespearean News'/>
      +           <item jid='catalog.shakespeare.lit' name='Buy Shakespeare Stuff!'/>
      +           <item jid='en2fr.shakespeare.lit' name='French Translation Service'/>
      +       </query>
      +    </iq>
      +
      -Packet extension providers provide a pluggable system for -packet extensions, which are child elements in a custom namespace -of IQ, message and presence packets. -Each extension provider is registered with an element name and namespace -in the smack.providers file as in the following example: +

      +Disco Items IQProvider +

      +    public class DiscoverItemsProvider implements IQProvider {
      +
      +        public IQ parseIQ(XmlPullParser parser) throws Exception {
      +            DiscoverItems discoverItems = new DiscoverItems();
      +            boolean done = false;
      +            DiscoverItems.Item item;
      +            String jid = "";
      +            String name = "";
      +            String action = "";
      +            String node = "";
      +            discoverItems.setNode(parser.getAttributeValue("", "node"));
      +            while (!done) {
      +                int eventType = parser.next();
      +	
      +                if (eventType == XmlPullParser.START_TAG && "item".equals(parser.getName())) {
      +                    // Initialize the variables from the parsed XML
      +                    jid = parser.getAttributeValue("", "jid");
      +                    name = parser.getAttributeValue("", "name");
      +                    node = parser.getAttributeValue("", "node");
      +                    action = parser.getAttributeValue("", "action");
      +                }
      +                else if (eventType == XmlPullParser.END_TAG && "item".equals(parser.getName())) {
      +                    // Create a new Item and add it to DiscoverItems.
      +                    item = new DiscoverItems.Item(jid);
      +                    item.setName(name);
      +                    item.setNode(node);
      +                    item.setAction(action);
      +                    discoverItems.addItem(item);
      +                }
      +                else if (eventType == XmlPullParser.END_TAG && "query".equals(parser.getName())) {
      +                    done = true;
      +                }
      +            }
      +            return discoverItems;
      +        }
      +    }
      +
      + +

      Extension Providers

      + +Packet extension providers are responsible for parsing packet extensions, which are +child elements in a custom namespace of IQ, message and presence packets. +

      +Pubsub Subscription Packet

      -<?xml version="1.0"?>
      -<smackProviders>
      +   <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'>
      +       <pubsub xmlns='http://jabber.org/protocol/pubsub'>
      +           <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'>
      +               <subscribe-options>
      +                   <required/>
      +               </subscribe-options>
      +           </subscription>
      +       </pubsub>
      +    </iq>
      +
      + +

      +Subscription PacketExtensionProvider Implementation + +

      +    public class SubscriptionProvider implements PacketExtensionProvider {
      +        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
      +            String jid = parser.getAttributeValue(null, "jid");
      +            String nodeId = parser.getAttributeValue(null, "node");
      +            String subId = parser.getAttributeValue(null, "subid");
      +            String state = parser.getAttributeValue(null, "subscription");
      +            boolean isRequired = false;
      +
      +            int tag = parser.next();
      +
      +            if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options")) {
      +                tag = parser.next();
      +	
      +                if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
      +                    isRequired = true;
      +			
      +                while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
      +            }
      +            while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
      +            return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state)), isRequired);
      +        }
      +    }
      +
      + +

      Provider file format

      + +This is the format for a provider file which can be parsed by the ProviderFileLoader. +
      + <?xml version="1.0"?>
      + <smackProviders>
      +     <iqProvider>
      +         <elementName>query</elementName>
      +         <namespace>jabber:iq:time</namespace>
      +         <className>org.jivesoftware.smack.packet.Time</className>
      +     </iqProvider>
      +     
      +     <iqProvider>
      +         <elementName>query</elementName>
      +         <namespace>http://jabber.org/protocol/disco#items</namespace>
      +         <className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
      +     </iqProvider>
      +
           <extensionProvider>
      -        <elementName>x</elementName>
      -        <namespace>jabber:iq:event</namespace>
      -        <className>org.jivesoftware.smack.packet.MessageEvent</className>
      +        <elementName>subscription</elementName>
      +        <namespace>http://jabber.org/protocol/pubsub</namespace>
      +        <className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
           </extensionProvider>
      -</smackProviders>
      + </smackProviders> -If multiple provider entries attempt to register to handle the same element -name and namespace, the first entry loaded from the classpath will take -precedence.

      +Each provider is associated with an element name and a namespace. If multiple provider entries attempt to register to +handle the same namespace, the last entry added to the ProviderManager will overwrite any other that was loaded +before it. +

      -Whenever a packet extension is found in a packet, parsing will -be passed to the correct provider. Each provider can either implement the -PacketExtensionProvider interface or be a standard Java Bean. In the -former case, each extension provider is responsible for parsing the raw -XML stream to contruct an object. In the latter case, bean introspection -is used to try to automatically set the properties of the class using -the values in the packet extension sub-element.

      - -When an extension provider is not registered for an element name and -namespace combination, Smack will store all top-level elements of the -sub-packet in DefaultPacketExtension object and then attach it to the packet.


      diff --git a/source/org/jivesoftware/smack/LoggingInitializer.java b/source/org/jivesoftware/smack/LoggingInitializer.java new file mode 100644 index 000000000..ddc49d461 --- /dev/null +++ b/source/org/jivesoftware/smack/LoggingInitializer.java @@ -0,0 +1,28 @@ +package org.jivesoftware.smack; + +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import org.jivesoftware.smack.util.FileUtils; + +/** + * Initializes the Java logging system. + * + * @author Robin Collier + * + */ +public class LoggingInitializer implements SmackInitializer { + + private static Logger log = Logger.getLogger(LoggingInitializer.class.getName()); + + @Override + public void initialize() { + try { + LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:META-INF/jul.properties", null)); + } + catch (Exception e) { + log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e); + } + } +} diff --git a/source/org/jivesoftware/smack/SmackConfiguration.java b/source/org/jivesoftware/smack/SmackConfiguration.java index 02f1d2115..77ffb3b84 100644 --- a/source/org/jivesoftware/smack/SmackConfiguration.java +++ b/source/org/jivesoftware/smack/SmackConfiguration.java @@ -20,16 +20,18 @@ package org.jivesoftware.smack; +import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; -import java.util.Enumeration; +import java.util.Collections; import java.util.List; -import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; -import org.jivesoftware.smack.parsing.ParsingExceptionCallback; import org.jivesoftware.smack.parsing.ExceptionThrowingCallback; +import org.jivesoftware.smack.parsing.ParsingExceptionCallback; +import org.jivesoftware.smack.util.FileUtils; import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; @@ -49,16 +51,22 @@ import org.xmlpull.v1.XmlPullParser; * @author Gaston Dombiak */ public final class SmackConfiguration { - private static final String SMACK_VERSION = "3.3.1"; - + private static final String DEFAULT_CONFIG_FILE = "classpath:META-INF/smack-config.xml"; + + private static final Logger log = Logger.getLogger(SmackConfiguration.class.getName()); + + private static InputStream configFileStream; + private static int packetReplyTimeout = 5000; private static int keepAliveInterval = 30000; - private static Vector defaultMechs = new Vector(); + private static List defaultMechs = new ArrayList(); private static boolean localSocks5ProxyEnabled = true; private static int localSocks5ProxyPort = 7777; private static int packetCollectorSize = 5000; + + private static boolean initialized = false; /** * The default parsing exception callback is {@link ExceptionThrowingCallback} which will @@ -81,72 +89,39 @@ public final class SmackConfiguration { * 1) a set of classes will be loaded in order to execute their static init block * 2) retrieve and set the current Smack release */ - static { + + /** + * Sets the location of the config file on the classpath. Only required if changing from the default location of classpath:META-INF/smack-config.xml. + * + *

      + * This method must be called before accessing any other class in Smack. + * + * @param configFileUrl The location of the config file. + * @param loader The classloader to use if the URL has a protocol of classpath and the file is not located on the default classpath. + * This can be set to null to use defaults and is ignored for all other protocols. + * @throws IllegalArgumentException If the config URL is invalid in that it cannot open an {@link InputStream} + */ + public static void setConfigFileUrl(String configFileUrl, ClassLoader loader) { try { - // Get an array of class loaders to try loading the providers files from. - ClassLoader[] classLoaders = getClassLoaders(); - for (ClassLoader classLoader : classLoaders) { - Enumeration configEnum = classLoader.getResources("META-INF/smack-config.xml"); - while (configEnum.hasMoreElements()) { - URL url = configEnum.nextElement(); - InputStream systemStream = null; - try { - systemStream = url.openStream(); - XmlPullParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(systemStream, "UTF-8"); - int eventType = parser.getEventType(); - do { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("className")) { - // Attempt to load the class so that the class can get initialized - parseClassToLoad(parser); - } - else if (parser.getName().equals("packetReplyTimeout")) { - packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); - } - else if (parser.getName().equals("keepAliveInterval")) { - keepAliveInterval = parseIntProperty(parser, keepAliveInterval); - } - else if (parser.getName().equals("mechName")) { - defaultMechs.add(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyEnabled")) { - localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyPort")) { - localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); - } - else if (parser.getName().equals("packetCollectorSize")) { - packetCollectorSize = parseIntProperty(parser, packetCollectorSize); - } - else if (parser.getName().equals("autoEnableEntityCaps")) { - autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); - } - } - eventType = parser.next(); - } - while (eventType != XmlPullParser.END_DOCUMENT); - } - catch (Exception e) { - e.printStackTrace(); - } - finally { - try { - systemStream.close(); - } - catch (Exception e) { - // Ignore. - } - } - } - } - } + configFileStream = FileUtils.getStreamForUrl(configFileUrl, loader); + } catch (Exception e) { - e.printStackTrace(); - } + throw new IllegalArgumentException("Failed to create input stream from specified file URL ["+ configFileUrl + "]", e); + } + initialize(); } - + + /** + * Sets the {@link InputStream} representing the smack configuration file. This can be used to override the default with something that is not on the classpath. + *

      + * This method must be called before accessing any other class in Smack. + * @param configFile + */ + public static void setConfigFileStream(InputStream configFile) { + configFileStream = configFile; + initialize(); + } + /** * Returns the Smack version information, eg "1.3.0". * @@ -163,6 +138,8 @@ public final class SmackConfiguration { * @return the milliseconds to wait for a response from the server */ public static int getPacketReplyTimeout() { + initialize(); + // The timeout value must be greater than 0 otherwise we will answer the default value if (packetReplyTimeout <= 0) { packetReplyTimeout = 5000; @@ -177,6 +154,8 @@ public final class SmackConfiguration { * @param timeout the milliseconds to wait for a response from the server */ public static void setPacketReplyTimeout(int timeout) { + initialize(); + if (timeout <= 0) { throw new IllegalArgumentException(); } @@ -192,6 +171,7 @@ public final class SmackConfiguration { * no keep-alive should be sent. */ public static int getKeepAliveInterval() { + initialize(); return keepAliveInterval; } @@ -204,6 +184,7 @@ public final class SmackConfiguration { * or -1 if no keep-alive should be sent. */ public static void setKeepAliveInterval(int interval) { + initialize(); keepAliveInterval = interval; } @@ -214,6 +195,7 @@ public final class SmackConfiguration { * @return The number of packets to queue before deleting older packets. */ public static int getPacketCollectorSize() { + initialize(); return packetCollectorSize; } @@ -224,6 +206,7 @@ public final class SmackConfiguration { * @param The number of packets to queue before deleting older packets. */ public static void setPacketCollectorSize(int collectorSize) { + initialize(); packetCollectorSize = collectorSize; } @@ -233,6 +216,8 @@ public final class SmackConfiguration { * @param mech the SASL mechanism to be added */ public static void addSaslMech(String mech) { + initialize(); + if(! defaultMechs.contains(mech) ) { defaultMechs.add(mech); } @@ -244,6 +229,8 @@ public final class SmackConfiguration { * @param mechs the Collection of SASL mechanisms to be added */ public static void addSaslMechs(Collection mechs) { + initialize(); + for(String mech : mechs) { addSaslMech(mech); } @@ -255,9 +242,8 @@ public final class SmackConfiguration { * @param mech the SASL mechanism to be removed */ public static void removeSaslMech(String mech) { - if( defaultMechs.contains(mech) ) { - defaultMechs.remove(mech); - } + initialize(); + defaultMechs.remove(mech); } /** @@ -266,9 +252,8 @@ public final class SmackConfiguration { * @param mechs the Collection of SASL mechanisms to be removed */ public static void removeSaslMechs(Collection mechs) { - for(String mech : mechs) { - removeSaslMech(mech); - } + initialize(); + defaultMechs.removeAll(mechs); } /** @@ -279,7 +264,7 @@ public final class SmackConfiguration { * @return the list of SASL mechanisms to be used. */ public static List getSaslMechs() { - return defaultMechs; + return Collections.unmodifiableList(defaultMechs); } /** @@ -288,6 +273,7 @@ public final class SmackConfiguration { * @return if the local Socks5 proxy should be started */ public static boolean isLocalSocks5ProxyEnabled() { + initialize(); return localSocks5ProxyEnabled; } @@ -297,6 +283,7 @@ public final class SmackConfiguration { * @param localSocks5ProxyEnabled if the local Socks5 proxy should be started */ public static void setLocalSocks5ProxyEnabled(boolean localSocks5ProxyEnabled) { + initialize(); SmackConfiguration.localSocks5ProxyEnabled = localSocks5ProxyEnabled; } @@ -306,6 +293,7 @@ public final class SmackConfiguration { * @return the port of the local Socks5 proxy */ public static int getLocalSocks5ProxyPort() { + initialize(); return localSocks5ProxyPort; } @@ -316,6 +304,7 @@ public final class SmackConfiguration { * @param localSocks5ProxyPort the port of the local Socks5 proxy to set */ public static void setLocalSocks5ProxyPort(int localSocks5ProxyPort) { + initialize(); SmackConfiguration.localSocks5ProxyPort = localSocks5ProxyPort; } @@ -324,6 +313,7 @@ public final class SmackConfiguration { * @return */ public static boolean autoEnableEntityCaps() { + initialize(); return autoEnableEntityCaps; } @@ -333,6 +323,7 @@ public final class SmackConfiguration { * @param true if Entity Caps should be auto enabled, false if not */ public static void setAutoEnableEntityCaps(boolean b) { + initialize(); autoEnableEntityCaps = b; } @@ -343,6 +334,7 @@ public final class SmackConfiguration { * @see ParsingExceptionCallback */ public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) { + initialize(); defaultCallback = callback; } @@ -353,6 +345,7 @@ public final class SmackConfiguration { * @see ParsingExceptionCallback */ public static ParsingExceptionCallback getDefaultParsingExceptionCallback() { + initialize(); return defaultCallback; } @@ -360,11 +353,15 @@ public final class SmackConfiguration { String className = parser.nextText(); // Attempt to load the class so that the class can get initialized try { - Class.forName(className); + Class initClass = Class.forName(className); + + if (SmackInitializer.class.isAssignableFrom(initClass)) { + SmackInitializer initializer = (SmackInitializer) initClass.newInstance(); + initializer.initialize(); + } } catch (ClassNotFoundException cnfe) { - System.err.println("Error! A startup class specified in smack-config.xml could " + - "not be loaded: " + className); + log.log(Level.WARNING, "A startup class [" + className + "] specified in smack-config.xml could not be loaded: "); } } @@ -375,27 +372,93 @@ public final class SmackConfiguration { return Integer.parseInt(parser.nextText()); } catch (NumberFormatException nfe) { - nfe.printStackTrace(); + log.log(Level.SEVERE, "Could not parse integer", nfe); return defaultValue; } } - /** - * Returns an array of class loaders to load resources from. - * - * @return an array of ClassLoader instances. + /* + * Order of precedence for config file is VM arg, setConfigXXX methods and embedded default file location. */ - private static ClassLoader[] getClassLoaders() { - ClassLoader[] classLoaders = new ClassLoader[2]; - classLoaders[0] = SmackConfiguration.class.getClassLoader(); - classLoaders[1] = Thread.currentThread().getContextClassLoader(); - // Clean up possible null values. Note that #getClassLoader may return a null value. - List loaders = new ArrayList(); - for (ClassLoader classLoader : classLoaders) { - if (classLoader != null) { - loaders.add(classLoader); + private static void initialize() { + if (initialized) { + return; + } + initialized = true; + + String configFileLocation = System.getProperty("smack.config.file"); + + if (configFileLocation != null) { + try { + configFileStream = FileUtils.getStreamForUrl(configFileLocation, null); + } + catch (Exception e) { + log.log(Level.SEVERE, "Error creating input stream for config file [" + configFileLocation + "] from VM argument", e); + } + } + + if (configFileStream == null) { + try { + configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null); + } + catch (Exception e) { + log.log(Level.INFO, "Could not create input stream for default config file [" + DEFAULT_CONFIG_FILE + "]", e); + } + } + + if (configFileStream != null) { + readFile(configFileStream); + } + else { + log.log(Level.INFO, "No configuration file found"); + } + } + + private static void readFile(InputStream cfgFileStream) { + XmlPullParser parser = new MXParser(); + try { + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(cfgFileStream, "UTF-8"); + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("className")) { + // Attempt to load the class so that the class can get initialized + parseClassToLoad(parser); + } + else if (parser.getName().equals("packetReplyTimeout")) { + packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); + } + else if (parser.getName().equals("keepAliveInterval")) { + keepAliveInterval = parseIntProperty(parser, keepAliveInterval); + } + else if (parser.getName().equals("mechName")) { + defaultMechs.add(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyEnabled")) { + localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyPort")) { + localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); + } + else if (parser.getName().equals("packetCollectorSize")) { + packetCollectorSize = parseIntProperty(parser, packetCollectorSize); + } + else if (parser.getName().equals("autoEnableEntityCaps")) { + autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); + } + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + } catch (Exception e) { + log.log(Level.SEVERE, "Error occurred while reading config file", e); + } + finally { + try { + cfgFileStream.close(); + } catch (IOException e) { + log.log(Level.INFO, "Error while closing config file input stream", e); } } - return loaders.toArray(new ClassLoader[loaders.size()]); } } diff --git a/source/org/jivesoftware/smack/SmackInitializer.java b/source/org/jivesoftware/smack/SmackInitializer.java new file mode 100644 index 000000000..d22aebc4b --- /dev/null +++ b/source/org/jivesoftware/smack/SmackInitializer.java @@ -0,0 +1,14 @@ +package org.jivesoftware.smack; + +/** + * Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization. + * + *

      + * Any implementation of this class MUST have a default constructor. + * + * @author Robin Collier + * + */ +public interface SmackInitializer { + void initialize(); +} diff --git a/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java b/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java new file mode 100644 index 000000000..487f29463 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java @@ -0,0 +1,26 @@ +package org.jivesoftware.smack.provider; + + +abstract class AbstractProviderInfo { + private String element; + private String ns; + private Object provider; + + AbstractProviderInfo(String elementName, String namespace, Object iqOrExtProvider) { + element = elementName; + ns = namespace; + provider = iqOrExtProvider; + } + + public String getElementName() { + return element; + } + + public String getNamespace() { + return ns; + } + + Object getProvider() { + return provider; + } +} diff --git a/source/org/jivesoftware/smack/provider/CoreInitializer.java b/source/org/jivesoftware/smack/provider/CoreInitializer.java new file mode 100644 index 000000000..b39aa0f82 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/CoreInitializer.java @@ -0,0 +1,15 @@ +package org.jivesoftware.smack.provider; + +import org.jivesoftware.smack.SmackInitializer; + +/** + * Loads the default provider file for the Smack core on initialization. + * + * @author Robin Collier + * + */ +public class CoreInitializer extends UrlProviderFileInitializer implements SmackInitializer { + protected String getFilePath() { + return "classpath:META-INF/core.providers"; + } +} diff --git a/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java b/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java new file mode 100644 index 000000000..4d6333cd9 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java @@ -0,0 +1,33 @@ +package org.jivesoftware.smack.provider; + +/** + * Defines the information required to register a packet extension Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class ExtensionProviderInfo extends AbstractProviderInfo { + + /** + * Defines an extension provider which implements the PacketExtensionProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param extProvider The provider implementation. + */ + public ExtensionProviderInfo(String elementName, String namespace, PacketExtensionProvider extProvider) { + super(elementName, namespace, extProvider); + } + + /** + * Defines an extension provider which is adheres to the JavaBean spec for parsing the extension. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param beanClass The provider bean class. + */ + public ExtensionProviderInfo(String elementName, String namespace, Class beanClass) { + super(elementName, namespace, beanClass); + } +} diff --git a/source/org/jivesoftware/smack/provider/IQProviderInfo.java b/source/org/jivesoftware/smack/provider/IQProviderInfo.java new file mode 100644 index 000000000..c707e61d3 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/IQProviderInfo.java @@ -0,0 +1,35 @@ +package org.jivesoftware.smack.provider; + +import org.jivesoftware.smack.packet.IQ; + +/** + * Defines the information required to register an IQ Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class IQProviderInfo extends AbstractProviderInfo { + + /** + * Defines an IQ provider which implements the IQProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProvider The provider implementation. + */ + public IQProviderInfo(String elementName, String namespace, IQProvider iqProvider) { + super(elementName, namespace, iqProvider); + } + + /** + * Defines an IQ class which can be used as a provider via introspection. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProviderClass The IQ class being parsed. + */ + public IQProviderInfo(String elementName, String namespace, Class iqProviderClass) { + super(elementName, namespace, iqProviderClass); + } +} diff --git a/source/org/jivesoftware/smack/provider/ProviderFileLoader.java b/source/org/jivesoftware/smack/provider/ProviderFileLoader.java new file mode 100644 index 000000000..57c9916fb --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ProviderFileLoader.java @@ -0,0 +1,146 @@ +package org.jivesoftware.smack.provider; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.PacketExtension; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; + +/** + * Loads the {@link IQProvider} and {@link PacketExtensionProvider} information from a standard provider file in preparation + * for loading into the {@link ProviderManager}. + * + * @author Robin Collier + * + */ +public class ProviderFileLoader implements ProviderLoader { + private final static Logger log = Logger.getLogger(ProviderFileLoader.class.getName()); + + private Collection iqProviders; + private Collection extProviders; + private InputStream providerStream; + + public ProviderFileLoader(InputStream providerFileInputStream) { + setInputStream(providerFileInputStream); + } + + public ProviderFileLoader() { + } + + @Override + public Collection getIQProviderInfo() { + initialize(); + return iqProviders; + } + + @Override + public Collection getExtensionProviderInfo() { + initialize(); + return extProviders; + } + + @SuppressWarnings("unchecked") + private synchronized void initialize() { + // Check to see if already initialized + if (iqProviders != null) { + return; + } + + if (providerStream == null) { + throw new IllegalArgumentException("No input stream set for loader"); + } + iqProviders = new ArrayList(); + extProviders = new ArrayList(); + + // Load processing providers. + try { + XmlPullParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(providerStream, "UTF-8"); + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + String typeName = parser.getName(); + + try { + if (!"smackProviders".equals(typeName)) { + parser.next(); + parser.next(); + String elementName = parser.nextText(); + parser.next(); + parser.next(); + String namespace = parser.nextText(); + parser.next(); + parser.next(); + String className = parser.nextText(); + + try { + // Attempt to load the provider class and then create + // a new instance if it's an IQProvider. Otherwise, if it's + // an IQ class, add the class object itself, then we'll use + // reflection later to create instances of the class. + if ("iqProvider".equals(typeName)) { + // Add the provider to the map. + Class provider = Class.forName(className); + + if (IQProvider.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider) provider.newInstance())); + } + else if (IQ.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (Class)provider)); + } + } + else { + // Attempt to load the provider class and then create + // a new instance if it's an ExtensionProvider. Otherwise, if it's + // a PacketExtension, add the class object itself and + // then we'll use reflection later to create instances + // of the class. + Class provider = Class.forName(className); + if (PacketExtensionProvider.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider) provider.newInstance())); + } + else if (PacketExtension.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider)); + } + } + } + catch (ClassNotFoundException cnfe) { + log.log(Level.SEVERE, "Could not find provider class", cnfe); + } + } + } + catch (IllegalArgumentException illExc) { + log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc); + } + } + eventType = parser.next(); + } + while (eventType != XmlPullParser.END_DOCUMENT); + } + catch (Exception e){ + log.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e); + } + finally { + try { + providerStream.close(); + } + catch (Exception e) { + // Ignore. + } + } + } + + public void setInputStream(InputStream providerFileInput) { + if (providerFileInput == null) { + throw new IllegalArgumentException("InputStream cannot be null"); + } + providerStream = providerFileInput; + initialize(); + } +} diff --git a/source/org/jivesoftware/smack/provider/ProviderLoader.java b/source/org/jivesoftware/smack/provider/ProviderLoader.java new file mode 100644 index 000000000..fd813b74b --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ProviderLoader.java @@ -0,0 +1,23 @@ +package org.jivesoftware.smack.provider; + +import java.util.Collection; + +/** + * Used to load providers into the {@link ProviderManager}. + * + * @author Robin Collier + */ +public interface ProviderLoader { + + /** + * Provides the IQ provider info for the creation of IQ providers to be added to the ProviderManager. + * @return The IQ provider info to load. + */ + Collection getIQProviderInfo(); + + /** + * Provides the extension providers for the creation of extension providers to be added to the ProviderManager. + * @return The extension provider info to load. + */ + Collection getExtensionProviderInfo(); +} diff --git a/source/org/jivesoftware/smack/provider/ProviderManager.java b/source/org/jivesoftware/smack/provider/ProviderManager.java index fda6b1b81..fb1ba19b6 100644 --- a/source/org/jivesoftware/smack/provider/ProviderManager.java +++ b/source/org/jivesoftware/smack/provider/ProviderManager.java @@ -20,16 +20,13 @@ package org.jivesoftware.smack.provider; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.PacketExtension; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; - -import java.io.InputStream; -import java.net.URL; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jivesoftware.smack.packet.IQ; + /** * Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of * providers exist:

        @@ -102,20 +99,14 @@ import java.util.concurrent.ConcurrentHashMap; * can either implement the PacketExtensionProvider interface or be a standard Java Bean. In * the former case, each extension provider is responsible for parsing the raw XML stream to * contruct an object. In the latter case, bean introspection is used to try to automatically - * set the properties of the class using the values in the packet extension sub-element. When an + * set the properties of th class using the values in the packet extension sub-element. When an * extension provider is not registered for an element name and namespace combination, Smack will * store all top-level elements of the sub-packet in DefaultPacketExtension object and then * attach it to the packet.

        * - * It is possible to provide a custom provider manager instead of the default implementation - * provided by Smack. If you want to provide your own provider manager then you need to do it - * before creating any {@link org.jivesoftware.smack.Connection} by sending the static - * {@link #setInstance(ProviderManager)} message. Trying to change the provider manager after - * an Connection was created will result in an {@link IllegalStateException} error. - * * @author Matt Tucker */ -public class ProviderManager { +public final class ProviderManager { private static ProviderManager instance; @@ -123,9 +114,7 @@ public class ProviderManager { private Map iqProviders = new ConcurrentHashMap(); /** - * Returns the only ProviderManager valid instance. Use {@link #setInstance(ProviderManager)} - * to configure your own provider manager. If non was provided then an instance of this - * class will be used. + * Returns the ProviderManager instance. * * @return the only ProviderManager valid instance. */ @@ -136,130 +125,28 @@ public class ProviderManager { return instance; } - /** - * Sets the only ProviderManager valid instance to be used by all Connections. If you - * want to provide your own provider manager then you need to do it before creating - * any Connection. Otherwise an IllegalStateException will be thrown. - * - * @param providerManager the only ProviderManager valid instance to be used. - * @throws IllegalStateException if a provider manager was already configued. - */ - public static synchronized void setInstance(ProviderManager providerManager) { - if (instance != null) { - throw new IllegalStateException("ProviderManager singleton already set"); - } - instance = providerManager; + private ProviderManager() { + super(); } - protected void initialize() { - // Load IQ processing providers. - try { - // Get an array of class loaders to try loading the providers files from. - ClassLoader[] classLoaders = getClassLoaders(); - for (ClassLoader classLoader : classLoaders) { - Enumeration providerEnum = classLoader.getResources( - "META-INF/smack.providers"); - while (providerEnum.hasMoreElements()) { - URL url = providerEnum.nextElement(); - InputStream providerStream = null; - try { - providerStream = url.openStream(); - XmlPullParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(providerStream, "UTF-8"); - int eventType = parser.getEventType(); - do { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("iqProvider")) { - parser.next(); - parser.next(); - String elementName = parser.nextText(); - parser.next(); - parser.next(); - String namespace = parser.nextText(); - parser.next(); - parser.next(); - String className = parser.nextText(); - // Only add the provider for the namespace if one isn't - // already registered. - String key = getProviderKey(elementName, namespace); - if (!iqProviders.containsKey(key)) { - // Attempt to load the provider class and then create - // a new instance if it's an IQProvider. Otherwise, if it's - // an IQ class, add the class object itself, then we'll use - // reflection later to create instances of the class. - try { - // Add the provider to the map. - Class provider = Class.forName(className); - if (IQProvider.class.isAssignableFrom(provider)) { - iqProviders.put(key, provider.newInstance()); - } - else if (IQ.class.isAssignableFrom(provider)) { - iqProviders.put(key, provider); - } - } - catch (ClassNotFoundException cnfe) { - cnfe.printStackTrace(); - } - } - } - else if (parser.getName().equals("extensionProvider")) { - parser.next(); - parser.next(); - String elementName = parser.nextText(); - parser.next(); - parser.next(); - String namespace = parser.nextText(); - parser.next(); - parser.next(); - String className = parser.nextText(); - // Only add the provider for the namespace if one isn't - // already registered. - String key = getProviderKey(elementName, namespace); - if (!extensionProviders.containsKey(key)) { - // Attempt to load the provider class and then create - // a new instance if it's a Provider. Otherwise, if it's - // a PacketExtension, add the class object itself and - // then we'll use reflection later to create instances - // of the class. - try { - // Add the provider to the map. - Class provider = Class.forName(className); - if (PacketExtensionProvider.class.isAssignableFrom( - provider)) { - extensionProviders.put(key, provider.newInstance()); - } - else if (PacketExtension.class.isAssignableFrom( - provider)) { - extensionProviders.put(key, provider); - } - } - catch (ClassNotFoundException cnfe) { - cnfe.printStackTrace(); - } - } - } - } - eventType = parser.next(); - } - while (eventType != XmlPullParser.END_DOCUMENT); - } - finally { - try { - providerStream.close(); - } - catch (Exception e) { - // Ignore. - } - } - } + public void addLoader(ProviderLoader loader) { + if (loader == null) { + throw new IllegalArgumentException("loader cannot be null"); + } + + if (loader.getIQProviderInfo() != null) { + for (IQProviderInfo info : loader.getIQProviderInfo()) { + iqProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider()); } } - catch (Exception e) { - e.printStackTrace(); + + if (loader.getExtensionProviderInfo() != null) { + for (ExtensionProviderInfo info : loader.getExtensionProviderInfo()) { + extensionProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider()); + } } } - + /** * Returns the IQ provider registered to the specified XML element name and namespace. * For example, if a provider was registered to the element name "query" and the @@ -411,28 +298,4 @@ public class ProviderManager { buf.append("<").append(elementName).append("/><").append(namespace).append("/>"); return buf.toString(); } - - /** - * Returns an array of class loaders to load resources from. - * - * @return an array of ClassLoader instances. - */ - private ClassLoader[] getClassLoaders() { - ClassLoader[] classLoaders = new ClassLoader[2]; - classLoaders[0] = ProviderManager.class.getClassLoader(); - classLoaders[1] = Thread.currentThread().getContextClassLoader(); - // Clean up possible null values. Note that #getClassLoader may return a null value. - List loaders = new ArrayList(); - for (ClassLoader classLoader : classLoaders) { - if (classLoader != null) { - loaders.add(classLoader); - } - } - return loaders.toArray(new ClassLoader[loaders.size()]); - } - - private ProviderManager() { - super(); - initialize(); - } } \ No newline at end of file diff --git a/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java b/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java new file mode 100644 index 000000000..68cbfe5c6 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java @@ -0,0 +1,54 @@ +package org.jivesoftware.smack.provider; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackInitializer; +import org.jivesoftware.smack.util.FileUtils; + +/** + * Loads the provider file defined by the URL returned by {@link #getFilePath()}. This file will be loaded on Smack initialization. + * + * @author Robin Collier + * + */ +public abstract class UrlProviderFileInitializer implements SmackInitializer { + private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName()); + + @Override + public void initialize() { + String filePath = getFilePath(); + + try { + InputStream is = FileUtils.getStreamForUrl(filePath, getClassLoader()); + + if (is != null) { + log.log(Level.INFO, "Loading providers for file [" + filePath + "]"); + ProviderManager.getInstance().addLoader(new ProviderFileLoader(is)); + } + else { + log.log(Level.WARNING, "No input stream created for " + filePath); + } + } + catch (Exception e) { + log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e); + } + } + + protected abstract String getFilePath(); + + /** + * Returns an array of class loaders to load resources from. + * + * @return an array of ClassLoader instances. + */ + protected ClassLoader getClassLoader() { + return null; + } +} diff --git a/source/org/jivesoftware/smack/provider/VmArgInitializer.java b/source/org/jivesoftware/smack/provider/VmArgInitializer.java new file mode 100644 index 000000000..54e1be9cf --- /dev/null +++ b/source/org/jivesoftware/smack/provider/VmArgInitializer.java @@ -0,0 +1,23 @@ +package org.jivesoftware.smack.provider; + + +/** + * Looks for a provider file location based on the VM argument smack.provider.file. If it is supplied, its value will + * be used as a file location for a providers file and loaded into the {@link ProviderManager} on Smack initialization. + * + * @author Robin Collier + * + */ +public class VmArgInitializer extends UrlProviderFileInitializer { + + protected String getFilePath() { + return System.getProperty("smack.provider.file"); + } + + @Override + public void initialize() { + if (getFilePath() != null) { + super.initialize(); + } + } +} diff --git a/source/org/jivesoftware/smack/util/FileUtils.java b/source/org/jivesoftware/smack/util/FileUtils.java new file mode 100644 index 000000000..e400230e3 --- /dev/null +++ b/source/org/jivesoftware/smack/util/FileUtils.java @@ -0,0 +1,59 @@ +package org.jivesoftware.smack.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class FileUtils { + + private FileUtils() { + } + + public static InputStream getStreamForUrl(String url, ClassLoader loader) throws MalformedURLException, IOException { + URI fileUri = URI.create(url); + + if (fileUri.getScheme() == null) { + throw new MalformedURLException("No protocol found in file URL: " + url); + } + + if (fileUri.getScheme().equals("classpath")) { + // Get an array of class loaders to try loading the providers files from. + ClassLoader[] classLoaders = getClassLoaders(); + for (ClassLoader classLoader : classLoaders) { + InputStream is = classLoader.getResourceAsStream(fileUri.getSchemeSpecificPart()); + + if (is != null) { + return is; + } + } + } + else { + return fileUri.toURL().openStream(); + } + return null; + } + + /** + * Returns default classloaders. + * + * @return an array of ClassLoader instances. + */ + public static ClassLoader[] getClassLoaders() { + ClassLoader[] classLoaders = new ClassLoader[2]; + classLoaders[0] = FileUtils.class.getClassLoader(); + classLoaders[1] = Thread.currentThread().getContextClassLoader(); + // Clean up possible null values. Note that #getClassLoader may return a null value. + List loaders = new ArrayList(); + + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + loaders.add(classLoader); + } + } + return loaders.toArray(new ClassLoader[loaders.size()]); + } + +} diff --git a/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java b/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java new file mode 100644 index 000000000..3a907ddba --- /dev/null +++ b/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java @@ -0,0 +1,16 @@ +package org.jivesoftware.smackx.provider; + +import org.jivesoftware.smack.provider.UrlProviderFileInitializer; + +/** + * Loads the default provider file for the Smack extensions on initialization. + * + * @author Robin Collier + * + */ +public class ExtensionInitializer extends UrlProviderFileInitializer { + @Override + protected String getFilePath() { + return "classpath:META-INF/extension.providers"; + } +} diff --git a/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java b/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java new file mode 100644 index 000000000..473a1f19e --- /dev/null +++ b/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java @@ -0,0 +1,55 @@ +package org.jivesoftware.smackx.provider; + +import java.util.ArrayList; +import java.util.Collection; + +import junit.framework.Assert; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.ExtensionProviderInfo; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.IQProviderInfo; +import org.jivesoftware.smack.provider.ProviderFileLoader; +import org.jivesoftware.smack.provider.ProviderLoader; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.util.FileUtils; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ProviderConfigTest { + + @Test + public void addGenericLoaderProvider() { + ProviderManager.getInstance().addLoader(new ProviderLoader() { + + @Override + public Collection getIQProviderInfo() { + ArrayList l = new ArrayList(1); + l.add(new IQProviderInfo("provider", "test:provider", new TestIQProvider())); + return l; + } + + @Override + public Collection getExtensionProviderInfo() { + return null; + } + }); + + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:provider")); + } + + @Test + public void addClasspathFileLoaderProvider() throws Exception{ + ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:org/jivesoftware/smackx/provider/test.providers", null))); + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:file_provider")); + } + + public static class TestIQProvider implements IQProvider { + + @Override + public IQ parseIQ(XmlPullParser parser) throws Exception { + return null; + } + + } +} diff --git a/test-unit/org/jivesoftware/smackx/provider/test.providers b/test-unit/org/jivesoftware/smackx/provider/test.providers new file mode 100644 index 000000000..90bafc4f7 --- /dev/null +++ b/test-unit/org/jivesoftware/smackx/provider/test.providers @@ -0,0 +1,11 @@ + + + + + + provider + test:file_provider + org.jivesoftware.smackx.provider.ProviderConfigTest$TestIQProvider + + + From c143f0de2596f3c223f04315f81cf647effeca3f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 17 Jan 2014 10:40:10 +0000 Subject: [PATCH 03/22] SMACK-525 fix NPE when initConnection() fails git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13862 b35dd754-fafc-0310-a699-88a17e54d16e --- source/org/jivesoftware/smack/XMPPConnection.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index f442e7386..c775e52e4 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -1043,10 +1043,13 @@ public class XMPPConnection extends Connection { */ synchronized void notifyConnectionError(Exception e) { // Listeners were already notified of the exception, return right here. - if (packetReader.done && packetWriter.done) return; + if ((packetReader == null || packetReader.done) && + (packetWriter == null || packetWriter.done)) return; - packetReader.done = true; - packetWriter.done = true; + if (packetReader != null) + packetReader.done = true; + if (packetWriter != null) + packetWriter.done = true; // Closes the connection temporary. A reconnection is possible shutdown(new Presence(Presence.Type.unavailable)); // Notify connection listeners of the error. From 962bd277f1f7d031a923258d7baec340d8d7376e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 17 Jan 2014 11:24:07 +0000 Subject: [PATCH 04/22] SMACK-524 Use correct block-size definition for IBB transfers Revert IBB related changes introduced by SMACK-349 to use the correct block size for IBB transfers. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13863 b35dd754-fafc-0310-a699-88a17e54d16e --- .../ibb/InBandBytestreamSession.java | 2 +- .../InBandBytestreamSessionMessageTest.java | 41 ++++++++------- .../ibb/InBandBytestreamSessionTest.java | 51 +++++++++---------- 3 files changed, 46 insertions(+), 48 deletions(-) diff --git a/source/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java b/source/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java index a33682c87..115ece714 100644 --- a/source/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java +++ b/source/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java @@ -594,7 +594,7 @@ public class InBandBytestreamSession implements BytestreamSession { * Constructor. */ public IBBOutputStream() { - this.buffer = new byte[(byteStreamRequest.getBlockSize()/4)*3]; + this.buffer = new byte[byteStreamRequest.getBlockSize()]; } /** diff --git a/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java b/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java index 5789bbb25..75450f3ab 100644 --- a/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java +++ b/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java @@ -57,7 +57,6 @@ public class InBandBytestreamSessionMessageTest { String sessionID = "session_id"; int blockSize = 10; - int dataSize = blockSize/4 * 3; // protocol verifier Protocol protocol; @@ -120,7 +119,7 @@ public class InBandBytestreamSessionMessageTest { protocol.addResponse(null, incrementingSequence); protocol.addResponse(null, incrementingSequence); - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; OutputStream outputStream = session.getOutputStream(); outputStream.write(controlData); @@ -145,7 +144,7 @@ public class InBandBytestreamSessionMessageTest { protocol.addResponse(null, incrementingSequence); protocol.addResponse(null, incrementingSequence); - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; OutputStream outputStream = session.getOutputStream(); for (byte b : controlData) { @@ -172,11 +171,11 @@ public class InBandBytestreamSessionMessageTest { protocol.addResponse(null, incrementingSequence); protocol.addResponse(null, incrementingSequence); - byte[] controlData = new byte[(dataSize * 3) - 2]; + byte[] controlData = new byte[(blockSize * 3) - 2]; OutputStream outputStream = session.getOutputStream(); int off = 0; - for (int i = 1; off+i <= controlData.length; i++) { + for (int i = 1; i <= 7; i++) { outputStream.write(controlData, off, i); off += i; } @@ -193,7 +192,7 @@ public class InBandBytestreamSessionMessageTest { */ @Test public void shouldSendThirtyDataPackets() throws Exception { - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); @@ -220,7 +219,7 @@ public class InBandBytestreamSessionMessageTest { */ @Test public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); @@ -291,7 +290,7 @@ public class InBandBytestreamSessionMessageTest { public void shouldReadAllReceivedData1() throws Exception { // create random data Random rand = new Random(); - byte[] controlData = new byte[3 * dataSize]; + byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); // get IBB sessions data packet listener @@ -301,8 +300,8 @@ public class InBandBytestreamSessionMessageTest { PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); // verify data packet and notify listener - for (int i = 0; i < controlData.length / dataSize; i++) { - String base64Data = StringUtils.encodeBase64(controlData, i * dataSize, dataSize, + for (int i = 0; i < controlData.length / blockSize; i++) { + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, false); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Message dataMessage = new Message(); @@ -310,14 +309,14 @@ public class InBandBytestreamSessionMessageTest { listener.processPacket(dataMessage); } - byte[] bytes = new byte[3 * dataSize]; + byte[] bytes = new byte[3 * blockSize]; int read = 0; - read = inputStream.read(bytes, 0, dataSize); - assertEquals(dataSize, read); - read = inputStream.read(bytes, dataSize, dataSize); - assertEquals(dataSize, read); - read = inputStream.read(bytes, dataSize*2, dataSize); - assertEquals(dataSize, read); + read = inputStream.read(bytes, 0, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 10, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 20, blockSize); + assertEquals(blockSize, read); // verify data for (int i = 0; i < bytes.length; i++) { @@ -337,7 +336,7 @@ public class InBandBytestreamSessionMessageTest { public void shouldReadAllReceivedData2() throws Exception { // create random data Random rand = new Random(); - byte[] controlData = new byte[3 * dataSize]; + byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); // get IBB sessions data packet listener @@ -347,8 +346,8 @@ public class InBandBytestreamSessionMessageTest { PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); // verify data packet and notify listener - for (int i = 0; i < controlData.length / dataSize; i++) { - String base64Data = StringUtils.encodeBase64(controlData, i * dataSize, dataSize, + for (int i = 0; i < controlData.length / blockSize; i++) { + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, false); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Message dataMessage = new Message(); @@ -357,7 +356,7 @@ public class InBandBytestreamSessionMessageTest { } // read data - byte[] bytes = new byte[3 * dataSize]; + byte[] bytes = new byte[3 * blockSize]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) inputStream.read(); } diff --git a/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java b/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java index b715af10a..5754b1031 100644 --- a/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java +++ b/test-unit/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java @@ -57,8 +57,7 @@ public class InBandBytestreamSessionTest { String xmppServer = "xmpp-server"; String sessionID = "session_id"; - int blockSize = 20; - int dataSize = blockSize/4 * 3; + int blockSize = 10; // protocol verifier Protocol protocol; @@ -120,7 +119,7 @@ public class InBandBytestreamSessionTest { protocol.addResponse(resultIQ, incrementingSequence); protocol.addResponse(resultIQ, incrementingSequence); - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; OutputStream outputStream = session.getOutputStream(); outputStream.write(controlData); @@ -146,7 +145,7 @@ public class InBandBytestreamSessionTest { protocol.addResponse(resultIQ, incrementingSequence); protocol.addResponse(resultIQ, incrementingSequence); - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; OutputStream outputStream = session.getOutputStream(); for (byte b : controlData) { @@ -174,11 +173,11 @@ public class InBandBytestreamSessionTest { protocol.addResponse(resultIQ, incrementingSequence); protocol.addResponse(resultIQ, incrementingSequence); - byte[] controlData = new byte[(dataSize * 3) - 2]; + byte[] controlData = new byte[(blockSize * 3) - 2]; OutputStream outputStream = session.getOutputStream(); int off = 0; - for (int i = 1; i+off <= controlData.length; i++) { + for (int i = 1; i <= 7; i++) { outputStream.write(controlData, off, i); off += i; } @@ -195,7 +194,7 @@ public class InBandBytestreamSessionTest { */ @Test public void shouldSendThirtyDataPackets() throws Exception { - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); @@ -223,7 +222,7 @@ public class InBandBytestreamSessionTest { */ @Test public void shouldSendNothingOnSuccessiveCallsToFlush() throws Exception { - byte[] controlData = new byte[dataSize * 3]; + byte[] controlData = new byte[blockSize * 3]; InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); @@ -254,7 +253,7 @@ public class InBandBytestreamSessionTest { public void shouldSendDataCorrectly() throws Exception { // create random data Random rand = new Random(); - final byte[] controlData = new byte[256 * dataSize]; + final byte[] controlData = new byte[256 * blockSize]; rand.nextBytes(controlData); // compares the data of each packet with the control data @@ -264,7 +263,7 @@ public class InBandBytestreamSessionTest { byte[] decodedData = request.getDataPacketExtension().getDecodedData(); int seq = (int) request.getDataPacketExtension().getSeq(); for (int i = 0; i < decodedData.length; i++) { - assertEquals(controlData[(seq * dataSize) + i], decodedData[i]); + assertEquals(controlData[(seq * blockSize) + i], decodedData[i]); } } @@ -272,7 +271,7 @@ public class InBandBytestreamSessionTest { // set acknowledgments for the data packets IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); - for (int i = 0; i < controlData.length / dataSize; i++) { + for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ, incrementingSequence, dataVerification); } @@ -480,7 +479,7 @@ public class InBandBytestreamSessionTest { public void shouldReadAllReceivedData1() throws Exception { // create random data Random rand = new Random(); - byte[] controlData = new byte[3 * dataSize]; + byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); @@ -491,24 +490,24 @@ public class InBandBytestreamSessionTest { InputStream inputStream = session.getInputStream(); PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); - // set data packet acknowledgement and notify listener - for (int i = 0; i < controlData.length / dataSize; i++) { + // set data packet acknowledgment and notify listener + for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ); - String base64Data = StringUtils.encodeBase64(controlData, i * dataSize, dataSize, + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, false); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Data data = new Data(dpe); listener.processPacket(data); } - byte[] bytes = new byte[3 * dataSize]; + byte[] bytes = new byte[3 * blockSize]; int read = 0; - read = inputStream.read(bytes, 0, dataSize); - assertEquals(dataSize, read); - read = inputStream.read(bytes, dataSize, dataSize); - assertEquals(dataSize, read); - read = inputStream.read(bytes, dataSize*2, dataSize); - assertEquals(dataSize, read); + read = inputStream.read(bytes, 0, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 10, blockSize); + assertEquals(blockSize, read); + read = inputStream.read(bytes, 20, blockSize); + assertEquals(blockSize, read); // verify data for (int i = 0; i < bytes.length; i++) { @@ -528,7 +527,7 @@ public class InBandBytestreamSessionTest { public void shouldReadAllReceivedData2() throws Exception { // create random data Random rand = new Random(); - byte[] controlData = new byte[3 * dataSize]; + byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); @@ -540,9 +539,9 @@ public class InBandBytestreamSessionTest { PacketListener listener = Whitebox.getInternalState(inputStream, PacketListener.class); // set data packet acknowledgment and notify listener - for (int i = 0; i < controlData.length / dataSize; i++) { + for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ); - String base64Data = StringUtils.encodeBase64(controlData, i * dataSize, dataSize, + String base64Data = StringUtils.encodeBase64(controlData, i * blockSize, blockSize, false); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Data data = new Data(dpe); @@ -550,7 +549,7 @@ public class InBandBytestreamSessionTest { } // read data - byte[] bytes = new byte[3 * dataSize]; + byte[] bytes = new byte[3 * blockSize]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) inputStream.read(); } From 9c9eb3efe2e8c803fa167ca0c103a3b67585f23f Mon Sep 17 00:00:00 2001 From: rcollier Date: Sun, 19 Jan 2014 17:24:41 +0000 Subject: [PATCH 05/22] Added version info to the release build zip/tar file git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13865 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 12 ++-- build/release.xml | 149 ++++++++-------------------------------------- 2 files changed, 32 insertions(+), 129 deletions(-) diff --git a/build/build.xml b/build/build.xml index 7d5d73acd..dabc54564 100644 --- a/build/build.xml +++ b/build/build.xml @@ -26,9 +26,9 @@ - - - + + + @@ -42,7 +42,7 @@ + value="${version.major}.${version.minor}.${version.revision}.${version.extra}"/> @@ -205,7 +205,7 @@ - + @@ -225,7 +225,7 @@ - + diff --git a/build/release.xml b/build/release.xml index 667b71d2f..745e917c7 100644 --- a/build/release.xml +++ b/build/release.xml @@ -32,26 +32,22 @@ - + - - + - - + - - - + + - - - + + @@ -62,78 +58,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -141,57 +79,22 @@ - - - - - - - - - - - - - - - - - - - - - + + + ----------------------------------------------- -Release made, testing Ant targets of release... +Release made ----------------------------------------------- - - - From e49c500ac31c5ced8e7c901f329529d389aa15ec Mon Sep 17 00:00:00 2001 From: rcollier Date: Mon, 20 Jan 2014 02:25:35 +0000 Subject: [PATCH 06/22] SMACK-436 Moved workgroups to its own source location and jar since it is not (and never will be) part of the specification. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13867 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 44 +++- build/eclipse/classpath | 1 + build/release.xml | 1 + build/resources/META-INF/extension.providers | 211 ----------------- .../resources/META-INF/workgroup.providers | 214 ++++++++++++++++++ .../smackx/workgroup/MetaData.java | 0 .../smackx/workgroup/QueueUser.java | 0 .../smackx/workgroup/WorkgroupInvitation.java | 0 .../WorkgroupInvitationListener.java | 0 .../smackx/workgroup/agent/Agent.java | 0 .../smackx/workgroup/agent/AgentRoster.java | 0 .../workgroup/agent/AgentRosterListener.java | 0 .../smackx/workgroup/agent/AgentSession.java | 0 .../workgroup/agent/InvitationRequest.java | 0 .../smackx/workgroup/agent/Offer.java | 0 .../workgroup/agent/OfferConfirmation.java | 0 .../agent/OfferConfirmationListener.java | 0 .../smackx/workgroup/agent/OfferContent.java | 0 .../smackx/workgroup/agent/OfferListener.java | 0 .../workgroup/agent/QueueUsersListener.java | 0 .../smackx/workgroup/agent/RevokedOffer.java | 0 .../workgroup/agent/TranscriptManager.java | 0 .../agent/TranscriptSearchManager.java | 0 .../workgroup/agent/TransferRequest.java | 0 .../smackx/workgroup/agent/UserRequest.java | 0 .../workgroup/agent/WorkgroupQueue.java | 0 .../workgroup/ext/forms/WorkgroupForm.java | 0 .../ext/history/AgentChatHistory.java | 0 .../ext/history/AgentChatSession.java | 0 .../workgroup/ext/history/ChatMetadata.java | 0 .../smackx/workgroup/ext/macros/Macro.java | 0 .../workgroup/ext/macros/MacroGroup.java | 0 .../smackx/workgroup/ext/macros/Macros.java | 0 .../smackx/workgroup/ext/notes/ChatNotes.java | 0 .../smackx/workgroup/packet/AgentInfo.java | 0 .../smackx/workgroup/packet/AgentStatus.java | 0 .../workgroup/packet/AgentStatusRequest.java | 0 .../workgroup/packet/AgentWorkgroups.java | 0 .../workgroup/packet/DepartQueuePacket.java | 0 .../workgroup/packet/MetaDataProvider.java | 0 .../workgroup/packet/MonitorPacket.java | 0 .../workgroup/packet/OccupantsInfo.java | 0 .../packet/OfferRequestProvider.java | 0 .../workgroup/packet/OfferRevokeProvider.java | 0 .../smackx/workgroup/packet/QueueDetails.java | 0 .../workgroup/packet/QueueOverview.java | 0 .../smackx/workgroup/packet/QueueUpdate.java | 0 .../workgroup/packet/RoomInvitation.java | 0 .../smackx/workgroup/packet/RoomTransfer.java | 0 .../smackx/workgroup/packet/SessionID.java | 0 .../smackx/workgroup/packet/Transcript.java | 0 .../workgroup/packet/TranscriptProvider.java | 0 .../workgroup/packet/TranscriptSearch.java | 0 .../smackx/workgroup/packet/Transcripts.java | 0 .../workgroup/packet/TranscriptsProvider.java | 0 .../smackx/workgroup/packet/UserID.java | 0 .../packet/WorkgroupInformation.java | 0 .../workgroup/settings/ChatSetting.java | 0 .../workgroup/settings/ChatSettings.java | 0 .../workgroup/settings/GenericSettings.java | 0 .../workgroup/settings/OfflineSettings.java | 0 .../workgroup/settings/SearchSettings.java | 0 .../workgroup/settings/SoundSettings.java | 0 .../settings/WorkgroupProperties.java | 0 .../smackx/workgroup/user/QueueListener.java | 0 .../smackx/workgroup/user/Workgroup.java | 0 .../util/ListenerEventDispatcher.java | 0 .../smackx/workgroup/util/MetaDataUtils.java | 0 .../smackx/workgroup/util/ModelUtil.java | 0 69 files changed, 256 insertions(+), 215 deletions(-) create mode 100644 workgroup/resources/META-INF/workgroup.providers rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/MetaData.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/QueueUser.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/Agent.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/AgentSession.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/Offer.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/OfferContent.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/OfferListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/UserRequest.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/MonitorPacket.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/SessionID.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/Transcript.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/Transcripts.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/UserID.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/GenericSettings.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/SoundSettings.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/settings/WorkgroupProperties.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/user/QueueListener.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/user/Workgroup.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java (100%) rename {source => workgroup/source}/org/jivesoftware/smackx/workgroup/util/ModelUtil.java (100%) diff --git a/build/build.xml b/build/build.xml index dabc54564..141b27ff2 100644 --- a/build/build.xml +++ b/build/build.xml @@ -132,6 +132,23 @@ + + + + + + + + + + + @@ -221,7 +238,7 @@ - + @@ -237,16 +254,35 @@ - + - + + + + + + + + + + + + + + + + @@ -292,7 +328,7 @@ - + diff --git a/build/eclipse/classpath b/build/eclipse/classpath index 67dd4e3b5..eadefb938 100644 --- a/build/eclipse/classpath +++ b/build/eclipse/classpath @@ -5,6 +5,7 @@ + diff --git a/build/release.xml b/build/release.xml index 745e917c7..bc8466ce6 100644 --- a/build/release.xml +++ b/build/release.xml @@ -53,6 +53,7 @@ + diff --git a/build/resources/META-INF/extension.providers b/build/resources/META-INF/extension.providers index a0e420c20..123b876d8 100644 --- a/build/resources/META-INF/extension.providers +++ b/build/resources/META-INF/extension.providers @@ -265,217 +265,6 @@ http://jabber.org/protocol/commands org.jivesoftware.smackx.provider.AdHocCommandDataProvider$SessionExpiredError - - - - - offer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider - - - - offer-revoke - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider - - - - agent-status-request - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider - - - - transcripts - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider - - - - transcript - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptProvider - - - - workgroups - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider - - - - agent-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider - - - - transcript-search - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider - - - - occupants-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider - - - - chat-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider - - - - chat-notes - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider - - - - chat-sessions - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider - - - - offline-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider - - - - sound-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider - - - - workgroup-properties - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider - - - - - search-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider - - - - workgroup-form - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider - - - - macros - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider - - - - chat-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider - - - - generic-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider - - - - monitor - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider - - - - - queue-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider - - - - workgroup - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider - - - - metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MetaDataProvider - - - - session - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.SessionID$Provider - - - - user - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.UserID$Provider - - - - agent-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider - - - - notify-queue-details - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider - - - - notify-queue - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider - - - - invite - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider - - - - transfer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider - diff --git a/workgroup/resources/META-INF/workgroup.providers b/workgroup/resources/META-INF/workgroup.providers new file mode 100644 index 000000000..cc7085085 --- /dev/null +++ b/workgroup/resources/META-INF/workgroup.providers @@ -0,0 +1,214 @@ + + + + + + offer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider + + + + offer-revoke + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider + + + + agent-status-request + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider + + + + transcripts + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider + + + + transcript + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptProvider + + + + workgroups + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider + + + + agent-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider + + + + transcript-search + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider + + + + occupants-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider + + + + chat-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider + + + + chat-notes + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider + + + + chat-sessions + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider + + + + offline-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider + + + + sound-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider + + + + workgroup-properties + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider + + + + + search-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider + + + + workgroup-form + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider + + + + macros + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider + + + + chat-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider + + + + generic-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider + + + + monitor + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider + + + + + queue-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider + + + + workgroup + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider + + + + metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MetaDataProvider + + + + session + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.SessionID$Provider + + + + user + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.UserID$Provider + + + + agent-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider + + + + notify-queue-details + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider + + + + notify-queue + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider + + + + invite + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider + + + + transfer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider + + + diff --git a/source/org/jivesoftware/smackx/workgroup/MetaData.java b/workgroup/source/org/jivesoftware/smackx/workgroup/MetaData.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/MetaData.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/MetaData.java diff --git a/source/org/jivesoftware/smackx/workgroup/QueueUser.java b/workgroup/source/org/jivesoftware/smackx/workgroup/QueueUser.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/QueueUser.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/QueueUser.java diff --git a/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitation.java diff --git a/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupInvitationListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/Agent.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/Agent.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/Agent.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/Agent.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRosterListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/InvitationRequest.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/Offer.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/Offer.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/Offer.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/Offer.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmation.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferConfirmationListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/OfferContent.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferContent.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/OfferContent.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferContent.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/OfferListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/OfferListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/OfferListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/QueueUsersListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/RevokedOffer.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/TranscriptManager.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/TranscriptSearchManager.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/TransferRequest.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/UserRequest.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/UserRequest.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/UserRequest.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/UserRequest.java diff --git a/source/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/agent/WorkgroupQueue.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/forms/WorkgroupForm.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatHistory.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/AgentChatSession.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/history/ChatMetadata.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/Macro.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/MacroGroup.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/macros/Macros.java diff --git a/source/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java b/workgroup/source/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/ext/notes/ChatNotes.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentInfo.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentStatusRequest.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/AgentWorkgroups.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/DepartQueuePacket.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/MetaDataProvider.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/MonitorPacket.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/MonitorPacket.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/MonitorPacket.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/MonitorPacket.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/OccupantsInfo.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/OfferRequestProvider.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/OfferRevokeProvider.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueOverview.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueUpdate.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/RoomInvitation.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/RoomTransfer.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/SessionID.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/SessionID.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/SessionID.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/SessionID.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/Transcript.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/Transcript.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/Transcript.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/Transcript.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptProvider.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptSearch.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/Transcripts.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/Transcripts.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/Transcripts.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/Transcripts.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/TranscriptsProvider.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/UserID.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/UserID.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/UserID.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/UserID.java diff --git a/source/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/packet/WorkgroupInformation.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/ChatSetting.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/ChatSettings.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/GenericSettings.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/GenericSettings.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/GenericSettings.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/GenericSettings.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/OfflineSettings.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/SearchSettings.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/SoundSettings.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/SoundSettings.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/SoundSettings.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/SoundSettings.java diff --git a/source/org/jivesoftware/smackx/workgroup/settings/WorkgroupProperties.java b/workgroup/source/org/jivesoftware/smackx/workgroup/settings/WorkgroupProperties.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/settings/WorkgroupProperties.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/settings/WorkgroupProperties.java diff --git a/source/org/jivesoftware/smackx/workgroup/user/QueueListener.java b/workgroup/source/org/jivesoftware/smackx/workgroup/user/QueueListener.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/user/QueueListener.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/user/QueueListener.java diff --git a/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java b/workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/user/Workgroup.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java diff --git a/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java b/workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java diff --git a/source/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java b/workgroup/source/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/util/MetaDataUtils.java diff --git a/source/org/jivesoftware/smackx/workgroup/util/ModelUtil.java b/workgroup/source/org/jivesoftware/smackx/workgroup/util/ModelUtil.java similarity index 100% rename from source/org/jivesoftware/smackx/workgroup/util/ModelUtil.java rename to workgroup/source/org/jivesoftware/smackx/workgroup/util/ModelUtil.java From 722ec5a48ad231f7d70d3098b27b83d0ce2bd2f9 Mon Sep 17 00:00:00 2001 From: rcollier Date: Mon, 20 Jan 2014 02:45:12 +0000 Subject: [PATCH 07/22] SMACK-461 Reverting last changes as the original deprecation marker was correct. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13868 b35dd754-fafc-0310-a699-88a17e54d16e --- source/org/jivesoftware/smackx/packet/DiscoverInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/source/org/jivesoftware/smackx/packet/DiscoverInfo.java b/source/org/jivesoftware/smackx/packet/DiscoverInfo.java index 46b408af7..ba873a96d 100644 --- a/source/org/jivesoftware/smackx/packet/DiscoverInfo.java +++ b/source/org/jivesoftware/smackx/packet/DiscoverInfo.java @@ -328,6 +328,7 @@ public class DiscoverInfo extends IQ { * 'type' attribute refer to Jabber::Registrar * * @param type the identity's type. + * @deprecated As per the spec, this field is mandatory and the 3 argument constructor should be used instead. */ public void setType(String type) { this.type = type; From 86df178654934ee9f64359e0f9327f423f050260 Mon Sep 17 00:00:00 2001 From: rcollier Date: Mon, 20 Jan 2014 02:52:44 +0000 Subject: [PATCH 08/22] SMACK-464 Updated javadoc to make method usage more clear. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13869 b35dd754-fafc-0310-a699-88a17e54d16e --- source/org/jivesoftware/smack/Connection.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/org/jivesoftware/smack/Connection.java b/source/org/jivesoftware/smack/Connection.java index d1d9cd41b..925c26ef0 100644 --- a/source/org/jivesoftware/smack/Connection.java +++ b/source/org/jivesoftware/smack/Connection.java @@ -593,10 +593,14 @@ public abstract class Connection { } /** - * Registers a packet listener with this connection. A packet filter determines + * Registers a packet listener with this connection. A packet listener will be invoked only + * when an incoming packet is received. A packet filter determines * which packets will be delivered to the listener. If the same packet listener * is added again with a different filter, only the new filter will be used. * + *

        + * NOTE: If you want get a similar callback for outgoing packets, see {@link #addPacketInterceptor(PacketInterceptor, PacketFilter)}. + * * @param packetListener the packet listener to notify of new received packets. * @param packetFilter the packet filter to use. */ @@ -681,6 +685,9 @@ public abstract class Connection { * invoked every time a packet is about to be sent by this connection. Interceptors * may modify the packet to be sent. A packet filter determines which packets * will be delivered to the interceptor. + * + *

        + * NOTE: For a similar functionality on incoming packets, see {@link #addPacketListener(PacketListener, PacketFilter)}. * * @param packetInterceptor the packet interceptor to notify of packets about to be sent. * @param packetFilter the packet filter to use. From 2789591a5b13bf2dc807f0d19b25247258214c89 Mon Sep 17 00:00:00 2001 From: rcollier Date: Wed, 22 Jan 2014 02:25:32 +0000 Subject: [PATCH 09/22] SMACK-403 Added support for Stanza forwarding. Code submitted by George Lukas...no, not that one. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13871 b35dd754-fafc-0310-a699-88a17e54d16e --- build/resources/META-INF/extension.providers | 7 ++ .../smackx/forward/Forwarded.java | 91 +++++++++++++++++++ .../forward/provider/ForwardedProvider.java | 55 +++++++++++ .../smackx/forward/ForwardedTest.java | 86 ++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 source/org/jivesoftware/smackx/forward/Forwarded.java create mode 100644 source/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java create mode 100644 test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java diff --git a/build/resources/META-INF/extension.providers b/build/resources/META-INF/extension.providers index 123b876d8..a5eb372fb 100644 --- a/build/resources/META-INF/extension.providers +++ b/build/resources/META-INF/extension.providers @@ -435,4 +435,11 @@ org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider + + + forwarded + urn:xmpp:forward:0 + org.jivesoftware.smackx.forward.provider.ForwardedProvider + + diff --git a/source/org/jivesoftware/smackx/forward/Forwarded.java b/source/org/jivesoftware/smackx/forward/Forwarded.java new file mode 100644 index 000000000..eecd819fa --- /dev/null +++ b/source/org/jivesoftware/smackx/forward/Forwarded.java @@ -0,0 +1,91 @@ +/** + * Copyright 2013 Georg Lukas + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.forward; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.packet.DelayInfo; +import org.jivesoftware.smackx.provider.DelayInfoProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * Packet extension for XEP-0297: Stanza Forwarding. + * + * @author Georg Lukas + */ +public class Forwarded implements PacketExtension { + public static final String NAMESPACE = "urn:xmpp:forward:0"; + public static final String ELEMENT_NAME = "forwarded"; + + private DelayInfo delay; + private Packet forwardedPacket; + + /** + * Creates a new Forwarded packet extension. + * + * @param delay an optional {@link DelayInfo} timestamp of the packet. + * @param fwdPacket the packet that is forwarded (required). + */ + public Forwarded(DelayInfo delay, Packet fwdPacket) { + this.delay = delay; + this.forwardedPacket = fwdPacket; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"") + .append(getNamespace()).append("\">"); + + if (delay != null) + buf.append(delay.toXML()); + buf.append(forwardedPacket.toXML()); + + buf.append(""); + return buf.toString(); + } + + /** + * get the packet forwarded by this stanza. + * + * @return the {@link Packet} instance (typically a message) that was forwarded. + */ + public Packet getForwardedPacket() { + return forwardedPacket; + } + + /** + * get the timestamp of the forwarded packet. + * + * @return the {@link DelayInfo} representing the time when the original packet was sent. May be null. + */ + public DelayInfo getDelayInfo() { + return delay; + } +} diff --git a/source/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java b/source/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java new file mode 100644 index 000000000..e2bef78c8 --- /dev/null +++ b/source/org/jivesoftware/smackx/forward/provider/ForwardedProvider.java @@ -0,0 +1,55 @@ +/** + * Copyright 2013 Georg Lukas + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.forward.provider; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.forward.Forwarded; +import org.jivesoftware.smackx.packet.DelayInfo; +import org.xmlpull.v1.XmlPullParser; + +/** + * This class implements the {@link PacketExtensionProvider} to parse + * forwarded messages from a packet. It will return a {@link Forwarded} packet extension. + * + * @author Georg Lukas + */ +public class ForwardedProvider implements PacketExtensionProvider { + public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + DelayInfo di = null; + Packet packet = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("delay")) + di = (DelayInfo)PacketParserUtils.parsePacketExtension(parser.getName(), parser.getNamespace(), parser); + else if (parser.getName().equals("message")) + packet = PacketParserUtils.parseMessage(parser); + else throw new Exception("Unsupported forwarded packet type: " + parser.getName()); + } + else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(Forwarded.ELEMENT_NAME)) + done = true; + } + if (packet == null) + throw new Exception("forwarded extension must contain a packet"); + return new Forwarded(di, packet); + } +} \ No newline at end of file diff --git a/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java new file mode 100644 index 000000000..6ffe05657 --- /dev/null +++ b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java @@ -0,0 +1,86 @@ +/** + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.forward; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringReader; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Properties; +import java.util.TimeZone; + +import org.jivesoftware.smack.TestUtils; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.packet.DelayInfo; +import org.jivesoftware.smackx.packet.DelayInformation; +import org.jivesoftware.smackx.forward.Forwarded; +import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.junit.Test; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.jamesmurty.utils.XMLBuilder; + +public class ForwardedTest { + + private static Properties outputProperties = new Properties(); + static { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @Test + public void forwardedTest() throws Exception { + XmlPullParser parser; + String control; + Forwarded fwd; + + control = XMLBuilder.create("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .e("message") + .a("from", "romeo@montague.com") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "forwarded"); + fwd = (Forwarded) new ForwardedProvider().parseExtension(parser); + + // no delay in packet + assertEquals(null, fwd.getDelayInfo()); + + // check message + assertEquals("romeo@montague.com", fwd.getForwardedPacket().getFrom()); + + // check end of tag + assertEquals(XmlPullParser.END_TAG, parser.getEventType()); + assertEquals("forwarded", parser.getName()); + + } + + @Test(expected=Exception.class) + public void forwardedEmptyTest() throws Exception { + XmlPullParser parser; + String control; + + control = XMLBuilder.create("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "forwarded"); + new ForwardedProvider().parseExtension(parser); + } +} From 1c5b28cf9fd1a047e95337095a45d981fc0a81d9 Mon Sep 17 00:00:00 2001 From: rcollier Date: Wed, 22 Jan 2014 03:17:34 +0000 Subject: [PATCH 10/22] SMACK-530 Moved DNSUtilTest to integration tests git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13872 b35dd754-fafc-0310-a699-88a17e54d16e --- {test-unit => test}/org/jivesoftware/smack/util/DNSUtilTest.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {test-unit => test}/org/jivesoftware/smack/util/DNSUtilTest.java (100%) diff --git a/test-unit/org/jivesoftware/smack/util/DNSUtilTest.java b/test/org/jivesoftware/smack/util/DNSUtilTest.java similarity index 100% rename from test-unit/org/jivesoftware/smack/util/DNSUtilTest.java rename to test/org/jivesoftware/smack/util/DNSUtilTest.java From fb6dbda30ffa1a7684ded766657511aef8202c09 Mon Sep 17 00:00:00 2001 From: rcollier Date: Wed, 22 Jan 2014 04:00:03 +0000 Subject: [PATCH 11/22] SMACK-286 Fixed ant test-unit target to work with configuration changes git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13873 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build/build.xml b/build/build.xml index 141b27ff2..91ba2d2dc 100644 --- a/build/build.xml +++ b/build/build.xml @@ -288,17 +288,20 @@ + + + + @@ -410,7 +413,7 @@ @@ -430,7 +433,7 @@ - + From 3a0b1b6197f2efe5dd720bf971d3a0349ec93bc8 Mon Sep 17 00:00:00 2001 From: rcollier Date: Sat, 25 Jan 2014 22:48:44 +0000 Subject: [PATCH 12/22] Added some versioning to the jar files to match the versioning on the release build. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13876 b35dd754-fafc-0310-a699-88a17e54d16e --- build/ant-contrib-1.0b2.jar | Bin 0 -> 194050 bytes build/ant-contrib.jar | Bin 10378 -> 0 bytes build/build.xml | 49 ++++++++++++++++++------------------ build/release.xml | 40 +++++++++++------------------ 4 files changed, 39 insertions(+), 50 deletions(-) create mode 100644 build/ant-contrib-1.0b2.jar delete mode 100644 build/ant-contrib.jar diff --git a/build/ant-contrib-1.0b2.jar b/build/ant-contrib-1.0b2.jar new file mode 100644 index 0000000000000000000000000000000000000000..ea817cd4348bae81caf5b10956dac89f93355bed GIT binary patch literal 194050 zcmb4qbCf4dlkT)_d)l^b+jjrjwx?}-+P3YUwvB1qwsGg%_ubw5E$-f}KeFnaip)AE zGBTdPQwq``pwQp`aYd&IGyTiM-viX2yR4{+Ag!dF7`?*ZVvygW{)nZWy6kTLd94TZ z?HlU`%8nTcaFs3=3v}ZxPyeH^c0U|-B0gn^-tsrV2Ar?R95#nRRl^+(GjvH z@G!gPpR=cSoi6c4+)=#n60kg1o>y(d9TVIpFB;0Fp;sT5P4Dk7p#{QWYAzE6ll7>@ z$F*=bVWzez(Ttfh4r2x$XIi>FKyGTZuucIK!Kgj|s*i8~qL05d_AfWkKcg+c`QJ45 zpCusvmT)rtzl8py_L2Wx$k5i=*v{73(ZcB8DP8N2ybjH?(PEOIP7V0>vn7-gbrc7Drkce6|8~`|J6w9-@Ii(bIQQ==gij`|5 zZBW9AKRKNGch#N__}$;j39J@>#XFsgPv3}pcwbFOP>3K+wYhD6c;vioX7GQ#UbwOB z5_PLGhGGCZNsRRd$K6FqU8IJDKn)AgXv!Pu!w(iAFAX$@BB0^i&?+N7SnE3JQdrUK zwzDIE*3l@pRIXDZT5e*Z+|V$oxZ;r=#YE9L<1;Bos3{;lbp0tG;lQ2-^;8rR3?&s_ zxU`w-pQ3>iXHr%f*rH8lYoSL4I0+3ov{`gowv0NrCZBN7r&rP4CS<-E2hBdFRS#Q> zWlyva0BLB3s05mub*0ek4ah>I{Uj|K@0*0f04YN>vJmxAKC2^038%3}jdrCm2D8Bt zR7n)s70rTHOOE0=5Wt8%G+l3XqD(;N3qq+ss-b-QiwT_9Ev9P(q&_=sqP&!OEP+)& z$nbAjD)MkXnu7hja^=?1Mn~(6_e0W5syCKmUbo=2?o zZ;3X|6*p(h#fZCnV4x(Ny1>H2M{M8>;&AO%y8T^vx>bJ6nXF9s!t~fSHq~-ram*Z> z^61e|;?8E08P?UI7>&)}_8^H&d^6XCI&cL3@On}s&l>$8-b8s7DaUeyyQblImR_d8 zVuDZb(j^GkcD%#4Vnrhl+XF8BV<(#Z*t?z~C+kbpbT&j()3ACDX(94AKcl8uz5VG~ zVU5Bp%LUEavwoN9@*cCtOUwvVtnZ7u3iqo6TZwEKVg240x@oF`B+EwKa^EOL*0R*4 zF=f+8nLpriEKlOJ>pN_gfbfNGjN-C)nT`pm zse|yuK|~q?RMs$T#X#+a1c&zUZZs_2%+=sGKk(NQ#X@?~bzuoi|7RG&0L7?y?|72b zeel(sEphh?q<#JwT-8TJ%{Q!D1js~F49im_xeHE# zX2&gv$HMHdH@H9&38;6<#<;2GS7I31y#Okj@JRe`&)K`_<^%*QqBx|s`k0ZfDo7dU zpYSD5vu)_S9g$A4!`K}a$L;9YoEWS9%00W@RgUzmkYi{>ltM1rrCQvor+HL?D&&&x zpy|}UcnNe~B2g$zg3>_}o~G60 z5yM<~QWdHQdrGnfc1geQqvooII3Yx{(l>G|`DG+nFUl6FD=p7Ss7cmDR60jn|`MNQT3wN*I0f|ZstPm^;EH6ClJMnjFgUmNPq*=%;1ac9U9W0~@1N)3{M4xn> z`c}H(A~UdMU|hO(;9J4W{aHDvG*tU^!kMa!_Kw6nwpF%??MQ87?Ec+CTg$Tdfl^b1 zvI1}mB1!5h?EdnqyDQ3~X}sTfAyb8AZ4rrzD)X}1#a3qp+2+Tb;>x=rryL;V+`LiNXmQTFJJ?W;3R$1Dy>fEH+g^s@?%w<^r?3 z<}tVuUdTMtq|%zZ)^bi(-fGHf02chT#>wQ_kVcWiOs)-eu$JZqDJMW+h|q1lSuVM` zxh~*cL32`ho@^dad8?MJTm#!_T`hXBJEr-TUBAvpZ8x>M!C@1BT(c1BBAv0qt1ot> zgE7e`25#8t><)X|X(k1TlPp8&T;`DKn>f(rB9+bB)=Tz7lg0{jIv~9SVf8gPMchExG8R!PqNp6U;X}0Wc$2wMfNEeZ4=mAy8 zsvAC+t{8rWS#ksPF@sLiQ1;*}9=HuWfNQe7vsp;vxBregTX3G}DB6Y9^KY<31?G zwJcSoKEHh0(zYx=Rcx*NA$t+Vxr&eVLW_-czqTw(DlMiWy|eqbJR$A*;2Uekq1dM6eIB@zPaoq8>JkOCq^hCwv$0|be4MZ_6(kC)=a`-sv+ya0M+nj;VV&bJf< zK16le{myX7nUF2wKtyS}hI-P)xM0jvK)@QW1?h}5Wwz&TH5B0W#P@mB#(K99!-Gwr z-M6MSZa_ph2YRnkg@1G4vy&5RQsgHKNN)zvxNn+BZlbvgak8kR)|pDhRAvy7>WK)T z*9`X6U%4%eb^|Zz`|*=~V0H(6??Qt(8EqTLXt>|szeD4K=lhCY+Iof79^dhJ!(7qm(F@*S-%!1tc zJYTytw0cpmUBMM)3Ew-GPiMgR84Aj%0YSZvfIY4+=Xh8j^Sdd8J&N#vHD-%_8YUjV z5ZCNsCTx+ys;~`fPtOPS*kWCPk!_E#94DB=j!+GFFg0w2Z7!>zvjwFA@#dJ!RJYMH z#k&0l5>SA0mgSywZfg)+f)X~ZHFrU@Asa^W5a%3u?WzP16ahaVnc#kre3nqSk%V+(9lBgIuS&;}DIXOET8aoTSI62$dNEq6hSpO@!6eY;o%?lugY{_mcODbzY z+q@JD_<7HyahHzBMqGP8`zoi1lN7!>^)ljX7Rr4+C~Q z`-;G&v;Skh`iKCLUAPENy?8oMgTp3e^aWlpbU2yt)iT#A(Q?umd19|0&45*hAo?lkewYaR>I z3Z_ag5E~*smPw5ad(U5s6o@3IWkpG0bHbp>9;sVJ@4v&H$BpQt3i8}(=d(VKEl7S; z2gzEe+T)$EVZ+vZ-id@vGGKRL%>P<^@5?fCYm=knFHmzlQi{ez2j={9`b9n&*cz4@ z{FstOmQIVFYMYM@wZ7rjM~)~vL$G;x3ZWh;e5W&Y4{uG{0qjy^=@IHpUaRgl!Riwe zwS}?Ac{j3JxCg$=M!1#|iFa$f0jpK~5W7@fb&0g*tNI9AGhz2tojrd$N!s-`h1a(D z09luAAb}ifwb~*7XMpR|X|!VGU3Q{8#qWr4Hqdx`_4Fvobkl zg7v+Uq?MwbO1xfD19br#HTWA^co>wDAZ6BhHwj2b!}vrcc-azubJMC3J@x&4>0@I+ zz2cq?Ohnh>+|p7(v-?7`ns(EoRF9phn^SG?^;!JKLD!Yf)6<{fZu7F`suKc;C9<3$ zg-Ab|*&YZ1BV;x*+E6D*)-W^RDU=ZfLmg$D)X@7|YRHYCw?u!a$7fi`c-p%n+)u`5 zOup(}N4OkFW8mr;1B+IresWcBswl~2+#%Ix7U7)PYt3wWwVO7EVg>*--*o??^(zdl zZvVP=c^#Vm!Zj(JKKvf_b9qROMQe)W?;HEB5FGc<_6Qu-brsod_P%Gb?!lQBRd4dB zmF3Evjz!er$@AkUj!JD7>-pNO_!dXhma^sEc)i|8U<4m*+oKnF*E>1551DT1{tfKQ zS^JuhHHJTH$Ou9oUQ`+Bb+`J4nhqyn@WpQE}*6qF2FsD3`D=wGK+j!?#6AO1u=9xA; zfSJz_gv9%${0*$wNWq3$ex~T8Ogl??1<(E@lPRRlV40(aTibU*!ZJ|@BSX)!o8-1- zb80s%nwvwPN!3VcKJY7PZgM7 z1jl#&jZ)zTWNsmTa!s;r*@=|Nq{Xz}5YMbYcbF-&r~uBae7VdR17GQ;HfFF(%N^fv4yftR{nnN_*Jf6}?U^A1>6*k!=f{+iiHjIuuMWPnK}Gb955W7e?oVSFa4Dwj+E;oGV zE@%L(8ZND7T*MSL>pHk@eGW{VmCA@lfBH;3jFDSTgkf%i;oKS2#q}A~B_f%T#q~L1 zFnMiI$`S3%H8hwaKbjQNvkgG|v3LWmw|K+eNn?M{O`r}mTe)}TE<1eYEewCU%^4`hhlLBTX22&)2r~Fl26W^mh%P{TF{h631?00ZM9y8z%Mm;mlX6IW3QjXI#B7P`I)G}yC1JvZxvlcR zW!b*PIa0%4!$Wn4`GdahFbM>nI}rM^6p635$a62VqF$Yh(gnm2 zZ`=@XJ=*GFMz3ww0T9y6H?AMF>KIQ){-g^ngpK9+6$fozgKd%A48G18vq4E~il8^K zBRbRQM3P#HYq7~XAMUzB>*X|ENK23mBqts#C0 zo57!2#S31BQiWPc>tqo~&*}0|RfQHQ%&t@>n3^YL3Sl^PN^@o9vVy;%SW1CDMi*Gk zSMLoO_)zhaet?DV1LZW-73E4!a)4A#l4wEKB8UcNM>OkX65~ zQOMb6hVpBL*+W~I0pc{V-vZ5ecTFia^7N~wVSB2O#b<<*=YyP^*c?oop{LI_kmn+J z@?b$NLS__9#ov5>c?Thb@<{C&mk#c60DJK?wAU1yyGEx<_&?2kX?0Gst1ZgPse|}K z;1Cz=P?#7%o)x z;Yh+&u@!Ih8;$Qbr`WzuDfg8joI|3`GpCS(;z=Nr0?=R^nbKE@i&9pIzts<0_eNc$ zX)R#<6w}hMbTr`-7Sy)@VooQ1Al9^f{?fodiyUmp&r1p+DtDkhO(&L(Qj#H9>=f0+ z>BM1H2O|oPNud(k4AJCf%!91^G}bN`kscApRVTwcN~8$L8rzwjp#vz5uEzLIkf|Rv zmcAWryH6ioaO4%Y`JqHY%|KHSe#zK9Rq$#b7K*tUX-nmHQW`R&lrn=py zz~Tce`rUkJzA@DJDHl*-Tv)J&-$}Q<5p?i<^;~jQalN8@~-uh4yN_ig7Bl|GGoMbA2V+q2T71~2_uFWYtyheTK zE-8cH4dm!5Lv}|eH>5|Ov9g5I6TomVJMP_|dplP0#Mc$O%AdSHZbPftrSt4P@3kWN z;L7gD5y^9d{dnAwRg7*CVSWs!QzT^zi(4jPacF~#q#nd{Xu_nnMcf;lBqDkhscMy9 z2tOJmxk=|p($ZGpR2xV_8B`q8a%eP)u!x{NO_t3UY||S^w#ipK!H^x3Hfp|&NJB}Z zi9wJ-qxE-XWUtlA#2pD&2F3{Y@aG=L{{^xOTQjRGzTpkm#0iqMFx6gKKo*~$-B}Xl z)gFZMv?C^=RkrJ)Bh;BSlO!8-W}4~f1BGEO4$sRFZWrjtwq(d#K9D>HC4qt}L}CvE zs7ieGi1EL!lj3Ba=}(aAhxwF~a{Ro~$Sr7wEv{r(B} z&w-zXKbhM*_@DYc!nbdN|Es|7AH}KvB8%u!*LKEPMB5-wVBH%Xs6$&*QHW4VcrK$r zSOtfGjKPu8I+*21<6!xv(Vjq#HA%s&-b)r7WG01!9}J2OQq;)eql_PpD?#w1R*nC4 zoc<%v>Sh+9+nj$jyCUA2d}KgRw_;AqZHo6P+v(9`gZnP^N6#C%NL*hBINSD=Y2`2< zYt-qoodMkCT%8qVcOVG%<&r}z5Z=@2a%uk@V=P~sD@_Yg^=Q`}4I&@iq^)}??JY$}*675D!NC8)OMK*%DyTWjtDOoIzJ0t@2 zDH2}nsr$sQJ&wE`FYu*ZJ&?7VppYx8XCOkY`h5;Vz|b9xMTH4qSX9e18>Ns|z4*XH z67*!F6TnPJHhf{Q$R(sdgvNxoznD^aB(;TKkr1N`E#f&+7a$OR-V`8Av&6%5GUh;d zK~vE>>T2TwW2pW_^q1~tpsK(-K~NJ%q=$QYu&T#)`n&!R-NlIsg=XSMzMfF#c= zdzmRMbHvHrid$0(g^&bJ9yx_-gc(y=eQc(jb4@eZutU=<7IF+8rlz$5NlaK~!ZsYg zegl_~s0ui5)F_mb@wc}es)p~Hh+0zNMVo&}i96qU@<(OsWOd2_siJ=9GfJ7vH%b{>kMRbj({i`u`DgfV`_+MI2ep2BnRn!#-DYfbC8i*gu9<{P zzryE>@1xYyR@A)ySSU%$OIDysNdMu^I@k!lcRIk6?<8z%TAlR8mXXBFs|VZ@4jZv zgJ@w=@o`}a?EnV=Y_7wdC))g8$lp^JQ9Hdp9kl$2PN(&%xOCcbwO!ku-I(>jw&5}0 z!!pypLPncpd*3mFa22r*uq{b8OKWgecbKW?s)n+`AmU)g#!N+jVa#!LCp6QXCfR_} zm?ldJWifrgIFg>zxui+e=p|olz_D8S$L&~DLNEhqZ!qMoTS0)4Z&9c4w4_S~8td#K zCs=Ra?Ju-pOsw=cX76=%>jvwfpb(8yX2BhMqRkBI{^@{R%#8{}5FITjLnwgfAhCQSO_64QY^1Rv?-EBH5DA}^+Uej{ z$yVJ}ibUR~t}#zukVGo3uTV^8=^V|^hrVv7c4o*)*M#>+O?7tk?2elDYN0y-R>bRc=@aslxpeLu>GDQj(i6nMs7pzm8CTOZl`HeKII-8UVx*=?a;wll-EX83nU(L6d1KEDJt1C z*i%?)UfLLNrgXFKoudBvgN7bvu$w11+Y{5W@~mX$XGBeTy&jZi#q_rGeXIsEcDNfH z0t@9Kr*lteGj`Ga6D-TD0FBB06wO13^m(t>a$;ClEFQD{+tII!iomERb)i`XY(mp) zaViNobEpfVr5LjOc?tDe+vcF+=M1-bX^8k@;nsK^>mp?M8N>Zi z@pxc~1P(a>kC^y1%SLF+3B{)X!ZwH}BEcNvdsfUI!9C)qD8e59JtXUVK~g?SQVID9 z1Fxv~36^I-c2VLzF0Xva39F~SUI}Hp0R7z7G3jfXUa|biq9x}R0ty5}K#egDyBa>X zJx;ejCSTf)kH*pL@H%44p7spp8I)}f-g*%Ejsdwc@qb>Oo+{`T3!rl+tpRso(tL1no&U<6V}&xX_&Q5*!o(Q2UH zSOZHl>BZA;$!&9EXZO3IJZDEuSGW`UUs;*=M^gFb?$FVcKj(!I>F(*;p7w9>Mf&cv$?6d@4S@*n=-t9{Yue(lvz2EVMy*PZ$Mo zWEyE?F<+2k^Wp=It(28Gfa`49)5gw=Od0N1HnT<&o^C|RT3gkSG@8IK+?ABP-&KCVM?^<7u9`}b|W;IRB zZ#p^9NJk}7w#ri%2417W81d`c7-b@SQN;lkic6(2T&6@sPo4p;-@Z$Bbs8#RQteVjH_=ZiG z-FteHuOZxKucKh-ijG4v>F9eGWZdO4c#@&xafQO`g_^o+r6^!ewvHewoh5q+&jo&8 zl7nwtw%Z_JUD`d+EsgH}uL`|+lV>@xf(kw^jZ&uD;q_z0rpRzzJ732~ zX`SlK_q%pD*BmAV``1cI?>1SJRnJiACL~&5Q7!LKWwl?u#e*V|_>i9ZT-)c1uY|AbXWxLpU!zE; zm8j0s3@7K-bl<0m*wI$hP`Z^zjb7OXl21$Z63Enq1-i+bvb8I5-hRAj zdnAH7C))q!d5i9S6Ae9;fk{h2zr~u?7z;w96Wk}n-A9h1CW=EnZ?ET#bO@PPY-7Q=gj#!{rc z2w*A3NtyjlloCTP9Iwe!MMF%OuNm~H>F1Hc1Q%Toa$~R*tjiJ6bzhdHhbI6Y>JZfv z7F(c=)oZ-4FmBBeY8V5JKa7$`>LU{6U@f&JyTl4kyL=%^{DBta1E@k2G)2bXQyekc zN3X1QSxTv!LbNgmsr3JD3OyyhhE_9-O}O*|SL-R%zqs;1k1JJ^53mGl z2Yqm2aZ9ch9$LyGJ#R-6^NP0R2lAH$f0YFHfOa!V@BJVPd{cq*F-z`7DafPNUy0%q zTosYXDJ_y;nAa!y;5nh4(KDmH99u|7NIj&?`62E04emMt^=fN_P6)_`Ki+$j! z(_8*_rjnLiBWBFHVB95Bsm}JxlVTt}>e0cCcS+T#s~c!3d}g?&j?XxtD6`NtbBoV% zqkqDjv{6so>kRMUW%F#gE~2??$%nf2E~lb3vdwy0jkSibDdI!<1o=|0m-h&;pE`7j z++7<1qI`0Rw$AMcQlb{W-y3*_+OF-)Z051U;ThxWsR>Z zF3r(?cqMtB+d1^ZujY<_r)3#$^Kg^9Q~&C=yEC+A&u9PnZLAl<9g2X7i9mT1)@UAM zz2ArCo)Y~$@jav1H|mx58C<$s&x6^o6pskE=ARZD6lMd_7ZIe;43D%| zgz*BK(cTyY-u!GE;BS+Q^W^01w|B$w3G?r8`w!-tLJiy#DDWR<#h+}J{J(|UznExr z_KtS;07qvFfRj&>tli#s#E`3ZXz4EE3xJ*1a(lE&nQA%5S|$|Mxgw*x;kxzb2Rw%y z{2-s<9bdmUaJoLcgcxx$@dX|jkJOp=!tLO>>O1NXghIRa02?1q16?W1XQ#wPA{B*B zV}5e~;Rk(v!~b0Jn!zuk6&dsh%RP>E;Jg4KAQ5|{=LGhi28^#I&z40E?^as zaRfg-$M{c>lS^U(=MjzDMek<9Ng4_Y=5iwM9@i8nCy1HD*=t>uF*ivBN-^7z$8}N$ zXT3T`uD76|x)Nif{2d=U;{#ik_q4KWBryW%A6YLt8(1E4-tG0=X zE>GB3pzcH^j2fFHkm!g0n5$BY^oCfX_06W!wm4dX>rJhuf7NLojUYWQ+j2lRp?O^5 zG986CVook8IE+9j)4JUC@t;yoi97zZqF5gD?^ZhG^3)~m;^IPFgodiKpPnYs6v11S z-LU7K;nYcguCtc%K-0OC2!H4FRHsCmwpMGfv0U)#E8xU(<+Qg+7^HtZx(eF2i`qqJ;p~eX0_;O&-PH4fx2NK(zv#e}! zmgVnAl4dff(nVRFEv7QvR;)RoR6uRbvoVcl3GnL@-`C7to5Q*r3dw+*#2UEOoJfU< zukM|jNwk0mGlJ!nF|hHxN}(1!Ui^0{zfM9uX^Yr?CU=U%+?q*VpZy4M`;40PepE2S z%D_h8RNb1Wly5N{NIz))DU*b+M_K`lk)9c)kF1$vylfxGV_ff4@5nhOF5yE|1hRrd zvIIO#ea5#EC;w3l9`5auRoc=$2rc_WAimT|*5Co;537#umx?lC2(g_Vn)(Rr`jeI^ zV^7bhu+!wxm+_5bS%4}E*Orj9n%LJ&;RF0%x9~4M!8s***XFKrtzxSD6fej`c92tqGF-wWfepDNGcHuXWp~Y27#JONWm7SnF{eai?Lx8 z3`j&>X3D){ysEGMbS?fR!#qzAN3eKOO=E(XKabGOeZkxDcHZFS`!ucpqX41dxpzQx z0>KE=O_pXGKj#vo)|@Ez1vo_pwRVDi>-GdFLtUni$7GvWuX_*B4pMKAn5{W<>;(l5R@E zYtU?M4l_ct?L|nh{DuhNO{6N^CnjD9CdzD6sWt=$v8U{^2}31y)4UBcV`h`1j}ZyMiPy8ap;UAd!pTHM51z zf@y7Ydcy;2G7W+%vr#Ij%Y^)bj}=zY__0IvUQ45yIW(~M1>+)y$OXcA=7N^5u*4gQ z1@?ZziH1yodlB~{@kTGkP07zS^16_>O8wX>`#ivPo-@Ec3S|E?tXACuFaHPbFMoe% zaK^y0<&2qUKgYl+f}K4MrDWIcaqb^PPgcl<{7jpCJDKvCH9MZ?odUX~DNukZ$)qP}mCA69mX%h{ZLzdQkth=KRa zSwDq!ID!_X-IG6AHIsqXNo_DB8oW$82?|#}&`istEh}v;}e+@U$mkf|O5O{3SLw5`b+; z*W$;cKO*k+w34yorxLWAEYDKR-rkrQ@?9_5e;JI%k5$@bMCrQV9ju^jRZ3qecpj$L zTQy8n^Ng3-Nz*^cNhS;_74e?UFxmX3;edc^D(A7dm`0zTbrfdZfz!{dUdNbQHDaB} zvYs24-%>m9uh$LrU|EZOoc@B@!73!n!fP7;E^52}JzQY=zyfS{Fo>sv(NCix6R$yMB~jJV)<#G=Mc>VR%anmum8LinLz%UM#GsJP zDOe~ywfL>RJMMF+A(_W*f5%*~fWZ&*ip0Ia(*X>snsz;SW{mKCj?)d1u?@Of>kwwZ zVS8>=6XBPjvx8hFS1yiZb=PsPs%Jcn(0wG5_KC!fm$Mj**@OL(1oqV%pKr)Yv;u+6 z3tBwFig~d*40!?hO#N$^-6{D@qL|W;EY_IMcPTNx-jU2-906=7yrx57sC7ZdKeMgB z{{RIS+u^T8k5Zll4PlTpfu6tD*dC=dis=MnI#Lv)oB4{s4z&mQts$9Is^9OTwIF+M zdhMrf57QFJr9Knt+ikyvZ2&%RpK z|2gC!{vWLT4;33=`|tn4`~`R!Xq}ltf57e?{{K;%`y257UO@O?*7+Ot)a{&=53s(v zEXHK}gXaXKEnp&1S!H1ahZp?cKqw`oGnk+$HWrFTi{Of%Oh;vbHj3p$MHe_{;n*Ce zEF7kysU*>+)?Hm=hA$$`%*@_)F?jKN&aKUkCg^<~%zBPCNeWpOe2?FFuI|3J-q!Ew zzdEkjPHXXd9*l{_un~LN51vzSTdZ39V?Cpu?Mr^yy&+<7P>J`&lEA`2d}stPA`xKF z9P|_VyFroCE(aMDgOA6eo9y4>!D;fV5t3tQ$8_qDv*JGPLB?3NuEl!Ut2D;4ZFf9p zm-=qW_j>uX%6D${VDkS0apXB37Un44F(kmhDOe-rE8l4i8AfdkcQ@hzU_<-+3*U^o zli;xr6ZN~Y0b*PMR_uw6}-3FA-d9HD9HhykJ@x2|M%^La=)h_(^yhtMt4m8MIY`D+f6ZYsEX zrg>AyC>NxWCFHCFuNs7(nphI`+rvBu@2 zuvfZnW*e1NsFCEaV$GBt>5;k~TZ5K)zq^d$p+ZE39*TiYVy#FL<46`NgX4+g{fcW+ z8j)cjp+keKs)8m@k2!K=9N#oNnB0FPh9WjDp>Z0oG=f*1|44>0zBw4WbDiU|PRN!y zpz20Hf@Q{PA|u-+lV-(CXWvg)PpS4OdctKyFQN_W75bL%Sv|MMR4o_pnYqgmK6{`P?nK2L)kzzAM03tv zJHgQ(KZ8A{Q06+e`J=ICnr zjb~6w6l2mT4qy3wjT2lA)bS z!50}lw1>w$Oyj+@e*V;qa-8dZp}A~Owg@J5WwSlR{MB60+&kG_=W6$AbX`;B4NkM#&0GmuH?%48->nI!4J?472e7+DX)4&}SbC5YDTx$*-6FT=`8X!4 zd4Su1HHLP9IbW~p4V-G1Y~yjj_&^VBYc4#!^NEQKTb!d@xNT!73}Ez0;a;0(34NH> zEIa<36xAWUH|1O%pAbtOHD7}6n$7D`VviQ(h;(uXvC=rk(lKD9-?oA1jzqXd3V6oM zsaq91P@F7Jc549)*QJ;xX>oHx`p|gr)4=Z94m>lvvh$~JZM&JKE!h=(teqHw9yvhx z@Ccn1H1dT!F~t1%9{l7G)=lFnJ+BS_6%Z{SH(Zi7VAdMC8B2k1sS4LBPuu@djx~%q z3Q`_j3lhs|TpvEkC(rQ<6SWEwJy6|)N<$kfF^?j89`UO)3E?)DU|??MGnF$!&)Rjg zh%t4HuVWXGt^J@>G(Iq7kN3ts2?QqI5x&W#(ee<)esg18vCv2Wlr1l~XO~d)E;&0u z1#S;b?U0UbHZywVg-Ne?2WI_i9QTz{%RQNGYIe-(e2C@FGZ2kK--F9vX(<95K%>>0 zW?Y6G@j=Cl_c^?KxOF0z?Ur8q;Npy4SM4d$FRH^UJNRw2HXUT2tPY*eER02mmB|yBEYk4|vPmd`(Pc+e2`(-94zv-tc<5%9wCcBWevx7l}2E{~O`3{!^+so8v&|GAo2_r!*KkS;Z-t51> znZ7RY)7lz-;R~tfh>TWKLVAdR zll&=j>*~!bb89bC{nPE9L#K4GN0p{88vmSb5Uo`bo_%}=#j#bi7atAUHi;l7>FaT9 zXNhS&WbME{G=N)mJ-B0B!QTsYy}}6gQrUr@k2Dlw|0vM$xiKR2x;9|CG>xL@BJs|b z(_0NbR)UnTWZRF#OJ&g5U1yL5SLMcoXQww2;12Brcm~c+>Bm6x9t||(ac_p@<2mY0 zRmDv>4@L79m3k`=IO084XGEZU@e%2TxiuE(RRkMG^_CR~Kgqicvw&1-wq9&G^Dnh^ z1zSCLA2`>d*}kmXZT2%qzYeO@kYVo+C!8z93kP4qB3z3|RJmZ>4VUO!k{zv;%&k<8 z6;aQSy=g_mplD+<5<4#frqiF~0%q4TgvMJGTB%5=gD2jsb`!y4t&GFhEg2@h;wtw2Mg;O6iRcyFs{Znbcwdbk626_G}vgfPXr2wjh)=GU^0&uNh`26{E@ zNdv2zs(nzs^GjI}B>Zvn2_P{yoPPy*XvBxGQPaB3YdD_#Z?^-S$N z+v%Qzk6FV?Vk1^b2tKiTJl4TYv&K#Jub7uSf#nD=HO5da%))gSQ8;BH@#r){UY!TJ zzT*3O5lMw}60-(&bU2dxO#$tXkmAj_Sz~dUMARZC8=R`b#39(*pTy~HOA_}QrhVTF zAdgO4CZI`~9i>~a#HOJmDaCdB@v?w@hqv{_qw#s=ItLJig_)4yaBdFTD`~llR(Ih2 zu`mwf3dL%E9e7Cm=D0IJJ)T>)?x+ZX&-HM5Y|rf{GePSlO9Y-jcVs8AUkStKa=Row zfhJ|>xaK0W(#S}T3XO996~m>zc_@{|!eWHAfs?G-uqL&P9iUOUBSkZNEx2H1F3|Ps zcs-!Xkg|I2+6DKwJuo?1=G--r-rO})x1cC&>8gR%r(xtTDhB8?-fgDc#FGvv+^wJyIw!R zwK&+1xofj`(SH3K_LLSL&7s`oiAJ`Ew7i|GXNaB}#p~u@13UFGfD5Csu8<#N4LO8{ z5BRQ}#0J`GEQFdMazZAOY4xitp7QTgOQZcYeu}DWYT4M zW~lEoT#jG(mcN4^Yo%fG3XtPPw3$$uqG|<6rS7xwg)N!Mx4`Z?i<}9yGllsu$NLk# ztf{*aaKeOJT+|nK51)Co3Tv98_*dhiR>R@yjt!JYhzej6MxcMjrEaEA!T}F5@;JDWws#R&qIyv?qZW*5P9bT4J=%F@@ z(sW&05>LE0TZx;I5+90QylHmLO-fq;0vs4EKf$tCUW%iV67JoMj%Ra2h{N+Ja?+x= zHt||qM?w+japzyabwP76MeFD9^y94`rzTP^C(hrd*0(39f;vg;t#{RfrgeKgQobOA zdg@CF=)TfIE`3`+s(n%4Ha;kB2mnZr*z(9F`Z6yjul>jGF z+LD1)ZC9s+428~2Xc~}KPlf49B@s=Ley$|B!>hWerOs!f!S_A0sDcZACW_Su=Yv%p z8Oj!~wrS7~SmJ?8b4eLh^__Za47O=lg)g$fra0FRXeAOUtvSojT@wOV4qyf!uD#D>Oli&^IaalvH1I>4e%cTlPk`04d?Wg z0!7gYZLP4!8cvA4$iid%xFjysc>o>)-_Ub|-U}G4ZckW6EuR94$R!HEchf!jeru&# z^mcv9`MkqU{pz|ap=tGvS z0Ik{afw$?qDk%g*bZgR?@H6B1n#S`7ajI|r0}EN~4?%n}2=Qy-IQ&gT@)riq<76Yf zm#PbqhtEEqXko+TD*&BZI9qM7c)h3&xhW0mT2L4~$L&20-H9-NlsgXcRretfIU^dk z9i+$3&`_FLo92v-q5Ddo%GOvzOO(Gmci#zINO#t>J-YC-O}{<8%MRaqz-!CPw~L-Y zHz@cWO`b?$H_#HhZ`Iq1d>nXv*lswccCkw@w&*~qtLzPNA^ zq{rwJ5@BpOsHgUkXgH__lWR{lO!hi=+a#54if6rFhO)|7pbR0>&HTZox;GUC3-YRh zXsUxMQ3&Mf$k~jtYb3O3=7voIQO+V@T^VB3%KV_RhM-_W=#Rk4cS8Fg7<-W@gj8V=CZiAlzqB6q68kiTI{tG%|?8Ckd8KG7sORp z-VU1AemZhU+b8~itZ4reav}Y{R=0#`?l0Yrn8bu7CF{{?oAd z-}#>`r8PNaK@{CNG`LEF;elOP+R_G*H1zy14CFa8Lr0>1Bs{N-?CA4Ym*O<$$|w2s z75085>GiyT!>{O4bm%a%*Wv`5sD~27bfNm#3FGzjQm($EiIh~k%ja{>Y3f1YH;t&wlWAztqZF4iY1 z{S3?qxCL~KK1@3rM7DgykI}X)3oq;J z@#Zj^S+YHC+gIb3=5QM-pHsbhbRaQbrtgq}#$yY8PT+=jNiavsDldO*NsK4}dtqpZ zXJ4$a0DzUYs`4`?It;?@7^lXW_&;sX2Xxa}`)i|60%I1zKludEvz<1dr!KlhyW zUl{pEF?w<`kyhbmk5-sp3hQ-%WEiA5W1eQXBsP*8lH6HO82`QSJV=4IU-U!7&wg^HFZELWOOUMcc%o zTIG9HV_SXwN(D2GrxXUe$EAa@SEURE_hO_72U?!?Tp*D8236mDh{|lci9~h91*4Hg zD>cR;Pnn1#%W-JU1UHaqc=m&P&5*G9J4q^|Hzs2{7kdi-2x^Nn=1etGC#N(&@1g&h zgRtQNs{Bvh6!?}c^r{)I(2DR^w)vm^Wve%tJN*V4JoqvS_9Sm{sCu72?fSU)0T)u| zHota;8)PV+a$7yR87$inN8N<(WpeykCD_9lY2K2CVz;<%2Bsw{i69=!hG6+6LNaYn z6ptg=XXUEzh6J)TdVn2#-L+ISEyYSEB zpE2diP^W>>qGV;#0tv7qGXv0K3SDL4mcN3xU8xzJ0GZLM0>6>BZ@Q zNwFtIbVGE!qIRbO=Z^|28Zi}WP(!@6pZukf%TGN+KySH{7Sm;3F@$BQ5)$<}FF}g6 zNVTJtNf`XKku_)7|Ac8OU0N&ARWpQPxCJYFM=sTHoW|QPbt{4xLSk zVz&w6(&%!pOHkJL-`|%o(Q+s&*BwK23RX;pFVm`wG{TaSZN&nk8^rSyluEENL<+49 zlB!E4+E0H)erdgn_EsS18l)p#)SSK2CApK6$xRp#S~ISfm}DnvYt3D4XDt{}hKN*k zcO)tnL!-{^8Brwl9ZY4?-BCXl(~;Afle_d-GErc(Z+k$KN|-R+cgTGa`9kIUHSCO^ zI`N{{^Xipgv?d;5#y(h;+BE)TpEB2g%w|1cEPf6HD7tuL_opcf>c2ae!%qy8-6Y*4 zTW8wuWUH4E2*nvte#s{}dZVChcG3FwKI>C9ZS0AA zTk^@N89HV!G#Hni#6dy00lC2C@RaGqWi&{D}6T?5!5E)o^6tH(uRam{qZk4VK(Vi z|BSCLV*G#UB89&RnB4!0)-o`+`Gog`aeT@DWoB3=Ik1Fj09!wD- zDeQOm_=X=>-n{HC2kX$DCDR|Ou!Sf$)BFU!D#vFYzjK#T>=>B2xo@+k&UpEF-^9>p zagt((^bk4QnM}5N`~#QZzGh;$Jg9IB_aXBIm`+r?UPTdB7URAtPwMQ!s(6`^*JNxZ z`^)?+k?OI`>XannbFIhN*=l1%8nNr-(vtB`8ZH@=_^pHj2_?x477 z<~&~hOqpgZu}yT`O(d<3~mNW%Po&|^eI|=Rrak95fgW>3=g>qA#1%PiqzpVBXD(P!MZVmk}`td zPO)c9!{mQjp5P@If!xhKT|U44^U+~+X5W_dRdn93qVxW{lS#?g#Mr^u24MU@s=g-G z_m$l6c}{5^X{tRHuC6BPm`5O57C`V_v`6Z)=|oF?7S<{soi8bX$ZZGYNnzIt3x3{> z*gtDyg#Xj4{`KMGCY*+?)`~c01<4_md8|Dc3_1cW8RJGvC)){w3~T8vxs6XkeH)8-jO%q#b%H9s}jWPZsa zYLI47iJ2YUiDC?3h=$n9kbqyL^bj*W>VpS!w4`bAgq0OGD?Pk|k5vL@!d)8o`j>k5y(rh|ei4p(U!i&& z|8~8dZT=%Y@4qRZvedNQaEvj1$bWv7j-`;6)~ZL!8d_4S4a3t<7B+|_DN#WRyP#4n z%ZhBIA!Ehaq!EJ0kBCSdEUo}TP6;%ecDfRwRMh)V0lSXpc@p~O)7`a_S6AA~%F1~m zt@p#qCBM(*=f~D3?&0O9=O>C*EgHW0u_$RE9u9)N!T=$xbZAE_Zw!mmt`8zV+O=P3PN z1+S4HVpV3#b1k~rfR(v-C@peYalujMO!GPA+@vg}6(^oCC*t66Q&h)2lOAeLm1!Ku zQ74%;0&bSEY)0HM=5SH4Q9Ll*D|NKCekD$ik<@0`RI>3y#$P&po9c17 z)X7vvL^y&dq`^($Z6?wdw?iAxkHZh1+sD!N$gP z0I#7V?}tP>rk*KH`~1NMucZ1^^F6I}>(mBeBD$Tk_&5ooN`WF;bJUuJ-L)=ja~9)k zn4VXK_JzcFKFeD64`r{$O3#=WQcu9(#a+_?84DhphNss=6s(fY%5GO1wo5WbLV_@< z=V*Er$Dxs1QXkYy8~x-PE3Ba#cn*8BO)C!2hFcj2R5F`g#CXw7iP*Jcs4;j}SoQCnkHgL~YuK#k=C&bv4ouWFi{%niJzH6=@GjQEkuPYjR7%a}>%W)+Jteo4Kc14?{RFZ)m+c^$`5h7sSiQIl$QSCpxpND z_KSYBgYLLN3KZ)}m%rxY+DzFya`GY=@}(HciQmMx*Ba8ZQks4|u>_?a6QzWeYt9Cs z{IQ^(DnUEf(xE2@Fw33%P?%#WuG`7y#O~K?KV0WTBn|yFg?`dc!63$~Ch6K0prf=Q z<>I4w3}4<``P=WjJgMGenC{Fb^IBTA0~l5c>`z6ED|iuEDV?%Ud*Q@(Nahq<;@IG^ zetdKHR3K(wN5Ot-AEMMxYrGqxdRDdDX`6kN)FQoBkG3O55hX1}^Q7}ZniYLddoC4w zODc1!80>*?rwtGG(tBd35Y>^?pJOI8T)+nk!g}TvD%j0C-Ma!M&;w33%?cM*GS0kv zZwhBSuC&4kR9YmoKvqk^)^t{B9dC{FR%w6UTmrY~iusrc4x(^SpIRzvbT>Saq-;+i z8$!*_vxL_!63qDhRXTp*JsQ~L>x7^jkokN(5cYv^K_(xJQtdy3+eQ$bX8{mbbf^!KCUx% zIK@sII4+qzBNQV{M6BiO4$DD23<*<~Y<5+qzbKj= zJ`R>JjvFb9b?%MjCvupTyvw2tbDU?X<0X$s6@1qHqzI7~bKEO(*qH=bJ}a_&Dp$<7 zu#v9BK3%J$A1j8TF^R>Z%FGPRBgd>85qL`2Hq&x2BmTvh)2ZXmJV|rO_s2UPyDx*Q zck{+Fa3)BUJsDK#0WVdZ zIQ1kitr63@Kc3A>J@p#Y9Tfp{8p-meYrmSqBhqLHtEzUH1#R6?b9iq?h79Wvgp_Y^ z5&H+f@20o2{X~`7%-M4gSNmmXjQT5J^AA-O>1#Aq75B{uCTAHpnuRNX^FaEzdrTu# z-Hx=&O?^+*;}8++nYDHPbo__y^jgdf>sJ1KG~-|l0DsfOuQM?4?W`SMm8XAdlKgq$ zndq?e8pzdmIIHAI)eXz^3XyLx=L-G}^Rr+7%JvQQvuEoqh3eL*Q}9m7v@a!oll_|Z z6L*S#*7swmgW8W7yV-%aiEEzW;H7s}MHfXT8{6Qr>O9lk7#gxS4qOJN) zXtx}}5QWciA^2lg@=jKR^3FV=KSew=3Ukb#T0-=XmqW^Yyit8^}#Y!+$OIZX;&Se`Xa4Ap7u?6sQq?#4Hb0?g&#vyrNh zI`S+$mYX*;B}7=Q=PI;QaN}CCg1E}VYjOl2D!Wc5Ip@ngKEazky3A1Cdl#8SH3p+D z6~YrQ6$i2z^{mop4%8;rgLhUZO%SgE<+72y%F(EaZ_n{3pr}iPGn$CrW8CKMMd8}j z8Izg%T(eHaeNj>3#75}=+t!YiiNLnm#*Et(RdDc6bzzO_y|bMlb;DA*u8Ldkh2-sc zEonP(Bs!Efqph0vkuT+83m{I(p~3b_Bb2li(zhCZ0SN@;aDBb`bbJk3b}HR+j=P9~ ztC_2sP7{Hc!QEm8tu6|~>{81frP0YQLN~X@Hfi55LC%HK7V}RrE3thF`9wOeDR|Hd zAIE_QG--Qoa>*vG4%4HF<_Msux?pvxh3d^J)nt6FI$((%cgPu4L~0&hQmLxw797$fIhANbL*CxwwfZSejjqtJL|pB0Cw zaIe>lsBW%6wLi4V+e14Y-}aw})LU1b>zcc-2*V(~)QAu~3!Rj1Jcpzb#pbp0Fv(Ii zwF~wxS^$e`fKPMwcw1dqneNwT8kuz)tY8(r1K7Wc2C~%^B9ps+aL+BsvsQ>`i9k2d zI=Q*Vc zG^bI@$??u{WdkNWVxx$bHE3Kvd)<{RMG3-)6LJf2(l7;N@n4UeA{lLJ1VlfHp?!&y zLKkKCwaY{vN|0w-!`7uUUeySctdLtUn+g$5+)E-t)1-_dVP#|Eyu?(hPVhhasm~YJdA{&!l7;UL5@KR z__vmJ7jM&K9l>uU45RPCS8v)H=ujQB6)7sTrci0V1mcU>6c6Z3v{oq94R(^6$_Eux zM^LK}%Y{!$m}nX$7=mJnye#T#-rxq}%q$>x4#>+CaI7cBJD z$YR=e2szz?j*7%H-rVdl^(gO1-tw=$_U~aIVC!Eo#_V4)#{V{rqik$r^jB2iKaWMVQ(Kg;JY(5M#CQJQu#htf!BdyGqJh&5n*KU z5|&nmU4py(JHCcIrY3Fl$rf)lj+dF9cwM*LN9!y6pC9k=pmrFKfa7jA6#5-ppY~sd zL?HxZ0JA>hWSQ#=sLQGwfY!k8?^Q!JknBWm^dXPeK$h!~J>8MvPM~QRyUlq*HM14r zG&xgc3;;9Jq1)zxw#@K$DCywJTuy6kB@HIhii%9-To~%_lH33;sNFJmK##;e40T3o zY_z$tJmq(3&&6>_Ff4kI5Nfd~o(^#D6O*mM24vy8$(It@u|!i8&{of43G3;}*wy4j z^RO91A2E(EF$ngcvdikQEN1k`C8>eJWLi}~XCkRlZq@2XwDw;knml z4U>(bkOpQn*D*9jb2_TSaNol0hH^|rFdT$sJ$YmwD@0-=!wuAG`8d61u;Gbcd;cVT zv8Z5rqo{zYEAQsw#j0JVXX2Y&^0K9_tHzkkd}XQCrH@|Vh6oS4UNDi3aodmm@kqiE zJ6qRLmpaITAB9IELaQ#5#;r(u(Y*AtvyS)ZWJSEpcv)JVaIDh_zmB_j=J zUh+^}Y|$d+hKhv|k>#KI3b5=X%|}?z6!&JZx#>ip%)Lc>-4(K;%c^~E8j5Nxswky~ z$SyOI&|Pws&a#d{)~rqi;(gS|v$+8Jq^?zfs+ro^k{$M!c3GeV9Ab#|>}Z3}iR#!Y z^;%$N+<4SWsY6U=sp!w2=w@9O+_)Lo5nj256Ww)aU7Vk@IC|=twFf$)BMWVBd1tef zAU=7~xgGP4y>>-5+Co$4=FG%SO&S#YGI5GK7O@=Vd7%kZXe{eJ`2icMxZ=)^GVV=K z$5*)qnvL^OPFl@XHpe(BDUK*3PR@MxR@z8Db3|p+($@K}yaIGiz+%8_Ex&$f2X5~2 zoJjmC&M?NOT7J&YFK;f{A@-`+LB6Blxc03I|Fm#%8BB_fOE#{bj2a)+TBE9vo8)}J z{dCwH8|mWiYngqoH!Xn7o1Az-_K1eqY%78pYm{kae^0Y6>!Kg-PYNO4peD;+Jq!s~fLEBH&uveXr96d?ajz}uI7 z^$l)D`4;t<+dD`tSSFg!^o=^*r;M3zary}(IwdM&bkINNH1ZV-t1BG2aN#ryqMg|q zwZ}ZVGeCjYZ%&o3AwFj57@cIq<>fxF3)DEgopq+Q;Bgz7 zdz*$3OMn@CWQ>!35N@j#CD2ud)$x`~j%}Mt3;tfI&nbaJm5EPy6^Cg2TLG#7qdb=w z+n%aGuQ7qa*UEo&5+Y!DFF?l*dLsrPsP!ON8aW(<_B*2n;tXV$?))HHOp~wnnVN17 zm;K5U1e>swnPF~V>UMrMNmdqYq$w~Cf;GeC^)kcM#;+0c;_9>ptF)9h!!vjnre3an zNCm#%wvJTF^GY3}WZfoHdC6%;HBZlgF;AUOy6vB$rLvB`vrPyVtw6I&>V(dmgp@kN zaM_`i$`51ND_oJh8bT;i!?=sjq9f;)wYJ}scKMF}7NYtxEF3wBmQ=U|BtO#_-e^7< zs;^$djHxALnnZIrqA@**R{mH{()Y`Zd<4pcwC`N7gBRxswhiCl7KtnV4{^}i!f&HO z-Zzvxjlpcvp!gYlV*i})=bEEOt?I=Yn^ZsM)IG8?2n|7-#XazmXJ4JFy~H|hI8o1_ z>)wzky+qHiS)ac!y)MF2+pyo4!$ZP1H-4!epcZ+?&-T(f~FmrY? zvURoj=b522W`oR#!kb-~#@2xHvb$0Kr?^aH11v^Vp{N`<%q%EY$+>6JUS@EiKbj}I z0e>I_6)D2!8-8%Ji8zH9p%;d%*{<-9W#}Xx_E?7Oo$J#a;aCv%helO#^H_vh zeI7i7J>YDRO``S*9`MJW7^g8l|Cw9;KSd(yLhTkQeG`$b*%Z_*0bfl&8N+&pW~S zzE00u@&U=~z3G~4&_^oG3vt--ZgY9*zPZ(QssG{mfvl1bh7R~LePbvcqvKPLw)kVr zVxZ*6zzAcYIclO3Gm6ZRgExj~@7l#8Gs6$<^dj@>i|X~2hSXw28}fvR+tEZPJyl`g zE!lOMb(QQW!p4-j@r2wTD_@JiEbYBAzI~Gq--H*vY}W9U>Lc7a2nxXudoYKc`p!OO z;U=fy!54yll^>p>(q6GU1f{ZcbECPXT+=vaKQ~faS!UG59KU{Ectf0geyNbFttsvF z_-bOI(KA{_=r}KP_%6dnSwit;z0C5G6SS^LW<7VRbDjP@{?Rr7Oj_=0au(_PGhj4i zFc6C9T&}BVySysW+WAQQg{EogY|Mdd=Adx=OmJ=@)ZU(H7v2P8XOeF1CSg-xw&6Vy z@M=>m@5DQX`+ME!i(0l?lX}J++Dw`9nu}T<_<`t)Ns^PiFaOnT&Uh21gA7R9G}lky2B+u4a+1k=D{rx65nqd#Yze}9h0Iijl<&# zOJS$bnV&{4fYw!Wyqwi=GU8c33KuYwqnaU>tAu=d=f%RC)w!}L0c<`>1eJBxw z*!pYOok+Ickk@7~TYH{%$$7ma`6$hNB%0Glkql5GO2ymLtG6VhX;>iSKMsY`;e?S# zH_)5M#bl{+E&N&E?>q>44Vv0{0{fGDX`qyZQ50EMj*E7%_FZmMRx=B;XXRcR!9K#S z5WsDt{*^t<-$%{m6Rqy-1-7R0CNyN}ru&80goLv@w+GANHf^ifm~EEL*<&}ByJ`Oi z&h5!jwwo9|mpXk--Cq4;fvBI@4M?tkh)tXZ7614N0l)0&cl6eFWVGa)+R&xQB~3BK zWZ|>BJYKd8+oc0wNf%M!MHf`Eb}Jy<9QTN!V2{UU$zG7-nU{~uM6V`K{)!UU{aGd4 zec?K7SyZG@XXs0oi|RXjZJMb#{cgQ#M?hb1_;j<#NYwt#W^TYl z5fu=a3kzK)kzbKX>wz~ayCdh=g)P3(`0T7#ocrWK;+2(V-|Y_E`+O4R2u(u(!hoZ; zdOH>x1t@TX>p5z&d*{>n;Y<#`oQ9n9)XkcgefbSvk&PBfU&MZ_cjhL{;3w;|#(12>h*Jg7V(95KLx5P^0;XNE-OF*r-Xl&k~e!xow?fO7wq z)&d3<|CXdhXanmjZ}irXok^0H>tVRf5Zo)sH%us#XGR&h{Y&-`eU(KQSH`pdP9H%s#|*(51m-1Fp5Ual?6c*x!nVNoN|l7>f9egFF#tiL^920<9rmth=P; z477yLR$i>Qq-8A<`3KkrA;*JQsXIh%p_J+e;1^kfeSwUar3&04SYRq5vaoPgPcc-G z`3zD<;C(Y;nw1%Iv$@MgC{}F^I=sh(7|U^{Sk(7AL2yi~U$rD%!p$LD#N(HN^*VesU-kLbI3$QVXdB6o=J zt$hbLKEn2%(DC|wubTT$5KTPcw;Xt%$X3?J9=E)gCF52^ZVk7|1p7;p)-RQ-jTX5%b?&!|cHxuh`e+sgu@wh9aN1Zf!0<81l(87Exa>Ab9cqj?F+EnF!rB zQH}Vq3%jel^`$4K{oY!uN^4xKHacEP7-peD-!qPMILWA-QAFrjt7&2}W_XB*rf`*| z7^t2)qMnwv6#D0M{0iG_FoQ#$kd<9R0?+>Z`SO8rUB5u#%_ex&E6^#MK?m-JJnB~%0n{KhFMG!VIbN(L% z%?xzG8hTK*Z!AW8?jwT!2Z0A;7XwVjkA#%AAO9^L=C6wfWkJT&UB9j?Z2R&m{a>r> z-*=e*T0#El!i_9-XfNdh)K6b_Nm~!cHKJf4DhaS41udfd(mdeC_Hb)KE&o>Zrja7h z_;tFLoxBX2VXxD5qc4tn$cdIJqg|;sypFQ7c<#h&JZyKmECcS*&N2TY(=&lh7Jv8V z^?-Ye_tLS`aq|5lA!nlt9!@3P3(R>rF~;$VSC8)CfN5zUXN?~-Wvp4qA3I~cIetgA zU1ws970O75dhZn~+zxl_-OrVOf8!pCZBM*?onUVWO3JPvk`_wL@K6XV)Blh5HGZpEh@CcipEHm9v<8w7cNV2U-3cS>X%=4M~R*BkP3kur?l zl>BpcI-rW{wpGryjmZmKDVL*oD;Njbzf&<;3{7~}d)I>S^nVS#n@^khlOd%N#-=a?SUM?hH@7aVYE4YtM4^R; z9hNs&#f9auG3F0wz00D}$+9jEfQCF4W1Ti6hfcz%?r1D$QY!4d#{1k~8_z0**A9L^ z0Ty-MN!^T%Y-FIvG`x(=Mbj)F=zGf)lq zEbK=k`i2^DD-EOqe*y{T%!C8E5R6=mS09Cy`LNdpqBUQaj$sK{ z>EVEZ^H714m+P0J$o#%)LZN;HU234F1Q023k^m-${bQOQb=SP-kv|biT1rL+LsdsI zA8f6Uw}onu(4n;4bC0jb%qw~J8( zD_49*qLN;jM3voR>+Y^L;P`}#?WXrbqDpMAD>onVsO$;6tp;Vx=uK}R!cFq8DxrVRIZYS9)Hll~I{0-t+^#$o z+#apJ{g49x{_gs{2ZQ;8Dmuj;P6sW*)90(`K+bh^xa)Oxc-M9{)#eZc@W8taEUIrG zA}anauK!FR95mG}AOm4uXov+<Fb=Gr}rj~Uzb&?IYA+E}_#W3bk zEH()1uN)%@*efr@%pcjPw8c1;F0940uCpue4Mqhv)@Q+IB46?38YYrQWl|gNeFw{i zy>t1q5b1KH5aOvI9bcRJjx;{Ey6Oa1)T^Gd_RLInFSyd@XEJ@=862pvQEUL95bf;r z;z4_57O$S6>;&4(boTCO0u6Vd0|pMh3z@(o(X3wKb8HON@N^v>>HUC@vxSG;u#y1}Y{i=`Bh2i)T_FLqnOeys z?yoPW)^k*eNB#DYvl}4kqQGK1!sszde6v}d7PAN`sb#S_I_?QYTRO8>NE}8fi>Q`tG>&=PwQ+pQAHMVZS(je)pDF(XCit>$ z#Uq5s9)RPugtDp*&MC z2?M~)RRc&W{y5|tm2wwdDd-PdgKC5-K4tJI>HcYtLJzT1c+yor`nS1{YCf!HOSU-g zL}?V@(NQ|T&UeD6p*C2-sbFHrB)5@}!`$87WICQJnH?^4g(ACzAH}V|h8Myx60q z4a{N^04K;er9{Z4ae>T?IQ5KJ3;PzZhlX$mW;V%l_vR&8rNLsH$o=d%wBM0b2|#(z z8*&IB_K7?P9$m1<>SKQ=3oV#PP@`z1g)KkR7(_@EYR!jv%m`2wqJ%80*tvP5OQn;$ z8jAP@;H~J)K-zX6I6LPh2JrI*D@LEV6A?uCdAeX||r!|(O` zWr~?9u_Y@Vl1AGnGMmU)LN~WDWV4a87Cuuer*2Pt_dMuGrC@NU_oAb@ZIyg3gp0Hs zY7ppLlEq1Ax3TqsO5j#ljz$KSg1BiP6$X+&077}7s8~Kc5hB%0LmIve$YCRgNTib~ znxF)VSrWD=ap&##Dp$YuP~wRCW}bDSD)DrOGFelZnI=R)UXIDBco6$5_3VN$0gpQ$ zW%8yql_?zY4l}XK9a_qbKLR{1s@!h#=Zc;?|0G$wqlX^Z)IvoWE+9>%9;`^Skdv2h z-1?Jd-d}mOHb1Z30Y03YArDtJ&D)$C&FCSqrQB~cO&K2wMTYO2vjY@#DJ5_)>YYNI zQ00C1g1w)|3{`wEnuT@`TzP?X4Ya4C9D}Gnbnd${niu$#_B172cwee%4{@q(PeK1B z%1;stSbL3j+aBUw05o2b4h(6N%8wqTG8L?-=UEQ(d=)vnbd=ahB3`wDh_uBY=D8)J z^DMISK+>g1CW^=td3qe^2J!lV&wYAS z0FVJkpnRJI z1A=ELKabbW8C=4eYQ)=rCc5Qh$K)z|g?8)`uha7t9~B%Ug9A`z^B{V(t%Dbs&S=!0KuE>lpoz2mD>aQDFDe=S@2Q>ZEMz#4w z@9ESOU2Z5F*=y1r3KfFr2?Hp2hLP!&2VtQaAF z*>f)%;u=w%XxC)d{V8_SNnC;6$(3+>q+Ruv;%+=8h%wlK8d(~*Q8O8uqv9PQF=xvf z5oH<6oR#59x}08VyWY5P=S$CiKH7!s?8EZrEvSuOj9P4N-}n7zL~Um`IQ!$vkahHB zzyJSDss8&O`}Y^+|H$(DyX{_8+VP9R`?zT5VwoU@gp}{2$WKU%MGX87No3*%Mg|!h z3Mz8u#FYS-&?)RXb*13zr>dqoFz9g9`=uO!PLwH^NusH6*qIIP>i_+;aX|>5Amtyy zS#sHO+_6ikJF9Lr6+0tGRslcPf03AZ7&rk>; zhLXildkH{7Piig%fEHl!&CdjkNqH6SgoiNVo;Q~-E~Q`9hMaP_>>!g)nm6a|DW_UE z>I4=U2nEu{ewXvW-?&`DTMZlS!f-tb_{7I@7j)ft7YWsHm(W81_c%AP4A!WOX7tbup{)5eVg2 zr-f~2*PldkvtV=oi!*vXyrtZ8?W}X2T^<2RFvw5@W zHnR$hS2UjgBs$+PH8=|opRA2uJn0;%-pX;j6Afm{K~0708uP${z4wlFW-=V^L-oo! zbhgUZudW2IMbx3-+Zq#vUH1X4q zORx*4l>**QwSZSHYjRT9DC4!{5MgZa2w^ImlccVNlce6$%oB&mN!dl9U>vz5!PipL zic|}Ga~FS6@a#Z2Ufm3ltP;ZiO8S7Lmp(}~a?8u9R22@rFVk;h1*~u|HMF=ihd@zW zxsZ)!rfC|}4IMl>7gbzx4O0e@|lf-B_ax zJefcnMDR^aMfGDUKUKDrj#sb_&Kn4O=+L0SsvN*9Tg%lrU#t47z#WJdFv=9OXjrBB zraRQAX@#XD@T#%I)5w3I_okgWyig0M0a~nMhFUKIPPkj;b;^zAt)Ke9%JhCXa;&c6 z;@Z8-@P4=hVw=h@V88xWmK9dgF|vNSEYsbQ&k4)!svKxQ1 zgH4+U2~mEz4avWb)Bh{Q%lFKc*)b zM3haY84NOpqykeR8Kld9wSXtws&qa**?9&TA~XiyzxJf89%S9bO(0)O z%Ii99FRWipE+>5d9_p{(;kFjV7ypeJ>5p!HPbrJ@hR%G9s(dzR1G!uF9WyY;xi<{&Z( zX1O|A#qB9uqtoJ_&msn^;j7R#;iXgPszMx*^ik|u{!~$O0@G6cU&b(FpCU1jkrE?> zeRHF%Q-mj8WJ*0XhSvHTt?)g)0C96GLy5iUZ;dmAXOa(sitldtY=Ix@tcyE~N z8kvC|(Xief3n$}`*H|X!Xd%iImC3RLxw#y0Vnk`;NMSWP`=#P0xQh5G)OHW!I9wu9QL((@ z9^)+hfGTJIHv?|OO(Mt^$vjjf@U!>^^nKgu7t$Vm1LW7aDTsm;F_vIlmTQH{P>+1{_F{olhP|Nb!%W4kZ?j^p3M z@8TxpdIeB|rxIs{Dnq|NC8RzzeS=}EVzpwa_)f=gw#Kwb$iQlS-NG`H+I$7%MX_gH z_QOwvo%wMy^EQk7{I39BCNsk+zi^A}Ki0V1TrQX@<}sYYWy}ru)|N)nQ-4JLV>iJ9 zT__7fwP4b;I^<`meFbKtqGQ{+F}Zn~e`hHgKInSRH-R8No+w0Jhz{G$$MMwla*rj) zok|OFFZ%uLO;}|0-F84wQYdP~!6*Mi7}Gl96RFVpIG1F3uljmO0n#~c?tXm_<_12f=08O6MB6D0iNMfDAwv{)gjC_UHdu&n;rZPj%^pWKyPKLZGXj7&DRD~< zuJ;wcYOa@ZD8HBZ%#8Tf&Y~ZC2n*s%_u6H)Ee*cOa%Bvn&v>N@;%l&N3H!wL!4zVP z@nnKCX1Cg{KX8rqp(W?Z9c{a_#`J=1{=pXA)#qyX^or4mJtURMc*T3tMIW)ncm)sc zL$LK}ww(jh4%p5)IMv_P@Sk&-=)!t30QrFZwAtq0kN!+N>?(Y=1^FaS|E$iu^9A=Y z+Sc`dei78gR97!Ud)6^46%A=!RanCKqa{qsrZTIrG%$b9=o}~nAmp_x|uBP z{rzR*Ij1^kodsjxLs`$~olRO$(gm5MfM>mtBRgM(bEU?12>`lN|xme3>~EqbLDLr25?EY)`{Sxw7cDx+U8ifo^+ zkmEs%M2FSX>ou2#80LgKyFOdDOa`b-KO1jj4_63w52!p9#nj|s(OF9Rs#xWoPFbIM z(lb1GXqk-jyAWh5GY)RXx^=pJ0<=eTd-@GvW$WT>esO)-G_!VlyF)_@oW#pE0I~j z+4U4FC^|dTn<_ z3R%n*M93qbwl+N!dq7dPQA}x6aYQXDHvwE?dGJ)BTP(9g6yXilYi$IAm_b=!G9a4n zi)%R0Fb}TmQbBKv$3?10NEOW`3gJ3NUk87Z4YCtwZ%V66`mR}8)u=+6>NamHQ_Z*} z)f|ykUNE0Kt6g~ZIkIo=ktI?UaGkO3oWI*9DM{1Pyd#A%@#Z~m#^}RylTwu|%~Gjy z%s#Z}CDW|sl5QETg{yoazf`?Ex=2@Y-oF3?Tluo1O5gU}rs{MPm@cxW zGIK+0r=q>6rfj49RnnomB?MY(cl-9&cpvjNtsG)e3qtu9vDQWdGA@BzNL(+jbcyHBk`u@~3&ToP`6-X0iiam|^yr1KbX_+ch z=0VAKpcOjonYR)oqm!w>Oq{=(QjP6ScL|P?_E~^W$$E|J6m@$9Ylm_H6@AeK+Pq7~ zt0b?;5msMzv5py=KabCXRU|4Z#cfsjwGxl9Wor5LF{CeksAjQhyv&ryyvwP7TdBxA z)uKj4ScImcd{$%{62NfH-J0xc@?!8ij-rB&xh#0+pPdBQ!pyW;QadnJws zs!Ilw`=hGnMqZ_9qP^wr2E2m?zk(=QnvK%%!b;|dVQDsS!b{wi!g0uI4RCl7v zB_OwF)ryMZ>YvH^nvPp1G(2?NYEGhzoH(xF=gIl;$=M6Du-U+SIEO(OYOGN5iR-BI z!5zPJ-_Ca7*?j9}rsAirj7jZ+ zf$2v2zgj5>ntXpQUU7L5Z_V7YRYdTXBo&LiFlai=x(mEgXn!t6AAfvjDhOhI zJ)h1s_nSK~NyE@*bZE^Ve|Zr=F^Dv;^GN}#~Q8%^CJPiB9X|v!)n=8uSd`AWA{dJ+tq~IC4<{X z_KAc*5cre3)R(h-t>{ULP_a;$TNwG9_sfu1sx*jeQfUB`F!Pm6NtU&^=L(#*dT=t$ zjkk`xLVhFPPJ34pqLpn=*qJ@ofXe*yp=UgE;4*Y$CjSmC1)jn->-U8w+bVmK#x_&< zB68Kf>Rrr)>YuCH-HI6Y8a%P0FZU19^+!Sc8+U>$VeuWGY!K6F=e%|DDL`lB%9gC^ z^X&xzDPxrAs&w8I9(z}=ml%|143Aj*;Ie5kZ!l( zZH9>Y@aeUL^dVkL_CUm*vW6r`tyGs8N~OmLj%#@He3kKmO(&H@qm1*4M3os!xOT~3 zr|0-O?)PidbswD~SdO4yLs5}h-)70#*>L=fNhBsxS$G!Yvlt6aOK_{17MH@GzCrnK ziW#q@Zcu`_=Es%;&onx*yVMO2g(gM*&niIc${E_U)8h;J2OciGUyMJ&OgOA+QvBvS zyFHvAjBGj@^nFxjTbW?BJl8^9B@$i3|GufSly{<}BKG0gi zU~$G8tHqJ{h3>=x|MFcm*ldzu=bIW&TmJJXZSg4G^yTENYve)E`f)L~h@5V#NEdN+ zRPZdL6#8{Wt3)yd^K@J&+t59ImxvcQ|II9AWSqK!ZxWu;U=7Z13{Zb&ZV@D{HLRj1 zNua@!x02}Tl<4%5Xor(bB|ASDdKMOWN*sDx5P2ev*@Z*)L+=gec%|8^d|cCh9Co|o zc_ld6=~uk!$7AyQlYR>23(6!lJvw?y@}~j*mGet2c9u~%T)l8VGeVu~JqRI~?G?lh zsdoF1@%jSM`F+r{a~h8ITuzKaLa)lF%q?YcFiG`(I}wBbNlX3B-x}GVSQmFfl-E7u zd}Cmo6(#XqME7l`F}rk{JF4TwgQ+}h>FiEYXm zokeS!WNyPZ%^a5%^x}NiUN1J>#BWZ+_WQQb_m)7l#H4+&tT&?EPh=3c7TnKpgz>3w zMcyff?_51?C+4@6r2SSw*DmlH^DI0-m)L}LoH^u%L(LKQ@*gu7MhpgGK@BYWe`YU0 zW#xZy-nxNCDSzYMx>sj+&c@#HCAOU~a&MpvCa!KQV038hce&2?qM}Ds8&Qh3PeEJ0{W887d_O|1TrP;e!o>E` zC+^)o9J~V@ch>I8c}L5St-J+w4ddH6&9=5(Y($OTB)$niIT}NzdnDB>i$f2udWSvx z1zGim?yTeq5EwjiYWfK7^d@a${Jl78^^_e7`%H+dJsFAIhe(S^y2i=P72jB;J0YMI-Ec)qq(YLP__5x+sEA$Qk^X z(GFIg$We{umkh$9?7`@UP%{#lbPfp&bEd&a!bca10+U;~Z{olIu$GY1w33hi)Ok3B zKtPQDQyDMvzpDBFny2{>LkYCIj=IbDjk)Re!l?(YcEhMNa|(xK3LmFjmPm^+nsx&^ zO^gwRtMy!yJd6DNYGL{~?Fd3Knv}T~ND+M8s-QX5ujRlEiHgFw;NMChhM{#hT2h(6 zS6e~A-*Z2&IxO3)*1RiZsSb+|xH{qZ}NB z7!ii*@QNL~MaCvb-eUrd4K*-a974VSEDQHbJ{di~b>OwwFGoT+*pcBRLh&0ITDbo# z3V*o&ObRfQ@#YSi-EAc2H`{+8sv5u)S;mC-9v@1T<)x9 z56?||Ef3F4e{B!fPkSv6*H3@d2kk}3M=fOj6bspzxbo`RDyELZIa)pPs%0;!>2k-I19*zfqgce ze+$f}$7@y=74JW_*zv0K@q;w`;P&K3xZ){LNSuJ9)LG7Ug{k-UiyFF<$$)z|f~LsD zlNjw_8JO6~m5CqxtuDiidU43Jf=Th`I&D zI{O`qF91MPkUE$O<;BT?`P=YH6--tmHT;`dbh3Sjk0d#KBUnZ*zlSFgH!K@lYWV&c zd%Yml&9d$7&QZTZ1T3@<_BuI%O)%wqJMLxxqerKvuR)rrxTUrOS| z6ou7Q(T({Ej~d~Lt6lq&ADD~Qq`c6Tp6X`1pqDXiil%8MxHL?_pcIu#e5wlH9)_&9 z%E~e)lm+83%bVO~{Y(NgA-EC7E2Bhw`7!xelF_ZWad;qJh6xi>JblebpBno{%05FN zlYvv18UCbPlUxt4;Gk1_TWd~bNjFd}?Ro~?(^1avek$6PUAi15^SBf*7P9f36kmUe zEZqd(Y+p`IC!Rwk%Y`^pAG4^^Eu$mkl{R#^nSqhE=>xSfr4Ks1S?O4bo)`AMG-0hO zk@DyY1iz#hjzmXxpd*HYV)^%-5HSh^Ckm06^38S)JzcI{&+D(y?QYxb>3B|wE=U+1 zT`4kVu5zhSh(wW4PGw&o_SA5mH{R&e{o+KL5pWBF0}Biy2-5-j)uglKU^$;`R+&mo zZE$2*Y&h*#w`=xAC22Zk*m7N~$K@s`^2sb$6va{abaRpbyP~6tZ6$ci z_JN7?2OYy?ht3Ym2}Nwa<5wLJ=8d@3uiJ!>n9>tlq^+6KM|^y<&%p?fmCNWlkD-n%0%4e zxyJ#IYcSzmo+o8w1@~IgY!#E?&gpv!Otr(M*bTOR(kW(UiTt%@lw-!OhN@Pb zS+TyZW~?W~K4%#|6mtF*xZ&pJ^+IO1U6WAwH0+YJU%c^jlIS*no?%VSgZ|zSQ|*Ms zGMsuPOL6=vlRoMux$N+&3kZis{Sx(2R$p3Wlyq&BCSI6NCGYfdV;a(I7pgpwPSv)F zQDHa#E@Rp2;JiIms1N^;wTf>z0oA;|hoE zW7+;0a9A`XlgmqeNCmph&$;nax$DWgX;tqMe>MInIJq=Pj9n2Sc5)*LSzqa z#xqntp__j_s0m_BItjjnI_})9dUl%r$f^-)FeB zi($lFt2$-L1TEV4rn1alvR1vi1AW>zxL@svF2P16Yb&)iDtd;jnfCZ4+pBbHdc;D( zFE=r*+#qy4DTUN*VS~x&3c9~IH7jBLq{jJ0s~envrXJnEDZ?I9blJf$4iE%o)o#*}FNF;~r!;!ro>7e;5Jjr=cHUt5)wN&8`$ z(~h!BwNRDJT&n!_z>4XzHN)l1%AJ77%!}#B)oWpG8+VH$HOPM;-LAz|XKSii!2>*9 zTk8_1e;M^K^sc)@iSyb2G0KIIpuHXlXNxGVKJFBR8FtnqqR&8*u9w0BmcWRT#+S5ya@*@fAeq-ar11{ZOrnm zhr2QoQZQYSH*BFXa-VwkY+OsbO3W8d!&Xg;E=&x4ZcRr{Ot=4KB=%W&df&tgNn?~S z3qSanF&>S}yXN`l2J#7l3PB7+-biu?PyEH9T$&?hUtuUK?1Q2{`oQNBZ#zueLEs|< z(j7GGfX*|A?1SzSVSD1@+9v7?QZsscX8!Y~3i)4Ow%fk$r=P&~v=sBy%r4*IlOFG% zmp#oIy#dHuJpM4kJz-1y?UIZ;B&&(A4v@lg%c$~fca5w5HmQ9Q>S*yLP{RJfNd?FpTk%_*Po8>w|~fxEVBrJK2>x#!_9 zA%;edJu5Pl?y4%#9@G0vfG+P3%2&nHP+?FzeEJuc`tiR%L*w1>3=XpA!*A4YJ6y4! zu}KepZ&7aou@zY5O6X+2M(h_vNLf!e4sfG>oW>P)OfWsuQqS2D6>dE`Pc!nBo-lr+ zWA@eG)owO~50)O80{f-iCw0{Oet6mM>yMLk>xEt;hMImLTPJlY&z(=cg3p>U=Nq5z zM%|kFRp0Z&Fipn#Z$>T~V>b(Dvkwtpz#F54Ms&ip*aD{R|AM9ur4mGH%@05akHo@&$XtcsW!OJZqizw7~JU{w@&VRfB?TF@vKVwR$WeBsRb+h5Zr=pB|Hxw#hx?wI3yg=spXqFSf-8Qr9+y2*Pw?)mnjM z)KP4fRLhls)e7R(5}Z0+x>15=5RUR>Kx>WRuP*(QE6*H^R*17@_#Ew4udjr*=F%un zM5|@U=0}3h!Wt;%4h1N}ecKWB#}%8efo(cKR_km-La3nguxoE=^M*Q}$Og_DJ|o#TH? zfz|3(Ubw^9e8y|*%?$ml8c-z;lT?)#k$pM_ahTE8r4~8 z(l>P7KsFXyP)$i7bgg<4L@>}k@1XDL`U8lk3oM-tw3FM>U%an_Q#?0bt~2Rw+v)ND zK6)?y3Be7%x`BMeTa^MP$Qd$!gTU6IWy$`E$%+tN!vr|Vu|KnOQj*=0UV`Z)q23yl zmz8NKIp_@sgQeUhMU|=z zUyCD70RI9bFjYULGI~>{?Nqu*KXxkG%68pgd>QhR@JijMn!w*n@mk{+r90{k$Qwg6 zWiVtmoaso?9WNV7H@2;T@X^E2vjh9Gm0QlpCf`VnLyWy@l4Xyl8dU2NmE{8J+!;|; zabvP>d7$~N*HT{m`;2x43 z7EYAE!2&OtW#{nH)lp=H-x;{VbdaDlCtNxpjL2u>2%x54a!=&)@y=CprbG((d3w^b z@MmXaaZ%gLbM{ly{f9a574`mf;>A5<{*A0i|BHygA;b;mY(OZ5%+0 zZ{$9Q;L;wxo42=4(fxVZP)#1NHB__8+vRNyjVUHo*1AaM91))Fyw$md6ilOSh6K%r zdz~&{YwHYF8iVS$4I!l0OZJ2a=A2=|FnV+L(6um^mh=xK$==(fXV-i*ekob4vHQ`Y z!VF$t#yCF#9wras@U;i>Fxzn~|1|qEVYcUQ*}n^RYcI?w8yb8n{i4LnsmwD^iaXzM zvwgQ*o&eZg-BjjpE7^wJZ{JMdH|Fk`H}$V8`6*eVi4wESq}M+1gu8t3eu>jNfCt1+ zWgr~>BxnnAD%Gy_)^`XLwZgTrGGy#&QL*VU^PD-G0G~zXNG|YHwoh2XyYX9^V)4Pz zwt@KEk({#f>w_6J2h~A}HjH2%!A%!$9B?iP)j!(q;O@tR2;+zC39UMz!|#Xj>zJNf zvo-DM$$@8p&kx@2H#3$`+ZsM(hi(<-t1Q2}S}(HqmG(eXwso-F@fjo}WXX+tzL>FayCqc8T$Klr}&iZQRlJEk(^<>0rhe%$9m@ zli6lsN$W1-9nK3|vnOJV4Zw+G%vhiF3MV*W?FC}0p$|kF;y4X&tFq1X&%m!@7JZR7 za580@fsQ!;yh0C6TIGlFpPZ}hyyOnAq)rLF6`I_ZuHZObQc6A6t3a^Z!{WEkB$_qH zXajY9?ruYV^c7lkm%*es6-!X4=lfp&FQ9+I=J5=u)fjI-^Pf8u&(vUqHMI?2IO3x3 z+G{s>Z7@uX?%;Hw!sI2!#~3@@GS^t$#_o9|pS+{@lfr*M2@8uOS`sgfbBTSAsgvZIBcAiAQBL z{xHa*&T;DuKbry|%g%p!jx&uV;~#!jP9Q8L;}86C54*e)i9Ucb!z#)i2&%P45f@I{ zY7)~NO-YJQ*keqNj=vD1_M#M7W5PpJO&Q@}PyR_DRZ1Zq1WT4pxWG!rM^tnLQO=)` zGxAiP;BGCt1j8;VyCiKlEwg4xKsg%lpQd0Ou74!@XzXdUv%QM>ODxf?h(SScG z{RQjANb)6*n{$jmbrsa9oh-xdEU_A?CaGq-Xo+vh=*cLIY`@sVAHxf;jo#YEQpQK7gV>>c%;X+fsMS5pL5r6u8pFOo=awpD$CZt-Vb(uA=?huwKS7Q z|Iiyo(CTiYC;CSCidNntDOJKPJy+t{pI=ZH{TH?#hQA86u~6R|(cj#c?;@y)G{2yL z?BQI$)Sj!U78+oC*mgo5$vC@CxobhYi>~p>=82JOh@4wU$!!q1WiNs#WkjgsATY7d z$jcN$c84_s3yV4Aq=CV(M|DS(gS>KEr4NXORBcoOG#zV3RxvxIKT%dzDR)UH(SF$8 zabGwEKCRQxD~*H2E+9Jq@06gAqV79*ho=0-w^R5E`X>SSQpNty9I;$K0@vW59WwRj zNBlqSlrnZ^|GhQxJlU=ALzqJ7Lddy7xVl0hh(R1@eL9OlFu~_m4K;{-Wes&Xf9)m| zOm#V1$xkziLDcMi&KDf2v`%HIh(RFaeme8Taf?BucP*lP?X!LjbtN2r^5%t*ekCpL zPd)Pb5XO0cKrMVGHYg=ZuVP_iVW(jU`p1Kt(}2Q;4fny2rem%C2P&9HgZWzghm(=< zgA4v2U0Di-##V-A|4$INmGXiD5+9X~vJ|BPm?#pkKAq};HX2xbtD3>#9%T$m>vXf2 zaE0u7l)&)6UC1nCY!~Ox<0yd%hZ|Y{1hHM=n`2wImn^={@!Z}W-vVIFx-=2o5R|s$ zAsaXf#`!R{P2|xs&JmSOS>k|v@~DHH2xvnJ)zkf1DhHX$G)1OL!R)LI0{w9Y8i^DA zdN-NYG~M~s%?OLs5Oh}gB%4t{OXoxtwK`JsS_h+@#vL0O?k9XJytq(hs*a1S^lWB~ zbh)^U*$lmw>K_JGSmK;yPEl%ks`cZ|v_sKI)if)Vz`)lqi_Gh?zz`YNWFYbhh`pvm z+00q>71*wZDHse)+DTE<>V^oa>mHW#9@g>^!R_Ok>#o1ycJ@7Ynr1owz~hNmk9#EKG6 z*DFTJQUvrRA-Z5$nhXzWDvUz|bLXqyJsoGovZdSUI@IbkqWm@y$QC_fn87(Xp7y_i zRi-T3Vj*GH#o)dJRq0x1M+uhx2Drj!?{h{N2X%EC9wftfF)Yjc;2ne&^KSCU*`|zz zgfU~RPDk#(m|aKKI(R;V2Wa%1U)s8S@|KS5DyJMlV2{Y)V5gP5cHk2dmV=(B=qAO` zOqi{R(uw#V7dkf}e#u1_%^|ESc8DSs6P}Uf^7u$sNJ`U5Z_wGG9>u*oKhMijNdFPE zwuhadz;;zi!JP4ZoGbSLcEhI2-K{#bY+@eh-yXYTj zPD%-%8ukg$Y+jgrtc)l{SVN#=pIMIoh*}l%-&|5~JaHY1em~p~RPat!Q1Y}>no)p{ ze?^FITa*Vf`yD>?MDZ7r+J;;oE0t4FCy#sUP&)*MXKo$`i@~Ho? zix{>4EFO>yo5wr$fTfyy3VC#L#KTN<3Tj6Fi+_Qs&M#XMC8BURS5G7#Pb z7G9+|(+C=-gGe@yLOSp}d^1jw<3ACge}dgcAb-q}oIj3Un*V7%Q3E(yn0oxTEc4$B zOL9WL?ZSUC*=0AmnaFKm(4?d-wC2gF#);$F(1?U!AWfcrqla&^&yedfx&nC0crj4G zph*e*Lw`?k0!uB1Ij-JKU4HMZ_P%_?GVH{OqrTvAt~NM~3Lz{JN30%OhYCjvXA2LG z)I@i(XMhCfAUp~dYYBSh%K_$7sb#(>WsDb+`Lff5D0#Tqzg|8x>jjW6p{E3x^Cbyk z53QbJ{Fh=phcs_x$`C{BI=T5(w*0xJ^$siZ3XKMziY$sp$&pvvS5Ta23J>jgZs?%8;9+73xdD{j{VlgiO&Xx0hlXwmqx*ZvQ-@#W?E0b zSp$+O2_#$!K8@)XC}B|4#)Ol*kTo*LZwmtYmfPn`g;5`Uaevk6J($Mi=?B1UaYKTn znjgJ-0Zl2&kA`p-w(f^-A{C^uc|KHz}2*4U(X6XFC*ZE`(SOXo^^zZMf136n;eJTqc zpTAT-vJ@7O(sY1%jEtsYXp2mK^UM?wj>OEvmc(>Mr&3v=DuaAt>WBgiaPY;zQ%g4h zhzM01J!NFgMFags$8}9-N6mF$TV7`kjql0!xJkk>8DNI%<;T!@Gy3+hf0OGA!C#d$ zdp369m27{@fupzFe&YLS1GS_6uv5O5jHIVP;YT=t`1>^!$ro#2<@f6-k}ui-9n602 z;_WQ=H@JY>V6WvX2fV->6YdV|Ua94`8{4N0{J(r-4Bwgq%vX8%f5iu?T_wguzM8=+ zca=!rJV6}JMqbK;wJx-ScC4SVF_hox+5gmpzH^D6dEo!m8k6}JhV1+ZJOMZ&i7@z; z!!SbMCHqX5*@Sz92la;-^|+6 zPK!v(CN(iO%<}LLE}FkEFB%kxEt@6syG0U*xm_nvWK+&p$tD-;bW+Qzpd@KYo6&U7 z(GDl#s$e8_@O5z#S{45_Xr)dLl}_S#bdp=SY*MCH9=K%az)IAB-n#Y3Q^9b z*mR0RWG&4V%5W`yt>GomMO0T4wR4khEE*;%-DYO6S~*6O(}8pkPORykRsTF!#)iv? zfaSoIyr$;@d>xtP0$;Y(=|}NOmrq}tzJ?FLLwm(%aZ_QeB}=@D$m}+4*ofdsm!Zbw zSdjB(Kny2aRqU!SBeUVU(<$YaF`buNwvF{Nb3mjDc-pSoGUj<^qM=ATV@h(nRPoMC zzt}eB0{aVNh1#55KT(UcIob0#DsV%6LQPn^BaC zGmKqlT$vB8*Esi%`dObID`QCvi*wfdn;gOlP6>+YT>CyF`@mtt;7R|UjY0@ zbF{5Va6R*2@yXgsG#t=U``Um^2UyfoaM8iOPWwzC2;0$4_%6^^oCaZ6r7p2=_J`Kw zUeoVqEY1La(9yY4$x5P5lU5=xlU9Sfvg9HnyHakcHGie(q;jI2wQ|;-({}qI=Okg4 zQ1MIWA!*=Y)QE+_B>eFwEy>P>7H(qG48hT>PD54bf3NXo`03&+24 z!j9R$*x&G~#|?#RQgP8jaM_sPE!1=B`>baVv%?a@|3Wv&i5$U1>>}&SUD0yfqV!Bp zaf->QtGN@#j!K+H=!EtcZCOa9I^()i$T5q@X3)WptRTzA;3O!KpT;LRGEka?m>dEr zZ&0tqvYoLVl8@sgpyGTxcn6T#SD_g0D)O32=*2J*#pm+g zt0FH?>5ymtO->t4HxZ-atKtRdBYz8k#0v2`BOd|qp=ICUxB|T1@DyjRDLYi=@>n*j zincMv0mKx{ZxZ6<2{W9?BbGz;o}QYj}?O$9TTo`uut0d>P(<;}D_Qn!NY%&Mhb65GTdq+U5Btnz-(=t+HoaLcC=x?hit; z;y=#BM7JS!x~es;K!xw(h;kL=z9oadfj$1l$&Z&N%OGOk*Gbtdh;J4m#Y*5d9=sz| zE=-lGxOa}H-@^ItBU;=CsRa1j7-fwkZH!H*!^VC=%}x1Gy0laOdI%0lrb3j z8~*^RO1iY%i_X>SfSD#(kgotwYh0?~Z~Vs=$=Ms$X3&|6h5R8XV>CM}X^h zT%jPwt4ZYhMZ+ zB83@v@{^Gf8uUN}U_>q12fR`i8}ED7)BVu5Q~_3EfUZ@Lc%VtcdLw8IvCwd?&(&de z3hwhv4SeuTGlpyyZqvs6KI+bp^}6B3v7xJqk_=kyr;0rx?b!L!#VF4{H-pynjht~U z?XG!wT2+h6St{R{$J~O8c*?cM9F+y}cjT>D`kW0ro-O0oHK7|J^%5f2rn!30A#`Kb z)CuUxK<8&hu}SUnRZ8BZtIMky)0oQZ^@mW{w%Y;jI2l0FTS@$R$+Hgm@QZR~xd@!+ z7aI0G2G4@_yR})^Wzl=#(O|Y;R*&f&X&>(q>rcDDXb;R*q&=zkq*1d{gN;`*)QzIv zAsuf{c8l%J)#x)Wrw%robF3CSHkIfo29$NRvpxRuIXscNBxl=vQ_>aiP#}9y^mO)T zHnh9=vB#EKi=dkY2EGFd_Oosk`?1M_qy;QhMkex3u**yJU<71!*Q3C3pv33|U3cyf z@STr4XRbVG!dp4!JP0ig=oB^9R{%`7%C+HK5Q%tD(h`#4;^Ct41*k?{#^>@{YCUwmR4K=var(ixE z@N#!b85Ou5@B(%#K0F~obvy78rQ$#l69P}BT@r3`achMHHo_f1r`?0u|5~gbcnX+v zn8pjIk=Gfe=yO5jcL%QVSp1aGX+>EXXR@c#rqLlB=;kGF423YxaW?!VS_k#%kl?9`p;0X}sz8u&1OPfEYd?;mW#St{}CB_t;?CyI4q^!OJxJt!- z=}TitNn4N?84facpg534qf*Tdm0hwDRZD|rx8a-(yz2|@c)6msIXrH^mLAV>_TAxa ztu@CFt#o$3=c<0)S4UA@aNlFG?2KzMS14JFFY^82Xup=h*bj8YC5Pbrz5cE%v;^>+oBP))H&o06XNEYg%u_}{+^MR+(jD|-k@l%=KAUdQ$ye9Y ze1wCv($w{a;>b0}Yor%bnsd8fkTZJ8d7sTW&4tyOhCk}d-T(TD3gT9H(+7-jP}7%h z#!r8U%NrULPromB55OBhSdjbJ8?w^w`R5^%Gj^xpt7>2cK;9!1lau=NT4FsAd3a8C zHsp3l*&`jJmvZphvl*y_LC_{{%KM<*$w%_NK@~7L0Ha0ps?n6UR zgnnZt#T88Q3dTR4*Ar^^3G@PbXbtM|#rkwl-jeVyR8i=irp+^`@~3+VmAiM2W>MZv zeKaMSs!!r|tLYts;FqHBJ3~!MFl;<5_>N-n&dGtG+27lJSRb8(TnW26rTUaYRQlxF zYMbUK;B4pf#8m(WUPX<4h~f(@K(?0a@Z==6RvC7X+J@I#z_cR>>U%{^DF#(I))lcb zqSVEXk=~nil52KYQRauIe@mQt4#oTc`~g{4U2O{x$Uyn7>-9et`GrBAy21#(lEB05 z6ZyWK12rBOT%AL8Lalotf~-A z*YuP-zt2=v#NZpeZ!;p}`Gck39=Tj;~XkpS-eniC}IhDEw8X|lJAG-6-ZQR#opl16~=z7(L_ z7cWu=_45~oQ%kU-!<{U@9HzR#YBt>wdmdo^M{jkp{ANn?H6&?MWL(5_6SsQlO0TYCN0m@7zs%#cHpTKKYr8x?a)QCs5IbQQS#P@*9%u0k;~?Gdt*O*w z+f?#h64}^X`Rr7``?7RqUYa&)<{o>zp5*%V9DL3E)6VgJ%NM2;SD;&*3$dc9Cs-MG z?54d*Jo>`n&npBWOiION^_CDPvrS4xRNo_MlN_vz!ER&skDYih=H`!)AGa zpm6bqX$8Brz|ZaX#c$nj0s5hMLOw&@t^r*qJST4OakL!Aj_Y=!t_v6$e+LB z^w3XoID4bYhZmHBx{Hmr-rB(LAg|4yoA)~bdvo~TfZu)r`R46;zze9{4jb2p{nizy zo!h~S2ps@bbCO?;U=1b7H))uS(U`WWH=lPV(!I2t3`>T!4oCt|c0het)5QT8|gvIr_4nIJ&JF$BLkn+;|q>9Tl#CViP` zL9k+Hq^)__lP*{j-*xd^3jSFmygEMzO1>hPwt+6G?ugIl)CuCUXyCq_t<-0MA1f8B zQjcTCFxbNdzf)K^|5$=@eWxoJZ|*zA6*w=Uz-pcY+;0e{Ax)`5*RTXP-$`I@^A(QHFPpLgu&%V!WYWbCEwEKInE9wC=?uBF zbq_v`NWM-RpF`fvDF=L(x9iYTtF|&AxF~YMf~!p&f9sON{s1+eQBTKoO%W+sQ+|8P zQLbN5)8d+RYH^cAn&qr0bmd6`0EY6YO?X->*~1_y($l;qLZOhZ9@qyz8!G2y3C@J%npKEOilv?OBAQgpQ>dq4shauZ_2G)5 zVr`>xk=1qc$nQ1OjAn}L; zpnD(T0SL~P?m|s&jVDLAjr>b1W=zx}XW#ABMN56ipDKg1aG!V#IIqF9y+hJCzJT48 z*XRL7f@XY|DiQrKuAPd#RtVovOt#PMK03;}998`E#%Gn^NGW}NN2 zyv*jITZ*nSg-o)S8Cf;UkbVxdMlZI{wLblyoEpEu9SZ|~AKoM=_-mxd@BY9wNu+p-_fZHApu_~S}8AQq03$?nFX+00Wkm1A;oaS1ca6hrm^v|S6L z>bBTIkS(}sQ!TIkIGW{zf_X5Ah+NTrOp>e$Bv@d^J>OW|+KezWVMV(^CzGy5(axW; zfYZ)o{_xp>kCSHyEM%^O7Cqt+*WGv4mgcf)(asF#&`=}wR32VS(JhK-13LIq>F?gd z#d>5>FxS@Op(uwNM#3QtJFQGc*11&i^g_x^u_mvTG#Lu3KsH8d5+jkEtJpYz7Q4mT z68vI-8NxOy=p8`Znt0*4dM|7^YQqqv+h=;l`5XtgU~6GgdyT+zCh?K}-fBePARW(T zl(6Yv)PX8xk4q~n>bh9Pke((G?P!x=LMy2$t+y~?@?mAJ1zsd24JX}$_Q{uIIQHqE zGS(4nape+$;j~S;rF9=&-)+#EM88Px*0~~@MsX!-R~=5=T`-nq!adg2@%#j?I-Sy) zwzkT?Jpk(xIlvi9bB)Z9Px0l*kYqCq@sJFDEAt@$$yiTd14OMr4$9#MV0P-1kJ-9T zRBrk>RJr8m3V$dUc8KfrzcB5&Dg_hBt;8A0!@&_(SpLfzcptJ0-X#MV4$>cVb4R&v}b_4#8+w76ItYkarcy zHv9VpE!bJ4D%_E&rxC@AAq2}oFY4KWwEH<~k>;c)@^8c|#n-{qq~))lY@Os=T@ zoNr1ZFhHE_kbR%?K$m&Dk!8*lz&gC({z4)TKto6`KxG$H#epi~4H1~`ih60NM>;2X zsgc#5$of-;DgZ%GxX8{;6DVeEx!DGKBJF59H0jFN6M9Z6xC7 zpR6`Pzi5H9L|uwDqtC>lS6wFG@fwH}>#eqf4vnY;;)ir3>2x?cnNavBCCIJDDbwY& zI{oy|?5w*|Re3CG`5P=h~7L(sikIxNSS)p%T|u6B%7JHupMemq+f2 znO=4!-m_g45_IiuIcaKY{1M7UsaYMSdyL*M<8uRR>y&De5J`l67L{L`iE@~UpL%1> z&SOX2)_lp?u`E88N| z`e?oWu$08!_?R`J#0#u(n)Zjpdur}67E6xaFjSe0hK|NF=tTk%X*X_HNP7skZ>A>x z4E-@g#hJmOPbdr~ff3lXsRHKk^Q*;uv*RAn8&bF8&jI<1~Tj6BL)jw3)7!bwEuw1OeAk54gOdd)PLd=DgMX%ByBDJYn1JO>!#{o zUU;hLzGiN&W{ueM&Kn{vaq}6zq?y*K<`H!P#MQ;tk{lA|MiZggCKTgICLB{UAtGo3 zNMPt-0%b>qc}9Qy*QvLVh<6fxA1M-kL=winzb5_*e%3Yd} z$?|e~7O3q(5ZHhH6(zqb2ajs_YHx<$!B|d&E0*^dUpb0)J#P@I80yW3hc?7W7t%Sr z9+fs7v4%OY*eibO!xUxeEje|K-oRk>8WHEMD$zq8@-b9WyK>;m`zyo_YV8jZFWLyl zT{UA*bSN7y%802qMUeRLjj^FO<__xM%kV8I6EDGl;@!s}QcU;`#cy}!pwgj73nsjk z+o%vT^q~9WFyo^J5Af)2@hA%tMf<97k=kT8Pqd(|CD4(iKsNLXGR!+3-hIwl7vJT)`sRE!#yf_Mjs3C<>dSmc zqN})*)@W-OS~IV;Tt=koa%W!egX(v4-VZ{cq>eX2wHXG)cx%!?NFu?PgT19&b=k*2 z-ez}mv3aai9ak%JqqR%R*I{|Gmz>o$x~NHYsCT>p~WjQaB;&8$%aD|>0e zkrHRX+3M>`b+FvG)p~?H#p@oPDgzH2$)FMJwDd?@&{f%I!2`RjQO_XOv3EG6?QuO7 z!9&4Qu_pna4#3UCkhd%Cb0uxeOY6E*I2zk4U|6Be?Ob=^m3H&UOGW4f9t~veQ zIh(ZyzAPXoE6_(x!Y}Tyoc^Z4WH1xWmw18$nv5q=bIQJiv#Vh04~9vREi>ZES}+t~ zbhPUjiJ_`Y4Gz9K(@FN4YZ@Jc6XsKyJ@b`rO&8=8*~w&4EG^RTeq_lnM9YYgzp1&^ zN?r^r0yapW71Pkp&Z)DSBETQFBQ7F<|6AY|8dnn6~1c^|JQHYH|1p z@7e7>l90;6*6JOhNpcODM}N3(_O4;HbC{yRle$6dIqZ1vJ3{Khx3d0)Ir;HvsW_B! zd=q@WddS?>K6hgz3a@eS+7S+90Yj`>*J&zIiCuf*N7lvUyut$XtLjz!8_p(y&TMy? zhaX^EHUx0cFE^CGD{FM=YS`1+_2`US@w|(jTh&*#;rOe#`~0lxB5oST!E$9RQK0#- z6t4#vSpbD1Tk-uoXZlAgVuJ9D*M}BhSd)co(6J`4HVs5_r^A$w>)=1LhZs@_Q?SK$ zId8jGOo$SZ!^7Ra+ZA9kk?wEKX2##+S(SvmDfz)|Zqe-%|FExs><-|Vq{f;%trBey zyalzhFugJ)MfwQ=xy~%>X3Fov5OcuVX@{iOhBpamdp}3;V_nQWWoN*tDk+YI-6q*u zRZrMi^^%@630@RTraT%lN;5q!zZzB}&OpUye@AS=v(s&?&`Q}|9duo+X}Vm3$c|Lt zv=3}?!r?M;+}!e89jLlg9pG4pZP)#HX&*TYynsLUuD z>Y5l**8_)ZFcwvxwhs&!6Pj5%)H(Y$t%nNft zF?vGMD5Fp|gsq`?Dto3&r0@YAPlqZBxy&d`c?ntLErzc8P-zF!UoE*8H1_XMjwf6V zeT;+A{JS#kIq`X{U5rTjSYZwkKL%FgbR6**&Q@v1uc3U(Cm6c{&Dn2qiJIxu)nCFU@-oLy&iXv z=TY1pvC2YzfI`@EkKMUnsj2+c(?Ocd7f59M8L(&9?9bqN7)tJ!jZkr-pSxl|7qL;d7ny+8=NV}LvZw640Z((IUh zOV5~PyMViHBA#JQC+Zux)?)VW>wYG?8qB|eo=GI~A3=MDi#w>3o!QBV>AjD9M$&%A z;`n4(aKaOiV+(|2fUM|NQTIsj?j%>>@bb&+Hp(}Br%r$-s|yo-cNs`6TO9@R06Hgy0X&8FA(;I|+qk7seOWj421WjGj5<=3hK*|ijpdxiVg z+JwQaMmpKW#*m?N%g0yR(+~Xe1}CtbPY89!8Lb9xww2YU3FgCtIuwV3f4Gtu zxb2ph&&@s4@p=vx`R7B00tAxuCc2FbUBe2S22azTq4e#ZLwuYN@7|5ZOHuz2=c%hZ zOfh=}>PERACeZkidJD0%oPH_0BB^?5U)gHI>xv*TT1kp5%u&dSXV_W7&|9}hQXr7E z*9b`n2AYLs{`E&J_VQ`TE-AR}Z#t&_#rJ~GD^PR`xAZe+&Y%ob5gDZ$NK3k&Dv0)K zcc#Y7g4)>KoJ}!1Hu`XFXkfZ(hJ#MRr_c`ZFfR=kG#6iWh*P`g1o(X0ZWJ07{=FRd|9nA9M@+HH;3!?(77U}B5V}-U!`0#%V=QYOucBMfEykjBha=!0 zF+@fFXIz9l{%w4$jx))@vdDFu0FgCo6aGrFR?{p|L{w?WfDR`aD8DED+^5nW4?^+h z-(HF}56KYL*Gn<_VjHslk6((i8NlA{pT)C^oc$N+(07B|b-$e`q!gJrrPaz2NFJ7^ z65Xy^srsd;Sg_4ygol~rYWgcR01Vpk+fZ(PU#3g~w*z=2QE0}Of}E1l!JyBqcN5!P z+jYJmStu_jy{RH_zY!A-v(`{4ngVW;I$M*&G1cgYf-G-JU%adTY1ui1xyZ z_Q7nFq7`fn>#8=1PZ%nx67}}+bFeO`sreR>OIJ9-_kA8OXo&Qx$92 zB_VYFP^!e?ZI!ojCf8&?CIms4peDTXi zzI^olv*PzZ8@hiqX8$K3tC+d`BMz(WIHP?je%m#UQ*L0ay`k%k^ih#;eI3>T%T z|D+I8juKMkJOgxMbDFtN%I@Ircw%_YL%A55I+#KN5i#&n1N*;E+~&w-k7NNkkSwkR zo^q~p?zf!nK40Iq!N3#ybHe<(P{fF~O>Z%y@z2Aw@aJzh#Z~c_ko)<%Jh_6N*kksw zZARFKm}~cZefm0mz=yZgO+ByV%lShQmyCNGMzYJyel)RI_AQ$CM^B39P~w$Kaz@XPf9Bqz3u~rsnFK&t4DrneeU;vByLQ%o z`~)UDaZ7B1)>b^WzzXU-83y&3*DQzFXhd9);C-aH9QRS|8%!xk1O)W;gIvxe=#-#!?o$Ge7h zYajQcJJPi5?3^^ar5M7^9!H0Gr9$HipIS&$c8aFdcRtYW5UMNnT z)~~Iqo@&$}e+hP*tl+}d0Vj8m=6Zz#prX}5zG%lI9_z$6pwDG<)>!)FFQ}IzJPb*ReluH* z83M$z*MZ4X$}Ly?s6Wj!Hg4zRdkX|cQ0+ISj-<3YOD(-Yn`)5^e znOxkmJ$gkjI8Q&*&;EW8j?@Q%`ia3joqfC_VBPXz-a_?^pnL{}-+&Fb^pb!^WV+gyfpqPOtI_P;gY4sb;MN^V>{)kgx-N_Ob=+i;6cLEUs} z^c%r@=wTNiG8iIhJuEz#P`v(|8qr%zU@GBP(boLs^ZlR6@BblA{Bwx&U#un0_*{Jq7yW}Y5m!&P(9%u&TYF*MAo_=D|H z92ts+m7389w3QCRVz9}u^qvJ~I%2MIY$f{~<5F~E(9f?35B+VdfowRzLx%Y* zR{89@oojq%#jc~x3_vzF&Rp{D_P2IF<@jv3_kv7TZSZ#!@i6YnZerP##vH|E7L{8P zva?kySV$eYNtboY_8y}MFM>zxTXpyBwE5&*d=4PktK~$(gqyMg!kLvyo#WFsUGLiAu=X7k>(gpd(BbpxMA)83up>KOL}Y{5WtAwDvGa%91>TueO5BxU_IxFZoc=f5^;-~$!H<`8$R}&=XlK|Nn`gzap5wx>v>!*TRePX<$~O&t zr5K*H$oib^Jo?Rkp-s|mu}y^Arz$pjrpD%j?4rfsxI= z(ugwYYMyPckFn-C&3P`Jay%fyr7PJfdokLr7L!+HPSmoenS>_mUyWt%oz7LEF^Fly zLd+t2KG6VvD;|O` zc%&A;d-Z6Kz>NV6!x=gTnQfKe1O0PVj(V`cMuxdpgrr9T$IpR<&e@C5oFu9qPXSqN zSL)(+&YOm;Wa0$5=w~R>x4((`0^bYKzcYa{J@tuzD-;Yv*bnO?bxngIiJNd0Y$F8x zR`x&QTC$I>42r9&`Erh05lZsr1y|`fo5TNrVFw zN}77`jZyZENjP+Y8Z|ey@F|)YHhKv*hFJ#k5jP2EA8Kj_rp;{{^g&Gdh*ksPNryhN zYdf{IgVf?aTEiNGD^W1Ek)A-P@54SnjZ@h!YBH%i2#zrm$`SYI%&8%6s}X1}_m5bD z_b(-ivdkDErVABYHj}$3VCK6x9ZKqU*>l@q)8;{7MPqrd5aX zid#xjyNoJSKOFPFKBRLVaIj|_tYiIE=v1;Wc%5;IuGO#|oJ};3hl8slVIh{HftEqE z`_z~^N~CV2^lpXP7viz2@w^S;Of{IcST(B*k4Ev9B5*6+MGFX%^7>uFaN>%*xpl*C zb+vGsOW|{aYG&D!_0M-$>3I*GOTO6I?X_e~eJHA<&0+0!;u-$LoW{|;H{bttK5%wF zv|Ikw?aY3C|GzkaqLI@-%1{CMN!S5qje| zm6D2Y>sqP*n$ds?gBvl6dvR0dA;WQ%n1zs?_Wqki#JS@5Me$dQmjLlU$f=4>_Fp3q z7tjC7sj({l3JZ$p0`aLWUo$tb_ekg{TLNXAi0{vZ!b1tj%>6%hd+l5f66YB&@qT}q z0$Y-j%?}~%hO=(3qy_%OZo0^QyZZICc`v_V(A)c06gIxTnhZ=uf@z9SJIX0@?#xg* zJaiPdlsrvU_WU%k!bp4{tz)XvqjEnp>I6Q@hKLt_tp)6Dto3)RP7`P7?AAKI$Wx-8 zJ!>N@rfMnFemX;3RE76{+Rbq z$u(O^PbTYV8Z&a&M&=~xU(`Lpg&r|W%P$BOOn^I=3|+}Z53E7Qdt*y5weVM7`wn3N zdy%mI@=6980vTg+PN@hWjVYPZm}DbziOLw_p9_6bax6HUVPD>4UBMTsDrqq*DLSXp zP#1N8&|6I5KGMTQ&W~ArtNsLI80~iH(A?RPz%bfNn~Z_aj+c_hcT_^dn9}dXnk4vT z{(`17@Ben$KMfQ8p1x)oyI*X&|IDQOe-w}^|K-vCkGrl0&_FlC?#a_z4P0|f6f0*& zhV#@);2>?s{p>1@HycL6MD?6c zdN&bP)n-P;XT`~Qb6f0SFR+2SiXrkB2)GP1%eqSvuPzA;E0zK8!yeceOnh9;X6l)K zJJT?E@K%(Sx@@GpZ5pNiGG(>U~v10`!uIuKM!_rUgl^4rYW(?9md51L{YW4I} ztRSXeTtO0%%g3LgbAQF@QZT`Y+L};wo1=59?$pbrYjGalu`b+Nc^Z2QiR3LkMi&}l zKxZ&6(Is*AJ8#L!WP5oDSeYJ*@W86K=gZcuOC7|}5Ovnx#Q5H|hS?2)|Av#aaJ7iU zC@DnKD^_RDX$JU;2o{EkoX*{lu48E80uAlL-AY2k(&y`T!4Np{5g64us`tXeT<0E1 z>?Q&&<&I|gyE^NMOOQ3#HwPqib(b7;jJM`*DB~12(c!vhSy(&Ef`fkH#_gFPJl#YF zDRB6N&2snzz5)Lh|9G>b&0WXJkey<~bf@D9_}=U*3aK^MT5J(ax{o?~NWQRQ^8 zcLgc{aq`h1f!r_(J;g8VfTpIa?u@e@a@XYEk#2&nhVRY&SQO_UKYe-tZK(x)wVy-r zo{0aBvkmW{4r73Jz9tF)VHlX}s}jW{u2|tOF0FP)l&UpFy-@B7`N=cY-Ud}^();6O zJRHt5d{8W;(pUIhQ?*@J$L~dVc(U$!4gRPRCe^ISt1R8aqLMl~Azy54b|3qG+8E)a zo|Ig9sR@B2;5#y-g-Z=I)Bb^&PZ!AVwBffz>xmVd+nd&cBa%_CzrxWemF{@npx8;1 zn2<5xEFW`lbq*R>eJ-DrZQfS&hbyq52c*9nLd5Bt#%hzLa7SPM$o1WFk6?Glt`m$F zJk^p3ZlL7!ottVY)QadrwEGRNJn&snTz{exke@v|0XNELn>NMS6{qSmy+F7srWn-E z0L{CI(I1RNpLw&jp}fayf8H)$owdbR$vxfPYn{_BKBp>p7w)kUF2a51M;O@0*Z0tU z!v-<)wg#L9=8p{;^5LXh@Mhs+A|0LVQJz6+bemx_m!FM(d-u8fJ@0%EMdw`w@AJj) z15^H+bo7Za?kgv~vkUNvR+4tb*?BulNXZ|9pFBg4Br=Qd4P6kuuvnRN39=QM(?J&5 zt;S-w{qMr)|0Z5w|G$ib9b8@hjdn5Kni4+uHQjUmqFylkf1Hv~lajLgI{q&L!m+9w z${XtFdQb+Ya467$id@}u#W0+Zi>0CDVMzfnhSrQn%jeO|Wv#5jP81#F*B_BSLiv0P zyqV^u>18vA3_J7wmcQa`H_`bKkdkJ2ysukU^w;0`|DK;1AP|X*wlX5=bv=YMx!IC76L5YV7b0^qA*M0sS!BNX3i&33hg^4TY!xIjKMF^vJNI z{`P3ZU%eLiqxNEA4!(zzfveiX?sNmH^l}-mu%l6fwqm&Qh>#a((UWM)MM&#v&)^jG z4nf&yHd~jazE!zcjN_GuM*Ylbci7C(Yc*DYkyEc@6wKf<>Qr@n+z+M|X@7-gcjqMAOgM5U&c-ZP6Ob8#dvu%SDbJ1+#;;Gi0yyiu?_oK%0V} zdME8*jT;?;ne$ebRGG|eF%GX_?3~+_@7>)ZG<=SriAczH(6vrHcvw?MEd%?V(?$(I z+}cj7HfyQim_R}_ebrcBZTD9#n;YgITac2bt*y}MwPanciNUodn8oT+;kuRs)&wK( zT?D=e^|v$=BJq`ZEYMJAb>NY~E`fd_R)6Ht!q1U!yA-sGsy6}yTlljOy|2$`5stTL zUVgi^P<6MyfjxZ)L${3fYj|T07R_C^ytOw~3EP%r0M^Rzoliud$LDv4U~teoKs(HU zf_Or@juAkzMOoDI@2*^z7~a=vBW^Ni?E7_uz=*{Q<^FkaWbCReKTk4psOcxyP?jWT zAqKmWWbEIFyvXrA56aAW)`~Jo3}EI1(W!UX!%6Tf+QVnoY;iVo)+yOy=%|11GoglR z0;pfq=bv}c(T9^>3VIbe!z@$t@*xK0?&-!W#Zb`8CVE>$lZ=xq!Ykvz^dZvG&WO7D zC|vh1#lJB+kyJeid-Ok?;xyrbuAenkQws`Tw8r6xF5#hbbX#nh#lO6$4+e%|^hwRK zWZ{lZID>K%yiw@R{Tj4KJ8ggYbb2BG1JClFHQp%Ik{<%R?@Fr-r>s(&j@dg36$P?+vzJ$&ny7`ug=zUSia}^6K8wKq2GgVuioy3ugv4`pxL4~j+;TI7J$$I{U*B$5jOjD8K zv*q6wOfra@W_gu*1Ud5XH5<@}kiW653Zk?LqT$bX8pr2cJSh)DW#6PdPGnIiLwkD^ zL$Ql$e1tG3AG%4u!Sg>CRtj^h{lr#jlj8oF(@fO2enIq1;EE43%(5T!gyI;SVH1P8 z>|vrpNK`JrYf;fGrjp}S)BPw!0Ylv@ocfo5da9>EI#dzDAZCWVRGNY1G|>8u@cmJb zcCODpEJjrZC+C0)zS4K%#F>6)-t3%bt!iZ#Hk$(XG*;t}lTe^fX*y_^ z%79-@f~)ZIxlgUn zd6y4iK2x!NC7#Q6`F&Y;a{$u~(C0H|2FuF|MFG_*F(DyAj+g6n^Rxtme|);sCp&~a z8dS$Le^0n4g9|S5bn|z~5~{s=jIHYzEr+OZWNO&t3O9|swh8i z5@t+}n=Ef?Ee@}sOekpS8e&kX7?T*+lYZvY`d~Pd*!x` zPetnY1lp&hWt+ZE12I#THoD)<^be19LMBCVZZL9te;?~~KWKesCDg&Kk=VLRKLLb$ zNO*tgo$!fXx<{?|(x|t~yt=`>q@5mb@b1*y3+T(=+HvjB$?Z7ayKlV<(D?1hY&=7e zP7`J}Xf!v?PCrEsUah?NF%j)H&urNVQu{pyWD}+Ihr9TVO(R9O$B%UlHr|l0O`xD0 zE8*N{%*3C+i%`yLH;u<+&qF4Um&b`uKLaJH6PeLu<3=2ED$0-d6(y*#;~+Fb$d36b z1<>WB6UQxF#f0aNcRNk_@d4s(bU0C5*;44nm*cH&=v{l3(Ktv{SQ4bNU4a#Ic6~t2 zkqylFmdsmI&E;mTR;s~7!`!umnBjgP-s~9l;LqbUj@#AYQ&Zj+N=$piuytVs26JYd zXfaeFNv!5KmL(K480kiDO)#3|geCYE?U>}tTT4JW#xI;5Vmu~@LFH(p3S&*!u#z>1 z@MBvo8dzQa^Uk7iOg0?5zS5KMpL>VG^tzlSapN8A%h1cgHUmmct=+E4I0i)PrWK6K z$We=-`$iQTu`{MJ*nByfXdKD?qRF-;B$%PbTo7ap#||P6j_e)m=#lg=9JV8!!Kmhq zyb>)ljR=kALK4(pH$|*_E3GJ(6jwARTRm_iW8#jE8AsaIZisxoc(oL6l1J<|17qZ` zHT?pzO?;Gih!!_GjDfXz%VALaG-Xa1`}a39bCq&|q-v#~ecEh%&atZ9UY-`gYiaPK zMf>*5M#*j~z+1RG3*QXJmNrup`rA<5NMzwLIo5{tFjJqVc6WRA3 zVY2;y0#hjo`AIEi?kv5laFEtmD=2Cb=>r!~qd3K?2AfYMSk#vNYHRKf)22dt4sA{bZ} z`>l#8)#xc`3?>j74|L`qj)EDVDhv z(2XepC&P1&JR&wG=j(Xd)qx!ouFaHL!!qWuGh(>2iGX(E7FuqTTL)(Y58du@NODL? zZZkgYc(qi|u(UtJnkYGi1Wb@W($g|=ylx6eOgL#Qy2{Ey0@9{d;j1SnOMUM87@IA^ z_Y2jdueBkfHC245R-R?qCO;LLHq?CG1 zUv)Q}_hNRz;qNTZW$$eGEFW$bH?uER!FfS3?{I!2yB04zrBeA*H1K|T2F%attF*ll zHFtHz4>mL(Vg5EZGPc<7jm;nO>-venD&<#!Uh^BGXV>7r)BtdfD zJ3qtD%2iO!+|X&r35|4j`(yHvCkOs&er!&20e-sBif8f&Us-nW&HGQ%V<{!iw-1Mr z%08f?ixX==AFL*i5-|=Oc9seqGdtEp@!4GR@iKqsF-NoTG;_0%w$g~VQ##nGtcBGpqtGDzw1WG|K`8a1ZHqQtWkAFR&5Z zn`Q`f`P6`LRJvjQ1yPZ&L@D-*t=OdWw32&P%hs}qJ-#2xi0(C6(lbjF&>C{(Yka)V zp9!OK^cSM3lXG-V{)C8aT8I``5eh(@(jpm011i_xL7c4!H5IW2D@G1~2@ilqZyN#1 zWQPg_j$~va?_i(^Gs{x1r?PpnK7fAA9_8a`r(*AsDGl)hdI}llFs0+D%jybqD;Rzs zOfOrN+e9M(B3sc%2O@9mt*ToPbjep99`hC;1!Utj$9Sf*c;u$hClw}RjC#NQT}{S6 zk#)xCk_n(3mT-u(z(9_pOd?scF=zq6Q0agwHncym21{5@A^<`~Z?)E?gMD>l>R-Si zLuQY*UcK7s2XJh(Jppk#HTIFCt(uJcq35-%XpxF+w1X3+hri+wMoVYJoe5ZVFIu?7 z<~CT@I_tC=qlV1C%tkG^RPHGFMH0V19Btd*-M(a}LX^kw z1fHs-(`<6{x7IPL)3~{AFLkLinw&9ZaBW~K?m4aWZPUk|N6L=Jw(_1!AIY0WvjS(h zShFTNzQkT~L#LDEC{(?Sk21SC6q-EP7J*h~dYO$n(@HOnYXP44S45G9c21Y_rWSHI zr|K*ANtW%<(G2l!AqNeXRZ%P*oR3z_%)(m53@NsOI{BK#9N6p%cKE)Osd*hx zg>vU`0gb5E7vA)AAZ0JT!A#!!Ui`0#{h4+)r{n)y6O+jy?^Two40&x`u3OrhqW1EOS7`98I?)K2l@fU6reNR}R&RQM;NPq5Db z^eSSEqK>aDF9V=c;Xcwf%p-mL=_&I94xSCS3STf<4h7@@Ud zM6vsDO9M>Sb=kO2Ww3?0i0%a#p*hFeY3{S4u za2*Dc*WW{aD-AYDv6P4CTumq(!a1G2fTqvPH*mFADfz{VHN7qBSz7g(oO;6WeRG@Y zLHYG%i|yW+O*Q$e$F&vB)1i9%Uj z?*+x<2l200Exm6S*S^gLzSnKNP}DU&TaQn~0tG!tK$1VclGDzU-C-Gs$?tSRXV3z$ zuh`anV}Ypiz&ksq)O!$MXnUwfh{t3uRC)cTV?fNE2zz-?b`;{=?)E-14!dfEeZKac z<6Pz{{5-uA5dUtDpSCDeRW{m+oZ%;n3ubItQ$>>cJWg{QrXm5uJnm_@G=wzzD+pX!P*CW$NtUl)QjShmvBd`+L^cd)VNWVXD19 zs_MQZ7fPnJ0pd}GiAGT6#qWN!QVR|*&*A}bvdKGKq;h5Wg-TYqTfYjBk|T{(B>~h1 z!bd4nN+O}%^_15%%TD+~EwFRTdU>TRh63DjWi#&d*aFsATVzY~R=D0Fpe@VmR=DBP zsSDTDvJ21ba(!hF)Hs#;S*4NBvJ;C`C04A0W|>}ES*5!wc@-wZus)^czf~4gBWIgs zeYLZKo@Hkj%gU_6A`ab!%QJO2qv)U`tj-cxuC zw=@UUhR53m`)^8O&B-l_Zd9c9$PzqYbmsI#%^Up0J3V)yyDZ)~a}#ciWKWJpJ_mzm8oqYrc5dj?DQae5nvASQwyX z#mgYq#E?BxQ4#Qn#f=$<(;Y275fQ~A{ZUqS7E^NDtC`hAdSD;liH!Tmau9aDy^4I`@`2@ z7&W5K({BVZl7FO=S!x{(aLt;LU*KuQTOMHM7`Oz{9*+1}Q?A6|*VI$nWkOEZY6{k^ z$kU+xfJhrv?^wiz@Wb-qT5_J+f1tr(?9%m1pSFogvDN1tIe+wN%c^_ZveM4}?YX?o zLTXAN7|Lgrpr(^wL@6_TV%)i%?jAeMC+DhWx8pfrGd+{H;&W1!l=KW?6RBbvA;A!- z{lP-pIN_&|#aZn2col3V_qC0&;Ix53R7ia4zKgjdGUlHz4&pls#$~bV&5P_#66A-1 z{6t>>5I|lAZ*>FJs{2-{AH2o|rr~b5@@v<{eGs6$`$}DK4)bkhR>&XX)RQ3LmWzA; zm*q=D-m$P(TKK#(e^lN{Xi7jfV@I$I3`dc~wA zJVf`$k+PE);aja|3^=OJ%jI7|<+k}e?T+I0WeKsXE4kn6y=`Bmi z>a6r;Paj!0Kox`?{5$U9)NH}%Fs7GB-2THTl^8_NwB?;xwVSL>O8`UyvYK*^J;!LV zQ^fl3l6ARs{Ni?IZgZG0Vg*U{c{!;nw?qa&*1G!~Xf(WjNAzWgj1jvm%iPEu({by=A#{9mPK#1gZ=>vt?&+oTWnVbl^5| zq=Efee(8Pk^d-y6-G9{WmF%yvR0N>hRFtAHRXg01C$9x{OMu0waKDe{h^%=q=3b;N zYbSK{NtQEcVQYsTum4;GL&Y7RyohnHM87A!_RJDFA3}1544XBrVub=_g#-G8pC{$YAIO=M1F# z;dhbksaXd+)4%-W3O9H;A%_GBAIevI^06LcMg?WKTiaxlLfSA;)N)4`s2WH{{Y zeYTsj6=#vXyv~fl<%XRYXpW3p z3fh7EEk-IhD{&Ew6XV&f{aN^6f6GTJvP`|~uxp7Y{#Pg)O~Nm93zBYCoo=A;!vJ3y zKH+=N&vW-xvDy#8qWDYIcSp_H*u2qUKgC%Minh*{2`w0BZeIgue){px6|sswu-+(L3fE z87CdNzxYOE?u&e;oV;#%=|zUAbYe$pwJ~egK@D{D!5U}#L}Q7%%RKcmg~zH5f7`$l zeAo%FT5D8;hrIE8ReaklKq?U1#P`ckCVC6{-{F6p@!(F*s?hZQSa{)LkM<7@@QB2` znN%I@Et7dhTI{b|Xn98j9B`e9`?TE~7*4fWas7=&y!-Wl{Z|C%S{Zy+6T33}D2ihv z>>JjV2wA4uMl3MwU^{fk7dp_T zSTpkC5#t5gnCHhXs5alwei@}3qqu{B|L~6Vue0uxOyT9kFD#?!3(NS=2CM%)>lQb2 zF|qu2AR|_JOBzoITYzoNbPK><6ZW{wknw`7#zKC{REhzNV1#_x<~*CeLab8}KMJsY5qF&zrDNKKRuI>{T$lH9 z6Dc{nUDeJM=GL#R^Dpir;el=F;??8lQ}y%MOtdP2wY~GlY_0FCE$NUpnk&2T1v7a( z*VtTx80DdCsR~6-i|5&B2w#4Ma`oIce{rP1t2oFv$g5mMwVzorN-^cEsDNT7O6lGIme0E!%G9&28kE07S>UWj%px65+#{)7qE&B_A6wTDZxP?W{$7ZPp%W%z}#CiiE0vG z*o4rdH-1?)NOi1E%6L>H{4D8^Juz_+T$W*LUzS;T;m%F>w!(}@cIrw%3&|bZfUZD;Sf1pW_R$|l5XN;y^t2+WL;b7@4GCq3iG+^yI@7Loc5cd6NOY5x z)Rh!oZGpDJaIHz~;jCzpxm0BeePOT2W%RXZe_65njLr5xKV~x%p_tNQ^=q9u89Ut5 z7fhc`&bv1oh5)uno9mq(MSFD4VEyu}Nk`p>4)uwgN$-4NWoo%S60SvdDskwM4y(g- zilsi&&aFceFktf1r?NI+GU0;mP)c!3p5Z$8iB7ja+Sv5@<+8PR%(vx}u6q3l49!6g z)H3{&8SeW@XO-B4ED%oeB z&0~W)my*ag#4&9)CLqvsB&b!^TE>P_W|19|?2nOi;pL?mg||egQz)~~H{2%k+34Y- zh6mIwTefp;)O`>XS5$_$?S3s#O`kMzjck;1?dId=X62L?t=8?$Tf-`??;4=1ic4!} z1=AYSC2k4-i?MeK5-keSbWho~t4`UrZQIr<+qP}nwr$(CZQbhWo{8v*8y)kspZ3ej zy>qS1{MY}vGe!`-=2w|ep_a3v84DZL=y`qZSH3#nqo)mDt%yc2M&Qffq6VGY%?ml! zDLYiMZ0r^9->q$W^v$$4UgS?*slzjqgg84~Y$&5ei%fz@1=J4{h@i`8)b&a+iCmO~ zu}lVDwWV@!S^SfB6qnR4V5Zk>h%R;wXdNZuZ?*lBw}(b6R6#LiLj89N840DjDmUm0 zR)P)oP*+hey%S%3O5xAgcrXM^HeXLK2#4!fXhrhWc!9hKFpg**;OAsMwyKM})Nfwr z&v*H^I^N2nI6VF*CdsM@DwrW^90i2ER?#AZ%y-2qmn1zE&i5?erOFEMv7Trhv4W9W z_BsjGpp%N#mGi|E@-ha6Cd!hqLfn_gB3`IyItHUN0FU)l-d%UpIlx==1>KR>is_Vx zpa;#K*NVZ6(aPX_;cJ@V6u^BpHzd$;RwV$pI7&+J*+~_nF-QTFmwyAH^EqnO`c~%b zlkjJ3SGyAnO}u3R@obAFcRR z&1ZdIP3_38YS9hz*+<5rPRjbu8%)4YpAF_`@?#gZ*UfDQXwOLLSHh3+E!byXAKu*v zs84?m<|l>^EaM6CMQyTNYxnxFen8JIt<(4TZq3^csL#L;!acR3el%33xt{Z_8i77c zpCvr*VGp;}#t|yRE9dw2F5atXfX_Z&L-Ry?RO-8n%h`a2N_Aq!G@NWD^dyAHFcBbl znQzh?FyfqV-_Jn~%r+3WIWV_*rFr`?9aBlNsTqy5tcNVBs-S}fuHZxexdzW<1%?K# zAf<0q)AlE+?`gQ+rczrP7AXcTzP_1PV|Vaz0Uk`1+=c0^$A0?{;(cNzr9bx>NZC_} zP9wnIX+Ejqn!z0|%0bYBjr3U2M-dJGjB*0KB0GRcnwl0zCqNOPJTbm0*wJPHY>5Y~ zb$tKnNK3=hbv;3>d>H0NAfov0gt@UU-Q?B;-Iphp zF)^HcoCx;@W#dwlSy`$Vpnrsf;Hyox) zAp^8!wirvR^mpErhL{=hufaCpa>o6(IrBsZ(p1>dK#&q|%D%KHs_qnXg1ysr9mIqS z*OIS5cWhP5ru3U_F78w(coG@xs&nh<*fIIaA~q1e81!|Qr~fG zJM8y`HVIuKKMQg($-7MJRAukeI8|2IYTRNe-+vNrP(d6{BhFkEOP5 zgu9l>!%wd5$09on7vzW0rsWI^<2dhy%t=qxQ6*pxSVYv}6-omV2%MVPpmxTkMN<+v z^G+=q1qvJSxQcY0Sodeh0D{3vm`FnttEVT2O;lhN{6upOSD@6mn(8h7c zdVsK;_B(&6`wPwQ6;A6*U2_o2nEysrrmvVEezSX4N*udkG2r{+Ux?>Umw4QOy7lkl zatm~&{y8+}riL7A48I(!=h^o9YQ=?3G;9OkL<4jjli61X*m!~x#v7DODLXH!|M3*P zO8DSW!V2ZZje>M$@wHZ;qUS!#zu<7&AKdNSQ{gnvp1m5HGfdycKqqvcKH?;O<9stA zH8}SbzJoqgH(WBQDC<@y8#zsJn{kxB=3qDRX^KM$+P4S%(nf9dpPDi$)LC=OiHW&9 zl=ItGC*E!s3&k24K@Rr0d4VWW4eXr9@AZ)j?>W#v$I)=M4y; z(R$*ix0a{L1WozA9c}9qvL*jp3w4Xu_w$U~OHFVyRd8TJ^&!mL2{t1YqN6b&xxQB@ zt=$cxMv!TK;?lg~pbO7l!^&TQ`Ghi~lcsCK($rYA)A;3Z%T5!8A42e=ENePO&H&RF zzDBuwES8Q+etW+11~6H9vq_F-a}?;0sviFD=4?xk@(i4DlS+OYjJ$tbPEfM);X`%E zd$Hpsq9-PGX}oU1#*=-vpBX{i=qg*E8>EvN;2%zBjQ;BM_h_Q|Ms_)(@moJWZaL;X z0R!s4`oPn2qz{L%t#~GS8rI6RyJvuX+pVYUyR1uE&Y_;e=84S;W$+76&a^1a9W4VB z#l|I>(&S=ZN{qFR%N=6Iyc3z76x*+OEj$V?3afWLSbpy#J8b3Iu@w%;QZ zBqM&ulpo-P!ym!vhrj|S79yC5E0(2aV5#bL7n2lmosz4Ngx7m-p4lQ}9h0~D`AJr7 zT6n5}81_3G449IRQ|rGBVhOYirRMkYB1tc6M>kjZ-s^?qQ_gRHxJ$=FF72U;l-Bq) zmEUSLPLfw3=)e39OYr{1%sbDH%xZ|jE`}Z_I>%Ndw#3p5QV$XiYMyTjycI=L8zov7 z=rrLd)dsn{sC)w7FP zp(zE05L``9O`J|s0_#*f4_u5zzWcKkjB0^KAqxV0t#NaGDV+S_f&#uU)=jZ8daSDS;xlE!n zygIjQrVA=CS%QK5tL#!_!j6(C*-_8VMf-*N)Gx zBa>LLx>O4`&}!$V5k4XtF4;IxRPr+rXy+ynMiM6KK@yPW5Is1|KFSj1jRW<~1_8;8 zI>-jPVM_^~bGr?H*keTNrKBl*bENk)qW(Nk;P9gH-I0Xfsldhd%2lvxV#Dohh57wVrc#Qmn!{+3UNdFy5_+=OHQeHT(N+jRp&J@JV zeF$i5MJMPv&@O&Qo+3e}X66GaG7Z(AEuJ%&`2-st&l0pO@TJMKD3$%>BI9^Q1D$+< zXKiac;zn>kYAZD+!N#RKmXy|$gz*6?R)6aOb+aszyox(<>2pc&aMsN9CSC|inYgKUi%LqL zk{t2e(z#K&aO&X{j!5v(sh5oeSNH$x!~ImRA(_xPuW&b~R_$`6ZS^2CS1-|WgTB{f z_LcqOIZ~b;|B7M}!Ot*M&@5Ep%!;Ds)I%yfLi}R64!w}>$VumteHFe@2Kb2EIwTVK z>BWR@#tA6KiEzZvx0=?wtj@PO;8)$$tBjk`8frdE=g&{`$_^^nBWT6*$aE5n8{svC36o*nal9} z#Ua5w0@0FZ!9-Wi#j>usa{>M-vctQ76 zxYZrei*`;|J|k#5#ZCgu2t&MPx6&<^C`V_=Tx-?mPggJxj>4hfzSZHdchV!r`{t_E zpIjS4H~bm9M;G^i|233&2m1H{z#&i`m^@L|;~v873Uh;P^vQ2IwX)yu#Ce2$zqdV? zex!NVioM`=-wB@AnEYQu31@=;7)p?9WDhziE%T4K0LfV9H3UGe2?MQV1+9KV8+_QU zM(N!o#teU~53jXNL2R|$>1PO7F5qoIZ=SGF=_=Bb%*sbKy{UaQ0DCfrdH{rq5-*4qDMSQ(8%UM}gZt4hmxHN14i zXcytK4`Yyr_=+!ACs6&uBv;w!+us0%#vM*{^oBWmWYPFuM`ffBp6`>i{-svo(Ztj& z->Q<1v)GvQw_mS?7=E9f0zwFkS+gp^l3dYooFiSIUcgnNro5C4H|RCyDjz5?PDv42 z^{cB{K7V;-p1&$)?VXRVhJSBEn3NDbC~9V9o4-V()N!4oJpTR>IZJ;4ZSbe5cbw*s zVs+sLF;G2A$)jve!=f?Q;gNNWTa!hOKi)*Coi%pi#=BC%SV$PPeBx_+thc~n8J&jM?8PW#`>?0&Mn4Ce={HEjpPmr2j&DBE>SK^%+m`}3b z&DA`XW1QBWu`rtdTID+x>65(WPIYs7=f3Qdmq&YO@QyEp@eKYQK5loviAjebcv`+Z zFXq%RcS=L-2LgU{EXUxx74`ET*SqhAuoattW4$P9=b*`_Dh?K-iu^Xz@nG z(=Q4fLopbpNCfF7hP;OIiOUNEu{19v5Y)(q&l}4^$`hiO&IPxR%nghaFfuMqI58KV zrz0p|L$rCZ?obK4X!2fucHKXmFe2&rV}U|HN}ATJG8AgV+D4xo)Py#@9JVE@m4Dc1 zg->pBp~|T-rjevh1E}^*I2OvYlohRW^4d0e27>H$OHTjZU2Qp2X;Ib5k??dobEBB% zzr`mWy()K0R+SF5E01x%RHUWg5L(itXrlZ%oDnn{t=i^SC3?~F-->c#!oZMr67iQm zO8eMkmsze>M!WLhL+DYY_NdYn7+FySs0sMl<^$-CBe|Dm%CaeD_k_afYneCb4oE)u z{&zy;`HahvaZ?oGN~k^jGN#8~A3)*vbPwwq zA(g|uP9sER`SVGI^Af7-m#hP;#>st@PIqvH0)QSZj3n>@q59vg$q)bxKi;Q_O1uFdgQs|o&N&Cm)_12ULJD!X}W`B^s%JQi)N^c z>f+LOtTn7$$o95xT*UYQ!+#}yJdNmp$VMJseQ9-WZn>J{jxTg_-!fDN`gt53ADvBC z;JKn)g{bmd>ug_l0`4+<@^-pA;AN~}W~i#G9M?wy+IDVKOjxGSB!mp$`1jima89r| z39D2^I$B{LSs<0kWe5c{jWKC)#t#2etPd3HafaH1^{@LYw6h6z@5DQcjcsNP2X?;( zMseuPQnSWF>qhj5;MUPlLD14n7m-CeFb(9q;~S;}$#{;v=D zHT5O+UXJurw&AU#Fl1-^);X>ZC{}5l3-4oT1oMJ0YB&HHCsGZ*4c*QiZQgC>|7M38 zw1EN`!~FRZ^=q0-^grq99UOiydH-iCI!hJ8Rm-@|yGNaYG-07pDr25WyBOxGFaf4E zc|Mr}M!ZaVVQE=~IMYZbp8OZ>D2X{o25A~{`2j<^Wr`6GlS&gcB=n6M{!4|vMnS)IfA z=uqO#^KX~N>SlR{n*bYz@bdaw4IOWUv|laAW1tl-hv}Bw>W1~!9B2gjvDKTWAZI4n z4$zPwGUV3R4{V@xL=e)EK`M6!;}tE)O${st-IXbDq+czP5R|9pst5DczgbtQP!UuH z%XPCi1)58LcLMZVdZpW7cL&CkIgB z^VJOK!;pC3Xy=kEPY-LpAO&X3`IYQ(Xp)Z&+UcW9%WMMycFvuHRQZ)hg#zXJJ{(T% zfPqVE3d{?3*5v}m2L5^xj6;^3;>oMA^_NC@|oS`tp%()R1)yHukM&wMTthAyPM(!t!?MlJ=nee zHDmdOG#cm)bxD*wM3Ls$ZGBD}{aediiwQ1GgA6R=KrYo{H%XR;l)O=C?{nhRWKzAF zb157+1yR$>GF|DrE;rSDIUT)KiTS}b>6#jB1prFU(JDqtDkL|L;@GQ}j(E&bX6xMM zGae*WeB*qyl#JS0(|lbIh^saHK05l`0Dfo)6Db*TB=Jj_y&vh{Rv!JGsz`JwInNy< z0$8QeGz&34|9S=nI{fQAhGIF9(f$-NMX*u`(WKuZxXP#D9)Ve<*i*XzLlRk=aILKa zhZEg+XIS-|nL1XJLfND@Cj#-)91M`vxZrf*^@3C<$gfo@e9@GQ#d(VxzmY_h6ssL| zDq4gy^x9nNa=F^#cttyOi5Uah)VeBaygKKch^n;nV0(YB?^I>h35RdEFtg9qVEHL3 zEqFe?qRFT_GJFIHgEI}zglHBbhIDa(hHpNhHGET$bXe$4Vj6>FE!FY$xb*fiVv)hz zrd)BX1bhXxmc09kAHp>;7Ci_1arT1G;A{f-c7y8ra{1CAys?4*5g z4Oxyh3hbDHBIcRMrDz?#P>RO{$vwwexOWk&_%)yxxkYKKTM(5Sv$Xp zTD_DXn(P?|iA}bXRD<4F+U80e2CjZ{@!802ZWb*&^Lpdu&C>F@r-}BRK&k7`HMvTC zy}HVfXK@A!!G`{k*ap>j*{fDt{V&rD!qp*%P{Xexp$*1d<<_Xbjg*Zs=iK8?sWZ2! z^m1H-kWfN!LJ3uwO$a7k=UT^IKYXKGYzeoErN8r>Q}MZ0&`G$WwQShQ&qbQLO6Z?( z^WvbRfR!gZ>E?Z@ixrRJV|WgEMas71jE&q7X8Lm`0IA*rQecDj$7%C>z9!}7trH5` zD;IXXeFlSiM?;`OLNnZTwipi6b;wHn9)B`?7E zzxn7qOOqZf&~)A+FcY_3yu$-iwVPvs?#E1VlTvYpEYfl-m>QK})=a;>G!)pfGY*NO zy3ig}6#-ie=-0 zHX>X$09`Ga-kL7oXaD!Ia8UtLJ+l@LZ^XT=gHm>Yhtz=dV;A!Bw=2Fs+Lu)&k&^UC zSg#cq%&9R^W;f}?RE2wGz*jf)=bmBi^WR?3}e45YII)0Y?UgVtdB%^eeh zv)LMxD_P5L5^8;8YN&PZ#k~@)YC(w0B5)POZ%lwA$g`<`B4^Fy{QjK5#-G!TW&f!r z2UyX|0qg7m-aG60|Q?tpBw)suP z69XJ-{p0RxR#S$bb}QIv^uZ0V1=Z|`b{r-SMkJgCrC}c!Y@!$hn0!bKriz%h%sn?E z$UlSducnRrUB>qt689UW8I(CT8r5Z=RXVmTvDxYc;EW;{(FPo%4M}uA=+oJkp>Tf# zvBW1s=+Fv`@0NEV+QWYUrWYS_ZWL!`u|di>>W)X~OpzT9a)h7EhdIbAd7KfSItU#` z=5E1Dq!s9G1Wp0Nfvyi<3Xt8=0=}&$`$yP(XcP9k86*vfB@JD%427t@IJfTSvZTMaYWnZGK&!o?11v`-;&@k$g zMWx{g$DqZW!4e*Y=u^n#9k_sTjlKJZM|vT8IB`%56-^b4*%R>0R-Hhr!-z42>;An( zGwSH6dc6Bl56+uN>?*SV zTGzNwIvfA_j?!WAf~(F*1rb8 z=bjqjE{m3D2lVcvS**83v)r>w;hX_pWLD;K^aq^8y*~1orDd(F9 zcg3J)>X19yYgyAuLH5u#8?E$JQ0ap8x*NOyq9q~a=~3x@P}dTEm8&0$vTWnNTW3fG zfMv2xZU3`hyPGRd6_8F7_y^DA`UK`hgDrja9)zws`{UV-T@5Bo!&8IRqCac!FXn|e z6s6ytBO(h5IL6?JWOyw|^yhs-{3(T(Y$hJWEH+3@s(SJ)?}GsVI@mw zcAhNu+E-f{q*#DvoxMhuVx7R8S7%j%qiOM3{Qkj`>gJd?NiANf`Ifwr`n*6Ao zzJ?kn;~F>Ol4@t}_{vtrIq9(iYEzV#`l!Q-J%<`CKf_16Gxy`|^333MtG|b4Gx~t7 z@0pxyqKAe=K67u4nA_29KTI8G0o)@Y?2K6V?WI6pQFiGm-{J4;szVos;)V544B3rk`g{@ zVnNmiYLwg?-pNmw3Bj5djenkhYBU|Ni&{4ZTrJQCE(mRGfoz~{GRfeYG~^G_0qrx3 z_85(8(nPRLZq0BsPQe#eCPgzHTE1cayJjemaV+TR*Q99Vw^SqeAIdZl8~gwACQ6!6 z#TG^$?%8LCOBWRb6bU2>CgL&>L1LyK6#FATdP@h7Y``=nHOIuXB`7P4OVW*JS3~)7 zKJOaspYFgVnqpTbseP{U0~qtN>s5K}s6*qSg1Fp7Sh zQ0gnC$6HB+s`s#X+$4-ku5MtbmaMLY8bdr%-@o5*sP2yrDSkQ3oULWr)@Pq^1e2^u z)uO&_( zRKc?JBo8L1ZhpKn=3#rc)g7MpYH#78Y(vyW#g}PTs*29YQ|$ywtYeFg>Y|dfjP-;+ckWI-99y%SifA2EnLC zHk@35fa0iW=%=0GoGRxXg~5@{Co`Vp?+CO}c(A#!)_IHyFD!QS1Pp$Z(GENwFCOy+;PJhebHvyyK}O5<2Vo^DHGTX@M6(EOv9 zkzRRKp$&W0>-SGI1DO=?gN1}=cMfL~xcP*_l0h*Yipj=`bhbW@P719b`V>jIWgXU3 zDY(k=uL~1ZG>4mwwcP^>wK5I+8Omqmp5gsG;i)AFDew zw&-{-L?+WMlMDviKtY4+GT$!=$Bk)}ArK8ZvQ~>P{k-jnzv8oa@>5~Gf$Gdb2@m}h z9uEUuIK+=WvO$Zvn3rSY>#+wA_)F+C^Vc=skOKBhKmBO|PIk7T&I2a@1DC>N zS?-Oxyt~*1g-t+17L^aK+!h+<(Sb{XvEVd7#*v&wndUGmRu#m<>7_ES{R2zssWP8L z!UJ$g>3}0ssqr{pRP11memw+}LN~aowJsJb#$FcTg~={w&K9pED^2o+@-Q2&4ML>3 z`fpvdRCy53;FuXs3fUf_E21np_LdS14aN`*lC@9(r?eg8btFssI@$H#Jrd^DZ90y3 z_vZe&Hw^hrHA?#!RilWY@5JJj=T}h3$;iLJ=D%LtX-{vMqh`Cs1~zi`b!z)+o|0l8u;8 z-wiN1R-KP)(aGz*bY9z?%l+RkwI0Fiec8&F)cffLJ8LJ45Z$@G-J8BvgzH40jFAzn6E*xqBzZDWM&dXTQ+V6GXQg8p8IgRYurkRCqx=dP}z!aoUZRsd=WfC|NqzxEc`4kLw%z>nx+kBaN}mTEz$ z*$MK)et^xfbC1^CAa($97BaVhD2OwInkc%oX@^91QE9;ZU!R+lz)L*~)mssB5@^=t zDs=~PAlhu#a2|ei*#4YAro37bHYeoukiftjhK+erjL|p0k1;=;gr$53}oNCQbsx#(Rv6> z9Oa;ExRrO`f2d~;*uuNj7sYdr|K0`*+Y$zFprKl2ImTf5X3VNRGtvd-_Ws4b$ui#d z#6AmsLXdl1*gqZ8(ti2*3%BbcLA$v}9g_VqZZ}0H=U8kiWhLW`60IFR2a4=iRLp0S z&<)V;H&P|5w`eVthS@^r#}KOs96zn2%cgf*2YMUkj@*FDObWdu8EO?245pV8KL8#k zQ!O2z%a!*(BA?Ni#`^E4fEKftquSp#g2y5-SMUh?UEh{c(B;g^50n}T?JSm9*T z&*1~s8N-|(4sJmZO*7HbJrf*8wQxJA7Q@ZHE%+Bfst`2-rRO1lyty5ukiR(WMGGtY zyBK-TS{O$<@ab1Q&nzwwOVwZaU-ZU@8FSx!$2UWK4uxwiX|o-FRdHhilwZVA0#jvg z*wUa(9R%tPBPTmFkb@4VNWBQPH9Vz)?9!NNR0EVY^2X=0r*tK#L;A;>AV8PdDrnX; zuBIT;kl5!qIma2-6m>>FfmfPkW#RYq{nZx*D@oJ_+(V;)S!?0<5eU9yrM2>q^MWvn zZNNMj98`(ih{Jh_?vTqHy+%%oas75q+O7wH`EZdMaxJ|YEN)-eXfki9@Z1>!*-p<`*_y@%oN*muV0uxtfBd5 zo<*1V&I$44@)iq?JP|w7<&K($?R9#ZmLt`HZU%lO2`1RlP0$jO7hZp>M`tZoSQE7&;EUYlJ>o!5d$rM*JVsi99B6 zi!HRgvh!J?@_n%O>hf>@Xqqv;<)AoH=Q?;Fg)V*UnIn(C>mnfEe}=V}uSzM<5>Y%{ zeQy$jJVy8HLt^O8^Z``GE@X(b-EgZ7<_1RC`eWA!C4$Xymb~VWmx3RomiZ?{_GX%t z}v|UK;v5PI?CrtJnV2DDHMpUhR^?vDzk-b7Zn~ zn6LFg4y66aql5lL3rcnYCOh|iyqsR$7S45{1kY{KRX1K3Jbx()kZhA=H&)mx z?&K}FTEo#>+$_Hwf2EFIxy4jnoV}Eiv2ojBz{43l7I1>d$*PuYFmfmgp{4_ z22dB~e2X`d+ouS}W83}B_6Mr%iPd|FHNcUo?};;pqZ`6C@Z;{I@3)Po3&M^zOm~h$ z&+HY?<*eU7vm&(c)~v^G6gGx>*`4JTQDDT0(L%LFv244Tdo^2ZNV9lJ8fosZJaDg@C2$j}cBYwYeme^cel0NW{-?G)IfQCn)e3&9+GnSa-I$|!drQe?* zR0T3gDEb|@n<+D5&8}1$Mlg);$9j=G*k!%4JmGM2g_ghQwA0;TLpUNpy z7%nss=Io^ypI=AGrZ-kA9@AYnpd7dXrF2@eA7QI+?!cefZ%O^dJrE#WSVPS2d}Q6F zV~cf9?F_W0w_rb;;JL_@{?FIs=of*n|;BPHo-4La+GYzv7SG~ASn;6_R(@6B;5FD}Lwj3ZcGA7nnnUy3+ z%XJU`eRC#ec+Sn*e-?kc0&~@PZ*p6^u^}~=-o(IG3Zf#HJ+LpT)xPFYKp#tVNMt2V zRn0c)X{q+-AVpkU0H9AOh{mN%Y9V{pzd(gah7ju zKTb~{o6Dw`TnM6B87yhhvN({a2HpsV-sDY}Qyl&2Rw9+1T+JS%wNz&TD9AAq9y2h& zXGxV_lkdxyaAp=c+}51uHauthceDBKof3xokzJWJI!HxfjH!;1V5*yA!x8K?e?4R3 zTDMRzz3M$*VI}+luP3g%J4J?@UBiL|B*r#QeS4BHlQhFkeSChsSrtwd|#v58ykT^2k%a0F;p zg_(m#nygIcKhKw!EGLYV+tG{+F}4WlXkbahARUsUtYu)H022O|o=yKuDz8w;e#A3%=@*%+A~$M%}LV9xiFdc?fh*=rG+8O{vWg-GY3QU7^Ib#pz}fFwn)`|r1sm`umd*zzj;z{ zWv-%4VF7;j-xh(aML$+ZH-mTy0Gaf6G?{ti(vw$Gn*oUg7Sf&C{J2RMFl~K_SVzSx zfukkxNThb-i7@ll`!Bb#1USW(iO&3*H3?DBrVDAKu}+)OY6w-qe-|^|U5E~ZxGd_F z6J(rY<6I^E{$@#&9F&L+bY?dyrYXh+GFwt2O!4d!{bgCZ2XJ+WQ~SHWqG(aww`+2I zX0{j|&xY5465iW6s*i*>2o!9|VBDaw8oq*1EB%SOZ8AP}Pomg-8*Nz#zq$!#E(LcUc$9NSfV zsW_3LBesb^8zpaZgMKkdS)GrS%mF;5_{=DPb^@blt-Pi>Zbd>P z8AAXj<<@ZT41ZIEA(i{A&Qxug4KFNDT#b7xk3?wSLGGfLNXRlD5y8<;rpAiU89%Q} zUjx1@8`$`Wk=vMpZcoC;DKnr$WaQWqFV5LVw4_m8-fl7;ArGB9MYTpz6U-3Js;IGz z%y7Q1N{PdnwO5#s`+)wwUAkjY!sy)CYI0tx)~#)LT{Ot$F+r4y%bhu{^(Pb~L$)LZ6s^xu}O?w)m zo4kr?#$xgP71>a;vZvHcLqnwZ{;8>N^4gBIW?#||)3)WlvP*{an!S)=IGgPfF_Y|q&kDo4;TC|k4n z4J3`T`woT!2DMiJ&vy*mY^qnDZvwQ1_xPm}e=|EMXoK`CTYcwBoubWaImZ-b|5@tH zdd6}8xiW7v8wBNJ4Zdo4^&7P3S_DOu&*%-@$W3sUt2|jXOVsv_tlUFX&H*>rJ0#l? zVf%c3rFJV2Uj;u>In{qDQ#0UgHF80Vo|}s+bjOG`-RP@<1&N@y+vF|$p%h0Dx}!l; zK1v2nKe%N=bT-a@`d)p;9xnsR1zgpJ1IgSdRyc$^)A?ccs_hx-NhubB!8R zmS-jMQ+~%X3mD!=zx?7;bpJ0+fLUr!YRF(yd}~|T@q4}h&BE;fuH)p#+fCghdE5ZWA=jS@LN}S0ECz*YNCNR98&XXi z?*hlQaMNpwVu(Yy(t}d3%!OKibHp<1OKi_~%#lluA5=Y!KX_QHq-~eO6Z&;l z)04&Gtk^;Ikgg_i0S-TKB!aDq+EydNg)zEx@rJw?MLcWZQ=zh_aM4GZ|D0>Nz_li( zX(tB%fy_EExqFwZx}fwO>ZDdKgq?~4@7RT`2#d(PD!mU72+$u?yhh0Pj@d~Zf|++w0w84NNsb+l4{{|6MLn_6H~j+M=^Hw z3Cwn`WjEH|GPX5W1jkYGK*sQ@wgBZHo!lr-jHg#flf8;xIws)+tpQFqznzZ3=U4ND z#vo_6?MaqvkHr~*V{F@q_Qadsw|9BBa0c96s%ANhiWTM%X#HllOVNl%h=OBv_ne$7ANjEw;4K5T2A0Pm}vMV+a)G$m(6Cs@t(3Ry5CH^nFL=Io)VD`qXI z>^=_Xtdv?IPTuPCYjT*4Ri>rG+O3Ex_DeQudsJp=X0#a{uNcwJ_BUva2*P{nx6san z0CtX5X4Lcwp6IPBe&#$$x~2(zS&!QD;PbO3>An(N(H*jO_YD7qik0Zxj;`UOWa!H$ z3>FH;N$n4=;3x)YEHqiwiM5KDZiH-Z$rGrWuJ*EbKx-6^PcM#k>02gun%zGsrAR(! z)-mQT>>;d_C#pSZC!GTao=a@g51LREw6uM{Z)bJ6|6s-Kiev6nu?+^QhEyn!CdiYX z94gue?PQbt;IP(x(e!kSE`0ECpMj+Yr_h7(4hnpNU)&j54$7J}^A1^mMw;=C0DJ<5 z4C0?tviVo&ZN=#8uP0J;Dwf*?Tf|79n-;<5B$c`9VBY#O?GvX(rx=5>rx62~|K)`; zuAfYHH&0G`pGlfbqGrgU{RXMmEPk-+QiGj9WT&pO50*VAH9~3Ar6#NoW^f!uQKy_4 z<#3Z(oc@TU>3f_{z9?Y4CsUY8h zpWdH=PjvTL$`$gsJ#qE_=nmlbOeL?-$j%e&sJI7S_|~N>T^Su%+zr{fl?j;|$?kOq z^`Iv7_mlZ#76nN4gY^XV8R&4;x$fIxP_r=LoXMa~VB(aV54k)%>!2uvqZViOirg*W zS;pAHO{3Jj#4z~43hSsM%b$6*ECRMJ77dD@7Gk4=8Sxe1k!3(xuqWNlv$Zo z+N*(U5lCKn>9kq2V(0R9;R;oA@C~_gC+;~8)~$s58VdL$;kMg?@Op*fOS|C7y-Qh> zbKnC18VA|?6Kc9?f4x96WeOjJy2>1hf%-1)6WcZQ4gLY}%zX)F@tAWDo-I^WDJZmD z@SO7Bi^o3J#LB|o2H(P;{1O0!NjzMuVG8=sG!#zz4Z1lx` z;9}%oNcdpssL495jTB(O3Ps3;{;f8X@l>f><2r=!`8P@srGt5Jvmxai7izZu8sbe& zO-6d&OkQ4cK6G#E!ev4!vRCeap{6^*?+|MMJ7H&GYe(fc(Zol>FBA3iOy>d6c7(VR zH!%~8w9F>pPH%w(y?zun@){{jO4swoIWC|hxT&dUqP6uc8>N+VNKJALx((;80Ow+s zVk9U(!t@=CEuValJK2p&Ez=0cW>3kfT^<^`rQn2k^Q0O~4dSrFmD*aRR5B$%>4b8c zlFwBB)=MdvxMfC9d(2;zw2^e|V}^W?$Qrg4V6#$GEXxeGh0=wo1VRWQ{3``h3$Kkt z)t*|#g;tF2Mal#7tnvA4D4D=j+o^nZpqNe#+VbII+;r@4@FV#WD#m0(b>W$dTtH1~=3$&c5Ghc5TaHERwZefl91kJ3SoZW#%r2i&WBvU&-{w1 zrz?)lD;-#mT)*yrs9jmTQiEE4MXuL9RRcfhuT5|_yFOj7QE)v&ztUG-a@747ijyD8 z9+xh*2}8h|P}Oi~^5;8T8k|iIS23bsxQQVPu`sxl2w4{iFEgYDrSZbeSV%k-NU{*A zxx9H%e+s;H<8nC8LX8kioZ_qr4HFjX5R<>Gp>FG3I(C{TW0asIKF{_+rh*eA3TX|k#l5N(<*Zq3 z?@DIqV8egRyRY-vth0t49m@Gdb85_tDboyABz|fPh@Nt;l8Ea<`OtRtuO=FCk!QEx zuAHgQCgd}`^!lhLK6*r1;{$2`MQvExkLaJ0&Xfr^q2YN?6*9@VnZ7E(&k1Vx@&p!&(lCM9g|a0kDO$ z5=(|`HnPHh%hkFW;?*-yiNgpEHkn+WBt)w?E3p(*h%g|7pM+ZU`1d}nb6fe>Ri|v& zPL!|Y%ge-o435#m7y;%mwoHWuz^(}EQ+ysf-#@W06*C0&EhKGwF`XDUC_R7`U5+AH zAmWVZ&}Gb}Qt3H9iffsBp~?Ue!Pp&1q3o3g9_en=S!WwKuHcD{?sCr!D(9Q8*Cc5b zt?Bu%h}1UrGK`39DbK3i zn(3T4lNNA3rmo>dJ_c4*l^IbD9c9*;M@BEALAN+JOcSvT9XMa<4@JzTC#4xaTR&IY zh%%wvG%K+WIw8Iw4l&vh!ls>yjt=V3DHP2O@@NM+q4Nz2AtP*Cc=E6!48V}=a}*|8 z@}`!ZNT;(W9f&I~j}--LnMhPrvKj|iBL9Dcy<>1~?Y6ERCo{G&lg!w*ZQHhO+qP|6 zGq!Epc5)TL1nBSjf7Q&I)+69j zOXC!hukbb`mvg-bi6wzEXm|8lIgmjjP=R~aqvpDj;mk%AkUcOVrdeSYk@za<^;*s| z$YGkyEQRxl6@k%Sy9ciuiE!hK#L=Zqh`CZ39rhBKDv#w@J~o(3kx*k*($u8BG&-ka`Ub17Cc6OZrqCYrR9m<_;NZHI$bHH}6lg$9vBs^?=h(BhcO{257TXqVaI5{8~!ZGJ09Ok>`Oo-xZ^PVPCRgG@^%E`YeYm=?Nv1{!-n3 zQW=Jr6?RQFxgmxKL{XGRx|vz0zkIJ13Hrvc*!Gy( zM_T4!Lo2LgIeUFl`1#m2QZ}Cn8*^bgqH7j@v6Hq2^CU978J`_KLfBH9+r716Ji1$! z&xy3uUk#~i`Vly7Z!WW@CF3TcFBg;E}R3XVk)-|$7`hL+1*=L#;kY|VU>c2rGP0?k3x?MDOP|oE!A|= zg|(+P;JL}CtQom!MAbW?`gf+ctu=nH1C_3j{zn=wA5TNfO3w3JozK*A&?nMRDow-$ z4Z5(7GRju>hL97(Jg)R;ySg$F#-WjRa=c|nQf-GAiN}F5k#neeS6<%4u*}n{Mb0S< z-R6SyhG5L6;0>{T$r7Rtchqq(P)WX^xC;g{kU+=$BYSgK* zDW7J#Sl)AU!EPr4OuqEUW*(Cz1edj!ZaEAgS!q_yTq0R%SxsESS!pmiTqT*$0aFvn z>)ER~cGZ5SEYr8d)n6ckos@Mvq%aq&(IRZE5@T&Z~c&Yy`4yd00##m4A!5#JYAwKA%*z`m{Z~evE z0=k^EQnRJtHaDn48y_LlAyNMIjLR7mUi~&K%k58w17oIW9fz7iC>hal`lmc_^O~4j z5`{D940W1p!Ey1Oi!gO*&s=ifAGP1+MaxX`8O!YD(>lu-x*&g$HAqd%+W@2?2b=r7 zbzABNH70grKbQF=0fs|_Z!Xp2;KG6(Jb3!6aL@wp`RsNd*^A0KABy1?V?l&- zy$on3mRL$bOh9=)w85-ah;ddP)0B!^+Grz;!WON0kCu*GD6yjs%11ECE4=&-TV_)t zVOs=gOQxLg;6~^Q`|xbqYzq7ah0~Pc{&$<@U&Rf-nBL$1F)J>2W@OYyZT&Z~>d)MO zHRMZj?cAGEA^Fot?+FJQf?_-(TRFuQiu(-*&uW+k%yd`ySs8bgfAQ|CLgcAP4!aKI z3UM0u^>q%%Cf%gGFwvAJWej>1R8t$YmJGk-65237s}oce19uy0nC%Ip1`Dd4@~AF# z=ybco$vFjGzA&}Uex^QHoy&x(lhMjHK6b$-SIQQd8cBH=IVIKMuI*cGN-(GjSN)wy z;`X7}q%O1fg1O^jp;yWels~E-?g)A&2c=Djvz!BAJLiJl)f+<=Os9Uk31=bJsKb(( z!fuTLo@a<;#~GZA7+utoh-O=xQv}zHIo0N(JTa}i3yQ(*p*vV3k934#vZx4-@a0Ks z&ppy+JFihQ&3pw+3Uy6ymHi7Nl3PE5#?}ZGS94BpmeF#|k1Nb+`o7KGxZMUD=z{uP zWM$_#XOIfVj@0#GY_^seQYs3~xjoeN`=6g!bTU;JnA()y7T2{8_EXjs4_4LHDGTs2 z#SXFe9Gg{g`jbIZPAjOU4Hfm1j#DO=*r_GVO}F+-NXOg>t|CY-LO6Q`wU}e_ozp5n z9X}QbdxJz_xF-KBbwJ0Qo^73G@cr^T z?hy(37Gb}7J`JbYleqY{xRAW;e15Y{yY9cxBZP=vjeT&pYejRH8gz-`-Rjq zh(R`VE+5WPh}aW*`cgE}pCKR8VhFmGJbq)bDw*#5A?&>CG8g3%MAYlt{w~L-MvkG1 z->!rP^aAvXwo9k^_cxAyDCdamHv?FC#NEW-6!dEA_8r*ZYj~Lt-1Q5Rn)a;y_H!e; zZp)6VN!P62Pk`I0*o<}uIwBt43H-gTNzZRIbmA?JN)Vq>_}d$5y5La(=#d%(u}u%u z@9rtv(ZC~ieH6Q=g=tQ|qJp>Q)9Th8)E2KAGmLUgTDOZef{$+)Y!5hP;EEB~N<{rv zNL{l{h06Wcx{dw1u`ne$0E?V}M^W#}y<1g#&IM&8<1-Hg4_*%Gm}B62_=Uun3~!)2zyRY%#XAp5>U zJH+;JMVZhK0$oN56{XATH2F;q3NN)w32t(Rt4l+UB+(Wkq1Z2?ACR#fkWEAH)sA;G=0@)XkjPea3=h?{r&qSqMv0j>Hl?4|B7RWK8-uxb z0c%N3FoWNRBc;vRTV~2TD?IF=cb;ihJYmn<6f1cwls((5K7I)}ciGt3?35G2P|;~% zHOdy(NtrausGLo_)oW3WH|G}2p8n$AJb3`&cvw-VvagKHs$|WqW`~*A)+r()j>=kn zoMPmjI6b>~J7a8T)k@IPNf@G*UN&A{F1|&OUN%}@F0$h)*<5|RV)XAfAv_~72+CNC zO|q83-0U}*W{f1ah`mG@;7@zV7k=AG@t!acu1V-pC4a9K@0&OWHcf$OmO$?K6U=1~ z{3HfsC$=YyF<@1X=Q0ksK?r^e$`)8)kAxeqaTB@`u3(Rq9k+h#zY&IZNZm;pa!c`Q zSbb}DJg?pEN!Q%K{lS%^X%0WEYUH|DjokP3Zr1@yzW088RG&G>gad){cwz1yEH|ge zgWW=PWo%cUX(6q^G0p|OKh2Cr&x0af=wpoU8{P7`+sZ1{S3Z>ToV&1>}_b3ynQtTt5r8{j?sXB*U04-de9*Yaq{ zN%=rONY35=)h+qoO&c*|MLh?LKSGucMqSNgjqLJ+nhSPqQrNfHH{=O_&|yjp&<^JLwY>uX|F}<2fHJSEc``nYReduKo)s=qX>p7jYVo4+ndHX zM5nit>30vUWiB)Vin{@e4yiP4qxu2cq7qodISoTeom6DnLVV`OL~|9)Pae{d0G(wd z!d~7t6Njf}C_3fJ)EisHF0TodG2W-MGfs`qr8IqXMevN%72R8?Zda;%T!|Ih#vZ^% zQgmaYi3{R?vs;cl26@)rc`0_tF`j(RguR|#VNn%O#VN0Mqi*HL{=@Rv=yah@AdkXV zY(%lQiuq2`e!;;aDmWzWi&-402olUSSTwg5pcQVyTlB%AFr{Tb^KYZ7;B49z7kw;H zb&p9she1*^Ab)$r5=ZZp`cV*r z%~zQg$>$1LYHx;|_y3D22oSoNdQe!P~-i`|!Mpa%0$v44)#|a#vw`1uet>xgE zAq0-uRjFb8cv2h4JXp2yamVUQO}6KYDI2cbN8rcXdc7vsq7WUooEruc`s5&lKNyVw z>57{6%Ow`3eu(sVm?{xu&T?qE!YS)s$GQqsfV?pZ+x#tn#`7Sf*C45b11A^?U`1}& z$c>5S?x?vz4YN7<3XCqwKJd7Bkw@PYToo|%>{DIjS$Lj<4A51WahlBgcAc`p zE(~T5#j~YU79*G zL%vdlu0mI)AD-tta{GEi8Nhwf-pP29+CA+R>`FVlDK_tuHg#I5LXLjaX*Ov`c*8l& zBFRDv)INEyww=0Dlh}r}Qas-;J6zN&+eGFu+G&Y-_}4ko%oby(!=$uG}!jmbA|*c)vqYSN4}lJpHgOLDPHXuirn=M-W;5 ziNY&T%a~v82spFk?GcwsEZQiIPaG_&z%nfDfz1}I`yptqX`c#I^KS3!MC|N0DYt}nN9+#_;wtIcn4}vOIvl)OyKD(u4Qx+J7UUvs^U6acFuK zF6Mp7)?^Z*Mj{q+psC8v;5mvlXou`mO5f@YT5d=nHmO9t0}himbvN^AOfuSAy8pJ= zB9%Q!)%3?|P~XL4JeiC)qb+1n>p(1VS!g%Zt}D?PDVhkV41UDK(DRUomt6oJlenD_eo5kY zljE~8}slNl&!3K&wrgCkF9!k_|Gzue~rjP^EkJ(Cv?zK~-(7RwVh3S;w zG%3}XpM~hP5BM-hgHTzn5lYo2(=SBR4oIm}q>>IDg_Xx1AHupJ^Tza*kqzO*f)$B# zG{q7cvb_w&Bc<>ii||t{$9)a*TbBouo*;w|U$T!-Z0|l!?;#-XUIOpIiUALto_&}P zzRSCk3E5!%9Fc%ZTNrBDppKj0Mh+mywwNXmZjivPp{{aq1+)oSq&s??{B9S-;(1)c z@mzEB+7~HYgQV}jHUb9zm!hEXMKyQvXFP-XbK5ijL)Tu$$x+5w@LzfY0UOJIp|@r> z)4xIQuSt%^Uh7?ALT!Ei9gQmAh-~OMPi?RE*5Za$*6OBwWP_| zRpn0490}hH-cAhS^`E>-F9fuwsZ57yE{Eg&DaWA^R(QLo?Wdv*janNkj^BuF#Ju zWzv@vU^UA*w2A@_lo-tUisLSWtCGeBbP59?abkh#p_ERpLdutB=?skm1&NZD<`1%H zCeT#^RYs`famVp@(*!>Q9~suB40Njo$DyROl3#cz&W?BLs%_6LgHXXuZJJk6(9Is7 zaI+$}VzxvNnZpOU$AxA?#UqHX#7>iqG!w9yjX@QyL$z*gkkZE#@w}06i`sANCeX9MaJ>7A6 zQE)2r#GbtdH_6olmnB-ITnhqOe%U11^71&82-szyOA{83i{U55t64 z6#=l=dECxi?Oc_!7CLKI?|<_eoq=j|R)HP zfy{)!6}SS`2qO$TB28)=@h{I+0W3^)B~;eN*ZP%a7~EnM(J&$ z?M4}L0tMV;L?Jy!E=>^+en7TNTZC~~5a)=#BsC?cHooNVt%WR1lAGa9oGK=nn>RQO zt1CObwPg2lc4tkPG#)}>gj-8KKTT#S%OJwgMTg|y%G||eE!6c{ir1dYl8WcwYUUgs zA(HCQJxC}ZvT<}4eLfJoW9c?^bC_eeb9^xCr|ZQ79*TcrCJT)-ZK%=ib$8`+ZL&^VaUjsF_$@f%bt;eUkE6 zSf&wl!20tq56D<8Y)PpOZGy8nr7YG|p!Me(WJc*5F+jD{ey)y3H!dEMglww}YLUbT z)Z?NI&I%6)NAacyg5eG4?zoznNE?aXrx$e>?1We-NPVYa`eYKE5=NP+c_=rwYmDZ> zzs6yPBB+<+_9M2KEBUD?{<-i6xn`}21*O^33W%`S?1Dy)mPk#t(QwRUs3(?=8`NxW zZta=%|KKOgLzNHfVt?)K= zWs##V4|QN^Uu4}zb$GAG zLpcJ5g;51_G>Q{ho!{Tj<-f14pKJtI*#{+LRBeDdR9ne=PynwXY?n%hBTQ3IpLUs{ zF9cp`7lMho6++Dd2nNqCCruAz5;_4@wihSfZzSvJMtz>U%M?)NMrcp4b{XMAT>WB1 z+CcrNYoV8S$gKkw!$iTpdxo7Sl!^*m8vY<@3p1A4V;wCqO(KNh+us_16wE({1q%C% z1EN++$^YvSwy)=rD(iO})I(zgF=H61`T%OIRhZHON;wh3^3e^wJgaw`M+GpOBqlB?bH~WSnk{s!Cb9 zf*nPa5mQi6EMPgLED4AZYrh4na3-(djDj_TCUc}suo^1(p$%M#7036ThMhIgW&0H zf%65(HFXzC0}_ghd^?O7+FGNFqpT+l(JFJN%0+QHsXQXaZGmIOj&^^M?-Azp#j)FS zz{4k>O7&{{6{&e{!2!PVdcqTUkvbV{krY=*J3An3x+wXm3LL7s<}ho>0m!V&NOMtk(|od$yC33vb~jcg$&pyaQWMli0k> zoI$Bh-seyso~;_%g&>|?A;YjV1b4^~G5YC;6Ghi&)4?|!E^l(@i@(_&E5i{nAzore z;ygvQSyzUYh$i3-6jOwh+I$VJNoSVndtOo2N?XHdgb`MO7etY-i*s^S$*_?~^3|yB za}$7Ol_Ip&(zr!^*4Oe3B*pl5TMK?L0-z6&0KnI)5A5{Y;twr5wEA-f%+hEV`S;AgbJywTe64gDp;I zw9-`X;_b125p6xSVF(0%^^h$~hkjkRg^Q(wmML&>vyeCMeu|@bMT>ZGRmXrU!Q$;u zaEFk8iS=x@W)q|mZ0Ku+X!yxPRc!30FFCf}clX*ypuMEi==9Ee&&xr?4v3L{gZ_8< zr~qmsQTmggUHm_cQ~u}Lm2&&h$JrYFf1>&L$$xP)!`~fZmiiMac{+{1@$E!2o8JL` zQwvs+vw?<%zYkIB#oeku6(iEjYIvB-bOMOxigP^xx{@xe4n#rnQ>ENaTwK_EF`S=g z8D0G6>~$&yp_L@zLOc@4id+mc2hP+R2XjJp2q8y zyR0R+RhN3aaE~k2WsTJ`1$^}7zGP2Zg`B96tc7>Z={ zL0-ak5T2Y|O^laS2&8Bza%hsu1{tw#3o#!0WV(POoBw|5leWdn_6Ps~fu#QoP5u8* z{omP7R4OJ|Ks=cX!ai=Xc+>!NRm>&Yx|hChrw zI^D;wU&o%u95o!%J`bA?h9#KY{Q?~)Xxfb5_ys!R_c_{YQZ{2JR4kw3Z#ok9SZH0B zL^XBy(Tv|jlQu=KS&Y}>_a0_j2}f{qA1xVQq@UlS_uY(mqW2_dUMEF$U`yZP_deQh zK5q>X+;?-JU@2k~x{?TkvBl!0#^OluZRJK(p&oWp_|tppCvoS7>>94iO2Z+^INhurOcS&P7BSZVWO45@1R|IrZM5&T-l}q zKSE>9E^~buPK-E#&fw9-qKLG{-|mf~jm{K>VI%-@rKk4JA&t_pl2cUCOWRe%7A*e4 z(tq~3c8s%0!=Ftzxzr=NgWb%AG^14+i-jpM9js?u1!hZyWHZyzJF@@05b?uf80_;? z5TK(4%{xspc90dDH;p>SwNv2+WIrtk@8iTZuN~o5wRs7NDwG&=!d>lKHQ<8p1hcCv zMwM|&>8OOnXQ`XYC~E!bJ4u;0Lza-YSc_kCqK+OQP$x|2GCxu#9=>x$uVKt5LsVdS zaJ-a5$^ezuCf-5~w}v4ipl3nDQ8ximDaU>jk1ZoDGasM7qN_1SPakO<6C%+6JjCm- zT1O;j2K&@gU!0Igi{lG6n4-StwMCZ%-;`qERa!S?6Lwo1%*JPti@zgIUTme?G6SXFI}T<)LvcK1hc5#>Xj?AquKXbCzPwZ9+S|SdO)}@)Pp=4MWi*Ga!g)`H|i~@ z|EZiv2k+)6OGw95C3welJ0#kp2Z z@Qoo1s59I|ZNyO!+;8!9zzS&m`q@56&{d9Gq3OXdW^f>xiaw~1iN&v#e13qS3CCAK zZ+b`v&0PU~uMfe=PNg4aQ%>vMN@jZbD@+7;w*|B#H-{O7B1^K9k&Uzx0h6WFoV86| zD6(v9*NrWaz}yMbBu~dc_hcDoR&kz-LN}UhbvgB1_gemap&1uuA^Rm)jb5sJ%rku= z%{K9O9h-INU&aP<6Uv;l`(GZnG4E>$1hm(Bm<|Z5_1RC(pD>*4SpBo8M+Wqcowa3h zXf?hI^g0+y9lBYx?|IVFQiLgzsOAC7BqdYT6$a_tk8wc%@uapLA6Uiv(6= zsqTJe=%|v}l5C-jBC*cU;HNdqy4x4XJ#Br{>+x4(s1a&?e0f->#6jAHDah?=!g6Qw zCkc+K_(^L-&&2MEK?DnBGur%F%JEw4WXJ`r{F{w3d4Y^#d5eXoam6a>pK}igmI0rT zkK-5aX54%%q>jF_5YZEMIzh1Eq9G^5Xf=wiC^f>m=-$DOw~LUFJx#4}H43d^%M=Uv z^pcqT5t2Z_Ty_OH*x%)eBzzsbS0ZUfJj*#**h za1}lyIwqlKPGU$tA`+Bitsg}6)i-+30e^4d1|vt^DZ8|_s_jO3!fdSjOFLIs#XWiZ zKpZQKw#?RGO4Cd1O65g! z2icrITgN;P^zp7mJGp0O%esPJJKj4jYpt#Tq; zAH$-#kdH`sXnAJ!$z92_4$)5BKUDI6OHPw*Oui#a+sf-jc_6Ycsw-r zebRb#XY=IUzc(^BG2g=v!d=98Eavcq2j1g ze-RVpSua!FDE8ZknN{h%o?e%6S)2w6TVuL=$zI{Y;=6)PT!c&Zv?l8{F-mjk(AphD z)_NYeMG<>h(h=9R`T72?sE1&8JG|_2{vr=c@o~6u8?C@OR!ZdlR0z zo$#V+h-(N1L0xwn*K`evMI|0IeZBbQ3=fl`7r=-}%txYOX)EJ6yoq(uXk&yk()@6# zzoq;cVxEXo=`%uu$Y8}YY4{^EVU;7AlC&Lx$gCckrJC^rtP04N~0)D&;pIb$2})BM3NR+$7s z+vLtFlhPYTvU;6AQgw`kg=BHXis|%b!r~0%vdZ4+2yFxz*DL_ksMN)n9(vY zmu0(w;7)EfLLC{x1yzLLRRnNaQiP^3QTa^=1x}1f>88Y0C2Ea+N3sl5g;9-x)p$j*JxzkxQgVgQw&Lm!tC*gU3?NQOWChb{Qw2cB1_(3pT zB(hw*NOoa~y4qfZ;?|^EyyVdQNP@!PCpG~6jwHD-tx~ehvPzjnVpV>CcR6{ZQhp&~ zfNzOs3aHL>ClE)!w|NV6!N@)!k#qpRr)Nx@K6;8eBtL`|G);2f7?q62-D4qjA;~C9 zh$<9Vw0he)Sd@jBmk@L}v%xF22ii$kk~y%>t#vmOz$x_gy`s5&5Ky596QZ23Ri6)g z5nYfgX;f7+BrEIfM9OGo0StJ4jiVLF>?ZdI?=S&yu7QU)K9}baE4!L8f)*SSn(bY*G43Uoyy}9UqQowsOYCDVZXxKNj+OnOjbcjz_3%TUDT>TrpbxLXNIRstAV%{jO^hh@kC^|1YbC~sX?AbXK0tDM?t_zHPg@3%P^U{zO_N}AB6-th zO=}k^JVFgNgZjQFQtIBd%L7)CJ0TAxja?CM?!*tv&DKYia zMwe6`+^-aM-WCYGj2-PpF@JtnZn2_ zgc}BA6b0;91aWxBmP3>>y|`1!_KdEzxYIoSoaiC=B*yL1MsnhSsd6)d0%mG9t_RoJ zg>O5K$!UCMbF+4gmW(!5Mw|nF40(=MbjWNj>pA9@Zjhs+sC}SNtARp00|`oX;_#Yy zm;VN+ZWpYKsX_+yQV{|*%k6mA$G>5A;D!tduNn-<_{7j??yGFTNZ!OSwowN&vxpug zdKnp;b-R={w;)}1QQM>tzeYwkooZ@SmPLOb*|tAxBy3HJQo|;reTtjMqV^v* z8Ibhi5-{6pqelG3%=A}~VsuDhbacKnv}E2CRu)A?fooX~czGT&TVVM{)BMnFA>=ix zGu3*R=)ugou)veZE2<{H#^A$BTS(^4+}&?^2F{JDYw-L8$2#A|4Y+H1vi3Sk)6bi; z#g@qC=(o-8YiE+m4YPLtvVKw0^R2YB{zgofz{ZdW>%?$dLQfw8?hdtgq`?Ow$qSQg z*W~*v*Hr`C3z!BzUaizYhSmH_tQFX{e#NndmC6mmI%4-`v;TE%(~XJavWLHL{zE$7 zc0M?KQF5;18#6f}czMFFw^9J5*qm;GlG-PHr85iMjXW^28mKijzWdl0X?*0x{K$k& zes@3$`9tIxxZQ3zz}2u;Pvp^Cz=Qhd-GdUlJ{YR&LO^!l&fjC*BC^IFbnj^HiQN=_ zea(Z=5X>4-S&I#g^GlN7fsqhc%~1l%oJAtp!*)=Fh+M$g(hn4uQTnrsd*Z=q`Uz=M{w zAQyM}kSrz+PVjKWW=Q{@i)17yeOAvV(>BrBKm@Ypc4gBP8bc5(M<|hHo^O>&6nJGz zafv`oj{#HGl3yR7&cL@h6*02qkFJ0}BbIrf5PS~+)*Cm_c!XjfPj1}@z0{FPX}pi% z;s-g^E?$4MH$TTqiDRmdbj$6zBigrr^!?AW7?4OTjU`8Z7mXzmeKvimVJVCH(dq5t z>W|bgjSY(}6Rv@c_PilUQE-b1PsQaO--Y$)c!Jj|pq7oJ{>vk=bb8e4Ca*Z~1k*vX^qIsPxMs)J`!!f!wQ4-xi0yqV39yhFPL1m5iBPZNBt2->+{9ppgefk?OU z9#mbM65ruA=E6d^Y9JNY=k6YH+Vn8be@cZkpytf)C8MM!7FJ;8VolADN1BV%qB&`dGQGtIH)IWsm9F)@aa+cCGEfCVRjknI(y^3YWIX=sC#dEJtq{6aFk z1#9xr@+_om5<=_?E2qk6mWp2G0lSKUzf{4U=4DQ{?Ri&Zw7>>r6DYoDWg-`jgb?0X=Aa?VHzFL*lRJ^gGEl89OEje)u7P96dGQ?t5Q$BP)`uaXQIE5$Lz(7t&)V8U{7`ssLFG;>#ghZt4f-_VuQ-1#@z z&Y+d;*j2nZM3%dVLVQbMOo|G7Mho_1xK8a`hhJ5ofy_U>eMIK^>dYO21aqY zi&S=y^iMcaPk}>*aGHuD21>8XoOl2tncdIi(s-^NX3jU6C0jU{jqiR-hLpA34nd5N z?)dD@k6-`Y?6YkVxAgyM?+*V^LS+9CH3}|f|8m9__>mg^CuS5S6y{~oxyX=>tow^#hCtFa26FPTobs^LeDkMTZ8hHuu&1RMlyC`~ z9c-?i*|*uYrQYwCTdqg+Azj$x1ZIPS;<=L^&F1m1!oe$z?7K>qiul^ zmNvr8U-YcDc(oTlT{G1RDDo7oJ9oon-Q-4G7h&?#uiKK2yYJ2>E-O1Bl(fjV4K}%W z9v1Wc>h{!vikN_pu*&R0|FHf!gQq=J*`Tl6%yuz2#OCmp#@e| zAqBA|+)1WRSgI>VnhBH5tBO2pUFF-2jYhRjnNS$&g{;=$oVnVHK1d^}Cgck@tiykE zgWZ(2lW{TWxaYs!BJ@y2u62f^C>U}gn7V830LbLL-mF3K^~mz=RDe4D!j=czBD~cFsx0+kiA|lut!VA>2Q?+> z?lGrd0!&D@Tnul%XevFiC~|HVRWrE_FfFL-M$kk*D{DC*3n6ngtkzH>#asTi^V>0fS+Vqm@lN0c z;rBfOGUun28yK~;q1SUZPD_IkRWr{kQc~xBQG$fyR#opdPXZZN1muPwD)JHI;$i?% z^*_+-@MwR}_PB04p1P>=@$LvGwhYPX6^<;lr42wAPM?huuxHKeGsUDaj2(!$EBwO} zQ)s8ijV-ZT1g(B!k6APFSqD92rwNNhmN}9OOla0gemu%peDIO}aM#|*Mh3Vgx~~Xn zQ=Cf?3U{6@n;X5>Y#Tzgc^_Evo32K~$=XE1;jL4<(b1!r^{Atmi(y1S0FGKH#A&-R z9I5XHCncm}EI)sliYJgN5!blK5B8QE*Nt~1~IT}bCy`aW9)8_DFX?@4Mi->hN) zr-@6?qu6F@UNcQTcEf6|n5Qt8Y?}jr-)W4~j4G{}RlPjiFc$o5SL5h%&2WfK6|~>vTc3_7xv2!_d7^91}rq z)Vxtv@=I8(l!VfrU<@CD?z+=|VuVd#!E?S2Ltzc+OVG4|*@tJ6ewnfcn#CS*T4uB% zau;$F4gYP)^BNM^+TQ4p-o4H9cV&YAf|dX_BT;V!{|gSiH%b>Nsi}ox zP&lro{4F}J%1lFL#i!FeIZd2Es4x2DLu2`=TcH3yTzP76o<2h}sVUEysUI{`jdq1v zF=+*90N`V#FE|Ry1gUDz!JtuaF~ykrpdIiE4tX;{PG2P?k^{MPzx>RqYT@UjxKJzP zl2EUyQWiDD{xl@jOh9oyB=JJQsk2%G4%Hc~mGX0=8NTIY#jpNKTJLtp!U{}C`gI0! z&058b5T|wLkCQD8^`s@^Nkj=zIR@*G?@pA1WFzlPq2glB`%Vb<$T6#8+ltC(ZXI%m z#%F3P+>C4kA@Cw!XaU!);;%1n(2hF_M4fB<>-gp&F7Yv!=w)+H4A4341f3=7(eTsc zrt{*W}3QHbj73wr`irql*k zOk5Ty&B_%cXI-0No^9RWNRJI)ftD`M(!);=`V5}=30nWEW1^RU{=?R+CFnt2=j*I3 zHb#~Pg}NGHIyp%61}A#7R|J=yi1+>3eNEgf8Y|>TyUMiM1a*=qn^~`gjd~L*c&)*E z1q-Eca=<#gAzgrjicxL9-7B5hOYOX%{le<+=1noJk9a(??B@Kke$npIdQUwJ^#Xmp zFR|4X2zN`x-FN+h?z5BNJKHbF?JqFk;SU{+bS?HK3SR&xW0y4WTYA@w6G?Gjc;G|Y zGsr|u>j{#3(1A0`jz}pN#0fW7N%a^%d%*A!$}1(VH9>`VPVf`MbAy?2Z!S zKIUkTH=3&m$d=@>hgg=q%-cKZ^~_}0h*S!4f0U>o77#*Q%tRJ;JtNqcA(Qp&qK@PJ zv({C6vi)g(2i0?HgdQs|8vxY~|87sdHD~zK_y5W)*;**eP=0cRv7gM6@E>vnMN@kl zm;bz36~?XSd4HI^6a@naEBxI>m9^q91@^zF3!qABLcY0YTXiH;$jeBpNDw)FXtl!r z1a&)gr~Wbg8+On_1i52(8zNUf3-vgrp7Cr*#DzUh`T}#g1Bo46^gT5; zlgX8p(hV{2`|us941?JOl}joS!&C+*Mv+p#yg?xEp0eM(gTcee4U|fMpR>Y96}sU( zHMrL?3i|oWj-h9V!>0z<;(<;XkQRBuc$6YuCZhPBKq!-vP{WzWzl*TMSHe-b{(#=x zk$oOWbI22{(RZ-ZEu|c)cbqxM&z8t&PRX7~K7^`3#dWm#+&3U`P zHr&l%0|{A1);$98+(}06+*{xkrhK!8)Ca#SiD8J{4v-r2fyPol#PvEiqQj@w=J*0e zv#3OR114PDaQs1gJhu89uKv}Dfg5F9%mCCF6A%>pyPhHduAk!xI+=1ir9A%fO;Lp-`T}E&HdpfKu?Tij2CeL zC|fUp`x_-QYxrI)7*DAI>-eMddiw^_*I>qN#v3Z0ZX8}H>$uk{?+e&moED5$tv@yJ zUM->yA(B0WUE>$~!%gBQ7p zKH)y?BkRB)vL3xu{7cr}^7EVY+w0E$j&BJ3!u3M{EOV$o# zF?L~HinHyHx-Q$=_Z0^qrQvfJ4Qr7|ctRmgt&S#2d46(SxNa3?Cyxk)1dHsrg3?h@ zR@O}eJYR>dW3Y_=MwFNuotxGzBu#TkD65VV$`DUTnpEeYa4X$fsx2ML^8;inrc>2+ z|6R`IoDRseHzhbUhWI#Jq##xzfz9r9R2cxcObG+SkkZClo|lceQTRJ&o;Bv2%p=md zYxUs8_z8+UI+7Aa~>ya$SQnyER3Bc4emO(=6qR( z!uVVopm1Fruz1PxTbAnd+h#CmYO~&hb5%%hk^GR0=5WoJIB%FU8-_`U+La5@R@OHx zLR#cg?A?E(Jm07+m6UMZL_ zj~Kw@SPsOO$L3tN*P4!Cyd;z#Q_W#k#6??Oo?B@SF|EWRjwsmf7>%f$3~v~`yL*xOV}sYn_YBdtC`1ZQe+32xnF%&c49jR{*P zz@rK2sDU2iagTaegYH>kQ7vM^A>v)D#FGIQ*l4Lj7%%=8<)MlJ3jj~_02wO^ru2R# z>|^G$;-u4;YUpC>q5NZoxI;H=`7v1uZcH< zXSx!04UV$H7G=u)?$J(9R3CN1DY;Kb{v*>dL!5O$p6>Yo2I_XaRzqRLaz&orLa&~g zG5Fb8YDNhj%~W7|f@wr$(CZQHhOCl#w>+qUgL-_^VKd-vV< z#$Q!u)TuFQ*Qlzo*4b;%HTRt96oWO7&5Isz(L?HMkk>9}SU;VLxH0y&XPeQ3!}FH= zn1jG1b?IhV!O)HFiK>}%(NSsd+sUd^ZD`U`W`r*n%VtQTITQEi)cqZynOv7^qPc-= z6uf}^HcZz-+z1_4GW$Hyol!zS897y%su1F{AUDB^ri^rNRf8`eOY(3hi_ZD9JdUS#4~Z{zY9Yvf%4%mFt0j&7uu(U&{jj zVi&_0X@1h3)>x@du(GkR<0u91bm?UY6NYFMWs<`26dYolN189mQtjvYC|f((l$GU(@HpII>@Im|gpF(%L{s ziXMJsU3`SrUsQq^zi9*1E!&Bo^fFy|wJO^!SZj`g*g~kxyn!ap{b_!Istc#1Na@|o zY5qz=ji#bOsx`F5RTn9qZeDCCr*8IvU47E)6|KAoB*$7P2uU3*a4G0g0Nc^RvAW$} zldHEdJDFyW_S$GKS;M>sd*Cwt?OxVw4V#|gQjUabJ6^1%jpN~hyDE|oip3GM=MEVA zb~fkB!78Y&86Yg6LZydPWe(c&c*nQ-tHW!U5@W6n-<>_o?~F|kH6!YX&_7Faa&sXN zS2k0jb(WZW!pv_XPo3x!gD=6J;Z3?joeuka|I}Z7|2y z5!Zt7&ammrHNLdLit&^i@YSiZ%bmRbrY4DNnB*SH@~$Vp=Z&y$XKX?vrm~HlK|q{C z^iga}`M9U;EzIK!vhEdp(eSS78I9`;ehs5N;Ngn?wKMwWs&u!*bny#N9dr|nvgb;? zIWOI-v5EnxdbX!LX-f`ep39%K@0P3^5TN*@L`dTlJYUTh1y<*k2|L_BK-Ixrm8~Kq z?5)lzv!IG=^++&6Vf-sXTvD@)y3eQ_IH#DYz8#v5#0)Tk!6In!r`uufG<HR zmRuS3V$Uw}ilEufb-w;ZVtv1MSP9-*IyFvNxCK$tFVZ|(m44en4 zUE;S9g+{`3Sduen8=>^X;}P!p*NDrjj_22>R#0Atf1XC?pntECT=hQ16QGmI*MoE-*~KP(K!gsuC~&n?pRtqZp}iQoqV9LV4q*^VbMP zfa9*=$h#PWK#{iG#BTux(}kF#^sv-LS)1S)Tg8_vAj1Dtwx^6N?Vsdi2 z3BtV-l^XFfcwxMxj#bR2DyHiw{hH8Z>o7g`>s1E1&sFSxfEPW*La|rs%J#w|i(x6z z7H*>n@JqT~S{ck9LRAe~3{eZTK$n!K1e&ld@C84^kJSyp2{2}%n}i~-L-J)rB=--S z&n2u=e!)0P_2aN`N^BlQ($N{cK$-W#9nRNYq%Wa2HbnP#`ddA7lWy_O?C?L;oCXt0 zF6A{Qv|lJwE6{kj__x;Y=*Uph#*$1Nh zYAg`wwS)dPeyrViD0kZn)|0*g*lMj!&4G`?E>U^lcLeJpA!W|nLK0%A&lGPr1~af$ z>|jk&l9{6=h0;Nz+RF74YcAFzV?%BDR8T4AhCtr)`gsy~wfQ?IqTPRa3Isa}*z?sw zcAuURdp8VW>!C9Xoa+2Vr!TG~6atjRvLIgD{1>T0~Y- zyhKm3yr@X(xN>YU0zzizO&T;W4U<>SQS>!PUOUx{n6%Y5lrXc$AZ&+*bY zPOu8N#6BHmjlnYf`A>);n&7j#{|RtVVX(a4{l;=3pnm+I_@9=B|0yT`6?aHd*7yga z`^m26oIDa3%j?VQi;S{N)#tw(l@b2~GT4{sX8>xZB>PCd=ku9xX2?A}B zO%0fY2yS7tH&m&ftlBaYS|2{sKI6Upiv5J|XN%s+2P@3FeOuj*Xx@R47Ls2rt`y0+(@^S{S*7{cKN;Bws`z`92wqS3dV!3~S!O5K z#a34ik#1)hlz{*8PgmHti-v+?2EMKBzLU08o zTcr9)w`{A~cT_3L@~|mXAO{BbgK?fCiEcv4>COa-gC4C4HPC0Oaa03Ti4H`35 zGS~i;05wi_N%TWXMpun40#nAb$^|v^D?(OM-XuK1X^9<2eaAa#<(tJ#PTt^2AMePP z1PdrmaKuhq|C{0q1<6Wa`=)qa@Mvs8y%O(OI5msH7f(-mH}5?An1Ews?NL9~BUg!% zs+5?$?hLoRTlHQ zrQg0sp>4r?p2FY{xcwQa(Ji!?HU$t@u`6Pg2Vrsp1K6zz3_ibYQMr@=ACW49Zq|pK zjg+OGf#_V&PN;Nu_Qu2kGJ&Retk-`S0U_>y7`A7;z-$G_nB0Y>4M1ZL*L$>Gc5kbLH9Cb6M=+~LzNXJ&&ha_0wzIL}p)v%`+t#h}OK%e3kaJjF>Gib!>4 zyN{+*R&va7ZFK-B4zXh1=rx9PKrbV6gAI!0hPRrpPaFw~`A$*WWC_l7|9kcJ<40tw z0R8vm@E2cdoz8Sf7Ocler0I(k8R^L|33a-J?*q_BhP}q zp>2il&BdJmPviM*Y%GoRtOfr41Nr}>Bc7GGrtpn<4|N|Xlb88wA|cn5f)-Lfon7-% zk4koq|Gn;#Nk>v#;hbEnhLC#wHJ}e5=So}LDT84^N21?`;=+tRDwPVJeBZ|rP?@O_5B{v*8(Er> z69VhV-<3a9Uug;IAO#kgN-#`ex8)B`swYK;eQLW-yzro~rC5Kkh@LQ0c1qmy@Em*6 zA_wf6=}QFKXX(|FC~>LW6Uv~Ia@=HCk^Y*}W}rGG9rKpm+f`i#kEhfRZYNVA@YG3q z(-Y`zmsEjYIF){8zK$`<<-P)oo3CWn%t5U>!`6rC*K{Yp*k(gq>w_K{lxIg3HnJSo z;UA{e0(rbr`N^hkPQklBakBUmGn(H{axElNH z)oMfRfvCx}x^BvpTR5xmi#aB7?=~J4dxV#0gMRiFR3L|xr!1Z_rtorhN5P*GfZ?OS zme@>SSHrf~I(T?zE0S&-5d|C$0crxKta zZJa4AZ16M>vCQIr+@loEMZy;C9j*-c0fJT-_lhI%71mv_Cz0TqU*w8L80N|be|EyUrYEPPSG0AvQLjOYZZ@Y!+)A8R`y`->*Q~pJeZ&?CH#DDJGz^nKR zVSF@Jf|6;x|?<_&3Qm>eiVS;>yBV zTCj}ZTS_@IWN2CXK(<6PXdeen{)%)SiovA$#_gt7ECs@U@7daIHoRp&c}?DVTirhK zbbkiJXbP(hqiZhF77+%YWw~O{iLZ<5318qIKf|#kr7G!5e`B3R1~|B9oSjtzmA9JH z*J6-4r^iD0K~st5b-zDGcVRBgTT6Djf{Fc9%gAi2bS0)tEzycElCahss`BNWD3nLG zHCAq^hMA4AODLo~E{pURE&r1HDQ-BO&OT)SHiVR!pq?vDcOFzF2-ezh(;usRmB(YV zT1jRhoQ}iPf)AZyGcxypOr}QtP?u{TGp?gR4ppG9pe(j1dy++eVy+sxG5wPfRf9AU zet+3M$uN697S#;jso+T=kzH@vNF{ynX^|^RdOC3@ndqZ3$1^@i38zPo6j;h)qi$v_ zWC`K8G=3x`)`zJ)=u$Rs!$mpLPB4|q^dgz;DMQRhpTzgMc=ER#;_@|quY;J)ri}4H z1eB-RweW_d9yno`(gDLp7cYtrs!YC>8`*@eP>(GNJXLfLvfivxSE>J7WKU14A65K? zHmc(E86M|%rptP`-r~e;F(c>Zj4V;4+E#h8#t!8E5*Clf9j5l8b1M<9c{F9iX0e$G zwuZ5W;INWt3MkkUSKq4zbIis{q)oH?AYr`uH8yf<7XFwV|tkLMITsqsq z+=*o7aam&`O-{XdyOx9h_wL~*4Y=RV`i$@l)&}; zyI3zXF!INh;~eRLf{a@vDC3`*`Y=vJ0@6DJC-^kaYL!m zRR4XRc<9;cMk5a=jM*tp9Btr|`|pQG(-gF<}-6hJf%B1PX%wbq*6x7xA0~`|E|Eogx-V>DJlh$(W&? z1JT%6q3e#2O2_kH5VaywRlUeY^#J#F;&z?mj3iJ>4&b#(RHhL!r-}Q5RTjN2AF!YKyS%T~uS4oytNLi00wY*xaH7NZkhTS(G;JXjlC(K(hB4~$H zSz-|ukABl^G%%W}XcSR=9d8g6p3s_XL`!JnR#ZO>1fz|Yb9`ToUc~A#`1S=BeX+Ipenc+pN2Ml zQ5J{fCOAZa^+Jz7f-du(HaY<~Ww#LdFZRl^G9SwDh2j%M>m2mjgg2O9E{@3>G1pXl zWcsUYk!+D5*JW^+d2(Or{AfZlz3~FX6o=|O+ZC(oZEYm7-|w3313hF5oJ;*EavcZ9 z?+7b1r2i-`Yn}zUQpRyH%CN%Z;MDe;I~dJ%;(}|#a+wIn-ii|||7-!fggjzj59NST z%(#K4I|D9xr8}!4lTOq|m%OQo0x;w*aKb z4Il^5rinbj-JbwQ99S1HqE)z}Rq#`N{i-a(SrpQAhd9F`#~CU|I2(djy{a|CIJ?!Q zI-^~rsyo#7a{^UyE2;a`(}|dudC$?~36W5G|4rC!a9m+`gdY7NCbHv^dGqi=gJ!Jo zP-hw~bn%XSsdbU}d2fEufxEpdqo={Mc-(j|F|=o=bZ|XI9Omp)CGq_fU{;p8<(DtVdmpg@K4mhAA*kxjt z^Q397eML#H*>1Iw_8IoS7Q1rVVFrTVnaTH^ng6Jq|L?`Jn1iH|gTsGLOAQ-D6}Zo6 zmn`Bjun^u^U{EYFU7A=c$rEbSUv0eVPr?L+x6YP1tZUUS{g>>g96tG?fV0KRf%n+E&(^Z6}`PXZ5GEpxIr?Up~q`$eSC*Awb$ zF0$t&Sv~}H%W=x*C73zUizmno>w-}Qt-4QU%It=4Hr2quSWSLu9C0TTOtVfNtWXki57vd78Nf7&`tfQ@?eHtUfsp!q6`kG{&|P zYiUQ~a1MH{&!)0w$6?F4OH3lO8ljEWGG$LGK%<0-_#SI&4>(NpPHTW2wXofMFIqHY zX3TCgX^WwQf}0r8A^l5H5pz73*cfa({kBEa zWlnj|jkwcv_+^-8c>9Vv_GY|Y{-!#JO5h%rwJ`%G7gV>zRA#PNIA;t;%hU2>knCf@GcKW#Gjng-yYOY43&BFVP?x4*s^ZG(Ff z(1zGD3lH|mS%YFqL8b4wLo!U1`B3d>u6ovm>KLv-k?iOMQvVjX|9W*yp0F>H!--T{ zk5M8{_V}@A2z!P#*&W~%j`U=(5bAXCqcY~j6yvNk5}32nt|#e zV(QS96FYR)NGfOl$dxpP3~k*R_K8Vt-Ptn`&#rAutb}+EbajsubH@NY^;}C|sSRxi zdzB6P2iK)OHQ{;uI2O4wD^{;YNH)dN(Vwe7d*aQEupU!(OlwMUR(pz%ft&wiL7khA zP{jMa0R2am&hr15ld|?U-x3>+Zo+!s(x?A6UnXpQ3!lObcdu!)xpN6pK8R4O_V9Zo zWe>%sn!5Um>d&GfKnZU)FCDM~Hkn;C@?k#-KS8>#_49QZXG9Rb$o9_Vewh=~rL0em zPkJ6^WNqN`bbAV*NVJ;t@)O5!7E6ss?llYTln1GetECQOe^jKY($@Xe6$Fr?gM-Abaj;N;@#rXMRLL9^H$Lso<&iMf9v0hXIX;BxiTL+3BG5#^fjuwbFS~&<$mZ* z%XaYfUFTv$9FTDQiAf|S%i%A7JudI$%P+^4BOauNrc5hcGwq?9X|zL?U{<$9zx;Y) zqirPwCC>(&hG*@!jOqzl8G`CP1(qxt*3b~ble44oaOq+jl`djlM4X!9B}F`%X@9=B zQAegPmHtjvu}{+dR>(oSW!75d+%WdVF9;D-PPR-({~#Ul5VaGoIKc8_J4d9){X4c@ zA3X!9sH+R6^0!`wUT$C^ku_*Gww^zY-DTV?8gHB+q%G0*g2+O2S{c8Hezi^7BmE&6 z>9o@5h>(6b^*Q`}EZ;70-Q_42L-rcZxWg?n(=L~gCOwDl6Q=kENBD8O(kX457y4ed z?17+Ijuao-y8CU{#L3MSNwu|# zHfeuXsIx1NXhhLifELcnYR*rG)1Dag?0f#aOxLZQJbNS2<1R$eDHrTpu&!I9V?g+p zpgj@6gB_{(Po_VE{8=Gr6cgQpMeYOeu<~{uP;T{z<`#hG?eMrEc?Saxk~m83SM}+> z1!k=YM!FD85^-8|Wy5_o&w&k)9WwF&RS9QiL>@FvTJ-WGC<)j}43fI~*FJ(?E$9lQ zX~r3UE#9>O14l{69oovTDkyc4SUx3ykE#tgAyeb z1yITj3mw(b&2k-=*~tf5+KZ=+Mp7P{Lc}Z(B(Bdf&{s17)Uy&`X!PzvhDIaBQ8O;t zfD$AVM~p}E)QK*68SoD1TK<4Z~yrM7|JhW6!OnuLKv)i`+YWlU$JEzD;I-L zrSx{7FjJ8$)?66u05Fg6vCH@|0Epjz2gK)(6Md`?#6x)*7DYk<|0?)MsFzQWSVluf zLr+EH^^FUHKc+x|i||7hrKhU}`loJK`%Ib7$G7mI^tXfAx4qSWQ?~ufPm=onGyZil zm{5cE)Kp6P+8Q7BV8|l5C=_FW{*cDc^5oOYV5V-u6|0Rco(^o!`vn*W5Gjx_jC_B) zAIOdMc&dDGtKscQ`c3(mt4!32>Z&fNDX_@!LbA$&MeUc?E^2nj10P#Yn*ho96zBEh zE0MR1Z|Cc$f5>MZ`Al^j9hAA?Z~>v34{-7TvlecTLE_x*Q9hUF?04T0hTlJMrv^kY_7>vnUUD)+o9Nho{J&t8tNCwFjT}pFu&lo(d3nVgpc- z-=||J07Qd2%GZR*JdXv+>F`&OhY`NtM>Cc5#q%Oy-w6=?HJC}y>g)d>*ANXS;zW-5%_DVVSEx3$nE2Iwn(Y9>Xn{BB($E@LzMVEZoPxxbW+_OM|`@E;A9W6T6h|u?#VkQ zySqAyl`-K}i6+x#J|>8fK_zAXfgIsLBxTT+dW@W!#1J>zvFlEx(84!$PgD?(kMFWa zB=&ZABu%siM!;39IrkX4nG!~fxUUi0-@uKt)Y0^uE-ENf;*e&JC8?T&R)|O}(2;z) z$k6uQWtfvqe<-YB;ua_!X^u|aP!J>RK1U=>-}p>13*9T2XvfOJ(?YkX&PY+)i;t3U z>lxYoFrSU13pG&m&UCjV8ld3rjwcRb%*`jTNPIf8lP(%YU2hiO=>}dmzv3Y;6=sdM#ndXbg3- zXjsg~7K;W-WmDtF1mu&GJnO!OZs|_J>TOhyfo1Svs)mm(El7kObP~97P^M}_g@`#1 zt>-5M^Q~K08!T}~)Izy3Y@)~5MwiDz0*>~KYSA;- zHeqxtbg!BTrFCOSFO=Wa86)^-g3Mih79Okk%}6$>bTe!AVA3nCa6?62ow1l$vV0>4 zW6p`I=*hfJP%*{bBPz(F>Ehlm?c*n8vko5a(Oiwyh@YaMP4vkALBdLzvNebA+;eLW zpdm<9SK8W{HWlOHPXc+8crntu%8#!o7U1KaufM}e2!P-@q<0cumVwI83T86eNt-ncy)u50?EgMvSN zdN_dI;jhSmf$#-;$w&|@fE}jdKY;s)pBS$!A!Kt;Wkh{|c2IDHTtQl;r#VsO;Ni-z zK?8Lp@q-O%~$k5`2 z4@Sy$(enKvl>p9H97j@3m2_G%^HMJqsTm_mxM`G(cCrB1ztOamJo;>OrDCVaD78SA zLxIDmqr@8>WCLap7Woq&*Q-2m?wd-ur^QNdmA_50PF0nP*%NXT_v5l>7^D?Z@a!m- zKANw+l0=5iD5c z>`+(uK)+;3AxuGjg>$rS#bPW`vYxn2N~UUV_OUjbZ%)JB8&2xyPw0Q%UnA zG0)K1V%}|1!QC++i9>}0jh;K;5BD5&??%^&_#d5h`bkD<_w=R4M!RN90#MFdI)V@P zODng7=Y7xQmf)1ag=kXEvRJKt%=jl>iJsg_bx}C-?wYmFG zAnI)Gtt`=EM+Vtw_+(VL^tLn#r#+aFr|u5>&Paev_GDa`#KAz3eV+~1Xk3~x+Z>fi zacY~rVY!?%(-u&B6Z5c8;%!9F>N0In)|O$xhLCl0qFS+a^De3 z^yxBWbN02ID=u?j&EcE{+|llbCzvFdW`M;?uq`&Df~sI%EaI}t0}+6)h?o=Vp}zl` zD=a_v3$54@%kK&gP#D-5gFn5)9$98zJ)N0hJ}zZ-W{WEdFvX6X7ukmXy`=sFw7A*x-y4M+`uFb39SJ+f{E=L*&>D7~@QGfb4WyCUGzg7S zkMYL^)o`zI^jzAtzV;McI(X_8GMvJ=ty&UX#M!5aIm6?z8CeKZ@ghAf{A+(=Sf7U# zcDB#;##bVK|29S%iCyV1M02RoI9)kN${ePvDHw1-y3>H&?X|V_BR{Mt zNtMq})i08l%vC`to zEk)CB3L0}zp&YA5iKc2%sW>GoP<67Hh>cpvO6@-qj;_|lV$MUe;)KjVo1(Pi=JUbd z=?;(Wj=Vtr`N@=cdgwrT!^z9&lX_7#a9@JfIYn>%hMp7h_vY7HLgWg=zNg=fTU?x5 zR2|#IqZAQ(r8nVghn(nqB4HF%0bu^rt<9SFK|;MoNTF107EFvo_wItlbWPM_<jlziRi4#}Y_;?g%cN##tnbC-Nj&FP=}hxDgbrqHxdv8}7yq-CGxPUa9tQ{J>27(Zs-mM zYxnpUw;v+sl}v*djLQ!4del-_{akCx(#cz3xQ7t_V<>(0{SB+S*`7gLQ~@?KH<%R` z^qQxLC03{2UDjmp3>BtGgC*8Py`5t6U|g#8-Z4nR6vrjRt@KpdU9^us0FlFC6GAf9 zr8%4J3P~!~rFsg4=8etW*OiSIz$psA!qbL9pwZcV;TZtj869Q`42%nmv+5WrqwB-C zrqZ!V5^+fmVBr&V4FMby)1%|g;6HhW0C!|}lS^qX(zK|VhMR7gb(|xByPy4!Q7&ei zFWWloR|BL8F7Dq4Lc$2g2&e5mlE~C_#uS3c7D042adejemRHKrm)l|16}-R{!xf+h za0fE97ZQ0WG0s7Q2u?$H^&WcT86^4!0slZDcc*ZerIO~NP55v$&QXKNQZkKWuqW*L~t7?`mMD_{vL*n$_aq06>G z6!F1}-Xw|M1d>3#zZHaqM72h-J~#G=xz_ELwH1A~6@0eim+YOh%s~VK@XrJO6z?z+ zJ|zEwRFXsu%2!Pci+3T9w%&cA9m^zTO?ENgC|4{ek~V+L7X|h;8q?(0EfpWyB2pIJ zXa-h$=F2RSbe$X}H$0w3l!K243QU^zH=HY0SKm zE>bfJy0XG>jqlE|BBr`R5LyGLEmjXy#mw|nJPGs%?a+oxp2YK#3o9?QonY~ zOoZ6|b+vbdlEuSXlXAC~#lszr3*}OL=bCFs!f_i{M$oi4w7s>uty=5)pm=ikHXRi% zn}={G)hblQQ5rz4vEC#vi3_oc`(Vbu@Xs(Ey=sV$SstV z{R~L0TbxF@BdQ*-YGk}Yi$A}3%53iOsM5sL8gK=X60*)z08k*~B0xyRk58o;FFzqr z$hjyoUz1o!PBG4nWI2kFbLM`s$K2Z^X}=uz_S%kGHSqK*rL!upvxKCWEd^^?d)=L$ zUfhOI$wJgeaiTp1Aw$N^7qDiIR5zk%9J|H9q+nuJG_R}&Bu;+?0wsYayWzB-|5-Ci zu@wgHMb*UqQ^Ot1WH4hd0;BHPjQfvAGA{pfVZdzDr1;gO#>obZ_!1lCMoP=9hh@=| zMyT2udJq}|JNBiy%Vq0=0l0)WlzA+xdFC&Xw=`~C!OmxWnCQh-<*KGCbqlqoUk%h) z3u~;9H;Y`}Z&H(MJn>JDR}(rX1#Xm`Q+#F(4YQ=T%I(8^C&ZKD-DxeeoiO5CXO;JB zZj>8GxsII;vsXt|_j54fG)~H|dw`_PuP?Eb6sezgIdoc|{wKHdM6*V}a_p8(BkqK^ z{(2PTG$k>|Y1~o0zLv#*ZKGU#x8IRkUBL@SjYS{-?V zbI`anH;~zI@O&MOrK?E(DDju4qDT#T^bC?8}v`113$5KY*CGXifDMI&q zA!R27@r>cDEj&*UOWG1rh}QooH7#EEy5bV7@o$89RkWQL@QNMB{^I!6+|9UA#{g6Wwbvbx*Ckw zllN)KXNofzf5~{`mToam`J4l>nll22BRkX+SEtKW+gjVy%Ucf5ry!EZO;vbjs;D09 zWoRmroyq_z!DZR;Kw`Mrx(ZzYjgn`O8ZzuPt)GY(r*hto7_f|x75@MOe0wNc(&Nid zNDB9ZYSdt?DN?z_jG&+6KT!}}t8*vlC>G)?axFaNbQT?mHz*E^HHRC+kB|$`>vxW; z%`_mvhN$}|lnx!*4-Ty&C>+{|H?;mrEjpU!CY`?&A431vqog1@)w zsuy#Vtrn8DE0&op>yf2Bs;c2p87f1 zsO>gsi!c-(SJTcIzHH=fP=Cho&?%?T*&NR@ionFwP8sasv@n}8{%)k=%N0tPHbK1V zN$^585ULnWc|5`qe2D?H*c4x9s7lqlLn0~wEt236A-rj%NVhuU5Pq}%`*nV8Jej*i zY+SEtP@jU#mFkWc$r%ZHiPA*C2gW$~Q$ZzcQCG=<>7wn{{oJNy;l2_$L1uMFIZvrQ ziS)5YxA16bC8u=FVeMq8E>5ipvDtdGn%!-$oqXT4#_20nQLJANYo-<#oUw4GMvr$O z*Ar&|ADWnoSl6spPI@~C3_R(lCbSY2>9u@6q%;LuW0t-&&Djem;nAvd83Fo!wc><4 zJ`r@&XE!`B6jChdh?*jEaZOi|Vc11WN63{1uu5cdU&>)-N!R z5?4u7QiY7pR|j!fn~(UHq1w z&s_2Ag2N$8v8mgCK4DfvE4vBV;=*34f9MKd%lxu4%Q!0vzu(`z771k+ls^1TM_Yw=5m_(Hm27c zo{p|~?^@h$BE4Vjj~uj!*Cy(I(4N}WoIaM@6eJ2br_Io;DL&2G)c3OXc=qa24&6GO z3AgD0C`{DuV%Cv_IkkW;>iP~IStBlHI|<&^6^A)>gC@$}r9gQ*HF3ZMEY9zWoK zH}C}ojnx3YZkQZF*P59e$C|Zox!FOCN3KJto(OMW==5@!ZC_3Qo=E7?{Q-w4WaZajrN36Fk>(@OWtyv@b6gOkcz?3U$FKdQ9W(V+(n2AZd~!kDS=D&$Y>?2Bq1Vw3 zy~=gkqLI|HkGL3qmUgl- zvNtpMS9H@$(ZXR)7>O$*r10;k1f7{L5`tupGoF7lrxXs6bZLZuFHtb#LH|%PHeI-( zW!kXKG0pdV$+4ISD$Oq@>oz4-ua$D7Ud;Dqd~>6q<0zZUr4yIC(Ax`cCG> z!FqYU46Q`0{AodRpWp`9O6OO2C9(N%vUYRwD56h;RW|7^#94n;#587`zxWJ4ywDyqQSXItsSZ&ARSnZgqQ<`iy3MpgVOU3-P0=D1lI+ zp$%U@A?ISP-xfOi<@dE~m<<|{Rm3@IJ=o`7dh7-bOyj^l+C+IgeY;I?a=E&6Y3Vil z$?vlM!hUsm>$MgM4}_Wsb5XRlDZIA=&}xN|eYWbPS4+25dp^56maq0ewS*7N782PY zZZHuoLkET1tk)&?CxZzADcfz%=BqJS>YcCIFx{hM#Ge$SdWWLF?8@cg_Xwqi%mhIe z!A%rbgq}@+2ZOCD4!Q-mg>8rTvHQ=)G$bbeN93ABA>qdguVmLIb9Un3-#Q(7BDp>B zP(vr(sXzMF9H(R2p=3F)M8a&_Q5lQPjARXwI_BPVzn;w!^blyne>0l%0e6BcAfa2e zAz6DLesfpEIt^g6eDYYLt65@ieGE+HQ@li@1mI=eZ<*$m{SXy_1-Aqm1q3<88zBg! zck6Y&-M(QW!qa4F8nuUH^j^MB;t*!F3%`WUdF~6HE|~Q7^PXOt-e9L&_$-`oKRUR% zrw70VG-0pvNN`|p41lVcZg$|l^qzhYV!qyZWxC>G8$;4z*zgWX-4g`t-SbN8rWa05 zvqoSY)<&;wrw+rQtZI6}x4k;-pGqE=6H!GpQ7L#a%4DfpH;9H)#a~O9&LC?-x78;S zo*s)(twy=N{T0@Yny*QC<t(*&$Q?bu)g_|7NY@r3MXBqtZ}AzmSzqn0w?+S4coKojEC%&Qfr6 za5!5JalkzvlVVQy$8ODe?EehadXs40m>L-|`*qUCRHMmu^~QbmZQOb1HOce&aYOyH z4lzft7WNJUC4DAXf+9N#FVIbmvt-ADo|`5Hb^pc*c`Not2${yrmCaw-Q(=%2KK$_D zrhMIt+8;%&d0T_PL#w}q2>{=l(OS7f4Q*rb936aR?kdv%#OSHh-$CIG*Q-3Q2d5$dg-TGynTg=O__ngFc+Pb z@)5a%Hrbb1hNrn*)L6iat}@MRfE0Yu7>9jx)naU;<01ofBuT22Dvr)ZYIWr5LC<8t z%1}lc(Lj>-3wP7zfa)mMUsvWT)qgrR3xAHQUwLvrPmIB5Hg!BbKSMVL-A!UKn|6O( zb$_KDWkPh&8KDVP^5~j>GPP+oAR3FDUC=f6NIGYo9DV``0TdFJA`~l4Cd0vBeB{+} zlM!nUggc(eWC9Dju>i)>Cdt(B7VATziXR}t1pJ5E*FQqp0xn$j10WY06e!wjI!t zq+J8PTYXmvyXV1p;b-`ug7zW<=4v^l=`w|=Z$Pu2?y^}H}k!*xq2E|xwlPLS1r?NAn#_zkiGi)5mklrb%jnoJ;Yp5V=5mnkr$eh2u1V|+scKAK z!xna(d%N0m=#(s#&FUN{+IMOBaYl-Z8`_*y{r*lPj$2T(n^l)L@55Ks&S#GywR9{d;-oUnuK0@JTKC2v>K-jsByW)Xiezt=9$H81D=G00 z`b{Xr?Jsf-ut4WMgufgNZGc$oQ(E_YTZ{?qh6OhNTKCprhcl9J;~tqu=jcKXYuy+r zYcSRZ>Ux6a&5fC=^Rwb8rQ{9$iNj@gX{fR_Wh<# z8q?j|E=CxvFN{Gw17~lS>Y@$1X)MRYbN!{O_g|cwaT;qqLd0b|-&{Q*7#J<#{F%lj zhKcOL+^6<~loeKPcJvGNh8Wf-zF_@alMhp>{mGSHj-SyRddCprq9h*o#Zqn)>@`=9 zh3+vI#G@jW!|PBwvV9!Gz&5_#{Lq(?Z>r{qJRxJ_CieLP<6bIn98$e>@Kw%cMZ}0% zvr0}6Hyv&90m?j5u!6$zjlyv+(ZrLb%YP0~rHh4#bMr$6oVEN$&=eiJ;u>Cslee4u zw9h#)=B2hAykfiJp(p*+7aIQD7NnNpp{LKO2ZwZd2259G0Yu@2)1|5N`2~3X1grD)jjEY{AQ7j)SY#akx zUy=3CM=tXwvxIFmB;~n5(1tdpYI4j23+$OC!KzxH32I)-+A(sbs340nkyiD-EUI{&2WAF$)%BatO2u?$5(*{ zJc;t_#f07snnGL-sLYnLiv(1J(h*AOHhLMw|mIPVV51D!X}K>30(W= z6x#hu>TtuzvQNWvu5+SPbEw}n#HQFKZ8@Ua$nJD~QY*uX{qI~1ZB4dal@q50$lp5^ z+o}VR9Fq(GwfPk{<=DbFj)D$x(J*za{2aFNHB#HOO=h^U12X&vv}XK0E~Riu=c1bmW?5^hR~M@?Xw zC;I!BwPb=BL+s>08m9P$-Ikru*`H9nKcW1i_DDIN*MlZoUiZBwQ~w`l@A%zW7;S4- zY}>YN+jde(ezEOTY}>YN+qRulY*)}Z_uRgt&*^(VbpHW+j6L>V~^PC60sZT~V`DRsB0FG`h` zjhf5F?LS2to{bjij?sM+W6lFA+bdUykk0E4B6(e#O;L1b`96bZi~kbALoG%hkKnhN zux61!a5H{^Hp!)!aOz2IyTBFb3Pitbs6(&Z6wz^$T(0)cV&>l(wRF#t4i@Wi8LT^p z@Cr4YIQ;`dmmRNQ;bS!597521k5kgT&iU#Vmu;w#icY(5SXhu1=a3qlFP`=cNIH>I z?syWMl9XC(;RNB4i%`Q~Z7RHQDSs(4p{5&e&qs(4+psFlPOI-f@|>Ou-JZh6-l!8F zbgY^~6pIX?VB-xgMdK~p|BqvtM_1mySXa@W30&=7T!_Y-J6velp5D$cwx{T#F~{f} zX|n0G8GkVZRyYI%4!E@$MU@RHZ1pHl?A2TDow-}`9c6pX!A*CG{<09ayPSX*Hg7)! zuoH~hoRHYt`Tzx1+J}nKt%LIOWPRQ@}X=lH*teZY1w|BBNYr>l^r9DX4qW zoU-5ESV8YNq+8tC7cJ^KhvJIyddgi%AZx|vypx;r9h%)yYPm~sLKw;Wb?RWm=_UGjuV?woli zxM6>+9(sN(sm(u0tfB$9BTHAX;3`#(XRvd1uwL2@Iyzp9C+HStu4dnG?gk%b?G%ic zEMji-BSd6%iVSODK+J8*8&D|+mKJtv#e^_+?Y%y^N)eC{V(ZCrOzR3Y7dy5R(K(Np*2kin8JC}{6Un~9jpvOf0~v3U@z6LP zJ~I)Drgc_Dmf5Ibinqi0i&NnK+I^`Ph#gUEZs~ypZMl3jWiGZpxNev&UTX_caaO9q z@{8cpz91E?`(CF%m$A2Zgo)tKW?jN94&;VxTW-$rlM&(*oY{V*9WTuHS=HVTd_fBF z6^3I$>ni+!d@GAhEr(?ak>?Sq3)_l!vBwC~0Q~dz6nXo}Y>fWTEK!`$I9Jm3BV+K~ z9w;XX#5;?klL05{3UitVya`rP?kKJQbo5^)9Rt)&2I4L&Sf>_5L;9;~L6bd-sv1(B zhmCOwERhFaQ8BNxwhOT2Vk}itMYf1`4~SL9s}0y1p05|E*cN+KY>|C2ELDBc>6$Ea z@YPsDhnF@*EqXfW){cI!6?WsgBWN0#^z8nI!_zfy+)8$@%MPWf_ON%o=qS@U#*h)2 zoF(3bjO`kCNhz;7<#uQ6h+7!3vami0MJ(b1dDie0+#PWfdJ<>>JH#sh+ubIlC63)8 zQUWM_A}bxt{vh$bL^q^CF{Kd3dbdnaorJk$SUM*0KjoyuytLp!0K0qybQTiKZFkuK z!vXdOb(<$bNFKF=2R0z-s|zuebck^Vyf@+^5`TfgukPv1j+(8iKti)CIm)=hCXn%7GZN{ zE@Z{satPnPRc;UpL!h32K$75{8qm2IAFd9@qZTzJQ5@h!+9WePTBl zAa3bP3oH~3H-3+V8sdQqt^KuY3t!D%cG%j&h#EgREvyJn<2G%Jw`LD!As2-jeIIK4 zK}^a=nI85J6h*OpW<=?&377(nd&pi>MpyoLBP5ES_IYat?Bs=E`|$7HRc+}udNa=cGb^ZaG1G1#Or`TDoa6lyZjv_f+6N}` z%B9ECo@UQEnimJ-3g?Jl0qO@YIQ_)A_|RBU>8w%Jys;4Vh#oy_8Ue~v@%)#muU-i$4|$pn&OJBQ-q00Y1*!? z#sVSAc4GiQg}$As$beOSOtrOVc6XRSlUDEPVyxB|WS-L>(koWWpxr6e8bN8CC8?N=8xnBVi8JqBdPXT} zGU9)d^H(=z&n;PSA2El2Yqdv-*3_haV5=J>hh*023aN@f$RmwxMi^^rXshRSR5Xnp zA$t`yLUXfaiKhnCn!BUVQgIgUdt|BI283YTsln-saQt<#PboqONyey*38_6KgZq2S zOypz$1gSz_*|%_IXZ}k5d;XR#OXK!GK*VlrT@f(YJ)t*NuRuO!|AQ~Q%K8ENmn(eS zs&wDwBMGKSba1w4kLyFXz^A_q?&IjbF`htekl|kwtQ1RsHeu2$h+!lhrl(U>$-aiW zNXLg&yYA90&PNI(wN&>TA!%KMHd9f^?s%E{-izZ~V2Hqh!q(O}xA0)D!|y@;+mevh zTkV0dzL>>Z4c)zZ18|&&C%tR-7O8KPgwHv^Oau?o$Qh| zWN@rip<#8CmFiDo-IMoJ?N~bh5u_Qc;H~VMb(13xf~o0}plRmlakK)ytI;p7-tZ(g z)JdZXDTHJkx)eUj)yZ*Vy9N~LOJnsVn*+_GD%O@D7Xk=IZZk@)h*DN*yAtw3@uTc-)fM5*rY?j-DcTaFO-^~{7VLu^1Cd#VXJxn~Kj;cnT5#+5%yT=B>; zDcP8dpii`qAR1_cb;g=4Ibx8*aGSPu%va$;R$AFcrs(;}v{Wr%X(% z^1lUzGM(4&%1Ak>w`SzH)xg=^Qk$Bkl$rHZ0@HdfSGrPui%v34&G~>V|MO{^XDwWD zdDG$jyL2M>(fK`?(2Um@d1_@WR+AUh^y#g(kX(g|3I36KZ>g@_NC)_5`+?$9T*onW z48{O;8eeE~F7WXf6!Lq!DB>}NW!ksw!5hGsK>sXs)zKd)5g88#rt>KHxv2D6!K@|^ z{-H{gdl@$_TT^%0D4621y5=&6>>%l?h%@Owk!0d^w~?ySCaUsa!g_0#fQPG{jk7%NW$+s;bYA7WIf@wJ_vB**{GbCqe;&UJX_|di$ zl4ZTVM)HQ%sE6Q%+ff$-uN&~XT6Rem1bQK`&bw4S^~q4^iS~AYS0Z(#cd0(>|4m@i!gI zOK;cz1AdA+`pWiH+!e#r$?+sf>RFdOIWe=5rM0Ec0XCSa#e*fLA4}*r@!OoW8}20x zj&vqf{+q`(AH(swV$t6zhb=@QEq@a7`Z~P~E!B&b7bR5Zyf`gplFK?*T=`bl5<(Ge zJ>2~UpTFYw!f}URnO9a486}Btkgjp>~W6Yb`)_e9kWMX2 z2&GF`uhbnq6Ek%H&jCAgUxeBy^UC2&f~&U?Q#)jBSJGOD?Ue7oUigWoqFSPUbQU5o z|0np&e>p>m*x6ZG{7>UWw)&$fz`7W zXWvVp;a067X8<*B#M~zk96|%@S{h)sDI}h83Nj@>m!R;2&_sMN4P0ihEo9J8Hpq}h z(Z*;%74Zs(BL;iC%Frw!P=2R@B^x?GBHB7WDnEWJ$guvQXg`VyaRrhpo2hcHPF$#J zmOb0pkkw*Mp4&{to_aiEyFH+Bl@hl^6fKa#+HA_Xignng$$Iiky{_kN7r1tKiCL7; z(~$(UqGGmT+W$awV(NxXS!-Q{-o`jIs%zLyK`QP6zImV^&)`m|R~#+Ur~7D%4{0bz z_qtB3-?P>x^DU=Ji88Io?j55K&K8#p_9)sYX=cof(t7mUe5v=zV8q!o{2$pgUC#q` z+sV1<_boX=cduC*G~C$m+$rl(jSyJz?zxSyScx}$qED+XEWDFKMxxW=d<&{Fbc&ve znT&(;(iF=1m_f}?%Y2g#e|11heq&u<0++sr{g4sV%+a zC-h?qM4y!a+HTQ!#qp!HWcY@Mt?x6Wacrf3Gx+KcV89gUx5U5?QyX# zMn7C`qHwn~7g+)4?wSese5|pXlqYq=)5@+<{0aq7s&M4O%@D4J=L4Z4y73GUOpakh(U5X~G zax+lXAl;A`-G~Ft3X0PZ4*MSb_=Q-k3BQYCkRaTSAcYtx=^>8Z$Q|=GSAz}zVPiEz zN;O5=6gz-9C#^rhaqwyG^tEF3)=HE1_o%c3_|pMx#t;|jP$shX7M*gys(QdG`KtIB z3IqJ$g>WlGt^SVxv8ccnNvD{mUNwy>DQQ&8S|D5F{ruz4d7jg2f5+gb+^BW@*mq zWB+|XvsKS?uPSq`7D!LZ)riq)X%{k4na59zZDHI(m>)=Zp7{tzzGC4FGn^vz0wg~| z4DbGBov7ejzQbU2x5?PvZZm5T*rp~?J@U;Suv7Z}x9M&x1}jP#;;&yuKhlitr1d&>tc&?D=8T90XQoPqg1w@X!|w5OhkxMESf2`A-htt z#GvxQyqwhV3teW8Vex}`I|<8}mPIZdtxq}&a?BbkoteUKnK_llF(&4O>gCNxD{m7f zS*g87)f`0~iYBs1x}v$9saRd(9JoOe{hvv=eo97zY&tE6NwB^;_#Aa*Q~9cCv;GX6 zL1EV4RJM)WT3H6gy=HD^SuKn{d9*4a`Xk8|t41Lyfs@o|AB)FHu?U}fN1J<@Mk>0J zri=!uJu694R|y;cWz5*k@eobYZ2eqW@`Qi`d5J~3Gg0P+K_mYeP_}DX2^0sRb*T*2BUkl{_AlcF@5ftdxVGsxRa~mf6v+iuaR$!y4Ue_ZOc=xA>?9-(=oi` z3}|({FIk8uEvB*`7?WIlC0||}<{?d6PLh0H5`QPo?sm1|jt?tdBC1Hdu4JWBmLnjy z4zbDvNFe+;c76sKd32KxIud#U>4q6$SIw&uguX2PBf!ZVk6&d z_DIT;6p%aT+Uyut_&Yt-(d~}K`)bVerqH94xA#U~-vn>1qTvWuHk^#Yofz76WHO$= zkFajAcv=+_4T&aZ#kX@JNqoKDoNLh8w0tUciM2ZDM#t+$N52uj+q0Qo;GnFf9w~UP zWbY|w@GIv<&}&BF(BX+q3>luaPDj`82qWhV~7V3>+8 z7Z;^Y)pj;o@;!@#n}`V%%PJkBsZuo?up~~4yWHIMd|6kLC-J&)-a&twqKaJt=#9pa zO-i8amgDTWOeURVsnVTPJhbansY)yqKnU^r^cs*Qbg~raA;cEV*o!sS#$~bKu*E1u+Oqt%6Uix}sHJ%&CE{0Z9Qr#dJl6OO zxT=ZpD%^Bu19IrQezr26MXL#A8}2y(YJTH!HBRA0nT3H?cih-R zTN&j9_Tt@yZp)=V0~nSAk_tMGg76uR&19=%%~Pv?@9c!ss}l*B)hZjJRfr)-gO=)4AO0rsiXEC`I0R#gi)nP5M(&D@)27 zZ~ng(*E;A!uX?Hk)0T{c(Yo}T=6=n2JcEZ+iCgQgdI4iRe?RD}CXeSr!CyE%|*H_IIQ9wD;^B6cptycW+Qc2dFjx~Ni zu2UffzdObz=SBVTrWq$sbqD!b;hD2V+ufU*Mn&iMPg~!iG^Z8N?+u_6{T zsIx~}uJzdfbH4Fs)0rGWAPGSocSQeNCe&tx0q%%{I^1CWOoxpqgj8A1h;Z)5AU+AD zyl90?WIdLFM5Z%J=|M1w{pDa`nGI@97X@= zvVJkm0P-qPn3+Mct@Qx$CZ`q@ohbe-Xu2I-Ee9N1Glo~xYx^fn5qWSg{kDVv1P6G| zO7^Z(SHu$IGF?!-KN%}#`F^u|Gv1nq;sCO+We)HavoosU4jO?JGtw0yfZiYMs@eTn zaYMOM7;(V1Y?aPP-@B7|5a18LK#Cpdir&!smPOqcv4HxxWR+K#%Z)`{k6-7*`Xpw! zi$Hx3dkKLG{EGg`Xa*=Bsn? zSEQq#80$bc{9fuk&NL75g%RGQ&l*F5_x9U#s=1X2_;=MKEL(NP-7?+>^+L4V=&Z%(zFN+ zCXnwCM4u6wf6NtkPE{k7nz5_6T7y_rjt#bb8nYaI#F*-|`^P482mjLq9SWo>aGJDo8KnAD{pT`-b!n3d5MS6~lC93rTE zT6p99?sq&%P5r@X4&j_~?%AZdLlnQ_mHW)Tobru4Lzt%&w~40Y>!ln@Yx&b_g>N@N zLm0EcPrSALrth<9KzEY{>1`tD>%J$vP7J+;Gkt?NM&hNGxJ~;>Q$YRl@W5plLvfet ziPSjSbXV&MT0Oo)tUkuqMn_`e3W9*K$QcJ>kpCW`pvL4mK^mg)-6n zeTF$TBd(fm+vMnr1lub(f!=*OA6H9|Qp1$sJdBpX4>=BhpP$26Q zlzxvg6_24f(-ZQ^?1A)mT&7L+cR$*_jOY=Y^Vmh$1VdbsK9{MsG}Rd?%5!M&O5}Vr$M-9$Qa}6WL5_`I9GyYtQXeCwC6x`1CE{S7=oeT8llJE zs0apsoR$yQ^0exb17o2YH^rMJn6B2w9J)&zG8V&Q_ldP_y-dc3AR3SKXyabY z$6xA&zxY?*l5mTwQ_2SdH0ghv~!(im0_yeTNq z;syZ}6dsxZ*29H+ogx+En0lt`$-bFswO*FPr9Eq1jqcEFopq*OmZE=VV;*+#c2izUx3=lN)=gkrhU3I{rwiKMI`UogI~bGY<&)>N`65 zU}#VE2q#~Ap4fNbaHG7Y#&YI4=aYXeCo@y|ml}$iTMWQdyTx9H#AHh|UILUTg?fw- z)3T==(J#p+wb|y5nsFVvHq36 z(>?hh_40*eM}<$G|5^9PdIbF=^uVNht#u8T0n&DWnB!!p*DWx%^x(j~zC@2m>s)Og z8^a8H9vL7uyVqer*lGYqci(l7IPt`6!lE7IR;`^|2XDj85^jU2H_FBxm@<+O*i@xV z!LE;GR#(^)R=~n$>d@h#%yD8>MhzbO7ATgUi94T1yGk49B6+uu;LCE9*xke)#fE8? z70Y}GJjK3ZSTpYzJQ7^SvSO56k8}|gS-)crP-HjCoFC}37E7&%ayUfBWo19)HiEfk z+_?2HqQU3bxJ#-BnZ@S0Gdg7MGS-b*ji$NtpC{ul)QO13Pq)2v_5OqM=?ro4=$jY% z*=2oTV#s`YT&)jM&EIs;V zJ1jlY?T&Ynk)exuI2uJ+#a z>5hbhQSJjq;1E>6)znAbgrUv8S$-SD$D`hH93kb+_P)bFD=haw|3NT{NWmuzLkrH7 z(3JC^>I+g@1R({H@tN;PfHAAgZtG0I@uUY)ml?>N6S`O-5wBU{Yo$jqB_|Hm@4*WX z!l=gh1B}-fUPMQB#NMe-RQnhdhYxKBUocMKhkc}&E%5|>u06ZDy*cjcUrLV#7;b}O zA39lQUj+ZPeXTnng600hPT%fR>HL0jB~!GsWdJ^YWSY_Wt!66riFC zSGFcc5?%mF24@0?7E&}D3NO;awTEKswRN+3Md|$c#TCfCzmrCCoL4hLSWels_w zX0Na;yZDs7;=blGz_asIBsq8ZJ2bRr>=3~0=_~=8Tyi7&eA<8FvjpSaaJo_8hy$)* zTpXQMMkaON{7pHacJb|cWcg&$#4Y7q(&y>x%DU0ddC9d}V;}|7%EDwt3^ynWodJc1 zEn>ljKX|frUBYAUi3EQNr}hgsDZesenLuU~xs+h6@Z9ul&3R{VL`&e6uu*2qNG!pYgh*2Ixi z#o5kY<3BJ!|5*pw%5n-zil}^UCE4{XV4$cHh~lVd5sCunBe9rp;o+tql7wY6_a(eV zMT-f~h6AyVhQOA(uOJ^3^jmF893x&bD&mLWot&*VJ*!SLe6_j0e?Ee!(e0aoApYXw z4tn@w`NIHX;J~J0rw!}kJ-FzSv#v51DmR)A1Of~&u&roYfG=JPbyMGJtb!U)+nW^z;Q4uN(80WR-Odu$KP5y4`EDj9RQvY7l z+&RAS4B9uKkaMwCcW)RqM3t(6kx(9C{q}|b1%s9zzDv!l{u|<+n;0bUi70m3Zg7hv z>fSQ&`x<(c>V|JDb5~;6=~)S_4%GSv&yr#1#F_$gRLSULCPF{$iN?uW1V(@{JePBv z#|Xk&SK+UvN-(=rU+@Xt3TaKv3y=%arpS|A`5Yqn!9}t3%qe~r9VV2Sbj~jqQ{W9* zr&+Idh|Zsstne$xh-acFOauhW0b-0oRzGw&KtjlSR4WWtq!>9DFB88S^t-qC)Ecc3 z!NN+BR5gFA>sgDQk9W3|$*f^(`*bf<$v?=ffV_Z4E_tUhWVkyMFHW#`xc|CDsnjLZ z!~N8)$WPr${Qs|85jz`u!yhw_o#TI2aIuP<-J&4kpF~b32Ut2f#SLK#B31%&Olne4 zvE&L#NiiiMXzIVw>&O~2+>Y#$9|&!PKEm|4zrsNdrh;K|4|)krw-phu$c7hYE~jRC zUR+oH?f>0$8!LdH6~g}uJ!UvR;t}eEVVz{H+f*+*b|JsUphxs7N7lT%`y9L=ROO7^ z@|h#s^6651Qy-gHqv&rP@`o;2RvjvM1*Mor`G-)n1+7-L3Q3d9}~q-pMDGPVceels2guE0cE6B*Zmh!cUmiQv@?fKSd!zq zD6Of7Lk0knnY*dO^G`fXU-T~omFA$sE`~`T_{aYx{Qgf7w+6&#*Tl~}cM9{@FP8tW z756{aN6r5f$@Q*Z&X~FmAW0en86w4z##svj=`A^8l41h8%wv`>NHI&HKRcSGgzc%> zUFEkl|5mfA3|m%LMP{_@mZo`DuY7K4uI6iMSy9)i{OnENo+hUawe{)cxaq#}+Wpg& z+I=JNfyrBj@xX?;R&#(42i=lt#MVO}>UC_N`|~vpGO<6}^x=7qZv7%4)Od^Xg>BE* zsA8WX82$8sE=L9(F_XMvB~J^f@fQ`NuY9~^1`pkZ>2n?rsRVLTZhg3T3l281O8~JU zH+!8vyd-az%VS?!?bF#e<3_tLhY-|0W%=j=34^hpui+7YKiq$d@cq6$ET#Uv#E9`@ zEW;9bIQ;ALQII9;TfVRH+8nZL5KB-iNv=@#S{)+hE;M|@iW{rJ_JQv^cPr6F9xCv5 zX9o2Z8PRL$j%W9J$MN=OF}8Q|M&Rf+h2T4hbvI>#pW5tO`Oc2&$(3O%Hzn8km^^27 zE4=N})s5Nn4B+-NM8n`lx!J7kOQ-FyU{LKUJkayMFUyv0#d0;P_1npk%}6(K7;KQ0 zG0+8SqD_{>!5Wk|0R?V}KV!NovsptfH3_UAhcvs=ioayl0f?4jvOsT1DK?HR|F#=N zF7eZpsu&$kVEP9Nq#$=?0cep`#|+eU>H9Vl%2Ng~f=To$X{7oSbmtLPmfDuwp4W3rRz6gvr8HWN9CR zB%_ThHLyAIP#8GRhrTOIFX_guy+1X6DYMBj3N5S`<+jZ+UpsMxpbjRxPFgsx8832_ zS5(pJ;T^*e^j-CEMnO^FN`)wOVg-*vk zv5gxMB}m_|i>p@?;)e#c*bs%^NVdF4vT6cFl7d=Rqt#j~Dt{l>*5m}mSZ!8>g{IB= zV_?ac#518to{0tb(3Su`Vsp){$ScI*u07%7Ie-$3~x+{!+-|jGm zv|}P^-Xn!0sPT-S{J|rbTNXYQM#5K1Ivb$z?obTY$2b;V6!RlgO`V~Ar=Q7KGDzo2 zs3@BorexB_FdEY2R#umiM!aYu29{Vx_!qo|3z-ejUT?DrK9~7CjRi68p{E|^REt^_ zOD;1FVmob9#J!C;d-FHk3+Oly&yU>8ZbOeNwW(^`=ljHXedQDf4jgF4)D8qnzG%^aL zA1F|QcS@fnn!H>Z<~{Q-9GVAyBA`a1oEr-vu^;;CT%Bw8U?XTKoH##p(e7LvTVI_t zCNVHxMpb*x_jZPVT|+BuIF7FV0f9zv+tdKoz-4S`SJ90>E9tiBsb98D241k$X$ohN zE@7cloY4=hEM=OFViyBrtJX2pvH7ye-kHee5aI>nLklu3ra(o~i0jzqx1LXs3XZIP zGO({%7A#oC>6!k<$)jGwWK7OP#~@Za#{K4c6SU9BJko|+{|ePOU{E*!y_#SwY(3U_aPnA%k(lenjUOh4UP`sk5;bw^lR0fvTSxQWpsixk7T(Qsn z0n44L4^+;R z)9ik8>pG&6a)#|H6`CAjX5$o1WJhWwelDSuo%z@D_p|q#7Ys|0*3Nzdo;rMaM@f5o zkwb;0^i3+ymqT~?iTAL~)z{|)4MoYFkg;GMh>17Folk#jG;uBFOlm9a-^s`jZw91T z4V0LlcYYH^`*X;tJ@OC-o$-lNAE8>z5iaoS$s2nwCwZ}Ul zX+UDRrJf-dbqgm3k?9#SAjvdpTR}of6NLYAb)-vOy1);pCD>rZFQmk-^J_vsV8lI~ zt9G3|Ih|N1e~fSF-fZ)4uQ)!5wBSWBNi`f%m5j`H4&!msB1V~Q z+DlWkI;##w2zWWKmjH!^!ol9uMyT2GelFl1pFcr{ee~GjmE<_7v5P2giK{SnGo}Iv?>5`lfR9Zq6D?ZRFYF4 zYQF}PJd1%s_X=|Vd`z1mMTvl@u#>IPecGt;Apl*KlCD;d^d@WIi|~At1_vCm#K2NM zYeF1~o?vLpwL=!KugeE_xEkOoGA4l$Cj!x*#SrWa!bD-pv;(3^ND_bpQ@+52Bq!N; z*?*L$?qMxJyuj_nXo(oP5%~TqhJ>{aVmgqshYu684+=Sl z^Ft$;P91b=9$K!EPzRXO*0^^sM(F`lR*2br2!;dqZy}!Fqgd`37n1)_3V_Wb5ND7Z z>wnXZP>7-80r0rR7&?OBRI*}i9nd_7o=(wPiO8=9!0SP9h8Mj#_+~ecG9{_;v#LSk zl*s#(O9s_f2rIK9RjvoyCj!?B9*ruQ!yb}K9eVCz=@oNE#U>`(G(FH#Ded-@ zkLh$vx&kNpP#oBrdly*HR^89-tIhap5}PGnHIIqQ228Ui&sT?pJAzq~-#fghn_XW% ze*FGkIEwOgn{{Y^g$iq`ISDWb1?9}{?;Mbv&zr}G3te$;sdQz#HZ>>ox;o9tJk(34 zjK)GKD6fEZBPR1y`1c%j``^d3Q)|Pr+R^53Ec%jZU!en*PN;RKJ9s8puyaSu_QXo> zarr0t2(DFYinlNn-@TG6?cqFLlg|LSKj7c|EZ0uV+)<3Yc~6Xk$ToZ+<)ch0jp^g> zd1H#x%nlg2>f}ihlb&~hLvDX(tDy)udi`;FuenGcVZ^=_@_fM!T9iJkA`yxX{w#S z5ypghW|3mosRtQ`iSt+f1w^K46dkL3N4sEEDYIV}^mwzgNRn-L&L6w06Irt@hSW;> z$c6p)SS^2fZjgc>f2YX*(ck$$nkfIRTN12(;q;@6{kC~nlhdV=!=dFj_s~|(Lu&>A z2`M7cg5?8)fwi8sz>#LASqmbG3f1`$zn)MPXyOwiY$^`V+xgrk9!3!?E14zT#oB*l zPqWJr#blqgzwlmj^*UX1xjlRe=>GvhC^>8hi58L_Dj_@}#^0kS8bVG$O+dcSN-GK+ z>w_nboW^b$d+&wAnI_6y!YZV!s#Qr^Br!q(n zS8KUO$MkIOE+O`A(8!a7+f{+K&_FoSWCJd}27IugP%6@$8(Z15BXTaGd^rT~ zim1bWdpeD*U{>gAh{Ckipk%K9C*@{K(qu~hx!1vDM_836aou^zo0eRSK#lSOwqD~G zUaVnxoESI$059IdU0n>AFgmmcXzaQ9Ff*Ej3vS|5TMP;zt%8wJVGM#p!ko-sanNX% zpA-ZYZvGJp?v+A0*V5rZ3-XnzZ}G2L1L9_RxnvjKCEdzHDyV$k!o25iY*EkgsWonX zvD^q2?ioONfDW|hT??`e^~@P(?5wkH-(T(&BP_4YDB}c;s_Pwph3T;0>7G^;+3G)IpP5m6($Oh7I{=Uzd=gPLF^%+^u{ zM+xQdZDsn{>r^EDkUZ%DjV~_1{);>r(NW0TB+6TXA5JTc|r2C6XK<^LS6CBScLW zTw`e9qKEzAZCFepE&**d^C8WxNC#2Ac&;U+-d~uB2nWD%*+-K?@jpt+Cgg~|s+dO+ zo&W=5L?NMrJpU*?rU#!Ypcinoy&M&axUA*x zBDrGVJ)^GuH;1xySfhNTG8}2H}_Jk;1X(-`iXNwGL)>2$f5)IMGQ9G7dUk%=!0j_VR z@+;N}l(qTfDoUj)%Y`}TH;Y1+QOc^ID)TozT{G3nX?JEn7PYkab&Uxt1EbHM(q+ zo5;Z&-cqbM7`#U9akNbH!VCWrXzV_ze@A-_ms^p~Cf+4_QGQV-Az@pcE-2<#9mG|i z#Pbb}@YEu!*G5{A(WvB#a!Z>7HN%R(<`f2jM_ECdxl=0fgiW-^eYp+fh}@mn$I7_~ zSb2T7?rRJ}@$-jo)oVc}_fN^w&q`JGp3 zN5AqSq{B@Y%9Wb_Lj4i3%d}a21l{AmXBSK;aAEjp2s~pCJPUxIQS!}M!B}TZt%FNl zSJzl$*WV+2*Lne3;R>~LNdBssKTLfE(cfqAzEbT?arq2P#TP4eLRd8{nDjXJ-A7qC zm~xMG%B}zJ`d7Z{t<8y_b}R0WIrM*zA^)$t-2XE{Qcnd%npchRM$wuTW2K2ogDIJ7p`l`Q^9sSimu}CNO99j%0csb%n?B2;p3!@ky4= zZ-z1?EuOv8^|$NBWA=gnW^~>7Z?3*RVU*LWvIs;C+a9a3GORh~kPDiKBc8g0$Z%Lh zCbT%(*1}wv74?E6ULrs@Fa!!M@&4Qf%#9Uyn2m}Vx&gZ&Ep9@uJu?BI9f5_`NPVLE zgUWefrQPcZVYn5<$A(5nI|4n&HJU~ZE7FK|A+f-jab!NkpfhV%*z=2jpj$;!WQP@K z1$Y9}q4g%%t&bxO>fA+(Z5CPSk@GN7+1t;#@b21wPz}-}weTm(ZI2@xP-bUL!RnQg z@x;p&hi}_)rTD55ZY}c-{|mfE{zNFI8^vX)rpp+v5=|M}$}&{I5|cSsGs^A-#LJp( zFnt!KDli+&xJY_2X)#86nC_vMkR4a;^O5olvwYqW&Zg9ShEKf0JxX4gTJf_xkqLwu zjmr?(iR>-u1Tw{KL1x%PhF*Z6^sHNah zocKFk&}TTQ#bIqlrEYPToCXh5$&F9U5dH*x>c|L4!ElmpsV;=s29~g5)entk^IzMn zC>->!G=iMEDnX4E#0TUYcOxH~3WbLLUyQwDcxBO=HCk0c#kNziZB=Yk?22}5I~6;r z*tTtF$F{X&+x*hE&*|HJZg<~ruYdb_=30N|yXIKq9q$;r5S>vd3+R@It`QX6VhTuy zu1R9BcvTwlBg>#gY2vBFHZvC>EdmaK$K2Yy&JA*Mv{cYp2Rk=vc$x9U#YtvdRlWiv zeq&+Lg5?>D3OA8Szl%#~K$0wbOP%VUH}Y0YDaTJ^wU@SUj!x0Bt{Zff*qvjR^~T5g zt7I)KY~kD*j$}*OZK}5Tv&3Noq@}7+VBt@N9wIhgvs`IA5Vn?eA0k_pkt+-C!<@tN zM&G|c5I;3G!ww(xtHiO4-GOh!%Ov%&bdQU2O7k`G+FJg3bCnWS5c_7UC#jH0x${=@ zX#G_)#goiHDL*n-+`rho(Q565}6|*EUiysgOrce_xW7sKZT|K*HHQ}^_rl5; z26Mc3fe-2c8MV5#r;Z(Y-W>AFI2{Ve7D>|UXLzE1!9 zSRUJdE|hl=)3-9Sbo=)XLS*c38x%pb{>d)@ecxOt>V}nygpWpxTz*qoHUqaG4U&?m zgu#G66{Ga$2!(Iz%78LX)BD`#9J>7c$S`c0}tvNRHFe2vLwE0^FJ)*8Q z>>&u(Yw{R{uythW4K-S+oyv;ofLvS`k&kOk><<3EG`7meF>%#{1u3qg&%iyCyA zE5S)v?xqgH$Le%tsqva-Ic&m;#W67m+U+@^D^Q(Mkj)Ef4HCm~sVVWjc1t$e64>um zNVljHe(eu&d{7quC)3ega7px!VfoP&sg^@EH(oHXp%-l)GCVCf?AzLV3PTaoVLqNpz zO7Z+QLI`R?>I8vQdcoW(Htidae}*;##BcZQB%0kf*vN`0(G{^(5=#oz=~I++_MSaN zI!SJ@r`1}PuoFAIMcUUNgDl_BQmNVHVje3RgjlzzSb0e*$lW}Ae>ndVIzc?UR&M** zVv%$2)Y^ZjOhOe@pYZ#?Hcu-D%$dnQGq8sc|9`ii!aw%mKenIRhY#u=z7JswdlGB> zeRUv4Fy)XGjO<=`SV3zgd>*@Oa!+C}dvjqD``?ZJHdr(}OPC>cj)UaZoHXX5GSn15 zElS3WG&v5tRg13ENjndLBnx+VJBHElgY!2x{>g)lESt%j-MGohj^Hospx^qIJ$4Y$ zfmc@PPOXt44Kciyw>EiSFECFj=YA;(G#u~EGtE^XD%KIy+x(RIGiUT9WF8c z5IpCgaP;PkVYxN3bJuRVhY|&KrA%Lwq$Z#)p^i)_M>Sr^V6~#w)iEsy4X|QmT&WXR zacRt^MxEX3em3wV*Cm&EbTrSuYlz$x(qTzKVX&G|S71_(xZ{j^J_8E0xY_4Mg!#~S z29Tj?4i0n#T(pb@o6^&*m5Ssjn68%nBu7vpD=te0BDd?U&EzIni*cs{YZfNi04wK< z%2~Z3nRYT|MKbO`C(ZlY<1%M%+)7vXOv-G?4fkN=T9e23jU_hkbyZa>&g#e|M{Pyk zx!F&v_FBs|;?i|>9Tb;ihCPnpi^g*krd{?$HWDW&3Z!Yn^cV(`zs#O9W9<=bgl$=A zhix$zuToym08StxK}>YV_~Y29`@eYnTrNFO7|*bzwY^!%w?`ZY8Z9sAyP8$#?nExX z$79Wz(Ft>+_MneVR}woT*4b`VTVuQHdUkbJ%ZKl-VfK8*i_inlvrK>(goGE*nxhkZ zGzVX_&8W~kV-L?)UGaM~1i#qsAbLmZd3kg|@(JAn^Tg`;eHCN7w*&Pa)D;ejgRR5Z;wQEEq)Y(ZT(U**n8@7aZuWlC$7rjJ$jhm1UIUEuN zj&pE~LwjlVt}sk-glo%05*lRC-9IOvL22c-@VaR1o$y#!2B%icZY4wC6kFd}G@1$( zs%A+7L_6jgiJnz_@{9M*r~{d+GT~p;9a@?6qxaKGCTj%ITMQLW*}gJY8gSbj7$du2 z&!$G0LC~=j4C2j>t^TlN60bv)Kh)sC$eLym6+czp`)Rb4%Qea>^6|(X)jz(XOo#e< z)4J$j6^=o-@k7&GuKKNr)nlpt*0mqzf$Moq?~o;mqI_eJnm(+Xae%mu)7qndZH0+b zoyCq0zhMu)u?yk6BF69IH#R!6uh4V}^VYamP5{VRm#y)V*9x>}NYi*_ZQI28WQTaw z!QYg7y8r0`YHGJ=+Rg{XrZIb$PyA3~_n7B)anF=M_oswuNv( zrSNLb_T%QwAco{M40%= z>?p!?eI18Y)*0wUDs9<1dzcheyFU*w{?)!hO+K*Pm2D3`p6V)NA;$h&sc-kkqIvku2=AV)E@8^l@k)5f-zKS2xNvgP?tO) z&3Hvk&?CC!3M;-wRH>gWmHPIO8v9LQy&`< zOw+ZV+;0R2kKk14MkN`&-0Axae<+qaO|UkKwB5BN_3;~u4&DFMox!cJqgWf$5E@*H z;NFr&><#=j}? zb~DAeGasCijnC4Tn=qiNTZlN2IE2`L5mn@u*Q{!Z*DBeRnZa&q5V?%8E0jA4(KNNXi#8vRWeIsrjYhP?X->2K@Hjd|1!DBlw`;MmVGyUX6;k5LtWqCR5ae~5Dj zekYXu2rftr^#mS<#QF#>d!Cy7dz;?;R(efk-CI&Z;`~~2H%+-$C>Lg!>|wAtWY~Ct z#I-xXt-5@>r#9xh6a!*BY1u6iB1-u8Au0l*$vSV7J&83u5gEd_e-V=k9~mU- zf0E+_@L#?#{_h}J$rNB|sbpwxX8ZpbA^tn&NKmtIK%2+V;Tx8ru)2`|rzbkjMk0?3 zXyFf8%+@3K6Qdwb{R4?+W3n$Zvoy=s)b>53>f|o+;sGqHKsAT3BsP>ximMnj>X??$du-K2$Z zczwh%{P{Z|1Q}dL+h#CvICI_~MTF?7+Tqf!2kc1N*s= zC#@=BbY!!F7<-IraC*us1t!p4z2v+Jzd+xKwy`6wuMf2@ON*u*4nZ$*ugnFr)#T}5i{N)5f_6m3C`GF zWe{dG^Y^eU%;4})3Kfm3cCtpnOgy*b=7I^m2|X>VUnPPv=dr4VetT7pKWy|%mUJoF~Qyb(!s1_1Iy&y*+L(#EP(%q%`n!m0^P z5a=T7*FdVO#615m*w@PaN-4QoADZMce$zpBkA787cGnC^2Dxo8>!M#)1-r0Rqj@}qbcbZHkYDMUVHNQ zU`(0YoZo}tFtEM$OWWpTcV#Yn+EQ-&%A{InAfe1H>4?aVpUP>5*Ug zXr(Y*>hGSK%qi?wL0cVW+jMZpuB&}Ku8Vyhu30Y51%F`Q5FI^Dw@ExLw^7sCuc-K0 zu37>fk1?P-jhe7&dY*)Y??)~&f-)EOWHlrc{L|Np)a^W^`=K{^3d8KjOEb%~G4h$p zdI#uPVvnqJ=HTHIR}#BoEe@~IhcVGPN=Fc_`e)9}=7}_&+Tv9zs9WCN)Esk{g?Pec zq>F>R%oy=35{B3u=A04)w$%lRSgkdNTSilXjiPWzDAHwr_!2-knb5q!cYOMR4(-+{ z7@dcoJlnaAW_a5U@o34k0yX_oaRFf==`DuVVPRsm-NGuKjdMY`yP09GNhNoUQHx>0 zrS`&<>L4gnG2BL}Q?yleMZ>e(J*mq?Cj|g=dSa2}&xj=R)0;m&pwYxVdKa5YyrnJ{ zTFgBD!fdX3d6Gr^Ixo*JmX=$$J0mhBaa>JVwOMskDX`*oeF`i)LjZA!`6=OVCHDBK z!Za^Z1y7FT=-#7zCHdp1=uSIV+ZQ?yg3u#~`V3(YRi5B{xRSc*o#ns&ifw^bD0yigO?D{GtWGqbINTCZDot_1?p8W(8Q($Iu3 zAzF1|wUCA^^@mr&^7ZDtQ1swQPDI1jmr{si%v9GB%0pC|M^}nTn1zjncEwRQJ}F&s zwJo-(t^R`u=AP$__7zSwtdHF)b@XX1vfJdH`SN@=wDO9)vZ(X&L)tC#^U4>5eQY@N zPT|^%-sIv}Kg;r#ch;Hk`Na-jY^!gM3s=$H?A}?N(mOi&&EY{VOm1zd^lM`<;8Fep z)+JZ83pe_m$dh&FzllBvD1H1_7!1Ly^x%rNw#Lbvw9J~h@zx1CdGKz+E6ugAlF`V- z8+xOcSQF1KHe}4HDxxeV^v~QZQ!ol)n;VH5Br12jz?`%f2SCra|AXN!Ley84Cw_4#D%6<98>2=_c2{#-%X#szE55yn=4QC5@FI+h`D(=he_MY;C5|6{O>_Ld zP-A$Snh*c?+nx$kzpUs(_sQR4Oa*z2?>M67Hn=9M&CMqURmeWD&|%;x0)Getlby;7 z<6(m{)>AT`2->>5eLg}VGk@HIKvBUo%_rNL@(nNKyo~zmYkZDZL!y)YhC%zvLvb=p z)nHOVp;EU9!X#^WEK5~&B}S&=p|LijaiL@42C<3-sDK)Y!5{K6x|mjc(l9}h5|ZQL z!u0V`ECn}4z4AA-iK0C&b*~b~F_asKSnWc`9BeK41Xx09-m!Ki&=F=pds=>&Oc@xsn`jE|qZ6&<4kD0FuQU$0nv5yc}ZHgsNs1-reU3esPSt_yTbLXvJ_<)_P{?_*@{M51ZV|Xzl&4@%Ru-6D_FkXm#UoC+Ne_#1c>8-LJ1g1;uc%Z}P$3>@7JjSaN$wqEGJOAc8z)w||SMiRiZerUw zF{Q2NB7u9EKG#8Xnv}mc4-sWO4vN`SSu5wX&T!x@v+pMMe5hUf`W2A@%XtERvyyx9 z{Xa%D{#`??nOy60@yYwqf8wFf`0D>AyV~|2n?%OU%FL1UpH9%q8uF;W(BA1GAPfD8 z%0vJ$rB)h+mh@=U;i55qbpzm0joA7Nint(n9i zCaq}Bgljm5(_2hhHn}#r94=n`Icz|J0vG_+Ed#n^JIMjSBLE!fHW$-N=nS*$D|D%X zkPzHRanIiX{hSCWCTdVvKpEV;^^C12anO14u+F@O65H%N-E2c-K&zP+w+kMdaXvi9 zm_pm_oMYS?o8tdJeyFvL{oVoGni!nWB+dghp$k=7Od0 zp_^d=W%yUPk#sYA5Ap&o1PW>S7Nbr<>N!IbnON_bWYtLIAq#Nw)lo9y)Xkz z#KgH4Px2$tt$6P3U7e{y8Ng2{7k+ zx`J@9togOO*f=m)SNY=&DnS#>t0C!yu=AKO(^ZV_exVq5mMJHdfZz{f`dX&BOEh_h z*@EGQ7qCJ0)`EQh_r&~tT6Oklf}ANCLW~jo*Dm|;R3XU@@=g1jA-M7P?d-NtcmpJQ zBODlbCSJ-cD(BPcl8_y*{?HJ`UDEjXKyoX4kMY`Bh&6p*kJviRxea7NcMy2KG=Akz zNt)Kf1@k|g%aPk(rkkL*OR}4=>GGRqsD)aqhkDfXdNz`Zm&OO`W6BG$wooVjT8@k6 z;2+dd+7rZqc)73D38@BzH$^TXsP93`!hrGzwoGN0xd9{*-R1TeXqG~TnqkW=)UrTtOpv@;iV)As8o z)$w=oFyg%9oN^4(Ri?6A+XC!HQSYu3%`(aw4Ln}FMjd*r| z%d|e+tjls+i2s$WEJ-tg8~)_UpFasp`u~fXQ8u#zs92jh{)4ep(Ne+_MSD-}7^esJ zL+2BjLxnZd4h=~E1s92tRw3 zG)@3`-IJx~)@nioW|?pfJaU-GeD8EPI)8f0%JLC~vH{tBqoVD7#-9+rnI3Y|4j z{VCLTX}+lpnEZj%HI;>;>iJgXfpYHlJhd53RxA{KyWtz4lJhY1n?&%Bw08VW;CLXaKdx|Ojvam6xbd|TYRi)(Bw1U@ zd9j%=+vrid#fh<7K#o~2Grk(zy6Akd8QgT<;i0y1XEkF#QsywaHA?{bV7L*jtn9j; zDg2izw0#y>xEVj)X_&%u<{|9axl?FiC%kce34rXlEYFFV0hI#9S2!nGOi*Ofyukd|HMFfx9D!|p!UlY71xW1pLKi*AiwQV7PJia(6i!#DFv>wFVs z6X2e8SZLHS_~+Y);sv8*3)&(wSF5(QWPFjjEcNU{tyWc%4MRoipz#8(4VSy7iDI(W zp?WB4aTh`S=GkkuaimeRq_|Q}Z-8Jtue`;94LDyGRJr^kTq9Ns=WP(~*z*gQg_}x( z7i-U&T9f^7!@eVMYdA2j4zG3?ZGLwFR(BBVDStW^q?R?L#nzTDd(vJWfHg}rm~e$W zP}1d;N?`~`I5S4=Cz1E7iitH#CdpA-is5640`4I)th_|mi;+H28iT^Sd0Tb!R1=o%S(($Lp?1~=~sI9%Xje|*o zl`EHJO#;jYB+Z(u&{>(9E!M&XD;2DFlp!%CNwx7EtDWY1X5<^G$H8gJT?pFU0t=k=Y`&O zI?kd(u;!t@VSUM?K)F>G9U_2Rva6l_szv@{-)&9&a`va<@0(n=HANb^h&u>a|X< zU1^u@%_4G*?$)5-X@S3lMMU*see7#;xxgVApx!%;`-?PwZjS4pDTe?h0pfBI1Y|mL z1fD z_85l5L5C?}sKuh_1@N`^DcO+Un05XG4^wyR3K4aQ;K!-=7aI2n^t&3Yt`zW-{H^M3-Hzmq(HJ?0&@os*4ey)UbFo{#*l_e5EyW!PSB?(t(4scOi{5INsQwkuj zJ^ykx(f3|~+>>BKZ6@z~mFyUO!e|h=^^=GA&}fT+PW%zs+i=O*CIod9z8W6TYWK(o z8g}sD%;Vd(zzNd2D4G4A#Y~V>&S2mMiKE7$4OEBdVTX`fzzw_(6_lfH8xR>Z|t9j|`zKJcP7%wp= zC?NIIZHHk|=Y@9!J|O*TQe1bqRMp0(4uk14g5tjq^87DA;-5?kw5#$w`nwJDunajv zI7yBk5=97i%3nC5W~7jQBqA)7KqNSwTc(>>-gL5wcH+=R&8Ei221k{{?FG#_O_~L` z3iXB+a|^9A-OtM$vms5V;lu3$@J*jQ^r>@gZ_VN3b8K@pjY7BOLohy9BS9@2pD-RI z%N(WKFlEON3X2%!D z#0&#S%xnp9$hJu8)QUEuV<9+V$yKFZFWr(o%p+-CQ%pnS)xafMOJFURFeb{Ss4%5A zD;Up+0Xb5e8!^Ywv*>GvQv@LA3~X~JG)%}A1x41gC1Rp2#`(uow5Q{drtR0>9bJ{@ zC}58l&Wg<(7g)pG>6F~+i4ZR235)ef6>MW8ZWnAJu{X?Q67~kjLC0SgS#2%1{eDpOInmIxZ(NjJ~W2h=H(n@fp6?T!)?u% zqHfqMg|mWgszrgSx8_SBa`122iVaHZ75bcC-K))(f5YZ-F5ohR3p? zF3<(cO}M)@P~ft*`aN#Cp7gN4i}e-v<7&GL=RH_WU6LmbW0sG{61yflzIDsIHK&4% z(kXzn<%ODs`49k9fMJ;JBkRe~prM>!XhCK%6f|~YOqGHggda7XR)*G9hw*A>ft;E^*Q;#a0(Vqkj z1Kq2^S$9gd4K2`EO9)$7i)Y27by26fqODFXmK@_G1Q*(Qj>;?B(4Vx#Og{-3eDA)3UpoWY zU@T~rLlcU)JtDQgeFx!+Yq%m%5B$RJ#9ZMl?KQ`n2mKu9GzVx<`%`FhN1g zay4eh?x0XNp|Pp^wy1~h*g;}|WZpkXEW-r@E}xKeIYZSl=XPkt6Dx>IW9@DYkkFwB z>fVAqJxCk56A>WGg~viQWJ7q{D}DlQR0%xv8#1T(El6zMg(d*|IC;OL>4i~&;ce6W zj-$zit}4z|CEx3QQ>_c9_m>Dpm{F%1Q-^LxHX7)VO@%ZBTJoBh-D3<6Xyw(f6n53g zWSk@(n?#LrfVy+*{T6KNnOUs>6^HXMozpGK${fW^st`#^MP=^IB=-}Cg2Z3xZ-IdG z&Aj+wI8)~dndgTzww%o#iOzs+bNW|12*MMo(_R%3j~D%g!JCCaYagbGni#9=cyfpo(;t*en5u<iXO(p%#+EouFC9E3L_h_bstuHxh-R?(1IJ9ifYZ zoveSM!s?=b&^T~11^1I?)XOIuy4=H;Sw!y4zR-$g31B60J`oJDOzf8Xy`%(U!U$-D1*CPFMF6edasGHYCS zu?O!}rmJK=Wp^sm)Y5u#_-fMjE}5IT16!=1t4u@qt@ImwDGaenq_K)WpcKPmj_FY0>urglehF8z;yg(6 z{Gr%gy9#5Aeg03|{Et1g@nbCSa0ca3W%UXLlj4N<1}Up8^E_^)j3Ui5tfe9An$V7O zMdPsGw==i)z|WM}Ou%|agoZOm-nM-u?_3$WjUkVcIf>RHy5*cf4+e)fd0KKb`ZIMC zkJzcJA3hA=VOO|jjhvpVyde=}j=PV;gXTqhenI&K=N9gG?DtsM<*tR3ym3>ZF<<-ac!G;naV*Ee)j_{Seg07plF{eP1EDqG2;3L<%9 zz^Fx`<`ZdN?S=UjRN#e#gl)q<>LP)$`r0fS`U3AOTUOa0m0lq{*{97}e+igvM>9|8 zMhjgk7}BS#W@NbSrz|f&UES=M)NeEItK9yW^#!k0+(pxlGE^3Y3kqY3C~NkErmB=; zS{7`zu)7@{kM}#FZnYH-OH-7gX)f4e7hbEdo2*;p0@KQ~>UW@7L#@I>d9tcA#vz+s zW=#w*;Ypi?J8QBW#9)D|IEwiuY`Kt-x$d?Kf}OXI@fbC(=_{zSNGb3;TDB*oHZ&N$ z$1c1^zzpirPmZeA4$clA&{f~joyN>G3+-2^=?nZ@{4O1~apZ8-KFpD*8;6%~Uo-qH z^6auQL#@jqecwtobn*B#*iWN$WR3>zsQttW$GE?~v6)!AX;<1WD0}L}6UcF|9U`t( z0VFB;;N1kjLIAM3q|l#RDP zJIF+FH^wOWwTj13LTtby9bPmOd4#>GuUxCL8NmfS!HuPvu>>$ROEQg#t~qs={xr+w zMAK98xVCDO3MwuPHUE%C4w`;jI3q;JRlbKxf6@9@x@TuTFP0D?VK1JTu!bJip_f=f zu~QGDr48a)D}^DDp1wSUm=YpnGpx?_&iBDhKK&J2WX2)rL*9_`pl9=~?rSXdn=f2s z2JOU@lm!-uc49g!^Xn~=9y!$4dK1WvoIS=~tfz?YP;c;MNP?B5blALsJ)70F7gO$7 zCph}D_`YOHc^ba3<9++4=~5G+V_NmzgEjIY733=$d2gfq)|ks*X0w?>3D)@kn*HcI z2lkVN7rA7vX|WDpqPqZ&654lKQ{trp;M*6ZqdvFXLz*q4=*l3UioULR(fHwcoQMAWFX4>tkSqd&oVmof^jS78o zXI8Skdn)VKl}7u}=z;4K@2HPp4<@>&B#|mRp6@t`!!ucj-cle1yb=Lgh}LsEn#6ZU zr6yxo)J#M5I>)5aO4**sVagcH-uG; z0{Mn186w=y{7PZGZxmXg`pwPWc{Qc@mNKDR(6{oi+e58%hK@FvSd@&>oad%ha^Pqq zWKQQIpg$)oVdD|zg&-8FzjR781+D6FqO_fjl?*XqjB)c=L`h_ZrnWwMnw2-`!ORsr z+MIGrAO3}@SRcy&o5v46UPYF%>#M-na`zxBXkJE}X8M#^VtMRSD2Pr$7?_TVt07+( zqkc+@M`m4=rOuTMk_IVJlo1u}SzG+yIjlx-9EQufVkVwav1bYq8S3GP?sIq)*8QaJeSj^yERf?;)_1~1t~|&FvVRnI!T&iw}-Z2?_Q96&J_S zFH+^&L`l#9rQw)Xig4B8@~c(Seuq=ed2E$X)q2_^JT|ArJu~ilUGKz|>9{CSekVG8 zJY`$0>`Nc17~>9YYpLB0ry_F)0FzUSBLxR_y|Tv4gkVGs`Zx&MM|)fPCkS%E~$_8WxVaC z@fFaO%R(uA;} z5aI+kxTtp)5i;Z5i=+KLd}mT!o;`Qtn%S<1GGOthOZ=3deO=UmyJAi7T3#fC1FGcj z)Cy5CF|q0(SoopAijL{5OEnddb)})SI}M#v{ZqM9zD8%16^YkFj1`K6fP`g_j(^() z!3w4tW8lNlnXHVDmFPEiFjQ7mN?X*5Rjon(uBf7Gz~D5QZ(f>0JF)aZjhvnIWS|?9d^^2k&7WA;uZr5j z{Z(ak5;3n_Gnb5LvEE_tkR@p!qFusa`3yaGXsYY!hd<6pl&x&G30-mWb{F|x;VBr! zy4p7iZBk7n*yWr%!tAc&ioSbQ1tjR^6>*+vj2L_8LirF^U(84{T!Ue@0v?_rCkYFNlTz)ZvRk%lC+^&(e*0|2nkRVD`q!{0V2-KH)6g{|;yW31fwAZ2viJ zp=|L9VA1%5VYL!PvxQW2F_1M!46l51iC`eW%S{!9z`L5QiU2jI>}^WlK7jJ?u&7j_ zq|dS_KA!^@jXwj0BxXHK&d2y3#!`4*4lbgzF7?r%(a*5V`eY-dSjdZ7S$ALvz%La< z!3av~nJc%M$d!3}lpx}$StGzNWu6bd%18>(&FWGn|GJFr$#Ocy57IMOU1Ga!*WbI% zx0Rvc=%3n3b+d+sY?O>Mn`Q2Y^Jn0$mvgh@0b*;S6!xoy^H80aW0&wINjR?zRpCZa z9VBG`woIW1D)ep(xzx4lht^yD%81vsJ^kj=*ZJPb9I_hdMP1pfU5>*OzlPi)F_EX| zHfq0<>k=Y{Lq-$-HZ9viQEjn19zndz-#Iud%2isV7t`KaowN_3zk*?>6z4oGc_XTH zZB<6jLSAOk>@Nq84`tRIxQe5jBM&6v#(uVF9UTUv*O>kk6t?eHt_S8kE^o zc!F|Qy|fJqQRYKo4~YN687cY54^fA+kEF&}rT;Y?F6ZWZ7L9S;8gU~9I1m}WszxN9 zM1K}us}ah?HIbf7IC)fk`V!I$F2(|8lF_bzVmY1$kAzN+IaV9W?dselh#cjEbb?Vq z2~~4*-we6Q1*UONCoc7-)+nAB4_`_xfYIV2faY3288lzB2kK|BgUu7 z8=&B(1|b-A{wsnX?64|ObZ)}10%f61x|*9>{ej8$YuQN`YGO@nK!Y=MB3jf&t7kBJ zIk*@7-jgt?me(oIan!VDKu~11eMooJ&9N6zn=iQ~N?EkZaiH9MIFfxv@N3}x$g^|} z3eHiL)GbOX3UCmspyq-G5>P;5iW_W-ut7P!?xv;bS79V&8iy9-Lqc?im}z$uD0Ghs zDw+y|Q`yOvj5>D?-)Piux-WpKQkD5}e8efjBH*^n%Yrx~_mAjzanNUytuJ zZWZZfVtEb|Z%G#)lU+JTRfMlclTdmT??}QhgCvgzXgrr>8kw>Ev4D}E?2urCd&CqJ zbThM%*T`didc9FGEcpGja!_qZK)f&}PD*T7?x1l_>ufyX?@@!G-naq-J^m;LL&3zj zULLYh7UXn{KofwQT;jow>sJPt3o;E|j1_DRU1W+0oX0TVD(ZlJS_YoHDS_ko8?!Pa zfQjRZL%LsDW0>v=kH(DkGjv83TKV;*P)WJYqH~xBT5l!&VR_LMKexrW){&peSkYid ztFX%HfR7nQbxZ4GnLXrdZTV7D?r6LgQ*Z|4JW*9<@V*(mYhhpu*jW&niM=-B;fxmFTi_l&F+ zo1&go;w{K}CAlX55xs6w3woB#@0q2j9__O}RXl7<=w+J@=STew%nl{??P$xne-G0c zqI)T^Js>U`wTY+ifScb-bX>@6bgKNVjmLWFgj%(o9%{3d2H6SNUVV)6y&=~Hx}bC8 zhzIvK@M}Qoa$NCwZuTMC>W6koO?Qi;3iL(GykaHEwEJ?%zJe!_i44lTq6x(99gutb z(m;BfZGW2Ng>G)qp?>1jfCmp$oA4PPEak(Fz=>bYALKJHh1yk?fDUxJ$+&YTdB(}p z5MUxNOX>rA1lPQ=8DJ=dhD($V!Z6;P^wXaA_neBl{&f4jc}5Q7jDy3GmRYS8hw!#( z-tx1P)wun}Or4=W!phvAu|z!qMYWY`9Uf-NJ(9BsA{VKhrUOFA( zMV-bkyfVl3VuC7zD-}ooz_AQ<|t2u8(+mv-b0AvG^?snEhn>X z5Lqwncb3Z8uwyXlEK}kvHWcsFC8i8j>6_9|gM?7+?{d<#`Lw6{+c}gxM(OU(15}Dl z*ZQ+4sJ;-h4zKN&NQzZ=Il#Q|e6Q)cxPO3(kFcl&^GqexvAdAb36yH!p!@OH{6tHH z5nWq^5?x*75&!p-l6KRNF(*tvR_weMb{v_hHB}*>RJ=OK!0&%SmDS(+%ZK2IG|&ssd;RTkGeuW*#ilt8ma4v2H0M*L0IMdjNLG)w{6nUXQ65}fg{*4Jw_w%!WdPvAn{MHcD11lJYA^+X>im=rIbB~vt z3>C`}@G3(Viytx25Rko#(>*td5ixMOz%v+3TVXlLz=vu!)FWvz?y4M?pU>!i;LOuF z0Ie@6>ddPB_+VGve^MAxuST)t2T+XBUBZm(M(XD*7-nNFOVTVibw!hg)>Qfj)x(Dq zTZ2;;j4YT_EfKVV>w-qPCNfTYJ~lDfoqzW8VZ+vK%319o5=$Ne$j^ACEtAitWa9Z~ulXBy;Jpd^zt(TNCH6oeFyGbHUt;a$*1zHK z;TS?^4shZ9;E=o67}(hr%ZECKKvC7={DM%c(6|GZc>Hyv6Zb>EC+g*6YGW|&5zmnE zrvQa1g-C}F$`O%aE#ZeADDtXm@z)QCP~!dv2m!yr_kR&V{URl-L;a+Nh(Ghk{zrA` zzf(hs026@g|4YIqs9HFz8DsO+E-v3`MrFM8TPVBt~pg(`hF zrMc+T7}by#nrW4jBDSKUn^Ls4qUd&f?3TgeZ$RRi64L)8Y4SJ}y=B6fEY8WHHS$h&blqvt>^(`8f*Zd-qBuh6>asQ zt$sL+y80#}ARBh&=!CSHCr@M&TYYk((jA*6K^6-}giwF=NTB2Ke%e9L6 zkm<3r5^s5D0~KK~U~Q;yCtk%1U{E(!Jm?Ph`63Xw83(>pXmb%#Eu%$Dxa+C4+67B&Og zP|6|l3=%0}Gk%4aswXs^=Ic0xAIDRHHn9fT86KSAwv(g2MICZd3nRKa?E*Lh_~Bnr zb$nXQRi$eDabQS-0L$JJ4$9N*_H-OJZPW*oII0tk$`kOyDN1&<5KihqP;h8h)PD} zfhP`!W4oW4Q+v=w!BuP2#ST2a&GvV0?-0U*tHh|LD?|K?Eef+&<-y6z8&+>(-i>)X zfM$rb!$!D;)IdQ>ofPsf>?mi;>pNxU)qcZUc8D!wDx1&_igy+n*H%KDx@` zl!|&6NAyFxp0UL9cWeAwTrR)KP&#*$1M^C@hfD4`D~ouM_^Dg)Rb&1EHwXnFN#A%F zrtICakvibMDDcLZkml2!XF_T`QZOlaize;nA@m!cr!Ok2pz6SU%-({nnY-!!TIvb% zVw52#ob2GaLxj|^Va~+x$@5z%six##l(^YnzfUC5NJ^HSDs>Gv@5fH4AL*m`es1gT zs=yB}I<_OzPuo1%&)IW7Uh6aXa3Fkpoi>YA9cdyr%kQZZNUaNmnDk^dxq;6V_dgIb zor&Y(A6TYbWaH;~KL2?lO_w7Xv>)vU8||gKIwr#6o+qP|2@@4Pt9^Jja?sLx1_s8?DG1i)o=Dg>1 z%Y{Jn$B!mjrKBQ>zA`^EUcx!@WAB&T$X^obF( zhm_g(1@x>6-E4-F)aqGR;w^j1qK`@)(0F+>w)x4W+=|hU4MUBW- zh60)syc4&9g?qwCyXANOP^mdU^4aD}0U zb3)RsaSux0#*V|`;t0Hrd@cZ2ridP=heMCIJ76D%lW|xd3f58vj0y(i3I_^BBQYXR z$RXx4^{kDCAbKbUF-tXU@~be4D@aQza^8Me^{jNA%JjuD3nr^f7Dc5q1?w!y=Ecsn zWI3%x7HeEM>!O2;nh?LZckAcz$wDcM14y8|!Zy(r=rBZb7{~24{nx+03 z3ozfCB%+Sc1$Pf1Q{0ep;>K?HLU1e8rQ48VB_*f8aJsM@vbR(_qjkNKq}h*0n4wtj zQ1t67KxvuDC%MBhzWZ_Q_=K0S_ii1?HXE!Le_WH6jXyKKO;7lz=(*+O^7=*S@%`7f zQJx*rAJXd3kMU%OJUyS1RJ%qT;?mR+B@w-3QvC z=FsQuXNR|e8;5-8MkwrXEYvId*pr&`o|5Y0_AU8mZ}pR$@5<+5+z}6wGgZf2TJFc0 zpgF2SrS1pcf7ch&&B6uCd~5P!zBR_2|L05UKetvDXA5g$F-JQW`+vKu%Gw$Khjp`( zmL0MJ;wSu1m~k{jt@1rE8dX}F^CEN_aYYGBad8=832ASQlOHKFI`thGkH;@Cmif%H z11}ff_0HA75L#9X@0B#BhHY;qGt;S>?QP$mVJ-{C19ixQz>bVp`YK@^$al!ljD70i zx#Z3gVJrkoMC4MfWV_u#5U_RKCNW~~`~C1O80vup+KX_TWeh=g(SjMuo7_9F;l;N0 z2+xY=u%n5V={hzmOVm0mN@gP-73)F1q~==QBOO!S*fq?sfm)Awrt6tNS~7uL<(o<@ z{io?6MyI$g#%V;h9QX_9ozvTY93Kp(#FX;xtd|YdC)ps@xMXlqWNi}18!5r2Sr($Y z*9c_`=mKlt_!lR28(z2(rKBXbs1(d5A>tjWdq@`Gf+v{qDa zAY(BTd>^*v&+CPzhO=Th?Ef;L;oH7@rEt&6Z=JB< z-QM2ts1Bv(NoF?}JB10G{qc}w&SM{bK&at$fDo4)Ijlc8p$5vE2|va^=tM1M&AP41CT|M>zHp1>}L_!bT$wvFWL14{u&`+3%O z2xTbN-3b_wFXC=W>pV>CG|?W0W22*)Wxl30z&Jhp1C_*t9HgVZNut^rN8td}qV0jL zCSG#V2n0wuO^2F8=)e1yOv;a5jgr3a;KcmyFk<v2w(=p$|r_`hV~aE zLeClRUt$7?Xy`Z(c*Cw3P~pT5vwHVC5HAXmX&Ume>!H31iz+@}U0d?Q7@HVX82CXW zb$b&i;Qcj$AMtw!FeI!7CKt{REsBcAgpbWLuSs!*N5)Lq=n+skR8y~=@R<@&(X#}Q zle(>4acgFx5NoP9RUGw?=(j=7^O9~BuG-n&*qks`t5ZqNF}Qip^*3ES%Bt^QNMS&N za3=DBee=;Xi!+suLBtEhKOq^`6z=||UMe67t8)MSLN>nhyZ;xd!hgG@n=A58C|6hHT{|^rBiYC(OEU7M(&4mtMCoc}cE=#4-)?&SAP! zym9Pbm#{&#_xtkDA9zl!_z=(*A^1$yCxCeHZt~WoeoRpjv~ECkvL6+?ZveF0Zbu%2 zutEY3K-dxt28!M;@NO9RJW4)tImTykhs%a*v)wY|W-gV;lh!ZWyl}?p!}mr(i%~Gf zC?}F&rK;LqUFoC`;;!F3Ll-5S5SkWxO&W2v1N!B2CVkSPM70&e*4L?Q|V+?s$X zrEBuYz-L5En0qLzU=A)7H-1{VP4#eMZyR`%O!p90smuYtlkdSPz}p9RdKQbqjq|@1 zl|^N9syZGeHe$)2c?aH_1Tay+K9tZGK@|sCwPqWhRS+J zEU7i^z5cc@EjrK#X_ib$3-8C2SQ`U)y?v{(72S5`Xz7IzySC1WSsQzsO}QWZ;zZf)&eMQD{f!YO&qe zBz-a*L~XKe19LLV3REhX{^sIeu%qysa{4~;y{IiXu@U4UqNeS3^FC)Db3bQxyzbX* z@s{<(HUKjb6d$8z4v!lqDcb+284;&~ z)K?V1Py%+T1FP9%nUx+-wGe+gjF~Qi7L}Ok* zVih{~7^eobfFxZe7AtJdWHEr-5p$0JB-vbCYP7v6o0AODN@ZJc+`u= z%658JX5*V{Ehy1pa8kuv(O zxbt*5Bj{`4}9Pv@lE z_f|K>Yy=9|85dLq7Gqq*U_~$eUhZsoN#G#M-9n_QA>QaZe=}~T=UYACBr$0i>@Y3m z6RKfu;#)D`s`qW?im4DO(^1Y(l z{r$7hmnBNpN7!u@K4Nq|2d_nYKP6*OI5sUKV^Ub$=01C(KK{zVYa zV8rX>Ay;ZkiWQtb=$L#2G1whz8f7fdVEGslub?|r-dA#12=NbjUy=#5LBD&x#4E&q zU$|6K8&!P1xzpF*mS6u1O71`OdP;VV&i}CP`uEN_br^ST5x}RJp4;;gsl~i)MiE6} zbDZ%!GpqOv7_1EUhXc_n3&8yIh=Hv*tR_{yCHT(BRSUwG%uj_;cNkp z8Tc(y?;Zq3f>(%+C2@4A#CoWXDRFdZ-%T2ZI~AnmwOS*~(m@U_;a>JN_8uVnq(??6 z{yd~;JLlk6ObU@*z8iFxAcPOOaXx~0$al5=cB!Sqv;?s)x227-P-DIXu`sWuVDLLy zg55SdP$+VHT;SOWPTVw}Jh(k>w^8BOyYHmFzlLFP9`-{?*J{^$8c1b(VuL=^bF)#d z$OL^UrTj>Z_|WfQ;=hXQ|1i>%uS{dVH2H6nySIoY_BRX4EbBX4HL_sJz%@RxuL@i4 z5QDibU8%8GQ)S_rN1Nu?O}bVj%91%%%_hyCMHydcpM!U7Ppy@u%G{2rGn*Y>DAVo| zHzO-$)#w+7gkD$^79)nOj}FN9)ippn&9Am*>Ey_mT~snTiV{kkmIr``J6AkFwb+Nu zc%CJ$RPS(c#7nYQlVzk4@^CMhnX!q!C%nm@sx!Ayq|1o^VLx$T zC3A!xb}h0lVOgd#3olaQcGR;fZdJ3A<2-jJafEN8cWR#JaR8Uwf345UG0K2;g<+jy z-Ux!UNerW6X6fby8LQ{eZgwpPR6$jsbtJL-%q85f>afkHI$@V+D%HkPl5rZ$0m-rJ zkGBCdv9=lR`nLxkq-mwRCaD@CRHN28Z1-i0Tk0A(7;4)v zxOfsvo=7eie)U5=$F0nS6@@a+R#sJUfH)pO0ty7HUnDJ^+UaeK6N>2ZU6DssadD8% zt2e&$lbk!4SlJuRz1GvH zs%=ab*-G(R?gQYsoN5KKgjstD`cPF)I>S$?Srjo|Cc_)bO3BJjk5baI;av&v$x~)r z-QBfY)c*DRW|)!#1@J747niQq>g+k+tuN02l8q!py4mW;n3cAj>*!M&S4xNQt)Z?( z(}PzLDG39OdA0gUBnHLWcw=`tPj>7E7}?TH@xK5_pzwkl|o1A z{|+gyS8hr-Ds6+78ebY_=?^#VY}DM9!G$tw$f_bMvzs|=$>YtcvYRn!&f`53hcnw* zK!7{WeBh;ZbhI01e@_EQDxiro(u+`#qB#gES0PB_8B>f@&MHDH zNO|jysSw%?`XXv-r#m(-U@*%EeprbiT2{3kS8j+o!0`&h#Gf=RAgXgV6(-nFFQ%MS zEv4M!+>1y7)JUQA^VcQYN>4lbxX42F38opR!;TEjI!4jH%2)lWqRqGBrDd z&QP6+V3awe4f05AQK{2Bp<aNzKJ(5-6io-_hcTE1VC61ER zYs*YvLajc;CgTm3HtXI=WPz|sVTMM) zQKJ+0Q`S7fqh#^09>x39I_79f*6cC{8Cq_ni%RjywWRS?St{AKNS;DZDa|01tf4>% zOs#fjIKjf@35goR3RCGcX{&DpH3T(=_Und%cDH$4ce+39mLV5kDcH6%w^4AN z`nnwIr5+-S$wxYG*QJZxnRRfkpF(UoR-536U0tK`eZZdoqKfM-K|gC2DFO3Tn!_S| zjBy&=zl=vQGCHH-)aFimQ7I%*m*%x*M%~qAH8iY~n5;Zf){lCZtydEWT-(tn-f0xB zSZm9^ou^?XTP5@Ouz1~evnC(`CjJ5EX~`ke4K`rcp|ti`x3%xeYZrbw?(u4AoHdU2 zS4tx;UL`+J6{rUhpRlYj!h!D`&`MZKgUU*fQ{+xX>)PNFx~}kq1hZ0Xw(vwEOJ6{M zGLis02e2J=X6P2C`s8yT8}2!?VO2Jm)C95m@jmXi%iF2C8RB_WgMN4!*m=O6nX}CB z8R61ugML?o`;gmjz5+MSC60kLdYC1RK_=a>$T5T05~D1s%6rq^1bd`PbB`|I`MNEM zO1H^?#i~cfLmHJH#q9%guw!`I>d2P&fNqW<+RADxwt#PAE_Md<#`nO__Vt+=oCmKx zn?f4_xtR`#Bbn6U?5Hb&!P2jrm0kSk?+&~h|4o-jx;xVK=p%cb8FEOiNrOFrN4+p% z7Joa^@7?yJ_FOvzO1I^v_wzN7jb+%kvfi^?2~G5a5n5_E2oP;^S=vk~06RODybX_0 z=^T&Kg0bC#oX(TXnkfnsFh;Hocp8D$iWFfG0ZuHctU*VzQI_{$RyfY(-b_TlxIXCh zAW3T`!VJRh-&Qnyemt%l@*GCFx-Ssz;!ExbNyA{IP&mGFBmq7S5PTDyhx2bv8!Y$; zm_1}F)Su4>X5_fh8{Ro0Q=aOvX`*7D}BUlp57A+K{CPoAI~ z-(1Ha)`iPLzx4Z5s;(Kl1>w%!I5xRwt8%h{><-D~m|CnK_$5a|xE-c~dqE=g-LoGKwNQX#9 zXMX~pqPN}9;vGwA*4Z0i3hnuLo?C|es%oh=)eoEJmM+45`N`%wYL$|N-M&<>Cs8t? zWOhCHvE=~T@oXqy_k#B_To-55)HObg6ya_e6XE`P-?kndM;0s6g>>z!jd?evksc~5 zu{@cIa@QUxiWhm$&bbFbz;T-2y;eF(l1TFq8+#ejkHBb)quV>K_WxVD9qCC~FYM{j znHPfHNpL_mYNRo+O?^?vr7)k5ITUCbgU!_$>@b6P8~ULqR*?Ud47pr*gV=`z4MtiB zqWRXAEWQJ;91N@%o$A(u&iE`snS32u#~pjb;7+uZ1z?UIttk~^-Xp+CR`qzHP?GsV z&vf7*0&30Z@*-~&`;-Xt+z2&Ui4@(4HRbN{vd5P#g2#1FwZhw-yu;|~%vpV8FW3jl zy0_unhw_G%+k-c~@^KGQN;9X2xjP5LTv0^L{J>66kl3Z@vE-m1FrcXjcvP^WQ;8VVltm}NGQ9TUeE z=|7OVg(p&C+)$lGWW8pjKnqsNGy3Py$8`a-JLi_ZI0TGCz6;|0dRBOL6GlZ7%2y&< zc9XaJ?(^MU?0GF|4OVfJGp)#!&pwpi*(Ng43QD&hB85Ps<^tl{3ZuI|eaPz96JNAj`RB^%0% zb=tO#3T8uin1K3liKwEQxaUw+1az|+i1~C9WX~5_Yv5J5%@h8exWFx%S&tYj{K^OD z6({N$C~yW9xdY%nBJ@V}bwmoX%gn_N`@qH`VSfAjs`wLtPqC_mKbK)$=mY6Uy6N`H z?O?y&dUcc!+5-9t%Hiei`kysiV0}x@ZjoP878Q#Y7%%I5-|WM{t~EKg$2ASl>~Hr( zuY7S~GSTIj2HqY4{MP^;D$T2fLwf*S*|~=H;bR*F+X-TO9N3d+^&>r;Y>y0ehpyyS ze!-E`zxwCIGxg?yeJ5Xr{!fUHf2(e*C`yai8hxkS|4W5i(f}f3<eKKC~p~&`mt*x6eel95Q>Lmg0f)#t<$p%!~4{*~qzb zGsbX&a#w``M+^WrXCDR^{AzT(gR^@;%~36mvjS_0xsnN%yUt2xDvpx7IcG(6@`+Ma z0km3el;HuwJ9gKx7lATarzMNBCFpt?EBO*i3TMHif9=4FdndbJ!sn*>yv@-6VlE9g zWF4n)mYER;dP1sirXU^sYjC6N|qyFgr0mFMQ1 z8xdmZ$<$!g3DRZU_Cp-L2^`((iB}`GXgf-8aHs5?o#HAee2D7uvdQ^+f))6S!A+QU zJ`zRMtku097m2@;F_W-{QDyfHPlyqE_OS}kGNj^utNH%+aNc4tF;J~#I)Z~@?goWg zc+;0Bbx6}R_W>?e;XEu)%uu{8yCaPJPpNo)WBwU*}IVA>1*}ISd$CK9c(~Q(;hlL?`T4^2hAEOESVz zB#*_VLbtv3DACXA}Zy z!?*teWYLHJ)0F9;F7`=dqBD&j<`xZk6Y7A%f6xdym897i*s1)d&IWHkKid)`Se+wm zOZ^0L%&76g33T(AyCFN>OjJEa`P2X31C22fTDMrhXgn&lLH!B z64H%f7vO^*`KyQwHwPMc{ zu4P9Ru4Q-J@rVk`c32Itk!Jw=G#XnoI=igmE%daV@LXDwCuo#Fm{s9mbSv^A%H{J5r7N5)d54WDDCG3L$EUML3+In z!|{>3`s|Lt7azw{xlxCDFF^icXuA_CJGxc;MrxYeCk2!o&YGceR~W<(d=Y)9$7Cl? z$|-*+4^At8XbXCaX8#S2&?|rW9dHcz(Clg23@eoOFF&dpa_z@w$j|z88Fgo0^GN>1 zWa;cQxr!81OEql`HT*&KGI_Yyx{A9#1*i+hHw!J;Juv(c+R_|tmOLb=U1kn1Ho2ti z)`6*K*C<9UQkRQl!L(=aY9q&)sIwc?GjUX!_CUt@A-Pi+ zpn_srwG_a<#tqjhS8FSvF-qI$>|@lAaw`Q}jmFNmhV1K*mVl0C8noP^o*DH@-W3g=sp14uGy546>NJjM=R_uc+ zT3Bt4wY8E64dLjtibDU1Jmw^6DI-3vXYWHuib#}D;Y4~%dE+7sv2dGH4mG>8X=$Zh zxprK0l&xJDd@e!8EmnT`5#ECkg$rsjW)}Ebd^rha zk(I97)^0b8Q4!@nIay3;({!VDF)A`6T-~^16*c?-X1)QmVRIFUjk9BtJWV_4XDC^; z{Z4|iSSV%*Khlw1MO26naXe8;QFk<6VTdF02$YBaNGF*w+z2aKy0R#$jV z&Oubbf4@EKkcFdOV#M}@xEE5|xS1G*B*c6$dN)Q?ll)5zT@cm8hmm>Gc^6R@8bFJS z-;bw*3L}D4PAhrSPRpzyc^@(?iO#7zId#mSiDc4Fjt*!qMKy9(AVEFiUuwg{ySbo8 z&emtglNO0`7nUs}Nc=U5t`qfpf+pPo%;{KC@dZR%MfxQ`SkI2Qxh@mFPZtoLnk8CD zfx(Ot-q{04tP&+=xX5m>(xdUkJ0#0z7%03Zqr;)Da*wl?4iAE~%NMSDHlYBS9a~P?fA79#R#O z^WP8(+0{N4jH#KJU>%n#f@v|ca@Bz+TP<{$$(UH&ss+1g!I{$8S^~oz=iaY`3V9-a-A;sW8Gx4pi{M8S+t}F}lY*Aw*s-CWsds z`wr}y*=A;5Ulo_!293BX-Bu)~G^V#~j%d%bYj7V~@%8Ne{Vy4jY3f?rxDD6T94!_$ z(i-_fO9WyNiqa;UPgQ*KJ9RAn2n|1B+zMZ@q{)N?7j#qHZ-Ww}L5s?9DQ z<$~2SV|nLQvAP*>esyOe^n??>GVYX$^TJnVUxQQ}qxkz^26+qB>P*)>W*(~qjCTKk z)Ki%iVE5kpnOX1fiUXmrVVAYp;|i>8Emso5lxILoNsq8kk8cxqjU)w4J0Nla#%h>Y zWxybZUPt$|sBo3bJekgu!k9q~g~Hfj4dqCDd(Yc57;M53GR~P(uNq5e)kJfSY)wl% z&Z52KIkrY0L47Jmao)eeD7Zu+#GM@96kJb>E3#A+6`o{5Pbi|8oXY>!;!qGZ=rRx~yk-~ZZ|i>fm$Qs0 zn^b?>1){O9Z60G;oMQrk3bz>uuG4<1wsr{oUX>TnYEk_4_nT2}*~^x$MccXzOn#bP zYN7!ad-ci+A9!dR;D(RyDV+YEYTKKaAFMG%kuI; zR67Tf1V6On72lu=!j5En%TmV%)JJ?tMqMf1U9r44-hn1JCVBTsPjU3||5TUs+v-zp zj-=^IwuU3y=8fA9T5pJIxXG_wa&Ly3v?hM9Q5siyT31CMzLu@ASXU+32s?~8h1C#c zmxwO7dCj3G8)DRMx&g>zV1o3gjr_I?wy~Mq@whboz|Oge6<&_-CkHqV&ugO^!f-mU zO@(|pFRq*$gPXEXP9Z^sO}iLrqv7pu%-lXRSO@x?I_gX58CNZayeM0l8ReJ`I|@nr z49dIYGVYn@)AqwkrGxYAAk&&hS))KnM)_dFy}uK1E;u+=oUB(IiKzmDCZ9b>@9l8~ zq*fZvV+_s=l+8T!T(DMkb4)&>h>%Yv{#NlK>#P0+vV2n7W3Klmtvga1Lnpr!sqUQk ztwtZ|7ep+JZ)R4<7?g^Hym^7sz8c0L6vWa#A((R5nKb^z%_=+ZdhH(GLxt4H%06Fe zvr}T{M+-Mjb8DxtxK6!zeUNmoFMH@tjA>#J%}B|TL>PR@N*BS=(62cCrQ_XUjl&iM?> z*-WdXda}L8eDq)-T|vzSS4r&zL4PgIH!E$Z>t41fg>76j7Z8d05LNX<6Gj14g9O+4G5jZ~$5_|xn09zWghN&ix$Io>8?tV7S{y-6 znfKbZB+*V@w}rTizs^jFGk{4PyD4(pq^uD(2*ohL{1x3!87^F{+MqV8*wj;!*{+w$ zGzCqWN2JWktdkEnxv8;id7=+7C7jL=h9h*N>cGt)B%!+GOdUn1Jh&;)hg7O2Ms^q$ zb;fRkeTLy$D9timqC!=YVp1NvzJ~Oro9EXN&2qIT2->Cwm>Ccj4S6jGfG5ykEVO~7 z1_-IqBWDSx1TxssGel4X4WaT4DQMBhYqh9{n+(BhQIB_j)M3R&m~zu<-ej5&-W$T{ zqM`5b%ntGGqK7lqDP$6|YSne8JG3YN-lY^(CllZsOfxOEz=ru4_RuSro)l^gK}u$+ z$!R&(9r9+?-JgV zPU6cQUl&}y11$9gkmuI$zOa3O@OFGmrJXWI*MX(bakO+jzjoVQH!%JA`*;FHII`!6 zgVnyD`7At3HPlTmJ9LZnW@J88MlLIM>xshv3pp-ea4U*qX3rYo>7+V(t~BF5=W1P}*Bhg}cpIcvmzCB%B>{Gp>ourYYck+k`7!4` zTaG$mxQe{Hk)V;yOx3Iok)c^;z13c%Z>3V(Ti@0?Qo;IL+ibRi`Zsq=es0xRzM&2c zN(@7*CBN4oQ8{HrpY>oDLG^Ch+2mRz^qLsXI1^e8R`EUqrviF3$uuqYzOOK*n9GER z70usYdT{6bsJ)!5MjoGvy-&(!Jcr8lbW|s$C0aT~$24|JUHSTMxJMiwq7~rRr2*Es zkp#z_k5AUi6sdrGE~f@#nlO^AJ>QeggEAOxGh5oE%LJK+ewUkX3|YP)4}4w1NReOc z9wmk*Uu(|mafFV$n}1!$a%_igD%=aW>oi7XFAt$_YQjrUhcbnRnT^6>cx@*<6dCS1 zedWGP)J@hx?eaIIPr%9MhFsJ|yC*fK%3PtgN;ZvZAugtcp-O*Sv?jcYm?T^5pVy6fE@Xy@~ zN~dk@+h2SsnXmpzr~d3(GaZm8jNx{Mfu_?W$g7=nt|cjFt8&`{B<&1P#~-m8l8XhT zo+7mF>m}zSx7OVK@H20AX4V9(DDKe%o@b3!W<_g)tt_W%wN`KqDnD+Jzf75dyutGo zIjr`)v77JRAk^GmVhbE63i0_*J;Oq@N$)V?O-f&#lbLKVqxpWm@vt8I3R+X%1KpN9 zpHvvq8L=^($$GXpfjc|0^?%h1bq?>mDrn*yts*(^Vz|!kvDXc_+fgMvSo_Cpy8A<} zwY?KQ*~f~Xtb&iM@`}n6Z#@BBYpg+5wDx3N5jJ{hzmg+Ifj%IM;O}Q8v0Xh>^`||z}r{emrc2N%HL#dZdr`U3$B4|!~2eRg*^?}S;mOV#>n zOWP1d^LkMe<`YtUK^MjYurv)$uV|Mn6$}Z&=@9bZ5{IxBp2Rpj1kvk#V9vs2S`|=6s}f~s?<&M*ia^9AsP?jojzGg$RNj%Gy#SrWDqY^4z=MV%ruyw zIATHy=O+O4LO=~cTw;+{mBoy*`gkQfH$)cxOV{!d5yZY4bC&S~DxO8gzbzW=ej}Sd z*d`*EK7>m_3GWpPK^22Q4O}3a!8w}Jn+lIPvWvgw_)`Zr1sH$w@oNjEhG;Xb=QCLxju?0T!Qoyav%6#bF zIZY|N1Ni$)KKS-epK>*RBqMv1o#u!o?WBk8O7)_y(bUVk5#CQ5W)|&EY2& zViTVl^;(%j&gp6sR8=KpD>nRi{tY|>{KWklD`JmRBMykUcd1d?<-Mi7fB3P=XME>p ztmPeq^dUVy{Nd@2j3$8H^+9-o5V<3)4w_OPk&96-5353ccZC*SG_w9d=&tfKjN%9K zA1glOKUe%B+{?p%m@*=KXQKaSIQu^rZ)I~w69Z#;JG=kT|0(JG6B015)81H(R-p(6 zoqRWI%g4$vl!_`7H5*j;D?F&s&URfSIY~#m_*cy*B1(?;Z&2o0VdS@?SjOztsvYdq z$qlaOEsp2R*A=}#f4+i3u*xk8>9hLCO_SPc`T`gmz#}Y(8O-#zdII<@hdI2L;{h_w{R@4xa0(`T^MQTdTRhEWmfsO2^Z5KLM88eR;t}T3ujF7;Qx= zZ8ApLD37e(`Atw(KqA*}*VdO-SmPP?v#M!o*^EI=?G~qbpM&fFtak1=)#jll1sOpPidbI+8?s+yUK zz-bJfDtOBhG3!EJ|`Gp4**fET7k*>T_eDP*?a8Z*RlOo2_Y|-+w+VGa!pLccTP@g3i zqh~~pyrvsFb<_@niQ;JzaOHSZ89Os+X$Pqho(Et5Ma%q;lY@crGy~*!@V$O(ng3@_ z#lHmKzlGtyOSuxa<-XO+!++eUsGI>q`q*Ph1g$P6a|0)VX>n!oC6cfSU2X-EGBy&+ zSYJfh5+6w3Gi>-D1yS4U0Mn$PqS3bF4e#mWY$x06@3w<(qS$wOmWg~`2tZ3wo6-Ol z7S@u3+F(e84B#0wDUtpGjP;@?x@s1C9BR)1V<4WbHeuzys(*jPB}P|a0gxJ<@Y5c= zo2u6Oj}tF@@-?VkRiJfBL>~&>&7O{bF#}lLrrD3_exf@xYdZVq#AgsTPMjS1=Kj3> z=d6)Lhvu?@;Vto8A0F?Ey28DYXaw5xYD_f%EzD>Gho;b5z}T2xA3A>MmBdBx50ABT z`tXpeY%+)6eh3@M5}dZV#YXVyF@(+l8C;bG8KM)g*q41iw0!&p$N& zk5=Kpo(Dq`sM_|NO4{12>#jL`iDNmiBezB!Wy3LTM+EG_9&2EtUS+^D6E%oOOJ)aC ziZSxN==O%rk>UqQ4fPUOJOjz{PdnfaDE`5%tRRPR~%X3~@fU==|DTUa)MG}a79 zMpF9@3J7y~+y||A3|EXL!l0vRUWQw)G3LN2C54 z8bZJ=b4sfJ8B+iM6YO^${C_yXx*1qo|5qQAe*|L6(eJ#;??BxAKE?|FA4Un;+1P*M z$J8vG&DH+#H^lM=j!xgo`hWfqaW^utceb#z{kQlm`ge}X{BrX+<&0w93$O@ha(KPW1nKzuKWQEqFR@wTwxHjZXT*Z7z;X?D06d)-Kk zi7Oe!U9Q+QgPDZco*SPv+l$kFJ8w0?H6$27wyE@|X&B10$y^m@IchTLY%)U86=`W! z9ThsMb{Q*2uE7^rrUgr2@Mr|4hBULuQ6v0QvXqsSmS zh;`%nD6GY;m|NS%v1NKUE&s>VADhQi20@CUFI8JxjF9dr@3=AQ!8UOOO5^m|8wTW6R z(Gr1rt1AjL?ws8V0HsLK)v9S`t|DYAO{kUgp&-~b_R2a>vgN<#B;ESpcf{@=PLzo? z#y{dz9R$-f=@YFDxWD-e{NE1^>gV7Mz~2z|Lc0Hr+4pag>|dwLzfTO#Zw;=>EYtDU zkQh^S1)`TXq|e|H3a&4!H+{ zloaYbkGxK(4--aM`U?RS)Z=A`B%rj1&*^GwbJ8_&=(^-`gTra+A4=S*iOdG>k1ZZC zet%7eZ-k^_TSy{v=0!in$+Lfel8-&Q?06`b17{#6*3^SQw@}*P&mMi&Kn~cWw_4z1 z3-)b@qSjm>DhGl^~j70KD9_C z9u8!Fd_6nB;OLg-FS$@9x3yEium&mUGP-T+kv!7h#QD_UZKXhN20;QjAqx1`!x(PQ zUblf8VfWgR{+InfHYOW+bT`H$VLfjR>2&+F=}g{2F}za`yC*{Ndt>W0c!zfOVV@XY zg3$R!x5vBOgVFg82jv9;OO z{q@o?c{7YQ9ECn|?8byU@S#iQ7p#I3>1GHB{HcqmESH`Y9a~{*dFl%>p{?y~ox?lq5}FjS$Q z6T3WGv7IYlFhF9+=XsnvsdQP_U$|nqv^USJ{C*Gw(w%Bhfq@qwK(Wo9DxjTq$)x^j zw{(?pX4ztBJzAF4SVM{0ShyMMmcAuXc<~Bu9!)j6JvfGA(#D`;d2@2~00K^7J?c5W zH~K~2lTMOK%oc4taFe;ftPzvsH%!Cx@_9SnBnj00+o*vI7N^4iYm+1;9C}w4-7^a?;dV-!@gq{GoEmm`y^<;6A+)>*oW^~LKZ7$~iz5e8eRS38>FNeX~GXK|z z!(FAjczG4ZEgl@cVCw43GCsq*ITM^~ezPwV>ectx8t?*74XgF#hEa{04E7n1A#qE1 zz*9L_Jtm)Z*CVr2z^0Kxf%XW+%Fi3&PqCm8D8&#d?)9Bd=vXnmOHVJjMAmhPf~pGH z97mRnJqG8n^L#8kRAI!hSq-{poRYzoz`NWH7N=gXqoR=rBr+J61&?5*2y9AC%}%+r z;B7PJqp`S^$eq3LnF38{CnoY{MiNWU3$Ymn%=5qrGhASF_2p_ynVxB*b>86+Zjrt&A493Evb>SXy*##MDu1@=Uiyv=?hbk z!xfmbls)nNZ-WHA>ovFbxysQ1)Ix8k0- z)%)<@LEn6ypA35A<_z(fzlh=H5YXIccqbvBnrk2$EV?%c4U#X4h*)upz82Gka{uUW zHi>|%H}%U8OeAU}oR~BRJF&9`huB5zi(-%|>*t}D{un}16~iW(SChoT&FMeZc;X{! z3~@5%K#L2?wMIO_uD#$d!K}@Mv~jJYOu7dl7>@HAiHk8UMww2#hB5y0q|20@Z3~)@ zdTZWXS+P-9fPmeLoOc#KO~;e(ocOCfDqcNI1nW~|_|M+9A~(DD>CFzi`$+tY zTdvY0baw<|(mOt+Se3n6%w#8Bwn3sZbwFW`%%NHGvmXL`hQQ@y)* z;UCDOunMZ*>TQ`{Ph^PAAUX{5<@X$S)eE<{A8dhNCog&*ZC)3~3z{gg*nye1&fe(T zb2mJ=+vvaKhdq*iZyKrp(B-}yBJK^dIjmYPJIVVxlXE0Zh%nBE#vi1!yzi}z;;pX- z*r$+QOI<-L5DQRd)mdlNWLd5O{bkAbpJ)Q9FBeZUopG4_5gBxalaxGu+RLJ-vd+?t z;zU03l>A=|0N@1A1EU9@Obn=_Mr@~r7Kx{g5D6!hbWs40Jv9V=55j|0XRMox$r76)BvtI* zY)i0AsKZ_X} zJ(-O^uS}X$W)PRrqwh*jB`Q8Kq?J(1vkiz8pU1zf4h4q|0)yR~JW^B!2M1`R>x;6SDCG?}h~in`S^V-awF4Su_bk41qWcy@4tk9KFR5LN#D+RhNKs#J`(I#KToHcY^#H_`L5<8B^q zlYc*HUbWUl!{uowQ>L9z0i+yL2DHSe+Kj|)w3>RIqX~)!cx&ddc#vf$b;f1eQ|an* zd}99%cYCTw@j#BZg+DhUut+nek!*5440~*NBWX}fZ}I0|!rQRctQ)b-X8r#vyAF6P zyEaZyM%g7JD>K<-6GHYL30aT5A2V4QNg{h>dsHadE7>EvNC?>}dsWKlJ5Q4LvGSew z*L$m<`+r^M+UMM7Sd15h4%(};b&P897rqO6#!Hl7e?nN{xy{WaVqqqk0@Y~J*<8aL zm$@MVS8_@N@&?-an=Xrnp6}0z&b#(5z%r;&} z+TSJ$ehByB_eBwU&Q6yf-a1wo^=YBbPWDkW6s>!eQ0a=^U`FfMSnXX(DToGn152-tK8rjW$QVRm>|o zG6ve`AI8e>ypXAAFIaSwerj*oBh(j5JmeE+?#FUD{rH;`jDqF1t`IT#ubB;cC6BxE z#w1?LOC>dihC42&J9~|p)Se@mhsr-3`}rs@=}7};gG=xYNPqJJhO5$1y%L{nXwLHCVQUaQT*2}$K$K$8EKxB zpnE|anuCrp=11D3A1AbU7-(9?MP(F`m5*f`oe=M+h5ie_|y zr*~xE=y0DXFD;qlxSd_0{mJ|Dk={{L*CrW+@0~4sM>{E=^M<(Z6Dqe7Z$F(*JfUoH z;P`+?pjQ)2(+GmQl-@*&IeEh@{>*@R1HWPXi|f^@7luX{#EdE;8|a#oE{>~kTgePC z6yCF(ppJebxr}_R>-oS+O^!HM9dkb8j|$jZ9$Hwfw5iYfNEHJu=0p^%A0J{TRllKk zd{X#AC+%!)%>>mG32qX%TKD7beQA;p`(Begu=Q2u)=&=)mBcaXjOZzVk<;3JEHf$y z=D!hbEjL=>?V??g%Tvd-L{s^$^u93;?&aY5$;j}H4=MLBai$v4&J~!FN8cHFdL7i5 zuZZZ~0&$5bCJH<_J~Fjbpj#`G;r_r(Fpq!C`(tqO^F&?D&-ha3cQP&Zjkmsx+O*SG z8(d{;s3~fU54fTPOfH$nmP)F&I~8(#@I| zVdYa0k}4H&?A~CgK_uP1X%3V{7|M@AMznUC%)6#;nh!^P6gve@rJq-;%XC3&)-pH8 zu0}y#kNteRIiZ6ZSq%~{h#sR{YluIrQ1woXRj#T{kwi6X{bH=;Ib#Ra$hMU%G1h6* zFwM_6Cj52$QoS^9kmOi(JWiug*AJnkqjb=Y5I0p!KBS4$JHZ*wbt9Sh^4rhQkyE|0 zP`;;69z300F~PL!#h~mxq2G)3Qva^1=NY0jRN5;1amtHd*}oy_!|Cux4Zr`gmWb;VsAiVXk7 z;h>7L+>=Nn*m8}_Sk>he?I|(H$uu=|O&%0AN%WpBBS9Z+2U6)+ZS`3kix{2`n)k*y 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 J@BW5@`hV+tBH{o5 diff --git a/build/build.xml b/build/build.xml index 91ba2d2dc..728d6aa3a 100644 --- a/build/build.xml +++ b/build/build.xml @@ -17,7 +17,7 @@ - + @@ -30,25 +30,26 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -190,7 +191,7 @@ - + @@ -204,7 +205,7 @@ - + - + @@ -242,7 +243,7 @@ - + @@ -258,7 +259,7 @@ - + - + @@ -28,19 +28,7 @@ - - - - - - - - - - - - - + @@ -49,11 +37,11 @@ - - - - - + + + + + @@ -76,19 +64,19 @@ - + - - From e4ae5565f0266032a00a538310398622afb01837 Mon Sep 17 00:00:00 2001 From: rcollier Date: Sun, 26 Jan 2014 14:49:45 +0000 Subject: [PATCH 13/22] SMACK-343 Made all non core jars set the smack jar as the fragment host. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13877 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/build/build.xml b/build/build.xml index 728d6aa3a..653f8569c 100644 --- a/build/build.xml +++ b/build/build.xml @@ -50,6 +50,7 @@ + @@ -201,7 +202,8 @@ - + + @@ -219,7 +221,9 @@ - + + + @@ -239,7 +243,9 @@ - + + + @@ -255,7 +261,9 @@ - + + + @@ -271,7 +279,9 @@ - + + + From 33e2094fe4c47a3f3aa030b501d888e9ea63942d Mon Sep 17 00:00:00 2001 From: rcollier Date: Fri, 31 Jan 2014 03:41:25 +0000 Subject: [PATCH 14/22] SMACK-434 (and 529) Added support for experimental code, that is XEP's that have not yet made it to the draft state. This new souce directory and artifact initially contains message carbons (XEP-0280) code provided by George Lukas. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13882 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 40 ++++ build/eclipse/classpath | 5 +- build/eclipse/project | 2 +- build/release.xml | 1 + .../resources/META-INF/experimental.providers | 17 ++ .../jivesoftware/smackx/carbons/Carbon.java | 117 ++++++++++ .../smackx/carbons/CarbonManager.java | 213 ++++++++++++++++++ .../provider/CarbonManagerProvider.java | 53 +++++ .../ExperimentalProviderInitializer.java | 11 + .../smackx/carbons/CarbonTest.java | 110 +++++++++ .../jivesoftware/smack/DummyConnection.java | 8 + .../smack/keepalive/KeepaliveTest.java | 2 +- .../smack/packet/StreamErrorTest.java | 2 +- .../smack/parsing/ParsingExceptionTest.java | 2 +- .../smack/{ => test/util}/TestUtils.java | 2 +- .../smack/util/PacketParserUtilsTest.java | 2 +- .../smackx/forward/ForwardedTest.java | 2 +- .../jivesoftware/smackx/ping/PingTest.java | 2 +- .../smackx/pubsub/ConfigureFormTest.java | 2 +- .../smackx/pubsub/ItemValidationTest.java | 2 +- .../WorkgroupProviderInitializer.java | 11 + 21 files changed, 595 insertions(+), 11 deletions(-) create mode 100644 experimental/resources/META-INF/experimental.providers create mode 100644 experimental/source/org/jivesoftware/smackx/carbons/Carbon.java create mode 100644 experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java create mode 100644 experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java create mode 100644 experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java create mode 100644 experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java rename test-unit/org/jivesoftware/smack/{ => test/util}/TestUtils.java (98%) create mode 100644 workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java diff --git a/build/build.xml b/build/build.xml index 653f8569c..4e2111a7e 100644 --- a/build/build.xml +++ b/build/build.xml @@ -31,11 +31,13 @@ + + @@ -46,10 +48,13 @@ + + + @@ -151,6 +156,23 @@ + + + + + + + + + + + @@ -285,6 +307,24 @@ + + + + + + + + + + + + + + + + + + + + + sent + urn:xmpp:carbons:2 + org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider + + + received + urn:xmpp:carbons:2 + org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider + + + diff --git a/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java b/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java new file mode 100644 index 000000000..39c188851 --- /dev/null +++ b/experimental/source/org/jivesoftware/smackx/carbons/Carbon.java @@ -0,0 +1,117 @@ +/** + * Copyright 2013 Georg Lukas + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.carbons; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.forward.Forwarded; + +/** + * Packet extension for XEP-0280: Message Carbons. The extension + * XEP-0280 is + * meant to synchronize a message flow to multiple presences of a user. + * + *

        + * It accomplishes this by wrapping a {@link Forwarded} packet in a sent + * or received element + * + * @author Georg Lukas + */ +public class Carbon implements PacketExtension { + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + + private Direction dir; + private Forwarded fwd; + + /** + * Construct a Carbon message extension. + * + * @param dir Determines if the carbon is being sent/received + * @param fwd The forwarded message. + */ + public Carbon(Direction dir, Forwarded fwd) { + this.dir = dir; + this.fwd = fwd; + } + + /** + * Get the direction (sent or received) of the carbon. + * + * @return the {@link Direction} of the carbon. + */ + public Direction getDirection() { + return dir; + } + + /** + * Get the forwarded packet. + * + * @return the {@link Forwarded} message contained in this Carbon. + */ + public Forwarded getForwarded() { + return fwd; + } + + @Override + public String getElementName() { + return dir.toString(); + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"") + .append(getNamespace()).append("\">"); + + buf.append(fwd.toXML()); + + buf.append(""); + return buf.toString(); + } + + /** + * Defines the direction of a {@link Carbon} message. + */ + public static enum Direction { + received, + sent + } + + /** + * Packet extension indicating that a message may not be carbon-copied. Adding this + * extension to any message will disallow that message from being copied. + */ + public static class Private implements PacketExtension { + public static final String ELEMENT = "private"; + + public String getElementName() { + return ELEMENT; + } + + public String getNamespace() { + return Carbon.NAMESPACE; + } + + public String toXML() { + return "<" + ELEMENT + " xmlns=\"" + Carbon.NAMESPACE + "\"/>"; + } + } +} diff --git a/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java b/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java new file mode 100644 index 000000000..9217860ce --- /dev/null +++ b/experimental/source/org/jivesoftware/smackx/carbons/CarbonManager.java @@ -0,0 +1,213 @@ +/** + * Copyright 2013 Georg Lukas + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.carbons; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.packet.DiscoverInfo; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the manager for registering {@link Carbon} support, enabling and disabling + * message carbons. + * + * You should call enableCarbons() before sending your first undirected + * presence. + * + * @author Georg Lukas + */ +public class CarbonManager { + + private static Map instances = + Collections.synchronizedMap(new WeakHashMap()); + + static { + Connection.addConnectionCreationListener(new ConnectionCreationListener() { + public void connectionCreated(Connection connection) { + new CarbonManager(connection); + } + }); + } + + private Connection connection; + private volatile boolean enabled_state = false; + + private CarbonManager(Connection connection) { + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + sdm.addFeature(Carbon.NAMESPACE); + this.connection = connection; + instances.put(connection, this); + } + + /** + * Obtain the CarbonManager responsible for a connection. + * + * @param connection the connection object. + * + * @return a CarbonManager instance + */ + public static CarbonManager getInstanceFor(Connection connection) { + CarbonManager carbonManager = instances.get(connection); + + if (carbonManager == null) { + carbonManager = new CarbonManager(connection); + } + + return carbonManager; + } + + private IQ carbonsEnabledIQ(final boolean new_state) { + IQ setIQ = new IQ() { + public String getChildElementXML() { + return "<" + (new_state? "enable" : "disable") + " xmlns='" + Carbon.NAMESPACE + "'/>"; + } + }; + setIQ.setType(IQ.Type.SET); + return setIQ; + } + + /** + * Returns true if XMPP Carbons are supported by the server. + * + * @return true if supported + */ + public boolean isSupportedByServer() { + try { + DiscoverInfo result = ServiceDiscoveryManager + .getInstanceFor(connection).discoverInfo(connection.getServiceName()); + return result.containsFeature(Carbon.NAMESPACE); + } + catch (XMPPException e) { + return false; + } + } + + /** + * Notify server to change the carbons state. This method returns + * immediately and changes the variable when the reply arrives. + * + * You should first check for support using isSupportedByServer(). + * + * @param new_state whether carbons should be enabled or disabled + */ + public void sendCarbonsEnabled(final boolean new_state) { + IQ setIQ = carbonsEnabledIQ(new_state); + + connection.addPacketListener(new PacketListener() { + public void processPacket(Packet packet) { + IQ result = (IQ)packet; + if (result.getType() == IQ.Type.RESULT) { + enabled_state = new_state; + } + connection.removePacketListener(this); + } + }, new PacketIDFilter(setIQ.getPacketID())); + + connection.sendPacket(setIQ); + } + + /** + * Notify server to change the carbons state. This method blocks + * some time until the server replies to the IQ and returns true on + * success. + * + * You should first check for support using isSupportedByServer(). + * + * @param new_state whether carbons should be enabled or disabled + * + * @return true if the operation was successful + */ + public boolean setCarbonsEnabled(final boolean new_state) { + if (enabled_state == new_state) + return true; + + IQ setIQ = carbonsEnabledIQ(new_state); + + PacketCollector collector = + connection.createPacketCollector(new PacketIDFilter(setIQ.getPacketID())); + connection.sendPacket(setIQ); + IQ result = (IQ) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + collector.cancel(); + + if (result != null && result.getType() == IQ.Type.RESULT) { + enabled_state = new_state; + return true; + } + return false; + } + + /** + * Helper method to enable carbons. + * + * @return true if the operation was successful + */ + public boolean enableCarbons() { + return setCarbonsEnabled(true); + } + + /** + * Helper method to disable carbons. + * + * @return true if the operation was successful + */ + public boolean disableCarbons() { + return setCarbonsEnabled(false); + } + + /** + * Check if carbons are enabled on this connection. + */ + public boolean getCarbonsEnabled() { + return this.enabled_state; + } + + /** + * Obtain a Carbon from a message, if available. + * + * @param msg Message object to check for carbons + * + * @return a Carbon if available, null otherwise. + */ + public static Carbon getCarbon(Message msg) { + Carbon cc = (Carbon)msg.getExtension("received", Carbon.NAMESPACE); + if (cc == null) + cc = (Carbon)msg.getExtension("sent", Carbon.NAMESPACE); + return cc; + } + + /** + * Mark a message as "private", so it will not be carbon-copied. + * + * @param msg Message object to mark private + */ + public static void disableCarbons(Message msg) { + msg.addExtension(new Carbon.Private()); + } +} diff --git a/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java b/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java new file mode 100644 index 000000000..f76f33b18 --- /dev/null +++ b/experimental/source/org/jivesoftware/smackx/carbons/provider/CarbonManagerProvider.java @@ -0,0 +1,53 @@ +/** + * Copyright 2013 Georg Lukas + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.carbons.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.carbons.Carbon; +import org.jivesoftware.smackx.carbons.Carbon.Direction; +import org.jivesoftware.smackx.forward.Forwarded; +import org.xmlpull.v1.XmlPullParser; + +/** + * This class implements the {@link PacketExtensionProvider} to parse + * cabon copied messages from a packet. It will return a {@link Carbon} packet extension. + * + * @author Georg Lukas + * + */ +public class CarbonManagerProvider implements PacketExtensionProvider { + + public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + Direction dir = Direction.valueOf(parser.getName()); + Forwarded fwd = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG && parser.getName().equals("forwarded")) { + fwd = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser); + } + else if (eventType == XmlPullParser.END_TAG && dir == Direction.valueOf(parser.getName())) + done = true; + } + if (fwd == null) + throw new Exception("sent/received must contain exactly one tag"); + return new Carbon(dir, fwd); + } +} diff --git a/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java new file mode 100644 index 000000000..70c8e8760 --- /dev/null +++ b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java @@ -0,0 +1,11 @@ +package org.jivesoftware.smackx.experimental; + +import org.jivesoftware.smack.provider.UrlProviderFileInitializer; + +public class ExperimentalProviderInitializer extends UrlProviderFileInitializer { + + @Override + protected String getFilePath() { + return "classpath:META-INF/experimental.providers"; + } +} diff --git a/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java b/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java new file mode 100644 index 000000000..f07afba57 --- /dev/null +++ b/experimental/test/org/jivesoftware/smackx/carbons/CarbonTest.java @@ -0,0 +1,110 @@ +/** + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.carbons; + +import static org.junit.Assert.assertEquals; + +import java.util.Properties; + +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smackx.carbons.provider.CarbonManagerProvider; +import org.jivesoftware.smackx.forward.Forwarded; +import org.jivesoftware.smackx.forward.provider.ForwardedProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +import com.jamesmurty.utils.XMLBuilder; + +public class CarbonTest { + + private static Properties outputProperties = new Properties(); + static { + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + + @BeforeClass + public static void setup() { + ProviderManager.getInstance().addExtensionProvider("forwarded", "urn:xmpp:forward:0", new ForwardedProvider()); + } + + @Test + public void carbonSentTest() throws Exception { + XmlPullParser parser; + String control; + Carbon cc; + Forwarded fwd; + + control = XMLBuilder.create("sent") + .e("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .e("message") + .a("from", "romeo@montague.com") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "sent"); + cc = (Carbon) new CarbonManagerProvider().parseExtension(parser); + fwd = cc.getForwarded(); + + // meta + assertEquals(Carbon.Direction.sent, cc.getDirection()); + + // no delay in packet + assertEquals(null, fwd.getDelayInfo()); + + // check message + assertEquals("romeo@montague.com", fwd.getForwardedPacket().getFrom()); + + // check end of tag + assertEquals(XmlPullParser.END_TAG, parser.getEventType()); + assertEquals("sent", parser.getName()); + } + + @Test + public void carbonReceivedTest() throws Exception { + XmlPullParser parser; + String control; + Carbon cc; + + control = XMLBuilder.create("received") + .e("forwarded") + .a("xmlns", "urn:xmpp:forwarded:0") + .e("message") + .a("from", "romeo@montague.com") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "received"); + cc = (Carbon) new CarbonManagerProvider().parseExtension(parser); + + assertEquals(Carbon.Direction.received, cc.getDirection()); + + // check end of tag + assertEquals(XmlPullParser.END_TAG, parser.getEventType()); + assertEquals("received", parser.getName()); + } + + @Test(expected=Exception.class) + public void carbonEmptyTest() throws Exception { + XmlPullParser parser; + String control; + + control = XMLBuilder.create("sent") + .a("xmlns", "urn:xmpp:forwarded:0") + .asString(outputProperties); + + parser = TestUtils.getParser(control, "sent"); + new CarbonManagerProvider().parseExtension(parser); + } +} diff --git a/test-unit/org/jivesoftware/smack/DummyConnection.java b/test-unit/org/jivesoftware/smack/DummyConnection.java index 57753a1ba..308644a49 100644 --- a/test-unit/org/jivesoftware/smack/DummyConnection.java +++ b/test-unit/org/jivesoftware/smack/DummyConnection.java @@ -26,6 +26,14 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.ConnectionListener; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.Roster; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.Connection.ListenerWrapper; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; diff --git a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java index 937bcebbb..870f9b937 100644 --- a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java +++ b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java @@ -13,7 +13,6 @@ import org.jivesoftware.smack.DummyConnection; import org.jivesoftware.smack.PacketInterceptor; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.filter.IQTypeFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; @@ -22,6 +21,7 @@ import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.ping.PingFailedListener; import org.jivesoftware.smack.ping.packet.Ping; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.After; import org.junit.Before; diff --git a/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java b/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java index 9c3e9dfc6..302ef0e7d 100644 --- a/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java +++ b/test-unit/org/jivesoftware/smack/packet/StreamErrorTest.java @@ -2,8 +2,8 @@ package org.jivesoftware.smack.packet; import static org.junit.Assert.*; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.packet.StreamError; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.Test; import org.xmlpull.v1.XmlPullParser; diff --git a/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java b/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java index fd674ae0c..121d1e2a1 100644 --- a/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java +++ b/test-unit/org/jivesoftware/smack/parsing/ParsingExceptionTest.java @@ -3,11 +3,11 @@ package org.jivesoftware.smack.parsing; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.provider.PacketExtensionProvider; import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.After; import org.junit.Before; diff --git a/test-unit/org/jivesoftware/smack/TestUtils.java b/test-unit/org/jivesoftware/smack/test/util/TestUtils.java similarity index 98% rename from test-unit/org/jivesoftware/smack/TestUtils.java rename to test-unit/org/jivesoftware/smack/test/util/TestUtils.java index 0eae51e35..7d240bb03 100644 --- a/test-unit/org/jivesoftware/smack/TestUtils.java +++ b/test-unit/org/jivesoftware/smack/test/util/TestUtils.java @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jivesoftware.smack; +package org.jivesoftware.smack.test.util; import java.io.IOException; import java.io.StringReader; diff --git a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java index e7c25161a..5b4025ba4 100644 --- a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -24,10 +24,10 @@ import java.util.TimeZone; import org.custommonkey.xmlunit.DetailedDiff; import org.custommonkey.xmlunit.Diff; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smackx.packet.DelayInformation; import org.junit.Ignore; import org.junit.Test; diff --git a/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java index 6ffe05657..7cea6539f 100644 --- a/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java +++ b/test-unit/org/jivesoftware/smackx/forward/ForwardedTest.java @@ -24,8 +24,8 @@ import java.util.GregorianCalendar; import java.util.Properties; import java.util.TimeZone; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smackx.packet.DelayInfo; import org.jivesoftware.smackx.packet.DelayInformation; import org.jivesoftware.smackx.forward.Forwarded; diff --git a/test-unit/org/jivesoftware/smackx/ping/PingTest.java b/test-unit/org/jivesoftware/smackx/ping/PingTest.java index c127f18dc..96b0fba63 100644 --- a/test-unit/org/jivesoftware/smackx/ping/PingTest.java +++ b/test-unit/org/jivesoftware/smackx/ping/PingTest.java @@ -16,11 +16,11 @@ package org.jivesoftware.smackx.ping; import org.jivesoftware.smack.DummyConnection; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.ping.packet.Ping; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smackx.packet.DiscoverInfo; import org.junit.Before; diff --git a/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java b/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java index 784c56f39..68834d6b8 100644 --- a/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java +++ b/test-unit/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java @@ -22,7 +22,7 @@ package org.jivesoftware.smackx.pubsub; import static org.junit.Assert.assertEquals; import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.ThreadedDummyConnection; +import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.PacketExtension; diff --git a/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java b/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java index eb967e5f7..b28b6b9ae 100644 --- a/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java +++ b/test-unit/org/jivesoftware/smackx/pubsub/ItemValidationTest.java @@ -26,10 +26,10 @@ import static org.junit.Assert.assertTrue; import java.io.Reader; import java.io.StringReader; -import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; import org.jivesoftware.smackx.pubsub.provider.ItemsProvider; diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java new file mode 100644 index 000000000..a198a32e6 --- /dev/null +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/WorkgroupProviderInitializer.java @@ -0,0 +1,11 @@ +package org.jivesoftware.smackx.workgroup; + +import org.jivesoftware.smack.provider.UrlProviderFileInitializer; + +public class WorkgroupProviderInitializer extends UrlProviderFileInitializer { + + @Override + protected String getFilePath() { + return "classpath:META-INF/workgroup.providers"; + } +} From faa0f21906c259ad526ca73004fb108963154450 Mon Sep 17 00:00:00 2001 From: rcollier Date: Fri, 31 Jan 2014 03:49:47 +0000 Subject: [PATCH 15/22] SMACK-434 Added missing javadoc and some test code cleanup git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13883 b35dd754-fafc-0310-a699-88a17e54d16e --- .../ExperimentalProviderInitializer.java | 22 +++++++++++++++++++ .../smack/keepalive/KeepaliveTest.java | 4 ---- .../jivesoftware/smackx/ping/PingTest.java | 6 +++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java index 70c8e8760..6d8abbc20 100644 --- a/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java +++ b/experimental/source/org/jivesoftware/smackx/experimental/ExperimentalProviderInitializer.java @@ -1,7 +1,29 @@ +/** + * Copyright 2013 Robin Collier + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.jivesoftware.smackx.experimental; import org.jivesoftware.smack.provider.UrlProviderFileInitializer; +/** + * Initializes the providers in the experimental code stream. + * + * @author Robin Collier + * + */ public class ExperimentalProviderInitializer extends UrlProviderFileInitializer { @Override diff --git a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java index 870f9b937..240f9160b 100644 --- a/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java +++ b/test-unit/org/jivesoftware/smack/keepalive/KeepaliveTest.java @@ -1,7 +1,6 @@ package org.jivesoftware.smack.keepalive; import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Properties; @@ -16,13 +15,10 @@ import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.filter.IQTypeFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; -import org.jivesoftware.smack.keepalive.KeepAliveManager; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.ping.PingFailedListener; import org.jivesoftware.smack.ping.packet.Ping; -import org.jivesoftware.smack.test.util.TestUtils; -import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/test-unit/org/jivesoftware/smackx/ping/PingTest.java b/test-unit/org/jivesoftware/smackx/ping/PingTest.java index 96b0fba63..7058e89a1 100644 --- a/test-unit/org/jivesoftware/smackx/ping/PingTest.java +++ b/test-unit/org/jivesoftware/smackx/ping/PingTest.java @@ -15,6 +15,10 @@ */ package org.jivesoftware.smackx.ping; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import org.jivesoftware.smack.DummyConnection; import org.jivesoftware.smack.ThreadedDummyConnection; import org.jivesoftware.smack.packet.IQ; @@ -26,8 +30,6 @@ import org.jivesoftware.smackx.packet.DiscoverInfo; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - public class PingTest { private DummyConnection dummyCon; private ThreadedDummyConnection threadedCon; From 782448b3ec668c4b10d4d1626d05d7beb4924793 Mon Sep 17 00:00:00 2001 From: rcollier Date: Fri, 31 Jan 2014 03:52:13 +0000 Subject: [PATCH 16/22] SMACK-403 New constructor added with no DelayInfo. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13884 b35dd754-fafc-0310-a699-88a17e54d16e --- .../org/jivesoftware/smackx/forward/Forwarded.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/org/jivesoftware/smackx/forward/Forwarded.java b/source/org/jivesoftware/smackx/forward/Forwarded.java index eecd819fa..16ab403b7 100644 --- a/source/org/jivesoftware/smackx/forward/Forwarded.java +++ b/source/org/jivesoftware/smackx/forward/Forwarded.java @@ -18,11 +18,7 @@ package org.jivesoftware.smackx.forward; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.provider.PacketExtensionProvider; -import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smackx.packet.DelayInfo; -import org.jivesoftware.smackx.provider.DelayInfoProvider; -import org.xmlpull.v1.XmlPullParser; /** * Packet extension for XEP-0297: Stanza Forwarding. @@ -47,6 +43,16 @@ public class Forwarded implements PacketExtension { this.forwardedPacket = fwdPacket; } + /** + * Creates a new Forwarded packet extension. + * + * @param delay an optional {@link DelayInfo} timestamp of the packet. + * @param fwdPacket the packet that is forwarded (required). + */ + public Forwarded(Packet fwdPacket) { + this.forwardedPacket = fwdPacket; + } + @Override public String getElementName() { return ELEMENT_NAME; From 5bbf6cf22471a6f6c767881395ffdc1e583a9b61 Mon Sep 17 00:00:00 2001 From: rcollier Date: Sat, 1 Feb 2014 22:22:30 +0000 Subject: [PATCH 17/22] SMACK-387 Added some configuration to the ChatManager to allow for different matching modes on incoming messages with no thread id. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13885 b35dd754-fafc-0310-a699-88a17e54d16e --- source/org/jivesoftware/smack/Chat.java | 14 +- .../org/jivesoftware/smack/ChatManager.java | 158 ++++-- .../jivesoftware/smack/packet/Message.java | 5 +- .../org/jivesoftware/smack/packet/Packet.java | 7 + .../smack/ChatConnectionTest.java | 526 +++++++++++------- 5 files changed, 478 insertions(+), 232 deletions(-) diff --git a/source/org/jivesoftware/smack/Chat.java b/source/org/jivesoftware/smack/Chat.java index 66f5a54ba..c4ec1726d 100644 --- a/source/org/jivesoftware/smack/Chat.java +++ b/source/org/jivesoftware/smack/Chat.java @@ -170,7 +170,19 @@ public class Chat { } } - + @Override + public String toString() { + return "Chat [(participant=" + participant + "), (thread=" + threadID + ")]"; + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 31 + threadID.hashCode(); + hash = hash * 31 + participant.hashCode(); + return hash; + } + @Override public boolean equals(Object obj) { return obj instanceof Chat diff --git a/source/org/jivesoftware/smack/ChatManager.java b/source/org/jivesoftware/smack/ChatManager.java index 1523b4fdc..90e68ab82 100644 --- a/source/org/jivesoftware/smack/ChatManager.java +++ b/source/org/jivesoftware/smack/ChatManager.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -33,39 +34,59 @@ import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.ThreadFilter; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.collections.ReferenceMap; /** * The chat manager keeps track of references to all current chats. It will not hold any references - * in memory on its own so it is neccesary to keep a reference to the chat object itself. To be + * in memory on its own so it is necessary to keep a reference to the chat object itself. To be * made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}. * * @author Alexander Wenckus */ public class ChatManager { - - /** - * Returns the next unique id. Each id made up of a short alphanumeric - * prefix along with a unique numeric value. - * - * @return the next id. + /* + * Sets the default behaviour for allowing 'normal' messages to be used in chats. As some clients don't set + * the message type to chat, the type normal has to be accepted to allow chats with these clients. */ - private static synchronized String nextID() { - return prefix + Long.toString(id++); + private static boolean defaultIsNormalInclude = true; + + /* + * Sets the default behaviour for how to match chats when there is NO thread id in the incoming message. + */ + private static MatchMode defaultMatchMode = MatchMode.BARE_JID; + + /** + * Defines the different modes under which a match will be attempted with an existing chat when + * the incoming message does not have a thread id. + */ + public enum MatchMode { + /** + * Will not attempt to match, always creates a new chat. + */ + NONE, + /** + * Will match on the JID in the from field of the message. + */ + SUPPLIED_JID, + /** + * Will attempt to match on the JID in the from field, and then attempt the base JID if no match was found. + * This is the most lenient matching. + */ + BARE_JID; } - - /** - * A prefix helps to make sure that ID's are unique across mutliple instances. + + /* + * Determines whether incoming messages of type normal can create chats. */ - private static String prefix = StringUtils.randomString(5); - - /** - * Keeps track of the current increment, which is appended to the prefix to - * forum a unique ID. + private boolean normalIncluded = defaultIsNormalInclude; + + /* + * Determines how incoming message with no thread will be matched to existing chats. */ - private static long id = 0; - + private MatchMode matchMode = defaultMatchMode; + /** * Maps thread ID to chat. */ @@ -101,11 +122,11 @@ public class ChatManager { return false; } Message.Type messageType = ((Message) packet).getType(); - return messageType != Message.Type.groupchat && - messageType != Message.Type.headline; + return (messageType == Type.chat) || (normalIncluded ? messageType == Type.normal : false); } }; - // Add a listener for all message packets so that we can deliver errant + + // Add a listener for all message packets so that we can deliver // messages to the best Chat instance available. connection.addPacketListener(new PacketListener() { public void processPacket(Packet packet) { @@ -116,10 +137,6 @@ public class ChatManager { } else { chat = getThreadChat(message.getThread()); - if (chat == null) { - // Try to locate the chat based on the sender of the message - chat = getUserChat(message.getFrom()); - } } if(chat == null) { @@ -130,6 +147,44 @@ public class ChatManager { }, filter); } + /** + * Determines whether incoming messages of type normal will be used for creating new chats or matching + * a message to existing ones. + * + * @return true if normal is allowed, false otherwise. + */ + public boolean isNormalIncluded() { + return normalIncluded; + } + + /** + * Sets whether to allow incoming messages of type normal to be used for creating new chats or matching + * a message to an existing one. + * + * @param normalIncluded true to allow normal, false otherwise. + */ + public void setNormalIncluded(boolean normalIncluded) { + this.normalIncluded = normalIncluded; + } + + /** + * Gets the current mode for matching messages with NO thread id to existing chats. + * + * @return The current mode. + */ + public MatchMode getMatchMode() { + return matchMode; + } + + /** + * Sets the mode for matching messages with NO thread id to existing chats. + * + * @param matchMode The mode to set. + */ + public void setMatchMode(MatchMode matchMode) { + this.matchMode = matchMode; + } + /** * Creates a new chat and returns it. * @@ -138,12 +193,7 @@ public class ChatManager { * @return the created chat. */ public Chat createChat(String userJID, MessageListener listener) { - String threadID; - do { - threadID = nextID(); - } while (threadChats.get(threadID) != null); - - return createChat(userJID, threadID, listener); + return createChat(userJID, null, listener); } /** @@ -155,7 +205,7 @@ public class ChatManager { * @return the created chat. */ public Chat createChat(String userJID, String thread, MessageListener listener) { - if(thread == null) { + if (thread == null) { thread = nextID(); } Chat chat = threadChats.get(thread); @@ -191,20 +241,25 @@ public class ChatManager { } /** - * Try to get a matching chat for the given user JID. Try the full - * JID map first, the try to match on the base JID if no match is - * found. + * Try to get a matching chat for the given user JID, based on the {@link MatchMode}. + *

      • NONE - return null + *
      • SUPPLIED_JID - match the jid in the from field of the message exactly. + *
      • BARE_JID - if not match for from field, try the bare jid. * - * @param userJID - * @return + * @param userJID jid in the from field of message. + * @return Matching chat, or null if no match found. */ private Chat getUserChat(String userJID) { - Chat match = jidChats.get(userJID); + if (matchMode == MatchMode.NONE) { + return null; + } + + Chat match = jidChats.get(userJID); - if (match == null) { - match = baseJidChats.get(StringUtils.parseBareAddress(userJID)); - } - return match; + if (match == null && (matchMode == MatchMode.BARE_JID)) { + match = baseJidChats.get(StringUtils.parseBareAddress(userJID)); + } + return match; } public Chat getThreadChat(String thread) { @@ -278,4 +333,21 @@ public class ChatManager { interceptors.put(packetInterceptor, filter); } } + + /** + * Returns a unique id. + * + * @return the next id. + */ + private static String nextID() { + return UUID.randomUUID().toString(); + } + + public static void setDefaultMatchMode(MatchMode mode) { + defaultMatchMode = mode; + } + + public static void setDefaultIsNormalIncluded(boolean allowNormal) { + defaultIsNormalInclude = allowNormal; + } } diff --git a/source/org/jivesoftware/smack/packet/Message.java b/source/org/jivesoftware/smack/packet/Message.java index b2e25bf9c..a62b0e10a 100644 --- a/source/org/jivesoftware/smack/packet/Message.java +++ b/source/org/jivesoftware/smack/packet/Message.java @@ -80,7 +80,10 @@ public class Message extends Packet { */ public Message(String to, Type type) { setTo(to); - this.type = type; + + if (type != null) { + this.type = type; + } } /** diff --git a/source/org/jivesoftware/smack/packet/Packet.java b/source/org/jivesoftware/smack/packet/Packet.java index 041d8c892..27427acda 100644 --- a/source/org/jivesoftware/smack/packet/Packet.java +++ b/source/org/jivesoftware/smack/packet/Packet.java @@ -452,6 +452,7 @@ public abstract class Packet { return DEFAULT_LANGUAGE; } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -472,6 +473,7 @@ public abstract class Packet { return !(xmlns != null ? !xmlns.equals(packet.xmlns) : packet.xmlns != null); } + @Override public int hashCode() { int result; result = (xmlns != null ? xmlns.hashCode() : 0); @@ -483,4 +485,9 @@ public abstract class Packet { result = 31 * result + (error != null ? error.hashCode() : 0); return result; } + + @Override + public String toString() { + return toXML(); + } } diff --git a/test-unit/org/jivesoftware/smack/ChatConnectionTest.java b/test-unit/org/jivesoftware/smack/ChatConnectionTest.java index 3ded1ca44..72d56ff50 100644 --- a/test-unit/org/jivesoftware/smack/ChatConnectionTest.java +++ b/test-unit/org/jivesoftware/smack/ChatConnectionTest.java @@ -22,267 +22,419 @@ package org.jivesoftware.smack; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import org.jivesoftware.smack.ChatManager.MatchMode; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; -/** - * Tests that verifies the correct behavior of the {@see Roster} implementation. - * - * @see Roster - * @see Roster Management - * @author Guenther Niess - */ public class ChatConnectionTest { private DummyConnection connection; @Before public void setUp() throws Exception { - // Uncomment this to enable debug output - //Connection.DEBUG_ENABLED = true; - - connection = new DummyConnection(); - connection.connect(); - connection.login("me", "secret"); + connection = getConnection(); } @After public void tearDown() throws Exception { - if (connection != null) - connection.disconnect(); + if (connection != null) + connection.disconnect(); + } + + @Test + public void validateDefaultSetNormalIncluded() { + ChatManager.setDefaultIsNormalIncluded(false); + assertFalse(getConnection().getChatManager().isNormalIncluded()); + + ChatManager.setDefaultIsNormalIncluded(true); + assertTrue(getConnection().getChatManager().isNormalIncluded()); + } + + @Test + public void validateDefaultSetMatchMode() { + ChatManager.setDefaultMatchMode(MatchMode.NONE); + assertEquals(MatchMode.NONE, getConnection().getChatManager().getMatchMode()); + + ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); + assertEquals(MatchMode.BARE_JID, getConnection().getChatManager().getMatchMode()); + } + + @Test + public void validateMessageTypeWithDefaults() { + DummyConnection dc = getConnection(); + ChatManager cm = dc.getChatManager(); + TestChatManagerListener listener = new TestChatManagerListener(); + cm.addChatListener(listener); + Message incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.chat); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.groupchat); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.headline); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + } + + @Test + public void validateMessageTypeWithNoNormal() { + ChatManager.setDefaultIsNormalIncluded(false); + DummyConnection dc = getConnection(); + ChatManager cm = dc.getChatManager(); + TestChatManagerListener listener = new TestChatManagerListener(); + cm.addChatListener(listener); + Message incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.chat); + processServerMessage(incomingChat, dc); + assertNotNull(listener.getNewChat()); + + dc = getConnection(); + cm = dc.getChatManager(); + listener = new TestChatManagerListener(); + cm.addChatListener(listener); + incomingChat = createChatPacket("134", true); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat, dc); + assertNull(listener.getNewChat()); + } + + // No thread behaviour + @Test + public void chatMatchedOnJIDWhenNoThreadBareMode() { + // MatchMode.BARE_JID is the default, so setting required. + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + con.getChatManager().addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + + // Should match on chat with full jid + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + + // Should match on chat with bare jid + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(3, msgListener.getNumMessages()); + } + + @Test + public void chatMatchedOnJIDWhenNoThreadJidMode() { + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + ChatManager cm = con.getChatManager(); + cm.setMatchMode(MatchMode.SUPPLIED_JID); + cm.addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + cm.removeChatListener(listener); + + // Should match on chat with full jid + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + + // Should not match on chat with bare jid + TestChatManagerListener listener2 = new TestChatManagerListener(); + cm.addChatListener(listener2); + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(2, msgListener.getNumMessages()); + assertNotNull(listener2.getNewChat()); + } + + @Test + public void chatMatchedOnJIDWhenNoThreadNoneMode() { + DummyConnection con = getConnection(); + TestMessageListener msgListener = new TestMessageListener(); + TestChatManagerListener listener = new TestChatManagerListener(msgListener); + ChatManager cm = con.getChatManager(); + cm.setMatchMode(MatchMode.NONE); + cm.addChatListener(listener); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertEquals(1, msgListener.getNumMessages()); + cm.removeChatListener(listener); + + // Should not match on chat with full jid + TestChatManagerListener listener2 = new TestChatManagerListener(); + cm.addChatListener(listener2); + incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat, con); + assertEquals(1, msgListener.getNumMessages()); + assertNotNull(newChat); + cm.removeChatListener(listener2); + + // Should not match on chat with bare jid + TestChatManagerListener listener3 = new TestChatManagerListener(); + cm.addChatListener(listener3); + incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat, con); + assertEquals(1, msgListener.getNumMessages()); + assertNotNull(listener3.getNewChat()); } /** - * Confirm that a new chat is created when a chat message is received but - * there is no thread id for a user with only a base jid. + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread + * id and the user is a full jid. */ @Test - public void chatCreatedWithIncomingChatNoThreadBaseJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); + public void chatFoundWhenNoThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(null, true); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); } /** - * Confirm that a new chat is created when a chat message is received but - * there is no thread id for a user with a full jid. + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has no thread + * id and the user is a base jid. */ @Test - public void chatCreatedWhenIncomingChatNoThreadFullJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); + public void chatFoundWhenNoThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(null, false); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); } /** - * Confirm that an existing chat created with a base jid is matched to an - * incoming chat message that has no thread id and the user is a full jid. + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id + * and the user is a full jid. */ @Test - public void chatFoundWhenNoThreadFullJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + public void chatFoundWithSameThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(null, true); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(outgoing.getThreadID(), true); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); } /** - * Confirm that an existing chat created with a base jid is matched to an - * incoming chat message that has no thread id and the user is a base jid. + * Confirm that an existing chat created with a base jid is matched to an incoming chat message that has the same id + * and the user is a base jid. */ @Test - public void chatFoundWhenNoThreadBaseJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + public void chatFoundWithSameThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(null, false); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(outgoing.getThreadID(), false); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertTrue(newChat == outgoing); } /** - * Confirm that an existing chat created with a base jid is matched to an - * incoming chat message that has the same id and the user is a full jid. + * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a + * different id and the same user as a base jid. */ @Test - public void chatFoundWithSameThreadFullJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + public void chatNotFoundWithDiffThreadBaseJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(outgoing.getThreadID(), true); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", false); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertFalse(newChat == outgoing); } /** - * Confirm that an existing chat created with a base jid is matched to an - * incoming chat message that has the same id and the user is a base jid. + * Confirm that an existing chat created with a base jid is not matched to an incoming chat message that has a + * different id and the same base jid. */ @Test - public void chatFoundWithSameThreadBaseJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + public void chatNotFoundWithDiffThreadFullJid() { + TestChatManagerListener listener = new TestChatManagerListener(); + connection.getChatManager().addChatListener(listener); + Chat outgoing = connection.getChatManager().createChat("you@testserver", null); - Packet incomingChat = createChatPacket(outgoing.getThreadID(), false); - processServerMessage(incomingChat); + Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", true); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertTrue(newChat == outgoing); + Chat newChat = listener.getNewChat(); + assertNotNull(newChat); + assertFalse(newChat == outgoing); } - /** - * Confirm that an existing chat created with a base jid is not matched to - * an incoming chat message that has a different id and the same user as a - * base jid. - */ - @Ignore @Test - public void chatNotFoundWithDiffThreadBaseJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + public void chatNotMatchedWithTypeNormal() { + TestChatManagerListener listener = new TestChatManagerListener(); + DummyConnection con = getConnection(); + ChatManager cm = con.getChatManager(); + cm.setNormalIncluded(false); + cm.addChatListener(listener); - Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", false); - processServerMessage(incomingChat); + Message incomingChat = createChatPacket(null, false); + incomingChat.setType(Type.normal); + processServerMessage(incomingChat); - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertFalse(newChat == outgoing); + assertNull(listener.getNewChat()); } - /** - * Confirm that an existing chat created with a base jid is not matched to - * an incoming chat message that has a different id and the same base jid. - */ - @Ignore - @Test - public void chatNotFoundWithDiffThreadFullJid() - { - TestChatManagerListener listener = new TestChatManagerListener(); - connection.getChatManager().addChatListener(listener); - Chat outgoing = connection.getChatManager().createChat("you@testserver", null); + private ChatManager getChatManager(boolean includeNormal, MatchMode mode) { + ChatManager cm = getConnection().getChatManager(); + cm.setMatchMode(mode); + cm.setNormalIncluded(includeNormal); + return cm; + } + + private DummyConnection getConnection() { + DummyConnection con = new DummyConnection(); + + try { + con.connect(); + con.login("me", "secret"); + } catch (XMPPException e) { + // No need for handling in a dummy connection. + } + return con; + } + private Message createChatPacket(final String threadId, final boolean isFullJid) { + Message chatMsg = new Message("me@testserver", Message.Type.chat); + chatMsg.setBody("the body message - " + System.currentTimeMillis()); + chatMsg.setFrom("you@testserver" + (isFullJid ? "/resource" : "")); - Packet incomingChat = createChatPacket(outgoing.getThreadID() + "ff", true); - processServerMessage(incomingChat); - - Chat newChat = listener.getNewChat(); - assertNotNull(newChat); - assertFalse(newChat == outgoing); + if (threadId != null) + chatMsg.setThread(threadId); + return chatMsg; } - private Packet createChatPacket(final String threadId, final boolean isFullJid) - { - Message chatMsg = new Message("me@testserver", Message.Type.chat); - chatMsg.setBody("the body message"); - chatMsg.setFrom("you@testserver" + (isFullJid ? "/resource" : "")); - - if (threadId != null) - chatMsg.addExtension(new PacketExtension() - { - @Override - public String toXML() - { - return "" + threadId + ""; - } - - @Override - public String getNamespace() - { - return null; - } - - @Override - public String getElementName() - { - return "thread"; - } - }); - return chatMsg; + private void processServerMessage(Packet incomingChat) { + processServerMessage(incomingChat, connection); + } + + private void processServerMessage(Packet incomingChat, DummyConnection con) { + TestChatServer chatServer = new TestChatServer(incomingChat, con); + chatServer.start(); + try { + chatServer.join(); + } catch (InterruptedException e) { + fail(); + } } - private void processServerMessage(Packet incomingChat) - { - TestChatServer chatServer = new TestChatServer(incomingChat); - chatServer.start(); - try - { - chatServer.join(); - } catch (InterruptedException e) - { - fail(); - } + class TestChatManagerListener implements ChatManagerListener { + private Chat newChat; + private MessageListener listener; + + public TestChatManagerListener(TestMessageListener msgListener) { + listener = msgListener; + } + + public TestChatManagerListener() { + } + + @Override + public void chatCreated(Chat chat, boolean createdLocally) { + newChat = chat; + + if (listener != null) + newChat.addMessageListener(listener); + } + + public Chat getNewChat() { + return newChat; + } + } + + private class TestChatServer extends Thread { + private Packet chatPacket; + private DummyConnection con; + + TestChatServer(Packet chatMsg, DummyConnection conect) { + chatPacket = chatMsg; + con = conect; + } + + @Override + public void run() { + con.processPacket(chatPacket); + } } - class TestChatManagerListener implements ChatManagerListener - { - private Chat newChat; - - @Override - public void chatCreated(Chat chat, boolean createdLocally) - { - newChat = chat; - } - - public Chat getNewChat() - { - return newChat; - } - } - - private class TestChatServer extends Thread - { - private Packet chatPacket; - - TestChatServer(Packet chatMsg) - { - chatPacket = chatMsg; - } - - @Override - public void run() - { - connection.processPacket(chatPacket); - } - } + private class TestMessageListener implements MessageListener { + private Chat msgChat; + private int counter = 0; + + @Override + public void processMessage(Chat chat, Message message) { + msgChat = chat; + counter++; + } + + public Chat getChat() { + return msgChat; + } + + public int getNumMessages() { + return counter; + } + }; } From 9bb940da4b80a727317e173a4673fda0eb0df4ce Mon Sep 17 00:00:00 2001 From: rcollier Date: Sat, 1 Feb 2014 22:40:30 +0000 Subject: [PATCH 18/22] SMACK-526 Classes marked as deprecated to discourage their usage. Pubsub should be used instead. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13886 b35dd754-fafc-0310-a699-88a17e54d16e --- source/org/jivesoftware/smackx/PEPListener.java | 1 + source/org/jivesoftware/smackx/PEPManager.java | 1 + source/org/jivesoftware/smackx/packet/PEPEvent.java | 1 + source/org/jivesoftware/smackx/packet/PEPItem.java | 1 + source/org/jivesoftware/smackx/packet/PEPPubSub.java | 1 + 5 files changed, 5 insertions(+) diff --git a/source/org/jivesoftware/smackx/PEPListener.java b/source/org/jivesoftware/smackx/PEPListener.java index 1d3948438..bfe81cedb 100644 --- a/source/org/jivesoftware/smackx/PEPListener.java +++ b/source/org/jivesoftware/smackx/PEPListener.java @@ -29,6 +29,7 @@ import org.jivesoftware.smackx.packet.PEPEvent; * * @author Jeff Williams */ +@Deprecated public interface PEPListener { /** diff --git a/source/org/jivesoftware/smackx/PEPManager.java b/source/org/jivesoftware/smackx/PEPManager.java index 159b1d378..c30ee88a5 100644 --- a/source/org/jivesoftware/smackx/PEPManager.java +++ b/source/org/jivesoftware/smackx/PEPManager.java @@ -60,6 +60,7 @@ import org.jivesoftware.smackx.packet.PEPPubSub; * * @author Jeff Williams */ +@Deprecated public class PEPManager { private List pepListeners = new ArrayList(); diff --git a/source/org/jivesoftware/smackx/packet/PEPEvent.java b/source/org/jivesoftware/smackx/packet/PEPEvent.java index 48f1de25a..e9dab0e84 100644 --- a/source/org/jivesoftware/smackx/packet/PEPEvent.java +++ b/source/org/jivesoftware/smackx/packet/PEPEvent.java @@ -30,6 +30,7 @@ import org.jivesoftware.smack.packet.PacketExtension; * * @author Jeff Williams */ +@Deprecated public class PEPEvent implements PacketExtension { PEPItem item; diff --git a/source/org/jivesoftware/smackx/packet/PEPItem.java b/source/org/jivesoftware/smackx/packet/PEPItem.java index c3ff6f4f0..b216ebb66 100644 --- a/source/org/jivesoftware/smackx/packet/PEPItem.java +++ b/source/org/jivesoftware/smackx/packet/PEPItem.java @@ -30,6 +30,7 @@ import org.jivesoftware.smack.packet.PacketExtension; * * @author Jeff Williams */ +@Deprecated public abstract class PEPItem implements PacketExtension { String id; diff --git a/source/org/jivesoftware/smackx/packet/PEPPubSub.java b/source/org/jivesoftware/smackx/packet/PEPPubSub.java index 420ce6127..780bea210 100644 --- a/source/org/jivesoftware/smackx/packet/PEPPubSub.java +++ b/source/org/jivesoftware/smackx/packet/PEPPubSub.java @@ -30,6 +30,7 @@ import org.jivesoftware.smack.packet.IQ; * * @author Jeff Williams */ +@Deprecated public class PEPPubSub extends IQ { PEPItem item; From 1b651d4939e9e9bc6e62b70ba84a58f672263a2f Mon Sep 17 00:00:00 2001 From: rcollier Date: Sun, 2 Feb 2014 22:39:07 +0000 Subject: [PATCH 19/22] SMACK-534 Refactored all System.out/err and printStackTrace calls with appropriate Java util logging calls. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13887 b35dd754-fafc-0310-a699-88a17e54d16e --- .../jivesoftware/smack/AccountManager.java | 10 ++-- source/org/jivesoftware/smack/Connection.java | 8 +-- .../org/jivesoftware/smack/PacketReader.java | 11 ++-- .../org/jivesoftware/smack/PacketWriter.java | 13 +++-- .../smack/ReconnectionManager.java | 8 +-- .../smack/ServerTrustManager.java | 9 ++-- .../jivesoftware/smack/XMPPConnection.java | 1 - .../org/jivesoftware/smack/packet/Packet.java | 7 ++- .../parsing/ExceptionLoggingCallback.java | 13 +++-- .../org/jivesoftware/smack/util/Base64.java | 52 ++++++++----------- source/org/jivesoftware/smack/util/Cache.java | 10 ++-- .../smack/util/PacketParserUtils.java | 16 +++--- .../jivesoftware/smack/util/StringUtils.java | 12 +++-- .../smackx/MessageEventManager.java | 21 +++----- .../smackx/MultipleRecipientManager.java | 8 +-- .../org/jivesoftware/smackx/XHTMLManager.java | 6 ++- .../bytestreams/socks5/Socks5Proxy.java | 8 +-- .../smackx/commands/AdHocCommandManager.java | 9 +--- .../smackx/debugger/EnhancedDebugger.java | 30 +++-------- .../cache/SimpleDirectoryPersistentCache.java | 13 +++-- .../smackx/muc/MultiUserChat.java | 28 +++++----- .../org/jivesoftware/smackx/packet/Time.java | 9 ++-- .../org/jivesoftware/smackx/packet/VCard.java | 10 ++-- .../provider/StreamInitiationProvider.java | 7 ++- .../smackx/provider/VCardProvider.java | 9 ++-- .../smackx/workgroup/agent/AgentRoster.java | 5 +- .../smackx/workgroup/agent/AgentSession.java | 7 ++- .../smackx/workgroup/packet/QueueDetails.java | 6 ++- .../smackx/workgroup/user/Workgroup.java | 16 ------ .../util/ListenerEventDispatcher.java | 10 ++-- 30 files changed, 189 insertions(+), 183 deletions(-) diff --git a/source/org/jivesoftware/smack/AccountManager.java b/source/org/jivesoftware/smack/AccountManager.java index aa71c07ec..b90391c4b 100644 --- a/source/org/jivesoftware/smack/AccountManager.java +++ b/source/org/jivesoftware/smack/AccountManager.java @@ -32,6 +32,8 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Allows creation and management of accounts on an XMPP server. @@ -40,7 +42,8 @@ import java.util.Map; * @author Matt Tucker */ public class AccountManager { - + private static Logger logger = Logger.getLogger(AccountManager.class.getName()); + private Connection connection; private Registration info = null; @@ -134,7 +137,7 @@ public class AccountManager { } } catch (XMPPException xe) { - xe.printStackTrace(); + logger.log(Level.SEVERE, "Error retrieving account attributes from server", xe); } return Collections.emptySet(); } @@ -155,7 +158,7 @@ public class AccountManager { return info.getAttributes().get(name); } catch (XMPPException xe) { - xe.printStackTrace(); + logger.log(Level.SEVERE, "Error retrieving account attribute " + name + " info from server", xe); } return null; } @@ -175,6 +178,7 @@ public class AccountManager { return info.getInstructions(); } catch (XMPPException xe) { + logger.log(Level.SEVERE, "Error retrieving account instructions from server", xe); return null; } } diff --git a/source/org/jivesoftware/smack/Connection.java b/source/org/jivesoftware/smack/Connection.java index 925c26ef0..bd289e34a 100644 --- a/source/org/jivesoftware/smack/Connection.java +++ b/source/org/jivesoftware/smack/Connection.java @@ -34,6 +34,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; import org.jivesoftware.smack.compression.JzlibInputOutputStream; import org.jivesoftware.smack.compression.XMPPInputOutputStream; @@ -83,7 +84,8 @@ import org.jivesoftware.smack.packet.Presence; * @author Guenther Niess */ public abstract class Connection { - + private static Logger log = Logger.getLogger(Connection.class.getName()); + /** * Counter to uniquely identify connections that are created. */ @@ -764,7 +766,7 @@ public abstract class Connection { debuggerClass = Class.forName(className); } catch (Exception e) { - e.printStackTrace(); + log.warning("Unabled to instantiate debugger class " + className); } } if (debuggerClass == null) { @@ -778,7 +780,7 @@ public abstract class Connection { Class.forName("org.jivesoftware.smack.debugger.LiteDebugger"); } catch (Exception ex2) { - ex2.printStackTrace(); + log.warning("Unabled to instantiate either Smack debugger class"); } } } diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index 3aaad38c6..a00f53c5a 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -34,6 +34,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.util.concurrent.*; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Listens for XML traffic from the XMPP server and parses it into packet objects. @@ -45,6 +47,8 @@ import java.util.concurrent.*; */ class PacketReader { + private static Logger log = Logger.getLogger(PacketReader.class.getName()); + private Thread readerThread; private ExecutorService listenerExecutor; @@ -134,7 +138,7 @@ class PacketReader { catch (Exception e) { // Catch and print any exception so we can recover // from a faulty listener and finish the shutdown process - e.printStackTrace(); + log.log(Level.SEVERE, "Error in listener while closing connection", e); } } } @@ -156,7 +160,7 @@ class PacketReader { parser.setInput(connection.reader); } catch (XmlPullParserException xppe) { - xppe.printStackTrace(); + log.log(Level.WARNING, "Error while resetting parser", xppe); } } @@ -451,8 +455,7 @@ class PacketReader { try { listenerWrapper.notifyListener(packet); } catch (Exception e) { - System.err.println("Exception in packet listener: " + e); - e.printStackTrace(); + log.log(Level.SEVERE, "Exception in packet listener", e); } } } diff --git a/source/org/jivesoftware/smack/PacketWriter.java b/source/org/jivesoftware/smack/PacketWriter.java index 7e347cb7d..6529ce330 100644 --- a/source/org/jivesoftware/smack/PacketWriter.java +++ b/source/org/jivesoftware/smack/PacketWriter.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.io.Writer; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Writes packets to a XMPP server. Packets are sent using a dedicated thread. Packet @@ -38,7 +40,8 @@ import java.util.concurrent.BlockingQueue; * @author Matt Tucker */ class PacketWriter { - + private static Logger log = Logger.getLogger(PacketWriter.class.getName()); + private Thread writerThread; private Writer writer; private XMPPConnection connection; @@ -88,7 +91,7 @@ class PacketWriter { queue.put(packet); } catch (InterruptedException ie) { - ie.printStackTrace(); + log.log(Level.SEVERE, "Failed to queue packet to send to server: " + packet.toString(), ie); return; } synchronized (queue) { @@ -165,14 +168,14 @@ class PacketWriter { // we won't have time to entirely flush it before the socket is forced closed // by the shutdown process. try { - while (!queue.isEmpty()) { - Packet packet = queue.remove(); + while (!queue.isEmpty()) { + Packet packet = queue.remove(); writer.write(packet.toXML()); } writer.flush(); } catch (Exception e) { - e.printStackTrace(); + log.warning("Error flushing queue during shutdown, ignore and continue"); } // Delete the queue contents (hopefully nothing is left). diff --git a/source/org/jivesoftware/smack/ReconnectionManager.java b/source/org/jivesoftware/smack/ReconnectionManager.java index cc3e3af19..0f539b508 100644 --- a/source/org/jivesoftware/smack/ReconnectionManager.java +++ b/source/org/jivesoftware/smack/ReconnectionManager.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack; import org.jivesoftware.smack.packet.StreamError; import java.util.Random; +import java.util.logging.Logger; /** * Handles the automatic reconnection process. Every time a connection is dropped without * the application explictly closing it, the manager automatically tries to reconnect to @@ -34,7 +35,8 @@ import java.util.Random; * @author Francisco Vives */ public class ReconnectionManager implements ConnectionListener { - + private static Logger log = Logger.getLogger(ReconnectionManager.class.getName()); + // Holds the connection to the server private Connection connection; private Thread reconnectionThread; @@ -132,8 +134,8 @@ public class ReconnectionManager implements ConnectionListener { ReconnectionManager.this .notifyAttemptToReconnectIn(remainingSeconds); } - catch (InterruptedException e1) { - e1.printStackTrace(); + catch (InterruptedException e1) { + log.warning("Sleeping thread interrupted"); // Notify the reconnection has failed ReconnectionManager.this.notifyReconnectionFailed(e1); } diff --git a/source/org/jivesoftware/smack/ServerTrustManager.java b/source/org/jivesoftware/smack/ServerTrustManager.java index 19faed90c..a9e45a308 100644 --- a/source/org/jivesoftware/smack/ServerTrustManager.java +++ b/source/org/jivesoftware/smack/ServerTrustManager.java @@ -29,6 +29,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.*; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -41,7 +42,8 @@ import java.util.regex.Pattern; * @author Gaston Dombiak */ class ServerTrustManager implements X509TrustManager { - + private static Logger log = Logger.getLogger(ServerTrustManager.class.getName()); + private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); private ConnectionConfiguration configuration; @@ -153,8 +155,7 @@ class ServerTrustManager implements X509TrustManager { trusted = trustStore.getCertificateAlias(x509Certificates[nSize - 1]) != null; if (!trusted && nSize == 1 && configuration.isSelfSignedCertificateEnabled()) { - System.out.println("Accepting self-signed certificate of remote server: " + - peerIdentities); + log.info("Accepting self-signed certificate of remote server: " + peerIdentities); trusted = true; } } @@ -268,7 +269,7 @@ class ServerTrustManager implements X509TrustManager { } } // Other types are not good for XMPP so ignore them - System.out.println("SubjectAltName of invalid type found: " + certificate); + log.info("SubjectAltName of invalid type found: " + certificate); }*/ } catch (CertificateParsingException e) { diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index c775e52e4..13c16e1cd 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -788,7 +788,6 @@ public class XMPPConnection extends Connection { if(config.getCallbackHandler() == null) { ks = null; } else if (context == null) { - //System.out.println("Keystore type: "+configuration.getKeystoreType()); if(config.getKeystoreType().equals("NONE")) { ks = null; pcb = null; diff --git a/source/org/jivesoftware/smack/packet/Packet.java b/source/org/jivesoftware/smack/packet/Packet.java index 27427acda..aeae72fdf 100644 --- a/source/org/jivesoftware/smack/packet/Packet.java +++ b/source/org/jivesoftware/smack/packet/Packet.java @@ -27,6 +27,8 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Base class for XMPP packets. Every packet has a unique ID (which is automatically @@ -41,7 +43,8 @@ import java.util.concurrent.CopyOnWriteArrayList; * @author Matt Tucker */ public abstract class Packet { - + private static Logger log = Logger.getLogger(Packet.class.getName()); + protected static final String DEFAULT_LANGUAGE = java.util.Locale.getDefault().getLanguage().toLowerCase(); @@ -411,7 +414,7 @@ public abstract class Packet { buf.append(encodedVal).append(""); } catch (Exception e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error encoding java object", e); } finally { if (out != null) { diff --git a/source/org/jivesoftware/smack/parsing/ExceptionLoggingCallback.java b/source/org/jivesoftware/smack/parsing/ExceptionLoggingCallback.java index 771913ebf..b9073c306 100644 --- a/source/org/jivesoftware/smack/parsing/ExceptionLoggingCallback.java +++ b/source/org/jivesoftware/smack/parsing/ExceptionLoggingCallback.java @@ -20,18 +20,21 @@ package org.jivesoftware.smack.parsing; +import java.util.logging.Level; +import java.util.logging.Logger; + /** - * Simple parsing exception callback that only logs the encountered parsing exception to stderr. + * Simple parsing exception callback that only logs the encountered parsing exception to java util logging. * * @author Florian Schmaus * */ public class ExceptionLoggingCallback extends ParsingExceptionCallback { - + private static Logger log = Logger.getLogger(ExceptionLoggingCallback.class.getName()); + @Override public void handleUnparsablePacket(UnparsablePacket unparsed) throws Exception { - System.err.print("Smack message parsing exception: " + unparsed.getParsingException().getMessage()); - unparsed.getParsingException().printStackTrace(); - System.err.println("Unparsed content: " + unparsed.getContent()); + log.log(Level.SEVERE, "Smack message parsing exception: ", unparsed.getParsingException()); + log.severe("Unparsed content: " + unparsed.getContent()); } } diff --git a/source/org/jivesoftware/smack/util/Base64.java b/source/org/jivesoftware/smack/util/Base64.java index ba6eb371f..2ddd0138f 100644 --- a/source/org/jivesoftware/smack/util/Base64.java +++ b/source/org/jivesoftware/smack/util/Base64.java @@ -5,6 +5,9 @@ * */ package org.jivesoftware.smack.util; + +import java.util.logging.Level; +import java.util.logging.Logger; /** *

        Encodes and decodes to and from Base64 notation.

        @@ -17,9 +20,9 @@ package org.jivesoftware.smack.util; */ public class Base64 { - -/* ******** P U B L I C F I E L D S ******** */ - + private static Logger log = Logger.getLogger(Base64.class.getName()); + + /* ******** P U B L I C F I E L D S ******** */ /** No options specified. Value is zero. */ public final static int NO_OPTIONS = 0; @@ -311,18 +314,6 @@ public class Base64 /** Defeats instantiation. */ private Base64(){} - /** - * Prints command line usage. - * - * @param msg A message to include with usage info. - */ - private final static void usage( String msg ) - { - System.err.println( msg ); - System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); - } // end usage - - /* ******** E N C O D I N G M E T H O D S ******** */ @@ -494,7 +485,7 @@ public class Base64 } // end try catch( java.io.IOException e ) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error encoding object", e); return null; } // end catch finally @@ -623,7 +614,7 @@ public class Base64 } // end try catch( java.io.IOException e ) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error encoding bytes", e); return null; } // end catch finally @@ -777,11 +768,12 @@ public class Base64 destination[ destOffset + 2 ] = (byte)( outBuff ); return 3; - }catch( Exception e){ - System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + }catch( Exception e){ + log.log(Level.SEVERE, e.getMessage(), e); + log.severe(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + log.severe(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + log.severe(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + log.severe(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); return -1; } // end catch } @@ -839,7 +831,7 @@ public class Base64 } // end if: white space, equals sign or better else { - System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + log.warning("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); return null; } // end else: } // each input character @@ -967,12 +959,12 @@ public class Base64 } // end try catch( java.io.IOException e ) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error reading object", e); obj = null; } // end catch catch( java.lang.ClassNotFoundException e ) { - e.printStackTrace(); + log.log(Level.SEVERE, "Class not found for encoded object", e); obj = null; } // end catch finally @@ -1079,7 +1071,7 @@ public class Base64 // Check for size of file if( file.length() > Integer.MAX_VALUE ) { - System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); + log.warning("File is too big for this convenience method (" + file.length() + " bytes)."); return null; } // end if: file too big for int index buffer = new byte[ (int)file.length() ]; @@ -1100,7 +1092,7 @@ public class Base64 } // end try catch( java.io.IOException e ) { - System.err.println( "Error decoding from file " + filename ); + log.log(Level.SEVERE, "Error decoding from file " + filename, e); } // end catch: IOException finally { @@ -1148,7 +1140,7 @@ public class Base64 } // end try catch( java.io.IOException e ) { - System.err.println( "Error encoding from file " + filename ); + log.log(Level.SEVERE, "Error encoding from file " + filename, e); } // end catch: IOException finally { @@ -1175,7 +1167,7 @@ public class Base64 out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. } // end try catch( java.io.IOException ex ) { - ex.printStackTrace(); + log.log(Level.SEVERE, "Error encoding file " + infile, ex); } // end catch finally { try { out.close(); } @@ -1201,7 +1193,7 @@ public class Base64 out.write( decoded ); } // end try catch( java.io.IOException ex ) { - ex.printStackTrace(); + log.log(Level.SEVERE, "Error decoding file " + infile, ex); } // end catch finally { try { out.close(); } diff --git a/source/org/jivesoftware/smack/util/Cache.java b/source/org/jivesoftware/smack/util/Cache.java index 964ac2382..5426a4ad8 100644 --- a/source/org/jivesoftware/smack/util/Cache.java +++ b/source/org/jivesoftware/smack/util/Cache.java @@ -22,6 +22,7 @@ package org.jivesoftware.smack.util; import org.jivesoftware.smack.util.collections.AbstractMapEntry; import java.util.*; +import java.util.logging.Logger; /** * A specialized Map that is size-limited (using an LRU algorithm) and @@ -49,7 +50,7 @@ import java.util.*; * @author Matt Tucker */ public class Cache implements Map { - + private static Logger log = Logger.getLogger(Cache.class.getName()); /** * The map the keys and values are stored in. */ @@ -382,8 +383,7 @@ public class Cache implements Map { while (expireTime > node.timestamp) { if (remove(node.object, true) == null) { - System.err.println("Error attempting to remove(" + node.object.toString() + - ") - cacheObject not found in cache!"); + log.warning("Error attempting to remove(" + node.object.toString() + ") - cacheObject not found in cache!"); // remove from the ageList node.remove(); } @@ -417,9 +417,7 @@ public class Cache implements Map { for (int i=map.size(); i>desiredSize; i--) { // Get the key and invoke the remove method on it. if (remove(lastAccessedList.getLast().object, true) == null) { - System.err.println("Error attempting to cullCache with remove(" + - lastAccessedList.getLast().object.toString() + ") - " + - "cacheObject not found in cache!"); + log.warning("Error attempting to cullCache with remove(" + lastAccessedList.getLast().object.toString() + ") - cacheObject not found in cache!"); lastAccessedList.getLast().remove(); } } diff --git a/source/org/jivesoftware/smack/util/PacketParserUtils.java b/source/org/jivesoftware/smack/util/PacketParserUtils.java index 7fba71f90..a8d6b9e36 100644 --- a/source/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/source/org/jivesoftware/smack/util/PacketParserUtils.java @@ -29,6 +29,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.packet.Authentication; @@ -57,7 +59,8 @@ import org.xmlpull.v1.XmlPullParserException; * @author Gaston Dombiak */ public class PacketParserUtils { - + private static Logger logger = Logger.getLogger(PacketParserUtils.class.getName()); + /** * Namespace used to store packet properties. */ @@ -198,7 +201,7 @@ public class PacketParserUtils { type = Presence.Type.valueOf(typeString); } catch (IllegalArgumentException iae) { - System.err.println("Found invalid presence type " + typeString); + logger.warning("Found invalid presence type " + typeString); } } Presence presence = new Presence(type); @@ -242,7 +245,7 @@ public class PacketParserUtils { presence.setMode(Presence.Mode.valueOf(modeText)); } catch (IllegalArgumentException iae) { - System.err.println("Found invalid presence mode " + modeText); + logger.warning("Found invalid presence mode " + modeText); } } else if (elementName.equals("error")) { @@ -263,7 +266,7 @@ public class PacketParserUtils { presence.addExtension(PacketParserUtils.parsePacketExtension(elementName, namespace, parser)); } catch (Exception e) { - System.err.println("Failed to parse extension packet in Presence packet."); + logger.warning("Failed to parse extension packet in Presence packet."); } } } @@ -639,7 +642,7 @@ public class PacketParserUtils { value = in.readObject(); } catch (Exception e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Error parsing java object", e); } } if (name != null && value != null) { @@ -782,8 +785,7 @@ public class PacketParserUtils { } } catch (IllegalArgumentException iae) { - // Print stack trace. We shouldn't be getting an illegal error type. - iae.printStackTrace(); + logger.log(Level.SEVERE, "Could not find error type for " + type.toUpperCase(), iae); } return new XMPPError(Integer.parseInt(errorCode), errorType, condition, message, extensions); } diff --git a/source/org/jivesoftware/smack/util/StringUtils.java b/source/org/jivesoftware/smack/util/StringUtils.java index 7e3cfdc73..eecd84dda 100644 --- a/source/org/jivesoftware/smack/util/StringUtils.java +++ b/source/org/jivesoftware/smack/util/StringUtils.java @@ -34,6 +34,8 @@ import java.util.Date; import java.util.List; import java.util.Random; import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -41,7 +43,8 @@ import java.util.regex.Pattern; * A collection of utility methods for String objects. */ public class StringUtils { - + private static Logger log = Logger.getLogger(StringUtils.class.getName()); + /** * Date format as defined in XEP-0082 - XMPP Date and Time Profiles. The time zone is set to * UTC. @@ -619,8 +622,7 @@ public class StringUtils { digest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException nsae) { - System.err.println("Failed to load the SHA-1 MessageDigest. " + - "Jive will be unable to function normally."); + log.log(Level.SEVERE, "Failed to load the SHA-1 MessageDigest. Smack will be unable to function normally.", nsae); } } // Now, compute hash. @@ -628,7 +630,7 @@ public class StringUtils { digest.update(data.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { - System.err.println(e); + log.log(Level.SEVERE, "Error computing hash", e); } return encodeHex(digest.digest()); } @@ -664,7 +666,7 @@ public class StringUtils { bytes = data.getBytes("ISO-8859-1"); } catch (UnsupportedEncodingException uee) { - uee.printStackTrace(); + throw new IllegalStateException(uee); } return encodeBase64(bytes); } diff --git a/source/org/jivesoftware/smackx/MessageEventManager.java b/source/org/jivesoftware/smackx/MessageEventManager.java index 3502509d4..900914b3d 100644 --- a/source/org/jivesoftware/smackx/MessageEventManager.java +++ b/source/org/jivesoftware/smackx/MessageEventManager.java @@ -25,6 +25,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Connection; @@ -42,7 +44,8 @@ import org.jivesoftware.smackx.packet.MessageEvent; * @author Gaston Dombiak */ public class MessageEventManager { - + private static Logger log = Logger.getLogger(MessageEventManager.class.getName()); + private List messageEventNotificationListeners = new ArrayList(); private List messageEventRequestListeners = new ArrayList(); @@ -157,12 +160,8 @@ public class MessageEventManager { for (int i = 0; i < listeners.length; i++) { method.invoke(listeners[i], new Object[] { from, packetID, this }); } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + } catch (Exception e) { + log.log(Level.SEVERE, "Error while invoking MessageEventRequestListener", e); } } @@ -188,12 +187,8 @@ public class MessageEventManager { for (int i = 0; i < listeners.length; i++) { method.invoke(listeners[i], new Object[] { from, packetID }); } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + } catch (Exception e) { + log.log(Level.SEVERE, "Error while invoking MessageEventNotificationListener", e); } } diff --git a/source/org/jivesoftware/smackx/MultipleRecipientManager.java b/source/org/jivesoftware/smackx/MultipleRecipientManager.java index 83afaceb8..2fb525aea 100644 --- a/source/org/jivesoftware/smackx/MultipleRecipientManager.java +++ b/source/org/jivesoftware/smackx/MultipleRecipientManager.java @@ -33,6 +33,8 @@ import org.jivesoftware.smackx.packet.MultipleAddresses; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * A MultipleRecipientManager allows to send packets to multiple recipients by making use of @@ -42,7 +44,8 @@ import java.util.List; * @author Gaston Dombiak */ public class MultipleRecipientManager { - + private static Logger log = Logger.getLogger(MultipleRecipientManager.class.getName()); + /** * Create a cache to hold the 100 most recently accessed elements for a period of * 24 hours. @@ -309,13 +312,12 @@ public class MultipleRecipientManager { break; } } - } // Cache the discovered information services.put(serviceName, serviceAddress == null ? "" : serviceAddress); } catch (XMPPException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error occurred retrieving multiple recipients service", e); } } } diff --git a/source/org/jivesoftware/smackx/XHTMLManager.java b/source/org/jivesoftware/smackx/XHTMLManager.java index a446819c4..d77c2e2d0 100644 --- a/source/org/jivesoftware/smackx/XHTMLManager.java +++ b/source/org/jivesoftware/smackx/XHTMLManager.java @@ -28,6 +28,8 @@ import org.jivesoftware.smackx.packet.DiscoverInfo; import org.jivesoftware.smackx.packet.XHTMLExtension; import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Manages XHTML formatted texts within messages. A XHTMLManager provides a high level access to @@ -38,6 +40,8 @@ import java.util.Iterator; */ public class XHTMLManager { + private static Logger log = Logger.getLogger(XHTMLManager.class.getName()); + private final static String namespace = "http://jabber.org/protocol/xhtml-im"; // Enable the XHTML support on every established connection @@ -137,7 +141,7 @@ public class XHTMLManager { return result.containsFeature(namespace); } catch (XMPPException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error checking if service is available", e); return false; } } diff --git a/source/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/source/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java index 11ef7a9c5..cbafd4115 100644 --- a/source/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java +++ b/source/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java @@ -29,6 +29,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.XMPPException; @@ -67,7 +69,8 @@ import org.jivesoftware.smack.XMPPException; * @author Henning Staib */ public class Socks5Proxy { - + private static Logger log = Logger.getLogger(Socks5Proxy.class.getName()); + /* SOCKS5 proxy singleton */ private static Socks5Proxy socks5Server; @@ -150,8 +153,7 @@ public class Socks5Proxy { } catch (IOException e) { // couldn't setup server - System.err.println("couldn't setup local SOCKS5 proxy on port " - + SmackConfiguration.getLocalSocks5ProxyPort() + ": " + e.getMessage()); + log.log(Level.SEVERE, "couldn't setup local SOCKS5 proxy on port " + SmackConfiguration.getLocalSocks5ProxyPort(), e); } } diff --git a/source/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/source/org/jivesoftware/smackx/commands/AdHocCommandManager.java index 28401d67d..64159aea9 100755 --- a/source/org/jivesoftware/smackx/commands/AdHocCommandManager.java +++ b/source/org/jivesoftware/smackx/commands/AdHocCommandManager.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; /** * An AdHocCommandManager is responsible for keeping the list of available @@ -58,7 +59,6 @@ import java.util.concurrent.ConcurrentHashMap; * @author Gabriel Guardincerri */ public class AdHocCommandManager { - private static final String DISCO_NAMESPACE = "http://jabber.org/protocol/commands"; private static final String discoNode = DISCO_NAMESPACE; @@ -470,7 +470,6 @@ public class AdHocCommandManager { executingCommands.remove(sessionId); } respondError(response, error); - e.printStackTrace(); } } else { @@ -527,7 +526,7 @@ public class AdHocCommandManager { } try { - // TODO: Check that all the requierd fields of the form are + // TODO: Check that all the required fields of the form are // TODO: filled, if not throw an exception. This will simplify the // TODO: construction of new commands @@ -585,8 +584,6 @@ public class AdHocCommandManager { executingCommands.remove(sessionId); } respondError(response, error); - - e.printStackTrace(); } } } @@ -650,12 +647,10 @@ public class AdHocCommandManager { command.setNode(commandInfo.getNode()); } catch (InstantiationException e) { - e.printStackTrace(); throw new XMPPException(new XMPPError( XMPPError.Condition.interna_server_error)); } catch (IllegalAccessException e) { - e.printStackTrace(); throw new XMPPException(new XMPPError( XMPPError.Condition.interna_server_error)); } diff --git a/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java b/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java index ab9c29c4a..55aec6060 100644 --- a/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java +++ b/source/org/jivesoftware/smackx/debugger/EnhancedDebugger.java @@ -52,6 +52,8 @@ import java.io.Writer; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; /** * The EnhancedDebugger is a debugger that allows to debug sent, received and interpreted messages @@ -64,6 +66,8 @@ import java.util.Date; */ public class EnhancedDebugger implements SmackDebugger { + private static Logger log = Logger.getLogger(EnhancedDebugger.class.getName()); + private static final String NEWLINE = "\n"; private static ImageIcon packetReceivedIcon; @@ -427,7 +431,7 @@ public class EnhancedDebugger implements SmackDebugger { receivedText.replaceRange("", 0, receivedText.getLineEndOffset(0)); } catch (BadLocationException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } receivedText.append(str.substring(0, index + 1)); @@ -462,7 +466,7 @@ public class EnhancedDebugger implements SmackDebugger { sentText.replaceRange("", 0, sentText.getLineEndOffset(0)); } catch (BadLocationException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e); } } @@ -895,28 +899,10 @@ public class EnhancedDebugger implements SmackDebugger { } catch (TransformerConfigurationException tce) { - // Error generated by the parser - System.out.println("\n** Transformer Factory error"); - System.out.println(" " + tce.getMessage()); - - // Use the contained exception, if any - Throwable x = tce; - if (tce.getException() != null) - x = tce.getException(); - x.printStackTrace(); - + log.log(Level.SEVERE, "Transformer Factory error", tce); } catch (TransformerException te) { - // Error generated by the parser - System.out.println("\n** Transformation error"); - System.out.println(" " + te.getMessage()); - - // Use the contained exception, if any - Throwable x = te; - if (te.getException() != null) - x = te.getException(); - x.printStackTrace(); - + log.log(Level.SEVERE, "Transformation error", te); } return str; } diff --git a/source/org/jivesoftware/smackx/entitycaps/cache/SimpleDirectoryPersistentCache.java b/source/org/jivesoftware/smackx/entitycaps/cache/SimpleDirectoryPersistentCache.java index ae0e11633..3f70660e7 100644 --- a/source/org/jivesoftware/smackx/entitycaps/cache/SimpleDirectoryPersistentCache.java +++ b/source/org/jivesoftware/smackx/entitycaps/cache/SimpleDirectoryPersistentCache.java @@ -24,6 +24,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.provider.IQProvider; @@ -40,13 +42,14 @@ import org.xmlpull.v1.XmlPullParserException; /** * Simple implementation of an EntityCapsPersistentCache that uses a directory * to store the Caps information for every known node. Every node is represented - * by an file. + * by a file. * * @author Florian Schmaus * */ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache { - + private static Logger log = Logger.getLogger(SimpleDirectoryPersistentCache.class.getName()); + private File cacheDir; private StringEncoder filenameEncoder; @@ -55,7 +58,7 @@ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache * cacheDir exists and that it's an directory. *

        * Default filename encoder {@link Base32Encoder}, as this will work on all - * filesystems, both case sensitive and case insensitive. It does however + * file systems, both case sensitive and case insensitive. It does however * produce longer filenames. * * @param cacheDir @@ -92,7 +95,7 @@ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache if (nodeFile.createNewFile()) writeInfoToFile(nodeFile, info); } catch (IOException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to write disco info to file", e); } } @@ -161,7 +164,7 @@ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(reader); } catch (XmlPullParserException xppe) { - xppe.printStackTrace(); + log.log(Level.SEVERE, "Exception initializing parser", xppe); return null; } diff --git a/source/org/jivesoftware/smackx/muc/MultiUserChat.java b/source/org/jivesoftware/smackx/muc/MultiUserChat.java index d657ee711..db8a80f73 100644 --- a/source/org/jivesoftware/smackx/muc/MultiUserChat.java +++ b/source/org/jivesoftware/smackx/muc/MultiUserChat.java @@ -31,6 +31,8 @@ import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ConnectionCreationListener; @@ -76,7 +78,8 @@ import org.jivesoftware.smackx.packet.MUCUser; * @author Gaston Dombiak, Larry Kirschner */ public class MultiUserChat { - + private static Logger log = Logger.getLogger(MultiUserChat.class.getName()); + private final static String discoNamespace = "http://jabber.org/protocol/muc"; private final static String discoNode = "http://jabber.org/protocol/muc#rooms"; @@ -179,7 +182,7 @@ public class MultiUserChat { return result.containsFeature(discoNamespace); } catch (XMPPException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error checking user [" + user + "] for MUC support", e); return false; } } @@ -222,7 +225,7 @@ public class MultiUserChat { return answer.iterator(); } catch (XMPPException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error getting joined rooms for user [" + user + "]", e); // Return an iterator on an empty collection return new ArrayList().iterator(); } @@ -953,13 +956,12 @@ public class MultiUserChat { DiscoverInfo.Identity identity = identities.next(); return identity.getName(); } - // If no Identity was found then the user does not have a reserved room nickname - return null; } catch (XMPPException e) { - e.printStackTrace(); - return null; + log.log(Level.SEVERE, "Error retrieving room nickname", e); } + // If no Identity was found then the user does not have a reserved room nickname + return null; } /** @@ -2061,11 +2063,11 @@ public class MultiUserChat { method.invoke(listener, params); } } catch (NoSuchMethodException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on UserStatusListener", e); } catch (InvocationTargetException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on UserStatusListener", e); } catch (IllegalAccessException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on UserStatusListener", e); } } @@ -2112,11 +2114,11 @@ public class MultiUserChat { method.invoke(listener, params.toArray()); } } catch (NoSuchMethodException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on ParticipantStatusListener", e); } catch (InvocationTargetException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on ParticipantStatusListener", e); } catch (IllegalAccessException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to invoke method on ParticipantStatusListener", e); } } diff --git a/source/org/jivesoftware/smackx/packet/Time.java b/source/org/jivesoftware/smackx/packet/Time.java index 5294e77c3..fddc86998 100644 --- a/source/org/jivesoftware/smackx/packet/Time.java +++ b/source/org/jivesoftware/smackx/packet/Time.java @@ -27,6 +27,8 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; /** * A Time IQ packet, which is used by XMPP clients to exchange their respective local @@ -61,7 +63,8 @@ import java.util.TimeZone; * @author Matt Tucker */ public class Time extends IQ { - + private static Logger log = Logger.getLogger(Time.class.getName()); + private static SimpleDateFormat utcFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"); private static DateFormat displayFormat = DateFormat.getDateTimeInstance(); @@ -94,7 +97,7 @@ public class Time extends IQ { /** * Returns the local time or null if the time hasn't been set. * - * @return the lcocal time. + * @return the local time. */ public Date getTime() { if (utc == null) { @@ -109,7 +112,7 @@ public class Time extends IQ { date = cal.getTime(); } catch (Exception e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error getting local time", e); } return date; } diff --git a/source/org/jivesoftware/smackx/packet/VCard.java b/source/org/jivesoftware/smackx/packet/VCard.java index c9aa65856..98598915d 100644 --- a/source/org/jivesoftware/smackx/packet/VCard.java +++ b/source/org/jivesoftware/smackx/packet/VCard.java @@ -33,6 +33,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.PacketCollector; @@ -85,6 +87,8 @@ import org.jivesoftware.smack.util.StringUtils; * @author Kirill Maximov (kir@maxkir.com) */ public class VCard extends IQ { + private static Logger log = Logger.getLogger(VCard.class.getName()); + private static final String DEFAULT_MIME_TYPE = "image/jpeg"; /** @@ -332,7 +336,7 @@ public class VCard extends IQ { bytes = getBytes(avatarURL); } catch (IOException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error getting bytes from URL: " + avatarURL, e); } setAvatar(bytes); @@ -489,7 +493,7 @@ public class VCard extends IQ { digest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to get message digest", e); return null; } @@ -582,7 +586,7 @@ public class VCard extends IQ { result = (VCard) packet; } catch (ClassCastException e) { - System.out.println("No VCard for " + user); + log.log(Level.SEVERE, "No VCard for " + user, e); return; } diff --git a/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java b/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java index 11ab33976..bd2d54ee1 100644 --- a/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java +++ b/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java @@ -21,6 +21,8 @@ package org.jivesoftware.smackx.provider; import java.text.ParseException; import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.provider.IQProvider; @@ -37,7 +39,8 @@ import org.xmlpull.v1.XmlPullParser; * */ public class StreamInitiationProvider implements IQProvider { - + private static Logger log = Logger.getLogger(StreamInitiationProvider.class.getName()); + public IQ parseIQ(final XmlPullParser parser) throws Exception { boolean done = false; @@ -90,7 +93,7 @@ public class StreamInitiationProvider implements IQProvider { fileSize = Long.parseLong(size); } catch (NumberFormatException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Failed to parse file size from " + fileSize, e); } } diff --git a/source/org/jivesoftware/smackx/provider/VCardProvider.java b/source/org/jivesoftware/smackx/provider/VCardProvider.java index 8fa04211d..25c7110a2 100644 --- a/source/org/jivesoftware/smackx/provider/VCardProvider.java +++ b/source/org/jivesoftware/smackx/provider/VCardProvider.java @@ -34,6 +34,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * vCard provider. @@ -42,7 +44,8 @@ import java.util.List; * @author Derek DeMoro */ public class VCardProvider implements IQProvider { - + private static Logger log = Logger.getLogger(VCardProvider.class.getName()); + private static final String PREFERRED_ENCODING = "UTF-8"; public IQ parseIQ(XmlPullParser parser) throws Exception { @@ -71,10 +74,10 @@ public class VCardProvider implements IQProvider { } } catch (XmlPullParserException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Exception parsing VCard", e); } catch (IOException e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Exception parsing VCard", e); } String xmlText = sb.toString(); diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java index 70c95ee2e..0dd74b16f 100644 --- a/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentRoster.java @@ -37,6 +37,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; /** * Manges information about the agents in a workgroup and their presence. @@ -45,7 +46,7 @@ import java.util.Set; * @see AgentSession#getAgentRoster() */ public class AgentRoster { - + private static Logger log = Logger.getLogger(AgentRoster.class.getName()); private static final int EVENT_AGENT_ADDED = 0; private static final int EVENT_AGENT_REMOVED = 1; private static final int EVENT_PRESENCE_CHANGED = 2; @@ -284,7 +285,7 @@ public class AgentRoster { String from = presence.getFrom(); if (from == null) { // TODO Check if we need to ignore these presences or this is a server bug? - System.out.println("Presence with no FROM: " + presence.toXML()); + log.warning("Presence with no FROM: " + presence.toXML()); return; } String key = getPresenceMapKey(from); diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java index 46d19d0d5..16499f1bb 100644 --- a/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java @@ -40,6 +40,8 @@ import org.jivesoftware.smackx.ReportedData; import org.jivesoftware.smackx.packet.MUCUser; import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This class embodies the agent's active presence within a given workgroup. The application @@ -53,7 +55,8 @@ import java.util.*; * @author Derek DeMoro */ public class AgentSession { - + private static Logger log = Logger.getLogger(AgentSession.class.getName()); + private Connection connection; private String workgroupJID; @@ -118,7 +121,7 @@ public class AgentSession { handlePacket(packet); } catch (Exception e) { - e.printStackTrace(); + log.log(Level.SEVERE, "Error processing packet", e); } } }; diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java index 86b3673e8..230ceba6a 100644 --- a/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/packet/QueueDetails.java @@ -29,13 +29,15 @@ import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.logging.Logger; /** * Queue details packet extension, which contains details about the users * currently in a queue. */ public class QueueDetails implements PacketExtension { - + private static Logger log = Logger.getLogger(QueueDetails.class.getName()); + /** * Element name of the packet extension. */ @@ -178,7 +180,7 @@ public class QueueDetails implements PacketExtension { } else if( parser.getName().equals( "waitTime" ) ) { Date wait = dateFormat.parse(parser.nextText()); - System.out.println( wait ); + log.fine(wait.toString()); } eventType = parser.next(); diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java b/workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java index 237337f00..d19c6d669 100644 --- a/workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/user/Workgroup.java @@ -849,20 +849,4 @@ public class Workgroup { } return Form.getFormFrom(response); } - - /* - public static void main(String args[]) throws Exception { - Connection con = new XMPPConnection("anteros"); - con.connect(); - con.loginAnonymously(); - - Workgroup workgroup = new Workgroup("demo@workgroup.anteros", con); - WorkgroupProperties props = workgroup.getWorkgroupProperties("derek@anteros.com"); - - System.out.print(props); - con.disconnect(); - } - */ - - } \ No newline at end of file diff --git a/workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java b/workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java index 533b9a132..1c7f5beec 100644 --- a/workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java +++ b/workgroup/source/org/jivesoftware/smackx/workgroup/util/ListenerEventDispatcher.java @@ -19,6 +19,8 @@ package org.jivesoftware.smackx.workgroup.util; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.ListIterator; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This class is a very flexible event dispatcher which implements Runnable so that it can @@ -32,8 +34,8 @@ import java.util.ListIterator; * * @author loki der quaeler */ -public class ListenerEventDispatcher - implements Runnable { +public class ListenerEventDispatcher implements Runnable { + private static Logger log = Logger.getLogger(ListenerEventDispatcher.class.getName()); protected transient ArrayList triplets; @@ -93,9 +95,7 @@ public class ListenerEventDispatcher try { tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments()); } catch (Exception e) { - System.err.println("Exception dispatching an event: " + e); - - e.printStackTrace(); + log.log(Level.SEVERE, "Exception dispatching an event", e); } } From 719f44dfdf0ea6c708ae6c67f3dc609c43953c91 Mon Sep 17 00:00:00 2001 From: rcollier Date: Sun, 2 Feb 2014 22:40:22 +0000 Subject: [PATCH 20/22] SMACK-534 Removed the setting of JUL properties form the default config, they now have to be enabled explicitly so as to not mess up any existing config a user might have. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13888 b35dd754-fafc-0310-a699-88a17e54d16e --- build/resources/META-INF/smack-config.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/resources/META-INF/smack-config.xml b/build/resources/META-INF/smack-config.xml index f3cad304e..7bfda704b 100644 --- a/build/resources/META-INF/smack-config.xml +++ b/build/resources/META-INF/smack-config.xml @@ -22,7 +22,6 @@ - org.jivesoftware.smack.LoggingInitializer org.jivesoftware.smack.provider.CoreInitializer org.jivesoftware.smack.provider.VmArgInitializer org.jivesoftware.smack.PrivacyListManager From 9a2993e661a1a70152333c110b8414fc440ec9b6 Mon Sep 17 00:00:00 2001 From: rcollier Date: Mon, 3 Feb 2014 01:55:16 +0000 Subject: [PATCH 21/22] Updated versioning for release (3.4.0) git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13891 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 2 +- build/release.xml | 7 +++- build/resources/releasedocs/README.html | 2 +- build/resources/releasedocs/changelog.html | 41 +++++++++++++++++++ .../smack/SmackConfiguration.java | 2 +- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/build/build.xml b/build/build.xml index 4e2111a7e..574660cc3 100644 --- a/build/build.xml +++ b/build/build.xml @@ -28,7 +28,7 @@ - + diff --git a/build/release.xml b/build/release.xml index cd20c2dce..71d3e491e 100644 --- a/build/release.xml +++ b/build/release.xml @@ -30,7 +30,10 @@ - + + + + @@ -65,7 +68,7 @@ - + diff --git a/build/resources/releasedocs/README.html b/build/resources/releasedocs/README.html index f935622c9..242dcb007 100644 --- a/build/resources/releasedocs/README.html +++ b/build/resources/releasedocs/README.html @@ -148,7 +148,7 @@ hr { @version@ released: - @builddate@ + @releasedate@ diff --git a/build/resources/releasedocs/changelog.html b/build/resources/releasedocs/changelog.html index 8540a94dd..96e5dd8c2 100644 --- a/build/resources/releasedocs/changelog.html +++ b/build/resources/releasedocs/changelog.html @@ -140,6 +140,47 @@ hr {

        + +

        3.4.0 -- Feb 2, 2014

        + +

        Bug Fixes

        +
          +
        • [SMACK-442] - Manager's should also handle connectionClosedOnError()
        • +
        • [SMACK-443] - ReconnectionSuccessful listeners are invoked twice on reconnection if connect() failed before
        • +
        • [SMACK-452] - PacketParserUtils.parseStreamError() is not aware of optional text element and therefore failes to parse stream error's correctly. Prevents ReconnectionManager from reconnecting.
        • +
        • [SMACK-458] - Smack's Managers should not remove itself when the connection is closed or should re-add themselfs if the connection get reconnected
        • +
        • [SMACK-462] - Prevent duplicate manager instances by using the manager's constructor in the ConnectionCreationListener's connectionCreated
        • +
        • [SMACK-463] - packet listeners silently fail when preceding listener caused exception
        • +
        • [SMACK-524] - Use correct block-size definition for IBB transfers
        • +
        • [SMACK-525] - NPE in XMPPConnection.notifyConnectionError
        • +
        • [SMACK-529] - Add support for XEP-0280 "Message Carbons"
        • +
        • [SMACK-530] - DNSUtilTest requires an internet connection to work, it should be moved to integration tests.
        • +
        + +

        New Feature

        +
          +
        • [SMACK-286] - Need to change ProviderManager to support loading smack.providers from alternative locations
        • +
        • [SMACK-387] - Allow configuration of ChatManager to be able to allow message handling to be customized.
        • +
        • [SMACK-403] - Add support for XEP-0297 "Stanza Forwarding"
        • +
        • [SMACK-434] - Create a project to contain non production ready implementations of specifications
        • +
        + +

        Improvement

        +
          +
        • [SMACK-343] - Make Smack jar an OSGi bundle.
        • +
        • [SMACK-381] - Separate the configuration for smack extension related classes from the smack jar.
        • +
        • [SMACK-444] - Allow 'null' for TruststorePath and TruststorePassword in ServerTrustManager
        • +
        • [SMACK-456] - Add the causing exception to the XMPPExceptions thrown in XMPPConnection
        • +
        • [SMACK-457] - Remove unnecessary printStackTrace() in XMPPConnection
        • +
        • [SMACK-460] - ServiceDiscoveryManager should not use the constructor in connectionCreated()
        • +
        • [SMACK-461] - Remove incorrect deprecated marker for DiscoverInfo.Identity.setType()
        • +
        • [SMACK-464] - Make it clear that PacketListener's added with Connection.addPacketListener() are only for received packets
        • +
        • [SMACK-534] - Convert all System.out and printStackTrace calls to use Java util logging.
        • +
        • [SMACK-339] - Allow ConnectionListeners to be added before Connection is connected. Currently throws exception
        • +
        • [SMACK-373] - Don't remove listeners after a disconnect() , keep state of Connection between disconnect() and connect()/login()
        • +
        • [SMACK-434] - Create a project to contain non production ready implementations of specifications
        • +
        • [SMACK-526] - Deprecate all PEP related classes.
        • +

        3.3.1 -- Oct 6, 2013

        diff --git a/source/org/jivesoftware/smack/SmackConfiguration.java b/source/org/jivesoftware/smack/SmackConfiguration.java index 77ffb3b84..57ff2b76e 100644 --- a/source/org/jivesoftware/smack/SmackConfiguration.java +++ b/source/org/jivesoftware/smack/SmackConfiguration.java @@ -51,7 +51,7 @@ import org.xmlpull.v1.XmlPullParser; * @author Gaston Dombiak */ public final class SmackConfiguration { - private static final String SMACK_VERSION = "3.3.1"; + private static final String SMACK_VERSION = "3.4.0"; private static final String DEFAULT_CONFIG_FILE = "classpath:META-INF/smack-config.xml"; private static final Logger log = Logger.getLogger(SmackConfiguration.class.getName()); From ab189706edc15763dc3f460fe116fb81bbfd6cb6 Mon Sep 17 00:00:00 2001 From: rcollier Date: Mon, 3 Feb 2014 02:03:54 +0000 Subject: [PATCH 22/22] Updated version for post release git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13893 b35dd754-fafc-0310-a699-88a17e54d16e --- build/build.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.xml b/build/build.xml index 574660cc3..bf190da6d 100644 --- a/build/build.xml +++ b/build/build.xml @@ -27,8 +27,8 @@ - - + +