NekoX/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java

348 lines
16 KiB
Java

/*
* This is the source code of Telegram for Android v. 1.3.2.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013.
*/
package org.telegram.messenger;
import android.app.Activity;
import android.content.SharedPreferences;
import org.telegram.ui.ApplicationLoader;
import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
public class FileUploadOperation {
private int uploadChunkSize = 1024 * 32;
private String uploadingFilePath;
public int state = 0;
private byte[] readBuffer;
public FileUploadOperationDelegate delegate;
private long requestToken = 0;
private int currentPartNum = 0;
private long currentFileId;
private boolean isLastPart = false;
private long totalFileSize = 0;
private int totalPartsCount = 0;
private long currentUploaded = 0;
private int saveInfoTimes = 0;
private byte[] key;
private byte[] iv;
private byte[] ivChange;
private boolean isEncrypted = false;
private int fingerprint = 0;
private boolean isBigFile = false;
private String fileKey;
FileInputStream stream;
MessageDigest mdEnc = null;
public static interface FileUploadOperationDelegate {
public abstract void didFinishUploadingFile(FileUploadOperation operation, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile);
public abstract void didFailedUploadingFile(FileUploadOperation operation);
public abstract void didChangedUploadProgress(FileUploadOperation operation, float progress);
}
public FileUploadOperation(String location, boolean encrypted) {
uploadingFilePath = location;
isEncrypted = encrypted;
}
public void start() {
if (state != 0) {
return;
}
state = 1;
startUploadRequest();
}
public void cancel() {
if (state != 1) {
return;
}
state = 2;
if (requestToken != 0) {
ConnectionsManager.getInstance().cancelRpc(requestToken, true);
}
delegate.didFailedUploadingFile(this);
cleanup();
}
private void cleanup() {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("uploadinfo", Activity.MODE_PRIVATE);
preferences.edit().remove(fileKey + "_time").
remove(fileKey + "_size").
remove(fileKey + "_uploaded").
remove(fileKey + "_id").
remove(fileKey + "_iv").
remove(fileKey + "_key").
remove(fileKey + "_ivc").commit();
}
private void startUploadRequest() {
if (state != 1) {
return;
}
TLObject finalRequest;
try {
if (stream == null) {
File cacheFile = new File(uploadingFilePath);
stream = new FileInputStream(cacheFile);
totalFileSize = cacheFile.length();
if (totalFileSize > 10 * 1024 * 1024) {
isBigFile = true;
} else {
try {
mdEnc = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
FileLog.e("tmessages", e);
}
}
uploadChunkSize = (int) Math.max(32, Math.ceil(totalFileSize / (1024.0f * 3000)));
if (1024 % uploadChunkSize != 0) {
int chunkSize = 64;
while (uploadChunkSize > chunkSize) {
chunkSize *= 2;
}
uploadChunkSize = chunkSize;
}
uploadChunkSize *= 1024;
totalPartsCount = (int) Math.ceil((float) totalFileSize / (float) uploadChunkSize);
readBuffer = new byte[uploadChunkSize];
fileKey = Utilities.MD5(uploadingFilePath + (isEncrypted ? "enc" : ""));
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("uploadinfo", Activity.MODE_PRIVATE);
long fileSize = preferences.getLong(fileKey + "_size", 0);
int currentTime = (int)(System.currentTimeMillis() / 1000);
boolean rewrite = false;
if (fileSize == totalFileSize) {
currentFileId = preferences.getLong(fileKey + "_id", 0);
int date = preferences.getInt(fileKey + "_time", 0);
long uploadedSize = preferences.getLong(fileKey + "_uploaded", 0);
if (isEncrypted) {
String ivString = preferences.getString(fileKey + "_iv", null);
String keyString = preferences.getString(fileKey + "_key", null);
if (ivString != null && keyString != null) {
key = Utilities.hexToBytes(keyString);
iv = Utilities.hexToBytes(ivString);
ivChange = new byte[32];
System.arraycopy(iv, 0, ivChange, 0, 32);
} else {
rewrite = true;
}
}
if (!rewrite && date != 0) {
if (isBigFile && date < currentTime - 60 * 60 * 24) {
date = 0;
} else if (!isBigFile && date < currentTime - 60 * 60 * 1.5f) {
date = 0;
}
if (date != 0) {
if (uploadedSize > 0) {
currentUploaded = uploadedSize;
currentPartNum = (int) (uploadedSize / uploadChunkSize);
if (!isBigFile) {
for (int b = 0; b < currentUploaded / uploadChunkSize; b++) {
int read = stream.read(readBuffer);
int toAdd = 0;
if (isEncrypted && read % 16 != 0) {
toAdd += 16 - read % 16;
}
ByteBufferDesc sendBuffer = BuffersStorage.getInstance().getFreeBuffer(read + toAdd);
if (read != uploadChunkSize || totalPartsCount == currentPartNum + 1) {
isLastPart = true;
}
sendBuffer.writeRaw(readBuffer, 0, read);
if (isEncrypted) {
for (int a = 0; a < toAdd; a++) {
sendBuffer.writeByte(0);
}
Utilities.aesIgeEncryption(sendBuffer.buffer, key, ivChange, true, true, 0, read + toAdd);
}
sendBuffer.rewind();
mdEnc.update(sendBuffer.buffer);
BuffersStorage.getInstance().reuseFreeBuffer(sendBuffer);
}
} else {
stream.skip(uploadedSize);
if (isEncrypted) {
String ivcString = preferences.getString(fileKey + "_ivc", null);
if (ivcString != null) {
ivChange = Utilities.hexToBytes(ivcString);
} else {
rewrite = true;
currentUploaded = 0;
currentPartNum = 0;
}
}
}
} else {
rewrite = true;
}
}
} else {
rewrite = true;
}
} else {
rewrite = true;
}
if (rewrite) {
if (isEncrypted) {
iv = new byte[32];
key = new byte[32];
ivChange = new byte[32];
Utilities.random.nextBytes(iv);
Utilities.random.nextBytes(key);
System.arraycopy(iv, 0, ivChange, 0, 32);
}
currentFileId = Utilities.random.nextLong();
SharedPreferences.Editor editor = preferences.edit();
editor.putInt(fileKey + "_time", currentTime);
editor.putLong(fileKey + "_size", totalFileSize);
editor.putLong(fileKey + "_id", currentFileId);
editor.remove(fileKey + "_uploaded");
if (isEncrypted) {
editor.putString(fileKey + "_iv", Utilities.bytesToHex(iv));
editor.putString(fileKey + "_ivc", Utilities.bytesToHex(ivChange));
editor.putString(fileKey + "_key", Utilities.bytesToHex(key));
}
editor.commit();
}
if (isEncrypted) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] arr = new byte[64];
System.arraycopy(key, 0, arr, 0, 32);
System.arraycopy(iv, 0, arr, 32, 32);
byte[] digest = md.digest(arr);
for (int a = 0; a < 4; a++) {
fingerprint |= ((digest[a] ^ digest[a + 4]) & 0xFF) << (a * 8);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
} else {
if (saveInfoTimes >= 4) {
saveInfoTimes = 0;
}
if (isBigFile && currentUploaded % (1024 * 1024) == 0 || !isBigFile && saveInfoTimes == 0) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("uploadinfo", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(fileKey + "_uploaded", currentUploaded);
if (isEncrypted) {
editor.putString(fileKey + "_ivc", Utilities.bytesToHex(ivChange));
}
editor.commit();
}
saveInfoTimes++;
}
int read = stream.read(readBuffer);
int toAdd = 0;
if (isEncrypted && read % 16 != 0) {
toAdd += 16 - read % 16;
}
ByteBufferDesc sendBuffer = BuffersStorage.getInstance().getFreeBuffer(read + toAdd);
if (read != uploadChunkSize || totalPartsCount == currentPartNum + 1) {
isLastPart = true;
}
sendBuffer.writeRaw(readBuffer, 0, read);
if (isEncrypted) {
for (int a = 0; a < toAdd; a++) {
sendBuffer.writeByte(0);
}
Utilities.aesIgeEncryption(sendBuffer.buffer, key, ivChange, true, true, 0, read + toAdd);
}
sendBuffer.rewind();
if (!isBigFile) {
mdEnc.update(sendBuffer.buffer);
}
if (isBigFile) {
TLRPC.TL_upload_saveBigFilePart req = new TLRPC.TL_upload_saveBigFilePart();
req.file_part = currentPartNum;
req.file_id = currentFileId;
req.file_total_parts = totalPartsCount;
req.bytes = sendBuffer;
finalRequest = req;
} else {
TLRPC.TL_upload_saveFilePart req = new TLRPC.TL_upload_saveFilePart();
req.file_part = currentPartNum;
req.file_id = currentFileId;
req.bytes = sendBuffer;
finalRequest = req;
}
currentUploaded += read;
} catch (Exception e) {
FileLog.e("tmessages", e);
delegate.didFailedUploadingFile(this);
cleanup();
return;
}
requestToken = ConnectionsManager.getInstance().performRpc(finalRequest, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
requestToken = 0;
if (error == null) {
if (response instanceof TLRPC.TL_boolTrue) {
currentPartNum++;
delegate.didChangedUploadProgress(FileUploadOperation.this, (float) currentUploaded / (float) totalFileSize);
if (isLastPart) {
state = 3;
if (key == null) {
TLRPC.InputFile result;
if (isBigFile) {
result = new TLRPC.TL_inputFileBig();
} else {
result = new TLRPC.TL_inputFile();
result.md5_checksum = String.format(Locale.US, "%32s", new BigInteger(1, mdEnc.digest()).toString(16)).replace(' ', '0');
}
result.parts = currentPartNum;
result.id = currentFileId;
result.name = uploadingFilePath.substring(uploadingFilePath.lastIndexOf("/") + 1);
delegate.didFinishUploadingFile(FileUploadOperation.this, result, null);
cleanup();
} else {
TLRPC.InputEncryptedFile result;
if (isBigFile) {
result = new TLRPC.TL_inputEncryptedFileBigUploaded();
} else {
result = new TLRPC.TL_inputEncryptedFileUploaded();
result.md5_checksum = String.format(Locale.US, "%32s", new BigInteger(1, mdEnc.digest()).toString(16)).replace(' ', '0');
}
result.parts = currentPartNum;
result.id = currentFileId;
result.key_fingerprint = fingerprint;
result.iv = iv;
result.key = key;
delegate.didFinishUploadingFile(FileUploadOperation.this, null, result);
cleanup();
}
} else {
startUploadRequest();
}
} else {
delegate.didFailedUploadingFile(FileUploadOperation.this);
cleanup();
}
} else {
delegate.didFailedUploadingFile(FileUploadOperation.this);
cleanup();
}
}
}, null, true, RPCRequest.RPCRequestClassUploadMedia, ConnectionsManager.DEFAULT_DATACENTER_ID);
}
}