Search in sources :

Example 41 with AuthToken

use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.

the class AuthProvider method getAuthToken.

public static AuthToken getAuthToken(Element authTokenElem, Account acct) throws AuthTokenException {
    AuthToken at = null;
    List<AuthProvider> providers = getProviders();
    AuthTokenException authTokenExp = null;
    for (AuthProvider ap : providers) {
        try {
            at = ap.authToken(authTokenElem, acct);
            if (at == null) {
                authTokenExp = new AuthTokenException("auth provider " + ap.getName() + " returned null");
            } else {
                return at;
            }
        } catch (AuthProviderException e) {
            // if there is no auth data for this provider, log and continue with next provider
            if (e.canIgnore()) {
                logger().debug(ap.getName() + ":" + e.getMessage());
            } else {
                authTokenExp = new AuthTokenException("auth provider error", e);
            }
        } catch (AuthTokenException e) {
            //log and store exception reference
            authTokenExp = e;
            logger().debug("getAuthToken error: provider=" + ap.getName() + ", err=" + e.getMessage(), e);
        }
    }
    //If multiple auth providers caused AuthTokenException, then last exception is rethrown from here.
    if (null != authTokenExp) {
        throw authTokenExp;
    }
    // there is no auth data for any of the enabled providers
    return null;
}
Also used : AuthTokenException(com.zimbra.cs.account.AuthTokenException) AuthToken(com.zimbra.cs.account.AuthToken)

Example 42 with AuthToken

use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.

the class AuthProvider method getAuthToken.

/**
     * The static getAuthToken methods go through all the providers, trying them in order
     * until one returns an AuthToken.
     *
     * If any provider in the chain throws AuthTokenException,
     * it will be stored and re-thrown to caller at the end.
     *
     * If more than one provider throws AuthTokenException then exception reported
     * by last provider will be thrown to caller.
     *
     * If AuthProviderException is thrown by provider then-
     *    - For AuthProviderException that is ignorable(AuthProviderException.NO_AUTH_TOKEN, AuthProviderException.NOT_SUPPORTED),
     *      it will be logged and next provider will be tried.
     *    - For AuthProviderExceptions that is not ignorable, AuthTokenException is generated and stored,
     *      thrown at the end if all provider fails.
     *
     * Return null when all providers fails to get AuthToken and no exception thrown by any provider.
     */
/**
     * @param req http request
     * @return an AuthToken object, or null if auth data is not present for any of the enabled providers
     * @throws ServiceException
     */
public static AuthToken getAuthToken(HttpServletRequest req, boolean isAdminReq) throws AuthTokenException {
    AuthToken at = null;
    List<AuthProvider> providers = getProviders();
    AuthTokenException authTokenExp = null;
    for (AuthProvider ap : providers) {
        try {
            at = ap.authToken(req, isAdminReq);
            if (at == null) {
                authTokenExp = new AuthTokenException("auth provider " + ap.getName() + " returned null");
            } else {
                return at;
            }
        } catch (AuthProviderException e) {
            // if there is no auth data for this provider, log and continue with next provider
            if (e.canIgnore()) {
                logger().debug(ap.getName() + ":" + e.getMessage());
            } else {
                authTokenExp = new AuthTokenException("auth provider error", e);
            }
        } catch (AuthTokenException e) {
            //log and store exception reference
            authTokenExp = e;
            logger().debug("getAuthToken error: provider=" + ap.getName() + ", err=" + e.getMessage(), e);
        }
    }
    //If multiple auth providers caused AuthTokenException, then last exception is rethrown from here.
    if (null != authTokenExp) {
        throw authTokenExp;
    }
    // there is no auth data for any of the enabled providers
    return null;
}
Also used : AuthTokenException(com.zimbra.cs.account.AuthTokenException) AuthToken(com.zimbra.cs.account.AuthToken)

Example 43 with AuthToken

use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.

