diff --git a/iped-app/resources/localization/iped-parsers-messages.properties b/iped-app/resources/localization/iped-parsers-messages.properties index c9836cfa6e..3d48cc6f15 100644 --- a/iped-app/resources/localization/iped-parsers-messages.properties +++ b/iped-app/resources/localization/iped-parsers-messages.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=pinned a message WhatsAppReport.AIThirdParty=This AI is from a third-party developer. Meta receives your AI chats to improve AI quality. WhatsAppReport.Over256MembersOnlyAdminsCanEdit=This group has over 256 members so now only admins can edit the groups settings. WhatsAppReport.SecurityNotificationsNoLongerAvailable=Security code notifications are no longer available for this chat. +UFEDChatParser.Broadcast=Broadcast +UFEDChatParser.Group=Group +UFEDChatParser.Status=Status +UFEDChatParser.Unknown=Unknown VCardParser.FormattedName=Formatted Name VCardParser.Name=Name VCardParser.Nickname=Nickname diff --git a/iped-app/resources/localization/iped-parsers-messages_de_DE.properties b/iped-app/resources/localization/iped-parsers-messages_de_DE.properties index 75f3839d10..53c65f6519 100644 --- a/iped-app/resources/localization/iped-parsers-messages_de_DE.properties +++ b/iped-app/resources/localization/iped-parsers-messages_de_DE.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=pinned a message[TBT] WhatsAppReport.AIThirdParty=This AI is from a third-party developer. Meta receives your AI chats to improve AI quality.[TBT] WhatsAppReport.Over256MembersOnlyAdminsCanEdit=Diese Gruppe hat mehr als 256 Mitglieder. Daher können jetzt nur noch Admins die Gruppeneinstellungen bearbeiten. WhatsAppReport.SecurityNotificationsNoLongerAvailable=Benachrichtigungen zur Sicherheitsnummer sind für diesen Chat nicht länger verfügbar. +UFEDChatParser.Broadcast=Broadcast[TBT] +UFEDChatParser.Group=Group[TBT] +UFEDChatParser.Status=Status[TBT] +UFEDChatParser.Unknown=Unknown[TBT] VCardParser.FormattedName=Name formatiert VCardParser.Name=Name VCardParser.Nickname=Nickname diff --git a/iped-app/resources/localization/iped-parsers-messages_es_AR.properties b/iped-app/resources/localization/iped-parsers-messages_es_AR.properties index 8f2e03964c..657264be21 100644 --- a/iped-app/resources/localization/iped-parsers-messages_es_AR.properties +++ b/iped-app/resources/localization/iped-parsers-messages_es_AR.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=pinned a message[TBT] WhatsAppReport.AIThirdParty=This AI is from a third-party developer. Meta receives your AI chats to improve AI quality.[TBT] WhatsAppReport.Over256MembersOnlyAdminsCanEdit=This group has over 256 members so now only admins can edit the groups settings.[TBT] WhatsAppReport.SecurityNotificationsNoLongerAvailable=Security code notifications are no longer available for this chat.[TBT] +UFEDChatParser.Broadcast=Broadcast[TBT] +UFEDChatParser.Group=Group[TBT] +UFEDChatParser.Status=Status[TBT] +UFEDChatParser.Unknown=Unknown[TBT] VCardParser.FormattedName=Nombre con formato VCardParser.Name=Nombre VCardParser.Nickname=Sobrenombre diff --git a/iped-app/resources/localization/iped-parsers-messages_fr_FR.properties b/iped-app/resources/localization/iped-parsers-messages_fr_FR.properties index b4f6b74443..6fb435b33d 100644 --- a/iped-app/resources/localization/iped-parsers-messages_fr_FR.properties +++ b/iped-app/resources/localization/iped-parsers-messages_fr_FR.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=a epinglé un message WhatsAppReport.AIThirdParty=Cette IA provient d'un développeur tiers. Meta reçoit vos discussions IA pour améliorer la qualité de l'IA. WhatsAppReport.Over256MembersOnlyAdminsCanEdit=Comme ce groupe inclut plus de 256 membres, désormais, seulement les admins peuvent modifier les paramètres du groupe. WhatsAppReport.SecurityNotificationsNoLongerAvailable=Les notifications relatives aux codes de sécurité ne sont plus disponibles pour cette discoussion. +UFEDChatParser.Broadcast=Broadcast[TBT] +UFEDChatParser.Group=Group[TBT] +UFEDChatParser.Status=Status[TBT] +UFEDChatParser.Unknown=Unknown[TBT] VCardParser.FormattedName=Nom formaté VCardParser.Name=Nom VCardParser.Nickname=Surnom diff --git a/iped-app/resources/localization/iped-parsers-messages_it_IT.properties b/iped-app/resources/localization/iped-parsers-messages_it_IT.properties index c03a30d7e1..9fccc84fda 100644 --- a/iped-app/resources/localization/iped-parsers-messages_it_IT.properties +++ b/iped-app/resources/localization/iped-parsers-messages_it_IT.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=pinned a message[TBT] WhatsAppReport.AIThirdParty=This AI is from a third-party developer. Meta receives your AI chats to improve AI quality.[TBT] WhatsAppReport.Over256MembersOnlyAdminsCanEdit=Dato che questo gruppo ha più di 256 membri, solo gli amministratori potranno modificarne le impostazioni. WhatsAppReport.SecurityNotificationsNoLongerAvailable=Le notifiche sul codice di sicurezza non sono più disponibili per questa chat. +UFEDChatParser.Broadcast=Broadcast[TBT] +UFEDChatParser.Group=Group[TBT] +UFEDChatParser.Status=Status[TBT] +UFEDChatParser.Unknown=Unknown[TBT] VCardParser.FormattedName=Nome formattato VCardParser.Name=Nome VCardParser.Nickname=Nickname diff --git a/iped-app/resources/localization/iped-parsers-messages_pt_BR.properties b/iped-app/resources/localization/iped-parsers-messages_pt_BR.properties index fae85613f2..ce941a11d9 100644 --- a/iped-app/resources/localization/iped-parsers-messages_pt_BR.properties +++ b/iped-app/resources/localization/iped-parsers-messages_pt_BR.properties @@ -333,6 +333,10 @@ WhatsAppReport.PinnedMessage=fixou uma mensagem WhatsAppReport.AIThirdParty=Esta IA pertence a um desenvolvedor terceirizado. A Meta recebe suas conversas com IA para melhorar a qualidade desse recurso. WhatsAppReport.Over256MembersOnlyAdminsCanEdit=Agora somente admins podem editar as configurações porque o grupo tem mais de 256 membros. WhatsAppReport.SecurityNotificationsNoLongerAvailable=As notificações sobre o código de segurança não estão mais disponíveis para esta conversa. +UFEDChatParser.Broadcast=Broadcast +UFEDChatParser.Group=Grupo +UFEDChatParser.Status=Status +UFEDChatParser.Unknown=Desconhecido VCardParser.FormattedName=Nome Formatado VCardParser.Name=Nome VCardParser.Nickname=Apelido diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/telegram/ReportGenerator.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/telegram/ReportGenerator.java index 6f8b16ba84..2efe13e10d 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/telegram/ReportGenerator.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/telegram/ReportGenerator.java @@ -444,8 +444,8 @@ private static void printMessageFileHeader(PrintWriter out, String title, byte[] + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " \n" //$NON-NLS-1$ //$NON-NLS-2$ + + "\n" + "\n" + "" diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/ReportGenerator.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/ReportGenerator.java index 6c4ba2171d..4d904d052b 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/ReportGenerator.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/ReportGenerator.java @@ -12,28 +12,21 @@ import iped.parsers.whatsapp.Message; import iped.parsers.whatsapp.Util; import iped.properties.ExtraProperties; -import iped.search.IItemSearcher; import iped.utils.EmojiUtil; import iped.utils.SimpleHTMLEncoder; /** - * * @author Fabio Melo Pfeifer */ public class ReportGenerator { private int minChatSplitSize = 6000000; - private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$ - private final SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); //$NON-NLS-1$ - private IItemSearcher searcher; + private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private final SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); private boolean firstHtml = true; private int currentMsg = 0; - public ReportGenerator(IItemSearcher searcher) { - this.searcher = searcher; - } - public int getNextMsgNum() { return currentMsg; } @@ -48,35 +41,101 @@ private static final String format(String text) { public byte[] generateNextChatHtml(IItemReader c, List msgs) throws UnsupportedEncodingException { - if ((!firstHtml && currentMsg == 0) || (currentMsg > 0 && currentMsg == msgs.size())) + if ((!firstHtml && currentMsg == 0) || (currentMsg > 0 && currentMsg == msgs.size())) { return null; + } ByteArrayOutputStream bout = new ByteArrayOutputStream(); - PrintWriter out = new PrintWriter(new OutputStreamWriter(bout, "UTF-8")); //$NON-NLS-1$ + PrintWriter out = new PrintWriter(new OutputStreamWriter(bout, "UTF-8")); - String[] split = c.getName().split("_", 3); //$NON-NLS-1$ + String[] split = c.getName().split("_", 3); String title = split[split.length - 1]; - printMessageFileHeader(out, title, c.getName(), null); + + String source = c.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Source"); + String phoneOwner = c.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "phoneOwner"); + String idProperty = "ufedId=" + c.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "id"); + String nameProperty = c.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Name"); + String chatType = c.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "ChatType"); + String[] parties = c.getMetadata().getValues(ExtraProperties.UFED_META_PREFIX + "Participants"); + + if (chatType != null) { + if (chatType.equals(UFEDChatParser.CHATTYPE_ONEONONE)) { + if (parties != null) { + title = ((parties.length > 1) && (parties[0].equals(phoneOwner)) ? parties[1] : parties[0]); + } else { + title = idProperty; + } + + } else if (chatType.equals(UFEDChatParser.CHATTYPE_GROUP)) { + title = UFEDChatParser.CHATTYPE_GROUP_TITLE + ": " + (nameProperty != null ? nameProperty : idProperty); + + } else if (chatType.equals(UFEDChatParser.CHATTYPE_BROADCAST)) { + if (parties != null) { + if ((parties.length == 1) && ((source != null) && (source.equals(UFEDChatParser.WHATSAPP) + || source.equals(UFEDChatParser.WHATSAPP_BUSINESS) + || source.equals(UFEDChatParser.TELEGRAM)))) { + // "Status" chat type (known from behaviour) + // NOTE: Apps with this behaviour should be added to this if condition + title = UFEDChatParser.CHATTYPE_STATUS_TITLE + ": " + parties[0]; + } else { + title = UFEDChatParser.CHATTYPE_BROADCAST_TITLE + ": " + + (nameProperty != null ? nameProperty : idProperty); + } + + } else { + title = UFEDChatParser.CHATTYPE_BROADCAST_TITLE + ": " + + (nameProperty != null ? nameProperty : idProperty); + } + + } else if (chatType.equals(UFEDChatParser.CHATTYPE_UNKNOWN)) { + if ((source != null) && (source.equals(UFEDChatParser.WHATSAPP) + || source.equals(UFEDChatParser.WHATSAPP_BUSINESS) || source.equals(UFEDChatParser.TELEGRAM))) { + // "Unknown" chat type regarding apps for which there are specific chat types + // NOTE: Apps with similar behaviour should be added to this if condition + title = UFEDChatParser.CHATTYPE_UNKNOWN_TITLE + ": " + idProperty; + + } else { + // "Unknown" chat type regarding apps for which there aren't specific chat types + // Communication type is derived from the number of participants + if ((parties != null) && (parties.length > 0)) { + if (parties.length > 2) { + title = UFEDChatParser.CHATTYPE_GROUP_TITLE + ": " + idProperty; + } else { + title = parties.length > 1 && parties[0].equals(phoneOwner) ? parties[1] : parties[0]; + } + + } else { + title = UFEDChatParser.CHATTYPE_UNKNOWN_TITLE + ": " + idProperty; + } + } + } else { + title = chatType + ": " + idProperty; + } + + } else { + title = idProperty; + } + + printMessageFileHeader(out, title, c.getName(), null, source); if (currentMsg > 0) - out.println("
" //$NON-NLS-1$ - + Messages.getString("WhatsAppReport.ChatContinuation") + "
"); //$NON-NLS-1$ //$NON-NLS-2$ + out.println("
" + + Messages.getString("WhatsAppReport.ChatContinuation") + "
"); String lastDate = null; while (currentMsg < msgs.size()) { UfedMessage m = msgs.get(currentMsg); String thisDate = m.getTimeStamp() != null ? dateFormat.format(m.getTimeStamp()) - : Messages.getString("ReportGenerator.UnknownDate"); //$NON-NLS-1$ + : Messages.getString("ReportGenerator.UnknownDate"); if (lastDate == null || !lastDate.equals(thisDate)) { - out.println("
" //$NON-NLS-1$ - + thisDate + "
"); //$NON-NLS-1$ + out.println("
" + thisDate + "
"); lastDate = thisDate; } - boolean isGroup = c.getMetadata().getValues(ExtraProperties.UFED_META_PREFIX + "Participants").length > 2; //$NON-NLS-1$ + boolean isGroup = c.getMetadata().getValues(ExtraProperties.UFED_META_PREFIX + "Participants").length > 2; printMessage(out, m, isGroup, c.isDeleted()); if (currentMsg++ != msgs.size() - 1 && bout.size() >= minChatSplitSize) { - out.println("
" //$NON-NLS-1$ - + Messages.getString("WhatsAppReport.ChatContinues") + "
"); //$NON-NLS-1$ //$NON-NLS-2$ + out.println("
" + + Messages.getString("WhatsAppReport.ChatContinues") + "
"); break; } } @@ -94,38 +153,36 @@ private void printMessage(PrintWriter out, UfedMessage message, boolean group, b boolean isFrom = false; boolean isTo = false; - out.println("
"); //$NON-NLS-1$ + out.println("
"); String name = null; if (message.isSystemMessage()) { - out.println("
"); //$NON-NLS-1$ + out.println("
"); } else { if (message.isFromMe()) { - out.println("
"); //$NON-NLS-1$ + out.println("
"); isTo = true; name = message.getLocalResource(); } else { out.println( - "
"); //$NON-NLS-1$ + "
"); isFrom = true; name = message.getRemoteResource(); } if (name == null) - name = Messages.getString("ReportGenerator.Unknown"); //$NON-NLS-1$ + name = Messages.getString("ReportGenerator.Unknown"); } - if (name != null) - out.println( - "" + format(name) + "
"); //$NON-NLS-1$ //$NON-NLS-2$ + out.println("" + format(name) + "
"); if (message.getData() != null && !message.getData().trim().isEmpty()) { - if (message.getData().startsWith("BEGIN:VCARD")) { //$NON-NLS-1$ - String[] lines = message.getData().split("\n"); //$NON-NLS-1$ + if (message.getData().startsWith("BEGIN:VCARD")) { + String[] lines = message.getData().split("\n"); for (String line : lines) { - if (line.startsWith("PHOTO;BASE64:")) { //$NON-NLS-1$ - out.print("PHOTO:
"); //$NON-NLS-1$ //$NON-NLS-2$ + if (line.startsWith("PHOTO;BASE64:")) { + out.print("PHOTO:
"); } else { - out.print(format(line) + "
"); //$NON-NLS-1$ + out.print(format(line) + "
"); } } } else { @@ -134,56 +191,56 @@ private void printMessage(PrintWriter out, UfedMessage message, boolean group, b out.print("
"); } } else if (message.isSystemMessage()) { - out.print("System Message"); //$NON-NLS-1$ + out.print("System Message"); } if (message.getMediaHash() != null || message.getThumbData() != null || message.getMediaName() != null) { if (message.getMediaHash() != null) { out.println(""); - out.println(""); //$NON-NLS-1$ + out.println("href=\"" + format(exportPath) + "\""); + out.println(">"); } byte[] thumb = message.getThumbData(); if (thumb != null) { - if (getTitle(message).equals("video")) //$NON-NLS-1$ - out.println(Messages.getString("WhatsAppReport.Video") + ":
"); //$NON-NLS-1$ //$NON-NLS-2$ - out.print("
"); //$NON-NLS-1$ //$NON-NLS-2$ + if (getTitle(message).equals("video")) + out.println(Messages.getString("WhatsAppReport.Video") + ":
"); + out.print("
"); } else if (message.getMediaMime() != null) { - if (message.getMediaMime().startsWith("audio")) { //$NON-NLS-1$ - out.println("
"); //$NON-NLS-1$ - } else if (message.getMediaMime().startsWith("video")) { //$NON-NLS-1$ - out.println("
"); //$NON-NLS-1$ - } else if (message.getMediaMime().startsWith("image")) { //$NON-NLS-1$ - out.println("
"); //$NON-NLS-1$ - } else if (message.getMediaMime().contains("contact")) { //$NON-NLS-1$ - out.println("
"); //$NON-NLS-1$ + if (message.getMediaMime().startsWith("audio")) { + out.println("
"); + } else if (message.getMediaMime().startsWith("video")) { + out.println("
"); + } else if (message.getMediaMime().startsWith("image")) { + out.println("
"); + } else if (message.getMediaMime().contains("contact")) { + out.println("
"); } else - out.println("Attachment:
"); //$NON-NLS-1$ + out.println("Attachment:
"); } - out.println("
"); //$NON-NLS-1$ + out.println(""); } if (message.getMediaCaption() != null) - out.println("
" + format(message.getMediaCaption())); //$NON-NLS-1$ + out.println("
" + format(message.getMediaCaption())); String transcription = message.getTranscription(); if (transcription != null) { out.print("
"); - out.print(Messages.getString("ReportGenerator.TranscriptionTitle")); //$NON-NLS-1$ + out.print(Messages.getString("ReportGenerator.TranscriptionTitle")); String confidence = message.getTranscriptConfidence(); if (confidence != null) { float score = Float.valueOf(confidence) * 100; - out.print(" [" + (int) score + "%]"); //$NON-NLS-1$ //$NON-NLS-2$ + out.print(" [" + (int) score + "%]"); } - out.println(": "); //$NON-NLS-1$ + out.println(": "); out.println(format(transcription)); - out.println("
"); //$NON-NLS-1$ + out.println("

"); } if (!message.getChildPornSets().isEmpty()) { @@ -191,64 +248,80 @@ private void printMessage(PrintWriter out, UfedMessage message, boolean group, b + format(message.getChildPornSets().toString()) + "

"); } if (message.getTimeStamp() != null) { - out.println(""); //$NON-NLS-1$ + out.println(""); out.println(timeFormat.format(message.getTimeStamp())); // $NON-NLS-1$ - out.println(""); //$NON-NLS-1$ + out.println(""); } if (chatDeleted || message.isDeleted()) { - out.println("
"); //$NON-NLS-1$ - out.println("" + Messages.getString("WhatsAppReport.MessageDeletedRecovered") + ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - out.println("
"); //$NON-NLS-1$ - out.println("
"); //$NON-NLS-1$ + out.println("
"); + out.println("" + Messages.getString("WhatsAppReport.MessageDeletedRecovered") + ""); + out.println("
"); + out.println("
"); } if (isTo) - out.println("
"); + out.println("
"); if (isFrom) out.println("
"); - - - out.println("
"); //$NON-NLS-1$ + out.println("
"); } private static String getTitle(Message message) { if (message.getMediaMime() != null && !message.getMediaMime().isEmpty()) return message.getMediaMime().substring(0, message.getMediaMime().indexOf('/')); else - return "File"; //$NON-NLS-1$ + return "File"; } - private static void printMessageFileHeader(PrintWriter out, String chatName, String title, byte[] avatar) { - out.println("\n" //$NON-NLS-1$ - + "\n" //$NON-NLS-1$ - + "\n" //$NON-NLS-1$ - + " " + format(title) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " \n" //$NON-NLS-1$ - + " \n" //$NON-NLS-1$ - + " \n" //$NON-NLS-1$ - + " \n" //$NON-NLS-1$ - + "\n" + "" + "\n" //$NON-NLS-3$ - + "\n" //$NON-NLS-1$ - + "
\n" //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + "   "); //$NON-NLS-1$ - if (avatar != null) - out.println(""); //$NON-NLS-1$ - out.println(format(chatName) + "\n" //$NON-NLS-1$ - + "
\n" //$NON-NLS-1$ - + "
\n" //$NON-NLS-1$ - + "


"); //$NON-NLS-1$ + private static void printMessageFileHeader(PrintWriter out, String chatName, String title, byte[] avatar, + String source) { + + String topbarClass = " class=\"other\""; + String backImage = " style=\"background-image:url(" + Util.getImageResourceAsEmbedded("img/other-chat-back.jpg") + ")\""; + String icon = "message"; + if (source != null) { + if (source.equals(UFEDChatParser.TELEGRAM)) { + topbarClass = " class=\"telegram\""; + backImage = " style=\"background-image:url(" + Util.getImageResourceAsEmbedded("img/telegramwallpaper.jpg") + ")\""; + icon = "telegram"; + } else if (source.contains(UFEDChatParser.WHATSAPP)) { + topbarClass = ""; + backImage = ""; + icon = "whatsapp"; + } + } + + out.println("\n" + + "\n" + + "\n" + + "" + format(title) + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + "" + "\n" + + "\n" + + "
\n" + + " " + + "   "); + + if (avatar != null) { + out.println(""); + } + + out.println(format(chatName) + "\n" + + "
\n" + + "
\n" + + "


"); } private static void printMessageFileFooter(PrintWriter out) { - out.println("


\n" //$NON-NLS-1$ - + "
\n" //$NON-NLS-1$ - + "
 
\n" //$NON-NLS-1$ - + "\n" //$NON-NLS-1$ - + ""); //$NON-NLS-1$ + out.println("


\n" + + "
\n" + + "
 
\n" + + "\n" + + ""); } - } diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/UFEDChatParser.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/UFEDChatParser.java index 61940fd25a..237b43aa38 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/UFEDChatParser.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/ufed/UFEDChatParser.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -15,6 +14,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; import org.apache.tika.config.Field; import org.apache.tika.exception.TikaException; import org.apache.tika.extractor.EmbeddedDocumentExtractor; @@ -32,7 +32,9 @@ import iped.data.IItemReader; import iped.parsers.standard.StandardParser; +import iped.parsers.util.Messages; import iped.parsers.whatsapp.Message; +import iped.parsers.whatsapp.WAContact; import iped.properties.BasicProps; import iped.properties.ExtraProperties; import iped.properties.MediaTypes; @@ -41,14 +43,11 @@ public class UFEDChatParser extends AbstractParser { - /** - * - */ private static final long serialVersionUID = 1L; - public static final MediaType UFED_CHAT_MIME = MediaType.application("x-ufed-chat"); //$NON-NLS-1$ - public static final MediaType UFED_CHAT_WA_MIME = MediaType.application("x-ufed-chat-whatsapp"); //$NON-NLS-1$ - public static final MediaType UFED_CHAT_TELEGRAM = MediaType.application("x-ufed-chat-telegram"); //$NON-NLS-1$ + public static final MediaType UFED_CHAT_MIME = MediaType.application("x-ufed-chat"); + public static final MediaType UFED_CHAT_WA_MIME = MediaType.application("x-ufed-chat-whatsapp"); + public static final MediaType UFED_CHAT_TELEGRAM = MediaType.application("x-ufed-chat-telegram"); public static final MediaType UFED_CHAT_PREVIEW_MIME = MediaType.application("x-ufed-chat-preview"); @@ -59,12 +58,28 @@ public class UFEDChatParser extends AbstractParser { MediaType.application("x-ufed-chat-preview-facebook"), "instagram", MediaType.application("x-ufed-chat-preview-instagram")); - public static final String META_PHONE_OWNER = ExtraProperties.UFED_META_PREFIX + "phoneOwner"; //$NON-NLS-1$ - public static final String META_FROM_OWNER = ExtraProperties.UFED_META_PREFIX + "fromOwner"; //$NON-NLS-1$ - public static final String CHILD_MSG_IDS = ExtraProperties.UFED_META_PREFIX + "msgChildIds"; //$NON-NLS-1$ + public static final String META_PHONE_OWNER = ExtraProperties.UFED_META_PREFIX + "phoneOwner"; + public static final String META_FROM_OWNER = ExtraProperties.UFED_META_PREFIX + "fromOwner"; + public static final String CHILD_MSG_IDS = ExtraProperties.UFED_META_PREFIX + "msgChildIds"; public static final String ATTACHED_MEDIA_MSG = "ATTACHED_MEDIA: "; + protected static final String WHATSAPP = "WhatsApp"; + protected static final String WHATSAPP_BUSINESS = "WhatsApp Business"; + protected static final String TELEGRAM = "Telegram"; + + // Types used by UFED + protected static final String CHATTYPE_ONEONONE = "OneOnOne"; + protected static final String CHATTYPE_GROUP = "Group"; + protected static final String CHATTYPE_BROADCAST = "Broadcast"; + protected static final String CHATTYPE_UNKNOWN = "Unknown"; + + // Strings used in item names and titles + protected static final String CHATTYPE_GROUP_TITLE = Messages.getString("UFEDChatParser.Group"); + protected static final String CHATTYPE_BROADCAST_TITLE = Messages.getString("UFEDChatParser.Broadcast"); + protected static final String CHATTYPE_STATUS_TITLE = Messages.getString("UFEDChatParser.Status"); + protected static final String CHATTYPE_UNKNOWN_TITLE = Messages.getString("UFEDChatParser.Unknown"); + private int minChatSplitSize = 6000000; private static Set SUPPORTED_TYPES = MediaType.set(UFED_CHAT_MIME, UFED_CHAT_WA_MIME, @@ -109,7 +124,7 @@ public void parse(InputStream inputStream, ContentHandler handler, Metadata meta if (chat == null || searcher == null) return; - String query = BasicProps.PARENTID + ":" + chat.getId(); //$NON-NLS-1$ + String query = BasicProps.PARENTID + ":" + chat.getId(); List messages = new ArrayList<>(); @@ -117,7 +132,7 @@ public void parse(InputStream inputStream, ContentHandler handler, Metadata meta Iterator subItems = null; String[] attachRefs = msg.getMetadata().getValues(ExtraProperties.LINKED_ITEMS); if (attachRefs.length > 0) { - String attachQuery = Arrays.asList(attachRefs).stream().collect(Collectors.joining(" ")); //$NON-NLS-1$ + String attachQuery = Arrays.asList(attachRefs).stream().collect(Collectors.joining(" ")); subItems = searcher.searchIterable(attachQuery).iterator(); } else if (msg.hasChildren()) { String contactQuery = BasicProps.PARENTID + ":" + msg.getId() + " && " + BasicProps.CONTENTTYPE @@ -139,10 +154,10 @@ public void parse(InputStream inputStream, ContentHandler handler, Metadata meta } } - Collections.sort(messages, new MessageComparator()); + Collections.sort(messages); if (extractor.shouldParseEmbedded(metadata)) { - ReportGenerator reportGenerator = new ReportGenerator(searcher); + ReportGenerator reportGenerator = new ReportGenerator(); reportGenerator.setMinChatSplitSize(this.minChatSplitSize); byte[] bytes = reportGenerator.generateNextChatHtml(chat, messages); int frag = 0; @@ -167,9 +182,9 @@ public void parse(InputStream inputStream, ContentHandler handler, Metadata meta } String chatName = getChatName(chat); - + if (frag > 0 || nextBytes != null) - chatName += "_" + frag++; //$NON-NLS-1$ + chatName += "_" + frag++; chatMetadata.set(TikaCoreProperties.TITLE, chatName); chatMetadata.set(StandardParser.INDEXER_CONTENT_TYPE, previewMime.toString()); chatMetadata.set(ExtraProperties.DECODED_DATA, Boolean.TRUE.toString()); @@ -221,8 +236,8 @@ private UfedMessage createMessage(IItemReader msg, IItemReader attach) { } m.setMediaName(attach.getName()); m.setMediaTrueExt(attach.getType()); - m.setMediaUrl(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "URL", msg, attach)); //$NON-NLS-1$ - m.setMediaCaption(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "Title", msg, attach)); //$NON-NLS-1$ + m.setMediaUrl(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "URL", msg, attach)); + m.setMediaCaption(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "Title", msg, attach)); m.setThumbData(attach.getThumb()); m.setTranscription(attach.getMetadata().get(ExtraProperties.TRANSCRIPT_ATTR)); m.setTranscriptConfidence(attach.getMetadata().get(ExtraProperties.CONFIDENCE_ATTR)); @@ -233,7 +248,7 @@ private UfedMessage createMessage(IItemReader msg, IItemReader attach) { if (attach.getMediaType() != null && !attach.getMediaType().equals(MediaType.OCTET_STREAM)) m.setMediaMime(attach.getMediaType().toString()); else - m.setMediaMime(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "ContentType", msg, attach)); //$NON-NLS-1$ + m.setMediaMime(getMetaFromAttachOrMsg(ExtraProperties.UFED_META_PREFIX + "ContentType", msg, attach)); } return m; } @@ -247,21 +262,107 @@ private String getMetaFromAttachOrMsg(String key, IItemReader msg, IItemReader a } public static String getChatName(IItemReader item) { - String name = "Chat"; //$NON-NLS-1$ - String source = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Source"); //$NON-NLS-1$ - if (source != null) - name += "_" + source; //$NON-NLS-1$ - String[] parties = item.getMetadata().getValues(ExtraProperties.UFED_META_PREFIX + "Participants"); //$NON-NLS-1$ - if (parties != null && parties.length > 2) { - name += "_Group_" + item.getName().split("_")[1]; //$NON-NLS-1$ //$NON-NLS-2$ - } else if (parties != null && parties.length > 0) { - name += "_" + parties[0]; //$NON-NLS-1$ - if (parties.length > 1) - name += "_" + parties[1]; //$NON-NLS-1$ + String name = "Chat"; + String source = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Source"); + String account = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Account"); + String phoneOwner = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "phoneOwner"); + String idProperty = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "id"); + String nameProperty = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "Name"); + String chatType = item.getMetadata().get(ExtraProperties.UFED_META_PREFIX + "ChatType"); + String[] parties = item.getMetadata().getValues(ExtraProperties.UFED_META_PREFIX + "Participants"); + + if (source != null) { + name += "_" + source; } + + if (account != null) { + name += "_" + clean(account); + } else if (phoneOwner != null) { + name += "_" + clean(phoneOwner); + } + + if (chatType != null) { + if (chatType.equals(CHATTYPE_ONEONONE)) { + if (parties != null) { + name += "_" + clean( + ((parties.length > 1) && (parties[0].equals(phoneOwner)) ? parties[1] : parties[0])); + } else { + name += "_" + idProperty; + } + + } else if (chatType.equals(CHATTYPE_GROUP)) { + name += "_" + CHATTYPE_GROUP_TITLE + "_" + (nameProperty != null ? nameProperty : idProperty); + + } else if (chatType.equals(CHATTYPE_BROADCAST)) { + if (parties != null) { + if ((parties.length == 1) && ((source != null) && (source.equals(WHATSAPP) + || source.equals(WHATSAPP_BUSINESS) || source.equals(TELEGRAM)))) { + // "Status" chat type (known from behaviour) + // NOTE: Apps with this behaviour should be added to this if condition + name += "_" + CHATTYPE_STATUS_TITLE + "_" + clean(parties[0]); + } else { + name += "_" + CHATTYPE_BROADCAST_TITLE + "_" + + (nameProperty != null ? nameProperty : idProperty); + } + } else { + name += "_" + CHATTYPE_BROADCAST_TITLE + "_" + (nameProperty != null ? nameProperty : idProperty); + } + + } else if (chatType.equals(CHATTYPE_UNKNOWN)) { + if ((source != null) + && (source.equals(WHATSAPP) || source.equals(WHATSAPP_BUSINESS) || source.equals(TELEGRAM))) { + // "Unknown" chat type regarding apps for which there are specific chat types + // NOTE: Apps with similar behavior should be added to this if condition + name += "_" + CHATTYPE_UNKNOWN_TITLE + "_" + idProperty; + + } else { + // "Unknown" chat type regarding apps for which there aren't specific chat types + // Communication type is derived from the number of participants + if ((parties != null) && (parties.length > 0)) { + if (parties.length > 2) { + name += "_" + CHATTYPE_GROUP_TITLE + "_" + idProperty; + } else { + name += "_" + clean( + (parties.length > 1) && (parties[0].equals(phoneOwner)) ? parties[1] : parties[0]); + } + } else { + name += "_" + CHATTYPE_UNKNOWN_TITLE + "_" + idProperty; + } + } + + } else { + name += "_" + chatType + "_" + idProperty; + } + + } else { + name += "_" + idProperty; + } + return name; } + private static String clean(String s) { + if (s != null) { + s = s.trim(); + if (s.endsWith(")")) { + int p = s.indexOf("("); + if (p > 0) { + int cnt = 0; + for (int i = p + 1; i < s.length() - 1; i++) { + if (Character.isDigit(s.charAt(i))) { + cnt++; + } + } + if (cnt >= 5) { + s = s.substring(0, p); + } + } + } + s = StringUtils.remove(s, WAContact.waSuffix); + } + return s; + } + private void storeMsgIds(List messages, Metadata metadata) { for (Message m : messages) { metadata.add(CHILD_MSG_IDS, Long.toString(m.getId())); @@ -277,22 +378,4 @@ private void storeLinkedHashes(List messages, Metadata metadata) { } } } - - private class MessageComparator implements Comparator { - - @Override - public int compare(Message o1, Message o2) { - if (o1.getTimeStamp() == null) { - if (o2.getTimeStamp() == null) - return 0; - else - return -1; - } else if (o2.getTimeStamp() == null) - return 1; - else - return o1.getTimeStamp().compareTo(o2.getTimeStamp()); - } - - } - } diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/Message.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/Message.java index 7feb40419f..e50b6b3c02 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/Message.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/Message.java @@ -21,7 +21,6 @@ import iped.parsers.util.ChildPornHashLookup; /** - * * @author Fabio Melo Pfeifer */ public class Message implements Comparable { @@ -214,8 +213,8 @@ public String getMediaMime() { } public void setMediaMime(String mediaMime) { - if (mediaMime != null && mediaMime.contains(";")) { //$NON-NLS-1$ - mediaMime = mediaMime.split(";")[0]; //$NON-NLS-1$ + if (mediaMime != null && mediaMime.contains(";")) { + mediaMime = mediaMime.split(";")[0]; } this.mediaMime = mediaMime; } diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/ReportGenerator.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/ReportGenerator.java index 9d43fd4152..947b31aeb9 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/ReportGenerator.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/ReportGenerator.java @@ -22,7 +22,6 @@ import iped.parsers.util.Messages; import iped.parsers.vcard.VCardParser; import iped.parsers.whatsapp.Message.MessageType; -import iped.parsers.whatsapp.Message.MessageQuotedType; import iped.properties.ExtraProperties; import iped.utils.EmojiUtil; import iped.utils.LocalizedFormat; @@ -1630,6 +1629,7 @@ private void printMessageFile(PrintWriter out, String title, String id, byte[] a } else { deletedDiv = ""; } + String favicon = Util.getImageResourceAsEmbedded("img/whatsapp.png"); StringSubstitutor interpolator = new StringSubstitutor(new StringLookup() { @Override @@ -1649,6 +1649,8 @@ public String lookup(String key) { return css; case "deleted": return deletedDiv; + case "favicon": + return favicon; } return StringLookupFactory.INSTANCE.interpolatorStringLookup().lookup(key); } diff --git a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/WAContact.java b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/WAContact.java index 7a055d9bbf..981f259b13 100644 --- a/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/WAContact.java +++ b/iped-parsers/iped-parsers-impl/src/main/java/iped/parsers/whatsapp/WAContact.java @@ -2,7 +2,7 @@ public class WAContact { - protected static final String waSuffix = "@s.whatsapp.net"; + public static final String waSuffix = "@s.whatsapp.net"; private String id; diff --git a/iped-parsers/iped-parsers-impl/src/main/resources/iped/parsers/threema/threema-html-template.txt b/iped-parsers/iped-parsers-impl/src/main/resources/iped/parsers/threema/threema-html-template.txt index 247a5b636f..bf5da10a97 100644 --- a/iped-parsers/iped-parsers-impl/src/main/resources/iped/parsers/threema/threema-html-template.txt +++ b/iped-parsers/iped-parsers-impl/src/main/resources/iped/parsers/threema/threema-html-template.txt @@ -5,7 +5,7 @@ - +