MAP: make MMS parsing more robust

Some devices are not following the specs correctly and send plan-text as MMS without the right headers.
This patch makes the MMS parser more robust against faulty MMS messages.

Bug: 11161383
Change-Id: If5e59f9daaab4537cfe5d06e6203ae783e311fd3
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index 11ff0ff..58528f6 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -662,9 +662,7 @@
                         /* Send message if folder is outbox */
                         /* to do, support MMS in the future */
                         /*
-                        if (folder.equals("outbox")) {
                            handle = sendMmsMessage(folder, phone, (BluetoothMapbMessageMmsEmail)msg);
-                        }
                         */
                         break;
                     }
@@ -720,22 +718,16 @@
          *else if folder !outbox:
          *1) push message to folder
          * */
-        if (folder != null && (folder.equalsIgnoreCase("outbox")||  folder.equalsIgnoreCase("drafts"))) {
-            long handle = pushMmsToFolder(Mms.MESSAGE_BOX_DRAFTS, to_address, msg);
-            /* if invalid handle (-1) then just return the handle - else continue sending (if folder is outbox) */
-            if (BluetoothMapAppParams.INVALID_VALUE_PARAMETER != handle && folder.equalsIgnoreCase("outbox")) {
-                moveDraftToOutbox(handle);
+        long handle = pushMmsToFolder(Mms.MESSAGE_BOX_DRAFTS, to_address, msg);
+        /* if invalid handle (-1) then just return the handle - else continue sending (if folder is outbox) */
+        if (BluetoothMapAppParams.INVALID_VALUE_PARAMETER != handle && folder.equalsIgnoreCase("outbox")) {
+            moveDraftToOutbox(handle);
 
-                Intent sendIntent = new Intent("android.intent.action.MMS_SEND_OUTBOX_MSG");
-                Log.d(TAG, "broadcasting intent: "+sendIntent.toString());
-                mContext.sendBroadcast(sendIntent);
-            }
-            return handle;
-        } else {
-            /* not allowed to push mms to anything but outbox/drafts */
-            throw  new IllegalArgumentException("Cannot push message to other folders than outbox/drafts");
+            Intent sendIntent = new Intent("android.intent.action.MMS_SEND_OUTBOX_MSG");
+            Log.d(TAG, "broadcasting intent: "+sendIntent.toString());
+            mContext.sendBroadcast(sendIntent);
         }
-
+        return handle;
     }
 
 
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index a876740..932645c 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -239,10 +239,11 @@
             if(folderName == null || folderName.equals("")) {
                 folderName = mCurrentFolder.getName();
             }
