From 21cad7ebb838492734880f6fa0d45f67d44e148f Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Tue, 3 Feb 2015 13:22:02 +0800 Subject: [PATCH] commit for sync --- build.gradle | 2 + twidere.component.common/build.gradle | 1 + .../org/mariotaku/twidere/api/APIFactory.java | 56 ++ .../org/mariotaku/twidere/api/TwitterAPI.java | 34 ++ .../twidere/api/TwitterMediaAPI.java | 29 + .../twidere/model/ParcelableAccount.java | 6 + .../java/twitter4j/http/HttpClientImpl.java | 537 +++++++++--------- twidere/build.gradle | 7 +- twidere/src/main/AndroidManifest.xml | 10 +- .../support/BrowserSignInActivity.java | 23 +- .../activity/support/ComposeActivity.java | 4 - .../activity/support/SignInActivity.java | 4 +- .../org/mariotaku/twidere/util/Utils.java | 9 +- .../util/net/ApacheHttpClientFactory.java | 22 - .../net/ApacheHttpClientHttpResponseImpl.java | 86 --- .../util/net/ApacheHttpClientImpl.java | 222 -------- .../net/HostResolvedHostnameVerifier.java | 34 ++ .../net/HostResolvedSSLSocketFactory.java | 94 +++ .../util/net/HostResolvedSocketFactory.java | 118 ++++ .../util/net/HttpParameterFormEntity.java | 19 - .../net/{ssl => }/OkHttpClientFactory.java | 9 +- .../twidere/util/net/OkHttpClientImpl.java | 148 +++-- .../util/net/TwidereHostAddressResolver.java | 4 +- .../ssl/AbstractCheckSignatureVerifier.java | 347 ----------- ...ostResolvedSSLConnectionSocketFactory.java | 194 ------- .../net/ssl/TrustAllX509TrustManager.java | 20 - .../util/net/ssl/TwidereHostnameVerifier.java | 34 -- .../util/net/ssl/TwidereSSLSocketFactory.java | 66 --- .../main/res/drawable-hdpi/ic_launcher.png | Bin 4815 -> 4479 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 2777 -> 2776 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 7753 -> 6331 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 14902 -> 10317 bytes .../main/res/drawable-xxxhdpi/ic_launcher.png | Bin 22502 -> 14674 bytes .../src/main/res/layout/activity_compose.xml | 5 - twidere/src/main/res/values/themes.xml | 19 +- 35 files changed, 772 insertions(+), 1391 deletions(-) create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/api/APIFactory.java create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterAPI.java create mode 100644 twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterMediaAPI.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientFactory.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientHttpResponseImpl.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientImpl.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedHostnameVerifier.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSSLSocketFactory.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSocketFactory.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/HttpParameterFormEntity.java rename twidere/src/main/java/org/mariotaku/twidere/util/net/{ssl => }/OkHttpClientFactory.java (86%) delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/AbstractCheckSignatureVerifier.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/HostResolvedSSLConnectionSocketFactory.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TrustAllX509TrustManager.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereHostnameVerifier.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereSSLSocketFactory.java diff --git a/build.gradle b/build.gradle index 2fa7532f0..0c531e3c8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,12 @@ buildscript { repositories { jcenter() mavenCentral() + maven { url 'https://raw.github.com/xujiaao/mvn-repository/master/releases' } } dependencies { classpath 'com.github.ben-manes:gradle-versions-plugin:0.7' classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.xujiaao:aarLinkSources:1.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/twidere.component.common/build.gradle b/twidere.component.common/build.gradle index db3fe4395..284634395 100644 --- a/twidere.component.common/build.gradle +++ b/twidere.component.common/build.gradle @@ -40,6 +40,7 @@ android { dependencies { compile 'com.android.support:support-annotations:21.0.3' compile 'org.apache.commons:commons-lang3:3.3.2' + compile 'com.squareup.retrofit:retrofit:1.9.0' compile project(':twidere.component.jsonserializer') compile project(':twidere.component.querybuilder') compile project(':twidere.component.twitter4j') diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/APIFactory.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/APIFactory.java new file mode 100644 index 000000000..3f02f2578 --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/APIFactory.java @@ -0,0 +1,56 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.api; + +import java.lang.reflect.Type; + +import retrofit.RestAdapter; +import retrofit.RestAdapter.Builder; +import retrofit.converter.ConversionException; +import retrofit.converter.Converter; +import retrofit.mime.TypedInput; +import retrofit.mime.TypedOutput; + +/** + * Created by mariotaku on 15/2/3. + */ +public class APIFactory { + + public static TwitterAPI getTwitterAPI() { + Builder builder = new RestAdapter.Builder(); + builder.setEndpoint("https://api.twitter.com"); + builder.setConverter(new ParcelableDataConverter()); + return builder.build().create(TwitterAPI.class); + } + + private static class ParcelableDataConverter implements Converter { + @Override + public Object fromBody(TypedInput typedInput, Type type) throws ConversionException { +// Class typeClass = (Class) type; +// typeClass.isAssignableFrom(); + return null; + } + + @Override + public TypedOutput toBody(Object o) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterAPI.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterAPI.java new file mode 100644 index 000000000..2ef42719c --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterAPI.java @@ -0,0 +1,34 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.api; + +import org.mariotaku.twidere.model.ParcelableAccount; + +import retrofit.http.GET; + +/** + * Created by mariotaku on 15/2/3. + */ +public interface TwitterAPI { + + @GET("/account/verify_credentials.json") + ParcelableAccount verifyCredentials(); + +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterMediaAPI.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterMediaAPI.java new file mode 100644 index 000000000..7a0cb377c --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/api/TwitterMediaAPI.java @@ -0,0 +1,29 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.api; + +import org.mariotaku.twidere.model.ParcelableAccount; + +/** + * Created by mariotaku on 15/2/3. + */ +public interface TwitterMediaAPI { + +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java index 19e0bc2bf..0caeee999 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableAccount.java @@ -26,6 +26,10 @@ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + import org.mariotaku.querybuilder.Columns.Column; import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.RawItemArray; @@ -33,6 +37,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.util.TwitterContentUtils; import org.mariotaku.twidere.util.content.ContentResolverUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -362,4 +367,5 @@ public class ParcelableAccount implements Parcelable { + ", same_oauth_signing_url=" + same_oauth_signing_url + "}"; } } + } diff --git a/twidere.component.twitter4j/src/main/java/twitter4j/http/HttpClientImpl.java b/twidere.component.twitter4j/src/main/java/twitter4j/http/HttpClientImpl.java index a6cfaa16d..80c2c124e 100644 --- a/twidere.component.twitter4j/src/main/java/twitter4j/http/HttpClientImpl.java +++ b/twidere.component.twitter4j/src/main/java/twitter4j/http/HttpClientImpl.java @@ -61,294 +61,293 @@ import static twitter4j.http.RequestMethod.POST; * @since Twitter4J 2.1.2 */ public class HttpClientImpl extends HttpClientBase implements HttpClient, HttpResponseCode { - private static final Logger logger = Logger.getLogger(HttpClientImpl.class); + private static final Logger logger = Logger.getLogger(HttpClientImpl.class); - private static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[] { new TrustAllX509TrustManager() }; + private static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{new TrustAllX509TrustManager()}; - private static final SSLSocketFactory IGNORE_ERROR_SSL_FACTORY; + private static final SSLSocketFactory IGNORE_ERROR_SSL_FACTORY; - static { - System.setProperty("http.keepAlive", "false"); - SSLSocketFactory factory = null; - try { - final SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, TRUST_ALL_CERTS, new SecureRandom()); - factory = sc.getSocketFactory(); - } catch (final KeyManagementException e) { - } catch (final NoSuchAlgorithmException e) { - } - IGNORE_ERROR_SSL_FACTORY = factory; - } + static { + System.setProperty("http.keepAlive", "false"); + SSLSocketFactory factory = null; + try { + final SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, TRUST_ALL_CERTS, new SecureRandom()); + factory = sc.getSocketFactory(); + } catch (final KeyManagementException | NoSuchAlgorithmException e) { + } + IGNORE_ERROR_SSL_FACTORY = factory; + } - private static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = new AllowAllHostnameVerifier(); + private static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = new AllowAllHostnameVerifier(); - private static final Map instanceMap = new HashMap( - 1); + private static final Map instanceMap = new HashMap( + 1); - public HttpClientImpl() { - super(ConfigurationContext.getInstance()); - }; + public HttpClientImpl() { + super(ConfigurationContext.getInstance()); + } - public HttpClientImpl(final HttpClientConfiguration conf) { - super(conf); - } + public HttpClientImpl(final HttpClientConfiguration conf) { + super(conf); + } - public HttpResponse get(final String url, final String sign_url) throws TwitterException { - return request(new HttpRequest(RequestMethod.GET, url, sign_url, null, null, null)); - } + public HttpResponse get(final String url, final String sign_url) throws TwitterException { + return request(new HttpRequest(RequestMethod.GET, url, sign_url, null, null, null)); + } - public HttpResponse post(final String url, final String sign_url, final HttpParameter[] params) - throws TwitterException { - return request(new HttpRequest(RequestMethod.POST, url, sign_url, params, null, null)); - } + public HttpResponse post(final String url, final String sign_url, final HttpParameter[] params) + throws TwitterException { + return request(new HttpRequest(RequestMethod.POST, url, sign_url, params, null, null)); + } - @Override - public HttpResponse request(final HttpRequest req) throws TwitterException { - int retriedCount; - final int retry = CONF.getHttpRetryCount() + 1; - HttpResponse res = null; - for (retriedCount = 0; retriedCount < retry; retriedCount++) { - int responseCode = -1; - try { - HttpURLConnection con; - OutputStream os = null; - try { - con = getConnection(req.getURL()); - con.setDoInput(true); - setHeaders(req, con); - con.setRequestMethod(req.getMethod().name()); - final HttpParameter[] params = req.getParameters(); - if (req.getMethod() == POST) { - if (HttpParameter.containsFile(params)) { - String boundary = "----Twitter4J-upload" + System.currentTimeMillis(); - con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); - boundary = "--" + boundary; - con.setDoOutput(true); - os = con.getOutputStream(); - final DataOutputStream out = new DataOutputStream(os); - for (final HttpParameter param : params) { - if (param.isFile()) { - write(out, boundary + "\r\n"); - write(out, "Content-Disposition: form-data; name=\"" + param.getName() - + "\"; filename=\"" + param.getFileName() + "\"\r\n"); - write(out, "Content-Type: " + param.getContentType() + "\r\n\r\n"); - final BufferedInputStream in = new BufferedInputStream( - param.hasFileBody() ? param.getFileBody() : new FileInputStream( - param.getFile())); - int buff; - while ((buff = in.read()) != -1) { - out.write(buff); - } - write(out, "\r\n"); - in.close(); - } else { - write(out, boundary + "\r\n"); - write(out, "Content-Disposition: form-data; name=\"" + param.getName() + "\"\r\n"); - write(out, "Content-Type: text/plain; charset=UTF-8\r\n\r\n"); - logger.debug(param.getValue()); - out.write(param.getValue().getBytes("UTF-8")); - write(out, "\r\n"); - } - } - write(out, boundary + "--\r\n"); - write(out, "\r\n"); + @Override + public HttpResponse request(final HttpRequest req) throws TwitterException { + int retriedCount; + final int retry = CONF.getHttpRetryCount() + 1; + HttpResponse res = null; + for (retriedCount = 0; retriedCount < retry; retriedCount++) { + int responseCode = -1; + try { + HttpURLConnection con; + OutputStream os = null; + try { + con = getConnection(req.getURL()); + con.setDoInput(true); + setHeaders(req, con); + con.setRequestMethod(req.getMethod().name()); + final HttpParameter[] params = req.getParameters(); + if (req.getMethod() == POST) { + if (HttpParameter.containsFile(params)) { + String boundary = "----Twitter4J-upload" + System.currentTimeMillis(); + con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + boundary = "--" + boundary; + con.setDoOutput(true); + os = con.getOutputStream(); + final DataOutputStream out = new DataOutputStream(os); + for (final HttpParameter param : params) { + if (param.isFile()) { + write(out, boundary + "\r\n"); + write(out, "Content-Disposition: form-data; name=\"" + param.getName() + + "\"; filename=\"" + param.getFileName() + "\"\r\n"); + write(out, "Content-Type: " + param.getContentType() + "\r\n\r\n"); + final BufferedInputStream in = new BufferedInputStream( + param.hasFileBody() ? param.getFileBody() : new FileInputStream( + param.getFile())); + int buff; + while ((buff = in.read()) != -1) { + out.write(buff); + } + write(out, "\r\n"); + in.close(); + } else { + write(out, boundary + "\r\n"); + write(out, "Content-Disposition: form-data; name=\"" + param.getName() + "\"\r\n"); + write(out, "Content-Type: text/plain; charset=UTF-8\r\n\r\n"); + logger.debug(param.getValue()); + out.write(param.getValue().getBytes("UTF-8")); + write(out, "\r\n"); + } + } + write(out, boundary + "--\r\n"); + write(out, "\r\n"); - } else { - con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); - final String postParam = HttpParameter.encodeParameters(req.getParameters()); - logger.debug("Post Params: ", postParam); - final byte[] bytes = postParam.getBytes("UTF-8"); - con.setRequestProperty("Content-Length", Integer.toString(bytes.length)); - con.setDoOutput(true); - os = con.getOutputStream(); - os.write(bytes); - } - os.flush(); - os.close(); - } - res = new HttpResponseImpl(con, CONF); - responseCode = con.getResponseCode(); - if (logger.isDebugEnabled()) { - logger.debug("Response: "); - final Map> responseHeaders = con.getHeaderFields(); - for (final String key : responseHeaders.keySet()) { - final List values = responseHeaders.get(key); - for (final String value : values) { - if (key != null) { - logger.debug(key + ": " + value); - } else { - logger.debug(value); - } - } - } - } - if (responseCode < OK || responseCode > ACCEPTED) { - if (responseCode == ENHANCE_YOUR_CLAIM || responseCode == BAD_REQUEST - || responseCode < INTERNAL_SERVER_ERROR || retriedCount == CONF.getHttpRetryCount()) - throw new TwitterException(res.asString(), req, res); - } else { - break; - } - } finally { - try { - if (os != null) { - os.close(); - } - } catch (final IOException ignore) { - } - } - } catch (final IOException ioe) { - // connection timeout or read timeout - if (retriedCount == CONF.getHttpRetryCount()) - // throw new TwitterException(ioe.getMessage(), ioe, - // responseCode); - throw new TwitterException(ioe.getMessage(), req, res); - } catch (final NullPointerException e) { - // This exception will be thown when URL is invalid. - e.printStackTrace(); - throw new TwitterException("The URL requested is invalid.", e); - } catch (final OutOfMemoryError e) { - throw new TwitterException(e.getMessage(), e); - } - try { - if (logger.isDebugEnabled() && res != null) { - res.asString(); - } - logger.debug("Sleeping " + CONF.getHttpRetryIntervalSeconds() + " seconds until the next retry."); - Thread.sleep(CONF.getHttpRetryIntervalSeconds() * 1000); - } catch (final InterruptedException ignore) { - // nothing to do - } - } - return res; - } + } else { + con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); + final String postParam = HttpParameter.encodeParameters(req.getParameters()); + logger.debug("Post Params: ", postParam); + final byte[] bytes = postParam.getBytes("UTF-8"); + con.setRequestProperty("Content-Length", Integer.toString(bytes.length)); + con.setDoOutput(true); + os = con.getOutputStream(); + os.write(bytes); + } + os.flush(); + os.close(); + } + res = new HttpResponseImpl(con, CONF); + responseCode = con.getResponseCode(); + if (logger.isDebugEnabled()) { + logger.debug("Response: "); + final Map> responseHeaders = con.getHeaderFields(); + for (final String key : responseHeaders.keySet()) { + final List values = responseHeaders.get(key); + for (final String value : values) { + if (key != null) { + logger.debug(key + ": " + value); + } else { + logger.debug(value); + } + } + } + } + if (responseCode < OK || responseCode > ACCEPTED) { + if (responseCode == ENHANCE_YOUR_CLAIM || responseCode == BAD_REQUEST + || responseCode < INTERNAL_SERVER_ERROR || retriedCount == CONF.getHttpRetryCount()) + throw new TwitterException(res.asString(), req, res); + } else { + break; + } + } finally { + try { + if (os != null) { + os.close(); + } + } catch (final IOException ignore) { + } + } + } catch (final IOException ioe) { + // connection timeout or read timeout + if (retriedCount == CONF.getHttpRetryCount()) + // throw new TwitterException(ioe.getMessage(), ioe, + // responseCode); + throw new TwitterException(ioe.getMessage(), req, res); + } catch (final NullPointerException e) { + // This exception will be thown when URL is invalid. + e.printStackTrace(); + throw new TwitterException("The URL requested is invalid.", e); + } catch (final OutOfMemoryError e) { + throw new TwitterException(e.getMessage(), e); + } + try { + if (logger.isDebugEnabled() && res != null) { + res.asString(); + } + logger.debug("Sleeping " + CONF.getHttpRetryIntervalSeconds() + " seconds until the next retry."); + Thread.sleep(CONF.getHttpRetryIntervalSeconds() * 1000); + } catch (final InterruptedException ignore) { + // nothing to do + } + } + return res; + } - private HttpURLConnection getConnection(final String url_string) throws IOException { + private HttpURLConnection getConnection(final String url_string) throws IOException { - final HttpURLConnection con; - final Proxy proxy; - if (isProxyConfigured()) { - if (CONF.getHttpProxyUser() != null && !CONF.getHttpProxyUser().equals("")) { - if (logger.isDebugEnabled()) { - logger.debug("Proxy AuthUser: " + CONF.getHttpProxyUser()); - logger.debug("Proxy AuthPassword: " + InternalStringUtil.maskString(CONF.getHttpProxyPassword())); - } - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - // respond only to proxy auth requests - if (getRequestorType().equals(RequestorType.PROXY)) - return new PasswordAuthentication(CONF.getHttpProxyUser(), CONF.getHttpProxyPassword() - .toCharArray()); - else - return null; - } - }); - } - proxy = new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(CONF.getHttpProxyHost(), - CONF.getHttpProxyPort())); - if (logger.isDebugEnabled()) { - logger.debug("Opening proxied connection(" + CONF.getHttpProxyHost() + ":" + CONF.getHttpProxyPort() - + ")"); - } - } else { - proxy = Proxy.NO_PROXY; - } - final HostAddressResolver resolver = FactoryUtils.getHostAddressResolver(CONF); - final URI url_orig; - try { - url_orig = new URI(url_string); - } catch (final URISyntaxException e) { - throw new IOException("Invalid URI " + url_string); - } - final String host = url_orig.getHost(), authority = url_orig.getAuthority(); - final String resolved_host = resolver != null ? resolver.resolve(host) : null; - con = (HttpURLConnection) new URL(resolved_host != null ? url_string.replace("://" + host, "://" - + resolved_host) : url_string).openConnection(proxy); - if (resolved_host != null && !host.equals(resolved_host)) { - con.setRequestProperty("Host", authority); - } - if (CONF.getHttpConnectionTimeout() > 0) { - con.setConnectTimeout(CONF.getHttpConnectionTimeout()); - } - if (CONF.getHttpReadTimeout() > 0) { - con.setReadTimeout(CONF.getHttpReadTimeout()); - } - con.setInstanceFollowRedirects(false); - if (con instanceof HttpsURLConnection && CONF.isSSLErrorIgnored()) { - ((HttpsURLConnection) con).setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER); - if (IGNORE_ERROR_SSL_FACTORY != null) { - ((HttpsURLConnection) con).setSSLSocketFactory(IGNORE_ERROR_SSL_FACTORY); - } - } - return con; - } + final HttpURLConnection con; + final Proxy proxy; + if (isProxyConfigured()) { + if (CONF.getHttpProxyUser() != null && !CONF.getHttpProxyUser().equals("")) { + if (logger.isDebugEnabled()) { + logger.debug("Proxy AuthUser: " + CONF.getHttpProxyUser()); + logger.debug("Proxy AuthPassword: " + InternalStringUtil.maskString(CONF.getHttpProxyPassword())); + } + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + // respond only to proxy auth requests + if (getRequestorType().equals(RequestorType.PROXY)) + return new PasswordAuthentication(CONF.getHttpProxyUser(), CONF.getHttpProxyPassword() + .toCharArray()); + else + return null; + } + }); + } + proxy = new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(CONF.getHttpProxyHost(), + CONF.getHttpProxyPort())); + if (logger.isDebugEnabled()) { + logger.debug("Opening proxied connection(" + CONF.getHttpProxyHost() + ":" + CONF.getHttpProxyPort() + + ")"); + } + } else { + proxy = Proxy.NO_PROXY; + } + final HostAddressResolver resolver = FactoryUtils.getHostAddressResolver(CONF); + final URI url_orig; + try { + url_orig = new URI(url_string); + } catch (final URISyntaxException e) { + throw new IOException("Invalid URI " + url_string); + } + final String host = url_orig.getHost(), authority = url_orig.getAuthority(); + final String resolved_host = resolver != null ? resolver.resolve(host) : null; + con = (HttpURLConnection) new URL(resolved_host != null ? url_string.replace("://" + host, "://" + + resolved_host) : url_string).openConnection(proxy); + if (resolved_host != null && !host.equals(resolved_host)) { + con.setRequestProperty("Host", authority); + } + if (CONF.getHttpConnectionTimeout() > 0) { + con.setConnectTimeout(CONF.getHttpConnectionTimeout()); + } + if (CONF.getHttpReadTimeout() > 0) { + con.setReadTimeout(CONF.getHttpReadTimeout()); + } + con.setInstanceFollowRedirects(false); + if (con instanceof HttpsURLConnection && CONF.isSSLErrorIgnored()) { + ((HttpsURLConnection) con).setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER); + if (IGNORE_ERROR_SSL_FACTORY != null) { + ((HttpsURLConnection) con).setSSLSocketFactory(IGNORE_ERROR_SSL_FACTORY); + } + } + return con; + } - /** - * sets HTTP headers - * - * @param req The request - * @param connection HttpURLConnection - */ - private void setHeaders(final HttpRequest req, final HttpURLConnection connection) { - if (logger.isDebugEnabled()) { - logger.debug("Request: "); - logger.debug(req.getMethod().name() + " ", req.getURL()); - } + /** + * sets HTTP headers + * + * @param req The request + * @param connection HttpURLConnection + */ + private void setHeaders(final HttpRequest req, final HttpURLConnection connection) { + if (logger.isDebugEnabled()) { + logger.debug("Request: "); + logger.debug(req.getMethod().name() + " ", req.getURL()); + } - String authorizationHeader; - if (req.getAuthorization() != null - && (authorizationHeader = req.getAuthorization().getAuthorizationHeader(req)) != null) { - if (logger.isDebugEnabled()) { - logger.debug("Authorization: ", InternalStringUtil.maskString(authorizationHeader)); - } - connection.addRequestProperty("Authorization", authorizationHeader); - } - final Map req_headers = req.getRequestHeaders(); - if (req_headers != null) { - for (final String key : req_headers.keySet()) { - connection.addRequestProperty(key, req.getRequestHeaders().get(key)); - logger.debug(key + ": " + req.getRequestHeaders().get(key)); - } - } - } + String authorizationHeader; + if (req.getAuthorization() != null + && (authorizationHeader = req.getAuthorization().getAuthorizationHeader(req)) != null) { + if (logger.isDebugEnabled()) { + logger.debug("Authorization: ", InternalStringUtil.maskString(authorizationHeader)); + } + connection.addRequestProperty("Authorization", authorizationHeader); + } + final Map req_headers = req.getRequestHeaders(); + if (req_headers != null) { + for (final String key : req_headers.keySet()) { + connection.addRequestProperty(key, req.getRequestHeaders().get(key)); + logger.debug(key + ": " + req.getRequestHeaders().get(key)); + } + } + } - public static String encode(final String str) { - try { - return URLEncoder.encode(str, "UTF-8"); - } catch (final java.io.UnsupportedEncodingException neverHappen) { - throw new AssertionError("will never happen"); - } - } + public static String encode(final String str) { + try { + return URLEncoder.encode(str, "UTF-8"); + } catch (final java.io.UnsupportedEncodingException neverHappen) { + throw new AssertionError("will never happen"); + } + } - public static HttpClient getInstance(final HttpClientConfiguration conf) { - HttpClient client = instanceMap.get(conf); - if (null == client) { - client = new HttpClientImpl(conf); - instanceMap.put(conf, client); - } - return client; - } + public static HttpClient getInstance(final HttpClientConfiguration conf) { + HttpClient client = instanceMap.get(conf); + if (null == client) { + client = new HttpClientImpl(conf); + instanceMap.put(conf, client); + } + return client; + } - static class AllowAllHostnameVerifier implements HostnameVerifier { - @Override - public boolean verify(final String hostname, final SSLSession session) { - return true; - } - } + static class AllowAllHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(final String hostname, final SSLSession session) { + return true; + } + } - final static class TrustAllX509TrustManager implements X509TrustManager { - @Override - public void checkClientTrusted(final X509Certificate[] chain, final String authType) { - } + final static class TrustAllX509TrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) { + } - @Override - public void checkServerTrusted(final X509Certificate[] chain, final String authType) { - } + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) { + } - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[] {}; - } - } + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } } diff --git a/twidere/build.gradle b/twidere/build.gradle index b4472f808..18968b38a 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -1,6 +1,7 @@ import java.text.SimpleDateFormat apply plugin: 'com.android.application' +apply plugin: 'aar-link-sources' apply from: rootProject.file('signing.gradle') android { @@ -62,18 +63,18 @@ dependencies { compile 'com.android.support:palette-v7:21.0.3' compile 'com.sothree.slidinguppanel:library:2.0.4' compile 'com.twitter:twitter-text:1.9.9' + aarLinkSources 'com.twitter:twitter-text:1.9.9:sources@jar' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3' - compile 'org.apache.httpcomponents:httpclient-android:4.3.5' - compile 'org.apache.httpcomponents:httpmime:4.3.5' compile 'org.apache.commons:commons-csv:1.1' compile 'com.google.android.apps.dashclock:dashclock-api:2.0.0' - compile 'com.squareup:otto:1.3.5' + compile 'com.squareup:otto:1.3.6' compile 'dnsjava:dnsjava:2.1.6' compile 'com.commonsware.cwac:merge:1.1.1' compile 'com.diegocarloslima:byakugallery:0.1.0' compile 'com.rengwuxian.materialedittext:library:1.8.2' compile 'com.pnikosis:materialish-progress:1.4' compile 'com.squareup.okhttp:okhttp:2.2.0' + aarLinkSources 'com.squareup.okhttp:okhttp:2.2.0:sources@jar' googleCompile 'com.google.android.gms:play-services:6.5.87' googleCompile 'com.google.maps.android:android-maps-utils:0.3.4' fdroidCompile 'org.osmdroid:osmdroid-android:4.3' diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml index 568e04d6b..467e7f4ad 100644 --- a/twidere/src/main/AndroidManifest.xml +++ b/twidere/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + @@ -569,9 +570,8 @@ diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BrowserSignInActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BrowserSignInActivity.java index 97032846b..8a1c4e72a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BrowserSignInActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BrowserSignInActivity.java @@ -27,6 +27,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; import android.os.Bundle; +import android.support.annotation.NonNull; import android.view.MenuItem; import android.view.View; import android.view.Window; @@ -45,8 +46,8 @@ import org.mariotaku.twidere.util.OAuthPasswordAuthenticator; import org.mariotaku.twidere.util.ParseUtils; import org.mariotaku.twidere.util.TwitterContentUtils; import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.util.net.ApacheHttpClientFactory; import org.mariotaku.twidere.util.net.TwidereHostResolverFactory; +import org.mariotaku.twidere.util.net.OkHttpClientFactory; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -134,9 +135,7 @@ public class BrowserSignInActivity extends BaseSupportDialogActivity implements private String readOAuthPin(final String html) { try { return OAuthPasswordAuthenticator.readOAuthPINFromHtml(new StringReader(html)); - } catch (final XmlPullParserException e) { - e.printStackTrace(); - } catch (final IOException e) { + } catch (final XmlPullParserException | IOException e) { e.printStackTrace(); } return null; @@ -179,7 +178,7 @@ public class BrowserSignInActivity extends BaseSupportDialogActivity implements } @Override - public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) { + public void onReceivedSslError(final WebView view, @NonNull final SslErrorHandler handler, final SslError error) { if (mActivity.mPreferences.getBoolean(KEY_IGNORE_SSL_ERROR, false)) { handler.proceed(); } else { @@ -234,7 +233,7 @@ public class BrowserSignInActivity extends BaseSupportDialogActivity implements final String consumerSecret = getNonEmptyString(mPreferences, KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET_3); cb.setHostAddressResolverFactory(new TwidereHostResolverFactory(mApplication)); - cb.setHttpClientFactory(new ApacheHttpClientFactory(mApplication)); + cb.setHttpClientFactory(new OkHttpClientFactory(mApplication)); if (TwitterContentUtils.isOfficialKey(mActivity, consumerKey, consumerSecret)) { Utils.setMockOfficialUserAgent(mActivity, cb); } else { @@ -301,13 +300,13 @@ public class BrowserSignInActivity extends BaseSupportDialogActivity implements @JavascriptInterface public void processHTML(final String html) { - final String oauth_verifier = mActivity.readOAuthPin(html); - final RequestToken request_token = mActivity.mRequestToken; - if (oauth_verifier != null && request_token != null) { + final String oauthVerifier = mActivity.readOAuthPin(html); + final RequestToken requestToken = mActivity.mRequestToken; + if (oauthVerifier != null && requestToken != null) { final Intent intent = new Intent(); - intent.putExtra(EXTRA_OAUTH_VERIFIER, oauth_verifier); - intent.putExtra(EXTRA_REQUEST_TOKEN, request_token.getToken()); - intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, request_token.getTokenSecret()); + intent.putExtra(EXTRA_OAUTH_VERIFIER, oauthVerifier); + intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.getToken()); + intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.getTokenSecret()); mActivity.setResult(RESULT_OK, intent); mActivity.finish(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java index 60f6b11e7..914c3e055 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ComposeActivity.java @@ -62,7 +62,6 @@ import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ItemDecoration; import android.support.v7.widget.RecyclerView.State; import android.support.v7.widget.RecyclerView.ViewHolder; -import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -195,7 +194,6 @@ public class ComposeActivity extends ThemedActionBarActivity implements TextWatc private View mLocationContainer; private ActionIconView mLocationIcon; private SupportMenuInflater mMenuInflater; - private Toolbar mToolbar; @Override public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { @@ -536,7 +534,6 @@ public class ComposeActivity extends ThemedActionBarActivity implements TextWatc @Override public void onSupportContentChanged() { super.onSupportContentChanged(); - mToolbar = (Toolbar) findViewById(R.id.compose_actionbar); mEditText = (EditText) findViewById(R.id.edit_text); mMediaPreviewGrid = (GridView) findViewById(R.id.media_thumbnail_preview); mMenuBar = (ActionMenuView) findViewById(R.id.menu_bar); @@ -587,7 +584,6 @@ public class ComposeActivity extends ThemedActionBarActivity implements TextWatc mValidator = new TwidereValidator(this); mImageLoader = app.getImageLoaderWrapper(); setContentView(R.layout.activity_compose); - setSupportActionBar(mToolbar); setSupportProgressBarIndeterminateVisibility(false); setFinishOnTouchOutside(false); final long[] defaultAccountIds = getAccountIds(this); diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java index ae75aa96e..3e08f320b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/SignInActivity.java @@ -61,8 +61,8 @@ import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.TwitterContentUtils; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.accessor.ViewAccessor; -import org.mariotaku.twidere.util.net.ApacheHttpClientFactory; import org.mariotaku.twidere.util.net.TwidereHostResolverFactory; +import org.mariotaku.twidere.util.net.OkHttpClientFactory; import twitter4j.Twitter; import twitter4j.TwitterConstants; @@ -355,7 +355,7 @@ public class SignInActivity extends BaseSupportActivity implements TwitterConsta final boolean ignore_ssl_error = mPreferences.getBoolean(KEY_IGNORE_SSL_ERROR, false); final boolean enable_proxy = mPreferences.getBoolean(KEY_ENABLE_PROXY, false); cb.setHostAddressResolverFactory(new TwidereHostResolverFactory(mApplication)); - cb.setHttpClientFactory(new ApacheHttpClientFactory(mApplication)); + cb.setHttpClientFactory(new OkHttpClientFactory(mApplication)); if (TwitterContentUtils.isOfficialKey(this, mConsumerKey, mConsumerSecret)) { Utils.setMockOfficialUserAgent(this, cb); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 393e1b121..9c9a203dc 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -202,9 +202,8 @@ import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts; import org.mariotaku.twidere.service.RefreshService; import org.mariotaku.twidere.util.content.ContentResolverUtils; import org.mariotaku.twidere.util.menu.TwidereMenuInfo; -import org.mariotaku.twidere.util.net.ApacheHttpClientFactory; import org.mariotaku.twidere.util.net.TwidereHostResolverFactory; -import org.mariotaku.twidere.util.net.ssl.OkHttpClientFactory; +import org.mariotaku.twidere.util.net.OkHttpClientFactory; import org.mariotaku.twidere.view.ShapedImageView; import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle; @@ -1801,8 +1800,7 @@ public final class Utils implements Constants, TwitterConstants { if (userAgent != null) { cb.setHttpUserAgent(userAgent); } - cb.setHttpClientFactory(new ApacheHttpClientFactory(context)); -// cb.setHttpClientFactory(new OkHttpClientFactory()); + cb.setHttpClientFactory(new OkHttpClientFactory(context)); return new HttpClientWrapper(cb.build()); } @@ -2527,8 +2525,7 @@ public final class Utils implements Constants, TwitterConstants { final ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setHostAddressResolverFactory(new TwidereHostResolverFactory(app)); if (apacheHttp) { - cb.setHttpClientFactory(new ApacheHttpClientFactory(app)); -// cb.setHttpClientFactory(new OkHttpClientFactory()); + cb.setHttpClientFactory(new OkHttpClientFactory(context)); } cb.setHttpConnectionTimeout(connection_timeout); cb.setGZIPEnabled(enableGzip); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientFactory.java deleted file mode 100644 index 32c046c16..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.mariotaku.twidere.util.net; - -import android.content.Context; - -import twitter4j.http.HttpClient; -import twitter4j.http.HttpClientConfiguration; -import twitter4j.http.HttpClientFactory; - -public class ApacheHttpClientFactory implements HttpClientFactory { - - private final Context context; - - public ApacheHttpClientFactory(final Context context) { - this.context = context; - } - - @Override - public HttpClient getInstance(final HttpClientConfiguration conf) { - return new ApacheHttpClientImpl(context, conf); - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientHttpResponseImpl.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientHttpResponseImpl.java deleted file mode 100644 index a85cd9b2d..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientHttpResponseImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2007 Yusuke Yamamoto - * - * 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.mariotaku.twidere.util.net; - -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.HttpResponse; - -import twitter4j.http.HttpClientConfiguration; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; - -/** - * @author Yusuke Yamamoto - yusuke at mac.com - * @since Twitter4J 2.1.2 - */ -final class ApacheHttpClientHttpResponseImpl extends twitter4j.http.HttpResponse { - private final HttpResponse res; - - ApacheHttpClientHttpResponseImpl(final HttpResponse res, final HttpClientConfiguration conf) throws IOException { - super(conf); - this.res = res; - is = res.getEntity().getContent(); - statusCode = res.getStatusLine().getStatusCode(); - if (is != null && "gzip".equals(getResponseHeader("Content-Encoding"))) { - // the response is gzipped - is = new GZIPInputStream(is); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void disconnect() throws IOException { - if (res != null) { - res.getEntity().consumeContent(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public final String getResponseHeader(final String name) { - final Header[] headers = res.getHeaders(name); - if (headers != null && headers.length > 0) - return headers[0].getValue(); - else - return null; - } - - @Override - public Map> getResponseHeaderFields() { - final Header[] headers = res.getAllHeaders(); - final Map> maps = new HashMap>(); - for (final Header header : headers) { - final HeaderElement[] elements = header.getElements(); - final List values = new ArrayList(1); - for (final HeaderElement element : elements) { - values.add(element.getValue()); - } - maps.put(header.getName(), values); - } - return maps; - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientImpl.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientImpl.java deleted file mode 100644 index a2af8dd59..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ApacheHttpClientImpl.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2007 Yusuke Yamamoto - * - * 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.mariotaku.twidere.util.net; - -import android.content.Context; - -import org.apache.http.Consts; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpDeleteHC4; -import org.apache.http.client.methods.HttpGetHC4; -import org.apache.http.client.methods.HttpHeadHC4; -import org.apache.http.client.methods.HttpPostHC4; -import org.apache.http.client.methods.HttpPutHC4; -import org.apache.http.client.methods.HttpRequestBaseHC4; -import org.apache.http.client.params.HttpClientParams; -import org.apache.http.conn.socket.LayeredConnectionSocketFactory; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.ContentBody; -import org.apache.http.entity.mime.content.FileBody; -import org.apache.http.entity.mime.content.InputStreamBody; -import org.apache.http.entity.mime.content.StringBody; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContextHC4; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.TextUtils; -import org.mariotaku.twidere.util.ParseUtils; -import org.mariotaku.twidere.util.Utils; -import org.mariotaku.twidere.util.net.ssl.HostResolvedSSLConnectionSocketFactory; -import org.mariotaku.twidere.util.net.ssl.TwidereSSLSocketFactory; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.util.Map; - -import twitter4j.TwitterException; -import twitter4j.auth.Authorization; -import twitter4j.http.FactoryUtils; -import twitter4j.http.HostAddressResolver; -import twitter4j.http.HttpClientConfiguration; -import twitter4j.http.HttpParameter; -import twitter4j.http.HttpResponseCode; -import twitter4j.http.RequestMethod; -import twitter4j.internal.logging.Logger; -import twitter4j.internal.util.InternalStringUtil; - -import static android.text.TextUtils.isEmpty; - -/** - * HttpClient implementation for Apache HttpClient 4.0.x - * - * @author Yusuke Yamamoto - yusuke at mac.com - * @since Twitter4J 2.1.2 - */ -public class ApacheHttpClientImpl implements twitter4j.http.HttpClient, HttpResponseCode { - private static final Logger logger = Logger.getLogger(ApacheHttpClientImpl.class); - private final HttpClientConfiguration conf; - private final CloseableHttpClient client; - - public ApacheHttpClientImpl(final Context context, final HttpClientConfiguration conf) { - this.conf = conf; - final HttpClientBuilder clientBuilder = HttpClients.custom(); - final LayeredConnectionSocketFactory factory = TwidereSSLSocketFactory.getSocketFactory(context, - conf.isSSLErrorIgnored()); - clientBuilder.setSSLSocketFactory(factory); - final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); - requestConfigBuilder.setConnectionRequestTimeout(conf.getHttpConnectionTimeout()); - requestConfigBuilder.setConnectTimeout(conf.getHttpConnectionTimeout()); - requestConfigBuilder.setSocketTimeout(conf.getHttpReadTimeout()); - requestConfigBuilder.setRedirectsEnabled(false); - clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build()); - if (conf.isProxyConfigured()) { - final HttpHost proxy = new HttpHost(conf.getHttpProxyHost(), conf.getHttpProxyPort()); - clientBuilder.setProxy(proxy); - if (!TextUtils.isEmpty(conf.getHttpProxyUser())) { - if (logger.isDebugEnabled()) { - logger.debug("Proxy AuthUser: " + conf.getHttpProxyUser()); - logger.debug("Proxy AuthPassword: " + InternalStringUtil.maskString(conf.getHttpProxyPassword())); - } - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(new AuthScope(conf.getHttpProxyHost(), conf.getHttpProxyPort()), - new UsernamePasswordCredentials(conf.getHttpProxyUser(), conf.getHttpProxyPassword())); - clientBuilder.setDefaultCredentialsProvider(credentialsProvider); - } - } - client = clientBuilder.build(); - } - - @Override - public twitter4j.http.HttpResponse request(final twitter4j.http.HttpRequest req) throws TwitterException { - final HostAddressResolver resolver = FactoryUtils.getHostAddressResolver(conf); - final String urlString = req.getURL(); - final URI urlOrig = ParseUtils.parseURI(urlString); - final String host = urlOrig.getHost(), authority = urlOrig.getAuthority(); - try { - HttpRequestBaseHC4 commonsRequest; - final String resolvedHost = resolver != null ? resolver.resolve(host) : null; - final String resolvedUrl = !isEmpty(resolvedHost) ? urlString.replace("://" + host, "://" + resolvedHost) - : urlString; - final RequestMethod method = req.getMethod(); - switch (method) { - case GET: - commonsRequest = new HttpGetHC4(resolvedUrl); - break; - case POST: - final HttpPostHC4 post = new HttpPostHC4(resolvedUrl); - post.setEntity(getAsEntity(req.getParameters())); - post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); - commonsRequest = post; - break; - case DELETE: - commonsRequest = new HttpDeleteHC4(resolvedUrl); - break; - case HEAD: - commonsRequest = new HttpHeadHC4(resolvedUrl); - break; - case PUT: - final HttpPutHC4 put = new HttpPutHC4(resolvedUrl); - put.setEntity(getAsEntity(req.getParameters())); - commonsRequest = put; - break; - default: - throw new TwitterException("Unsupported request method " + method); - } - final HttpParams httpParams = commonsRequest.getParams(); - HttpClientParams.setRedirecting(httpParams, false); - final Map headers = req.getRequestHeaders(); - for (final String headerName : headers.keySet()) { - commonsRequest.addHeader(headerName, headers.get(headerName)); - } - final Authorization authorization = req.getAuthorization(); - final String authorizationHeader = authorization != null ? authorization.getAuthorizationHeader(req) : null; - if (authorizationHeader != null) { - commonsRequest.addHeader(HttpHeaders.AUTHORIZATION, authorizationHeader); - } - if (resolvedHost != null && !resolvedHost.isEmpty() && !resolvedHost.equals(host)) { - commonsRequest.addHeader(HttpHeaders.HOST, authority); - } - - final ApacheHttpClientHttpResponseImpl res; - try { - final HttpContext httpContext = new BasicHttpContextHC4(); - httpContext.setAttribute(HostResolvedSSLConnectionSocketFactory.HTTP_CONTEXT_KEY_ORIGINAL_HOST, host); - res = new ApacheHttpClientHttpResponseImpl(client.execute(commonsRequest, httpContext), conf); - } catch (final IllegalStateException e) { - throw new TwitterException("Please check your API settings.", e); - } catch (final NullPointerException e) { - // Bug http://code.google.com/p/android/issues/detail?id=5255 - throw new TwitterException("Please check your APN settings, make sure not to use WAP APNs.", e); - } catch (final OutOfMemoryError e) { - // I don't know why OOM thown, but it should be catched. - System.gc(); - throw new TwitterException("Unknown error", e); - } - final int statusCode = res.getStatusCode(); - if (statusCode < OK || statusCode > ACCEPTED) - throw new TwitterException(res.asString(), req, res); - return res; - } catch (final IOException e) { - // TODO - if (resolver instanceof TwidereHostAddressResolver) { - final TwidereHostAddressResolver twidereResolver = (TwidereHostAddressResolver) resolver; - twidereResolver.removeCachedHost(host); - } - throw new TwitterException(e); - } - } - - @Override - public void shutdown() { - Utils.closeSilently(client); - } - - private static HttpEntity getAsEntity(final HttpParameter[] params) throws UnsupportedEncodingException { - if (params == null) return null; - if (!HttpParameter.containsFile(params)) return new HttpParameterFormEntity(params); - final MultipartEntityBuilder me = MultipartEntityBuilder.create(); - for (final HttpParameter param : params) { - if (param.isFile()) { - final ContentType contentType = ContentType.create(param.getContentType()); - final ContentBody body; - if (param.getFile() != null) { - body = new FileBody(param.getFile(), ContentType.create(param.getContentType())); - } else { - body = new InputStreamBody(param.getFileBody(), contentType, param.getFileName()); - } - me.addPart(param.getName(), body); - } else { - final ContentType contentType = ContentType.TEXT_PLAIN.withCharset(Consts.UTF_8); - final ContentBody body = new StringBody(param.getValue(), contentType); - me.addPart(param.getName(), body); - } - } - return me.build(); - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedHostnameVerifier.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedHostnameVerifier.java new file mode 100644 index 000000000..7655cb84d --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedHostnameVerifier.java @@ -0,0 +1,34 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util.net; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +public final class HostResolvedHostnameVerifier implements HostnameVerifier { + public HostResolvedHostnameVerifier(boolean ignoreSSLError) { + + } + + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } +} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSSLSocketFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSSLSocketFactory.java new file mode 100644 index 000000000..6adb34234 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSSLSocketFactory.java @@ -0,0 +1,94 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util.net; + +import android.net.SSLCertificateSocketFactory; + +import org.apache.http.conn.util.InetAddressUtils; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.ssl.SSLSocketFactory; + +import twitter4j.http.HostAddressResolver; + +/** + * Created by mariotaku on 15/1/31. + */ +public class HostResolvedSSLSocketFactory extends SSLSocketFactory { + + private final SSLSocketFactory defaultFactory; + private final HostAddressResolver resolver; + + public HostResolvedSSLSocketFactory(HostAddressResolver resolver, boolean ignoreError) { + if (ignoreError) { + defaultFactory = SSLCertificateSocketFactory.getInsecure(0, null); + } else { + defaultFactory = SSLCertificateSocketFactory.getDefault(0, null); + } + this.resolver = resolver; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return defaultFactory.createSocket(host, port); + } + + @Override + public Socket createSocket() throws IOException { + return defaultFactory.createSocket(); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + return defaultFactory.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return defaultFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return defaultFactory.createSocket(address, port, localAddress, localPort); + } + + + @Override + public String[] getDefaultCipherSuites() { + return defaultFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return defaultFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return defaultFactory.createSocket(s, host, port, autoClose); + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSocketFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSocketFactory.java new file mode 100644 index 000000000..be485a1e9 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/HostResolvedSocketFactory.java @@ -0,0 +1,118 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util.net; + +import org.apache.http.conn.util.InetAddressUtils; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import twitter4j.http.HostAddressResolver; + +/** + * Created by mariotaku on 15/1/31. + */ +public class HostResolvedSocketFactory extends SocketFactory { + + private final SocketFactory defaultFactory; + private final HostAddressResolver resolver; + + public HostResolvedSocketFactory(HostAddressResolver resolver) { + defaultFactory = SocketFactory.getDefault(); + this.resolver = resolver; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + final String resolvedHost = resolver.resolve(host); + if (resolvedHost != null && !resolvedHost.equals(host)) { + if (InetAddressUtils.isIPv6Address(resolvedHost)) { + final byte[] resolvedAddress = Inet6Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(host, resolvedAddress), port); + } else if (InetAddressUtils.isIPv4Address(resolvedHost)) { + final byte[] resolvedAddress = Inet4Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(host, resolvedAddress), port); + } + } + return defaultFactory.createSocket(host, port); + } + + @Override + public Socket createSocket() throws IOException { + return new Socket(); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + final String resolvedHost = resolver.resolve(host); + if (resolvedHost != null && !resolvedHost.equals(host)) { + if (InetAddressUtils.isIPv6Address(resolvedHost)) { + final byte[] resolvedAddress = Inet6Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(host, resolvedAddress), port); + } else if (InetAddressUtils.isIPv4Address(resolvedHost)) { + final byte[] resolvedAddress = Inet4Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(host, resolvedAddress), port); + } + } + return defaultFactory.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + final String hostName = host.getHostName(); + final String resolvedHost = resolver.resolve(hostName); + if (resolvedHost != null && !resolvedHost.equals(hostName)) { + if (InetAddressUtils.isIPv6Address(resolvedHost)) { + final byte[] resolvedAddress = Inet6Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(hostName, resolvedAddress), port); + } else if (InetAddressUtils.isIPv4Address(resolvedHost)) { + final byte[] resolvedAddress = Inet4Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(hostName, resolvedAddress), port); + } + } + return defaultFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + final String hostName = address.getHostName(); + final String resolvedHost = resolver.resolve(hostName); + if (resolvedHost != null && !resolvedHost.equals(hostName)) { + if (InetAddressUtils.isIPv6Address(resolvedHost)) { + final byte[] resolvedAddress = Inet6Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(hostName, resolvedAddress), port, localAddress, localPort); + } else if (InetAddressUtils.isIPv4Address(resolvedHost)) { + final byte[] resolvedAddress = Inet4Address.getByName(resolvedHost).getAddress(); + return new Socket(InetAddress.getByAddress(hostName, resolvedAddress), port, localAddress, localPort); + } + } + return defaultFactory.createSocket(address, port, localAddress, localPort); + } + + protected HostAddressResolver getResolver() { + return resolver; + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/HttpParameterFormEntity.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/HttpParameterFormEntity.java deleted file mode 100644 index 11de4ea28..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/HttpParameterFormEntity.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.mariotaku.twidere.util.net; - -import org.apache.http.Consts; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntityHC4; - -import twitter4j.http.HttpParameter; - -import java.io.UnsupportedEncodingException; - -public class HttpParameterFormEntity extends StringEntityHC4 { - - public static final ContentType CONTENT_TYPE = ContentType.APPLICATION_FORM_URLENCODED.withCharset(Consts.UTF_8); - - public HttpParameterFormEntity(final HttpParameter[] params) throws UnsupportedEncodingException { - super(HttpParameter.encodeParameters(params), CONTENT_TYPE); - } - -} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/OkHttpClientFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientFactory.java similarity index 86% rename from twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/OkHttpClientFactory.java rename to twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientFactory.java index 91b09e98d..bff9a0ded 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/OkHttpClientFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientFactory.java @@ -17,8 +17,11 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.util.net.ssl; +package org.mariotaku.twidere.util.net; +import android.content.Context; + +import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.util.net.OkHttpClientImpl; import twitter4j.http.HttpClient; @@ -29,6 +32,10 @@ import twitter4j.http.HttpClientFactory; * Created by mariotaku on 15/1/22. */ public class OkHttpClientFactory implements HttpClientFactory { + public OkHttpClientFactory(Context context) { + + } + @Override public HttpClient getInstance(HttpClientConfiguration conf) { return new OkHttpClientImpl(conf); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientImpl.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientImpl.java index bc32aa115..37a6aad8d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientImpl.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/OkHttpClientImpl.java @@ -20,55 +20,59 @@ package org.mariotaku.twidere.util.net; import android.net.Uri; -import android.util.Log; +import com.squareup.okhttp.Authenticator; import com.squareup.okhttp.Headers; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Protocol; +import com.squareup.okhttp.Request; import com.squareup.okhttp.Request.Builder; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import org.mariotaku.twidere.TwidereConstants; +import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Proxy.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.UUID; import java.util.zip.GZIPInputStream; import twitter4j.TwitterException; import twitter4j.auth.Authorization; +import twitter4j.http.HostAddressResolver; import twitter4j.http.HttpClient; import twitter4j.http.HttpClientConfiguration; import twitter4j.http.HttpParameter; import twitter4j.http.HttpRequest; import twitter4j.http.HttpResponse; +import twitter4j.http.RequestMethod; /** * Created by mariotaku on 15/1/22. */ public class OkHttpClientImpl implements HttpClient, TwidereConstants { + public static final MediaType APPLICATION_FORM_URLENCODED = MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8"); private final HttpClientConfiguration conf; private final OkHttpClient client; + private final HostAddressResolver resolver; public OkHttpClientImpl(HttpClientConfiguration conf) { this.conf = conf; + this.resolver = conf.getHostAddressResolverFactory().getInstance(conf); this.client = createHttpClient(conf); } - private OkHttpClient createHttpClient(HttpClientConfiguration conf) { - final OkHttpClient client = new OkHttpClient(); - if (conf.isSSLErrorIgnored()) { - } - return client; - } - @Override public HttpResponse request(HttpRequest req) throws TwitterException { final Builder builder = new Builder(); @@ -82,59 +86,34 @@ public class OkHttpClientImpl implements HttpClient, TwidereConstants { builder.header("Authorization", authHeader); } } - final String url; try { - switch (req.getMethod()) { - case GET: { - url = getUrl(req); - builder.get(); - break; - } - case POST: { - url = req.getURL(); - builder.post(getRequestBody(req.getParameters())); - break; - } - case DELETE: { - url = getUrl(req); - builder.delete(); - break; - } - case HEAD: { - url = getUrl(req); - builder.head(); - break; - } - case PUT: { - url = req.getURL(); - builder.put(getRequestBody(req.getParameters())); - break; - } - default: { - throw new AssertionError(); - } - } - builder.url(url); + setupRequestBuilder(builder, req); final Response response = client.newCall(builder.build()).execute(); - Log.d(TwidereConstants.LOGTAG, String.format("OkHttpClient finished a request to %s with %s protocol", url, response.protocol().name())); return new OkHttpResponse(conf, null, response); } catch (IOException e) { throw new TwitterException(e); } } - private String getUrl(HttpRequest req) { - final Uri.Builder uri = Uri.parse(req.getURL()).buildUpon(); - for (HttpParameter param : req.getParameters()) { - uri.appendQueryParameter(param.getName(), param.getValue()); - } - return uri.build().toString(); + @Override + public void shutdown() { + } - public static final MediaType APPLICATION_FORM_URLENCODED = MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8"); - public static final MediaType MULTIPART_FORM_DATA = MediaType.parse("multipart/form-data; charset=UTF-8"); + private OkHttpClient createHttpClient(HttpClientConfiguration conf) { + final OkHttpClient client = new OkHttpClient(); + final boolean ignoreSSLError = conf.isSSLErrorIgnored(); + client.setHostnameVerifier(new HostResolvedHostnameVerifier(ignoreSSLError)); + client.setSslSocketFactory(new HostResolvedSSLSocketFactory(resolver, ignoreSSLError)); + client.setSocketFactory(new HostResolvedSocketFactory(resolver)); + if (conf.isProxyConfigured()) { + client.setProxy(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(conf.getHttpProxyHost(), + conf.getHttpProxyPort()))); + } + return client; + } - private RequestBody getRequestBody(HttpParameter[] params) { + private RequestBody getRequestBody(HttpParameter[] params) throws IOException { if (params == null) return null; if (!HttpParameter.containsFile(params)) { return RequestBody.create(APPLICATION_FORM_URLENCODED, HttpParameter.encodeParameters(params)); @@ -148,12 +127,71 @@ public class OkHttpClientImpl implements HttpClient, TwidereConstants { return RequestBody.create(MediaType.parse(param.getContentType()), param.getFile()); } } - return null; + String boundary = String.format("----%s", UUID.randomUUID().toString()); + final MediaType mediaType = MediaType.parse("multipart/form-data; boundary=" + boundary); + boundary = "--" + boundary; + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (final HttpParameter param : params) { + os.write(String.format("%s\r\n", boundary).getBytes("UTF-8")); + if (param.isFile()) { + os.write(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", param.getName(), param.getFileName()).getBytes("UTF-8")); + os.write(String.format("Content-Type: %s\r\n\r\n", param.getContentType()).getBytes("UTF-8")); + final BufferedInputStream in = new BufferedInputStream(param.hasFileBody() ? + param.getFileBody() : new FileInputStream(param.getFile())); + byte[] buff = new byte[8192]; + while (in.read(buff) != -1) { + os.write(buff); + } + in.close(); + } else { + os.write(String.format("Content-Disposition: form-data; name=\"%s\"\r\n", param.getName()).getBytes("UTF-8")); + os.write("Content-Type: text/plain; charset=UTF-8\r\n\r\n".getBytes("UTF-8")); + os.write(param.getValue().getBytes("UTF-8")); + } + os.write("\r\n".getBytes("UTF-8")); + } + os.write(String.format("%s--\r\n", boundary).getBytes("UTF-8")); + return RequestBody.create(mediaType, os.toByteArray()); } - @Override - public void shutdown() { - + private void setupRequestBuilder(Builder builder, HttpRequest req) throws IOException { + final Uri.Builder uriBuilder = Uri.parse(req.getURL()).buildUpon(); + final RequestMethod method = req.getMethod(); + if (method != RequestMethod.POST && method != RequestMethod.PUT) { + final HttpParameter[] parameters = req.getParameters(); + if (parameters != null) { + for (HttpParameter param : parameters) { + uriBuilder.appendQueryParameter(param.getName(), param.getValue()); + } + } + } + final Uri uri = uriBuilder.build(); + switch (req.getMethod()) { + case GET: { + builder.get(); + break; + } + case POST: { + builder.post(getRequestBody(req.getParameters())); + break; + } + case DELETE: { + builder.delete(); + break; + } + case HEAD: { + builder.head(); + break; + } + case PUT: { + builder.put(getRequestBody(req.getParameters())); + break; + } + default: { + throw new AssertionError(); + } + } + builder.url(uri.toString()); } private static class OkHttpResponse extends HttpResponse { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java index 0c057c7c2..be941f1d8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import org.apache.http.conn.util.InetAddressUtilsHC4; +import org.apache.http.conn.util.InetAddressUtils; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.util.HostsFileParser; import org.mariotaku.twidere.util.Utils; @@ -191,7 +191,7 @@ public class TwidereHostAddressResolver implements Constants, HostAddressResolve private static boolean isValidIpAddress(final String address) { if (isEmpty(address)) return false; - return InetAddressUtilsHC4.isIPv4Address(address) || InetAddressUtilsHC4.isIPv6Address(address); + return InetAddressUtils.isIPv4Address(address) || InetAddressUtils.isIPv6Address(address); } private static class HostCache extends LinkedHashMap { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/AbstractCheckSignatureVerifier.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/AbstractCheckSignatureVerifier.java deleted file mode 100644 index c72175b73..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/AbstractCheckSignatureVerifier.java +++ /dev/null @@ -1,347 +0,0 @@ -package org.mariotaku.twidere.util.net.ssl; - -import android.util.Log; - -import org.apache.http.conn.ssl.X509HostnameVerifier; -import org.apache.http.conn.util.InetAddressUtilsHC4; - -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.cert.Certificate; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.StringTokenizer; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; - -public abstract class AbstractCheckSignatureVerifier implements X509HostnameVerifier { - - /** - * This contains a list of 2nd-level domains that aren't allowed to have - * wildcards when combined with country-codes. For example: [*.co.uk]. - *

- * The [*.co.uk] problem is an interesting one. Should we just hope that - * CA's would never foolishly allow such a certificate to happen? Looks like - * we're the only implementation guarding against this. Firefox, Curl, Sun - * Java 1.4, 5, 6 don't bother with this check. - */ - private final static String[] BAD_COUNTRY_2LDS = { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info", - "lg", "ne", "net", "or", "org" }; - - static { - // Just in case developer forgot to manually sort the array. :-) - Arrays.sort(BAD_COUNTRY_2LDS); - } - - private final static String TAG = "HttpClient"; - - @Override - public final boolean verify(final String host, final SSLSession session) { - try { - final Certificate[] certs = session.getPeerCertificates(); - final X509Certificate x509 = (X509Certificate) certs[0]; - verify(host, x509); - return true; - } catch (final SSLException e) { - return false; - } - } - - @Override - public final void verify(final String host, final SSLSocket ssl) throws IOException { - if (host == null) throw new NullPointerException("host to verify is null"); - - SSLSession session = ssl.getSession(); - if (session == null) { - // In our experience this only happens under IBM 1.4.x when - // spurious (unrelated) certificates show up in the server' - // chain. Hopefully this will unearth the real problem: - final InputStream in = ssl.getInputStream(); - in.available(); - /* - * If you're looking at the 2 lines of code above because you're - * running into a problem, you probably have two options: - * - * #1. Clean up the certificate chain that your server is presenting - * (e.g. edit "/etc/apache2/server.crt" or wherever it is your - * server's certificate chain is defined). - * - * OR - * - * #2. Upgrade to an IBM 1.5.x or greater JVM, or switch to a - * non-IBM JVM. - */ - - // If ssl.getInputStream().available() didn't cause an - // exception, maybe at least now the session is available? - session = ssl.getSession(); - if (session == null) { - // If it's still null, probably a startHandshake() will - // unearth the real problem. - ssl.startHandshake(); - - // Okay, if we still haven't managed to cause an exception, - // might as well go for the NPE. Or maybe we're okay now? - session = ssl.getSession(); - } - } - - final Certificate[] certs = session.getPeerCertificates(); - final X509Certificate x509 = (X509Certificate) certs[0]; - verify(host, x509); - } - - @Override - public final void verify(final String host, final String[] cns, final String[] subjectAlts) throws SSLException { - verify(host, cns, subjectAlts, null); - } - - public abstract void verify(final String host, final String[] cns, final String[] subjectAlts, - final X509Certificate cert) throws SSLException; - - @Override - public final void verify(final String host, final X509Certificate cert) throws SSLException { - final String[] cns = getCNs(cert); - final String[] subjectAlts = getSubjectAlts(cert, host); - verify(host, cns, subjectAlts, cert); - } - - /** - * @deprecated (4.3.1) should not be a part of public APIs. - */ - @Deprecated - public static boolean acceptableCountryWildcard(final String cn) { - final String parts[] = cn.split("\\.");// it's - // not an attempt to wildcard a 2TLD within a country code - if (parts.length != 3 || parts[2].length() != 2) return true; - return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0; - } - - /** - * Counts the number of dots "." in a string. - * - * @param s string to count dots from - * @return number of dots - */ - public static int countDots(final String s) { - int count = 0; - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) == '.') { - count++; - } - } - return count; - } - - public static String[] getCNs(final X509Certificate cert) { - final LinkedList cnList = new LinkedList(); - /* - * Sebastian Hauer's original StrictSSLProtocolSocketFactory used - * getName() and had the following comment: - * - * Parses a X.500 distinguished name for the value of the "Common Name" - * field. This is done a bit sloppy right now and should probably be - * done a bit more according to RFC 2253. - * - * I've noticed that toString() seems to do a better job than getName() - * on these X500Principal objects, so I'm hoping that addresses - * Sebastian's concern. - * - * For example, getName() gives me this: - * 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d - * - * whereas toString() gives me this: EMAILADDRESS=juliusdavies@cucbc.com - * - * Looks like toString() even works with non-ascii domain names! I - * tested it with "花子.co.jp" and it worked fine. - */ - - final String subjectPrincipal = cert.getSubjectX500Principal().toString(); - final StringTokenizer st = new StringTokenizer(subjectPrincipal, ",+"); - while (st.hasMoreTokens()) { - final String tok = st.nextToken().trim(); - if (tok.length() > 3) { - if (tok.substring(0, 3).equalsIgnoreCase("CN=")) { - cnList.add(tok.substring(3)); - } - } - } - if (!cnList.isEmpty()) { - final String[] cns = new String[cnList.size()]; - cnList.toArray(cns); - return cns; - } else - return null; - } - - /** - * Extracts the array of SubjectAlt DNS names from an X509Certificate. - * Returns null if there aren't any. - *

- * Note: Java doesn't appear able to extract international characters from - * the SubjectAlts. It can only extract international characters from the CN - * field. - *

- * (Or maybe the version of OpenSSL I'm using to test isn't storing the - * international characters correctly in the SubjectAlts?). - * - * @param cert X509Certificate - * @return Array of SubjectALT DNS names stored in the certificate. - */ - public static String[] getDNSSubjectAlts(final X509Certificate cert) { - return getSubjectAlts(cert, null); - } - - public static final boolean verify(final String host, final String[] cns, final String[] subjectAlts, - final boolean strictWithSubDomains) { - - // Build the list of names we're going to check. Our DEFAULT and - // STRICT implementations of the HostnameVerifier only use the - // first CN provided. All other CNs are ignored. - // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way). - final LinkedList names = new LinkedList(); - if (cns != null && cns.length > 0 && cns[0] != null) { - names.add(cns[0]); - } - if (subjectAlts != null) { - for (final String subjectAlt : subjectAlts) { - if (subjectAlt != null) { - names.add(subjectAlt); - } - } - } - - if (names.isEmpty()) return false; - - // StringBuilder for building the error message. - final StringBuilder buf = new StringBuilder(); - - // We're can be case-insensitive when comparing the host we used to - // establish the socket to the hostname in the certificate. - final String hostName = normaliseIPv6Address(host.trim().toLowerCase(Locale.US)); - boolean match = false; - for (final Iterator it = names.iterator(); it.hasNext();) { - // Don't trim the CN, though! - String cn = it.next(); - cn = cn.toLowerCase(Locale.US); - // Store CN in StringBuilder in case we need to report an error. - buf.append(" <"); - buf.append(cn); - buf.append('>'); - if (it.hasNext()) { - buf.append(" OR"); - } - - // The CN better have at least two dots if it wants wildcard - // action. It also can't be [*.co.uk] or [*.co.jp] or - // [*.org.uk], etc... - final String parts[] = cn.split("\\."); - final boolean doWildcard = parts.length >= 3 && parts[0].endsWith("*") && validCountryWildcard(cn) - && !isIPAddress(host); - - if (doWildcard) { - final String firstpart = parts[0]; - if (firstpart.length() > 1) { // e.g. server* - // e.g. server - final String prefix = firstpart.substring(0, firstpart.length() - 1); - // skip wildcard part from cn - final String suffix = cn.substring(firstpart.length());// skip - // wildcard part from host - final String hostSuffix = hostName.substring(prefix.length()); - match = hostName.startsWith(prefix) && hostSuffix.endsWith(suffix); - } else { - match = hostName.endsWith(cn.substring(1)); - } - if (match && strictWithSubDomains) { - // If we're in strict mode, then [*.foo.com] is not - // allowed to match [a.b.foo.com] - match = countDots(hostName) == countDots(cn); - } - } else { - match = hostName.equals(normaliseIPv6Address(cn)); - } - if (match) { - break; - } - } - return match; - } - - /** - * Extracts the array of SubjectAlt DNS or IP names from an X509Certificate. - * Returns null if there aren't any. - * - * @param cert X509Certificate - * @param hostname - * @return Array of SubjectALT DNS or IP names stored in the certificate. - */ - private static String[] getSubjectAlts(final X509Certificate cert, final String hostname) { - final int subjectType; - if (isIPAddress(hostname)) { - subjectType = 7; - } else { - subjectType = 2; - } - - final LinkedList subjectAltList = new LinkedList(); - Collection> c = null; - try { - c = cert.getSubjectAlternativeNames(); - } catch (final CertificateParsingException cpe) { - } - if (c != null) { - for (final List aC : c) { - final List list = aC; - final int type = ((Integer) list.get(0)).intValue(); - if (type == subjectType) { - final String s = (String) list.get(1); - subjectAltList.add(s); - } - } - } - if (!subjectAltList.isEmpty()) { - final String[] subjectAlts = new String[subjectAltList.size()]; - subjectAltList.toArray(subjectAlts); - return subjectAlts; - } else - return null; - } - - private static boolean isIPAddress(final String hostname) { - return hostname != null - && (InetAddressUtilsHC4.isIPv4Address(hostname) || InetAddressUtilsHC4.isIPv6Address(hostname)); - } - - /* - * Check if hostname is IPv6, and if so, convert to standard format. - */ - private static String normaliseIPv6Address(final String hostname) { - if (hostname == null || !InetAddressUtilsHC4.isIPv6Address(hostname)) return hostname; - try { - final InetAddress inetAddress = InetAddress.getByName(hostname); - return inetAddress.getHostAddress(); - } catch (final UnknownHostException uhe) { // Should not happen, because - // we check for IPv6 address - // above - Log.e(TAG, "Unexpected error converting " + hostname, uhe); - return hostname; - } - } - - static boolean validCountryWildcard(final String cn) { - final String parts[] = cn.split("\\."); - // it's not an attempt to wildcard a 2TLD within a country code - if (parts.length != 3 || parts[2].length() != 2) return true; - return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0; - } -} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/HostResolvedSSLConnectionSocketFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/HostResolvedSSLConnectionSocketFactory.java deleted file mode 100644 index 89c0536ef..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/HostResolvedSSLConnectionSocketFactory.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ - -package org.mariotaku.twidere.util.net.ssl; - -import android.net.SSLCertificateSocketFactory; -import android.os.Build; -import android.util.Log; - -import org.apache.http.HttpHost; -import org.apache.http.annotation.ThreadSafe; -import org.apache.http.conn.socket.LayeredConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.X509HostnameVerifier; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.Args; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; - -@ThreadSafe -public class HostResolvedSSLConnectionSocketFactory implements LayeredConnectionSocketFactory { - - private static final String TAG = "HttpClient"; - - public static final String HTTP_CONTEXT_KEY_ORIGINAL_HOST = "original_host"; - - private final javax.net.ssl.SSLSocketFactory socketfactory; - - private final X509HostnameVerifier hostnameVerifier; - - private final String[] supportedProtocols; - - private final String[] supportedCipherSuites; - - public HostResolvedSSLConnectionSocketFactory(final javax.net.ssl.SSLSocketFactory socketfactory, - final String[] supportedProtocols, final String[] supportedCipherSuites, - final X509HostnameVerifier hostnameVerifier) { - this.socketfactory = Args.notNull(socketfactory, "SSL socket factory"); - this.supportedProtocols = supportedProtocols; - this.supportedCipherSuites = supportedCipherSuites; - this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier - : SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; - } - - public HostResolvedSSLConnectionSocketFactory(final javax.net.ssl.SSLSocketFactory socketfactory, - final X509HostnameVerifier hostnameVerifier) { - this(socketfactory, null, null, hostnameVerifier); - } - - public HostResolvedSSLConnectionSocketFactory(final SSLContext sslContext) { - this(sslContext, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); - } - - public HostResolvedSSLConnectionSocketFactory(final SSLContext sslContext, final String[] supportedProtocols, - final String[] supportedCipherSuites, final X509HostnameVerifier hostnameVerifier) { - this(Args.notNull(sslContext, "SSL context").getSocketFactory(), supportedProtocols, supportedCipherSuites, - hostnameVerifier); - } - - public HostResolvedSSLConnectionSocketFactory(final SSLContext sslContext, - final X509HostnameVerifier hostnameVerifier) { - this(Args.notNull(sslContext, "SSL context").getSocketFactory(), null, null, hostnameVerifier); - } - - @Override - public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host, - final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) - throws IOException { - Args.notNull(host, "HTTP host"); - Args.notNull(remoteAddress, "Remote address"); - final Socket sock = socket != null ? socket : createSocket(context); - if (localAddress != null) { - sock.bind(localAddress); - } - try { - sock.connect(remoteAddress, connectTimeout); - } catch (final IOException ex) { - try { - sock.close(); - } catch (final IOException ignore) { - } - throw ex; - } - // Setup SSL layering if necessary - if (sock instanceof SSLSocket) { - final SSLSocket sslsock = (SSLSocket) sock; - sslsock.startHandshake(); - verifyHostname(sslsock, host.getHostName(), context); - return sock; - } else - return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context); - } - - @Override - public Socket createLayeredSocket(final Socket socket, final String target, final int port, - final HttpContext context) throws IOException { - final SSLSocket sslsock = (SSLSocket) socketfactory.createSocket(socket, target, port, true); - if (supportedProtocols != null) { - sslsock.setEnabledProtocols(supportedProtocols); - } - if (supportedCipherSuites != null) { - sslsock.setEnabledCipherSuites(supportedCipherSuites); - } - prepareSocket(sslsock); - - // Android specific code to enable SNI - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - - if (socketfactory instanceof SSLCertificateSocketFactory) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Enabling SNI for " + target); - } - ((SSLCertificateSocketFactory) socketfactory).setHostname(sslsock, target); - } - } - // End of Android specific code - - sslsock.startHandshake(); - verifyHostname(sslsock, target, context); - return sslsock; - } - - @Override - public Socket createSocket(final HttpContext context) throws IOException { - return SocketFactory.getDefault().createSocket(); - } - - /** - * Performs any custom initialization for a newly created SSLSocket (before - * the SSL handshake happens). - * - * The default implementation is a no-op, but could be overridden to, e.g., - * call {@link javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[])}. - */ - protected void prepareSocket(final SSLSocket socket) throws IOException { - } - - private String getHostname(final String hostname, final HttpContext context) { - if (context == null) return hostname; - final Object attr = context.getAttribute(HTTP_CONTEXT_KEY_ORIGINAL_HOST); - if (attr instanceof String) return (String) attr; - return hostname; - } - - private void verifyHostname(final SSLSocket sslsock, final String hostname, final HttpContext context) - throws IOException { - try { - hostnameVerifier.verify(getHostname(hostname, context), sslsock); - // verifyHostName() didn't blowup - good! - } catch (final IOException iox) { - // close the socket before re-throwing the exception - try { - sslsock.close(); - } catch (final Exception x) { /* ignore */ - } - throw iox; - } - } - - X509HostnameVerifier getHostnameVerifier() { - return hostnameVerifier; - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TrustAllX509TrustManager.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TrustAllX509TrustManager.java deleted file mode 100644 index 45a59d0be..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TrustAllX509TrustManager.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.mariotaku.twidere.util.net.ssl; - -import java.security.cert.X509Certificate; - -import javax.net.ssl.X509TrustManager; - -public final class TrustAllX509TrustManager implements X509TrustManager { - @Override - public void checkClientTrusted(final X509Certificate[] chain, final String authType) { - } - - @Override - public void checkServerTrusted(final X509Certificate[] chain, final String authType) { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } -} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereHostnameVerifier.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereHostnameVerifier.java deleted file mode 100644 index 4583a81fb..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereHostnameVerifier.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.mariotaku.twidere.util.net.ssl; - -import android.content.Context; - -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLException; - -public class TwidereHostnameVerifier extends AbstractCheckSignatureVerifier { - - private final Context context; - private final boolean ignoreSSLErrors; - - public TwidereHostnameVerifier(final Context context, final boolean ignoreSSLErrors) - throws NoSuchAlgorithmException, KeyStoreException { - this.context = context; - this.ignoreSSLErrors = ignoreSSLErrors; - } - - @Override - public void verify(final String host, final String[] cns, final String[] subjectAlts, final X509Certificate cert) - throws SSLException { - if (ignoreSSLErrors) return; - if (!checkCert(cert)) throw new SSLException(String.format("Untrusted cert %s", cert)); - if (!verify(host, cns, subjectAlts, false)) throw new SSLException(String.format("Unable to verify %s", host)); - } - - private boolean checkCert(final X509Certificate cert) { - return true; - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereSSLSocketFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereSSLSocketFactory.java deleted file mode 100644 index 7db41d043..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/ssl/TwidereSSLSocketFactory.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.mariotaku.twidere.util.net.ssl; - -import android.content.Context; - -import org.apache.http.HttpHost; -import org.apache.http.conn.socket.LayeredConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLInitializationException; -import org.apache.http.conn.ssl.X509HostnameVerifier; -import org.apache.http.protocol.HttpContext; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.security.GeneralSecurityException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -public final class TwidereSSLSocketFactory implements LayeredConnectionSocketFactory { - - private final Context context; - private final boolean ignoreSSLErrors; - private final HostResolvedSSLConnectionSocketFactory delegated; - - private TwidereSSLSocketFactory(final Context context, final boolean ignoreSSLErrors) - throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { - this.context = context; - this.ignoreSSLErrors = ignoreSSLErrors; - final TrustManager[] tm = { new TrustAllX509TrustManager() }; - final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, tm, null); - final X509HostnameVerifier hostnameVerifier = new TwidereHostnameVerifier(context, ignoreSSLErrors); - delegated = new HostResolvedSSLConnectionSocketFactory(sslContext, hostnameVerifier); - } - - @Override - public Socket connectSocket(final int connectTimeout, final Socket socket, final HttpHost host, - final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext httpContext) - throws IOException { - return delegated.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, httpContext); - } - - @Override - public Socket createLayeredSocket(final Socket socket, final String target, final int port, - final HttpContext httpContext) throws IOException { - return delegated.createLayeredSocket(socket, target, port, httpContext); - } - - @Override - public Socket createSocket(final HttpContext httpContext) throws IOException { - return delegated.createSocket(httpContext); - } - - public static LayeredConnectionSocketFactory getSocketFactory(final Context context, final boolean ignoreSSLErrors) - throws SSLInitializationException { - try { - return new TwidereSSLSocketFactory(context, ignoreSSLErrors); - } catch (final GeneralSecurityException e) { - throw new SSLInitializationException("Cannot create socket factory", e); - } - } - -} \ No newline at end of file diff --git a/twidere/src/main/res/drawable-hdpi/ic_launcher.png b/twidere/src/main/res/drawable-hdpi/ic_launcher.png index 29e5df6b743a005563f859710672f44f835fe14d..1255af9e4eac019bac176f4920d9b6bfb02742c5 100644 GIT binary patch delta 4467 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 FV1lEhixB_- literal 4815 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 diff --git a/twidere/src/main/res/drawable-mdpi/ic_launcher.png b/twidere/src/main/res/drawable-mdpi/ic_launcher.png index 85f893e9e0e497b46decf4f93e26a34d7c48313a..721870626e7c1ed74a96f1c7f990f1f041555f60 100644 GIT binary patch delta 2750 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 Ef`|DtSO5S3 delta 2769 zcmV;?3NH2771| zp7WPLdN*^)&3~90cm((u@MU1a_kF!tcL5IPi^dC2enP!Jyn*}LSNQxd6u|Sm5^%_t z;@Q!MGKW5tm0SPi63?7#gJ$$xrd+aF!`pEfF$e^N^QhwuB< zTe%DHYg-?Z&wudulE-%prJe-B1ZFQLSAfNz8-V9|7VseO+j|BxM_&AZv+Fm0RStq} z#z{(TsYyF2i71Zc-i+)&zI(Xu>npVf{`^MuD4zFsz$?D*2i@-C6pa_&`@8DMfg{PT zl8rD-P=5*3=L>pawsqe3{nlLrc%D}P4!W-M$-}!chhF@!)jzT$&1uVEInELzS+2qL z`Gkq@XXuGSD$?WurHl_3vfGaqh6X=3KmU#|ue844dEVaxJAvm8JZ&6(>f`FsN|{VM z0$>$Imgeaz#Ya}E%C4^j#`Jtlby?Ah5~MU(T2#o3 zHh%-s1V9_0&@8l5W>SqIC8Ij&Ikz_$J^H8VzDfz#=p^YlhBSsW1tB06Fn@OU%uh~F z{Y?-AjZJ1U%XQrz*LBOT>-I?i-}kQppPiU>{&{vO`?+%7Ir8KKX?8~?Hf`IY6~@#q zm)J&|w$e0}BGsCD)TWWPX(cJA4TGKeoPXH)zSI>lq!QXqKmdAmlycSoTOrJitBLgi z?0KF5vOo@SJL6Vh7}(yq9t8dkxB-m49mKxxYv1?Jd7k&tblbMazIVa-eEEtwdU$v0 zv}Tkj%&&+^ozjRC3=NJHR4j`JvIY8VNlw5wy@;U)F_c<;>u8WRkgSeL0)`E7wSPy? zUR%5fJmz^`x44Q^pqwv@Qnsjja%J+xA!K<6vNV8u`2_7B|EKstJJjcZH+|pN;x?ud zgM)+bv~ByZ%8`BOpa|rME%2m8NlIK(;0O~-tWGhP#Ju#bt32_ZgiIDJ3xWWeO=!0v zY(lFJStn1hlu0lDs5)WyYFQpadVf2yN@a3|GP$^p83mBEAPNkkhGzPbPN&Wm0;2rO z`F#El#>U1b)^v&u_}tKN@1F0zd-x;!9=9%ucht|5wH%bzXuuYNIBoOC?hvyFo}xGX z3R}zb7`8!AuZdFtX}8g`PS{+vM!O-9QV@p_EJ+$Gns%s3+o09z;w}Wlv43D`UZ%Bc z?ovKidTDZU@(aH2r<*M$=BAd%Pq(*(k6g$+Va|Dva=7{}1`P#5AOzHfCNpZW&;BB5 zT*Ht8-4-lPnh+kdmAH2nT8g&`-w;j;B8rkzlU=1?jn|E$Xy^-g|pW(WSc z6_&2H2v#)hCWL`NC4d6Z7zP+72u!qAXr))h>($y)rHYjWOE14wsF`W$OEWVwzcW5Q ze&v>FbR((Db=`(-*+;kcJlH2q6H^-8&_i4;JWSD;r1H^OoY>`u&VMMPI6|$Sr@a`U z+byh&!ju{z1WK)1Ob9`ms*e9^3`3w5M1i6@ZN<&`!eqfMf9lk!Q(p{%pncnQV1ocb zqrLRxuHyc^`D~962((Zv1tB9xPLeOKP|hs#4Sy83e*n9;LL!PRuegMZ0V;|x?NxWt zTCjE)s5NK=T5D>H27k*}axES8e%CPMbEi+A-q0$&A%GwV(u|X-Y%h%2KuI z!hGKC`;)6zuf8}rIa%BAO1#krUDvHkDSvaQ=i!n-&|jY5@qZ_dBQZ!)heo3Y=1$&B zhB1VRFb&Ln2`gX3$o3E>c^b7W@p1%&m?kVwVCnF6|t3EA}WK*B-uN=g791Pn+r3t2$K^A(EZXVOh5~>1? zTArpKupDmD#(&_UVCk*P$8KDoI~fGQ!lrAXjXoFzL2PBr?G?BD)W<*aKay~Tpc+!E zCbX&{D+@7J;|Bkoz6+~d0i_Va03lE)%#4efFJk5j2-_vG1}VP#Sz#T1CL?~CFs zb&KmbH(b}fwizqP4Hi(MlcissY`@Z4Xqfgk7hx3~a(}LcbTnqhz%Ug1r)vCEYlLMZ zq(&z(QW$7UVY)eFPam0qVUjQ)TAb6)#aGj9OVf+lO65B^9ZhfOw;``8R-h zC7f5@nDq{Xs#rOExFzg9OJqwnmTRJ=Kn7V}Z#{_NWIE+12x%d-MhFAL$r809GE*0l z()8KdR)0r-t+!PC)R{A9UIyZyTMT%fCtTNE6K<~cD2nF%#p=JVsB(JZTw&kVta7~G zNFW3nD2qA1;g4du4wxolbvJ;J7GV&Qj-N>jZypN<(&bl*`Rr$o9Xs~72Cw`jd1vruU+VW^qCL5A%lf| z^!)UAnp>J#Znaut=gy!1p4R#jP+hBxx*&`-;oZOEc^-EPYH^#4)9w4~f!5qhcE+_` zRDZEpJTg$}f4Nmo~$6S}oA?EvY!0T5mO-))KR z&)u=n?Op@rayeJ6R;M}wb*HPYzFxRqCthtNrHVVmz2~f9XHY|KYTgr|lSSOyF1hu%u+)DZ; z*W3a|uN^LIW+4-7vUNdm2Y}nPZt004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x00(qQO+^Ra0Ur_-G!q36oB#k9zez+vRCwCWoq4n*Rei@l zcd6>`x6aB8GsBE9FbwF34C0O&Bj7H9@lZMa~$XEdUA5=(y`+BhGMZa;2RUFYCD465klMYr?aFXbq+WS&jfS9ec|3GB1EMxd3C08i>i93jnBMZ|QdpAR=tzel)jYc0i6X@&NE zUqoFH*MV9|hw}J`>;{-#Nz#aqx^L7qLA5%t&`d@aB7EN$I_~D(fSwk-7h)E~-Ln%p zNwcIA8SOKswY_>fx);DFf!QtCy&@@%u1~9un(JjHBZ>&!-at!9SD?SxW#q;pw-IaU(Ymo%iLAN`$(03m8?Kp3;^8?uC1kOA;xWf;Y+$>IKr zNY6~x(cfObUX);lW(Pej;4kD}tkb=th)E(O%jl0P*S!Ef3H0n>r{7hlm8jioQa_Uj z=|o1-=x;99Q4O?rEP^EB)`B#U^d98cyEWP9Vd+Fp5+R+)NIUwWMnI-n(53TjHfCtI z*QRy0g~>@nNGCF)hzR$knTR$O)H~o`c;{SFyPfU*p#L-Vpri9GbSjF7=Dvi9h+l-~ z0Q47&C$v*vGCSX9PPMukQmQ9Cd*I_i&jR?dA}8MM+@15S8J{fbDYO%g2EW&CiL`a* zN_yvhfEh9(h-mz(l}8Yv!zaUygt~LgJfgiOC~GVrS{0?)fI-9O)A-e{p6Vp%wh?b< zmLH47n3;%IxY79X)`IYPYb_Ro0F;zH1v?GEaX?4apN;daULLhZbaY2sYw61=R?asF z>vsswhfr-U4wp0)8f)>5Wtkmh&xFV0r74t@nRIiKq&Gp+F2u8OzQwqs!@xH~XszYk zRr`4NDbFH>p%-E-ls(Jz$SMXO*6c=cjmWdPIE@k_Q=mL6ut^}N0lO36oyrs4(Q(eW ziL&9Fo8}|RBfS6RyKt3-S*T`fq2hsGTFSgf=c5meak3O#-9NF{?JrDt(!<8~; z=M_hC1)iF!%qj5MQc*( zuM_*Q#^*F!WyN4WTdS6F&!ZRHwO*0fi_s4?0@B6?-!Nbzgy(Egom_zzo0v z_=@7rQyAJlA62P#r^b!pWK-ef!9E`GV5I8L$xN6{d)?^$<}!8M%9A#90y(Kn3fMo{ z$M5&{vvxs|9;`09k=rm_OZ zMb>u9NiKuT_v+I8Oj|c6!!q_`nSl zJ~vKStfSG5yqfO%ITt>l_KLL5R2w{vlolbggd{U0*;--oDu&Y!y$d@ixwUXTqy9Mm zIOMQG3DOy$ZO_4vCVak8^6|BcqjVRo#6MG(&4m`2V+zkiVz?aC}mL&=$u98EpEZ$_8Hs(OK!lB8}P{u7~BDa z+wbG{8Fay*a|WHWJbTX|2cI6m8ii1S`plFSk)}@7l(E9#kmt10g=jy|Zw9}@73Kh+ z9dm8sN`1U63l{QJ01QED7#2K@MrHF=oopKTMm^_L(eNEwV$?SghKx=;hP-;is7 zYRC;5+yS3lzriU4KPqQYu0d&wP!=JBwuPt#S{e~8fsn9gbDk~#ttn6VIIe7fpQwV; z0N*GR;8Vp~145v1Su=GW%FlD>@O4}Yxx6W_Vo6ZI{bj?yjCvdqIvDvv&gfMe zHBxP3q-t$Zu0`j-$s620i`(A{X#KO>Z*ckyPCgi=%C*QksG=FvI+3D{q!`$x(1}qRlGon3$Cs_@-lzDn|HVgTS*A4y!9~!Wqr8arldTYV>KY9CEm@ zPa~i$w4i{k(}vF;t*{GCKBrNRK)WWG{&|a&52k*uK2ispN2(#$7l5ks7M(LFrw%H? zNTnIn@N8**p3scSXip0XrGttGzME%kpT@~MNTq_oRKqJ$1$6^{U3^i&;J1LQgl1Ls zwVXHlPi!2%maE4eW7JsQH{hU~>Pg@k>vR3`3G`Wp*DTTWFD#QE4Ag>CusHeJ^sj>| zEke$O_!2=iP4(tbgn)8U^3eAR9C*UP$vY^e8{jwN*wL)$#yw&1u@OtH6p|I*dd?gB zG#iJ$z!%2cNS|7K!igJ_fKEq-#r69*wRAo?JJ8-QAs8@=Ili)vLwXl?nCBVy z4eN7?l7XuZPjOFfn1ieSjPWO?Sg_zQ{R?ZOwl#k9qL=Nz%=5_4a!Bc*9k-@>L^=Yx z*_hdyg`rKJwWagPo1w^#2_fh=3pl>=az^!jerKL#chzv`q{p`Tl6PFNi2M4_W54rq zM(#btpmT`f73NsSW4ab0;kkQV9=s`!sXA!aK`GT@!KL;ErSW4lmsuOAAVY%NS-=9b zlDCe3iZzw2EffxUcQE%r(D9uThC_OFC0uY{;if5g9r%+9@A{P zDMximqg@9n6@*+SVO(Vtu-!Q201O_!s3vz-2=aCi?6-S@tpO*iJ4E(SLBi&2Dxu|10WZ^$!o*l7Vj4pRV`RpoS?85WH26A2Iq zJj&eL|1EYM_iL{BP@Z?cO#&8I8HuZgv#}@sBzkR>t&o2)A zCs+{>U_B55;pCui7_;~#jJ@##Y#Tq9JN~1yZkgW}_SY4g?5bTkclwoA*ur z^kR>XzF(4aTQlt(WpRYp;%gAff!qLg{xT-s_?$R;~=GMvv|^A zsMA)A9Q9!`FnD6R4g6=F`?+KIOT6*T<9zzBHN!(hMrj;vaitg8xR4U-7GP)*)31FG z{j8m@-92LI?z#eFyAm>ktOxxmL0ws=I!<2Exi-F_epe30#K zgbA#mhJ#eFM{nh}p)auhb%(h6Bbr4E=olh%l*Nr%Lxd0ror8h-OrQNuif{QaPj6eq z9oK61KBDGa9SI2&hb8xY$7N)9&DO8&a*22;F9}x&tpRC9eQQx2`kCYU8XyoHR8R4% z!D~3>tX=%;-#IK>LTr3eD@#uK(bH>&1%!5?Z3z#_nQuZHQ!p1Ktp8Bn7c#V4w zzJSVvVAJU)OB8S>sLjc;_s@_h8G-4o~0hpd5|THCw+P zgo(o{2HTW7;dtm9X&hLL@F>c|+&1ue`qpmcbC(Hn3jYTW@s4vxP}Ge zVn4U{e-VH61AOjdF6&>K0{ogNNhSgU5+pb|s;9h$;zb{)vScmWZ&Pgivcu8mW>12U z@Z5un%|CK6o<=!3AYO>}f!OprEeQ!`Xe;y~F4{8;tYBN={}^9&C;#$Mm)D;qGUcR5 zVJT=oTA9XyBm_b^cqgu>_>NEFtvZ8Un-#yl)@An=*|j5@jdP5^!w zvrq<#4Uy1-W9Yde*MPQ*qEt>qnK72W`6WJmsp8z%X9FLCKzUw!bY)KpflwN6#Y-7~ z$0zXCoXwGEC3jrs@c6G4wNP~?@W=NF?)kRE__P&;rEf9WZVU-Sv8r=P>ri01BZI&A*2rZPn;l_UgA zj|=YmuET*RoSK78x7qT^?wMo~WFq)6(`P0YV97a#%l<<0-nUC+w-$(iK3xTCW)%Pv zLSPoJqIluQDWCCHysBpNPc(ObLo;zWVOoxz=#7HKjym@*3DE(`81Q)_@4`J2cR?!!w4h+bcZv%q+u)0t${t_t~ToBn&R1 zbpA(~e$Aht+#Guz*4%!r!}AZd=jaFtJMNbJ{--Y1XrylTZnoAm&t@ zS@s|Dx&IGUPF$flX=SUqmJTh$!-tk}>ycL>^8LMi6GppKSFQu)klXh-)sn~VtpdLS z$E~r9?-%^$s}5s(T^zU7x8DwGMiOY9ZOtS!5nw}qQh;CYP3=cY$&pdRJr7k`vPg33 z>WmID3EM`F=avI?0($IcRu0t@UW&~RklVWrJ5^!d1=B7sq3>~nhGJ1QZQaJ-2G66!G6KZPSvQiI10-XBa8W;{jY}H zV7i3dtRfZO^0kmZ;`^2m-NB0ULB1hg&?S;22=T+EO# zIc2$La|H#w;&ca{CbeH!o*7-pPxieQo1Y^BYN&a~ufZQ)g1cuM%A2ZLJgRN8mF`WK z?n#j0MYkL9Th7;Zq}p|uswlqj?P;#Lev;yZCCz)LdB;cf$O=l5^15>wzvvU#g)4$D z36bq?i*$*}o$q1WH&;y`!@i)HnGkow@T)yS2&7VI$H6m->wh@SXTCJSk)sJD*uWX5 zgqP_hjPAu!S$hs+7hj3Dcom{4!z28BS)7)!S|qf&?cfWFQ$wA>Z$1`6B4t1VQuBkK zSGeMu5_|TWSQ7HeLwEH;Q(jg-LkRDWmgy3vC|J;28fWH^3ft)P^M>M4b_$F7`^yYl$W0djjvQ_-;rl|qbSWe z8Fx{)4A(m^1PCdRN}{xb<7n>Om6kX9Ip1SRo}+JGidH=52;cuPSo)gOxTCTon*7TVGIDVhrAK zFJbKBPcn7#t3z(_?n&rak2q&s7Y)XBRERnW3Mc3A_)g15Hx&8hon>s3<>5*nw<)vY zn2Ry^^OrMr;T24-eN*V+v#6hR^-?Xuoo<>I{ALkSB9+2%9iHDWxcuKr{P^cn__eBz z^gHGT9W${8Gjtr|7hKN7x^r9Kw#=|eO;b&? zr#pCPZrb@O4v6JQ?Ho(k+n&d|eNZ3@Ih);PCDNHK0X0vi2>-~L&$50T^&`OTqjKVU ztY5|1_avfaU6?%yiEl|p$*fFMn*2v1$nViWxeC|aFvZ>xhssL#tY0#V-UszzZ!h@J@6~wd$rG1gjWIMA-pQWD}(XzRSvIzA@11T z$hc-W{V=U`{0|!4sh%t;E={9BDKtUy(@j73sEj>{yU+^~UX6#Smcgq80C|-F6yHPm zRj8J0fce2+uTlfpLwJ?oYtl>|Tn}N*Oj$EY)*!5%`CNM*QQx_WAP>ok)@-0o5vXQD znl)Ezxka3~{&lwbq9g*+s-9HIyv_BE!5D+_{oo;`M2bc{l;Gv>gxOmAhQ$UX&H7q2 zoe?o*tsh$lN{8c4swj;bip-8q2YzQT+s~z_-vB+pyl%F(T$9o^wXHO?l!pPBF%3dx z9Y$@`arn7fMtH(3n8o+Mr1Kqh>SWY$&?z|+M-9bQvvjRFsUYpY6G{3mq0aR?tD+={ z%?nDadg8%NIv-&nH4eIbF420ejZ7uZ=3I+9A3B?eTNJjl{syFO-PWD1wbn+0p2b=V z$6%Y=i24HV_?Iaow zE7gN`xj;u|kA>7|H^6tgca#@0D+%2^cN&@fm#{^4FdmS|W6`-z+O{F3x~N;X!V><^ zMsfdJY%X4biq4Enp?-Q|o{|oHYiE9vB(832It`ICA$8wpfl+hyo~IpT4p7H{pBD7E zzQnFY5nhhj%)V}9PT#PN-5FyO!H;V<>L)F5VG^>W;`CaAZ^;Nj6Mge+Q!7rA7r-bG zXZMxEi6o@mW%pK9TWO6slJxN1NK!1c)45b`i;Va0)OI+HJazwjB-WTCa=J8jt2JhB z|8q|BO+%_D4xqS)PosXi**_dJ9E_<n;;gxG7GSU-MqnY303pzh*4O=ddD~siIrGQ8 z@AXr=TM|+WL8edD)w}(c`|kODf4}c<<^F%Q5z z0P|xWfO!Ds$2;LmHus|Qkl1acS!hU;MV1<^H5ek zue|aP3xG_^7E~&g(RUHuToaVI>-x{Do7b#OmMI^q8_yiyxbfS2{=xWwa=Gkh=Kbjf zCI4d!7OeViwOajV<#gEiG`hLsR(;16cP8fzEifJqCThJjYHY7mD)sj~09holR=}rj zxS(|H*X}4T9o(;T8`k%I?d$!6cfRcVUyq{b2bD^t_72m{ZOd2dPhbD}9Fe6aV@-@eUTTqtW_7XzX*>wIvNr>>LXW#2pd z?En6aTJ0X--vjF_l}dC91)l@1_DlToO`lgcox3(!){}=Mh9m+Z1P+H88?>XNqd$Gm z0Z=ZNHLwc!=j?bYlUbK`F2joP~M=!b62aufF=~o_9R} z<#Ks3@R2}s`!8SBfBBd0$Svtv;^q93CJMn>3ns>dhDOscIhIhZSz57T-_9}h=r41V^jeZ4n6KQQpE(b3Vrs8lKkPmpe|@k`uw{cp&dFSsgM zTFMFH$YBg1z*>+745=kJ<52Mcjk?`dsZ_#uB>?4eSph47Pc9quZ~m7z_MH2v8yHf( z1tqn{h9R{|NUdfVAB`Aq2^xt3`6v8Oti2;zUGA}30CD87mQ?j6 zNSQu5lY#*tnv02|Xv66YjrA-OD*zV(cV2Z);kvJWyl2IQ7uo?8c+@8i)qQoUqcIba zrXEV_iNsPQaS(=<#DW6C(6I02A}d^iR*Kj=#3jxY?$1XT z{mt^_fBI(=6ZdGXf9Jq~1M80>{DoOEb>~OFn0#P(anzIZAdJ8zU>!&WLZw1Sro8VQ zNCC!znpkX_&F1>k34oY^LqAjq^j#lc+kfSkZkI#(9uqX{j)NnHmebTDMLmMVNumU- z0kFg-otze_w#63qZ`br}b@b~LXVdkK4!~MGYgnNL=aowI1$hLHs4;Fo?+xz!>7Jdp zY}>YN>kM@BLBGVOKl+>Mrt{u!mh~4Qj?!y73o=tHYe9;%;A@=)+rD3D7!WVrIq}H- zFJ1Wb(@*a{Z5B{2mnE>GudnY5Lj(Sew_oZlzxCqDe9MG#ch#q|TVR|b#wQAG%Bg_G zA_37=(AJr7BLlVZ7`6xW>XZfAvbQdMltQE`sx+^ggk9A!&I=lxUFxM!7?6Kx;xm3_ z>C!*1R;#yHDwSqNH|GI&uD!$m#8tl(tuFT)Z4wAubnZ(Du<;ZCR;1r91xPQo1X6cJ z{XqV}tFOMg|8!H(5U@sx@E_fKV}AMIfbnHyQo3hG^Eqt(8h8KAixl zMx#+{G#cB4^ndW=R@5u|y`>8ae!-KG5>g?B>zJ_+E)6vuNa4Vxj?Sg07*d`+yjRh? zCm}B-j+A(X5Ez%zgb;$nS`6v+v=kTtPAFoj*%3zUuGhHQhU&Hcg_rJ&51)0@Z}Zt} z?=qJx9wabPsthiRaHN>hcw8!cB~nEZYU*{D0R)hYuXLlY_y5%kFTC*dX$7Et(r7fs zwAMd5Tx~tHVSD25u6V=m8}M>@-zX;}v9YOkbxou_UzIsHDJ0EFL-XZ;K_L-s!DPhP_=Fze)f$xzkAdhK6^ms13!^k zNknGSLZlzoDm@oKVk~=KE-_F`kWzL8>@wjdq{54wBeswTh^(dMEH#r*4I@Uwknu32 z9z|#zDi?^>hibCap9)gzlyY1aULsQJbFNGH8OUvLIt(OIi)b2fyRRDm;R8?nyV23n z+UXL0xm@;U3_~lGiUVFPm&^Zr)3(OyH})0pU9~Ltu}@s42YZUKr{j#~4)L|_r27s?Y^LEDtb=Ca2yIN=IIN3F633t?7}6d?xf~09pOW@S;*h^MyTMs! zrh-h8G@EsQa=sH;7Hm>0s_oVLtJUhkQz%?P+GQ&M#q3xH zoB@nv>T~$0iCCplvA~vcx%_J{ZmnOqZEx`ltC#0)zI`o&g`Cq$s4VKS)N6+1jerFT zLeu7aLueeaHPjPBWDQYb@B|Ecij_H!LES^Y?^DX0wF3ev(FitbpZv9ZQ?3V`2XT_V z$J43IO*b92hl4myK~X9N#F#8Mkl>jmB(d3&&F^GtVQs+{fc{Ll%YY@olAy$3u~!e| zdR@Pl7bU;w@}oPX4ab8&s?};gtW+x7j;j=r-RP!rxqQ#_uhlQ!`c~oI^OqNIxcv$S zgtppP$KF?p47NhXg`}CpG?Rqb8gS676vMt|$jh<7_bDoc5|Ti=)ImCl&Z?6x3s{?R z$fIk9mOr z5|L7^kzgQ>1BU`Hq8DBGqV}g|WtgC#RgEI5Gx6X8K=?8hmB>A=dnp80CZSgZ8p8 zMu7I|L|ECxtA=B)1{E(t{AUE#9{I`}aUHT`F zJ@(i!i+pp{ls|Ad+`9$qUnQgo0?#Aw5A&?Jf$d^D*Vlf|!dz~Ogan;Jy~+e+aD<6P z-m!)yJAQ(x?ZFcQk;U-NLO2Evm6ku+wFN|)gs8S35L2jTWskPZMF;&U1u%9>!wnAO z%n{*iJJOPfsn2#7Asr-5%jj<3gkyzm`Fzjs-SL?_e)#1tfBCqYJZdha-)J;kp-?FF z`a>V=FZA~bA(6tP;1!7E5YOvjWVnwdBE*w2t%hdNL)$M{XOI{4(s!Zb!NzB4jFpHE z9HcqXOhZRWrxYUECZENmLS=QI_Vuz1S&&*u$3Kdx+L7y~j!uO(i>Sm@$wg!rZHLGr zyROKJi}i|NWLHna)QjI=vSjIRJoC&mPd)zl_wvBVNAMP|A}X#XoP@Yq1=}Q7 z%=p(%fzWo6a*A0x0@q3*5Lo9>Sy3+iTXag)Z6H!`14dRME3KvhJJlHULDU+%nRVbncRubwJ)JF{Ap>d3f@H{bvoR|th+gdxt zAP$7Yl=M#YMs!2i;=n~t+O5V~rhMT)6DvuVH!CyU$3?xDd z!if?aqqC7(rTIju57ob%NG+vViD^~G5iY^Yi4OI2Q%d`2(|8*IA*TF^yHIIzTRjwPWWfX<_v($3MQkZb;~*Bix;QP&>)0F zh&1k(LLsFjr}NZBnT_5GL~9SjAM(hb-$;832Kvo74<2|w+E<`Fq;m*AhwkY|Em%p* z4bnW^B%W*_g+T{XT&<4u!I|@H`va9NopBvs(h++0gpx;s(H>FLGO|0Lgq6Z3E%TpS zvSi7(9)0xDi8(Dt&jA3s%Hdnq7$RXwn>iAR5;6@NGhS6v;;RyGpjrKa$0#f) zc1&KIQ68;ai_tk8X_Hq5LFRBJI^T=vTS^!&pjiot#+yi$bbt_Dlq50$W*XLk%HBT( zfI6aa1CEKqit3J1-8Ku~Sy&$WtqmJCJimSWb~_g`n^OQ9jYg6yfYZM0xsk4+GZ%iy>zgUKF7NI1x=Y0{>F>v;1vC{xy9Ld(c*-!=}E zw&(rcdlxNQ^z|p6c;e70Ysk!%GmcDj%Nc#=EOe5NDheUetk_wcvy3$-xc2fNGrar| zxq_y4z<>_P_2ts~QC;!W*tw`6$d)W!dKVQKdBe~gYXWdV-zHp=WRDP6=ChU0znZj-w_K zohT=cEtMVGjcn^XNYwM?l`B{N>f?_;zG-?9?OkgDjYcEN<%3nL7F>Es;0Jk6>dXPB z(U7qQsdsVHM;=5=J>~3#L}|(R?iz)K0ZLh(ow$s;%6gIQa7QVmbvS2I-=G9mDU|e( zegUO@RIY&W7cf~1XpKaeD8|d#biI(x!b=3Gw7=R;u)-FM?93&tYGK2I{=r}0y?gin z-Lz@bDcv`qPQ^tFg+j~PhP4G0u=4%wvXONE;rT36{PhSesLT&!E4i!tm7Ca?pO8t<|5(0!`Mq1VY}tG=9kbI8KqGEkck!ZYRw^g`9-_Q(tg}ATx^%fq*0VwS3Kt6+)fz(!EDw#WMF$?vrq#|gWwo6UvrQvHN`z2Y zA(5Vs@&Z(l!+CjhzC@z>s7-3ZN(?q8mrqxcHODNY`vY=f#dxjS{EAZQPu_UrjcPUz z(P%W@djK>Vji#5AYt}6M;3b~cfpLab)Z*gvUf{fQwjfbi_~$ySCejE=;;cXhiq@E= z$J@_3dka?iV3KqtUDo^+I7DU%2$eqIkV=762+v0-52*uGK8I8a=@&_KAN5*}s2b6# zB}|4(>G_p!;NSKy{4)yr{E@9Mx<;e%N~Kb1y=MW)nluLFjf17d{WwQoX%`>5=0^yt zI?mX{F00Q#99oDaNn&vZbf7r2BVlX2kb3V~IO|YKA(dpNpS#w|OP#&3I3bZrA^aR@ zg;XBG^HH9M%9W6XeiCmX<5iEOu#D#6D)Gdm2v>iwf6?VPT(hvg>*B`v_-oF&gYPK- z%H^^s6bkLwqSZ?3*PmOy;!MFPw_g7@crII(Yy@%aNFs}kEVku{YL>7O)0~W`jYQPP zVr&8vron6OBD_)&Cj>Szot948P0Q#f%Q2jiY5hq^gj5Kvzz@=h$;+dH075d37 z9Hvkn#`6Sr{2%vI&U-Vxa7FtDT1Xq>YB19}cMetN!H84nL{YPBXm z@Tr?0$+L9R#O`vSjLP|E4l85bl%D+cN#Jh}^nR5)uuN?f+g zK}auc&^U`zQ?Mu%kS=@#Nq`gv3JO^$;*tavhuLDGtQp`SNV`1bgv1@*CvmpN_x(k{ zV7XkL$ks}|y8y@pKU0}f}qL5eIraJ{i!q89!^SJlJ(c5N}C1QHwtaj0w5Y^T}pRKg41av8(oN zV-3zagiuq3J}Hr&KBAOBVm` zmMvQz*|>4z*bF5aH;q=s$<+T(3Vut*r*4kZ-R`l@m+s)WB8*{7H zET(s0&qgX1|5r`!-TYMEQ(rxJ@Zfj0Zr%D;v)Qy=8b6&{-L!Kt*Zx4wf$*6{ z@Zu<7D4-h0@w%;VMLU1=d~ntxUoKd@D3+eDj%cytISK<4IuOTSqK}dwwv@ORa zCS5`(Gso;4);f%Xc>9}(C%zfF*Pq?2Z2YH{O68locJ12HY&PSr`*X8AG6NtTIb_^_3>Kxlb>w1-j>(%D=%Fh2fcklh-`y4_ z)g(QGon<6+x?fpI!=+(oVg=Fu{m3W2nV6Rz+Z&FL+*hquzq^0`{?}WrR@9~0cAC%~ zzpyIk2q31ve$q}^oq+bwBz%q%l8RZ5ZfgjQF+blw8o&AAvv%qD0k3?)vQP#+US~^@ zkdmF_i#Xt&h1BW7(Dr^aA*Mps@NgCP__u7l;Rj>Qkps_;j*kA_*x1;XD2l2;1jJ;U zb(q$NpGG^(;)t0`5Y!21|FI5^6M$|IG?25_PDYV=eaqfhJoNW^cyU6o?96Oil2YVa zmM11J!V7}5_?GR;vqI6T*5T>z+Iana^~S+nuhb?dAFo!cFCVa+s0W$+Izu=%x6a@7)wO`D@?{5bHW}v0^eRx1FqoqByW_0L@*OGw^FUp>)hg-6^ zC=k8LejYe<9lEC%=`2=Anhk?{`hJ%@_3dVB-U6ul1Y1r7t_Kpkjh z0GP}Iy0@{pX+X>d2D4@Va}K~Uv|rBhJjXa^mg~4oFi|F`gVx&Zl}Q^u@G~*wS7fmI zJR>%?E~lO!CN>K5{3Gz#UpAY&x4aUy8V?>ibZA`^MK1%pfCIp2Ciu4IM_u&Ot^Kos zz$~;gX8?$kuz;fha6FpdoinKH(3vH8fDiPL?p}GH@B6o%wJ87obFRBQ|B|^Hz4@VL zI97Qzj^l?9A3nS#Ns=+30fgCMx{hwT`GsAwT_D(L&UyMV6JP-DMC(K+0)Ut$%oMYw z@6$3%(PeqE>oqmaF}Hn#%D^aUtv?{8ynAS9sP6mz{Ra*lc(KuFOlAU4W)ZZTCScbk z^!6fJ(oz|A0ZIm2~kd!8ArX0~W*HVc!PWentUIn!#jnqAKHXjH;1 zTtEFY%wPq~@EN9m?qsT@$K0SX%?jjE1U{S7JsLpA${}X5sJ4~N#@W-Om17;u00c8w z&vZ~bOVCdSfVa~RaI>7-E`f+?laezC$4+~`8yq|BZ#RnrcSljAnE-J|(dwDzb+q># z@tKqD5ggYRrCEFd$8f?keaCDy7mjfaW&o4p$W)I72*-$iW}>J$6aI->fSX1cZiY-z z&Z4b$2H=i2&bDv!9*@U@k0x|xamI5d{NuKOqqHE8rM2QHnt!zS9WDLmI7OAa1Td#+6~sHPbw>e4oCfXZD2|wSQt+p1;%|q_W^$177|!>2`~attQr004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x00(qQO+^Ra0Ur_-G!q36oB#kPQAtEWRCwC$oq3#GS9RyV zcX_q-s?FML%bUR1vgH+H1A!z48^V~GFiAeige4&iNenS0ArSHbl#qmw1STYeNyxAS z0|5pIOmHR!2sdnMLp2}w3=M!?!AxmyaxD;K4S2zkZ8xv4i4 zvn3%y^BAcGA&NqX9bo^)82fc0sAwUC1H7YBss6!Sp)gu1modiltSbr_A_K<;ehgnI z4n-~AH;TelAfgv(${!9$qL#N8Nh!0Lboz#&Ty9g!an7~yKQxyAIy*P_`zKDF8ZDR0 zT`LF!glNEuI3Ues`>kLpQ3Q$6p?HM6Au@O2XBtSS)1#}FFaQ1Fp`inMwothB_{79` zrBaF1cqgE=GuUm8TeI4p`CH0{7`fOgGhqrfX`$lHAxaO;}wel^sb;;iyOwBxo;i*!oFf~1Wr7o6Axqb#9 zh|vx?LgtIl!qm73 z@f@bUW(n0Vn0f)p^E`AI#=A8hkQyRS^fnixj-)9`)fH4VE{U zo_hgFL#GT~*$oAps1tQ2tFo}|E{5uAFB1k#ae<_RGIwYyanSTL*=MRMOIme>DH{e% zz3in<>P-h_XsWhLTDXCUo3Gz8XBx#-UF~JVf+@UI3O2gy#dSZx85C%e0*vi+eqy;Oh_hrcM`8ieX<&eu!5`N^$PN!PpBoG}dZ zaq~pUx1ih-2TD?X3Ja<@s;gVx@IaccOZSpENdYIa8XFK;25n9yv6^r1dl*1PTgLYS zN_Vg|OC;TCKzsX#mL6!9f~_5fho~Uy7f^A5(wwIgkRoqoB&y0{0%j=yr620Ve^_8e z1ys0|S2(HE$5uxFzzXVos4@cuOn7g4JNaXoT&zz!A8`QH4NS?{L6NsI5|Bs{L6Zbv z7Q=T`U*?9Pcpm8XIzp6*2&R64)MYE9^S~qxn!Z9?&k%i)gQz|`Ad?hd3# zHc*nli9YY~Rn1bS{yfmlWqRbTM=-{eN`2WtJ=NGiK$ARR7E4<7yEDIVNe;bitwDsK|y7L$?+u1|jRw3&~9 z^bkM_wt*H2IHxO4Xem(=G0@2dkp-l60g?{N*sXym1JO^uxcL{Wu5Og%GXC@gk%b46 zZcgY{VHl?R$rCkir*H03kW$GWWnKzFWNm3pK+>JS(|HU+L@ai%AacHi9k*C(G1ejw z2q6&S%-E$*b_u_jL1Y1G*r2V9&J0N*K*&a(PT|!4YSK183YD*G{yK94tTBM$d1J#& zl**hemQhlINN&Z`i`;ci)ii%`G|!Pj$p=x}fS&HdiHo;{PfB%l4f#b7m z-#g2@uiwk+q1nU%(tU)$cu;YnR9TC&Yc2B7bDZrspPdlw&lk{ACKFeh6x#{x z!nfD6II1fgkkp#Ml9Dc)=>6I^T`T_uTUqOJBg6F0)Ja#0*rj;|S z8m=ZXc%8^v@C;O4D3(W%Pj4o*rwU%ZU4b=*wWT?(SU$!+;c%!}rrE2%@Yb}!h|Z(| z8@0YL`I81x6M({>&QMZtWG=%)hjKiB#Vl(_!`*NemFnQBcu=V1u+LmUb~jM1EZFj~ z*0QoR$JOIQ9CFg^FO*}oIY{akU{T8V3#RUAP@e#`=Ddrx#0lw8QgX7C<$;4aE*P8R z-0@^5tRZB6Ru3mY1 zrK!~$F5D877zR{Q3Z^Pq?msxh*~10SUr|aV#N1ji*0*~KRfp=K%NTlM2w5!ljA6IH zP`SXhLn(^6VRp@xe1A~~b+*)KPST$UTwuytV-sNKcA5&R-oku+S8%2gr?$Ud51JfIEI z7Zmio1wcutGQT0PLi5PcVNT7$)$3--IGuO>z1kMV#MEk1PriVWqeY01_8eLI6^+^TY^8rX*Ld%aco)w(`EsQ}N*B%yQK3 z%UE`}47T&-=Y0;V@3pQJxpMgk`_LRLmb!Ya7mEhj?jTfGJx!f;?gd*qWrzc+_H-!~ zPfiT;%!KBuwRuJ|rj}>kQFt;x275NM@?aH=JFtOl=5J-W$kk&*9M)-`DV9SGbT>NP z+Zg@U88^ix;Vv=%$kTH96X#7V1@<7n=@L&}_kT)FiKnaPE^0Y3^2jO+@x1L@rvRF1yue5F?C7dH~VcXN^ zaOaUzJXG{pui$)@$XJ9T1?+S!@0)Vj=YgX%La9V1WU*9D((T4oYk{tie$`bsvUoVB zR&n*g_p?#HlxL0MebX-AESk0pWCueM*ymaPaI(siE@-6?vcFeO28~a@DyzM&uz3f; zcelDj-w%}3%^4O;*%ZjltmTHvZ(&ymp5z`rI9+ARXntu(;V_tJSqU8REPpgrWk(ep zMxUAA0I5JZpdCS_Xt73Mv4goEC~CQ?DTA;K z71wgZ@wXwDJ3OJj!$)UaCXMB-!UezHV<%|v6=@+{p?)R;MF?hhZlpYNeYz$Cv9=Ebx;`6!!=-x z7}CQYsUd?iWN@+`I%Ci&gLEuX8-%nVZNsT51RQ!`gr~PzsudTdREw0Z-)f>46FhaQ z+A;#Ymg9$jyU7r7Y4hNM#Yqf|w7cYndA> zTEJ-mOihzXIJrB^_S>_}PP%BVef8CLI4tmTg+T$QIlXGiSPTM*5E5lIIkSQnPW~Et zB*Tv3FLOt!$h5J%Z`|QREr_$ZApjTnRNm$DvmVqXPY1QtG{+%tk!a0pJ4p)F3W|p6 z$k}-*(FBatkip3roUEyV!XULpYU@jiKv0MU?OWT#V`D^mjG2V|;S~3OHp|oz59R2t z#UfoW6*z`5#J)=f+ay#D0xp)VQ_1B|fgo@fP-&%8id zMVb+=oc$#nlV$tZr+L_&=3S>Myno!`#TgY^oeAI@b1r{1U8N)xS}OnRWdvqH^nh)V z6gp)apm1`Q^iUfph65x;+M*oarVz^dFC?jft=*1UOi+X-0bW`1@K>@t`y+|c%2)Rd zX}!??fe;ss2T{K(HcwPBm>N^9%TtLWO^(Y8uS44`+s6K$r_2QJnXK~OG0ROk-BxNL zN#NTBmk&=>m{XEWMv}^knuPFtTcQUjbfyl9n(9!#`lw5xKu|QB7z;qL#K{Pr_}47E z@Ad)XgGV>)#7<}Hbd)ywy3w^s4|t-A6<*-z3Iqs+LzYX6ufefdwvPQRM>xv+r>aaD z%g+rvD1s+P0^6<27cS0o&U(Y8D+R-=1i5iRYRH!=vIYt%@mC&o)dAf>Va^~xL?9tx z-}f>+`V9wTBw8z!)~)KR1BC`qVmtH}jBqh{9VLl>Ws5?S=J};p;MgqRAO9Gq|69~)yhu))#31JdTp->{lh2@{%rzYRcD(^xHQt&r39v_`5Giz+4 z0}9w(@p#`vk-d7DlcWEchyUe#PVPygw6DYKYUTfvPeQ`vvkv!vHpAR0iE@0kRhK+r zY%#U!ob$JIN=!N~sFsT3<+ynEX3}=JcPE7a1f$+MR#h(IWa=sM`XrB(EyrDtE7FQl zC3wy?{K3gG50(uhp#T=ws}vSe~tV ztW$!IOjY>zqT!lNIbM3h3byk?W|p5vC9{r+dk$h}^Q^kapqxdUqa{m1K(!!w;2*O* z^MFD*K6qr^M;2f-cRZER4xl&yiL#`(fcDZ{JbM#aGajMSK8S|w3f5FEWy(3ojB}WM zRm*paE<4NcybCjY^u0M`I?cBxuctb^jLPr|+|hHG+WE58__ z6vvI?+_Tes=y!5#zRZwPo<9(oVaSd!yY>Z~dhr|C_4viy`&TI@pH+w-S9NA#&v(=8 zyu$%xlhlz5wkZOOw;yefTA=xP+?XMQ!6RPqybmHx){db~8Y?90&PwsYcjkD>wf?OP z8OIg2;_fSiO;{pLiqBp4`OUf0^RY4reJ{WVR?IBpm!viif`J;8rA3&pQf( zR$O5)A;V@2^E_;t#lwh7EPty&P?dB1VE7X}mHPoJ#!|fh)(o$FN$pAx1Zl@ZS{Iat zRL%!cc7)Pd7c%woH*ri{$vq!)*mGA3Ys8NQkc5Pjdo}leHbrs9_cSZj6c5$qHu`Q6 z1F}Bpi_sCy@4dz0mK$x$=4aY5NLvLV5K2QT+W=8z z(4v9>1VBfd4p}KoRqdGTpB?)LL!vHinRZA6cSG=W?u5k7$?o{ zd+uiTBwYWiRaRW!{l@{30Iwq0@xN2-zh5DhoCi;jEsC%p>bV=AB&eW1*U+uqei2em@NzuuSd^_Ui0KhhVfB3EmXVVD4Znz|(;RsCP6{7^>wiUX z&eiUMeXui6><_g~bgCAc0^{>fgC{?60Xh zMSlJ!hj+dSvYF1loRqd`;Vz8&1Ap*z$~Uev!`RVP%w7Kr6s~Dob!tHPDlL_1*hR&wUyF4z4FB*;JpA=M`4cIwdqb7sv(6mjT1c2Wpt$c-De}i9 zDy5K8Ba~dQ;_`=K2^!J8k0#l!5=bC8uJ&@*=!cj{9pEKbr1-BMy!)=*hQr)UyV&=8TtP0?7erA*^?=5cypCi8<)OuEd&%NCEGre;^aP6lRPTW z)7+SgKI|Xp;faoC2X*S6ZTtgpMX&@C2qxuo+%@_kj;8l=)x{}3c$?zf_2Iw+pp_+6 z2T+IzNg*XTHM=M~O8K%Infa-=V8&K)WGCG95r=2C={kc;lLS;tf(O5t;=m4t)HS=O z#pa3bEbMyhh%TP!I8vvZ%Cc=wTv1tIod^VZag4i0{+t7uN4ewzhd+6T;(6zX2M>T& zkXH3&e8}+^613wx9Wx_TE_oUG8-D|{Vja^5;hv9c9>2rERF|f4Z9RDGn-070_6=n} zu15!~xVqF4(TNCMJkik_=)zRMBw7|qAc3GDCb@g$Bkap=SSD zM2jF34bOE3GDB3Je*-f&y$x^m21?U#-)9xuZ`YJ(mzL*R2-tg%!z15xuvWG>3VrG0 zk8orByM5Usf=KpLL7kbv=K*r3nVKb#Ku~6udx!p--MPD2zgqK0Z&zG*MRf3BK}pD{ zx(bUv8$xM+BQ~9*dcif!+67aXdk)(z5L zqB02(g%0P4#6Z_~u=-NL6J@|ZZ(XIt)}g;+=g=LjT(0@RZ)$$>I%1z|RuZz>^`qcp zdIN=2pwp1f;%?Z?%&Xpkd(I{V@bp&2T_15c_T=dWk&rO4PjcU9914@oM{$}~T%zs{ zi#Uf#gmZK@qB=EfyD-{XmoI@V9#!toewK%Zzsl&C;JyD{^Xiw;mls${$Y>92@AtcQ zqj{%e25N#m{E$jqy5qx`(Rgj79G zQbd(Ty+9Pvi0;Hx$Ape`xuV3Q>2I)g$2=Op*vuG#ikhtkX<7}o+k7j}G2^VAR8Hb?_bkP>muI)NyG zaE_>qbe=VV;;8c^hejX8WQx4?bq;U&1%abUW&=|+NuZ=QL~9_jLSUAkP5za)VpCbN zkA55Dx;%D=Ab&z|{jXFRUq8U@4-32Q)I9brjUSTQy1Tw$yP@_43Umt6dgo8$xeYH( zLwcfvd(z=?2qPLwdIpr?^{;XGtv5(ADH5D)$U6S3!IR!n7gAvn*zwiO-uPCmlOgxe zx4^Bif16@%0$%)UE^9AMe)~g6*ngkm!7n*@Rn-(27G}j|`vy_a_D6`A=)k9u!P6`T zt?@tqmm3xDdb4CGOX8su4HY(k0~kU|e=~M?1#>_3X69b>bBI(1sRYOO`YW`(_bQUS z^ifDS_N2e!D&=LX-P5$Y9w%C&n{nNXx@zn|eb@ma*4j|&%vy`*dEE43$shcNVr-P; zXBfyjUSG@jUgR6s8k-wu?kC>B>`Q(bGDAou$e)mW?-QCGUsrhL{*7xPVfwIO>;H0? zK3ZF@OW)(%sq(0y$DNGpUWRp-Raij5i1to9v({Q%*YMJ-C0pL6ShbwQ->Kq{IOH4` z(dP=RXEU`OeX=7IUi^#9zWi4qH;R&iNueW&Jva~UXj05Xn24#0@+ME;I`n$lITVdmzyV@6jYr60uq z>;sDL{Ds4zMY9L)K&WK9?iMJg(WK7#b3T zn^<8vFg?PT4!#ICGmg-yMSBN^s9nnJtvnZZ<#{-VccTka2qme^2@XCYu@=s{$f7#9 zE`)?bj|sm2cPUCUwacCwXV*nbY)`jtMS1gJr{|*S|JJ9O>v`f7i$ih>K8$HE7s$#z6j^&Uew$vkOHp? zM;;fW2ZB;fd5jxRiO9i`MVgp*!W0Qo%l6YY(!p}Ov3oMTU; z^Cv*~66nO!f{AAYE6=y&miq%PB+N~~_dcOHx=YVr_|)aD!nP*6q=`_^I_B*lh1BwO z3nfsn0Tn%3Er5$!!L6t8;sdQ0oL_Gp&gei&8bmDs8XS0thLu0o_vp9E@R36mSAvSBl7<`}7y&(&gf1eJo|;KP!m zPb#dD^U^2Ag-@+%gf0^oMDu%L0|L+dW6}5~3F*7yrBY}|aqyVscRw`C7r$A?OKAVd zksc}Oh1<|sBobK6vNhy?=65J=z8NVMN=cNK7+1}oAeG@ZNV|=@pBBDM$9in{hk-Uw z=YstPpEWN%MeR;#|L?h?W$TZsq#eQKFVN_I-e4%8c5qF=O54V%mFVlr%Kp)04-Sr)do#D z6khc6{pSEwenaCmM*P zFl1zPAQ6ellLetDT>V;RUio&s+&Gxi527&7dY%gl_pJYpi2x8urO;Y(-wv1GeE$sF zAFhT5krbA!u2MT160Xo`C7;LQ=9`#$)oTKolA)fict(4;F?FeN7-}sS)qekVgKdqIM)lPQvefIL|+RwTSD{8AQU8Qx&KO zm7Qs14VCk*V)~Z%P+qffQ9%?16S3bH*6)%q%m6g(A}Jep-e@hEoDqEBV{?4`ALl5Q zZATDg_54k(XCQ>Y8mi}9%JePorF8bDL_iew_)b_26#9CXQ2mXh3d41L-d7|Vc9GVS zLP_x9PZs#I|2ao~&IVd@z63f`jOY$z4DQ;Cn7-vbl+L@lZxHn}6tS*%Nm}eh?Qorh zkn^ton!~Z`Nj~v~BJcZXo{3X!4o6X!K#LbxKQJ-|Z{-H2Zh1F_jW=LTP@nqS_>McL zJq{KoEAFCpbm8;nWxl`hXpr3gjWWOcr+E$?Y4sIlRmFd6&!CU?ECyMF8DGoHYkrr) zB|lkP8=M{xHB--{&9VC%SYLleU#qXk4|}Me4gcnMEBw|U&hXTJ*MEU3kkeHkKxaC5 z8Utc5BdeHs^*fon{AMg7G>8&qBa9y?dDWGS@1hQ(`ui_rZG|S4;_hv}!|{>FeTQR4 zyNiCbXW(QFHaAZGrnfPB&1x&+?%XY5q|Z_S&paP(M_ZcWiVht0*#2;Z zO&2JphZ z^(WDD8xtu!7n&!n{<$`B^PejSx5K1*l{OI6>#l-v5uOhm(F~+Iz^p;kz$a>e6ph_@ zTYJ_j5gIYcqJDdtgB3F9mLJ+t)#;rV3!n9lsI}H2&cEsg+y1cE1P(%V#kzyK?dA{v z0HeVe59@hU(?gh{zuP${OEJ-E<@uxq zc^gjyC1TJdMvqjCr^>>#9jL;>PGB4w1bFHlvj9>yjt-@&1(+<r*>~xKyFzJwM@Y3adX)(#{Jbg+g6%*DFuIs;d+8QFy46sJ%1=1E~3!>!288 z!ThFRAnT&`xpty9$SevsVe{7wi;h6CUD84w(CAW6d{x$~e%(EDIHrRJEU4j53`J=- zEC_{cTHZE|iES9c^tPWn<*5U!ogWc=db6GXq(Br+;w%JFs8T(^8_AK_Z@hHBWy4;g z)Dp1YwA^pGm8)M#F9LpSjh7xyr#M%zL>ZItp*Zo%hIAxG>PK_Qz8Fm+x)B0YMf+qA zf|}|M3#4ez!`BmKQPYdMVWFoH-L3KDw?8^P@AKOl!F~u;U1)>ESD8t?9l3dolJtl6 zYj}5zNDN{;)QANz817l;4n}!iPqP8`Pp5p{R7&h}?b4_RztNtD%Nth;MNwliI5FHH z4w~YE2k5G*q5>uipJ9QNWM7lyzjvPC=Q66%)FoG_a)S$vt)(p@a3a-T<0~l;Ed?nA zF%K5TorHcTFBEt>q%FobIaGTwL6pQ8^;<4zM?&|?H0X}>AE1`pghXjJjNRGMKo!xA z*SPytUHM3WdP<MPu!gm16s^8hDXFWwuQ=r;NnCv3Tne26~!8gmTWa)sz)?>v!J z*iv#hcCvZG0w*a73#0`U14Qw8OwR6Urk)p#hhQychx+S4Cz~e>cm_qWKoqCniS;RZ z!S=Qy2i3=7Ghyiy=OuSZOP#pwEa0)e#822!yI6ALT4PGRz5$Er$?Zy^9B0Y&BhDu4>7+$-0t>LllnT?6cdGK-Y*DAvt93ea zo)99EQQbEq5y}NQ43qR&N2#xuk=gexsU0F47Y4oeKF<}dT(q{VHRa;;sqai4diK-G z8Z%wW&lCWuQ>jrQRZa*I;!fPtu*B~}`V3yBG4FH&9^~0~ zeaLzEV4DCRJaO7Mnf|Vt+aYI@3k8z@da6mQW_OO9jJe$Qna&iPj{I9ePRm-Q_B)Aq z`d-bD0&|F8Z3{e}Mo z|EH_}qv!wm#N2D1wyLVCwYBwGC>bPfjVj2Dg2|*JD5GbupF8V%Zxd_|uH*ePhucOi z94+T%Pu=?{86b8!Y!bsURr=qkeJ8!oM)dZ4kLlADEv};P2cD1Q_rM?fT@Y_V(hc%I zPVlxDeAbqywz3rW8hXq-=_XA6lg~P`UO;>_ad;2?7mH=EywPigY6}xfd9~WRpD0n> zzy&&6NwoW8mvR?$cLq0hB=MTV#qFT0Nw@B6;A%KoThRyZQo~b$xvN}HPw)Nc{`M{N zxPM>^Y_qe4vh98FoT@f&m9zB}42s7AN&w*7J!i?5e|&Tn&fsr#SF5|NtJh}c(xy`< zniv9gLk#L=(#Yp(xuZ`8uzT;<2&4`4&~M=NdVF)p?LJAZn^zU6f!iSjUcW27sRy*! zZKQtv{2E{c{ldB#mMEU{%gd}KjgDVWwp@6csAqZ{DR z;PL@gj^yk7Z5Uvn4_<7TA7Mt-hQZ_2#WGf3sn3`g#fpyb9Ndf(q8zLlqBN{H469-( zRN&e%S>Qi-d^M11h;0__y(z(uvq-F-W685Fn(PJe+yeO3O@zE_epkURjE*XKV-*_D zqv*!>^!q8lfTrvI@NQrLG~c(j-d*7Nc!P+#pI`s!x4r8*Z9UPeiGy(jg7*(Cw8Zq; z1icI|`h9xTy)P&Rye{)oDN+`AE59mCPPuxPq zXuCa96qv?~Yq=xd*3l6aE8)>HnZK;6^q#AdO|Cx<$ygm;<`atp_}JMWEn+1Oc3sBx zK3wj@?it5CV$=sKPx%Vck({mi(4=}^whs6A_xWcub1m{h!FXWws0Mir?gz(Xzph&4 zeWtrs<$ilZpip-b>T->%00yuXl>>pnQnQe2Y7+itxept%=+nl?VpKT}jpK$FYMR?^ zCg)$Dyg{5p|V0;RUYnsR6N;)H2kB1DR?S*YtHd4j0_} zsF7f%g)kF@Kl7T$Yv!SnN0{xMMIA=A_*fk~-b_osH>0+FC&YOjUOt`{Wd-ZM`YkfE zUK#uOVfW*>IMOue9G02$a5~^*n8$R>X>=U2odgql zyaz_bB_wmfyc&!cx1yF69HI4J8Q0Mf0tjWLL?BN@~gW|>}L z!fc$JS69zl(VsW0`7e_*{)&MAEDLqBZaugW@ND*&{`tyFHJ!Vfr>34(qy`uloO`#yzXZxJxg&_YCjn4}G^=XgoJ*BlY zcm&4;)Et-@h!uoyCG{nXd$R3wAk{ywAV7G2C)d{|)W74a&hy%@SADpG%E}+|6{;_L zE|g1n{-+2B=^rQAP5Np!JeAnZPOIJhK8kgARt*0p*)c}525s@yI|g~efdv@UW(kEJ z>?5jaBF21EYe_F%HaS#-j`dXDf0#A?IZZnS-RZGwm=x(yc@Pry$%*6X%^mK|w|uTW zBtl*1=e`i%e6u&RPf~v+SY763q5WGgFpE>qS-iI=DfBsZegdj?MrZ^adCi#{f9@-f zl;nwZQo!I(5O1CK^TLk*^U>+)sZZZIn^H575p?@8iGPgt>oQ)hcQ*^mVxxkbe0`4A z`6#|sTZ`H%9cBSRwyDaIp*T5x7o!M2Rg9!aDyF=dg{wXC7QIv{Z2-;2L-vSpdx&A> zkF_wESWI};@^phk;h)qk2CT(1Q|U0M&RVh4U$m%|0q>iY=^r1t5}@PKs@gYW4}SpU%H zO78JUAw-3cVFRYdNoL9FrM9-$3m~o+h|jqpN>|;bvEgOc#(Z78^|^iV_lI*D)%u?l zolkWc09g-Pcw3*Wr5BlN<*1|0?Cj?QbLNAksA7MWv6!T1bgh?i#)dVma%m8*=^{N{6t+$2j*Ka?)W5a2!`E=xxWd#n#0UrtRv-#yz$r)}^I{mU%MP^bQRG}Ot2I;2hE$KD?tm?K%6SeMp- zd6RtvvZ%a%P4BewCiXo$1D6?mrQ#t?FBDk-m+?%B(Hf1)=y4?SPm7!W) z-@kEZpRz%NV}4``Peb@XGSGdrZW<*8WlZ}uwM;WenMoG&80C~wIP+QN!`Q3*++4wo9J1otO^%OOwRws$*;{$jnjQH`ruc zb@ioG(rTuJl1X$fzWGjJ*R(ZCn0cAuoDEecD%J$T4Za-dD3;?s)rOdhW{VPjv1-g& zW#Kb!^v6#}Udr@8CEehojgr+qeg4gLk{3Fa%_^p_TRtBJKukF?QAYm;uFTQH|J9d|d-}HifKB9T~Z~A2c)f zr5l&-%2i8w)Ax6O!Js}`M-f*%R^&3Z;;9il#Y;h#VI-geim8Rq>xX5+StqcCe<*4P zKRX^-L74sEA`)wK2?nBGwe9s5D^kT=(^goABqk6gK__4{!vaua2lq((U46TWUfdpb zROc{<$$FAr?6aNl*^^mH#nTVau>4>C&b@71KAFtveGCn+WCME+cG}wvZ&kXQ41}Xl zQ^0RdDlBqhf@@;}Tc%CK zlyv6m2M@f0=)D&q>``e9YvT-=(~o1v2+q>CpBN9z4L<%njn^cTdr_6)6s4Oc0U+_V z+;qd7;on(|@ImNwD7cOx>zKW{*Q?nhB4c!aT`x`T-SA^wzddmRKhxd2w*0rjD*8wh zo@njt1NL|4v3l8S+B|rcayP6wDq~5^evn5>RswKuENceWARa`CKrPXxb!z^M@U%^e zNbjFM^06Y>@8@U)slZae-V#O@+=A~C2gi5P5Sh1PV>$}(8A=DJhHiHB2a{r#u2~So zd-yEYJKz6XzEtcrK0Rvtw7!1j@A_yLYp6;QwhEsgkY~K>X8?Hz5i9EF6;2t%CPE!F z2);T8qisx>-}9{K^>TrYr;;{e9u<5YyT4lNbNC9dY1{bhf)o-JqWLzlf$~vtygZ}- zOORDl@shkpo+_=p#-p)ptja(#DpC>pbjUIYTT%Z;|C&tjTOOq_{_|gRNg!nmzrM;G zPlR>w$Q(_u1bKZ>g(U%Ldl6^(s~ko(T`2J1v?`CH@Uy0O{O%9_$#jYx>tlTD>B>g? zkygDG5Sbs~1lYrLP4Y?LXkp-CH1uFI^FFgyQc2cLwr5Evxj!P^gebdf zdE2H0?_M5?kp!N1%k!>rEvNLfisw!M`4Cs1&s$TP+xJ69h8=r@XS4?1KMiu%v#76y zaSEw7ot&gi_)0@2=|+9UM8?8^vpMuX2&Yzyf_>`zjM4$Hl6xeH$iT50umsN<7aoV?hNO-nMy-^P#tD9AQcdJLhp7*=)iMpMI8uc<%=_K%a&tqyCoc8P1hlpk9v~Flgbw_+ZTePkT z2CF2c*Lv+N0A$fo_dfSvpF=`J@Dc&M}k>86>P7cZ!YHFTAL~&I1ca1L8nn)%xDq7|2Et1@fpp z8s1+>UB4{4sMYf)bvXzON#Mp8a(Z!54WlAgjiYzYvNNi#*Lo0^!DeU{_ToFNbtP}bB6Q0TdRXw8Bp{#Lhd+Y~2~*~Y z6p8>-IJ6}UU^z^9t_xLIF2e9fzm52|ZX824xkN@2@rfUJoHW=jx?5D zHnA)xg%@ZHfp!#M+h~F%;!iX@quPl^q)Tq;HRC#Q*mNTf0YLqSA z&a!lYoo!^3ro4cQKrVC8Lu5&kxSME}Vu@1C*zV_(&aWBd8Ouob0c{Xr4K+uL`>sSY z=%W-#l<~V8V1mEe&$Ex_?ZxbP;0G^&DxV(BmRhz4ZpCpcna4l~m1TiKoQeV9rUEs^ zazxCkX_0P~vrn8lZMUsm_Uma3t+u7$^FvZ(V88Dp0XMt1P(wn;+p~wddj7k&#_4Z& zd}P?WhkCzX<=)b0w?U(#bJIJ-D^ArWC&)MQ)LnusT!ozz&78mKm3EiY+SELew5i={ zIsDQ>quM!LDT?6~TUXy6lwWnK^?Y73hr zSuhM<8I^cvnfB_iz_>u|tt^HVj%w`vhkW*&K3mYjjGK$Xk0f^&vn?GCP8OI~tCGyV zT8xwcv)lcAjc??m@>A9iO?Ra2?~!fH*qQm*NNYRY7`befz&(Zbc2Hx@&GdpcYTu1% zO@?5Sf5Syfz&9NEd{uBQ!Gx?qi9iMJ3q@UazLvJKzg`fqvTbTtmlQ@uP=DkepOA=q zw}VlZ9`j%Fm1pYmv=-P9uq$6oqcYS`*s{B|THqA1MN-olZ8`gIM;P_Q$9vg+3Gi9P zB-se&Ff+D)(EBhV-ti zE6o9xNE=nN#b$bI&Sv8<#u4y7EiLpD`UTp??{ZYgQAtGW%wuC!UJ z;sCYXCYMqu>3L5KWT|;z=}8Qz_(d~$FN|YOwZlE~O1&3*-w;3;8|I_vRrBq&;Dc8L{7-g?;(Myi~pEgAuOtPzq3ib5znQSna z@tB1%yhlCd{UpdiD1iqsokPRKWYa%M@1?-g#hyKPt!^i`Jby`}=w<%0{tN2;(JYF) zHarB~CHo_4oA__ykj_!#FFMc>au6{upMbms%U6_3yL1U+`jM z2I(Rv=Y30egp$(00IR1eiP>I7?EHWWLv#CK#=Om%U?lyo4*QvN4> zaDt4d-?jy@uH)O4cI#z?e@6=SrE=#C9jqqo_^Q%_p=a~BXvzlU0*9>)-EcudHFa7~ zZDSyvu6cE%76Z^x9H(dw68A0Hia7dDNh|0vSXZ%E=5Jf$`yq{1HA*v8zrlD-Ds`O= zv8*M%;S#kdj?OmnXM+fJ?z+OjCuT^hTq&l)5$NENG)%Fd&4b+G8gbYJ-2PtFvWj); zuP{tntBb<)g8GsaPKM{@z10&;eB5>0IF*bH4Ac%*=)~?7=p{Sl( zT%^YZTtYB0ODf{Y8YW0KFsed{Wl#8~gn2ik%V?zWFX4_m#M`u;b87f@(x@6ZYW(GJ zN(@)hdgHRR3U!Vl!(2ojR?rD`)M2$xl9=5e=K(EpnR&Xxtsh!ifu#_Z^uk@S8+~7q zwksI#%62*Osm#_Uyiy~Sl@Up>+vR4G(%TyCgL`(=Fpjb!KFXm5merN8B6PmT1$%s+ z+ze)FX|OfHrz`8B5xr37=rS7x3$IpAmp@!G9I(4w3!a8-iywRWX1;#*NGNcN{{C88 zx$^(W?{$1*_tEL`GGK^z&gGAxjwJ7H=Vd&iCI*&~P{ctN_T^vfeqMXZ6yeIQE@9^pv{2ZCrS7z*Rwe4+%|Y-IA;=g? zLk$EOb!gRzOQ40Au2MqTdp%4DJ`b@Twh7n#x~9H7ZO(BZBs`xcPzzYevHEQKo?r?U z5?Pnl-)9$GcKpxITnri{HQ9l#wR?#deA~+}xVkYC6($Gu+|E8`n2C^L*im?~pz;9!+L4Qmpawc}27lut%zD zMn&B=r>=z?p$2&CQ^?qhrk>e>8WExMUU%+s_xyJhp&s*T zqE&GVzf~icOo~!EGn{#T5~T8w)5uUdnYRNF6j`F~wg~9p!Wg#}+8|bdE}NNDMO_we z_2&FQtN>pY7wBp9K-{VBTl(rw0qLeQPkrUpn7)eMNSg^CuTXw)ngQwj zSmW{#a7Qkppd$4BH1S8Lf=Kv(Fax85NAIT;oT@~1s;z0W(lUMvnCm8AoJf;b<*%!C zPTS9-Df)kX@`~`D2G_uahkw zlH1Qgye*qYnEw2vq{RU2n#A;LN8%BRkoeueXXltDa8<`1QY|A8!o~eU%;)sZ5vEVs z6Z>Ig@1QbCRJqOV;(GNR@j4ht-qPY01sH#aXKOg~zK)O#xZKtlLM1A}DWWtiBIQr{ z9PIoeYj`}3r5A-bSQIfAKD_!4@4;!+QQx3!8?n4LxuaTL{5%u@|C+`EfGfgdNQyf@lw*hHXA{^32S&mSOzt=IW4i zI`%@QoJHwS4AE?ai>lz4!*=3*rkQ44k-PjrH~?obu9KZW$-p*UZrRvoJuW>z%b%_H z_gnTr*%|VR${F-P>lU3Z6J1WduJgBAjqxAFY%*+wlGfHGJO@7nhtZaDFD?YeC`(1< z8<84tu%atsl`BsCC}bcRS|9x&8~+{0F7({eT;&9y96lI$!{sVv(ZEvtbP-A}qtQT? zCifj^(&MML?m(VKb4>z@^udeSG);=F6KHaAGBJPbs7t{E<+bzm)vE6v{I!{Uh_+=* zz8nIB%viCmkq;sBTfVeB0`+_@jx2Pd0~_yWGqg%&#Tt|Nd& z`!+wnYJ+^aww>lu0d{kffVx4W6cHQwBG&5sT|8%oY0fq4V`XMkgam7xz;W`|_9K3= z8`wM2^3Ql@%6V;p!vKK0o@ASPF_$yCZmaa~y$iJv^e|p~)mQjgD3t({^f7zw87?A@ zRUe#p)sIfgmTHs1Yk$Xl4uQs~K^;LrwF@rEP}l3sWkB43-C?*uG7*a%NJI!QmRJP* z_~%vqU6aqb&oL54UweK1%|^Km;Q|io^Ikb->~fSf z%ZNZ<$Y|BZ+LBk&ZE;!nulqX z%+Rfa@10i*B2xRIp&w+7Hmn7T7p>vdOB2O3R@=M=umZG2^n-uxU9hGB zLEa08mJg~;3Jc%gw_h8MlpnM2UGDqVE!h-&V8(DDl)ARuQ?1S3o*M4$SJk$3uqdd_ zZnjDGnQ01LxpmY4X)30aCVhh8R8_pIOa8&P7;DeHSPu0Sv{V&_g7L8Yixu{{UFi(M zIQ*77OpnZs1Xv4Bx>`BYr&Dbx$;s*=yuBAX6$MU?x09OGPF0P6oXX6lzzB%yl(B2T zSRbbTC5-XrY4d%2_27XXMD<_N6p52 z`{J2qc~QM@H(i6byM=nmF6K-$r3@6t8RAyc5MH5D&UT-p=V)+UozFZ^DB+f-Y~TBI zKZK;TFuSUlVTyol*`zzK6!3Wj35G7RHARnQCP{Be(tfS}2gQB_d8`M>ppJow8}0vx z(y7;f&|Tj2?|pLt4bBB>>Uw5+`ipTij=<+^ZF8mxq`?o2En>a z9Gz68Xx?%GB6N^Cb`Hm93z4wS*39DVhl;WmQU~qr%3`HnPsBH3D1*d2o(JFE{;@!g z5SK#O-E0@j1y?F2*k_5HZCKK)Yw;ZY#qv36j-jY(fcLOW4)NqSi2ntB-&lE?-2-4D za(EMV?O@z}+g`CYXXX37Z=n@e&wDM37HIL(vaZaPA(+t~T0Tl-gMa$&TDA1Ln~B}- zHUjdE@Coal*Wh~&eQ!H!8g^wGvA~``C*bj;6?FD0kr5*usIn2C%T3Jq+3kWIfkT{Y zKaYPvaC|J{mz6b`0pjUHlrpq8#}4d~wlND~Rlv>Qi_*IIT{S4y_(BOc1_niB&Lc`s z9;B+`*cP~;<~dvbl$G?Iit3bItI$$d_RyJ!=N}&=oS3<*G`R7$Kl2-3XFpMIV zg*<3iZH&wa=$|vlQ4w*w+kS3I#^HAWAhN3$1Cf1}J zJ-l#!M`*~Y1@kmz8l&nQb2=5Xxj{u+;^mmdA(2X=@XV7O9~sd=8d&pC>SzsN-~)ft zLD2|GgwC6=RAF8DYp_3d#prqdY;*nWY;A|f`;BpkVRpX5=Q%5X#z&^R`62*-)AnC3 zKsDe$8c82twy9hD{4L8jV5@Br^5Y(S`4*9$M<9)i6gzj%Y2a`rj83}2I_RG9i%VKx znLzed;Guer#mF?~!`Jrc@kE9nTAu*6=Jip$$jw#+d`v}7LsIiBLnfCL$D$!~PQctr zJWiT`rY1j!@@W0NgL#wJ*I6QFBinhIr&vhVonb`OWP?!&wzT7c zm^CWZv~x7VKVpGt?havXy?b?)YJ70vI%<@vO4hT=)~K_Q`=3Xtlc#nAW)oe|gp53% zj0~nE>PZEzpggX~zhww=HJD72Q&dcxNQ$={+exViT)?(CPGnjdm>3WjVBL6xMZJz4 zml>&y-e49AefHPo6mH1(ewzDT!Taz)9&Ph06U-7mH- zB9}_dW7E#zrk!oJ)I;Tl6$(sqrvN?Al_DckXfA;O0r|#-TW9NiXO&0ynaI$dZzL)PXcUL!Dfn^JN$&7(o>U`! z150=^yWyjs}cCJ8v}3mnTYt`S>|;JqEMM3U5&VOb>0iE z{)*Shl2}2MhLYEi&#lWkS_D^YIlfG6 zZ+sQ#{5W>HRVu&ZIlT^Vt5=Z)PPVo-5g;A@gqx$=-n#QY;%~}U-q{~-nR5U5z?c;Z zq=cIt@CG1tL>-BZxJ#p z45kWl7(kU8lT~P5OcRrCF>i9QI#Tj*hY*P&vr#@sgw}Mdm6?!Ubtxi*5y+5;&)mz) zsZ4`o`_tIS+zS!6=gsa`Icxl5H=pu0Rt!koDIA`rH=Wi0d=zkKT0ZGt2dG$TsX4D(^S@uRO3Ii&YS?Ft zSY^WH@-(cuB3;q3u3n8Mr7Mz^O%-p4(ZdJig14qu1c8{rF8Nxj9kA@$4vyfY$_?Sw|vLrgCaL-}y8jUuVB|G-kd8vD%@KEw>df+oE3}ddkjZkA# z1_w7$lQ&q!e0sNif56%E?NZ3z{*1A9Jm&`yGQ&ZyR^ifB>11g}4P_bJ#i3E+fe9&V z#7#11ramQMrcNcb%5|?B*_RXP&?<6`8#mqU3NNX|{kPg3I*fCD{1nUBVzGcU&35!D zT|Bpn#hA{{W$>NNGbplup*y|d{4yOiRzE%5SwKFd)Lon++Wpc(Boad^&n|;+hQmXb z%(XcF2FMWz1dua7VB(9n4L?6gcSh^B_q8)&qhBjq1-D_2H6~C+e^?}yB$kr5?^|K= z7qgs{k;BB07A#PAL%1d-l3SS;CFI>$^XG_Xe%Lfb+9iiAm~g6+SKQu$PpouXe!= zbEdhnjInYTu?|VmZ7ckCrdx!xsHt8#bny7^!aUt`r6f|Cm}yy+p+&1>G&**8vvQ$Q z2rC-8X>M$5cnU;jUdx zVWX4+^+98cAUwl6oarq;Po~%1j$e#4iF$W%TUFZ08JPq$aBb10^zmzWB z0l+9kl*1yQKS0!L)%I9m%udYJQH)aQ3L6|EB|bv&6LWtWixAf86H8;$*Uht;8ZW?i z!}|58l!*! zy$3bh1~VI>SKkXlB6R3Cbu6PSxwMFmNN61I^+g_BQh)~3@v z(oO-Q;8W65L&#|0M~lvsK)O)L44WmXqXxrq_lDo}vlpjr&2K%?%L?^%@xJp3lM7RX zHAiQcn8~G;mw3Ewb4-0%$Nr&@mDO107DRK*X9~yh33GoSdsSKRBxC6@Y0qpY5p8+& zb8~Lkw`I>@05Vx22o*MmuyaYjPDhWg0u*~1trmcWZ`_sYU4^$Z=_v$?NRn;ztBv4v z;y4lw#BsMgrY2doO{A4Lf8lAAGi07C=MV2oE{-nzEyG@nb-NddJ<-CkC}E66(e>uR zS0)Z_#A2yKntGVj^l{h_b<-@FiE$N?q?3@@IbjSeJT-|!LF^hNid~~zAiRA^p*<9( zaL>^VUH_1afJoCkNM;{iVxI0%F~_^oGh}7?>R}>2E0t=2FlU*{Rb0zSRY9s&7SIICgq~>yOHoH~N>k?sD)DRm`vBqp zxI+wLJA6G`@Ga^_PdyQK=ld0CH|Yl>z(|@fk6bl&uhbhP`p7G{z}CxlGy|v+dCDEy z^+jIU_9l0LO4Nr0Q7A%51$UUjXr~)4!RV%+vr09n>%Z*TF+&RQ>h-(WHN4Eu`)vD; zbnze-CVeMD<-rjrvG#NM5Bng5oVCBjFz*BmOrHgO?D%(gK8|B)tEY|KrPPyyZ$}L# zk!KZI{25kCDhN^%N%}SVhnG6Zp8IFht945`i@xF`zflq@LoR5J7qJbamB5^1+?gYc15%;cx+t3j6}Hq#B!FkPwp5MSdf zbL<`boz0*OWNNwj1c?R)cyu6PBeGV?=Zz&%o!Kr-o^F7ew%`__wrr-+d`e}3E9$+a zu_uIt2~)B}o| z3_>GtyK?{^1LQAF0xpwx$LwUrp_PVo=+kEKisAc2qsPo>uh&hY!^h^u#l>5YD4k)7 zrM!(3hj57^>-k^EK(Z=;#i3o`=rQIx(aS}T-P=&ml%|1;LE_T60X{P%86g4Za4G}k zqG-A`HVOXgaB7t1`fWl6m%-`}xw`SK7mNsvId%^aQb{OXLyWJu*LKf?_SEic0o0G|3RhW6?U0_X2B)m!p@ z$`Uc&7|-&=)X>&YNwX1#xVX;>PQlXNJCLaN12!LMbSrA2tm zo{f#RM{WE(hrE2*suY07eHBc2l!)J8fvZ!TM<%;#U{R*^CJ664CZhBBSPB{`27bQ% zfhrFmnYnk!3v_y?G19qgXaXhm!(AJ#1HdJG`6bGwW8KinJsv*YV|_xpZy z>pAXHAq7$Mmpj8G7yJ<|{3s$pwt!xqjO@;SfveFEGWsj)zNl`VmTI|Fk!pm{JDKo1 z)tW>Bsb-ke5CPX1Z;U43Ev#rrqOo?T9(NhrK0ygi&*sm*53}Rz=VTMOc;EYwg}0|q z+qR7!GJnY)kWmQ*K+(Xp#d^-^0yv=p09gTrt14(3bcc^`y7eQ1G*&@40_LvA2B7K7h`@JocB$J0zbu zHy-hPISe1|OjPQ6m>ae^{Vvl3Cwsjb`V$%wU(3y|MIXESi$=07e}E=~)l`Ao*LV~| zFtzD*F2VvQs5H2_l1_!I%+-mWWRh8k?7d}8!*polGIboq^o=^Iu9qGT0JjQO9TjkJ zKr!mq-CQ`q-$O-o>pV)-hIRdMar`8Bd&1bPQ}c!Bx?=QgD6V&DzVu_hK#k?!IRGZw zfj^LI7Nuo6u>*Ow46Xw1qkaTmC)-o$hE58M1o>iOw9T+s_m7~`sK4AZKuVmJ3*~SA zj(o5M2!44>p{gYf1wUy`qtKF})rgiE;0>jc%&vNTh#@EM+Xev{IKa4+!ruFvJ@P+_ zjc>E_-(NpRi9WGpWn}}f(Tcn=?wjT^at-0w{`9R_x3CV1y|OjTSnp~f@Kq0pG>*G0 z;p)hMbhuni_#n}5K92eP6IsFC@fwDpPc`>;#1JpVXoaV!-u7vRA=M#|dFcswk~ymC za-8^>jX)SvS38*_Wl_lp--YZ?)OrX5o{;|`X+|Qt|7`l2V%YL}ONPq#efGe_!u$kS z0$?UqhRzMchVxC0fF+qHYmkZlVY|0Bwm04Y?Q3T3V+WwHX`soL!EI$7eag?o|V@@s$OM#UPsTnO`B=EvxX@Q zCjr-pr@EkZMYmo42e0t_f%ZGFX?YuX(i{4R=*#*L;=qi>|7AEdV^Jvuu^8f9WL8;>$v%-6 z)4r%$0dgV5EF|R@wtT>9LKM63kfq1=neyf%^Um8dk!!rb!y}XhZmgoV4G~CMk(B*t zeh+jJOR&ZB_#u^XVd|?C$4Tax&i0x%)GY3x_VUlHSWm+o^1!g5k|6X7G32G7VRx#4 zhv*%9&0bFjx3>w8WAA%+dhu$;M#O3augdZ`yJI7F5Sj@{e%)1Uu?wJ0hv%&!RSIxh z{2GnuFCK8Gqs{5!1S`78llm-2+lV3RP`m(a@2(C-Xc!p|0SVw-RX-C6*yk*W*4RGI zuMs+^1VkX%UwHTLu`w;wM_Nq7KR6eLs@4-LbEqHROpS@b6e3i-QGdY7M=M?6T2`~$yf>Q|$ z<3+Q100tGAp2Iu1*4}?u4jXx&Y40}DeV@uJAIrH@#ga&VWgD0Npj@chW%$NCT_VVJ zF}Kec1-G>QE=6xEpflqe{=;uIkDEbdJX;tscxA2XLd_bm#$^4g(C5`ut4lD(;?@{; zCE@njBzyW$27KC8?=wqN8*Pv4(fK*q*>5(xtFT**Y23ybfOd6`)w%&5dxC;zw4rDX zF!ZY7%)HpU6;%ji8(TuWzbm#WUK+AC)xbO(3bQ4e7!1bL2`%tNJ>P8^v>_h3hV7%; zGLOxuzCDibWpb+Jlg`C(6AZCSRKb1Ks-_@1^1W>}lQp|;j}CS~%}}vENJ1*lRGtv) zDGAb#$k$Ny$ppinMH=@R%de-)KhXm~nW`T>ws2pirqMkWAcXMLKWreIITFrq@qBku z3!3qum7WY$5&5_Eo#>W%vR6=BZ9snZY~Xoai0x|2vhr753R1xl2Y^bfQ9Eiu@lhG) zF0Edf9?1G5pp7S#twmTgt@}|Kmp!hU)O-us(%)2j4CZqqG?Qlh<;fms^F-$-Cp*^l-4z2 zU9nN-9MA2x{jRxm860Pkxi>8qHZr4DviEghMXDgvtUvQ({eh1{?ktZS<#2pd-5+{5 zG>_GQ6Sj~`waFa`Az~_!>J~_o8qJdyl%O8vCocui<7&_owxBc9{==DF4`BIC&M}5% zUNXQN()`v`E=v$>XGajsh~FXbz8f%W1z48ord->V$p8KMQVTli0THOc+8?(usp@wL z1l8)b0JyRc>)NO)V#@8r0<^J&wtXaQnibqC=SZ;s|I?5Ew?mKKSG8Y6L%qmVWf7`p R-zPi)(&7qYwIW8r{|ohnAvyp6 diff --git a/twidere/src/main/res/drawable-xxxhdpi/ic_launcher.png b/twidere/src/main/res/drawable-xxxhdpi/ic_launcher.png index ade8ae2ebcc202dd06d211cf70c37691e607bc7b..341c5b9f4f4263f9026c58f602309f70484ea754 100644 GIT binary patch literal 14674 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 diff --git a/twidere/src/main/res/layout/activity_compose.xml b/twidere/src/main/res/layout/activity_compose.xml index 457a048eb..61204a576 100644 --- a/twidere/src/main/res/layout/activity_compose.xml +++ b/twidere/src/main/res/layout/activity_compose.xml @@ -27,11 +27,6 @@ android:minWidth="@dimen/compose_min_width" android:orientation="vertical"> - - @color/message_bubble_color_light - -