/*
* PrincipalParser.java February 2001
*
* Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.simpleframework.http.parse;
import org.simpleframework.http.Principal;
import org.simpleframework.util.parse.ParseBuffer;
import org.simpleframework.util.parse.Parser;
/**
* PrincipalParser is a parser class for the HTTP basic authorization header. It
* decodes the <code>base64</code> encoding of the user and password pair.
* <p>
* This follows the parsing tree of RFC 2617. The goal of this parser is to
* decode the <code>base64</code> encoding of the user name and password. After
* the string has been decoded then the user name and password are extracted.
* This will only parse headers that are from the <code>Basic</code>
* authorization scheme. The format of the basic scheme can be found in RFC 2617
* and is of the form
*
* <pre>
* Basic SP base64-encoding.
* </pre>
*
* @author Niall Gallagher
*/
public class PrincipalParser extends Parser implements Principal {
/**
* Keeps the characters consumed for the password token.
*/
private ParseBuffer password;
/**
* Keeps the characters consumed for the user name token.
*/
private ParseBuffer user;
/**
* Keeps the <code>bytes</code> used for decoding base64.
*/
private byte[] four;
/**
* Tracks the write offset for the buffer.
*/
private int write;
/**
* Tracks the ready offset for the four buffer.
*/
private int ready;
/**
* Tracks the read offset for the buffer.
*/
private int read;
/**
* Creates a <code>Parser</code> for the basic authorization scheme. This
* allows headers that are of this scheme to be broken into its component
* parts i.e. user name and password.
*/
public PrincipalParser() {
this.password = new ParseBuffer();
this.user = new ParseBuffer();
this.four = new byte[4];
}
/**
* Creates a <code>Parser</code> for the basic authorization scheme. This
* allows headers that are of this scheme to be broken into its component
* parts i.e. user name and password. This constructor will parse the
* <code>String</code> given as the header.
*
* @param header
* this is a header value from the basic scheme
*/
public PrincipalParser(String header) {
this();
this.parse(header);
}
/**
* Gets the users password parsed from the Authorization header value. If
* there was not password parsed from the base64 value of the header this
* returns <code>null</code>
*
* @return the password for the user or <code>null</code>
*/
@Override
public String getPassword() {
if (this.password.length() == 0) return null;
return this.password.toString();
}
/**
* Gets the users name from the Authorization header value. This will return
* <code>null</code> if there is no user name extracted from the base64
* header value.
*
* @return this returns the name of the user
*/
@Override
public String getName() {
if (this.user.length() == 0) return null;
return this.user.toString();
}
/**
* Used to parse the actual header data. This will attempt to read the
* "Basic" token from the set of characters given, if this is successful
* then the username and password is extracted.
*/
@Override
protected void parse() {
if (this.skip("Basic ")) {
this.decode();
this.userpass();
}
}
/**
* This will initialize the <code>Parser</code> when it is ready to parse a
* new <code>String</code>. This will reset the <code>Parser</code> to a
* ready state. The <code>init</code> method is invoked by the
* <code>Parser</code> when the <code>parse</code> method is invoked.
*/
@Override
protected void init() {
this.password.clear();
this.user.clear();
this.write = this.ready = this.read = this.off = 0;
this.pack();
}
/**
* This is used to remove all whitespace characters from the
* <code>String</code> excluding the whitespace within literals. The
* definition of a literal can be found in RFC 2616.
* <p>
* The definition of a literal for RFC 2616 is anything between 2 quotes but
* excuding quotes that are prefixed with the backward slash character.
*/
private void pack() {
int len = this.count;
int seek = 0; /* read */
int pos = 0; /* write */
char ch = 0;
while (seek < len) { /* trim start */
if (!this.space(this.buf[seek])) {
break;
}
seek++;
}
while (seek < len) {
ch = this.buf[seek++];
if (this.space(ch)) {
while (seek < len) { /* skip spaces */
if (!this.space(this.buf[seek])) {
break;
}
seek++;
}
}
this.buf[pos++] = ch;
}
if (this.space(ch)) { /* trim end */
pos--;
}
this.count = pos;
}
/**
* Extracts the name and password of the user from the
* <code>name : password</code> pair that was given. This will take all data
* up to the first occurence of a ':' character as the users name and all
* data after the colon as the users password.
*/
private void userpass() {
this.userid();
this.off++;
this.password();
}
/**
* Extracts the user name from the buffer. This will read up to the first
* occurence of a colon, ':', character as the user name. For the BNF syntax
* of this see RFC 2617.
*/
private void userid() {
while (this.off < this.count) {
char ch = this.buf[this.off];
if (!this.text(ch) || (ch == ':')) {
break;
}
this.user.append(ch);
this.off++;
}
}
/**
* Extracts the password from the buffer. This will all characters from the
* current offset to the first non text character as the password. For the
* BNF syntax of this see RFC 2617.
*/
private void password() {
while (this.off < this.count) {
char ch = this.buf[this.off];
if (!this.text(ch)) {
break;
}
this.password.append(ch);
this.off++;
}
}
/**
* This is used to remove decode the <code>base64</code> encoding of the
* user name and password. This uses a standart <code>base64</code> decoding
* scheme.
* <p>
* For information on the decoding scheme used for <code>base64</code> see
* the RFC 2045 on MIME, Multipurpose Internet Mail Extensions.
*/
private void decode() {
for (this.write = this.read = this.off; (this.read + 3) < this.count;) {
while (this.ready < 4) {
int ch = this.translate(this.buf[this.read++]);
if (ch >= 0) {
this.four[this.ready++] = (byte) ch;
}
}
if (this.four[2] == 65) {
this.buf[this.write++] = this.first(this.four);
break;
} else if (this.four[3] == 65) {
this.buf[this.write++] = this.first(this.four);
this.buf[this.write++] = this.second(this.four);
break;
} else {
this.buf[this.write++] = this.first(this.four);
this.buf[this.write++] = this.second(this.four);
this.buf[this.write++] = this.third(this.four);
}
this.ready = 0;
}
this.count = this.write;
}
/**
* This uses a basic translation from the <code>byte</code> character to the
* <code>byte</code> number.
* <p>
* The table for translation the data can be found in RFC 2045 on MIME,
* Multipurpose Internet Mail Extensions.
*
* @param octet
* this is the octet ttat is to be translated
*
* @return this returns the translated octet
*/
private int translate(int octet) {
if ((octet >= 'A') && (octet <= 'Z')) {
octet = octet - 'A';
} else if ((octet >= 'a') && (octet <= 'z')) {
octet = (octet - 'a') + 26;
} else if ((octet >= '0') && (octet <= '9')) {
octet = (octet - '0') + 52;
} else if (octet == '+') {
octet = 62;
} else if (octet == '/') {
octet = 63;
} else if (octet == '=') {
octet = 65;
} else {
octet = -1;
}
return octet;
}
/**
* This is used to extract the <code>byte</code> from the set of four
* <code>bytes</code> given. This method is used to isolate the correct bits
* that corrospond to an actual character withing the <code>base64</code>
* data.
*
* @param four
* this is the four <code>bytes</code> that the character is to
* be extracted from
*
* @return this returns the character extracted
*/
private char first(byte[] four) {
return (char) (((four[0] & 0x3f) << 2) | ((four[1] & 0x30) >>> 4));
}
/**
* This is used to extract the <code>byte</code> from the set of four
* <code>bytes</code> given. This method is used to isolate the correct bits
* that corrospond to an actual character withing the <code>base64</code>
* data.
*
* @param four
* this is the four <code>bytes</code> that the character is to
* be extracted from
*
* @return this returns the character extracted
*/
private char second(byte[] four) {
return (char) (((four[1] & 0x0f) << 4) | ((four[2] & 0x3c) >>> 2));
}
/**
* This is used to extract the <code>byte</code> from the set of four
* <code>bytes</code> given. This method is used to isolate the correct bits
* that corrospond to an actual character withing the <code>base64</code>
* data.
*
* @param four
* this is the four <code>bytes</code> that the character is to
* be extracted from
*
* @return this returns the character extracted
*/
private char third(byte[] four) {
return (char) (((four[2] & 0x03) << 6) | (four[3] & 0x3f));
}
/**
* This is used to determine wheather or not a character is a
* <code>TEXT</code> character according to the HTTP specification, that is
* RFC 2616 specifies a <code>TEXT</code> character as one that is any octet
* except those less than 32 and not 127.
*
* @param c
* this is the character that is to be determined
*
* @return this returns true if the character is a <code>TEXT</code>
*/
private boolean text(char c) {
return (c > 31) && (c != 127) && (c <= 0xffff);
}
}