/** Accept the representation of a mail received from a sender, and create it. */
  @Override
  public void acceptRepresentation(Representation entity) throws ResourceException {
    final Form form = new Form(entity);
    final Mail mail = new Mail();
    mail.setStatus(Mail.STATUS_RECEIVED);

    // Look for an existing contact or create it.
    final String senderAddress = form.getFirstValue("senderAddress");
    final String senderName = form.getFirstValue("senderName");

    Contact contact = getObjectsFacade().lookForContact(senderAddress, this.mailbox);
    if (contact == null) {
      contact = new Contact();
      contact.setMailAddress(senderAddress);
      contact.setName(senderName);
    }
    mail.setSender(contact);

    mail.setMessage(form.getFirstValue("message"));
    mail.setSubject(form.getFirstValue("subject"));
    // form2.add("sendingDate", mail.getSendingDate().toString());
    final Series<Parameter> recipients = form.subList("recipient");
    for (final Parameter recipient : recipients) {
      contact = getObjectsFacade().lookForContact(recipient.getValue(), this.mailbox);
      if (contact == null) {
        contact = new Contact();
        final String[] recipientValues = recipient.getValue().split("\\$");
        contact.setMailAddress(recipientValues[0]);
        contact.setName(recipientValues[1]);
      }
      mail.getRecipients().add(contact);
    }
    getObjectsFacade().createMail(this.mailbox, mail);
  }
  @Override
  public void formatResponse(
      ChallengeWriter cw,
      ChallengeResponse challenge,
      Request request,
      Series<Header> httpHeaders) {

    if (challenge.getIdentifier() != null) {
      cw.appendQuotedChallengeParameter("username", challenge.getIdentifier());
    }

    if (challenge.getRealm() != null) {
      cw.appendQuotedChallengeParameter("realm", challenge.getRealm());
    }

    if (challenge.getServerNonce() != null) {
      cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce());
    }

    if (challenge.getDigestRef() != null) {
      challenge.setDigestRef(new Reference(request.getResourceRef().getPath()));
      cw.appendQuotedChallengeParameter("uri", challenge.getDigestRef().toString());
    }

    char[] responseDigest = formatResponseDigest(challenge, request);

    if (responseDigest != null) {
      cw.appendQuotedChallengeParameter("response", new String(responseDigest));
    }

    if ((challenge.getDigestAlgorithm() != null)
        && !Digest.ALGORITHM_MD5.equals(challenge.getDigestAlgorithm())) {
      cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm());
    }

    if (challenge.getClientNonce() != null) {
      cw.appendQuotedChallengeParameter("cnonce", challenge.getClientNonce());
    }

    if (challenge.getOpaque() != null) {
      cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque());
    }

    if (challenge.getQuality() != null) {
      cw.appendChallengeParameter("qop", challenge.getQuality());
    }

    if ((challenge.getQuality() != null) && (challenge.getServerNounceCount() > 0)) {
      cw.appendChallengeParameter("nc", challenge.getServerNounceCountAsHex());
    }

    for (Parameter param : challenge.getParameters()) {
      if (HeaderUtils.isToken(param.getValue())) {
        cw.appendChallengeParameter(param);
      } else {
        cw.appendQuotedChallengeParameter(param);
      }
    }
  }
Exemple #3
0
 /**
  * Writes a header line.
  *
  * @param header The header to write.
  * @param os The output stream.
  * @throws IOException
  */
 public static void writeHeader(Parameter header, OutputStream os) throws IOException {
   os.write(header.getName().getBytes());
   os.write(':');
   os.write(' ');
   os.write(header.getValue().getBytes());
   os.write(13); // CR
   os.write(10); // LF
 }
