use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class AppendMessage method store.
private int store(Mailbox mbox, Folder folder) throws ImapSessionClosedException, ServiceException, IOException {
boolean idxAttach = mbox.attachmentsIndexingEnabled();
Long receivedDate = date != null ? date.getTime() : null;
ParsedMessage pm = new ParsedMessage(content, receivedDate, idxAttach);
try {
if (!pm.getSender().isEmpty()) {
InternetAddress ia = new JavaMailInternetAddress(pm.getSender());
if (AccountUtil.addressMatchesAccountOrSendAs(mbox.getAccount(), ia.getAddress())) {
flags |= Flag.BITMASK_FROM_ME;
}
}
} catch (Exception e) {
}
DeliveryOptions dopt = new DeliveryOptions().setFolderId(folder).setNoICal(true).setFlags(flags).setTags(tags);
Message msg = mbox.addMessage(handler.getContext(), pm, dopt, null);
if (msg != null && sflags != 0 && handler.getState() == ImapHandler.State.SELECTED) {
ImapFolder selectedFolder = handler.getSelectedFolder();
// (note that this leaves session flags unset on remote appended messages)
if (selectedFolder != null) {
ImapMessage i4msg = selectedFolder.getById(msg.getId());
if (i4msg != null) {
i4msg.setSessionFlags(sflags, selectedFolder);
}
}
}
return msg == null ? -1 : msg.getId();
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class CalendarItem method processNewInviteReply.
boolean processNewInviteReply(Invite reply, String sender) throws ServiceException {
List<ZAttendee> attendees = reply.getAttendees();
String senderAddress = null;
if (sender != null && !sender.isEmpty()) {
try {
JavaMailInternetAddress address = new JavaMailInternetAddress(sender);
senderAddress = address.getAddress();
} catch (AddressException e) {
// ignore invalid sender address.
}
}
if (senderAddress != null && !attendees.isEmpty()) {
AccountAddressMatcher acctMatcher = null;
Account acct = Provisioning.getInstance().get(AccountBy.name, senderAddress);
if (acct != null) {
acctMatcher = new AccountAddressMatcher(acct);
}
Iterator<ZAttendee> iter = attendees.iterator();
while (iter.hasNext()) {
ZAttendee att = iter.next();
// Remove the attendee if not same as the sender.
if (!(att.addressMatches(senderAddress) || (acctMatcher != null && acctMatcher.matches(att.getAddress())))) {
iter.remove();
}
}
}
// trace logging
ZAttendee att1 = !attendees.isEmpty() ? attendees.get(0) : null;
if (att1 != null) {
String ptst = IcalXmlStrMap.sPartStatMap.toIcal(att1.getPartStat());
if (!reply.hasRecurId())
ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s", att1.getAddress(), ptst, mId, getFolderId(), reply.isPublic() ? reply.getName() : "(private)", mUid);
else
ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s, recurId=%s", att1.getAddress(), ptst, mId, getFolderId(), reply.isPublic() ? reply.getName() : "(private)", mUid, reply.getRecurId().getDtZ());
}
// Require private access permission only when we're replying to a private series/instance.
boolean requirePrivateCheck = requirePrivateCheck(reply);
OperationContext octxt = getMailbox().getOperationContext();
Account authAccount = octxt != null ? octxt.getAuthenticatedUser() : null;
boolean asAdmin = octxt != null ? octxt.isUsingAdminPrivileges() : false;
if (!canAccess(ACL.RIGHT_ACTION, authAccount, asAdmin, requirePrivateCheck))
throw ServiceException.PERM_DENIED("you do not have sufficient permissions to change this appointment/task's state");
boolean dirty = false;
// unique ID: UID+RECURRENCE_ID
// See RFC2446: 2.1.5 Message Sequencing
// UID already matches...next check if RecurId matches
// if so, then seqNo is next
// finally use DTStamp
Invite matchingInvite = matchingInvite(reply.getRecurId());
if (matchingInvite != null) {
// up to date with the organizer's event, provided there were no major changes.
if ((matchingInvite.isOrganizer() && (matchingInvite.getLastFullSeqNo() > reply.getSeqNo())) || (!matchingInvite.isOrganizer() && (matchingInvite.getSeqNo() > reply.getSeqNo()))) {
sLog.info("Invite-Reply %s is outdated (Calendar entry has higher SEQUENCE), ignoring!", reply);
return false;
}
// maybeStoreNewReply does some further checks which might invalidate this reply
// so, postpone updating attendee information until after that.
}
// they must be replying to a arbitrary instance)
for (ZAttendee at : attendees) {
if (mReplyList.maybeStoreNewReply(reply, at, this))
dirty = true;
}
if (!dirty) {
sLog.info("Invite-Reply %s is outdated ignoring!", reply);
return false;
}
if (matchingInvite != null) {
matchingInvite.updateMatchingAttendeesFromReply(reply);
updateLocalExceptionsWhichMatchSeriesReply(reply);
} else {
createPseudoExceptionForSingleInstanceReplyIfNecessary(reply);
}
saveMetadata();
return true;
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class FilterUtil method notifyMailto.
public static void notifyMailto(LmtpEnvelope envelope, OperationContext octxt, Mailbox mailbox, ParsedMessage parsedMessage, String from, int importance, Map<String, String> options, String message, String mailto, Map<String, List<String>> mailtoParams) throws MessagingException, ServiceException {
// X-Zimbra-Forwarded
MimeMessage mimeMessage = parsedMessage.getMimeMessage();
if (isMailLoop(mailbox, mimeMessage, new String[] { HEADER_FORWARDED, HEADER_AUTO_SUBMITTED })) {
String error = String.format("Detected a mail loop for message %s while notifying", Mime.getMessageID(mimeMessage));
throw ServiceException.FAILURE(error, null);
}
Account account = mailbox.getAccount();
MimeMessage notification = new Mime.FixedMimeMessage(JMSession.getSmtpSession(account));
MailSender mailSender = mailbox.getMailSender().setSaveToSent(false);
mailSender.setRedirectMode(true);
// add the forwarded header account names to detect the mail loop between accounts
for (String headerFwdAccountName : Mime.getHeaders(mimeMessage, HEADER_FORWARDED)) {
notification.addHeader(HEADER_FORWARDED, headerFwdAccountName);
}
notification.addHeader(HEADER_FORWARDED, account.getName());
// Envelope FROM
// RFC 5436 2.7. (1st item of the 'guidelines')
String originalEnvelopeFrom = envelope == null ? null : envelope.getSender().getEmailAddress();
if (originalEnvelopeFrom == null) {
// Whenever the envelope FROM of the original message is <>, set <> to the notification message too
mailSender.setEnvelopeFrom("<>");
} else if (!StringUtil.isNullOrEmpty(from)) {
List<com.zimbra.common.mime.InternetAddress> addr = com.zimbra.common.mime.InternetAddress.parseHeader(from);
String escapedFrom = StringEscapeUtils.escapeJava(addr.get(0).getAddress());
boolean matches;
do {
// if address contains single backslash, don't escape it
String patternString = ".*([\\p{ASCII}&&[^\\\\]])([\\\\][\\\\])([^\\\\])(.*)@.*";
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(escapedFrom);
matches = matcher.matches();
if (matches)
escapedFrom = new StringBuilder(escapedFrom).replace(matcher.start(2), matcher.end(2), "\\").toString();
} while (matches);
mailSender.setEnvelopeFrom(escapedFrom);
} else {
// System default value
mailSender.setEnvelopeFrom("<>");
}
// Envelope TO & Header To/Cc
// RFC 5436 2.7. (2nd and 5th item of the 'guidelines')
Set<String> envelopeTos = new HashSet<String>();
envelopeTos.add(mailto);
notification.addRecipient(javax.mail.Message.RecipientType.TO, new JavaMailInternetAddress(mailto));
List<String> tos = mailtoParams.get("to");
if (tos != null && tos.size() > 0) {
for (String to : tos) {
envelopeTos.add(to);
notification.addRecipient(javax.mail.Message.RecipientType.TO, new JavaMailInternetAddress(to));
}
}
List<String> ccs = mailtoParams.get("cc");
if (ccs != null && ccs.size() > 0) {
for (String cc : ccs) {
envelopeTos.add(cc);
notification.addRecipient(javax.mail.Message.RecipientType.CC, new JavaMailInternetAddress(cc));
}
}
List<String> bccs = mailtoParams.get("bcc");
if (bccs != null && bccs.size() > 0) {
for (String bcc : bccs) {
envelopeTos.add(bcc);
// No Bcc for the message header
}
}
mailSender.setRecipients(envelopeTos.toArray(new String[envelopeTos.size()]));
// Auto-Submitted
// RFC 5436 2.7. (3rd item of the 'guidelines') and 2.7.1.
StringBuilder autoSubmitted = new StringBuilder("auto-notified; owner-email=\"").append(account.getName()).append("\"");
notification.addHeader(HEADER_AUTO_SUBMITTED, autoSubmitted.toString());
// RFC 5436 2.7. (4th item of the 'guidelines')
if (!StringUtil.isNullOrEmpty(from)) {
// The "From:" header field of the notification message SHOULD be set
// to the value of the ":from" tag to the notify action, if one is
// specified, has email address syntax, and is valid according to the
// implementation-specific security checks.
notification.addHeader("from", from);
} else {
// If ":from" is not specified or is not valid, the
// "From:" header field of the notification message SHOULD be set
// either to the envelope "to" field from the triggering message, as
// used by Sieve...
// This MUST NOT be overridden by a "from" URI header, and any such
// URI header MUST be ignored.
notification.addHeader("from", mailbox.getAccount().getMail());
}
// RFC 5436 2.7. (6th item of the 'guidelines')
if (StringUtil.isNullOrEmpty(message)) {
List<String> subjectList = mailtoParams.get("subject");
if (subjectList != null && subjectList.size() > 0) {
message = subjectList.get(0);
} else {
String[] subjects = Mime.getHeaders(mimeMessage, "Subject");
if (subjects.length > 0) {
message = subjects[0];
}
}
}
notification.setSubject(message, getCharset(account, message));
// Body
// RFC 5436 2.7. (8th item of the 'guidelines')
List<String> bodys = mailtoParams.get("body");
if (bodys != null && bodys.size() > 0) {
String body = bodys.get(0);
notification.setText(body, getCharset(account, body));
} else {
notification.setText("");
}
notification.saveChanges();
// Misc.
notification.setSentDate(new Date());
for (String headerName : mailtoParams.keySet()) {
if (!("to".equalsIgnoreCase(headerName) || "cc".equalsIgnoreCase(headerName) || "bcc".equalsIgnoreCase(headerName) || "from".equalsIgnoreCase(headerName) || "subject".equalsIgnoreCase(headerName) || "auto-submitted".equalsIgnoreCase(headerName) || "x-zimbra-forwarded".equalsIgnoreCase(headerName) || "message-id".equalsIgnoreCase(headerName) || "date".equalsIgnoreCase(headerName) || "body".equalsIgnoreCase(headerName))) {
List<String> values = mailtoParams.get(headerName);
for (String value : values) {
notification.addHeaderLine(headerName + ": " + value);
}
}
}
mailSender.setDsnNotifyOptions(MailSender.DsnNotifyOption.NEVER);
mailSender.sendMimeMessage(octxt, mailbox, notification);
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class FilterUtil method notify.
public static void notify(OperationContext octxt, Mailbox mailbox, ParsedMessage parsedMessage, String emailAddr, String subjectTemplate, String bodyTemplate, int maxBodyBytes, List<String> origHeaders) throws MessagingException, ServiceException {
MimeMessage mimeMessage = parsedMessage.getMimeMessage();
if (isMailLoop(mailbox, mimeMessage, new String[] { HEADER_FORWARDED })) {
String error = String.format("Detected a mail loop for message %s.", Mime.getMessageID(mimeMessage));
throw ServiceException.FAILURE(error, null);
}
Account account = mailbox.getAccount();
MimeMessage notification = new Mime.FixedMimeMessage(JMSession.getSmtpSession(account));
// add the forwarded header account names to detect the mail loop between accounts
for (String headerFwdAccountName : Mime.getHeaders(mimeMessage, HEADER_FORWARDED)) {
notification.addHeader(HEADER_FORWARDED, headerFwdAccountName);
}
notification.addHeader(HEADER_FORWARDED, account.getName());
MailSender mailSender = mailbox.getMailSender().setSaveToSent(false);
// Map<String, String> vars = getVarsMap(mailbox, parsedMessage, mimeMessage);
if (origHeaders == null || origHeaders.isEmpty()) {
// no headers need to be copied from the original message
notification.setRecipient(javax.mail.Message.RecipientType.TO, new JavaMailInternetAddress(emailAddr));
notification.setSentDate(new Date());
if (!StringUtil.isNullOrEmpty(subjectTemplate)) {
notification.setSubject(subjectTemplate, getCharset(account, subjectTemplate));
}
} else {
if (origHeaders.size() == 1 && "*".equals(origHeaders.get(0))) {
// all headers need to be copied from the original message
Enumeration enumeration = mimeMessage.getAllHeaders();
while (enumeration.hasMoreElements()) {
Header header = (Header) enumeration.nextElement();
if (StringUtil.equal(header.getName(), HEADER_FORWARDED)) {
continue;
}
if (StringUtil.equal(header.getName(), HEADER_CONTENT_TYPE) || StringUtil.equal(header.getName(), HEADER_CONTENT_DISPOSITION)) {
// Zimbra Mime parser will add the correct Content Type if absent
continue;
}
notification.addHeader(header.getName(), header.getValue());
}
} else {
// some headers need to be copied from the original message
Set<String> headersToCopy = Sets.newHashSet(origHeaders);
boolean copySubject = false;
for (String header : headersToCopy) {
if ("Subject".equalsIgnoreCase(header)) {
copySubject = true;
}
if (StringUtil.equal(header, HEADER_FORWARDED)) {
continue;
}
String[] hdrVals = mimeMessage.getHeader(header);
if (hdrVals == null) {
continue;
}
for (String hdrVal : hdrVals) {
notification.addHeader(header, hdrVal);
}
}
if (!copySubject && !StringUtil.isNullOrEmpty(subjectTemplate)) {
notification.setSubject(subjectTemplate, getCharset(account, subjectTemplate));
}
}
mailSender.setRedirectMode(true);
mailSender.setRecipients(emailAddr);
}
String body = StringUtil.truncateIfRequired(bodyTemplate, maxBodyBytes);
notification.setText(body, getCharset(account, body));
notification.saveChanges();
if (isDeliveryStatusNotification(mimeMessage)) {
mailSender.setEnvelopeFrom("<>");
} else {
mailSender.setEnvelopeFrom(account.getName());
}
mailSender.setDsnNotifyOptions(MailSender.DsnNotifyOption.NEVER);
mailSender.sendMimeMessage(octxt, mailbox, notification);
}
use of com.zimbra.common.mime.shim.JavaMailInternetAddress in project zm-mailbox by Zimbra.
the class FilterUtil method reject.
public static void reject(OperationContext octxt, Mailbox mailbox, ParsedMessage parsedMessage, String reason, LmtpEnvelope envelope) throws MessagingException, ServiceException {
MimeMessage mimeMessage = parsedMessage.getMimeMessage();
if (isMailLoop(mailbox, mimeMessage, new String[] { HEADER_FORWARDED })) {
// Detected a mail loop. Do not send MDN, but just discard the message
String error = String.format("Detected a mail loop for message %s. No MDN sent.", Mime.getMessageID(mimeMessage));
ZimbraLog.filter.info(error);
throw ServiceException.FAILURE(error, null);
}
String reportTo = null;
if (envelope != null && envelope.hasSender()) {
reportTo = envelope.getSender().getEmailAddress();
}
if (reportTo == null || reportTo.isEmpty()) {
String[] returnPath = mimeMessage.getHeader(HEADER_RETURN_PATH);
if (returnPath == null || returnPath.length == 0) {
// >> NOT be generated if the MAIL FROM (or Return-Path) is empty.
throw new MessagingException("Neither 'envelope from' nor 'Return-Path' specified. Can't locate the address to reject to (No MDN sent)");
} else {
// At least one 'return-path' should exist.
reportTo = returnPath[0];
}
}
Account owner = mailbox.getAccount();
Locale locale = owner.getLocale();
String charset = owner.getPrefMailDefaultCharset();
if (charset == null) {
charset = MimeConstants.P_CHARSET_UTF8;
}
SMTPMessage report = AccountUtil.getSmtpMessageObj(owner);
// add the forwarded header account names to detect the mail loop between accounts
for (String headerFwdAccountName : Mime.getHeaders(mimeMessage, HEADER_FORWARDED)) {
report.addHeader(HEADER_FORWARDED, headerFwdAccountName);
}
report.addHeader(HEADER_FORWARDED, owner.getName());
// MDN header
report.setEnvelopeFrom("<>");
report.setRecipient(javax.mail.Message.RecipientType.TO, new JavaMailInternetAddress(reportTo));
String subject = L10nUtil.getMessage(MsgKey.seiveRejectMDNSubject, locale);
report.setSubject(subject);
report.setSentDate(new Date());
InternetAddress address = new JavaMailInternetAddress(owner.getName());
report.setFrom(address);
MimeMultipart multi = new ZMimeMultipart("report");
// part 1: human-readable notification
String text = L10nUtil.getMessage(MsgKey.seiveRejectMDNErrorMsg, locale) + "\n" + reason;
MimeBodyPart mpText = new ZMimeBodyPart();
mpText.setText(text, CharsetUtil.checkCharset(text, charset));
multi.addBodyPart(mpText);
// part 2: disposition notification
StringBuilder mdn = new StringBuilder();
mdn.append("Final-Recipient: rfc822;").append(owner.getName()).append("\r\n");
mdn.append("Disposition: automatic-action/MDN-sent-automatically");
mdn.append("; deleted\r\n");
MimeBodyPart mpMDN = new ZMimeBodyPart();
mpMDN.setText(mdn.toString(), MimeConstants.P_CHARSET_UTF8);
mpMDN.setHeader("Content-Type", "message/disposition-notification; charset=utf-8");
multi.addBodyPart(mpMDN);
// Assemble the MDN
report.setContent(multi);
report.setHeader("Content-Type", multi.getContentType() + "; report-type=disposition-notification");
report.saveChanges();
MailSender mailSender = mailbox.getMailSender().setSaveToSent(false);
mailSender.setRecipients(reportTo);
mailSender.setEnvelopeFrom("<>");
mailSender.sendMimeMessage(octxt, mailbox, report);
}
Aggregations