the class ProxyServlet method doProxy.

private void doProxy(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    ZimbraLog.clearContext();
    boolean isAdmin = isAdminRequest(req);
    AuthToken authToken = isAdmin ? getAdminAuthTokenFromCookie(req, resp, true) : getAuthTokenFromCookie(req, resp, true);
    if (authToken == null) {
        String zAuthToken = req.getParameter(QP_ZAUTHTOKEN);
        if (zAuthToken != null) {
            try {
                authToken = AuthProvider.getAuthToken(zAuthToken);
                if (authToken.isExpired()) {
                    resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authtoken expired");
                    return;
                }
                if (!authToken.isRegistered()) {
                    resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authtoken is invalid");
                    return;
                }
                if (isAdmin && !authToken.isAdmin()) {
                    resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "permission denied");
                    return;
                }
            } catch (AuthTokenException e) {
                resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unable to parse authtoken");
                return;
            }
        }
    }
    if (authToken == null) {
        resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "no authtoken cookie");
        return;
    }
    // get the posted body before the server read and parse them.
    byte[] body = copyPostedData(req);
    // sanity check
    String target = req.getParameter(TARGET_PARAM);
    if (target == null) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
        return;
    }
    // check for permission
    URL url = new URL(target);
    if (!isAdmin && !checkPermissionOnTarget(url, authToken)) {
        resp.sendError(HttpServletResponse.SC_FORBIDDEN);
        return;
    }
    // determine whether to return the target inline or store it as an upload
    String uploadParam = req.getParameter(UPLOAD_PARAM);
    boolean asUpload = uploadParam != null && (uploadParam.equals("1") || uploadParam.equalsIgnoreCase("true"));
    HttpMethod method = null;
    try {
        HttpClient client = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();
        HttpProxyUtil.configureProxy(client);
        String reqMethod = req.getMethod();
        if (reqMethod.equalsIgnoreCase("GET")) {
            method = new GetMethod(target);
        } else if (reqMethod.equalsIgnoreCase("POST")) {
            PostMethod post = new PostMethod(target);
            if (body != null)
                post.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
            method = post;
        } else if (reqMethod.equalsIgnoreCase("PUT")) {
            PutMethod put = new PutMethod(target);
            if (body != null)
                put.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
            method = put;
        } else if (reqMethod.equalsIgnoreCase("DELETE")) {
            method = new DeleteMethod(target);
        } else {
            ZimbraLog.zimlet.info("unsupported request method: " + reqMethod);
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
        }
        // handle basic auth
        String auth, user, pass;
        auth = req.getParameter(AUTH_PARAM);
        user = req.getParameter(USER_PARAM);
        pass = req.getParameter(PASS_PARAM);
        if (auth != null && user != null && pass != null) {
            if (!auth.equals(AUTH_BASIC)) {
                ZimbraLog.zimlet.info("unsupported auth type: " + auth);
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
                return;
            }
            HttpState state = new HttpState();
            state.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pass));
            client.setState(state);
            method.setDoAuthentication(true);
        }
        Enumeration headers = req.getHeaderNames();
        while (headers.hasMoreElements()) {
            String hdr = (String) headers.nextElement();
            ZimbraLog.zimlet.debug("incoming: " + hdr + ": " + req.getHeader(hdr));
            if (canProxyHeader(hdr)) {
                ZimbraLog.zimlet.debug("outgoing: " + hdr + ": " + req.getHeader(hdr));
                if (hdr.equalsIgnoreCase("x-host"))
                    method.getParams().setVirtualHost(req.getHeader(hdr));
                else
                    method.addRequestHeader(hdr, req.getHeader(hdr));
            }
        }
        try {
            if (!(reqMethod.equalsIgnoreCase("POST") || reqMethod.equalsIgnoreCase("PUT"))) {
                method.setFollowRedirects(true);
            }
            HttpClientUtil.executeMethod(client, method);
        } catch (HttpException ex) {
            ZimbraLog.zimlet.info("exception while proxying " + target, ex);
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        int status = method.getStatusLine() == null ? HttpServletResponse.SC_INTERNAL_SERVER_ERROR : method.getStatusCode();
        // workaround for Alexa Thumbnails paid web service, which doesn't bother to return a content-type line
        Header ctHeader = method.getResponseHeader("Content-Type");
        String contentType = ctHeader == null || ctHeader.getValue() == null ? DEFAULT_CTYPE : ctHeader.getValue();
        InputStream targetResponseBody = method.getResponseBodyAsStream();
        if (asUpload) {
            String filename = req.getParameter(FILENAME_PARAM);
            if (filename == null || filename.equals(""))
                filename = new ContentType(contentType).getParameter("name");
            if ((filename == null || filename.equals("")) && method.getResponseHeader("Content-Disposition") != null)
                filename = new ContentDisposition(method.getResponseHeader("Content-Disposition").getValue()).getParameter("filename");
            if (filename == null || filename.equals(""))
                filename = "unknown";
            List<Upload> uploads = null;
            if (targetResponseBody != null) {
                try {
                    Upload up = FileUploadServlet.saveUpload(targetResponseBody, filename, contentType, authToken.getAccountId());
                    uploads = Arrays.asList(up);
                } catch (ServiceException e) {
                    if (e.getCode().equals(MailServiceException.UPLOAD_REJECTED))
                        status = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE;
                    else
                        status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
                }
            }
            resp.setStatus(status);
            FileUploadServlet.sendResponse(resp, status, req.getParameter(FORMAT_PARAM), null, uploads, null);
        } else {
            resp.setStatus(status);
            resp.setContentType(contentType);
            for (Header h : method.getResponseHeaders()) if (canProxyHeader(h.getName()))
                resp.addHeader(h.getName(), h.getValue());
            if (targetResponseBody != null)
                ByteUtil.copy(targetResponseBody, true, resp.getOutputStream(), true);
        }
    } finally {
        if (method != null)
            method.releaseConnection();
    }
}
Also used : ContentType(com.zimbra.common.mime.ContentType) PostMethod(org.apache.commons.httpclient.methods.PostMethod) HttpState(org.apache.commons.httpclient.HttpState) Upload(com.zimbra.cs.service.FileUploadServlet.Upload) URL(java.net.URL) HttpException(org.apache.commons.httpclient.HttpException) DeleteMethod(org.apache.commons.httpclient.methods.DeleteMethod) Enumeration(java.util.Enumeration) InputStream(java.io.InputStream) UsernamePasswordCredentials(org.apache.commons.httpclient.UsernamePasswordCredentials) Header(org.apache.commons.httpclient.Header) ContentDisposition(com.zimbra.common.mime.ContentDisposition) ServiceException(com.zimbra.common.service.ServiceException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException) AuthTokenException(com.zimbra.cs.account.AuthTokenException) HttpClient(org.apache.commons.httpclient.HttpClient) GetMethod(org.apache.commons.httpclient.methods.GetMethod) AuthToken(com.zimbra.cs.account.AuthToken) PutMethod(org.apache.commons.httpclient.methods.PutMethod) HttpMethod(org.apache.commons.httpclient.HttpMethod) ByteArrayRequestEntity(org.apache.commons.httpclient.methods.ByteArrayRequestEntity)

