org.xwiki.contrib.mailarchive.utils.internal.MailUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.contrib.mailarchive.utils.internal.MailUtils.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.mailarchive.utils.internal;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.contrib.mail.MailItem;
import org.xwiki.contrib.mailarchive.IMAUser;
import org.xwiki.contrib.mailarchive.IMailMatcher;
import org.xwiki.contrib.mailarchive.IType;
import org.xwiki.contrib.mailarchive.internal.data.MAUser;
import org.xwiki.contrib.mailarchive.utils.DecodedMailContent;
import org.xwiki.contrib.mailarchive.utils.IMailUtils;
import org.xwiki.contrib.mailarchive.utils.ITextUtils;
import org.xwiki.contrib.mailarchive.xwiki.IExtendedDocumentAccessBridge;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryManager;

/**
 * @version $Id$
 */
@Component
@Singleton
public class MailUtils implements IMailUtils {
    @Inject
    private ITextUtils textUtils;

    @Inject
    private QueryManager queryManager;

    @Inject
    private Logger logger;

    @Inject
    @Named("extended")
    private IExtendedDocumentAccessBridge bridge;

    @Override
    public String extractAddress(final String id) {
        int start = id.indexOf('<');
        int end = id.indexOf('>');
        if (start != -1 && end != -1) {
            return id.substring(start + 1, end);
        } else {
            return id;
        }
    }

    @Override
    public IMAUser parseUser(final String user, final boolean isMatchLdap) {
        logger.debug("parseUser {}, {}", user, isMatchLdap);

        MAUser maUser = new MAUser();
        maUser.setOriginalAddress(user);

        if (StringUtils.isBlank(user)) {
            return maUser;
        }

        String address = null;
        String personal = null;
        // Do our best to extract an address and a personal
        try {
            InternetAddress ia = null;
            InternetAddress[] result = InternetAddress.parse(user, true);
            if (result != null && result.length > 0) {
                ia = result[0];
                if (!StringUtils.isBlank(ia.getAddress())) {
                    address = ia.getAddress();
                }
                if (!StringUtils.isBlank(ia.getPersonal())) {
                    personal = ia.getPersonal();
                }
            }
        } catch (AddressException e) {
            logger.info("Email Address does not follow standards : " + user);
        }
        if (StringUtils.isBlank(address)) {
            String[] substrs = StringUtils.substringsBetween(user, "<", ">");
            if (substrs != null && substrs.length > 0) {
                address = substrs[0];
            } else {
                // nothing matches, we suppose recipient only contains email address
                address = user;
            }
        }
        if (StringUtils.isBlank(personal)) {
            if (user.contains("<")) {
                personal = StringUtils.substringBeforeLast(user, "<");
                if (StringUtils.isBlank(personal)) {
                    personal = StringUtils.substringBefore(address, "@");
                }
            }

        }
        maUser.setAddress(address);
        maUser.setDisplayName(personal);

        // Now to match a wiki profile
        logger.debug("parseUser extracted email {}", address);
        String parsedUser = null;
        if (!StringUtils.isBlank(address)) {
            // to match "-external" emails and old mails with '@gemplus.com'...
            String pattern = address.toLowerCase();
            pattern = pattern.replace("-external", "").replaceAll("^(.*)@.*[.]com$", "$1%@%.com");
            logger.debug("parseUser pattern applied {}", pattern);
            // Try to find a wiki profile with this email as parameter.
            // TBD : do this in the loading phase, and only try to search db if it was not found ?
            String xwql = "select doc.fullName from Document doc, doc.object(XWiki.XWikiUsers) as user where LOWER(user.email) like :pattern";

            List<String> profiles = null;
            try {
                profiles = queryManager.createQuery(xwql, Query.XWQL).bindValue("pattern", pattern).execute();
            } catch (QueryException e) {
                logger.warn("parseUser Query threw exception", e);
                profiles = null;
            }
            if (profiles == null || profiles.size() == 0) {
                logger.debug("parseUser found no wiki profile from db");
                return maUser;
            } else {
                if (isMatchLdap) {
                    logger.debug("parseUser Checking for LDAP authenticated profile(s) ...");
                    // If there exists one, we prefer the user that's been authenticated through LDAP
                    for (String usr : profiles) {
                        if (bridge.exists(usr, "XWiki.LDAPProfileClass")) {
                            parsedUser = usr;
                            logger.debug("parseUser Found LDAP authenticated profile {}", parsedUser);
                        }
                    }
                    if (parsedUser != null) {
                        maUser.setWikiProfile(parsedUser);
                        logger.debug("parseUser return {}", maUser);
                        return maUser;
                    }
                }
            }

            // If none has authenticated from LDAP, we return the first user found
            maUser.setWikiProfile(profiles.get(0));
            logger.debug("parseUser return {}", maUser);
            return maUser;

        } else {
            logger.debug("parseUser No email found to match");
            return maUser;
        }

    }

