package ru.denull.mtproto;
import static ru.denull.mtproto.CryptoUtils.*;
import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.util.*;
import java.util.concurrent.*;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import net.sf.ehcache.CacheManager;
import ru.denull.mtproto.Auth.AuthCallback;
import ru.denull.mtproto.Auth.AuthState;
import ru.denull.wire.Main;
import ru.denull.wire.Utils;
import ru.denull.wire.model.*;
import ru.denull.wire.model.DialogManager.EncryptedDialog;
import tl.*;
import tl.Config;
import tl.Message;
import tl.auth.Authorization;
import tl.auth.ExportAuthorization;
import tl.auth.ExportedAuthorization;
import tl.auth.ImportAuthorization;
import tl.contacts.TForeignLink;
import tl.contacts.TMyLink;
import tl.help.GetConfig;
import tl.updates.Difference;
import tl.updates.DifferenceEmpty;
import tl.updates.DifferenceSlice;
import tl.updates.GetDifference;
import tl.updates.GetState;
import tl.updates.State;
import tl.updates.TDifference;
public class DataService {
private static final String TAG = "DataService";
public static final boolean DEBUG_SERVERS = false;
public static DataService instance = null; // still not sure if it's a good idea...
public Server mainServer; // current user's DC
public int mainServerID;
// TODO: close hanging connections after a while
HashMap<String, Server> servers; // <address:port> -> <Server>, for downloading files
public Preferences pref; // global config (there's also local configs per each server)
public String defaultServer;
public Config dcConfig;
// TODO: maybe make own subclass of ThreadPoolExecutor
public ThreadPoolExecutor threadPool; // for managing all threads
public ScheduledThreadPoolExecutor scheduledPool; // for timers
// TODO: fix
//public MainActivity activity;
// All data will be stored here
public UserSelf me;
public SQLiteDatabase db;
public DialogManager dialogManager;
public ChatManager chatManager;
public UserManager userManager;
public ContactManager contactManager;
public MessageManager messageManager;
public FileManager fileManager;
public TypingManager typingManager;
public CacheManager cacheManager;
public boolean networkDown = false;
public boolean wifi, mobile;
public int updates_pts, updates_date, updates_seq;
public boolean storePhone = true;
public boolean storeKeys = true;
public boolean storeCache = true;
public DataService() {
servers = new HashMap<String, Server>(10);
threadPool = new ThreadPoolExecutor(7, 12, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(200));
scheduledPool = new ScheduledThreadPoolExecutor(1);
final DataService self = this;
/*threadPool.submit(new Runnable() { // initialize managers
public void run() {*/
db = (new CacheDbHelper()).getWritableDatabase();
cacheManager = new CacheManager(getClass().getClassLoader().getResourceAsStream("/ehcache.xml"));
dialogManager = new DialogManager(self);
chatManager = new ChatManager(self, db);
userManager = new UserManager(self, db);
messageManager = new MessageManager(self, db);
contactManager = new ContactManager(self, db);
fileManager = new FileManager(self);
typingManager = new TypingManager(self);
// TODO: allow to setup listeners
/*ui(new Runnable() {
public void run() {
if (chatlist != null) {
chatlist.setListAdapter(new ChatListAdapter(activity));
}
}
}, true);*/
// warm up cache
for (Dialog dialog : dialogManager.all) {
if (dialog.peer instanceof PeerChat) {
chatManager.get(((PeerChat) dialog.peer).chat_id);
} else {
userManager.get(((PeerUser) dialog.peer).user_id);
}
messageManager.get(dialog.top_message);
}
contactManager.load();
for (int uid : contactManager.loaded.values()) {
userManager.get(uid);
}
contactManager.loadLocal();
Notifier.enter(Notifier.CACHING_DB_STARTED);
//}
//});
//pref = getSharedPreferences(getString(R.string.preferences_auth) + (DEBUG_SERVERS ? "-test" : "-production"), Context.MODE_PRIVATE);
pref = Preferences.userRoot().node("wire/config" + (DEBUG_SERVERS ? "-test" : "-production"));
setup();
}
public static DataService getInstance() {
if (instance == null) {
instance = new DataService();
}
return instance;
}
public void setup() {
try {
me = (pref.get("user", "").length() == 0) ? null : (UserSelf) TL.read(pref.get("user", ""));
} catch (Exception e) {
e.printStackTrace();
}
defaultServer = pref.get("server", DEBUG_SERVERS ? "173.240.5.253:443" : "173.240.5.1:443");// "95.142.192.65:80"
/*updates_pts = pref.getInt("updates_pts", -1);
updates_date = pref.getInt("updates_date", -1);
updates_seq = pref.getInt("updates_seq", -1);*/
updates_pts = -1;
updates_date = -1;
updates_seq = -1;
}
public void dropDatabases() {
mainServer.auth.loggedOut();
CacheDbHelper.reset(db);
try {
pref.clear();
} catch (BackingStoreException e) {
e.printStackTrace();
}
setup();
}
public void onDestroy() {
disconnectAll(null);
db.close();
Notifier.leave(Notifier.CACHING_DB_STARTED);
instance = null;
}
public void networkChanged() {
/*threadPool.submit(new Runnable() {
public void run() {
ConnectivityManager connMgr = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
wifi = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
mobile = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnected();
if (networkDown && (wifi || mobile)) {
// reconnect
for (Server server : servers.values()) {
server.resolveNetworkProblem(true);
}
}
networkDown = !wifi && !mobile;
}
});*/
}
public void logged(UserSelf user) {
me = user;
userManager.store(user);
if (storeKeys) {
try {
pref.put("user", user.writeToBase64());
pref.put("server", mainServer.address + ":" + mainServer.port);
} catch (Exception e) {
e.printStackTrace();
}
}
mainServer.auth.logged();
}
public boolean checkingState = false; // prevent multiple queries
public void checkUpdates() {
if (mainServer == null || checkingState) return; // TODO: check this
checkingState = true;
if (updates_pts < 0 || updates_date < 0) { // invalid (not initialized) state
mainServer.call(new GetState(), new Server.RPCCallback<tl.updates.State>() {
public void done(State result) {
System.out.println("reset state");
updates_pts = Math.max(updates_pts, result.pts);
updates_date = Math.max(updates_date, result.date);
updates_seq = Math.max(updates_seq, result.seq);
pref.putInt("updates_pts", updates_pts);
pref.putInt("updates_date", updates_date);
pref.putInt("updates_seq", updates_seq);
checkingState = false;
}
public void error(int code, String message) {
Log.e(TAG, "Error while checking for updates");
checkingState = false;
}
});
} else {
mainServer.call(new GetDifference(updates_pts, updates_date, 0), new Server.RPCCallback<TDifference>() {
public void done(TDifference result) {
checkingState = false;
if (result instanceof DifferenceEmpty) {
//System.out.println("no updates");
updates_date = Math.max(updates_date, ((DifferenceEmpty) result).date);
updates_seq = Math.max(updates_seq, ((DifferenceEmpty) result).seq);
} else
if (result instanceof DifferenceSlice) {
//System.out.println("diff slice");
DifferenceSlice diff = (DifferenceSlice) result;
chatManager.store(diff.chats);
userManager.store(diff.users);
updates_pts = Math.max(updates_pts, ((State) diff.intermediate_state).pts);
updates_date = Math.max(updates_date, ((State) diff.intermediate_state).date);
updates_seq = Math.max(updates_seq, ((State) diff.intermediate_state).seq);
for (TMessage message : diff.new_messages) {
processNewMessage(message, false);
}
for (TUpdate update : diff.other_updates) {
processUpdate(update, false);
}
checkUpdates(); // request next updates
} else
if (result instanceof Difference) {
//System.out.println("diff");
Difference diff = (Difference) result;
chatManager.store(diff.chats);
userManager.store(diff.users);
updates_pts = Math.max(updates_pts, ((State) diff.state).pts);
updates_date = Math.max(updates_date, ((State) diff.state).date);
updates_seq = Math.max(updates_seq, ((State) diff.state).seq);
for (TMessage message : diff.new_messages) {
processNewMessage(message, false);
}
for (TUpdate update : diff.other_updates) {
processUpdate(update, false);
}
}
}
public void error(int code, String message) {
Log.e(TAG, "Error while checking for updates");
checkingState = false;
}
});
}
}
public void processUpdates(TUpdates updates) {
if (updates_pts < 0 || updates_date < 0 || updates_seq < 0) { // not yet ready to process updates, need to get state first
checkUpdates();
return;
}
if (updates instanceof UpdatesTooLong) {
checkUpdates();
} else
if (updates instanceof UpdateShort) {
updates_date = ((UpdateShort) updates).date;
processUpdate(((UpdateShort) updates).update, true);
} else
if (updates instanceof UpdateShortMessage) {
UpdateShortMessage upd = (UpdateShortMessage) updates;
if (upd.seq > updates_seq + 1) {
checkUpdates();
return;
}
updates_pts = Math.max(updates_pts, upd.pts);
updates_date = Math.max(updates_date, upd.date);
updates_seq = Math.max(updates_seq, upd.seq);
processNewMessage(new Message(upd.id, upd.from_id, new PeerUser(me == null ? 0 : me.id), false, true, upd.date, upd.message, new MessageMediaEmpty()), true);
} else
if (updates instanceof UpdateShortChatMessage) {
UpdateShortChatMessage upd = (UpdateShortChatMessage) updates;
if (upd.seq > updates_seq + 1) {
checkUpdates();
return;
}
updates_pts = Math.max(updates_pts, upd.pts);
updates_date = Math.max(updates_date, upd.date);
updates_seq = Math.max(updates_seq, upd.seq);
processNewMessage(new Message(upd.id, upd.from_id, new PeerChat(upd.chat_id), false, true, upd.date, upd.message, new MessageMediaEmpty()), true);
} else
if (updates instanceof Updates) {
Updates upd = (Updates) updates;
if (upd.seq > updates_seq + 1) {
checkUpdates();
return;
}
updates_date = Math.max(updates_date, upd.date);
updates_seq = Math.max(updates_seq, upd.seq);
chatManager.store(upd.chats);
userManager.store(upd.users);
for (TUpdate update : upd.updates) {
processUpdate(update, true);
}
} else
if (updates instanceof UpdatesCombined) {
UpdatesCombined upd = (UpdatesCombined) updates;
if (upd.seq_start > updates_seq + 1) {
checkUpdates();
return;
}
updates_date = Math.max(updates_date, upd.date);
updates_seq = Math.max(updates_seq, upd.seq);
chatManager.store(upd.chats);
userManager.store(upd.users);
for (TUpdate update : upd.updates) {
processUpdate(update, true);
}
}
}
public OnUpdateListener updateListener = null;
public interface OnUpdateListener {
public void onNewMessage(TMessage message, boolean fresh);
public void onMessageID(int id, long random_id, boolean fresh);
public void onReadMessages(int[] messages, boolean fresh);
public void onDeleteMessages(int[] messages, boolean fresh);
public void onRestoreMessages(int[] messages, boolean fresh);
public void onUserTyping(int user_id, boolean fresh);
public void onChatUserTyping(int chat_id, int user_id, boolean fresh);
public void onChatParticipants(TChatParticipants participants, boolean fresh);
public void onUserStatus(int user_id, TUserStatus status, boolean fresh);
public void onUserName(int user_id, String first_name, String last_name, boolean fresh);
public void onUserPhoto(int user_id, TUserProfilePhoto photo, boolean fresh);
public void onContactRegistered(int user_id, int date, boolean fresh);
public void onContactLink(int user_id, TMyLink my_link, TForeignLink foreign_link, boolean fresh);
public void onActivation(int user_id, boolean fresh);
public void onNewAuthorization(long auth_key_id, int date, String device, String location, boolean fresh);
public void onNewEncryptedMessage(TEncryptedMessage encrypted, TDecryptedMessage message, boolean fresh);
public void onEncryptedChatTyping(int chat_id, boolean fresh);
public void onEncryption(TEncryptedChat chat, int date, boolean fresh);
public void onEncryptedMessagesRead(int chat_id, int max_date, int date, boolean fresh);
}
public void processUpdate(TUpdate update, boolean fresh) {
//Log.i(TAG, "New " + (fresh ? "(fresh) " : "") + "update: " + update);
if (update instanceof UpdateNewMessage) {
updates_pts = Math.max(updates_pts, ((UpdateNewMessage) update).pts);
processNewMessage(((UpdateNewMessage) update).message, fresh);
} else if (update instanceof UpdateMessageID) {
if (updateListener != null)
updateListener.onMessageID(((UpdateMessageID) update).id, ((UpdateMessageID) update).random_id, fresh);
} else if (update instanceof UpdateReadMessages) {
updates_pts = Math.max(updates_pts, ((UpdateReadMessages) update).pts);
int[] messages = ((UpdateReadMessages) update).messages;
for (int message_id : messages) {
TMessage message = messageManager.get(message_id);
if (message.unread) {
message.unread = false;
messageManager.store(message);
// TODO: update dialog
}
}
if (updateListener != null)
updateListener.onReadMessages(messages, fresh);
} else if (update instanceof UpdateDeleteMessages) {
updates_pts = Math.max(updates_pts, ((UpdateDeleteMessages) update).pts);
if (updateListener != null)
updateListener.onDeleteMessages(((UpdateDeleteMessages) update).messages, fresh);
} else if (update instanceof UpdateRestoreMessages) {
updates_pts = Math.max(updates_pts, ((UpdateDeleteMessages) update).pts);
if (updateListener != null)
updateListener.onRestoreMessages(((UpdateRestoreMessages) update).messages, fresh);
} else if (update instanceof UpdateUserTyping) {
typingManager.userTyping(((UpdateUserTyping) update).user_id);
if (updateListener != null)
updateListener.onUserTyping(((UpdateUserTyping) update).user_id, fresh);
} else if (update instanceof UpdateChatUserTyping) {
typingManager.userTyping(-((UpdateChatUserTyping) update).chat_id, ((UpdateChatUserTyping) update).user_id);
if (updateListener != null)
updateListener.onChatUserTyping(((UpdateChatUserTyping) update).chat_id, ((UpdateChatUserTyping) update).user_id, fresh);
} else if (update instanceof UpdateChatParticipants) {
if (updateListener != null)
updateListener.onChatParticipants(((UpdateChatParticipants) update).participants, fresh);
} else if (update instanceof UpdateUserStatus) {
int user_id = ((UpdateUserStatus) update).user_id;
TUserStatus status = ((UpdateUserStatus) update).status;
TUser user = userManager.get(user_id);
if (!(user instanceof UserEmpty)) {
user.status = status;
userManager.store(user);
}
if (updateListener != null)
updateListener.onUserStatus(user_id, status, fresh);
} else if (update instanceof UpdateUserName) {
int user_id = ((UpdateUserName) update).user_id;
String first_name = ((UpdateUserName) update).first_name;
String last_name = ((UpdateUserName) update).last_name;
TUser user = userManager.get(user_id);
if (!(user instanceof UserEmpty)) {
user.first_name = first_name;
user.last_name = last_name;
userManager.store(user);
}
if (updateListener != null)
updateListener.onUserName(user_id, first_name, last_name, fresh);
} else if (update instanceof UpdateUserPhoto) {
int user_id = ((UpdateUserPhoto) update).user_id;
TUserProfilePhoto photo = ((UpdateUserPhoto) update).photo;
TUser user = userManager.get(user_id);
if (!(user instanceof UserEmpty)) {
user.photo = photo;
userManager.store(user);
}
if (updateListener != null)
updateListener.onUserPhoto(user_id, photo, fresh);
} else if (update instanceof UpdateContactRegistered) {
if (updateListener != null)
updateListener.onContactRegistered(((UpdateContactRegistered) update).user_id, ((UpdateContactRegistered) update).date, fresh);
} else if (update instanceof UpdateContactLink) {
if (updateListener != null)
updateListener.onContactLink(((UpdateContactLink) update).user_id, ((UpdateContactLink) update).my_link, ((UpdateContactLink) update).foreign_link, fresh);
} else if (update instanceof UpdateActivation) {
if (updateListener != null)
updateListener.onActivation(((UpdateActivation) update).user_id, fresh);
} else if (update instanceof UpdateNewAuthorization) {
if (updateListener != null)
updateListener.onNewAuthorization(((UpdateNewAuthorization) update).auth_key_id, ((UpdateNewAuthorization) update).date, ((UpdateNewAuthorization) update).device, ((UpdateNewAuthorization) update).location, fresh);
} else if (update instanceof UpdateNewEncryptedMessage) {
processNewEncryptedMessage(((UpdateNewEncryptedMessage) update).message, fresh);
} else if (update instanceof UpdateEncryptedChatTyping) {
typingManager.userEncryptedTyping(update.chat_id);
if (updateListener != null)
updateListener.onEncryptedChatTyping(update.chat_id, fresh);
} else if (update instanceof UpdateEncryption) {
dialogManager.updateEncryptedChat(update.chat);
if (updateListener != null)
updateListener.onEncryption(update.chat, update.date, fresh);
} else if (update instanceof UpdateEncryptedMessagesRead) {
if (updateListener != null)
updateListener.onEncryptedMessagesRead(update.chat_id, update.max_date, update.date, fresh);
}
}
public void processNewEncryptedMessage(TEncryptedMessage message, boolean fresh) {
EncryptedDialog dialog = dialogManager.chats.get(message.chat_id);
if (dialog != null) {
if (dialog.chat instanceof EncryptedChat) {
try {
byte[] msg_key = Arrays.copyOfRange(message.bytes, 8, 24);
int x = 0;
byte[] sha1_a = SHA1(concat(msg_key, substr(dialog.key, x, 32)));
byte[] sha1_b = SHA1(concat(substr(dialog.key, 32 + x, 16), msg_key, substr(dialog.key, 48 + x, 16)));
byte[] sha1_c = SHA1(concat(substr(dialog.key, 64 + x, 32), msg_key));
byte[] sha1_d = SHA1(concat(msg_key, substr(dialog.key, 96 + x, 32)));
byte[] aes_key = concat(substr(sha1_a, 0, 8), substr(sha1_b, 8, 12), substr(sha1_c, 4, 12));
byte[] aes_iv = concat(substr(sha1_a, 8, 12), substr(sha1_b, 0, 8), substr(sha1_c, 16, 4), substr(sha1_d, 0, 8));
ByteBuffer buffer = ByteBuffer.wrap(AESDecrypt(message.bytes, 24, message.bytes.length - 24, aes_key, aes_iv));
buffer.order(ByteOrder.LITTLE_ENDIAN);
int size = buffer.getInt();
TLObject payload = TL.read(buffer);
TDecryptedMessage decrypted = null;
if (payload instanceof TDecryptedMessage) {
decrypted = (TDecryptedMessage) payload;
} else
if ((payload instanceof DecryptedMessageLayer) && ((DecryptedMessageLayer) payload).layer <= ru.denull.wire.model.Config.max_supported_layer) {
decrypted = (TDecryptedMessage) ((DecryptedMessageLayer) payload).message;
} else {
// TODO: show error
}
if (decrypted != null && decrypted instanceof DecryptedMessage) {
TMessage stub = new Message(messageManager.nextMessageID, (dialog.chat.admin_id == me.id) ? dialog.chat.participant_id : dialog.chat.admin_id, new PeerUser(me.id), false, true, message.date, decrypted.message, new MessageMediaEmpty());
messageManager.store(stub);
dialog.top_message = stub.id;
dialogManager.addEncryptedMessage(message.chat_id, (dialog.chat.admin_id == me.id) ? dialog.chat.participant_id : dialog.chat.admin_id, message, decrypted);
typingManager.userTyping((dialog.chat.admin_id == me.id) ? dialog.chat.participant_id : dialog.chat.admin_id, false);
messageManager.nextMessageID++; // TODO: replace this
if (updateListener != null)
updateListener.onNewEncryptedMessage(message, decrypted, fresh);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public void processNewMessage(TMessage message, boolean fresh) {
boolean exists = !(messageManager.get(message.id) instanceof MessageEmpty);
messageManager.store(message);
if (exists) {
return;
}
dialogManager.addMessage(message);
if (message.to_id instanceof PeerChat) {
typingManager.userTyping(-((PeerChat) message.to_id).chat_id, message.from_id, false);
} else if (message.to_id instanceof PeerUser) {
typingManager.userTyping(message.from_id, false);
}
if (updateListener != null) {
updateListener.onNewMessage(message, fresh);
} else { // app paused, show notification
if (fresh) {
//
}
}
}
// Connect to given socket
public class ConnectTask implements Runnable {
String address;
int port;
boolean main;
boolean forceReconnect;
ConnectTaskCallback callback;
public ConnectTask(String address, int port, boolean main, boolean forceReconnect, ConnectTaskCallback callback) {
super();
this.address = address;
this.port = port;
this.main = main;
this.forceReconnect = forceReconnect;
this.callback = callback;
}
public void run() {
String serverId = address + ":" + port;
Server server = null;
synchronized (servers) {
// try to get from socket pool
if (servers.containsKey(serverId)) {
server = servers.get(serverId);
if (forceReconnect) {
try {
server.reconnect();
} catch (IOException e) {
e.printStackTrace();
if (callback != null) {
callback.error(500, "Unable to reconnect");
}
}
}
}
// make new connection
if (server == null) {
try {
server = new Server(DataService.this, address, port);
} catch (IOException e) {
e.printStackTrace();
if (callback != null) {
callback.error(500, "Unable to connect");
}
return;
}
servers.put(serverId, server);
}
}
// set as our main socket
if (main) {
if (mainServer != null) {
mainServer.disconnect();
}
mainServer = server;
}
if (callback != null) {
callback.done(server);
}
}
}
public interface ConnectTaskCallback {
public void done(Server server);
public void error(int code, String message);
}
public void connect(String address, int port, boolean main, boolean forceReconnect, ConnectTaskCallback callback) {
if (!main && !forceReconnect && servers.containsKey(address + ":" + port)) {
callback.done(servers.get(address + ":" + port));
return;
}
threadPool.submit(new ConnectTask(address, port, main, forceReconnect, callback));
}
public void connect(boolean forceReconnect, ConnectTaskCallback callback) {
String[] server = defaultServer.split(":");
connect(server[0], Integer.parseInt(server[1]), true, forceReconnect, callback);
}
public void connect(final int dc_id, final boolean main, final boolean forceReconnect, final ConnectTaskCallback callback) {
if (dcConfig != null) {
for (TDcOption opt : dcConfig.dc_options) {
DcOption option = (DcOption) opt;
if (mainServer != null && option.ip_address.equals(mainServer.address) && option.port == mainServer.port) {
mainServerID = option.id;
}
}
for (TDcOption opt : dcConfig.dc_options) {
final DcOption option = (DcOption) opt;
if (option.id == dc_id) {
connect(option.ip_address, option.port, main, forceReconnect, callback);
return;
}
}
} else
if (mainServer != null) {
mainServer.call(new GetConfig(), new Server.RPCCallback<Config>() {
public void done(Config result) {
dcConfig = result;
connect(dc_id, main, forceReconnect, callback);
}
public void error(int code, String message) {
callback.error(code, message);
}
});
}
}
public void connectAndPrepare(boolean forceReconnect, final boolean forceRegenerateKey, final AuthCallback callback) {
connect(forceReconnect, new ConnectTaskCallback() {
public void done(Server server) {
server.auth.generateKey(forceRegenerateKey, callback);
}
public void error(int code, String message) {
callback.error();
}
});
}
public void connectAndPrepare(final int dc_id, final boolean main, final boolean forceReconnect, final boolean forceRegenerateKey, final AuthCallback callback) {
if (mainServerID == 0 || dc_id == mainServerID) {
connect(dc_id, main, forceReconnect, new ConnectTaskCallback() {
public void done(Server server) {
server.auth.generateKey(forceRegenerateKey, callback);
}
public void error(int code, String message) {
callback.error();
}
});
} else {
mainServer.call(new ExportAuthorization(dc_id), new Server.RPCCallback<ExportedAuthorization>() {
public void done(final ExportedAuthorization result) {
connect(dc_id, main, forceReconnect, new ConnectTaskCallback() {
public void done(Server server) {
if (server.auth.authorized) {
server.auth.generateKey(forceRegenerateKey, callback);
} else {
server.auth.generateKey(forceRegenerateKey, new AuthCallback() {
public void done(final Server server, final byte[] auth_key) {
server.call(new ImportAuthorization(result.id, result.bytes), new Server.RPCCallback<Authorization>() {
public void done(Authorization result) {
server.auth.logged();
callback.done(server, auth_key);
}
public void error(int code, String message) {
callback.error();
}
});
}
public void error() {
callback.error();
}
});
}
}
public void error(int code, String message) {
callback.error();
}
});
}
public void error(int code, String message) {
callback.error();
}
});
}
}
// Read from given socket
public class ReadTask implements Runnable {
Server server;
ReadTaskCallback callback;
public ReadTask(Server server, ReadTaskCallback callback) {
super();
this.server = server;
this.callback = callback;
}
public void run() {
if (server == null || !server.transport.isConnected()) {
if (callback != null) {
callback.error(500, "Socket is not connected");
}
return;
}
while (!Thread.interrupted()) {
try {
ByteBuffer buffer = server.transport.receive();
//Log.d(TAG, "<< " + server.address + " << " + buffer.limit() + " bytes");
if (callback != null) {
callback.incoming(buffer);
}
} catch (ClosedByInterruptException e) {
break;
} catch (ClosedChannelException e) {
//break;
} catch (SocketException e) {
Log.w(TAG, "Socket exception: " + e.getMessage());
while (!Thread.interrupted() && !server.resolveNetworkProblem(false)) {
// restoring connection
}
} catch (IOException e) { // Connection reset by peer
Log.w(TAG, "Socket exception: " + e.getMessage());
while (!Thread.interrupted() && !server.resolveNetworkProblem(false)) {
// restoring connection
}
} catch (Exception e) {
e.printStackTrace();
if (callback != null) {
callback.error(500, e.getMessage());
}
}
}
if (callback != null) {
callback.done(server);
}
}
}
public interface ReadTaskCallback {
public void done(Server server);
public void incoming(ByteBuffer packet);
public void error(int code, String message);
}
public void read(String address, int port, ReadTaskCallback callback) {
String serverId = address + ":" + port;
if (!servers.containsKey(serverId)) {
return;
}
read(servers.get(serverId), callback);
}
public void read(Server server, ReadTaskCallback callback) {
threadPool.submit(new ReadTask(server, callback));
}
// Write to given socket
public class WriteTask implements Runnable {
Server server;
ByteBuffer buffer;
WriteTaskCallback callback;
public WriteTask(Server server, ByteBuffer buffer, WriteTaskCallback callback) {
super();
this.server = server;
this.buffer = buffer;
this.callback = callback;
}
public void run() {
if (server == null || !server.transport.isConnected()) {
if (callback != null) {
callback.error(500, "Socket is not connected");
}
return;
}
try {
server.transport.send(buffer);
} catch (SocketException e) {
Log.w(TAG, "Socket exception: " + e.getMessage());
if (server.resolveNetworkProblem(true)) {
try {
server.transport.send(buffer);
} catch (Exception e1) {
callback.error(500, e.getMessage());
}
} else {
callback.error(500, e.getMessage());
}
} catch (Exception e) {
callback.error(500, e.getMessage());
}
if (callback != null) {
callback.done(server);
}
}
}
public interface WriteTaskCallback {
public void done(Server server);
public void error(int code, String message);
}
public void write(String address, int port, ByteBuffer buffer, WriteTaskCallback callback) {
String serverId = address + ":" + port;
if (!servers.containsKey(serverId)) {
return;
}
write(servers.get(serverId), buffer, callback);
}
public void write(Server server, ByteBuffer buffer, WriteTaskCallback callback) {
//Log.d(TAG, ">> " + server.address + " >> " + buffer.limit() + " bytes");
threadPool.submit(new WriteTask(server, buffer, callback));
}
// Disconnect from given socket
public class DisconnectTask implements Runnable {
Server[] server;
DisconnectTaskCallback callback;
public DisconnectTask(Server[] server, DisconnectTaskCallback callback) {
super();
this.server = server;
this.callback = callback;
}
public void run() {
for (Server s : server) {
if (s != null) {
s.disconnect();
}
}
if (callback != null) {
callback.done();
}
}
}
public interface DisconnectTaskCallback {
public void done();
public void error(int code, String message);
}
public void disconnect(String address, int port, DisconnectTaskCallback callback) {
String serverId = address + ":" + port;
if (!servers.containsKey(serverId)) {
return;
}
disconnect(servers.get(serverId), callback);
}
public void disconnect(Server server, DisconnectTaskCallback callback) {
disconnect(new Server[]{ server }, callback);
}
public void disconnect(Server[] server, DisconnectTaskCallback callback) {
threadPool.submit(new DisconnectTask(server, callback));
}
public void disconnectAll(DisconnectTaskCallback callback) {
disconnect((Server[]) servers.values().toArray(new Server[0]), callback);
}
public File getCacheDir() {
// TODO Auto-generated method stub
return new File(System.getProperty("java.io.tmpdir") + "/wire");
}
// TODO: implement possibility not to store data
public void setStoragePolicy(boolean storePhone, boolean storeKeys, boolean storeCache) {
this.storePhone = storePhone;
this.storeKeys = storeKeys;
this.storeCache = storeCache;
// If we already stored something, we need to clear it
if (!storePhone) {
pref.remove("phone");
}
if (!storeKeys) {
try {
pref.clear();
} catch (BackingStoreException e) {
e.printStackTrace();
}
}
if (!storeCache) {
cacheManager.clearAll();
}
}
public void setPhone(String string) {
// TODO Auto-generated method stub
}
}