Example 44 with AuthToken

use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.

the class SoapEngine method dispatch.

/**
     * dispatch to the given serviceName the specified document,
     * which should be a soap envelope containing a document to
     * execute.
     *
     * @param path  the path (i.e., /service/foo) of the service to dispatch to
     * @param envelope the top-level element of the message
     * @param context user context parameters
     * @return an XmlObject which is a SoapEnvelope containing the response
     * @throws CsrfTokenException if CSRF token validation fails
     */
private Element dispatch(String path, Element envelope, Map<String, Object> context) {
    SoapProtocol soapProto = SoapProtocol.determineProtocol(envelope);
    if (soapProto == null) {
        // FIXME: have to pick 1.1 or 1.2 since we can't parse any
        soapProto = SoapProtocol.Soap12;
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.INVALID_REQUEST("unable to determine SOAP version", null));
    }
    Element doc = soapProto.getBodyElement(envelope);
    if (doc == null) {
        return soapFaultEnv(soapProto, "SOAP exception", ServiceException.INVALID_REQUEST("No SOAP body", null));
    }
    ServletRequest servReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    //Check if this handler requires authentication.
    //Do not perform CSRF checks for handlers that do not require authentication
    DocumentHandler handler = dispatcher.getHandler(doc);
    ZimbraSoapContext zsc = null;
    Element ectxt = soapProto.getHeader(envelope, HeaderConstants.CONTEXT);
    try {
        zsc = new ZimbraSoapContext(ectxt, doc.getQName(), handler, context, soapProto);
    } catch (ServiceException e) {
        return soapFaultEnv(soapProto, "unable to construct SOAP context", e);
    }
    boolean doCsrfCheck = false;
    if (servReq.getAttribute(CsrfFilter.CSRF_TOKEN_CHECK) != null) {
        doCsrfCheck = (Boolean) servReq.getAttribute(CsrfFilter.CSRF_TOKEN_CHECK);
    } else if (zsc.getAuthToken() != null && zsc.getAuthToken().isCsrfTokenEnabled()) {
        doCsrfCheck = true;
    }
    if (handler == null) {
        // handler, all other request should be mapped to a Handler
        if (!doc.getName().equals("BatchRequest")) {
            doCsrfCheck = false;
        } else {
            LOG.info("Only BatchRequest does not have a handler mapped to it. Request: %s, does not have a " + "handler, log for future handling.", path);
        }
    } else {
        if (doc.getName().equals("AuthRequest")) {
            // this is a Auth request, no CSRF validation happens
            doCsrfCheck = false;
        } else {
            doCsrfCheck = doCsrfCheck && handler.needsAuth(context);
        }
    }
    if (doCsrfCheck) {
        try {
            HttpServletRequest httpReq = (HttpServletRequest) servReq;
            // Bug: 96167 SoapEngine should be able to read CSRF token from HTTP headers
            String csrfToken = httpReq.getHeader(Constants.CSRF_TOKEN);
            if (StringUtil.isNullOrEmpty(csrfToken)) {
                Element contextElmt = soapProto.getHeader(envelope).getElement(HeaderConstants.E_CONTEXT);
                csrfToken = contextElmt.getAttribute(HeaderConstants.E_CSRFTOKEN);
            }
            AuthToken authToken = zsc.getAuthToken();
            if (!CsrfUtil.isValidCsrfToken(csrfToken, authToken)) {
                LOG.info("CSRF token validation failed for account");
                return soapFaultEnv(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
            }
        } catch (ServiceException e) {
            // we came here which implies clients supports CSRF authorization
            // and CSRF token is generated
            LOG.info("Error during CSRF validation.", e);
            return soapFaultEnv(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
        }
    }
    SoapProtocol responseProto = zsc.getResponseProtocol();
    String rid = zsc.getRequestedAccountId();
    String proxyAuthToken = null;
    if (rid != null) {
        Provisioning prov = Provisioning.getInstance();
        AccountUtil.addAccountToLogContext(prov, rid, ZimbraLog.C_NAME, ZimbraLog.C_ID, zsc.getAuthToken());
        String aid = zsc.getAuthtokenAccountId();
        if (aid != null && !rid.equals(aid)) {
            AccountUtil.addAccountToLogContext(prov, aid, ZimbraLog.C_ANAME, ZimbraLog.C_AID, zsc.getAuthToken());
        } else if (zsc.getAuthToken() != null && zsc.getAuthToken().getAdminAccountId() != null) {
            AccountUtil.addAccountToLogContext(prov, zsc.getAuthToken().getAdminAccountId(), ZimbraLog.C_ANAME, ZimbraLog.C_AID, zsc.getAuthToken());
        }
        try {
            Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(rid, false);
            if (mbox != null) {
                ZimbraLog.addMboxToContext(mbox.getId());
            }
        } catch (ServiceException ignore) {
        }
        try {
            AuthToken at = zsc.getAuthToken();
            if (at != null) {
                proxyAuthToken = prov.getProxyAuthToken(rid, context);
                at.setProxyAuthToken(proxyAuthToken);
            }
        } catch (ServiceException e) {
            LOG.warn("failed to set proxy auth token: %s", e.getMessage());
        }
    }
    if (zsc.getUserAgent() != null) {
        ZimbraLog.addUserAgentToContext(zsc.getUserAgent());
    }
    if (zsc.getVia() != null) {
        ZimbraLog.addViaToContext(zsc.getVia());
    }
    if (zsc.getSoapRequestId() != null) {
        ZimbraLog.addSoapIdToContext(zsc.getSoapRequestId());
    }
    logRequest(context, envelope);
    context.put(ZIMBRA_CONTEXT, zsc);
    context.put(ZIMBRA_ENGINE, this);
    HttpServletRequest servletRequest = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    boolean isResumed = !ContinuationSupport.getContinuation(servletRequest).isInitial();
    Element responseBody = null;
    if (!zsc.isProxyRequest()) {
        // if the client's told us that they've seen through notification block 50, we can drop old notifications up to that point
        acknowledgeNotifications(zsc);
        if (doc.getQName().equals(ZimbraNamespace.E_BATCH_REQUEST)) {
            boolean contOnError = doc.getAttribute(ZimbraNamespace.A_ONERROR, ZimbraNamespace.DEF_ONERROR).equals("continue");
            responseBody = zsc.createElement(ZimbraNamespace.E_BATCH_RESPONSE);
            if (!isResumed) {
                ZimbraLog.soap.info(doc.getName());
            }
            for (Element req : doc.listElements()) {
                String id = req.getAttribute(A_REQUEST_CORRELATOR, null);
                long start = System.currentTimeMillis();
                Element br = dispatchRequest(dispatcher.getHandler(req), req, context, zsc);
                if (!isResumed) {
                    ZimbraLog.soap.info("(batch) %s elapsed=%d", req.getName(), System.currentTimeMillis() - start);
                }
                if (id != null) {
                    br.addAttribute(A_REQUEST_CORRELATOR, id);
                }
                responseBody.addNonUniqueElement(br);
                if (!contOnError && responseProto.isFault(br)) {
                    break;
                }
                if (proxyAuthToken != null) {
                    // requests will invalidate it when proxying locally;
                    // make sure it's set for each sub-request in batch
                    zsc.getAuthToken().setProxyAuthToken(proxyAuthToken);
                }
            }
        } else {
            String id = doc.getAttribute(A_REQUEST_CORRELATOR, null);
            long start = System.currentTimeMillis();
            responseBody = dispatchRequest(handler, doc, context, zsc);
            if (!isResumed) {
                ZimbraLog.soap.info("%s elapsed=%d", doc.getName(), System.currentTimeMillis() - start);
            }
            if (id != null) {
                responseBody.addAttribute(A_REQUEST_CORRELATOR, id);
            }
        }
    } else {
        // We stick to local server's session when talking to the client.
        try {
            // Detach doc from its current parent, because it will be added as a child element of a new SOAP
            // envelope in the proxy dispatcher.  IllegalAddException will be thrown if we don't detach it first.
            doc.detach();
            ZimbraSoapContext zscTarget = new ZimbraSoapContext(zsc, zsc.getRequestedAccountId()).disableNotifications();
            long start = System.currentTimeMillis();
            responseBody = zsc.getProxyTarget().dispatch(doc, zscTarget);
            ZimbraLog.soap.info("%s proxy=%s,elapsed=%d", doc.getName(), zsc.getProxyTarget(), System.currentTimeMillis() - start);
            responseBody.detach();
        } catch (SoapFaultException e) {
            responseBody = e.getFault() != null ? e.getFault().detach() : responseProto.soapFault(e);
            LOG.debug("proxy handler exception", e);
        } catch (ServiceException e) {
            responseBody = responseProto.soapFault(e);
            LOG.info("proxy handler exception", e);
        } catch (Throwable e) {
            responseBody = responseProto.soapFault(ServiceException.FAILURE(e.toString(), e));
            if (e instanceof OutOfMemoryError) {
                Zimbra.halt("proxy handler exception", e);
            }
            LOG.warn("proxy handler exception", e);
        }
    }
    // put notifications (new sessions and incremental change notifications) to header...
    Element responseHeader = generateResponseHeader(zsc);
    // ... and return the composed response
    return responseProto.soapEnvelope(responseBody, responseHeader);
}
Also used : HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletRequest(javax.servlet.ServletRequest) Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) Provisioning(com.zimbra.cs.account.Provisioning) SoapFaultException(com.zimbra.common.soap.SoapFaultException) HttpServletRequest(javax.servlet.http.HttpServletRequest) AdminDocumentHandler(com.zimbra.cs.service.admin.AdminDocumentHandler) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) Mailbox(com.zimbra.cs.mailbox.Mailbox) AuthToken(com.zimbra.cs.account.AuthToken)