Exemple #4
0
  /**
   * Read a header. Return null if the last header was already read.
   *
   * @param is The message input stream.
   * @param sb The string builder to reuse.
   * @return The header read or null.
   * @throws IOException
   */
  public static Parameter readHeader(InputStream is, StringBuilder sb) throws IOException {
    Parameter result = null;

    // Detect the end of headers
    int next = is.read();
    if (HttpUtils.isCarriageReturn(next)) {
      next = is.read();
      if (!HttpUtils.isLineFeed(next)) {
        throw new IOException(
            "Invalid end of headers. Line feed missing after the carriage return.");
      }
    } else {
      result = new Parameter();

      // Parse the header name
      while ((next != -1) && (next != ':')) {
        sb.append((char) next);
        next = is.read();
      }

      if (next == -1) {
        throw new IOException("Unable to parse the header name. End of stream reached too early.");
      } else {
        result.setName(sb.toString());
        sb.delete(0, sb.length());

        next = is.read();
        while (HttpUtils.isSpace(next)) {
          // Skip any separator space between colon and header value
          next = is.read();
        }

        // Parse the header value
        while ((next != -1) && (!HttpUtils.isCarriageReturn(next))) {
          sb.append((char) next);
          next = is.read();
        }

        if (next == -1) {
          throw new IOException(
              "Unable to parse the header value. End of stream reached too early.");
        } else {
          next = is.read();

          if (HttpUtils.isLineFeed(next)) {
            result.setValue(sb.toString());
            sb.delete(0, sb.length());
          } else {
            throw new IOException(
                "Unable to parse the HTTP header value. The carriage return must be followed by a line feed.");
          }
        }
      }
    }

    return result;
  }
  @Override
  public void doInit() {
    super.doInit();

    final Form form = getRequest().getResourceRef().getQueryAsForm();
    for (Parameter parameter : form) {
      params.put(parameter.getName(), parameter.getValue());
    }
  }
  /**
   * Returns the canonicalized resource name.
   *
   * @param resourceRef The resource reference.
   * @return The canonicalized resource name.
   */
  private static String getCanonicalizedResourceName(Reference resourceRef) {
    Form form = resourceRef.getQueryAsForm();
    Parameter param = form.getFirst("comp", true);

    if (param != null) {
      StringBuilder sb = new StringBuilder(resourceRef.getPath());
      return sb.append("?").append("comp=").append(param.getValue()).toString();
    }

    return resourceRef.getPath();
  }
 private static void completeOperationQueryParameter(
     Operation operation, MethodAnnotationInfo mai) {
   if (mai.getQuery() != null) {
     Form form = new Form(mai.getQuery());
     for (org.restlet.data.Parameter parameter : form) {
       QueryParameter queryParameter = new QueryParameter();
       queryParameter.setName(parameter.getName());
       queryParameter.setRequired(true);
       queryParameter.setDescription(
           StringUtils.isNullOrEmpty(parameter.getValue())
               ? ""
               : "Value: " + parameter.getValue());
       queryParameter.setDefaultValue(parameter.getValue());
       queryParameter.setAllowMultiple(false);
       operation.getQueryParameters().add(queryParameter);
     }
   }
 }
  @Override
  public void formatRequest(
      ChallengeWriter cw, ChallengeRequest challenge, Response response, Series<Header> httpHeaders)
      throws IOException {
    // Format the parameters WWW-Authenticate: OAuth realm='Example
    // Service', error='expired-token'
    cw.append("realm='");
    cw.append(challenge.getRealm());
    cw.append("'");

    for (Parameter p : challenge.getParameters()) {
      cw.append(", ");
      cw.append(p.getName());
      cw.append("='");
      cw.append(p.getValue());
      cw.append("'");
    }
  }
    @Override
    public void handle(Request request, Response response) {
      Form form = request.getResourceRef().getQueryAsForm();
      List<Range> ranges = request.getRanges();
      boolean match = false;

      for (Parameter parameter : form) {
        long index = 0;
        long length = 0;
        String value = parameter.getValue();
        if (value.startsWith("-")) {
          index = Range.INDEX_LAST;
          length = Long.parseLong(value.substring(1));
        } else if (value.endsWith("-")) {
          index = Long.parseLong(value.substring(0, value.length() - 1));
          length = Range.SIZE_MAX;
        } else {
          String[] tab = value.split("-");
          if (tab.length == 2) {
            index = Long.parseLong(tab[0]);
            length = Long.parseLong(tab[1]) - index;
          }
        }

        boolean found = false;
        for (Range range : ranges) {
          found = (index == range.getIndex()) && (length == range.getSize());
          if (found) {
            break;
          }
        }
        if (!found) {
          break;
        }
        match = true;
      }
      if (match) {
        response.setStatus(Status.SUCCESS_OK);
        response.setEntity(str1000, MediaType.TEXT_PLAIN);
      } else {
        response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
      }
    }
  /**
   * Extract the quality value. If the value is not found, 1 is returned.
   *
   * @param parameters The preference parameters.
   * @return The quality value.
   */
  protected float extractQuality(Series<Parameter> parameters) {
    float result = 1F;
    boolean found = false;

    if (parameters != null) {
      Parameter param = null;
      for (final Iterator<Parameter> iter = parameters.iterator(); !found && iter.hasNext(); ) {
        param = iter.next();
        if (param.getName().equals("q")) {
          result = PreferenceUtils.parseQuality(param.getValue());
          found = true;

          // Remove the quality parameter as we will directly store it
          // in the Preference object
          iter.remove();
        }
      }
    }

    return result;
  }
  @Override
  public PreferenceWriter append(Preference<?> pref) {
    append(pref.getMetadata().getName());

    if (pref.getQuality() < 1F) {
      append(";q=");
      appendQuality(pref.getQuality());
    }

    if (pref.getParameters() != null) {
      Parameter param;

      for (Iterator<Parameter> iter = pref.getParameters().iterator(); iter.hasNext(); ) {
        param = iter.next();

        if (param.getName() != null) {
          append(';').append(param.getName());

          if ((param.getValue() != null) && (param.getValue().length() > 0)) {
            append('=').append(param.getValue());
          }
        }
      }
    }

    return this;
  }
  /**
   * Extract the media parameters. Only leave as the quality parameter if found. Modifies the
   * parameters list.
   *
   * @param parameters All the preference parameters.
   * @return The media parameters.
   */
  protected Series<Parameter> extractMediaParams(Series<Parameter> parameters) {
    Series<Parameter> result = null;
    boolean qualityFound = false;
    Parameter param = null;

    if (parameters != null) {
      result = new Form();

      for (final Iterator<Parameter> iter = parameters.iterator();
          !qualityFound && iter.hasNext(); ) {
        param = iter.next();

        if (param.getName().equals("q")) {
          qualityFound = true;
        } else {
          iter.remove();
          result.add(param);
        }
      }
    }

    return result;
  }
  /**
   * Sends the request to the client. Commits the request line, headers and optional entity and send
   * them over the network.
   *
   * @param request The high-level request.
   * @return The result status.
   */
  @Override
  public Status sendRequest(Request request) {
    Status result = null;

    try {
      final Representation entity = request.getEntity();

      // Set the request headers
      for (final Parameter header : getRequestHeaders()) {
        getHttpMethod().addRequestHeader(header.getName(), header.getValue());
      }

      // For those method that accept enclosing entites, provide it
      if ((entity != null) && (getHttpMethod() instanceof EntityEnclosingMethod)) {
        final EntityEnclosingMethod eem = (EntityEnclosingMethod) getHttpMethod();
        eem.setRequestEntity(
            new RequestEntity() {
              public long getContentLength() {
                return entity.getSize();
              }

              public String getContentType() {
                return (entity.getMediaType() != null) ? entity.getMediaType().toString() : null;
              }

              public boolean isRepeatable() {
                return !entity.isTransient();
              }

              public void writeRequest(OutputStream os) throws IOException {
                entity.write(os);
              }
            });
      }

      // Ensure that the connection is active
      this.clientHelper.getHttpClient().executeMethod(getHttpMethod());

      // Now we can access the status code, this MUST happen after closing
      // any open request stream.
      result = new Status(getStatusCode(), null, getReasonPhrase(), null);

      // If there is no response body, immediately release the connection
      if (getHttpMethod().getResponseBodyAsStream() == null) {
        getHttpMethod().releaseConnection();
      }
    } catch (IOException ioe) {
      this.clientHelper
          .getLogger()
          .log(
              Level.WARNING,
              "An error occurred during the communication with the remote HTTP server.",
              ioe);
      result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION, ioe);

      // Release the connection
      getHttpMethod().releaseConnection();
    }

    return result;
  }
  @Get
  public void resetMethod(String body) {

    String urilistener = null;
    String timeoutString = null;
    Long timeout = -1l;
    short warmStartValue = 0x00;

    Parameter urilistenerParam =
        getRequest().getResourceRef().getQueryAsForm().getFirst(Resources.URI_PARAM_URILISTENER);
    if (urilistenerParam != null) {
      urilistener = urilistenerParam.getValue().trim();
    }
    Parameter timeoutParam =
        getRequest().getResourceRef().getQueryAsForm().getFirst(Resources.URI_PARAM_TIMEOUT);
    if (timeoutParam != null) {
      timeoutString = timeoutParam.getValue().trim();
      try {
        timeout = Long.decode("0x" + timeoutString);
      } catch (NumberFormatException nfe) {
        Info info = new Info();
        Status _st = new Status();
        _st.setCode((short) GatewayConstants.GENERAL_ERROR);
        _st.setMessage(nfe.getMessage());
        info.setStatus(_st);
        Info.Detail detail = new Info.Detail();
        info.setDetail(detail);
        getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
        return;
      }
      if (!Util.isUnsigned32(timeout)) {
        Info info = new Info();
        Status _st = new Status();
        _st.setCode((short) GatewayConstants.GENERAL_ERROR);
        _st.setMessage("Wrong timeout");
        info.setStatus(_st);
        Info.Detail detail = new Info.Detail();
        info.setDetail(detail);
        getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
        return;
      }
    } else {
      // The timeout parameter is optional. If not provided we use a
      // default.
      timeout = (long) INTERNAL_TIMEOUT;
    }

    // TODO Set the right warm start value.
    String warmparamString = null;

    Parameter warmparam =
        getRequest()
            .getResourceRef()
            .getQueryAsForm()
            .getFirst(Resources.URI_PARAM_START_MODE_RESET);
    if (warmparam != null) {
      warmparamString = warmparam.getValue().trim();
      try {
        warmStartValue = Short.decode(warmparamString);
      } catch (NumberFormatException nfe) {
        Info info = new Info();
        Status _st = new Status();
        _st.setCode((short) GatewayConstants.GENERAL_ERROR);
        _st.setMessage(nfe.getMessage());
        info.setStatus(_st);
        Info.Detail detail = new Info.Detail();
        info.setDetail(detail);
        getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
        return;
      }

    } else {
      // The warm start value is mandatory!!
      Info info = new Info();
      Status _st = new Status();
      _st.setCode((short) GatewayConstants.GENERAL_ERROR);
      _st.setMessage("The warm start value is mandatory");
      info.setStatus(_st);
      Info.Detail detail = new Info.Detail();
      info.setDetail(detail);
      getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
      return;
    }

    if (urilistenerParam == null) {
      // Sync reset
      try {
        proxyGalInterface =
            getRestManager()
                .getClientObjectKey(-1, getClientInfo().getAddress())
                .getGatewayInterface();

        Status result = proxyGalInterface.resetDongleSync(timeout, warmStartValue);
        Info info = new Info();
        info.setStatus(result);
        getResponse().setEntity(Util.marshal(info), MediaType.TEXT_XML);

      } catch (Exception e) {
        Info info = new Info();
        Status _st = new Status();
        _st.setCode((short) GatewayConstants.GENERAL_ERROR);
        _st.setMessage(e.getMessage());
        info.setStatus(_st);
        Info.Detail detail = new Info.Detail();
        info.setDetail(detail);
        getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
        return;
      }
    } else {
      // Async reset
      try {
        ClientResources rcmal =
            getRestManager()
                .getClientObjectKey(
                    Util.getPortFromUriListener(urilistener), getClientInfo().getAddress());
        proxyGalInterface = rcmal.getGatewayInterface();
        if (!urilistener.equals("")) {
          rcmal.getClientEventListener().setResetDestination(urilistener);
        }
        proxyGalInterface.resetDongle(timeout, warmStartValue);
        Info.Detail detail = new Info.Detail();
        Info infoToReturn = new Info();
        Status status = new Status();
        status.setCode((short) GatewayConstants.SUCCESS);
        infoToReturn.setStatus(status);
        infoToReturn.setRequestIdentifier(Util.getRequestIdentifier());
        infoToReturn.setDetail(detail);
        getResponse().setEntity(Util.marshal(infoToReturn), MediaType.TEXT_XML);
        return;
      } catch (Exception e) {
        Info info = new Info();
        Status _st = new Status();
        _st.setCode((short) GatewayConstants.GENERAL_ERROR);
        _st.setMessage("The warm start value is mandatory");
        info.setStatus(_st);
        Info.Detail detail = new Info.Detail();
        info.setDetail(detail);
        getResponse().setEntity(Util.marshal(info), MediaType.APPLICATION_XML);
        return;
      }
    }
  }
  @Override
  public void parseRequest(
      ChallengeRequest challenge, Response response, Series<Header> httpHeaders) {
    if (challenge.getRawValue() != null) {
      HeaderReader<Object> hr = new HeaderReader<Object>(challenge.getRawValue());

      try {
        Parameter param = hr.readParameter();

        while (param != null) {
          try {
            if ("realm".equals(param.getName())) {
              challenge.setRealm(param.getValue());
            } else if ("domain".equals(param.getName())) {
              challenge.getDomainRefs().add(new Reference(param.getValue()));
            } else if ("nonce".equals(param.getName())) {
              challenge.setServerNonce(param.getValue());
            } else if ("opaque".equals(param.getName())) {
              challenge.setOpaque(param.getValue());
            } else if ("stale".equals(param.getName())) {
              challenge.setStale(Boolean.valueOf(param.getValue()));
            } else if ("algorithm".equals(param.getName())) {
              challenge.setDigestAlgorithm(param.getValue());
            } else if ("qop".equals(param.getName())) {
              // challenge.setDigestAlgorithm(param.getValue());
            } else {
              challenge.getParameters().add(param);
            }

            if (hr.skipValueSeparator()) {
              param = hr.readParameter();
            } else {
              param = null;
            }
          } catch (Exception e) {
            Context.getCurrentLogger()
                .log(Level.WARNING, "Unable to parse the challenge request header parameter", e);
          }
        }
      } catch (Exception e) {
        Context.getCurrentLogger()
            .log(Level.WARNING, "Unable to parse the challenge request header parameter", e);
      }
    }
  }
  @Override
  public void parseResponse(
      ChallengeResponse challenge, Request request, Series<Header> httpHeaders) {
    if (challenge.getRawValue() != null) {
      HeaderReader<Object> hr = new HeaderReader<Object>(challenge.getRawValue());

      try {
        Parameter param = hr.readParameter();

        while (param != null) {
          try {
            if ("username".equals(param.getName())) {
              challenge.setIdentifier(param.getValue());
            } else if ("realm".equals(param.getName())) {
              challenge.setRealm(param.getValue());
            } else if ("nonce".equals(param.getName())) {
              challenge.setServerNonce(param.getValue());
            } else if ("uri".equals(param.getName())) {
              challenge.setDigestRef(new Reference(param.getValue()));
            } else if ("response".equals(param.getName())) {
              challenge.setSecret(param.getValue());
            } else if ("algorithm".equals(param.getName())) {
              challenge.setDigestAlgorithm(param.getValue());
            } else if ("cnonce".equals(param.getName())) {
              challenge.setClientNonce(param.getValue());
            } else if ("opaque".equals(param.getName())) {
              challenge.setOpaque(param.getValue());
            } else if ("qop".equals(param.getName())) {
              challenge.setQuality(param.getValue());
            } else if ("nc".equals(param.getName())) {
              challenge.setServerNounceCount(Integer.valueOf(param.getValue(), 16));
            } else {
              challenge.getParameters().add(param);
            }
          } catch (Throwable e) {
            Context.getCurrentLogger()
                .log(Level.WARNING, "Unable to parse the challenge request header parameter", e);
          }
          if (hr.skipValueSeparator()) {
            param = hr.readParameter();
          } else {
            param = null;
          }
        }
      } catch (Exception e) {
        Context.getCurrentLogger()
            .log(Level.WARNING, "Unable to parse the challenge request header parameter", e);
      }
    }
  }
  @Override
  public void formatRequest(
      ChallengeWriter cw, ChallengeRequest challenge, Response response, Series<Header> httpHeaders)
      throws IOException {

    if (challenge.getRealm() != null) {
      cw.appendQuotedChallengeParameter("realm", challenge.getRealm());
    }

    if (!challenge.getDomainRefs().isEmpty()) {
      cw.append(", domain=\"");

      for (int i = 0; i < challenge.getDomainRefs().size(); i++) {
        if (i > 0) {
          cw.append(' ');
        }

        cw.append(challenge.getDomainRefs().get(i).toString());
      }

      cw.append('"');
    }

    if (challenge.getServerNonce() != null) {
      cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce());
    }

    if (challenge.getOpaque() != null) {
      cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque());
    }

    if (challenge.isStale()) {
      cw.appendChallengeParameter("stale", "true");
    }

    if (challenge.getDigestAlgorithm() != null) {
      cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm());
    }

    if (!challenge.getQualityOptions().isEmpty()) {
      cw.append(", qop=\"");

      for (int i = 0; i < challenge.getQualityOptions().size(); i++) {
        if (i > 0) {
          cw.append(',');
        }

        cw.appendToken(challenge.getQualityOptions().get(i).toString());
      }

      cw.append('"');
    }

    for (Parameter param : challenge.getParameters()) {
      if (HeaderUtils.isToken(param.getValue())) {
        cw.appendChallengeParameter(param);
      } else {
        cw.appendQuotedChallengeParameter(param);
      }
    }
  }
  @Override
  public ContentType readValue() throws IOException {
    ContentType result = null;

    boolean readingMediaType = true;
    boolean readingParamName = false;
    boolean readingParamValue = false;

    StringBuilder mediaTypeBuffer = new StringBuilder();
    StringBuilder paramNameBuffer = null;
    StringBuilder paramValueBuffer = null;

    Series<Parameter> parameters = null;
    String nextValue = readRawValue();
    int nextIndex = 0;

    if (nextValue != null) {
      int nextChar = nextValue.charAt(nextIndex++);

      while (result == null) {
        if (readingMediaType) {
          if (nextChar == -1) {
            if (mediaTypeBuffer.length() > 0) {
              // End of metadata section
              // No parameters detected
              result = createContentType(mediaTypeBuffer, null);
              paramNameBuffer = new StringBuilder();
            } else {
              // Ignore empty metadata name
            }
          } else if (nextChar == ';') {
            if (mediaTypeBuffer.length() > 0) {
              // End of mediaType section
              // Parameters detected
              readingMediaType = false;
              readingParamName = true;
              paramNameBuffer = new StringBuilder();
              // [ifndef gwt] instruction
              parameters = new Series<Parameter>(Parameter.class);
              // [ifdef gwt] instruction uncomment
              // parameters = new
              // org.restlet.engine.util.ParameterSeries();
            } else {
              throw new IOException("Empty mediaType name detected.");
            }
          } else if (HeaderUtils.isSpace(nextChar)) {
            // Ignore spaces
          } else if (HeaderUtils.isText(nextChar)) {
            mediaTypeBuffer.append((char) nextChar);
          } else {
            throw new IOException(
                "The " + (char) nextChar + " character isn't allowed in a media type name.");
          }
        } else if (readingParamName) {
          if (nextChar == '=') {
            if (paramNameBuffer.length() > 0) {
              // End of parameter name section
              readingParamName = false;
              readingParamValue = true;
              paramValueBuffer = new StringBuilder();
            } else {
              throw new IOException("Empty parameter name detected.");
            }
          } else if (nextChar == -1) {
            if (paramNameBuffer.length() > 0) {
              // End of parameters section
              parameters.add(Parameter.create(paramNameBuffer, null));
              result = createContentType(mediaTypeBuffer, parameters);
            } else if (paramNameBuffer.length() == 0) {
              result = createContentType(mediaTypeBuffer, parameters);
            } else {
              throw new IOException("Empty parameter name detected.");
            }
          } else if (nextChar == ';') {
            // End of parameter
            parameters.add(Parameter.create(paramNameBuffer, null));
            paramNameBuffer = new StringBuilder();
            readingParamName = true;
            readingParamValue = false;
          } else if (HeaderUtils.isSpace(nextChar) && (paramNameBuffer.length() == 0)) {
            // Ignore white spaces
          } else if (HeaderUtils.isTokenChar(nextChar)) {
            paramNameBuffer.append((char) nextChar);
          } else {
            throw new IOException(
                "The \""
                    + (char) nextChar
                    + "\" character isn't allowed in a media type parameter name.");
          }
        } else if (readingParamValue) {
          if (nextChar == -1) {
            if (paramValueBuffer.length() > 0) {
              // End of parameters section
              parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer));
              result = createContentType(mediaTypeBuffer, parameters);
            } else {
              throw new IOException("Empty parameter value detected");
            }
          } else if (nextChar == ';') {
            // End of parameter
            parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer));
            paramNameBuffer = new StringBuilder();
            readingParamName = true;
            readingParamValue = false;
          } else if ((nextChar == '"') && (paramValueBuffer.length() == 0)) {
            // Parse the quoted string
            boolean done = false;
            boolean quotedPair = false;

            while ((!done) && (nextChar != -1)) {
              nextChar = (nextIndex < nextValue.length()) ? nextValue.charAt(nextIndex++) : -1;

              if (quotedPair) {
                // End of quoted pair (escape sequence)
                if (HeaderUtils.isText(nextChar)) {
                  paramValueBuffer.append((char) nextChar);
                  quotedPair = false;
                } else {
                  throw new IOException(
                      "Invalid character \""
                          + (char) nextChar
                          + "\" detected in quoted string. Please check your value");
                }
              } else if (HeaderUtils.isDoubleQuote(nextChar)) {
                // End of quoted string
                done = true;
              } else if (nextChar == '\\') {
                // Begin of quoted pair (escape sequence)
                quotedPair = true;
              } else if (HeaderUtils.isText(nextChar)) {
                paramValueBuffer.append((char) nextChar);
              } else {
                throw new IOException(
                    "Invalid character \""
                        + (char) nextChar
                        + "\" detected in quoted string. Please check your value");
              }
            }
          } else if (HeaderUtils.isTokenChar(nextChar)) {
            paramValueBuffer.append((char) nextChar);
          } else {
            throw new IOException(
                "The \""
                    + (char) nextChar
                    + "\" character isn't allowed in a media type parameter value.");
          }
        }

        nextChar = (nextIndex < nextValue.length()) ? nextValue.charAt(nextIndex++) : -1;
      }
    }

    return result;
  }
  @Override
  public ContactInfo readValue() throws IOException {
    ContactInfo result = null;

    skipSpaces();
    if (peek() != -1) {
      result = new ContactInfo();
      if (peek() == '"') {
        result.setDisplayName(readQuotedString());
        skipSpaces();
        result.setReference(new Reference(readReference()));
      } else if (peek() == '<') {
        result.setReference(new Reference(readReference()));
      } else if (HeaderUtils.isTokenChar(peek())) {
        // Read value until end or value or parameter separator
        StringBuilder sb = null;
        int next = read();

        while ((next != -1) && !isComma(next) && !isSemiColon(next)) {
          if (sb == null) {
            sb = new StringBuilder();
          }

          sb.append((char) next);
          next = read();
        }

        // Remove trailing spaces
        if (sb != null) {
          for (int i = sb.length() - 1; (i >= 0) && isLinearWhiteSpace(sb.charAt(i)); i--) {
            sb.deleteCharAt(i);
          }
        }

        // Unread the separator
        if (isComma(next) || isSemiColon(next)) {
          unread();
        }

        // The last token is the reference
        int index = sb.lastIndexOf(" ");
        if (index != -1) {
          if (sb.charAt(index + 1) == '<') {
            if (sb.charAt(sb.length() - 1) == '>') {
              result.setReference(new Reference(sb.substring(index + 2, sb.length() - 1)));
            } else {
              throw new IOException("Unexpected end of reference. Please check your value");
            }
          }
          result.setDisplayName(sb.substring(0, index).trim());
        } else {
          result.setReference(new Reference(sb.toString()));
        }
      }
    }

    // Read address parameters.
    if (skipParameterSeparator()) {
      Parameter param = readParameter();

      while (param != null) {
        if ("q".equals(param.getName())) {
          result.setQuality(PreferenceReader.readQuality(param.getValue()));
        } else if ("expires".equals(param.getName())) {
          result.setExpires(param.getValue());
        } else {
          addParameter(result, param);
        }

        if (skipParameterSeparator()) {
          param = readParameter();
        } else {
          param = null;
        }
      }
    }

    return result;
  }
  /**
   * Search against all repositories using provided parameters. Note there are a few different types
   * of searches you can perform. If you provide the 'q' query parameter, a keyword search will be
   * performed. If you provide the 'g, a, v, p or c' query parameters, a maven coordinate search
   * will be performed. If you provide the 'cn' query parameter, a classname search will be
   * performed. If you provide the 'sha1' query parameter, a checksum search will be performed.
   *
   * @param q provide this param for a keyword search (g, a, v, p, c, cn, sha1 params will be
   *     ignored).
   * @param sha1 provide this param for a checksum search (g, a, v, p, c, cn params will be
   *     ignored).
   * @param cn provide this param for a classname search (g, a, v, p, c params will be ignored).
   * @param g group id to perform a maven search against (can be combined with a, v, p & c params as
   *     well).
   * @param a artifact id to perform a maven search against (can be combined with g, v, p & c params
   *     as well).
   * @param v version to perform a maven search against (can be combined with g, a, p & c params as
   *     well).
   * @param p packaging type to perform a maven search against (can be combined with g, a, v & c
   *     params as well).
   * @param c classifier to perform a maven search against (can be combined with g, a, v & p params
   *     as well).
   * @param from result index to start retrieving results from.
   * @param count number of results to have returned to you.
   * @param repositoryId The repositoryId to which repository search should be narrowed. Omit if
   *     search should be global.
   */
  @Override
  @GET
  @ResourceMethodSignature(
      queryParams = {
        @QueryParam("q"),
        @QueryParam("g"),
        @QueryParam("a"),
        @QueryParam("v"),
        @QueryParam("p"),
        @QueryParam("c"),
        @QueryParam("cn"),
        @QueryParam("sha1"),
        @QueryParam("from"),
        @QueryParam("count"),
        @QueryParam("repositoryId")
      },
      output = SearchResponse.class)
  public Object get(Context context, Request request, Response response, Variant variant)
      throws ResourceException {
    Form form = request.getResourceRef().getQueryAsForm();

    final Map<String, String> terms = new HashMap<String, String>();

    for (Parameter parameter : form) {
      terms.put(parameter.getName(), parameter.getValue());
    }

    Integer from = null;
    Boolean exact = null;
    String repositoryId = null;
    Boolean expandVersion = Boolean.FALSE;
    Boolean collapseResults = Boolean.FALSE;

    if (form.getFirstValue("from") != null) {
      try {
        from = Integer.valueOf(form.getFirstValue("from"));
      } catch (NumberFormatException e) {
        from = null;
      }
    }

    int count = LUCENE_HIT_LIMIT;
    if (form.getFirstValue("count") != null) {
      try {
        // capping the possible count
        count = Math.min(LUCENE_HIT_LIMIT, Integer.valueOf(form.getFirstValue("count")));
      } catch (NumberFormatException e) {
        count = LUCENE_HIT_LIMIT;
      }
    }

    if (form.getFirstValue("repositoryId") != null) {
      repositoryId = form.getFirstValue("repositoryId");
    }

    if (form.getFirstValue("exact") != null) {
      exact = Boolean.valueOf(form.getFirstValue("exact"));
    }

    if (form.getFirstValue("versionexpand") != null) {
      expandVersion = Boolean.valueOf(form.getFirstValue("versionexpand"));
    }
    if (form.getFirstValue("collapseresults") != null) {
      collapseResults = Boolean.valueOf(form.getFirstValue("collapseresults"));
    }

    // A little explanation about collapseResults, that might seems little bit awkward, since
    // currently we have only
    // one column "collapsable" (the version), but before and maybe in the future that's not the
    // case. So, here is
    // it:
    // the "collapseResults" is just a flag "do we allow collapse at all". It is just a shorthand to
    // turn on or off
    // collapse generally
    // Let's assume we have columns colA, colB and colC collapsable. So, instead saying
    // expandColA=true,expandColB=true,expandColC=true,
    // it is just easy to say collapseresults=false
    // BUT, if collapseresults=true is sent by client, even then an "override" will happen if there
    // is actually NO
    // ROW to collapse!
    // So: collapseresults=false EQUALS-TO expandColA=true & expandColB=true & expandColC=true
    if (collapseResults) {
      // here we would like to have ANDed all the collapsable column flags and negated the result
      // currently we
      collapseResults = !(true && expandVersion); // && expandColA && expandColB;
    }

    IteratorSearchResponse searchResult = null;

    SearchNGResponse result = new SearchNGResponse();

    int runCount = 0;

    while (runCount < RETRIES) {
      try {
        List<ArtifactInfoFilter> filters = new ArrayList<ArtifactInfoFilter>();

        // we need to save this reference to later
        SystemWideLatestVersionCollector systemWideCollector =
            new SystemWideLatestVersionCollector();
        filters.add(systemWideCollector);

        RepositoryWideLatestVersionCollector repositoryWideCollector = null;

        if (collapseResults) {
          repositoryWideCollector = new RepositoryWideLatestVersionCollector();
          filters.add(repositoryWideCollector);
        }

        try {
          searchResult =
              searchByTerms(
                  terms,
                  repositoryId,
                  from,
                  count,
                  exact,
                  expandVersion,
                  collapseResults,
                  filters,
                  searchers);

          if (searchResult == null) {
            collapseResults = false;

            continue;
          } else {
            repackIteratorSearchResponse(
                request,
                terms,
                result,
                collapseResults,
                from,
                count,
                searchResult,
                systemWideCollector,
                repositoryWideCollector);

            if (!result.isTooManyResults()) {
              // if we had collapseResults ON, and the totalHits are larger than actual (filtered)
              // results, and the actual result count is below COLLAPSE_OVERRIDE_TRESHOLD,
              // and full result set is smaller than HIT_LIMIT
              // then repeat without collapse
              if (collapseResults
                  && result.getData().size() < searchResult.getTotalHitsCount()
                  && result.getData().size() < COLLAPSE_OVERRIDE_TRESHOLD
                  && searchResult.getTotalHitsCount() < GA_HIT_LIMIT) {
                collapseResults = false;

                continue;
              }
            }
          }
        } catch (IOException e) {
          throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e.getMessage(), e);
        }

        // we came here, so we break the while-loop, we got what we need
        break;
      } catch (NoSuchRepositoryException e) {
        throw new ResourceException(
            Status.CLIENT_ERROR_BAD_REQUEST, "Repository to be searched does not exists!", e);
      } catch (AlreadyClosedException e) {
        runCount++;

        getLogger()
            .info(
                "NexusIndexer issue (NEXUS-3702), we got AlreadyClosedException that happens when Reindexing or other \"indexer intensive\" task is running on instance while searching! Redoing search again.");

        if (getLogger().isDebugEnabled()) {
          // just keep it silent (DEBUG)
          getLogger().debug("Got AlreadyClosedException exception!", e);
        }

        result.setData(null);
      }
    }

    if (result.getData() == null) {
      try {
        repackIteratorSearchResponse(
            request,
            terms,
            result,
            collapseResults,
            from,
            count,
            IteratorSearchResponse.empty(null),
            null,
            null);
      } catch (NoSuchRepositoryException e) {
        // will not happen
      } catch (IOException e) {
        // will not happen
      }

      getLogger()
          .info(
              "Nexus issue (NEXUS-3702): Was unable to perform search "
                  + RETRIES
                  + " times, giving up, and lying about TooManyResults. Please retry to reproduce this with DEBUG logs and report this issue!");
    }

    return result;
  }