public Element sendRequestWithNotification(Element request) throws ServiceException {
   Server server =
       Provisioning.getInstance()
           .get(Key.ServerBy.name, OfflineConstants.SYNC_SERVER_PREFIX + getAccountId());
   if (server != null) {
     // when we first add an account, server is still null
     List<Session> soapSessions = getListeners(Session.Type.SOAP);
     Session session = null;
     if (soapSessions.size() == 1) {
       session = soapSessions.get(0);
     } else if (soapSessions.size() > 1) {
       // this occurs if user refreshes web browser (or opens ZD in two different browsers); older
       // session does not time out so there are now two listening
       // only the most recent is 'active'
       for (Session ses : soapSessions) {
         if (session == null || ses.accessedAfter(session.getLastAccessTime())) {
           session = ses;
         }
       }
     }
     if (session != null) {
       ZAuthToken zat = getAuthToken();
       if (zat != null) {
         AuthToken at =
             AuthProvider.getAuthToken(OfflineProvisioning.getOfflineInstance().getLocalAccount());
         at.setProxyAuthToken(zat.getValue());
         ProxyTarget proxy = new ProxyTarget(server, at, getSoapUri());
         // zscProxy needs to be for the 'ffffff-' account, but with target of *this* mailbox's
         // acct
         // currently UI receives SoapJS in its responses, we ask for that protocol so
         // notifications are handled correctly
         ZimbraSoapContext zscIn =
             new ZimbraSoapContext(
                 at, at.getAccountId(), SoapProtocol.Soap12, SoapProtocol.SoapJS);
         ZimbraSoapContext zscProxy = new ZimbraSoapContext(zscIn, getAccountId(), session);
         proxy.setTimeouts(OfflineLC.zdesktop_request_timeout.intValue());
         return DocumentHandler.proxyWithNotification(request, proxy, zscProxy, session);
       }
     }
   }
   return sendRequest(request);
 }
  /**
   * Always does a request -- caller is responsible for checking to see if this is necessary or not
   */
  private boolean bufferNextHits() throws ServiceException {
    if (atEndOfList || searchParams.getHopCount() > ZimbraSoapContext.MAX_HOP_COUNT) {
      return false;
    }

    bufferStartOffset = iterOffset;

    int chunkSizeToUse;
    if (singleShotRemoteRequest) {
      chunkSizeToUse = searchParams.getLimit();
    } else {
      chunkSizeToUse = searchParams.getLimit() * 2;
      if (chunkSizeToUse < MIN_BUFFER_CHUNK_SIZE) {
        chunkSizeToUse = MIN_BUFFER_CHUNK_SIZE;
      }
      if (chunkSizeToUse > 500) {
        chunkSizeToUse = 500;
      }
    }

    bufferEndOffset = bufferStartOffset + chunkSizeToUse;
    hitBuffer = new ArrayList<ProxiedHit>(chunkSizeToUse);

    Element searchElt = Element.create(responseProto, MailConstants.SEARCH_REQUEST);

    searchParams.setOffset(bufferStartOffset);
    searchParams.setLimit(chunkSizeToUse);
    searchParams.encodeParams(searchElt);
    if (singleShotRemoteRequest && (searchParams.getCursor() != null)) {
      Element cursorElt = searchElt.addElement(MailConstants.E_CURSOR);
      cursorElt.addAttribute(MailConstants.A_ID, searchParams.getCursor().getItemId().getId());
      if (searchParams.getCursor().getSortValue() != null) {
        cursorElt.addAttribute(MailConstants.A_SORTVAL, searchParams.getCursor().getSortValue());
      }
      if (searchParams.getCursor().getEndSortValue() != null) {
        cursorElt.addAttribute(
            MailConstants.A_ENDSORTVAL, searchParams.getCursor().getEndSortValue());
      }
    }

    // call the remote server now!
    Server targetServer = Provisioning.getInstance().get(Key.ServerBy.name, server);
    String baseurl = null;
    try {
      baseurl = URLUtil.getSoapURL(targetServer, false);
    } catch (ServiceException e) {
    }
    if (baseurl == null) {
      baseurl = URLUtil.getAdminURL(targetServer, AdminConstants.ADMIN_SERVICE_URI, true);
    }
    ProxyTarget proxy =
        new ProxyTarget(targetServer, authToken, baseurl + MailConstants.SEARCH_REQUEST.getName());
    if (mTimeout != -1) {
      proxy.setTimeouts(mTimeout);
    }

    ZimbraSoapContext zscInbound = searchParams.getRequestContext();
    ZimbraSoapContext zscProxy;
    if (zscInbound != null) {
      zscProxy = new ZimbraSoapContext(zscInbound, targetAcctId);
    } else {
      zscProxy =
          new ZimbraSoapContext(
              authToken,
              targetAcctId,
              responseProto,
              responseProto,
              searchParams.getHopCount() + 1);
    }

    long start = System.currentTimeMillis();
    Element searchResp = null;
    try {
      searchResp = DocumentHandler.proxyWithNotification(searchElt, proxy, zscProxy, zscInbound);
    } catch (SoapFaultException sfe) {
      ZimbraLog.index.warn(
          "Unable to (" + sfe + ") fetch search results from remote server " + proxy);
      atEndOfList = true;
      bufferEndOffset = iterOffset;
      return false;
    } catch (ServiceException e) {
      if (ServiceException.PROXY_ERROR.equals(e.getCode())) {
        ZimbraLog.index.warn(
            "Unable to (" + e + ") fetch search results from remote server " + proxy);
        atEndOfList = true;
        bufferEndOffset = iterOffset;
        return false;
      }
      throw e;
    } finally {
      long elapsed = System.currentTimeMillis() - start;
      ZimbraLog.index.debug(
          "Remote query took "
              + elapsed
              + "ms; URL="
              + proxy.toString()
              + "; QUERY="
              + searchElt.toString());
    }

    int hitOffset;
    if (singleShotRemoteRequest) {
      hitOffset =
          (int) searchResp.getAttributeLong(MailConstants.A_QUERY_OFFSET, bufferStartOffset);
    } else {
      hitOffset = (int) searchResp.getAttributeLong(MailConstants.A_QUERY_OFFSET);
    }
    boolean hasMore = searchResp.getAttributeBool(MailConstants.A_QUERY_MORE);

    assert (bufferStartOffset == hitOffset);

    SortBy sb = getSortBy();
    // put these hits into our buffer!
    int bufferIdx = 0;
    int stop = bufferEndOffset - bufferStartOffset;
    for (Iterator<Element> iter = searchResp.elementIterator();
        iter.hasNext() && bufferIdx < stop; ) {
      Element el = iter.next();
      if (el.getName().equalsIgnoreCase(MailConstants.E_INFO)) {
        for (Element info : el.listElements()) {
          queryInfo.add(new ProxiedQueryInfo(info));
        }
      } else {
        if (sb != null
            && ((SortBy.NAME_LOCALIZED_ASC.equals(sb))
                || (SortBy.NAME_LOCALIZED_DESC.equals(sb)))) {
          hitBuffer.add(
              bufferIdx++,
              new ProxiedContactHit(this, el, el.getAttribute(MailConstants.A_FILE_AS_STR)));
        } else {
          hitBuffer.add(
              bufferIdx++, new ProxiedHit(this, el, el.getAttribute(MailConstants.A_SORT_FIELD)));
        }
      }
    }

    // are we at the end of the line here?
    if (bufferIdx < stop || !hasMore) {
      // update the buffer-end-pointer
      bufferEndOffset = bufferStartOffset + bufferIdx;

      if (hasMore) {
        assert (!hasMore); // if bufferIdx < stop then !hasMore should be set...server bug!
      }
      atEndOfList = true;
    } else {
      assert (bufferEndOffset == bufferStartOffset + bufferIdx);
    }
    if (singleShotRemoteRequest) {
      atEndOfList = true;
    }

    assert (bufferStartOffset <= iterOffset);

    // OK, we were successful if we managed to buffer the current hit
    return (bufferEndOffset > iterOffset);
  }