Example 45 with AuthToken

use of com.zimbra.cs.account.AuthToken in project zm-mailbox by Zimbra.

the class DocumentHandler method getOperationContext.

public static OperationContext getOperationContext(ZimbraSoapContext zsc, Session session) throws ServiceException {
    AuthToken at = zsc.getAuthToken();
    OperationContext octxt = new OperationContext(at);
    octxt.setChangeConstraint(zsc.getChangeConstraintType(), zsc.getChangeConstraintLimit());
    octxt.setRequestIP(zsc.getRequestIP()).setSession(session);
    octxt.setUserAgent(zsc.getUserAgent());
    octxt.setmResponseProtocol(zsc.getmResponseProtocol());
    octxt.setmRequestedAccountId(zsc.getRequestedAccountId());
    octxt.setmAuthTokenAccountId(zsc.getAuthtokenAccountId());
    return octxt;
}
Also used : OperationContext(com.zimbra.cs.mailbox.OperationContext) AuthToken(com.zimbra.cs.account.AuthToken)

Aggregations

AuthToken (com.zimbra.cs.account.AuthToken)98 ServiceException (com.zimbra.common.service.ServiceException)46 Account (com.zimbra.cs.account.Account)44 ZimbraAuthToken (com.zimbra.cs.account.ZimbraAuthToken)27 AuthTokenException (com.zimbra.cs.account.AuthTokenException)26 Element (com.zimbra.common.soap.Element)24 Provisioning (com.zimbra.cs.account.Provisioning)23 ZMailbox (com.zimbra.client.ZMailbox)19 ZAuthToken (com.zimbra.common.auth.ZAuthToken)18 IOException (java.io.IOException)14 Server (com.zimbra.cs.account.Server)12 ZimbraSoapContext (com.zimbra.soap.ZimbraSoapContext)12 HttpClient (org.apache.commons.httpclient.HttpClient)12 HashMap (java.util.HashMap)11 GetMethod (org.apache.commons.httpclient.methods.GetMethod)11 Test (org.junit.Test)11 SoapHttpTransport (com.zimbra.common.soap.SoapHttpTransport)10 ServletException (javax.servlet.ServletException)10 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)8 MailServiceException (com.zimbra.cs.mailbox.MailServiceException)8