/* * PrincipalParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.common.parse.ParseBuffer; import org.simpleframework.common.parse.Parser; import org.simpleframework.http.Principal; /** * PrincipalParser is a parser class for the HTTP basic authorization * header. It decodes the base64 encoding of the user and * password pair. *

* This follows the parsing tree of RFC 2617. The goal of this parser * is to decode the base64 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 Basic authorization scheme. The format of the basic * scheme can be found in RFC 2617 and is of the form *

 *  Basic SP base64-encoding.
 * 
* * @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 bytes 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 Parser 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 Parser 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 String 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 null * * @return the password for the user or null */ public String getPassword(){ if(password.length() == 0){ return null; } return password.toString(); } /** * Gets the users name from the Authorization header value. * This will return null 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 Parser when it is ready * to parse a new String. This will reset the * Parser to a ready state. The init method * is invoked by the Parser when the parse * 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 * String excluding the whitespace within literals. * The definition of a literal can be found in RFC 2616. *

* 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 name : password 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 base64 encoding of * the user name and password. This uses a standart base64 * decoding scheme. *

* For information on the decoding scheme used for base64 * 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 byte character to the * byte number. *

* 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 * TEXT character according to the HTTP specification, * that is RFC 2616 specifies a TEXT 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 TEXT */ private boolean text(char c){ return c > 31 && c != 127 && c <= 0xffff; } }