/** * Computes the java String object hash code (32 bit) as the disaster ID of the message TODO: For * security reasons (to prevent intentional hash collisions), this should be a cryptographic hash * function instead of the string hash. * * @param cv * @return */ private int getDisasterID(ContentValues cv) { String text = cv.getAsString(DirectMessages.COL_TEXT); String senderId; if (!cv.containsKey(DirectMessages.COL_SENDER) || (cv.getAsString(DirectMessages.COL_SENDER) == null)) { senderId = LoginActivity.getTwitterId(getContext()).toString(); } else { senderId = cv.getAsString(DirectMessages.COL_SENDER); } return (new String(text + senderId)).hashCode(); }
/** Insert a direct message into the DB */ @Override public synchronized Uri insert(Uri uri, ContentValues values) { int disasterId; Cursor c = null; Uri insertUri = null; // the return value; switch (dmUriMatcher.match(uri)) { case LIST_NORMAL: Log.d(TAG, "Insert LIST_NORMAL"); /* * First, we check if we already have a direct messages with the * same disaster ID. If yes, two cases are possible 1 If the * existing message is a message of our own which was flagged to * insert, the insert operation may have been successful but the * success was not registered locally. In this case we update the * new message with the new information 2 It may be a hash function * collision (two different messages have the same hash code) * Probability of this should be small. */ disasterId = getDisasterID(values); c = database.query( DBOpenHelper.TABLE_DMS, null, DirectMessages.COL_DISASTERID + "=" + disasterId, null, null, null, null); if (c.getCount() > 0) { c.moveToFirst(); while (!c.isAfterLast()) { boolean isOurs = Long.toString(c.getLong(c.getColumnIndex(DirectMessages.COL_SENDER))) .equals(LoginActivity.getTwitterId(getContext())); boolean isForUs = Long.toString(c.getLong(c.getColumnIndex(DirectMessages.COL_RECEIVER))) .equals(LoginActivity.getTwitterId(getContext())); if (isOurs || isForUs) { // clear the to insert flag int flags = c.getInt(c.getColumnIndex(DirectMessages.COL_FLAGS)); values.put(DirectMessages.COL_FLAGS, flags & (~DirectMessages.FLAG_TO_INSERT)); Uri updateUri = Uri.parse( "content://" + DirectMessages.DM_AUTHORITY + "/" + DirectMessages.DMS + "/" + Integer.toString(c.getInt(c.getColumnIndex("_id")))); update(updateUri, values, null, null); return updateUri; } c.moveToNext(); } } c.close(); // if none of the before was true, this is a proper new tweet which // we now insert insertUri = insertDM(values); // delete everything that now falls out of the buffer purgeDMs(values); break; case LIST_DISASTER: Log.d(TAG, "Insert LIST_DISASTER"); // in disaster mode, we set the is disaster flag, encrypt and sign // the message // and sign the tweet (if we have a certificate for our key pair) values.put(DirectMessages.COL_ISDISASTER, 1); // if we already have a disaster tweet with the same disaster ID, // we discard the new one disasterId = getDisasterID(values); c = database.query( DBOpenHelper.TABLE_DMS, null, DirectMessages.COL_DISASTERID + "=" + disasterId + " AND " + DirectMessages.COL_ISDISASTER + ">0", null, null, null, null); if (c.getCount() > 0) { c.moveToFirst(); Uri oldUri = Uri.parse( "content://" + DirectMessages.DM_AUTHORITY + "/" + DirectMessages.DMS + "/" + Long.toString(c.getLong(c.getColumnIndex("_id")))); c.close(); return oldUri; } CertificateManager cm = new CertificateManager(getContext()); KeyManager km = new KeyManager(getContext().getApplicationContext()); // verify whether I was the author or not if (LoginActivity.getTwitterId(getContext()) .equals(values.getAsInteger(DirectMessages.COL_SENDER).toString())) { if (cm.hasCertificate()) { // we put the signature String text = values.getAsString(DirectMessages.COL_TEXT); String userId = LoginActivity.getTwitterId(getContext()).toString(); String signature = km.getSignature(new String(text + userId)); values.put(DirectMessages.COL_SIGNATURE, signature); // and the certificate values.put(DirectMessages.COL_CERTIFICATE, cm.getCertificate()); // and set the is_verified flag to show that the tweet is // signed values.put(DirectMessages.COL_ISVERIFIED, 1); // Long twitterId = // getIdFromScreenName(values.getAsString(DirectMessages.COL_RECEIVER_SCREENNAME)); // TODO: obtain peers public keys and encrypt values.put(DirectMessages.COL_CRYPTEXT, text); /* * if (twitterId != null) { String cipherText = * km.encrypt(text,twitterId ); Log.i(TAG, "ciphertext: " + * cipherText ); if (cipherText != null) { * values.put(DirectMessages.COL_CRYPTEXT,cipherText ); * * } } */ } else values.put(DirectMessages.COL_ISVERIFIED, 0); } else { // Is it for me? if (values .getAsLong(DirectMessages.COL_RECEIVER) .toString() .equals(LoginActivity.getTwitterId(getContext()))) { values.put( DirectMessages.COL_BUFFER, DirectMessages.BUFFER_DISASTER_ME | DirectMessages.BUFFER_MESSAGES); // TODO: DECRYPT THE MESSAGE // String plainText = // km.decrypt(values.getAsString(DirectMessages.COL_CRYPTEXT)); String plainText = values.getAsString(DirectMessages.COL_CRYPTEXT); // values.remove(DirectMessages.COL_CRYPTEXT); values.put(DirectMessages.COL_TEXT, plainText); // check signature String signature = values.getAsString(DirectMessages.COL_SIGNATURE); String certificate = values.getAsString(DirectMessages.COL_CERTIFICATE); String textForSignatureCheck = plainText + values.getAsString(DirectMessages.COL_SENDER); if (km.checkSignature(cm.parsePem(certificate), signature, textForSignatureCheck)) { values.put(DirectMessages.COL_ISVERIFIED, 1); } else values.put(DirectMessages.COL_ISVERIFIED, 0); } else values.put(DirectMessages.COL_BUFFER, DirectMessages.BUFFER_DISASTER_OTHERS); } c.close(); insertUri = insertDM(values); // purgeTweets(values); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } return insertUri; }
/** Query the direct messages table TODO: Create the queries more elegantly.. */ @Override public synchronized Cursor query( Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder) { if (TextUtils.isEmpty(sortOrder)) sortOrder = Tweets.DEFAULT_SORT_ORDER; Cursor c = null; String sql = null; Intent i = null; switch (dmUriMatcher.match(uri)) { case DMS: Log.d(TAG, "Query DMS"); c = database.query( DBOpenHelper.TABLE_DMS, projection, where, whereArgs, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), DirectMessages.CONTENT_URI); break; case DM_ID: Log.d(TAG, "Query DM_ID"); sql = "SELECT * " + "FROM " + DBOpenHelper.TABLE_DMS + " " + "WHERE " + DBOpenHelper.TABLE_DMS + "._id=" + uri.getLastPathSegment() + ";"; c = database.rawQuery(sql, null); c.setNotificationUri(getContext().getContentResolver(), uri); break; case USER: Log.d(TAG, "Query USER"); // get the twitter user id sql = "SELECT " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "FROM " + DBOpenHelper.TABLE_USERS + " " + "WHERE " + TwitterUsers.COL_ROW_ID + "=" + uri.getLastPathSegment() + ";"; c = database.rawQuery(sql, null); if (c.getCount() == 0) return null; // this should not happen c.moveToFirst(); long twitterUserId = c.getLong(c.getColumnIndex(TwitterUsers.COL_TWITTER_USER_ID)); c.close(); // get the direct messages sql = "SELECT " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_ROW_ID + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_TEXT + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_ISDISASTER + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_FLAGS + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_DMID + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_ROW_ID + " AS " + COL_USER_ROW_ID + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_SCREEN_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_PROFILE_IMAGE_URI + " FROM " + DBOpenHelper.TABLE_DMS + " " + "JOIN " + DBOpenHelper.TABLE_USERS + " " + "ON " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "WHERE " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_RECEIVER + "=" + twitterUserId + " " + "OR " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + "=" + twitterUserId + " " + "ORDER BY " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + " DESC " + "LIMIT 100"; c = database.rawQuery(sql, null); // TODO: Correct notification URI c.setNotificationUri(getContext().getContentResolver(), DirectMessages.CONTENT_URI); break; case USERS: Log.d(TAG, "Query USERS"); // selects the last DM of which the given user is either a sender or // recepient String subQuery1 = "SELECT " + DirectMessages.COL_TEXT + " " + "FROM " + DBOpenHelper.TABLE_DMS + " " + "WHERE " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_RECEIVER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "OR " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "ORDER BY " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + " DESC " + "LIMIT 1"; // selects the timestamp of the last DM of which the given user is // either a sender or recepient String subQuery2 = "SELECT " + DirectMessages.COL_CREATED + " " + "FROM " + DBOpenHelper.TABLE_DMS + " " + "WHERE " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_RECEIVER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "OR " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "ORDER BY " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + " DESC " + "LIMIT 1"; // selects all users who have sent or received at least one DM sql = "select " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_ROW_ID + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_SCREEN_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_PROFILE_IMAGE_URI + ", " + "(" + subQuery1 + ") AS text, " + "(" + subQuery2 + ") AS created " + "FROM " + DBOpenHelper.TABLE_USERS + " " + "LEFT JOIN " + DBOpenHelper.TABLE_DMS + " AS sent ON " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + "=sent." + DirectMessages.COL_SENDER + " " + "LEFT JOIN " + DBOpenHelper.TABLE_DMS + " AS received ON " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + "=received." + DirectMessages.COL_RECEIVER + " " + "WHERE " + "(sent." + DirectMessages.COL_TEXT + " IS NOT NULL OR " + "received." + DirectMessages.COL_TEXT + " IS NOT NULL) " + "AND " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + "<>" + LoginActivity.getTwitterId(getContext()) + " " + "GROUP BY " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "ORDER BY created DESC LIMIT 100;"; Log.e(TAG, sql); c = database.rawQuery(sql, null); // TODO: Correct notification URI c.setNotificationUri(getContext().getContentResolver(), DirectMessages.CONTENT_URI); // start synch service with a synch DMs request i = new Intent(getContext(), TwitterSyncService.class); i.putExtra( TwitterSyncService.EXTRA_KEY_ACTION, TwitterSyncService.EXTRA_ACTION_SYNC_MESSAGES); getContext().startService(i); break; case LIST_ALL: // TODO break; case LIST_DISASTER: Log.i(TAG, "Query DMS Disaster"); c = database.query( DBOpenHelper.TABLE_DMS, projection, DirectMessages.COL_BUFFER + "&" + DirectMessages.BUFFER_MYDISASTER + "!=0" + " OR " + DirectMessages.COL_BUFFER + "&" + DirectMessages.BUFFER_DISASTER_OTHERS + "!=0", whereArgs, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), DirectMessages.CONTENT_URI); break; case LIST_NORMAL: // TODO break; case INCOMING_RECEIVED_AFTER: Log.d(TAG, "Query INCOMING_RECEIVED_AFTER"); sql = "SELECT " + DBOpenHelper.TABLE_DMS + "._id, " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_TEXT + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_ISDISASTER + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_FLAGS + ", " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_DMID + ", " + DBOpenHelper.TABLE_USERS + "._id AS " + COL_USER_ROW_ID + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_SCREEN_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_NAME + ", " + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_PROFILE_IMAGE_URI + " " + "FROM " + DBOpenHelper.TABLE_DMS + " " + "JOIN " + DBOpenHelper.TABLE_USERS + " " + "ON " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_SENDER + "=" + DBOpenHelper.TABLE_USERS + "." + TwitterUsers.COL_TWITTER_USER_ID + " " + "WHERE " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_RECEIVER + "==" + LoginActivity.getTwitterId(getContext()) + " AND " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_RECEIVED + ">" + uri.getLastPathSegment() + " " + "ORDER BY " + DBOpenHelper.TABLE_DMS + "." + DirectMessages.COL_CREATED + " DESC " + "LIMIT 100"; c = database.rawQuery(sql, null); // TODO: Correct notification URI c.setNotificationUri(getContext().getContentResolver(), DirectMessages.CONTENT_URI); break; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } return c; }