Line data Source code
1 : // Copyright (C) 2016 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.mail; 16 : 17 : import com.google.common.base.Splitter; 18 : import com.google.common.base.Strings; 19 : import com.google.common.flogger.FluentLogger; 20 : import com.google.common.primitives.Ints; 21 : import java.sql.Timestamp; 22 : import java.time.Instant; 23 : import java.time.format.DateTimeParseException; 24 : 25 : /** Parse metadata from inbound email */ 26 0 : public class MailHeaderParser { 27 4 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 28 : 29 : public static MailMetadata parse(MailMessage m) { 30 4 : MailMetadata metadata = new MailMetadata(); 31 : // Find author 32 4 : metadata.author = m.from().email(); 33 : 34 : // Check email headers for X-Gerrit-<Name> 35 4 : for (String header : m.additionalHeaders()) { 36 2 : if (header.startsWith(MailHeader.CHANGE_NUMBER.fieldWithDelimiter())) { 37 1 : String num = header.substring(MailHeader.CHANGE_NUMBER.fieldWithDelimiter().length()); 38 1 : metadata.changeNumber = Ints.tryParse(num); 39 2 : } else if (header.startsWith(MailHeader.PATCH_SET.fieldWithDelimiter())) { 40 1 : String ps = header.substring(MailHeader.PATCH_SET.fieldWithDelimiter().length()); 41 1 : metadata.patchSet = Ints.tryParse(ps); 42 2 : } else if (header.startsWith(MailHeader.COMMENT_DATE.fieldWithDelimiter())) { 43 1 : String ts = header.substring(MailHeader.COMMENT_DATE.fieldWithDelimiter().length()).trim(); 44 : try { 45 1 : metadata.timestamp = 46 1 : Timestamp.from(MailProcessingUtil.rfcDateformatter.parse(ts, Instant::from)); 47 0 : } catch (DateTimeParseException e) { 48 0 : logger.atSevere().withCause(e).log( 49 0 : "Mail: Error while parsing timestamp from header of message %s", m.id()); 50 1 : } 51 2 : } else if (header.startsWith(MailHeader.MESSAGE_TYPE.fieldWithDelimiter())) { 52 1 : metadata.messageType = 53 1 : header.substring(MailHeader.MESSAGE_TYPE.fieldWithDelimiter().length()); 54 : } 55 2 : } 56 4 : if (metadata.hasRequiredFields()) { 57 1 : return metadata; 58 : } 59 : 60 : // If the required fields were not yet found, continue to parse the text 61 4 : if (!Strings.isNullOrEmpty(m.textContent())) { 62 4 : Iterable<String> lines = Splitter.on('\n').split(m.textContent().replace("\r\n", "\n")); 63 4 : extractFooters(lines, metadata, m); 64 4 : if (metadata.hasRequiredFields()) { 65 3 : return metadata; 66 : } 67 : } 68 : 69 : // If the required fields were not yet found, continue to parse the HTML 70 : // HTML footer are contained inside a <div> tag 71 3 : if (!Strings.isNullOrEmpty(m.htmlContent())) { 72 1 : Iterable<String> lines = Splitter.on("</div>").split(m.htmlContent().replace("\r\n", "\n")); 73 1 : extractFooters(lines, metadata, m); 74 1 : if (metadata.hasRequiredFields()) { 75 1 : return metadata; 76 : } 77 : } 78 : 79 2 : return metadata; 80 : } 81 : 82 : private static void extractFooters(Iterable<String> lines, MailMetadata metadata, MailMessage m) { 83 4 : for (String line : lines) { 84 4 : if (metadata.changeNumber == null && line.contains(MailHeader.CHANGE_NUMBER.getName())) { 85 3 : metadata.changeNumber = 86 3 : Ints.tryParse(extractFooter(MailHeader.CHANGE_NUMBER.withDelimiter(), line)); 87 4 : } else if (metadata.patchSet == null && line.contains(MailHeader.PATCH_SET.getName())) { 88 3 : metadata.patchSet = 89 3 : Ints.tryParse(extractFooter(MailHeader.PATCH_SET.withDelimiter(), line)); 90 4 : } else if (metadata.timestamp == null && line.contains(MailHeader.COMMENT_DATE.getName())) { 91 3 : String ts = extractFooter(MailHeader.COMMENT_DATE.withDelimiter(), line); 92 : try { 93 3 : metadata.timestamp = 94 3 : Timestamp.from(MailProcessingUtil.rfcDateformatter.parse(ts, Instant::from)); 95 1 : } catch (DateTimeParseException e) { 96 1 : logger.atSevere().withCause(e).log( 97 1 : "Mail: Error while parsing timestamp from footer of message %s", m.id()); 98 3 : } 99 4 : } else if (metadata.messageType == null && line.contains(MailHeader.MESSAGE_TYPE.getName())) { 100 3 : metadata.messageType = extractFooter(MailHeader.MESSAGE_TYPE.withDelimiter(), line); 101 : } 102 4 : } 103 4 : } 104 : 105 : private static String extractFooter(String key, String line) { 106 3 : return line.substring(line.indexOf(key) + key.length()).trim(); 107 : } 108 : }