-            if(!folderName.equals("outbox") && !folderName.equals("draft")) {
+            if(!folderName.equalsIgnoreCase("outbox") && !folderName.equalsIgnoreCase("draft")) {
                 if(D) Log.d(TAG, "Push message only allowed to outbox and draft. folderName: " + folderName);
                 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
             }
+            folderName = folderName.toLowerCase();
             /*  - Read out the message
              *  - Decode into a bMessage
              *  - send it.
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java
index 0f1feac..0fcba3b 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java
@@ -481,6 +481,7 @@
                 {
                     if(contentTypeParts[j].contains("boundary")) {
                         boundary = contentTypeParts[j].split("boundary[\\s]*=", 2)[1].trim();
+                        boundary = boundary.replaceAll("\"", ""); // " is allowed around a boundary, but is not allowed as part of the boundary
                     }
                 }
             }
@@ -497,49 +498,53 @@
     private void parseMmsMimePart(String partStr) {
         String[] parts = partStr.split("\r\n\r\n", 2); // Split the header from the body
         String body;
+        MimePart newPart = addMimePart();
+        String partEncoding = encoding; /* Use the overall encoding as default */
         if(parts.length != 2) {
             body = partStr;
         } else {
             body = parts[1];
-        }
-        String[] headers = parts[0].split("\r\n");
-        MimePart newPart = addMimePart();
-        String partEncoding = encoding; /* Use the overall encoding as default */
+            String[] headers = parts[0].split("\r\n");
 
-        for(String header : headers) {
-            if(header.length() == 0)
-                continue;
+            for(String header : headers) {
+                if(header.length() == 0)
+                    continue;
 
-            if(header.trim() == "" || header.trim().equals("--")) // Skip empty lines(the \r\n after the boundary tag) and endBoundary tags
-                continue;
-            String[] headerParts = header.split(":",2);
-            if(headerParts.length != 2)
-                throw new IllegalArgumentException("part-Header not formatted correctly: " + header);
-            String headerType = headerParts[0].toUpperCase();
-            String headerValue = headerParts[1].trim();
-            if(headerType.contains("CONTENT-TYPE")) {
-                // TODO: extract charset? Only UTF-8 is allowed for TEXT typed parts
-                newPart.contentType = headerValue;
-                Log.d(TAG, "*** CONTENT-TYPE: " + newPart.contentType);
-            }
-            else if(headerType.contains("CONTENT-LOCATION")) {
-                // This is used if the smil refers to a file name in its src=
-                newPart.contentLocation = headerValue;
-                newPart.partName = headerValue;
-            }
-            else if(headerType.contains("CONTENT-TRANSFER-ENCODING")) {
-                partEncoding = headerValue;
-            }
-            else if(headerType.contains("CONTENT-ID")) {
-                // This is used if the smil refers to a cid:<xxx> in it's src=
-                newPart.contentId = headerValue;
-            }
-            else if(headerType.contains("CONTENT-DISPOSITION")) {
-                // This is used if the smil refers to a cid:<xxx> in it's src=
-                newPart.contentDisposition = headerValue;
-            }
-            else {
-                if(D) Log.w(TAG,"Skipping unknown part-header: " + headerType + " (" + header + ")");
+                if(header.trim() == "" || header.trim().equals("--")) // Skip empty lines(the \r\n after the boundary tag) and endBoundary tags
+                    continue;
+                String[] headerParts = header.split(":",2);
+                if(headerParts.length != 2) {
+                    //throw new IllegalArgumentException("part-Header not formatted correctly: " + header);
+                    // If we find a part without headers, treat the entire content as body
+                    body = partStr;
+                    break;
+                }
+                String headerType = headerParts[0].toUpperCase();
+                String headerValue = headerParts[1].trim();
+                if(headerType.contains("CONTENT-TYPE")) {
+                    // TODO: extract charset? Only UTF-8 is allowed for TEXT typed parts
+                    newPart.contentType = headerValue;
+                    Log.d(TAG, "*** CONTENT-TYPE: " + newPart.contentType);
+                }
+                else if(headerType.contains("CONTENT-LOCATION")) {
+                    // This is used if the smil refers to a file name in its src=
+                    newPart.contentLocation = headerValue;
+                    newPart.partName = headerValue;
+                }
+                else if(headerType.contains("CONTENT-TRANSFER-ENCODING")) {
+                    partEncoding = headerValue;
+                }
+                else if(headerType.contains("CONTENT-ID")) {
+                    // This is used if the smil refers to a cid:<xxx> in it's src=
+                    newPart.contentId = headerValue;
+                }
+                else if(headerType.contains("CONTENT-DISPOSITION")) {
+                    // This is used if the smil refers to a cid:<xxx> in it's src=
+                    newPart.contentDisposition = headerValue;
+                }
+                else {
+                    if(D) Log.w(TAG,"Skipping unknown part-header: " + headerType + " (" + header + ")");
+                }
             }
         }
         // Now for the body
@@ -607,7 +612,7 @@
         else
         {
             mimeParts = messageBody.split("--" + boundary);
-            for(int i = 0; i < mimeParts.length - 1; i++) {
+            for(int i = 1; i < mimeParts.length - 1; i++) {
                 String part = mimeParts[i];
                 if (part != null && (part.length() > 0))
                     parseMmsMimePart(part);