    @Override
    public DecodedMailContent decodeMailContent(final String originalHtml, final String originalBody,
            final boolean cut) throws IOException {
        String html = "";
        String body = "";

        if (!StringUtils.isEmpty(originalHtml)) {
            html = textUtils.unzipString(originalHtml);
        } else {
            // body is only plain text
            if (!StringUtils.isBlank(originalBody)) {
                if (originalBody.startsWith("<html") || originalBody.startsWith("<meta")
                        || originalBody.contains("<br>") || originalBody.contains("<br/>")) {
                    html = originalBody;
                }
            }
        }

        if (!StringUtils.isBlank(html)) {
            Matcher m = Pattern.compile("<span [^>]*>From:<\\\\/span>", Pattern.MULTILINE).matcher(html);
            if (cut && m.find()) {
                html = html.substring(0, m.start() - 1);
            } else if (cut && html.contains("<b>From:</b>")) {
                html = html.substring(0, html.indexOf("<b>From:</b>") - 1);
            }
            return new DecodedMailContent(true, html);
        } else {
            body = originalBody;
            Matcher m = Pattern.compile("\\n[\\s]*From:", Pattern.MULTILINE).matcher(body);
            if (cut && m.find()) {
                body = body.substring(0, m.start() + 1);
            }
            return new DecodedMailContent(false, body);
        }

    }

    @Override
    public List<IType> extractTypes(final Collection<IType> types, final MailItem mailItem) {
        logger.debug("extractTypes(types={}, mailItem={})", types, mailItem);

        List<IType> result = new ArrayList<IType>();

        if (types == null || mailItem == null) {
            throw new IllegalArgumentException("extractTypes: Types and mailitem can't be null");
        }

        // set IType
        for (IType type : types) {
            logger.debug("Checking for type " + type);
            boolean matched = true;
            for (IMailMatcher mailMatcher : type.getMatchers()) {
                logger.debug("  Checking for matcher " + mailMatcher);
                List<String> fields = mailMatcher.getFields();
                String regexp = mailMatcher.getExpression();

                Pattern pattern = null;
                if (mailMatcher.isAdvancedMode()) {
                    // Manage multi line and ignore case in regexp if needed
                    if (mailMatcher.isIgnoreCase() || mailMatcher.isMultiLine()) {
                        String prefix = "(?";
                        if (mailMatcher.isIgnoreCase()) {
                            prefix += 'i';
                        }
                        if (mailMatcher.isMultiLine()) {
                            prefix += 'm';
                        }
                        prefix += ')';
                        regexp = prefix + regexp;
                    }

                    try {
                        pattern = Pattern.compile(regexp);
                    } catch (PatternSyntaxException e) {
                        logger.warn("Invalid Pattern " + regexp + "can't be compiled, skipping this mail type");
                        break;
                    }

                }

                Matcher matcher = null;
                boolean fieldMatch = false;
                for (String field : fields) {
                    String fieldValue = "";
                    if ("from".equals(field)) {
                        fieldValue = mailItem.getFrom();
                    } else if ("to".equals(field)) {
                        fieldValue = mailItem.getTo();
                    } else if ("cc".equals(field)) {
                        fieldValue = mailItem.getCc();
                    } else if ("subject".equals(field)) {
                        fieldValue = mailItem.getSubject();
                    }
                    logger.debug("  Checking field " + field + " with value [" + fieldValue + "] against pattern ["
                            + regexp + "] DEBUG [" + textUtils.byte2hex(regexp.getBytes()) + "]");
                    if (mailMatcher.isAdvancedMode()) {
                        matcher = pattern.matcher(fieldValue);
                        logger.debug("Matcher : " + matcher);
                        if (matcher != null) {
                            fieldMatch = matcher.find();
                            logger.debug("fieldMatch: " + fieldMatch);
                            logger.debug("matcher.matches : " + matcher.matches());
                        }
                    } else {
                        if (mailMatcher.isIgnoreCase()) {
                            fieldMatch = StringUtils.containsIgnoreCase(fieldValue, regexp);
                        } else {
                            fieldMatch = StringUtils.contains(fieldValue, regexp);
                        }
                    }
                    if (fieldMatch) {
                        logger.debug(
                                "Field " + field + " value [" + fieldValue + "] matches pattern [" + regexp + "]");
                        break;
                    }
                }
                matched = matched && fieldMatch;
            }
            if (matched) {
                logger.info("Matched type " + type.getName());
                result.add(type);
                matched = true;
            }
        }
        return result;
    }
}