1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.mail.internet; 18 19import com.android.email.mail.MessagingException; 20import com.android.email.mail.Multipart; 21import com.android.email.mail.Part; 22import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart; 23import com.android.email.provider.AttachmentProvider; 24 25import android.content.ContentResolver; 26import android.net.Uri; 27 28import java.util.regex.Matcher; 29import java.util.regex.Pattern; 30 31public class EmailHtmlUtil { 32 33 // Regex that matches characters that have special meaning in HTML. '<', '>', '&' and 34 // multiple continuous spaces. 35 private static final Pattern PLAIN_TEXT_TO_ESCAPE = Pattern.compile("[<>&]| {2,}|\r?\n"); 36 37 //TODO: make resolveInlineImage() work in the new content provider model. 38 /** 39 * Resolve content-id reference in src attribute of img tag to AttachmentProvider's 40 * content uri. This method calls itself recursively at most the number of 41 * LocalAttachmentPart that mime type is image and has content id. 42 * The attribute src="cid:content_id" is resolved as src="content://...". 43 * This method is package scope for testing purpose. 44 * 45 * @param text html email text 46 * @param part mime part which may contain inline image 47 * @return html text in which src attribute of img tag may be replaced with content uri 48 */ 49 public static String resolveInlineImage( 50 ContentResolver resolver, long accountId, String text, Part part, int depth) 51 throws MessagingException { 52 // avoid too deep recursive call. 53 if (depth >= 10 || text == null) { 54 return text; 55 } 56 String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); 57 String contentId = part.getContentId(); 58 if (contentType.startsWith("image/") && 59 contentId != null && 60 part instanceof LocalAttachmentBodyPart) { 61 LocalAttachmentBodyPart attachment = (LocalAttachmentBodyPart)part; 62 Uri attachmentUri = 63 AttachmentProvider.getAttachmentUri(accountId, attachment.getAttachmentId()); 64 Uri contentUri = 65 AttachmentProvider.resolveAttachmentIdToContentUri(resolver, attachmentUri); 66 // Regexp which matches ' src="cid:contentId"'. 67 String contentIdRe = "\\s+(?i)src=\"cid(?-i):\\Q" + contentId + "\\E\""; 68 // Replace all occurrences of src attribute with ' src="content://contentUri"'. 69 text = text.replaceAll(contentIdRe, " src=\"" + contentUri + "\""); 70 } 71 72 if (part.getBody() instanceof Multipart) { 73 Multipart mp = (Multipart)part.getBody(); 74 for (int i = 0; i < mp.getCount(); i++) { 75 text = resolveInlineImage(resolver, accountId, text, mp.getBodyPart(i), depth + 1); 76 } 77 } 78 79 return text; 80 } 81 82 /** 83 * Escape some special character as HTML escape sequence. 84 * 85 * @param text Text to be displayed using WebView. 86 * @return Text correctly escaped. 87 */ 88 public static String escapeCharacterToDisplay(String text) { 89 Pattern pattern = PLAIN_TEXT_TO_ESCAPE; 90 Matcher match = pattern.matcher(text); 91 92 if (match.find()) { 93 StringBuilder out = new StringBuilder(); 94 int end = 0; 95 do { 96 int start = match.start(); 97 out.append(text.substring(end, start)); 98 end = match.end(); 99 int c = text.codePointAt(start); 100 if (c == ' ') { 101 // Escape successive spaces into series of " ". 102 for (int i = 1, n = end - start; i < n; ++i) { 103 out.append(" "); 104 } 105 out.append(' '); 106 } else if (c == '\r' || c == '\n') { 107 out.append("<br>"); 108 } else if (c == '<') { 109 out.append("<"); 110 } else if (c == '>') { 111 out.append(">"); 112 } else if (c == '&') { 113 out.append("&"); 114 } 115 } while (match.find()); 116 out.append(text.substring(end)); 117 text = out.toString(); 118 } 119 return text; 120 } 121} 122