Beispiel #1
0
  /**
   * Read a list of space-separated "flag_extension" sequences and return the list as a array of
   * Strings. An empty list is returned as null. This is an IMAP-ism, and perhaps this method should
   * moved into the IMAP layer.
   */
  public String[] readSimpleList() {
    skipSpaces();

    if (buffer[index] != '(') // not what we expected
    return null;
    index++; // skip '('

    Vector v = new Vector();
    int start;
    for (start = index; buffer[index] != ')'; index++) {
      if (buffer[index] == ' ') { // got one item
        v.addElement(ASCIIUtility.toString(buffer, start, index));
        start = index + 1; // index gets incremented at the top
      }
    }
    if (index > start) // get the last item
    v.addElement(ASCIIUtility.toString(buffer, start, index));
    index++; // skip ')'

    int size = v.size();
    if (size > 0) {
      String[] s = new String[size];
      v.copyInto(s);
      return s;
    } else // empty list
    return null;
  }
Beispiel #2
0
  /**
   * Generic parsing routine that can parse out a Quoted-String, Literal or Atom and return the
   * parsed token as a String or a ByteArray. Errors or NIL data will return null.
   */
  private Object parseString(boolean parseAtoms, boolean returnString) {
    byte b;

    // Skip leading spaces
    skipSpaces();

    b = buffer[index];
    if (b == '"') { // QuotedString
      index++; // skip the quote
      int start = index;
      int copyto = index;

      while ((b = buffer[index]) != '"') {
        if (b == '\\') // skip escaped byte
        index++;
        if (index != copyto) { // only copy if we need to
          // Beware: this is a destructive copy. I'm
          // pretty sure this is OK, but ... ;>
          buffer[copyto] = buffer[index];
        }
        copyto++;
        index++;
      }
      index++; // skip past the terminating quote

      if (returnString) return ASCIIUtility.toString(buffer, start, copyto);
      else return new ByteArray(buffer, start, copyto - start);
    } else if (b == '{') { // Literal
      int start = ++index; // note the start position

      while (buffer[index] != '}') index++;

      int count = 0;
      try {
        count = ASCIIUtility.parseInt(buffer, start, index);
      } catch (NumberFormatException nex) {
        // throw new ParsingException();
        return null;
      }

      start = index + 3; // skip "}\r\n"
      index = start + count; // position index to beyond the literal

      if (returnString) // return as String
      return ASCIIUtility.toString(buffer, start, start + count);
      else return new ByteArray(buffer, start, count);
    } else if (parseAtoms) { // parse as an ATOM
      int start = index; // track this, so that we can use to
      // creating ByteArrayInputStream below.
      String s = readAtom();
      if (returnString) return s;
      else // *very* unlikely
      return new ByteArray(buffer, start, index);
    } else if (b == 'N' || b == 'n') { // the only valid value is 'NIL'
      index += 3; // skip past NIL
      return null;
    }
    return null; // Error
  }
Beispiel #3
0
  /**
   * Extract an ATOM, but stop at the additional delimiter (if not NUL). Used to parse a response
   * code inside [].
   */
  public String readAtom(char delim) {
    skipSpaces();

    if (index >= size) // already at end of response
    return null;

    /*
     * An ATOM is any CHAR delimited by :
     * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\'
     */
    byte b;
    int start = index;
    while (index < size
        && ((b = buffer[index]) > ' ')
        && b != '('
        && b != ')'
        && b != '%'
        && b != '*'
        && b != '"'
        && b != '\\'
        && b != 0x7f
        && (delim == '\0' || b != delim)) index++;

    return ASCIIUtility.toString(buffer, start, index);
  }
Beispiel #4
0
  /**
   * Read a string as an arbitrary sequence of characters, stopping at the delimiter Used to read
   * part of a response code inside [].
   */
  public String readString(char delim) {
    skipSpaces();

    if (index >= size) // already at end of response
    return null;

    int start = index;
    while (index < size && buffer[index] != delim) index++;

    return ASCIIUtility.toString(buffer, start, index);
  }
  /**
   * Read a list of space-separated "flag_extension" sequences and return the list as a array of
   * Strings. An empty list is returned as null. This is an IMAP-ism, and perhaps this method should
   * moved into the IMAP layer.
   */
  public String[] readSimpleList() {
    skipSpaces();

    if (buffer[index] != '(') // not what we expected
    return null;
    index++; // skip '('

    List<String> v = new ArrayList<String>();
    int start;
    for (start = index; buffer[index] != ')'; index++) {
      if (buffer[index] == ' ') { // got one item
        v.add(ASCIIUtility.toString(buffer, start, index));
        start = index + 1; // index gets incremented at the top
      }
    }
    if (index > start) // get the last item
    v.add(ASCIIUtility.toString(buffer, start, index));
    index++; // skip ')'

    int size = v.size();
    if (size > 0) return v.toArray(new String[size]);
    else // empty list
    return null;
  }
