Implement RawMailParser
This change adds a parser to parse raw emails received by either POP3 or IMAP into a MailMessage. It adds a dependency to Apache Mime4j to handle the mime message parsing and tests. Change-Id: I97ead9615ffcd0a7839ae1aa1581be4005cf67f1
This commit is contained in:
14
WORKSPACE
14
WORKSPACE
@@ -397,6 +397,20 @@ maven_jar(
|
|||||||
sha1 = 'ab5daef2f881c42c8e280cbe918ec4d7fdfd7efe',
|
sha1 = 'ab5daef2f881c42c8e280cbe918ec4d7fdfd7efe',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MIME4J_VERS = '0.8.0'
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = 'mime4j_core',
|
||||||
|
artifact = 'org.apache.james:apache-mime4j-core:' + MIME4J_VERS,
|
||||||
|
sha1 = 'd54f45fca44a2f210569656b4ca3574b42911c95',
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = 'mime4j_dom',
|
||||||
|
artifact = 'org.apache.james:apache-mime4j-dom:' + MIME4J_VERS,
|
||||||
|
sha1 = '6720c93d14225c3e12c4a69768a0370c80e376a3',
|
||||||
|
)
|
||||||
|
|
||||||
OW2_VERS = '5.1'
|
OW2_VERS = '5.1'
|
||||||
|
|
||||||
maven_jar(
|
maven_jar(
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ java_library(
|
|||||||
'//lib/lucene:lucene-analyzers-common',
|
'//lib/lucene:lucene-analyzers-common',
|
||||||
'//lib/lucene:lucene-core-and-backward-codecs',
|
'//lib/lucene:lucene-core-and-backward-codecs',
|
||||||
'//lib/lucene:lucene-queryparser',
|
'//lib/lucene:lucene-queryparser',
|
||||||
|
'//lib/mime4j:core',
|
||||||
|
'//lib/mime4j:dom',
|
||||||
'//lib/ow2:ow2-asm',
|
'//lib/ow2:ow2-asm',
|
||||||
'//lib/ow2:ow2-asm-tree',
|
'//lib/ow2:ow2-asm-tree',
|
||||||
'//lib/ow2:ow2-asm-util',
|
'//lib/ow2:ow2-asm-util',
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ java_library(
|
|||||||
'//lib/lucene:lucene-analyzers-common',
|
'//lib/lucene:lucene-analyzers-common',
|
||||||
'//lib/lucene:lucene-core-and-backward-codecs',
|
'//lib/lucene:lucene-core-and-backward-codecs',
|
||||||
'//lib/lucene:lucene-queryparser',
|
'//lib/lucene:lucene-queryparser',
|
||||||
|
'//lib/mime4j:core',
|
||||||
|
'//lib/mime4j:dom',
|
||||||
'//lib/ow2:ow2-asm',
|
'//lib/ow2:ow2-asm',
|
||||||
'//lib/ow2:ow2-asm-tree',
|
'//lib/ow2:ow2-asm-tree',
|
||||||
'//lib/ow2:ow2-asm-util',
|
'//lib/ow2:ow2-asm-util',
|
||||||
|
|||||||
@@ -24,7 +24,16 @@ public class Address {
|
|||||||
if (0 <= lt && lt < gt && lt + 1 < at && at + 1 < gt) {
|
if (0 <= lt && lt < gt && lt + 1 < at && at + 1 < gt) {
|
||||||
final String email = in.substring(lt + 1, gt).trim();
|
final String email = in.substring(lt + 1, gt).trim();
|
||||||
final String name = in.substring(0, lt).trim();
|
final String name = in.substring(0, lt).trim();
|
||||||
return new Address(name.length() > 0 ? name : null, email);
|
int nameStart = 0;
|
||||||
|
int nameEnd = name.length();
|
||||||
|
if (name.startsWith("\"")) {
|
||||||
|
nameStart++;
|
||||||
|
}
|
||||||
|
if (name.endsWith("\"")) {
|
||||||
|
nameEnd--;
|
||||||
|
}
|
||||||
|
return new Address(name.length() > 0 ?
|
||||||
|
name.substring(nameStart, nameEnd): null, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lt < 0 && gt < 0 && 0 < at && at < in.length() - 1) {
|
if (lt < 0 && gt < 0 && 0 < at && at < in.length() - 1) {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ package com.google.gerrit.server.mail.receive;
|
|||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
@@ -25,57 +27,63 @@ import org.joda.time.DateTime;
|
|||||||
* by the MailParser after MailReceiver has received a message. Transformations
|
* by the MailParser after MailReceiver has received a message. Transformations
|
||||||
* done by the parser include stitching mime parts together, transforming all
|
* done by the parser include stitching mime parts together, transforming all
|
||||||
* content to UTF-16 and removing attachments.
|
* content to UTF-16 and removing attachments.
|
||||||
|
*
|
||||||
|
* A valid MailMessage contains at least the following fields: id, from, to,
|
||||||
|
* subject and dateReceived.
|
||||||
*/
|
*/
|
||||||
@AutoValue
|
@AutoValue
|
||||||
public abstract class MailMessage {
|
public abstract class MailMessage {
|
||||||
// Unique Identifier
|
// Unique Identifier
|
||||||
public abstract String id();
|
public abstract String id();
|
||||||
// Envelope Information
|
// Envelop Information
|
||||||
public abstract String from();
|
public abstract Address from();
|
||||||
public abstract ImmutableList<String> to();
|
public abstract ImmutableList<Address> to();
|
||||||
public abstract ImmutableList<String> cc();
|
@Nullable
|
||||||
|
public abstract ImmutableList<Address> cc();
|
||||||
// Metadata
|
// Metadata
|
||||||
public abstract DateTime dateReceived();
|
public abstract DateTime dateReceived();
|
||||||
public abstract ImmutableList<String> additionalHeaders();
|
public abstract ImmutableList<String> additionalHeaders();
|
||||||
// Content
|
// Content
|
||||||
public abstract String subject();
|
public abstract String subject();
|
||||||
|
@Nullable
|
||||||
public abstract String textContent();
|
public abstract String textContent();
|
||||||
|
@Nullable
|
||||||
public abstract String htmlContent();
|
public abstract String htmlContent();
|
||||||
|
|
||||||
static Builder builder() {
|
public static Builder builder() {
|
||||||
return new AutoValue_MailMessage.Builder();
|
return new AutoValue_MailMessage.Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoValue.Builder
|
@AutoValue.Builder
|
||||||
abstract static class Builder {
|
public abstract static class Builder {
|
||||||
abstract Builder id(String val);
|
public abstract Builder id(String val);
|
||||||
abstract Builder from(String val);
|
public abstract Builder from(Address val);
|
||||||
abstract ImmutableList.Builder<String> toBuilder();
|
public abstract ImmutableList.Builder<Address> toBuilder();
|
||||||
|
|
||||||
public Builder addTo(String val) {
|
public Builder addTo(Address val) {
|
||||||
toBuilder().add(val);
|
toBuilder().add(val);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract ImmutableList.Builder<String> ccBuilder();
|
public abstract ImmutableList.Builder<Address> ccBuilder();
|
||||||
|
|
||||||
public Builder addCc(String val) {
|
public Builder addCc(Address val) {
|
||||||
ccBuilder().add(val);
|
ccBuilder().add(val);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract Builder dateReceived(DateTime val);
|
public abstract Builder dateReceived(DateTime val);
|
||||||
abstract ImmutableList.Builder<String> additionalHeadersBuilder();
|
public abstract ImmutableList.Builder<String> additionalHeadersBuilder();
|
||||||
|
|
||||||
public Builder addAdditionalHeader(String val) {
|
public Builder addAdditionalHeader(String val) {
|
||||||
additionalHeadersBuilder().add(val);
|
additionalHeadersBuilder().add(val);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract Builder subject(String val);
|
public abstract Builder subject(String val);
|
||||||
abstract Builder textContent(String val);
|
public abstract Builder textContent(String val);
|
||||||
abstract Builder htmlContent(String val);
|
public abstract Builder htmlContent(String val);
|
||||||
|
|
||||||
abstract MailMessage build();
|
public abstract MailMessage build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,4 +21,8 @@ public class MailParsingException extends Exception {
|
|||||||
public MailParsingException(String msg) {
|
public MailParsingException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MailParsingException(String msg, Throwable cause) {
|
||||||
|
super(msg, cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,157 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.mail.receive;
|
package com.google.gerrit.server.mail.receive;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.MimeException;
|
||||||
|
import org.apache.james.mime4j.dom.Entity;
|
||||||
|
import org.apache.james.mime4j.dom.Message;
|
||||||
|
import org.apache.james.mime4j.dom.MessageBuilder;
|
||||||
|
import org.apache.james.mime4j.dom.Multipart;
|
||||||
|
import org.apache.james.mime4j.dom.TextBody;
|
||||||
|
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||||
|
import org.apache.james.mime4j.message.DefaultMessageBuilder;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RawMailParser parses raw email content received through POP3 or IMAP into
|
* RawMailParser parses raw email content received through POP3 or IMAP into
|
||||||
* an internal {@link MailMessage}.
|
* an internal {@link MailMessage}.
|
||||||
*/
|
*/
|
||||||
public class RawMailParser {
|
public class RawMailParser {
|
||||||
|
private static final ImmutableSet<String> MAIN_HEADERS =
|
||||||
|
ImmutableSet.of("to", "from", "cc", "date", "message-id",
|
||||||
|
"subject", "content-type");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a MailMessage from a string.
|
||||||
|
* @param raw String as received over the wire
|
||||||
|
* @return Parsed MailMessage
|
||||||
|
* @throws MailParsingException
|
||||||
|
*/
|
||||||
public static MailMessage parse(String raw) throws MailParsingException {
|
public static MailMessage parse(String raw) throws MailParsingException {
|
||||||
// TODO(hiesel) Implement.
|
MailMessage.Builder messageBuilder = MailMessage.builder();
|
||||||
return null;
|
Message mimeMessage;
|
||||||
|
try {
|
||||||
|
MessageBuilder builder = new DefaultMessageBuilder();
|
||||||
|
mimeMessage =
|
||||||
|
builder.parseMessage(new ByteArrayInputStream(raw.getBytes()));
|
||||||
|
} catch (IOException | MimeException e) {
|
||||||
|
throw new MailParsingException("Can't parse email", e);
|
||||||
|
}
|
||||||
|
// Add general headers
|
||||||
|
messageBuilder.id(mimeMessage.getMessageId());
|
||||||
|
messageBuilder.subject(mimeMessage.getSubject());
|
||||||
|
messageBuilder.dateReceived(new DateTime(mimeMessage.getDate()));
|
||||||
|
|
||||||
|
// Add From, To and Cc
|
||||||
|
if (mimeMessage.getFrom() != null && mimeMessage.getFrom().size() > 0) {
|
||||||
|
Mailbox from = mimeMessage.getFrom().get(0);
|
||||||
|
messageBuilder.from(new Address(from.getName(), from.getAddress()));
|
||||||
|
}
|
||||||
|
if (mimeMessage.getTo() != null) {
|
||||||
|
for (Mailbox m : mimeMessage.getTo().flatten()) {
|
||||||
|
messageBuilder.addTo(new Address(m.getName(), m.getAddress()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mimeMessage.getCc() != null) {
|
||||||
|
for (Mailbox m : mimeMessage.getCc().flatten()) {
|
||||||
|
messageBuilder.addCc(new Address(m.getName(), m.getAddress()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add additional headers
|
||||||
|
mimeMessage.getHeader().getFields().stream()
|
||||||
|
.filter(f -> !MAIN_HEADERS.contains(f.getName().toLowerCase()))
|
||||||
|
.forEach(f -> messageBuilder.addAdditionalHeader(
|
||||||
|
f.getName() + ": " + f.getBody()));
|
||||||
|
|
||||||
|
// Add text and html body parts
|
||||||
|
StringBuilder textBuilder = new StringBuilder();
|
||||||
|
StringBuilder htmlBuilder = new StringBuilder();
|
||||||
|
try {
|
||||||
|
handleMimePart(mimeMessage, textBuilder, htmlBuilder);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MailParsingException("Can't parse email", e);
|
||||||
|
}
|
||||||
|
messageBuilder.textContent(Strings.emptyToNull(textBuilder.toString()));
|
||||||
|
messageBuilder.htmlContent(Strings.emptyToNull(htmlBuilder.toString()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// build() will only succeed if all required attributes were set. We wrap
|
||||||
|
// the IllegalStateException in a MailParsingException indicating that
|
||||||
|
// required attributes are missing, so that the caller doesn't fall over.
|
||||||
|
return messageBuilder.build();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new MailParsingException(
|
||||||
|
"Missing required attributes after email was parsed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a MailMessage from an array of characters. Note that the character
|
||||||
|
* array is int-typed. This method is only used by POP3, which specifies that
|
||||||
|
* all transferred characters are US-ASCII (RFC 6856). When reading the input
|
||||||
|
* in Java, io.Reader yields ints. These can be safely converted to chars
|
||||||
|
* as all US-ASCII characters fit in a char. If emails contain non-ASCII
|
||||||
|
* characters, such as UTF runes, these will be encoded in ASCII using either
|
||||||
|
* Base64 or quoted-printable encoding.
|
||||||
|
* @param chars Array as received over the wire
|
||||||
|
* @return Parsed MailMessage
|
||||||
|
* @throws MailParsingException
|
||||||
|
*/
|
||||||
|
public static MailMessage parse(int[] chars) throws MailParsingException {
|
||||||
|
StringBuilder b = new StringBuilder(chars.length);
|
||||||
|
for (int c : chars) {
|
||||||
|
b.append((char) c);
|
||||||
|
}
|
||||||
|
return parse(b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses a mime tree and parses out text and html parts. All other parts
|
||||||
|
* will be dropped.
|
||||||
|
* @param part MimePart to parse
|
||||||
|
* @param textBuilder StringBuilder to append all plaintext parts
|
||||||
|
* @param htmlBuilder StringBuilder to append all html parts
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static void handleMimePart(Entity part, StringBuilder textBuilder,
|
||||||
|
StringBuilder htmlBuilder) throws IOException {
|
||||||
|
if (isPlainOrHtml(part.getMimeType()) &&
|
||||||
|
!isAttachment(part.getDispositionType())) {
|
||||||
|
TextBody tb = (TextBody) part.getBody();
|
||||||
|
String result = CharStreams.toString(new InputStreamReader(
|
||||||
|
tb.getInputStream(), tb.getMimeCharset()));
|
||||||
|
if (part.getMimeType().equals("text/plain")) {
|
||||||
|
textBuilder.append(result);
|
||||||
|
} else if (part.getMimeType().equals("text/html")) {
|
||||||
|
htmlBuilder.append(result);
|
||||||
|
}
|
||||||
|
} else if (isMixedOrAlternative(part.getMimeType())) {
|
||||||
|
Multipart multipart = (Multipart) part.getBody();
|
||||||
|
for (Entity e : multipart.getBodyParts()) {
|
||||||
|
handleMimePart(e, textBuilder, htmlBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPlainOrHtml(String mimeType) {
|
||||||
|
return (mimeType.equals("text/plain") || mimeType.equals("text/html"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMixedOrAlternative(String mimeType) {
|
||||||
|
return mimeType.equals("multipart/alternative") ||
|
||||||
|
mimeType.equals("multipart/mixed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAttachment(String dispositionType) {
|
||||||
|
return dispositionType != null && dispositionType.equals("attachment");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.receive.data.AttachmentMessage;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.Base64HeaderMessage;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.HtmlMimeMessage;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.NonUTF8Message;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.QuotedPrintableHeaderMessage;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.RawMailMessage;
|
||||||
|
import com.google.gerrit.server.mail.receive.data.SimpleTextMessage;
|
||||||
|
import com.google.gerrit.testutil.GerritBaseTests;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class RawMailParserTest extends GerritBaseTests {
|
||||||
|
@Test
|
||||||
|
public void testParseEmail() throws Exception {
|
||||||
|
RawMailMessage[] messages = new RawMailMessage[] {
|
||||||
|
new SimpleTextMessage(),
|
||||||
|
new Base64HeaderMessage(),
|
||||||
|
new QuotedPrintableHeaderMessage(),
|
||||||
|
new HtmlMimeMessage(),
|
||||||
|
new AttachmentMessage(),
|
||||||
|
new NonUTF8Message(),
|
||||||
|
};
|
||||||
|
for (RawMailMessage rawMailMessage : messages) {
|
||||||
|
if (rawMailMessage.rawChars() != null) {
|
||||||
|
// Assert Character to Mail Parser
|
||||||
|
MailMessage parsedMailMessage =
|
||||||
|
RawMailParser.parse(rawMailMessage.rawChars());
|
||||||
|
assertMail(parsedMailMessage, rawMailMessage.expectedMailMessage());
|
||||||
|
}
|
||||||
|
if (rawMailMessage.raw() != null) {
|
||||||
|
// Assert String to Mail Parser
|
||||||
|
MailMessage parsedMailMessage = RawMailParser
|
||||||
|
.parse(rawMailMessage.raw());
|
||||||
|
assertMail(parsedMailMessage, rawMailMessage.expectedMailMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method makes it easier to debug failing tests by checking each
|
||||||
|
* property individual instead of calling equals as it will immediately
|
||||||
|
* reveal the property that diverges between the two objects.
|
||||||
|
* @param have MailMessage retrieved from the parser
|
||||||
|
* @param want MailMessage that would be expected
|
||||||
|
*/
|
||||||
|
private void assertMail(MailMessage have, MailMessage want) {
|
||||||
|
assertThat(have.id()).isEqualTo(want.id());
|
||||||
|
assertThat(have.to()).isEqualTo(want.to());
|
||||||
|
assertThat(have.from()).isEqualTo(want.from());
|
||||||
|
assertThat(have.cc()).isEqualTo(want.cc());
|
||||||
|
assertThat(have.dateReceived().getMillis())
|
||||||
|
.isEqualTo(want.dateReceived().getMillis());
|
||||||
|
assertThat(have.additionalHeaders()).isEqualTo(want.additionalHeaders());
|
||||||
|
assertThat(have.subject()).isEqualTo(want.subject());
|
||||||
|
assertThat(have.textContent()).isEqualTo(want.textContent());
|
||||||
|
assertThat(have.htmlContent()).isEqualTo(want.htmlContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that all mime parts that are neither text/plain, nor text/html are
|
||||||
|
* dropped.
|
||||||
|
*/
|
||||||
|
public class AttachmentMessage extends RawMailMessage {
|
||||||
|
private static String raw = "MIME-Version: 1.0\n" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"Message-ID: <CAM7sg=3meaAVUxW3KXeJEVs8sv_ADw1BnvpcHHiYVR2TQQi__w" +
|
||||||
|
"@mail.gmail.com>\n" +
|
||||||
|
"Subject: Test Subject\n" +
|
||||||
|
"From: Patrick Hiesel <hiesel@google.com>\n" +
|
||||||
|
"To: Patrick Hiesel <hiesel@google.com>\n" +
|
||||||
|
"Content-Type: multipart/mixed; boundary=001a114e019a56962d054062708f\n" +
|
||||||
|
"\n" +
|
||||||
|
"--001a114e019a56962d054062708f\n" +
|
||||||
|
"Content-Type: multipart/alternative; boundary=001a114e019a5696250540" +
|
||||||
|
"62708d\n" +
|
||||||
|
"\n" +
|
||||||
|
"--001a114e019a569625054062708d\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n" +
|
||||||
|
"\n" +
|
||||||
|
"Contains unwanted attachment" +
|
||||||
|
"\n" +
|
||||||
|
"--001a114e019a569625054062708d\n" +
|
||||||
|
"Content-Type: text/html; charset=UTF-8\n" +
|
||||||
|
"\n" +
|
||||||
|
"<div dir=\"ltr\">Contains unwanted attachment</div>" +
|
||||||
|
"\n" +
|
||||||
|
"--001a114e019a569625054062708d--\n" +
|
||||||
|
"--001a114e019a56962d054062708f\n" +
|
||||||
|
"Content-Type: text/plain; charset=US-ASCII; name=\"test.txt\"\n" +
|
||||||
|
"Content-Disposition: attachment; filename=\"test.txt\"\n" +
|
||||||
|
"Content-Transfer-Encoding: base64\n" +
|
||||||
|
"X-Attachment-Id: f_iv264bt50\n" +
|
||||||
|
"\n" +
|
||||||
|
"VEVTVAo=\n" +
|
||||||
|
"--001a114e019a56962d054062708f--";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
System.out.println("\uD83D\uDE1B test");
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<CAM7sg=3meaAVUxW3KXeJEVs8sv_ADw1BnvpcHHiYVR2TQQi__w" +
|
||||||
|
"@mail.gmail.com>")
|
||||||
|
.from(new Address("Patrick Hiesel", "hiesel@google.com"))
|
||||||
|
.addTo(new Address("Patrick Hiesel", "hiesel@google.com"))
|
||||||
|
.textContent("Contains unwanted attachment")
|
||||||
|
.htmlContent("<div dir=\"ltr\">Contains unwanted attachment</div>")
|
||||||
|
.subject("Test Subject")
|
||||||
|
.addAdditionalHeader("MIME-Version: 1.0")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC));
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests parsing a Base64 encoded subject.
|
||||||
|
*/
|
||||||
|
public class Base64HeaderMessage extends RawMailMessage {
|
||||||
|
private static String textContent = "Some Text";
|
||||||
|
private static String raw = "" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"Message-ID: <001a114da7ae26e2eb053fe0c29c@google.com>\n" +
|
||||||
|
"Subject: =?UTF-8?B?8J+YmyB0ZXN0?=\n" +
|
||||||
|
"From: \"Jonathan Nieder (Gerrit)\" <noreply-gerritcodereview-" +
|
||||||
|
"CtTy0igsBrnvL7dKoWEIEg@google.com>\n" +
|
||||||
|
"To: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes\n" +
|
||||||
|
"\n" + textContent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<001a114da7ae26e2eb053fe0c29c@google.com>")
|
||||||
|
.from(new Address("Jonathan Nieder (Gerrit)",
|
||||||
|
"noreply-gerritcodereview-CtTy0igsBrnvL7dKoWEIEg@google.com"))
|
||||||
|
.addTo(new Address("ekempin","ekempin@google.com"))
|
||||||
|
.textContent(textContent)
|
||||||
|
.subject("\uD83D\uDE1B test")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC));
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests a message containing mime/alternative (text + html) content.
|
||||||
|
*/
|
||||||
|
public class HtmlMimeMessage extends RawMailMessage {
|
||||||
|
private static String textContent = "Simple test";
|
||||||
|
|
||||||
|
// htmlContent is encoded in quoted-printable
|
||||||
|
private static String htmlContent = "<div dir=3D\"ltr\">Test <span style" +
|
||||||
|
"=3D\"background-color:rgb(255,255,0)\">Messa=\n" +
|
||||||
|
"ge</span> in <u>HTML=C2=A0</u><a href=3D\"https://en.wikipedia.org/" +
|
||||||
|
"wiki/%C3%=\n9Cmlaut_(band)\" class=3D\"gmail-mw-redirect\" title=3D\"" +
|
||||||
|
"=C3=9Cmlaut (band)\" st=\nyle=3D\"text-decoration:none;color:rgb(11," +
|
||||||
|
"0,128);background-image:none;backg=\nround-position:initial;background" +
|
||||||
|
"-size:initial;background-repeat:initial;ba=\nckground-origin:initial;" +
|
||||||
|
"background-clip:initial;font-family:sans-serif;font=\n" +
|
||||||
|
"-size:14px\">=C3=9C</a></div>";
|
||||||
|
|
||||||
|
private static String unencodedHtmlContent = "" +
|
||||||
|
"<div dir=\"ltr\">Test <span style=\"background-color:rgb(255,255,0)\">" +
|
||||||
|
"Message</span> in <u>HTML </u><a href=\"https://en.wikipedia.org/wiki/" +
|
||||||
|
"%C3%9Cmlaut_(band)\" class=\"gmail-mw-redirect\" title=\"Ümlaut " +
|
||||||
|
"(band)\" style=\"text-decoration:none;color:rgb(11,0,128);" +
|
||||||
|
"background-image:none;background-position:initial;background-size:" +
|
||||||
|
"initial;background-repeat:initial;background-origin:initial;background" +
|
||||||
|
"-clip:initial;font-family:sans-serif;font-size:14px\">Ü</a></div>";
|
||||||
|
|
||||||
|
private static String raw = "" +
|
||||||
|
"MIME-Version: 1.0\n" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"Message-ID: <001a114cd8be55b4ab053face5cd@google.com>\n" +
|
||||||
|
"Subject: Change in gerrit[master]: Implement receiver class structure " +
|
||||||
|
"and bindings\n" +
|
||||||
|
"From: \"ekempin (Gerrit)\" <noreply-gerritcodereview-qUgXfQecoDLHwp0Ml" +
|
||||||
|
"dAzig@google.com>\n" +
|
||||||
|
"To: Patrick Hiesel <hiesel@google.com>\n" +
|
||||||
|
"Cc: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Content-Type: multipart/alternative; boundary=001a114cd8b" +
|
||||||
|
"e55b486053face5ca\n" +
|
||||||
|
"\n" +
|
||||||
|
"--001a114cd8be55b486053face5ca\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes\n" +
|
||||||
|
"\n" +
|
||||||
|
textContent +
|
||||||
|
"\n" +
|
||||||
|
"--001a114cd8be55b486053face5ca\n" +
|
||||||
|
"Content-Type: text/html; charset=UTF-8\n" +
|
||||||
|
"Content-Transfer-Encoding: quoted-printable\n" +
|
||||||
|
"\n" +
|
||||||
|
htmlContent +
|
||||||
|
"\n" +
|
||||||
|
"--001a114cd8be55b486053face5ca--";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<001a114cd8be55b4ab053face5cd@google.com>")
|
||||||
|
.from(new Address("ekempin (Gerrit)",
|
||||||
|
"noreply-gerritcodereview-qUgXfQecoDLHwp0MldAzig@google.com"))
|
||||||
|
.addCc(new Address("ekempin","ekempin@google.com"))
|
||||||
|
.addTo(new Address("Patrick Hiesel","hiesel@google.com"))
|
||||||
|
.textContent(textContent)
|
||||||
|
.htmlContent(unencodedHtmlContent)
|
||||||
|
.subject("Change in gerrit[master]: Implement " +
|
||||||
|
"receiver class structure and bindings")
|
||||||
|
.addAdditionalHeader("MIME-Version: 1.0")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC));
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that non-UTF8 encodings are handled correctly.
|
||||||
|
*/
|
||||||
|
public class NonUTF8Message extends RawMailMessage {
|
||||||
|
private static String textContent = "Some Text";
|
||||||
|
private static String raw = "" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"Message-ID: <001a114da7ae26e2eb053fe0c29c@google.com>\n" +
|
||||||
|
"Subject: =?UTF-8?B?8J+YmyB0ZXN0?=\n" +
|
||||||
|
"From: \"Jonathan Nieder (Gerrit)\" <noreply-gerritcodereview-" +
|
||||||
|
"CtTy0igsBrnvL7dKoWEIEg@google.com>\n" +
|
||||||
|
"To: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes\n" +
|
||||||
|
"\n" + textContent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
int[] arr = new int[raw.length()];
|
||||||
|
int i = 0;
|
||||||
|
for (char c : raw.toCharArray()) {
|
||||||
|
arr[i++] = (int) c;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<001a114da7ae26e2eb053fe0c29c@google.com>")
|
||||||
|
.from(new Address("Jonathan Nieder (Gerrit)",
|
||||||
|
"noreply-gerritcodereview-CtTy0igsBrnvL7dKoWEIEg@google.com"))
|
||||||
|
.addTo(new Address("ekempin","ekempin@google.com"))
|
||||||
|
.textContent(textContent)
|
||||||
|
.subject("\uD83D\uDE1B test")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC));
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests parsing a quoted printable encoded subject
|
||||||
|
*/
|
||||||
|
public class QuotedPrintableHeaderMessage extends RawMailMessage {
|
||||||
|
private static String textContent = "Some Text";
|
||||||
|
private static String raw = "" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"Message-ID: <001a114da7ae26e2eb053fe0c29c@google.com>\n" +
|
||||||
|
"Subject: =?UTF-8?Q?=C3=A2me vulgaire?=\n" +
|
||||||
|
"From: \"Jonathan Nieder (Gerrit)\" <noreply-gerritcodereview-" +
|
||||||
|
"CtTy0igsBrnvL7dKoWEIEg@google.com>\n" +
|
||||||
|
"To: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes\n" +
|
||||||
|
"\n" + textContent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
System.out.println("\uD83D\uDE1B test");
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<001a114da7ae26e2eb053fe0c29c@google.com>")
|
||||||
|
.from(new Address("Jonathan Nieder (Gerrit)",
|
||||||
|
"noreply-gerritcodereview-CtTy0igsBrnvL7dKoWEIEg@google.com"))
|
||||||
|
.addTo(new Address("ekempin","ekempin@google.com"))
|
||||||
|
.textContent(textContent)
|
||||||
|
.subject("âme vulgaire")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC));
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all email parsing tests.
|
||||||
|
*/
|
||||||
|
public abstract class RawMailMessage {
|
||||||
|
// Raw content to feed the parser
|
||||||
|
public abstract String raw();
|
||||||
|
public abstract int[] rawChars();
|
||||||
|
// Parsed representation for asserting the expected parser output
|
||||||
|
public abstract MailMessage expectedMailMessage();
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.server.mail.receive.data;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.mail.Address;
|
||||||
|
import com.google.gerrit.server.mail.receive.MailMessage;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests parsing a simple text message with different headers.
|
||||||
|
*/
|
||||||
|
public class SimpleTextMessage extends RawMailMessage {
|
||||||
|
private static String textContent = "" +
|
||||||
|
"Jonathan Nieder has posted comments on this change. ( \n" +
|
||||||
|
"https://gerrit-review.googlesource.com/90018 )\n" +
|
||||||
|
"\n" +
|
||||||
|
"Change subject: (Re)enable voting buttons for merged changes\n" +
|
||||||
|
"...........................................................\n" +
|
||||||
|
"\n" +
|
||||||
|
"\n" +
|
||||||
|
"Patch Set 2:\n" +
|
||||||
|
"\n" +
|
||||||
|
"This is producing NPEs server-side and 500s for the client. \n" +
|
||||||
|
"when I try to load this change:\n" +
|
||||||
|
"\n" +
|
||||||
|
" Error in GET /changes/90018/detail?O=10004\n" +
|
||||||
|
" com.google.gwtorm.OrmException: java.lang.NullPointerException\n" +
|
||||||
|
"\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:303)\n" +
|
||||||
|
"\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:285)\n" +
|
||||||
|
"\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:263)\n" +
|
||||||
|
"\tat com.google.gerrit.change.GetChange.apply(GetChange.java:50)\n" +
|
||||||
|
"\tat com.google.gerrit.change.GetDetail.apply(GetDetail.java:51)\n" +
|
||||||
|
"\tat com.google.gerrit.change.GetDetail.apply(GetDetail.java:26)\n" +
|
||||||
|
"\tat \n" +
|
||||||
|
"com.google.gerrit.RestApiServlet.service(RestApiServlet.java:367)\n" +
|
||||||
|
"\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:717)\n" +
|
||||||
|
"[...]\n" +
|
||||||
|
" Caused by: java.lang.NullPointerException\n" +
|
||||||
|
"\tat \n" +
|
||||||
|
"com.google.gerrit.ChangeJson.setLabelScores(ChangeJson.java:670)\n" +
|
||||||
|
"\tat \n" +
|
||||||
|
"com.google.gerrit.ChangeJson.labelsFor(ChangeJson.java:845)\n" +
|
||||||
|
"\tat \n" +
|
||||||
|
"com.google.gerrit.change.ChangeJson.labelsFor(ChangeJson.java:598)\n" +
|
||||||
|
"\tat \n" +
|
||||||
|
"com.google.gerrit.change.ChangeJson.toChange(ChangeJson.java:499)\n" +
|
||||||
|
"\tat com.google.gerrit.change.ChangeJson.format(ChangeJson.java:294)\n" +
|
||||||
|
"\t... 105 more\n" +
|
||||||
|
"-- \n" +
|
||||||
|
"To view, visit https://gerrit-review.googlesource.com/90018\n" +
|
||||||
|
"To unsubscribe, visit https://gerrit-review.googlesource.com\n" +
|
||||||
|
"\n" +
|
||||||
|
"Gerrit-MessageType: comment\n" +
|
||||||
|
"Gerrit-Change-Id: Iba501e00bee77be3bd0ced72f88fd04ba0accaed\n" +
|
||||||
|
"Gerrit-PatchSet: 2\n" +
|
||||||
|
"Gerrit-Project: gerrit\n" +
|
||||||
|
"Gerrit-Branch: master\n" +
|
||||||
|
"Gerrit-Owner: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Gerrit-Reviewer: Dave Borowitz <dborowitz@google.com>\n" +
|
||||||
|
"Gerrit-Reviewer: Edwin Kempin <ekempin@google.com>\n" +
|
||||||
|
"Gerrit-Reviewer: GerritForge CI <gerritforge@gmail.com>\n" +
|
||||||
|
"Gerrit-Reviewer: Jonathan Nieder <jrn@google.com>\n" +
|
||||||
|
"Gerrit-Reviewer: Patrick Hiesel <hiesel@google.com>\n" +
|
||||||
|
"Gerrit-Reviewer: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Gerrit-HasComments: No";
|
||||||
|
|
||||||
|
private static String raw = "" +
|
||||||
|
"Authentication-Results: mx.google.com; dkim=pass header.i=" +
|
||||||
|
"@google.com;\n" +
|
||||||
|
"Date: Tue, 25 Oct 2016 02:11:35 -0700\n" +
|
||||||
|
"In-Reply-To: <gerrit.1477487889000.Iba501e00bee77be3bd0ced" +
|
||||||
|
"72f88fd04ba0accaed@gerrit-review.googlesource.com>\n" +
|
||||||
|
"References: <gerrit.1477487889000.Iba501e00bee77be3bd0ced72f8" +
|
||||||
|
"8fd04ba0accaed@gerrit-review.googlesource.com>\n" +
|
||||||
|
"Message-ID: <001a114da7ae26e2eb053fe0c29c@google.com>\n" +
|
||||||
|
"Subject: Change in gerrit[master]: (Re)enable voting buttons for " +
|
||||||
|
"merged changes\n" +
|
||||||
|
"From: \"Jonathan Nieder (Gerrit)\" <noreply-gerritcodereview-CtTy0" +
|
||||||
|
"igsBrnvL7dKoWEIEg@google.com>\n" +
|
||||||
|
"To: ekempin <ekempin@google.com>\n" +
|
||||||
|
"Cc: Dave Borowitz <dborowitz@google.com>, Jonathan Nieder " +
|
||||||
|
"<jrn@google.com>, Patrick Hiesel <hiesel@google.com>\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes\n" +
|
||||||
|
"\n" + textContent;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] rawChars() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MailMessage expectedMailMessage() {
|
||||||
|
MailMessage.Builder expect = MailMessage.builder();
|
||||||
|
expect
|
||||||
|
.id("<001a114da7ae26e2eb053fe0c29c@google.com>")
|
||||||
|
.from(new Address("Jonathan Nieder (Gerrit)",
|
||||||
|
"noreply-gerritcodereview-CtTy0igsBrnvL7dKoWEIEg@google.com"))
|
||||||
|
.addTo(new Address("ekempin","ekempin@google.com"))
|
||||||
|
.addCc(new Address("Dave Borowitz", "dborowitz@google.com"))
|
||||||
|
.addCc(new Address("Jonathan Nieder", "jrn@google.com"))
|
||||||
|
.addCc(new Address("Patrick Hiesel", "hiesel@google.com"))
|
||||||
|
.textContent(textContent)
|
||||||
|
.subject("Change in gerrit[master]: (Re)enable voting"
|
||||||
|
+ " buttons for merged changes")
|
||||||
|
.dateReceived(
|
||||||
|
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC))
|
||||||
|
.addAdditionalHeader("Authentication-Results: mx.google.com; " +
|
||||||
|
"dkim=pass header.i=@google.com;")
|
||||||
|
.addAdditionalHeader("In-Reply-To: <gerrit.1477487889000.Iba501e00bee" +
|
||||||
|
"77be3bd0ced72f88fd04ba0accaed@gerrit-review.googlesource.com>")
|
||||||
|
.addAdditionalHeader("References: <gerrit.1477487889000.Iba501e00bee" +
|
||||||
|
"77be3bd0ced72f88fd04ba0accaed@gerrit-review.googlesource.com>");
|
||||||
|
return expect.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/mime4j/BUCK
Normal file
35
lib/mime4j/BUCK
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
include_defs('//lib/maven.defs')
|
||||||
|
|
||||||
|
VERSION = '0.8.0'
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = 'core',
|
||||||
|
exported_deps = [
|
||||||
|
':core_library',
|
||||||
|
],
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = 'core_library',
|
||||||
|
id = 'org.apache.james:apache-mime4j-core:' + VERSION,
|
||||||
|
sha1 = 'd54f45fca44a2f210569656b4ca3574b42911c95',
|
||||||
|
license = 'Apache2.0',
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
)
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = 'dom',
|
||||||
|
exported_deps = [
|
||||||
|
':dom_library',
|
||||||
|
],
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = 'dom_library',
|
||||||
|
id = 'org.apache.james:apache-mime4j-dom:' + VERSION,
|
||||||
|
sha1 = '6720c93d14225c3e12c4a69768a0370c80e376a3',
|
||||||
|
license = 'Apache2.0',
|
||||||
|
visibility = ['PUBLIC'],
|
||||||
|
)
|
||||||
13
lib/mime4j/BUILD
Normal file
13
lib/mime4j/BUILD
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
java_library(
|
||||||
|
name = "core",
|
||||||
|
data = ["//lib:LICENSE-Apache2.0"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
exports = ["@mime4j_core//jar"],
|
||||||
|
)
|
||||||
|
|
||||||
|
java_library(
|
||||||
|
name = "dom",
|
||||||
|
data = ["//lib:LICENSE-Apache2.0"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
exports = ["@mime4j_dom//jar"],
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user