package com.xabber.android.data.extension.httpfileupload;
import com.xabber.android.R;
import com.xabber.android.data.Application;
import com.xabber.android.data.account.AccountItem;
import com.xabber.android.data.account.AccountManager;
import com.xabber.android.data.connection.ConnectionItem;
import com.xabber.android.data.entity.AccountJid;
import com.xabber.android.data.entity.UserJid;
import com.xabber.android.data.log.LogManager;
import com.xabber.android.data.message.MessageManager;
import com.xabber.xmpp.httpfileupload.Slot;
import org.jivesoftware.smack.ExceptionCallback;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.Jid;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class HttpFileUploadManager {
private static HttpFileUploadManager instance;
private static final MediaType CONTENT_TYPE = MediaType.parse("application/octet-stream");
private Map<AccountJid, Jid> uploadServers = new ConcurrentHashMap<>();
public static HttpFileUploadManager getInstance() {
if (instance == null) {
instance = new HttpFileUploadManager();
}
return instance;
}
private HttpFileUploadManager() {
}
public boolean isFileUploadSupported(AccountJid account) {
return uploadServers.containsKey(account);
}
public void uploadFile(final AccountJid account, final UserJid user, final String filePath) {
final Jid uploadServerUrl = uploadServers.get(account);
if (uploadServerUrl == null) {
return;
}
AccountItem accountItem = AccountManager.getInstance().getAccount(account);
if (accountItem == null) {
return;
}
final File file = new File(filePath);
final com.xabber.xmpp.httpfileupload.Request httpFileUpload = new com.xabber.xmpp.httpfileupload.Request();
httpFileUpload.setFilename(file.getName());
httpFileUpload.setSize(String.valueOf(file.length()));
httpFileUpload.setTo(uploadServerUrl);
try {
accountItem.getConnection().sendIqWithResponseCallback(httpFileUpload, new StanzaListener() {
@Override
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
if (!(packet instanceof Slot)) {
return;
}
uploadFileToSlot(account, (Slot) packet);
}
private void uploadFileToSlot(final AccountJid account, final Slot slot) {
OkHttpClient client = new OkHttpClient().newBuilder()
.writeTimeout(5, TimeUnit.MINUTES)
.connectTimeout(5, TimeUnit.MINUTES)
.readTimeout(5, TimeUnit.MINUTES)
.build();
Request request = new Request.Builder()
.url(slot.getPutUrl())
.put(RequestBody.create(CONTENT_TYPE, file))
.build();
final String fileMessageId;
fileMessageId = MessageManager.getInstance().createFileMessage(account, user, file);
LogManager.i(HttpFileUploadManager.this, "starting upload file to " + slot.getPutUrl() + " size " + file.length());
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
LogManager.i(HttpFileUploadManager.this, "onFailure " + e.getMessage());
MessageManager.getInstance().updateMessageWithError(fileMessageId);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
LogManager.i(HttpFileUploadManager.this, "onResponse " + response.isSuccessful() + " " + response.body().string());
if (response.isSuccessful()) {
MessageManager.getInstance().updateFileMessage(account, user, fileMessageId, slot.getGetUrl());
} else {
MessageManager.getInstance().updateMessageWithError(fileMessageId);
}
}
});
}
}, new ExceptionCallback() {
@Override
public void processException(Exception exception) {
LogManager.i(this, "On HTTP file upload slot error");
LogManager.exception(this, exception);
Application.getInstance().onError(R.string.http_file_upload_slot_error);
}
});
} catch (SmackException.NotConnectedException | InterruptedException e) {
LogManager.exception(this, e);
}
}
private void discoverSupport(AccountJid account, XMPPConnection xmppConnection) throws SmackException.NotConnectedException,
XMPPException.XMPPErrorException, SmackException.NoResponseException, InterruptedException {
uploadServers.remove(account);
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(xmppConnection);
List<DomainBareJid> services = discoManager.findServices(com.xabber.xmpp.httpfileupload.Request.NAMESPACE, true, true);
if (!services.isEmpty()) {
final DomainBareJid uploadServerUrl = services.get(0);
LogManager.i(this, "Http file upload server: " + uploadServerUrl);
uploadServers.put(account, uploadServerUrl);
}
}
public void onAuthorized(final ConnectionItem connectionItem) {
try {
discoverSupport(connectionItem.getAccount(), connectionItem.getConnection());
} catch (SmackException.NotConnectedException | XMPPException.XMPPErrorException
| SmackException.NoResponseException | InterruptedException e) {
LogManager.exception(this, e);
}
}
}