Beispiel #6
0
  /**
   * Extract a long number, starting at the current position. Updates the internal index to beyond
   * the number. Returns -1 if a long number was not found.
   *
   * @return a long
   */
  public long readLong() {
    // Skip leading spaces
    skipSpaces();

    int start = index;
    while (index < size && Character.isDigit((char) buffer[index])) index++;

    if (index > start) {
      try {
        return ASCIIUtility.parseLong(buffer, start, index);
      } catch (NumberFormatException nex) {
      }
    }

    return -1;
  }
Beispiel #7
0
 public Response(String s) {
   buffer = ASCIIUtility.getBytes(s);
   size = buffer.length;
   parse();
 }
Beispiel #8
0
 public String toString() {
   return ASCIIUtility.toString(buffer, 0, size);
 }
Beispiel #9
0
 /**
  * Return the rest of the response as a string, usually used to return the arbitrary message text
  * after a NO response.
  */
 public String getRest() {
   skipSpaces();
   return ASCIIUtility.toString(buffer, index, size);
 }
  public boolean authenticate(
      String[] mechs, final String realm, final String authzid, final String u, final String p)
      throws ProtocolException {

    synchronized (pr) { // authenticate method should be synchronized
      List<Response> v = new ArrayList<Response>();
      String tag = null;
      Response r = null;
      boolean done = false;
      if (logger.isLoggable(Level.FINE)) {
        logger.fine("SASL Mechanisms:");
        for (int i = 0; i < mechs.length; i++) logger.fine(" " + mechs[i]);
        logger.fine("");
      }

      SaslClient sc;
      CallbackHandler cbh =
          new CallbackHandler() {
            public void handle(Callback[] callbacks) {
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL callback length: " + callbacks.length);
              for (int i = 0; i < callbacks.length; i++) {
                if (logger.isLoggable(Level.FINE))
                  logger.fine("SASL callback " + i + ": " + callbacks[i]);
                if (callbacks[i] instanceof NameCallback) {
                  NameCallback ncb = (NameCallback) callbacks[i];
                  ncb.setName(u);
                } else if (callbacks[i] instanceof PasswordCallback) {
                  PasswordCallback pcb = (PasswordCallback) callbacks[i];
                  pcb.setPassword(p.toCharArray());
                } else if (callbacks[i] instanceof RealmCallback) {
                  RealmCallback rcb = (RealmCallback) callbacks[i];
                  rcb.setText(realm != null ? realm : rcb.getDefaultText());
                } else if (callbacks[i] instanceof RealmChoiceCallback) {
                  RealmChoiceCallback rcb = (RealmChoiceCallback) callbacks[i];
                  if (realm == null) rcb.setSelectedIndex(rcb.getDefaultChoice());
                  else {
                    // need to find specified realm in list
                    String[] choices = rcb.getChoices();
                    for (int k = 0; k < choices.length; k++) {
                      if (choices[k].equals(realm)) {
                        rcb.setSelectedIndex(k);
                        break;
                      }
                    }
                  }
                }
              }
            }
          };

      try {
        sc = Sasl.createSaslClient(mechs, authzid, name, host, (Map) props, cbh);
      } catch (SaslException sex) {
        logger.log(Level.FINE, "Failed to create SASL client", sex);
        throw new UnsupportedOperationException(sex.getMessage(), sex);
      }
      if (sc == null) {
        logger.fine("No SASL support");
        throw new UnsupportedOperationException("No SASL support");
      }
      if (logger.isLoggable(Level.FINE)) logger.fine("SASL client " + sc.getMechanismName());

      try {
        Argument args = new Argument();
        args.writeAtom(sc.getMechanismName());
        if (pr.hasCapability("SASL-IR") && sc.hasInitialResponse()) {
          String irs;
          byte[] ba = sc.evaluateChallenge(new byte[0]);
          if (ba.length > 0) {
            ba = BASE64EncoderStream.encode(ba);
            irs = ASCIIUtility.toString(ba, 0, ba.length);
          } else irs = "=";
          args.writeAtom(irs);
        }
        tag = pr.writeCommand("AUTHENTICATE", args);
      } catch (Exception ex) {
        logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex);
        return false;
      }

      OutputStream os = pr.getIMAPOutputStream(); // stream to IMAP server

      /*
       * Wrap a BASE64Encoder around a ByteArrayOutputstream
       * to craft b64 encoded username and password strings
       *
       * Note that the encoded bytes should be sent "as-is" to the
       * server, *not* as literals or quoted-strings.
       *
       * Also note that unlike the B64 definition in MIME, CRLFs
       * should *not* be inserted during the encoding process. So, I
       * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine,
       * which should be sufficiently large !
       */

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      byte[] CRLF = {(byte) '\r', (byte) '\n'};

      // Hack for Novell GroupWise XGWTRUSTEDAPP authentication mechanism
      // http://www.novell.com/developer/documentation/gwimap/?
      //   page=/developer/documentation/gwimap/gwimpenu/data/al7te9j.html
      boolean isXGWTRUSTEDAPP =
          sc.getMechanismName().equals("XGWTRUSTEDAPP")
              && PropUtil.getBooleanProperty(
                  props, "mail." + name + ".sasl.xgwtrustedapphack.enable", true);
      while (!done) { // loop till we are done
        try {
          r = pr.readResponse();
          if (r.isContinuation()) {
            byte[] ba = null;
            if (!sc.isComplete()) {
              ba = r.readByteArray().getNewBytes();
              if (ba.length > 0) ba = BASE64DecoderStream.decode(ba);
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL challenge: " + ASCIIUtility.toString(ba, 0, ba.length) + " :");
              ba = sc.evaluateChallenge(ba);
            }
            if (ba == null) {
              logger.fine("SASL no response");
              os.write(CRLF); // write out empty line
              os.flush(); // flush the stream
              bos.reset(); // reset buffer
            } else {
              if (logger.isLoggable(Level.FINE))
                logger.fine("SASL response: " + ASCIIUtility.toString(ba, 0, ba.length) + " :");
              ba = BASE64EncoderStream.encode(ba);
              if (isXGWTRUSTEDAPP) bos.write(ASCIIUtility.getBytes("XGWTRUSTEDAPP "));
              bos.write(ba);

              bos.write(CRLF); // CRLF termination
              os.write(bos.toByteArray()); // write out line
              os.flush(); // flush the stream
              bos.reset(); // reset buffer
            }
          } else if (r.isTagged() && r.getTag().equals(tag))
            // Ah, our tagged response
            done = true;
          else if (r.isBYE()) // outta here
          done = true;
          else // hmm .. unsolicited response here ?!
          v.add(r);
        } catch (Exception ioex) {
          logger.log(Level.FINE, "SASL Exception", ioex);
          // convert this into a BYE response
          r = Response.byeResponse(ioex);
          done = true;
          // XXX - ultimately return true???
        }
      }

      if (sc.isComplete() /*&& res.status == SUCCESS*/) {
        String qop = (String) sc.getNegotiatedProperty(Sasl.QOP);
        if (qop != null
            && (qop.equalsIgnoreCase("auth-int") || qop.equalsIgnoreCase("auth-conf"))) {
          // XXX - NOT SUPPORTED!!!
          logger.fine("SASL Mechanism requires integrity or confidentiality");
          return false;
        }
      }

      /* Dispatch untagged responses.
       * NOTE: in our current upper level IMAP classes, we add the
       * responseHandler to the Protocol object only *after* the
       * connection has been authenticated. So, for now, the below
       * code really ends up being just a no-op.
       */
      Response[] responses = v.toArray(new Response[v.size()]);
      pr.notifyResponseHandlers(responses);

      // Handle the final OK, NO, BAD or BYE response
      pr.handleResult(r);
      pr.setCapabilities(r);

      /*
       * If we're using the Novell Groupwise XGWTRUSTEDAPP mechanism
       * to run as a specified authorization ID, we have to issue a
       * LOGIN command to select the user we want to operate as.
       */
      if (isXGWTRUSTEDAPP && authzid != null) {
        Argument args = new Argument();
        args.writeString(authzid);

        responses = pr.command("LOGIN", args);

        // dispatch untagged responses
        pr.notifyResponseHandlers(responses);

        // Handle result of this command
        pr.handleResult(responses[responses.length - 1]);
        // If the response includes a CAPABILITY response code, process it
        pr.setCapabilities(responses[responses.length - 1]);
      }
      return true;
    }
  }
 /** Return the next 20 characters in the buffer, for exception messages. */
 private String next20() {
   if (index + 20 > size) return ASCIIUtility.toString(buffer, index, index + size);
   else return ASCIIUtility.toString(buffer, index, index + 20) + "...";
 }