SlotRequester.java

  1/*
  2 * Copyright (c) 2018, Daniel Gultsch All rights reserved.
  3 *
  4 * Redistribution and use in source and binary forms, with or without modification,
  5 * are permitted provided that the following conditions are met:
  6 *
  7 * 1. Redistributions of source code must retain the above copyright notice, this
  8 * list of conditions and the following disclaimer.
  9 *
 10 * 2. Redistributions in binary form must reproduce the above copyright notice,
 11 * this list of conditions and the following disclaimer in the documentation and/or
 12 * other materials provided with the distribution.
 13 *
 14 * 3. Neither the name of the copyright holder nor the names of its contributors
 15 * may be used to endorse or promote products derived from this software without
 16 * specific prior written permission.
 17 *
 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28 */
 29
 30package eu.siacs.conversations.http;
 31
 32import android.util.Log;
 33
 34import com.google.common.collect.ImmutableMap;
 35
 36import java.util.Map;
 37
 38import eu.siacs.conversations.Config;
 39import eu.siacs.conversations.entities.Account;
 40import eu.siacs.conversations.entities.DownloadableFile;
 41import eu.siacs.conversations.parser.IqParser;
 42import eu.siacs.conversations.services.XmppConnectionService;
 43import eu.siacs.conversations.xml.Element;
 44import eu.siacs.conversations.xml.Namespace;
 45import eu.siacs.conversations.xmpp.Jid;
 46import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 47import okhttp3.Headers;
 48import okhttp3.HttpUrl;
 49
 50public class SlotRequester {
 51
 52    private final XmppConnectionService service;
 53
 54    public SlotRequester(XmppConnectionService service) {
 55        this.service = service;
 56    }
 57
 58    public void request(Method method, Account account, DownloadableFile file, String mime, OnSlotRequested callback) {
 59        if (method == Method.HTTP_UPLOAD_LEGACY) {
 60            final Jid host = account.getXmppConnection().findDiscoItemByFeature(Namespace.HTTP_UPLOAD_LEGACY);
 61            requestHttpUploadLegacy(account, host, file, mime, callback);
 62        } else {
 63            final Jid host = account.getXmppConnection().findDiscoItemByFeature(Namespace.HTTP_UPLOAD);
 64            requestHttpUpload(account, host, file, mime, callback);
 65        }
 66    }
 67
 68    private void requestHttpUploadLegacy(Account account, Jid host, DownloadableFile file, String mime, OnSlotRequested callback) {
 69        IqPacket request = service.getIqGenerator().requestHttpUploadLegacySlot(host, file, mime);
 70        service.sendIqPacket(account, request, (a, packet) -> {
 71            if (packet.getType() == IqPacket.TYPE.RESULT) {
 72                Element slotElement = packet.findChild("slot", Namespace.HTTP_UPLOAD_LEGACY);
 73                if (slotElement != null) {
 74                    try {
 75                        final String putUrl = slotElement.findChildContent("put");
 76                        final String getUrl = slotElement.findChildContent("get");
 77                        if (getUrl != null && putUrl != null) {
 78                            final Slot slot = new Slot(
 79                                    HttpUrl.get(putUrl),
 80                                    HttpUrl.get(getUrl),
 81                                    Headers.of("Content-Type", mime == null ? "application/octet-stream" : mime)
 82                            );
 83                            callback.success(slot);
 84                            return;
 85                        }
 86                    } catch (IllegalArgumentException e) {
 87                        //fall through
 88                    }
 89                }
 90            }
 91            Log.d(Config.LOGTAG, account.getJid().toString() + ": invalid response to slot request " + packet);
 92            callback.failure(IqParser.extractErrorMessage(packet));
 93        });
 94
 95    }
 96
 97    private void requestHttpUpload(Account account, Jid host, DownloadableFile file, String mime, OnSlotRequested callback) {
 98        IqPacket request = service.getIqGenerator().requestHttpUploadSlot(host, file, mime);
 99        service.sendIqPacket(account, request, (a, packet) -> {
100            if (packet.getType() == IqPacket.TYPE.RESULT) {
101                final Element slotElement = packet.findChild("slot", Namespace.HTTP_UPLOAD);
102                if (slotElement != null) {
103                    try {
104                        final Element put = slotElement.findChild("put");
105                        final Element get = slotElement.findChild("get");
106                        final String putUrl = put == null ? null : put.getAttribute("url");
107                        final String getUrl = get == null ? null : get.getAttribute("url");
108                        if (getUrl != null && putUrl != null) {
109                            final ImmutableMap.Builder<String, String> headers = new ImmutableMap.Builder<>();
110                            for (Element child : put.getChildren()) {
111                                if ("header".equals(child.getName())) {
112                                    final String name = child.getAttribute("name");
113                                    final String value = child.getContent();
114                                    if (HttpUploadConnection.WHITE_LISTED_HEADERS.contains(name) && value != null && !value.trim().contains("\n")) {
115                                        headers.put(name, value.trim());
116                                    }
117                                }
118                            }
119                            headers.put("Content-Type", mime == null ? "application/octet-stream" : mime);
120                            final Slot slot = new Slot(HttpUrl.get(putUrl), HttpUrl.get(getUrl), headers.build());
121                            callback.success(slot);
122                            return;
123                        }
124                    } catch (IllegalArgumentException e) {
125                        //fall through
126                    }
127                }
128            }
129            Log.d(Config.LOGTAG, account.getJid().toString() + ": invalid response to slot request " + packet);
130            callback.failure(IqParser.extractErrorMessage(packet));
131        });
132
133    }
134
135    public interface OnSlotRequested {
136        void success(Slot slot);
137        void failure(String message);
138    }
139
140    public static class Slot {
141        public final HttpUrl put;
142        public final HttpUrl get;
143        public final Headers headers;
144
145        private Slot(HttpUrl put, HttpUrl get, Headers headers) {
146            this.put = put;
147            this.get = get;
148            this.headers = headers;
149        }
150
151        private Slot(HttpUrl put, HttpUrl getUrl, Map<String, String> headers) {
152            this.put = put;
153            this.get = getUrl;
154            this.headers = Headers.of(headers);
155        }
156    }
157}