/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
/*
* 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 divconq.www.http.parse;
import divconq.www.http.Principal;
import divconq.www.util.parse.ParseBuffer;
import divconq.www.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();
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>
*/
public String getPassword(){
if(password.length() == 0){
return null;
}
return 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
*/
public String getName(){
if(user.length() == 0){
return null;
}
return 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.
*/
protected void parse(){
if(skip("Basic ")){
decode();
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.
*/
protected void init() {
password.clear();
user.clear();
write = ready =
read = off = 0;
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 = count;
int seek = 0; /* read */
int pos = 0; /* write */
char ch = 0;
while(seek <len){ /* trim start*/
if(!space(buf[seek])){
break;
}
seek++;
}
while(seek < len){
ch = buf[seek++];
if(space(ch)){
while(seek < len){ /* skip spaces */
if(!space(buf[seek])){
break;
}
seek++;
}
}
buf[pos++] = ch;
}
if(space(ch)){ /* trim end */
pos--;
}
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(){
userid();
off++;
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(off < count){
char ch = buf[off];
if(!text(ch) || ch ==':'){
break;
}
user.append(ch);
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(off < count){
char ch = buf[off];
if(!text(ch)){
break;
}
password.append(ch);
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(write = read = off; read + 3 < count;) {
while(ready < 4) {
int ch = translate(buf[read++]);
if(ch >= 0) {
four[ready++] = (byte)ch;
}
}
if(four[2] == 65) {
buf[write++] = first(four);
break;
} else if(four[3] == 65) {
buf[write++] = first(four);
buf[write++] = second(four);
break;
} else {
buf[write++] = first(four);
buf[write++] = second(four);
buf[write++] = third(four);
}
ready = 0;
}
count = 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;
}
}