mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
970 lines
23 KiB
Java
970 lines
23 KiB
Java
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (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.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Grendel mail/news client.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1997 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Created: Eric Bina <ebina@netscape.com>, 30 Oct 1997.
|
|
*/
|
|
|
|
package grendel.storage.addressparser;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
|
|
/*
|
|
* Parse a comma separated list of RFC822 addresses.
|
|
* The important thing here is all the ERROR cases.
|
|
* If you aren't rigidly RFC822 compliant, you will hit
|
|
* one of them.
|
|
*/
|
|
class RFC822AddressParser
|
|
{
|
|
static final int NO_ERROR = 0;
|
|
static final int LIST_OF_ADDRESSES = -1;
|
|
static final int NO_DOMAIN = -2;
|
|
static final int NO_VECTOR = -3;
|
|
static final int COMMENTS_IN_VECTOR = -4;
|
|
static final int EXTRA_TOKENS_AT_START = -5;
|
|
static final int NO_ADDR_SPEC = -6;
|
|
static final int NO_ROUTE_ADDR = -7;
|
|
static final int BAD_ROUTE = -8;
|
|
static final int BAD_ROUTE_DOMAIN = -9;
|
|
static final int EXTRA_TOKENS_IN_ROUTE_ADDR = -10;
|
|
static final int NO_PHRASE = -11;
|
|
static final int EXTRA_TOKENS_IN_MAILBOX_LIST = -12;
|
|
static final int NO_LOCAL_PART = -13;
|
|
static final int BAD_GROUP = -14;
|
|
static final int ILLEGAL_SPECIAL = -15;
|
|
|
|
private Vector address_list;
|
|
private Vector tokens;
|
|
private Vector all_tokens;
|
|
private int error_code;
|
|
|
|
/*
|
|
* This is the complete vector of tokens
|
|
* straight from the tokenizer.
|
|
*/
|
|
public RFC822AddressParser(Vector tokens) throws RFC822ParserException
|
|
{
|
|
AddressToken addr_token = null;
|
|
int num;
|
|
int start, end;
|
|
|
|
this.error_code = NO_ERROR;
|
|
this.all_tokens = tokens;
|
|
this.address_list = new Vector();
|
|
this.tokens = stripComments(tokens);
|
|
|
|
num = this.tokens.size();
|
|
|
|
if (num == 0)
|
|
{
|
|
// ERROR: zero length input vector.
|
|
this.error_code = NO_VECTOR;
|
|
return;
|
|
}
|
|
|
|
start = 0;
|
|
end = num - 1;
|
|
/*
|
|
* Arbitrary commas can start and end this list
|
|
*/
|
|
start = skipForwardCommas(start, end);
|
|
end = skipCommas(end);
|
|
if (start > end)
|
|
{
|
|
// ERROR: zero length input vector.
|
|
this.error_code = NO_VECTOR;
|
|
return;
|
|
}
|
|
|
|
addr_token = collectAddress(start, end);
|
|
if (addr_token == null)
|
|
{
|
|
return;
|
|
}
|
|
addMailAddress(addr_token);
|
|
end = addr_token.getTokenStart() - 1;
|
|
while (end >= start)
|
|
{
|
|
RFC822Token token;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(end);
|
|
|
|
/*
|
|
* Skip the comma separator if found, but
|
|
* don't require it.
|
|
*/
|
|
if (token.isSpecialChar(',') == true)
|
|
{
|
|
end = skipCommas(end);
|
|
}
|
|
|
|
addr_token = collectAddress(start, end);
|
|
if (addr_token == null)
|
|
{
|
|
return;
|
|
}
|
|
addMailAddress(addr_token);
|
|
end = addr_token.getTokenStart() - 1;
|
|
}
|
|
if (addr_token.getTokenStart() != start)
|
|
{
|
|
// ERROR: All tokens in input vector not part
|
|
// of a legal address list.
|
|
this.error_code = EXTRA_TOKENS_AT_START;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
public Vector getTokenList()
|
|
{
|
|
return(this.tokens);
|
|
}
|
|
|
|
|
|
public boolean isError()
|
|
{
|
|
if (this.error_code != NO_ERROR)
|
|
{
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
|
|
public String getErrorString()
|
|
{
|
|
String err;
|
|
|
|
switch(this.error_code)
|
|
{
|
|
case NO_ERROR:
|
|
err = new String("No error.");
|
|
break;
|
|
case LIST_OF_ADDRESSES:
|
|
err = new String("Cannot handle non-comma separated list of addresses!");
|
|
break;
|
|
case NO_DOMAIN:
|
|
err = new String("No valid domain.");
|
|
break;
|
|
case NO_VECTOR:
|
|
err = new String("No input vector.");
|
|
break;
|
|
case COMMENTS_IN_VECTOR:
|
|
err = new String("Comment tokens in input vector.");
|
|
break;
|
|
case EXTRA_TOKENS_AT_START:
|
|
err = new String("Extra tokens at the start of the input vector after parsing address list.");
|
|
break;
|
|
case NO_ADDR_SPEC:
|
|
err = new String("No valid address specification.");
|
|
break;
|
|
case NO_ROUTE_ADDR:
|
|
err = new String("No valid route address.");
|
|
break;
|
|
case BAD_ROUTE:
|
|
err = new String("Bad route format.");
|
|
break;
|
|
case BAD_ROUTE_DOMAIN:
|
|
err = new String("Bad domain in a route list.");
|
|
break;
|
|
case EXTRA_TOKENS_IN_ROUTE_ADDR:
|
|
err = new String("Extra tokens within the bounds of the route address.");
|
|
break;
|
|
case NO_PHRASE:
|
|
err = new String("No valid phrase.");
|
|
break;
|
|
case EXTRA_TOKENS_IN_MAILBOX_LIST:
|
|
err = new String("Extra tokens within the bounds of the mailbox list.");
|
|
break;
|
|
case NO_LOCAL_PART:
|
|
err = new String("No valid local part.");
|
|
break;
|
|
case BAD_GROUP:
|
|
err = new String("Bad group format.");
|
|
break;
|
|
case ILLEGAL_SPECIAL:
|
|
err = new String("Found a special character where another token type was expected.");
|
|
break;
|
|
default:
|
|
err = new String("");
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
|
|
public Vector getAddressList()
|
|
{
|
|
return(this.address_list);
|
|
}
|
|
|
|
|
|
/***********************
|
|
***********************
|
|
** PRIVATE METHODS **
|
|
***********************
|
|
***********************/
|
|
|
|
|
|
/*
|
|
* create a new vector from the passed vector with all
|
|
* the comments stripped from it.
|
|
*/
|
|
private Vector stripComments(Vector tokens)
|
|
{
|
|
Vector new_vec = new Vector();
|
|
int num = tokens.size();
|
|
|
|
for (int i=0; i < num; i++)
|
|
{
|
|
RFC822Token token = (RFC822Token)tokens.elementAt(i);
|
|
if (token.getType() != RFC822Token.COMMENT)
|
|
{
|
|
new_vec.addElement(token);
|
|
}
|
|
}
|
|
return(new_vec);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an address (either group or mailbox) to the
|
|
* beginning of an address list vector.
|
|
*/
|
|
private void addMailAddress(AddressToken addr_token)
|
|
{
|
|
MailAddressToken address;
|
|
|
|
if (addr_token.getType() == AddressToken.GROUP)
|
|
{
|
|
address = new MailAddressToken((GroupToken)addr_token);
|
|
}
|
|
else
|
|
{
|
|
address = new MailAddressToken((MailboxToken)addr_token);
|
|
}
|
|
this.address_list.insertElementAt((Object)address, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Collect an address. Starts from a rightmost
|
|
* token and works left.
|
|
* In all cases where we return null we set an error code
|
|
*/
|
|
private AddressToken collectAddress(int start, int end) throws
|
|
RFC822ParserException
|
|
{
|
|
MailboxToken mailbox = null;
|
|
AddressToken addr_token = null;
|
|
RFC822Token token;
|
|
int type;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(end);
|
|
type = token.getType();
|
|
if (type == RFC822Token.SPECIAL_CHAR)
|
|
{
|
|
/*
|
|
* A '>' must be the right edge of a
|
|
* route-addr which must be a mailbox
|
|
* made up of a phrase route-addr pair.
|
|
*/
|
|
if (token.isSpecialChar('>') == true)
|
|
{
|
|
mailbox = collectMailbox(end);
|
|
if (mailbox == null)
|
|
{
|
|
return(null);
|
|
}
|
|
return((AddressToken)mailbox);
|
|
}
|
|
/*
|
|
* A ';' must be the right edge of a
|
|
* group which must have a ':' separating
|
|
* a phrase from zero or more mailboxes
|
|
* in a comma separated list.
|
|
*/
|
|
else if (token.isSpecialChar(';') == true)
|
|
{
|
|
GroupToken group;
|
|
|
|
group = collectGroup(end);
|
|
if (group == null)
|
|
{
|
|
return(null);
|
|
}
|
|
return((AddressToken)group);
|
|
}
|
|
/*
|
|
* No other special character can terminate a
|
|
* legal address.
|
|
*/
|
|
else
|
|
{
|
|
this.error_code = ILLEGAL_SPECIAL;
|
|
return(null);
|
|
}
|
|
}
|
|
else if (type == RFC822Token.COMMENT)
|
|
{
|
|
// ERROR: No comments in address vector.
|
|
this.error_code = COMMENTS_IN_VECTOR;
|
|
return(null);
|
|
}
|
|
else
|
|
{
|
|
addr_token = collectAddrSpec(end);
|
|
// if returns null error code already set.
|
|
if (addr_token == null)
|
|
{
|
|
return(null);
|
|
}
|
|
mailbox = new MailboxToken(addr_token);
|
|
return((AddressToken)mailbox);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Collect a group having been passed the rightmost ';'.
|
|
* In all cases where we return null we set an error code.
|
|
*/
|
|
private GroupToken collectGroup(int indx2) throws RFC822ParserException
|
|
{
|
|
GroupToken group = null;
|
|
Vector mailboxes = new Vector();
|
|
AddressToken phrase;
|
|
RFC822Token token;
|
|
int colon_at;
|
|
int indx;
|
|
|
|
/*
|
|
* A ';' must be the right edge of a group which
|
|
* must have a ':' separating a phrase from zero or
|
|
* more mailboxes in a comma separated list.
|
|
*/
|
|
token = (RFC822Token)this.tokens.elementAt(indx2);
|
|
|
|
if (token.isSpecialChar(';') == false)
|
|
{
|
|
// ERROR: group must be terminated
|
|
// by a ';'.
|
|
this.error_code = BAD_GROUP;
|
|
return(null);
|
|
}
|
|
|
|
/*
|
|
* Stupid RFC allows extra commas here at the end
|
|
* of the list.
|
|
*/
|
|
indx = skipCommas(indx2 - 1);
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
|
|
/*
|
|
* We have one or more mailboxes in a comma
|
|
* separated list.
|
|
*/
|
|
if (token.isSpecialChar(':') == false)
|
|
{
|
|
MailboxToken tmp_mbox;
|
|
RFC822Token comma;
|
|
|
|
tmp_mbox = collectMailbox(indx);
|
|
// if returns null error code already set.
|
|
if (tmp_mbox == null)
|
|
{
|
|
return(null);
|
|
}
|
|
mailboxes.insertElementAt((Object)tmp_mbox, 0);
|
|
|
|
indx = tmp_mbox.getTokenStart();
|
|
comma = (RFC822Token)this.tokens.elementAt(indx - 1);
|
|
while (comma.isSpecialChar(',') == true)
|
|
{
|
|
/*
|
|
* Skip the extra commas the RFC allows here
|
|
*/
|
|
indx = skipCommas(indx - 1);
|
|
|
|
/*
|
|
* List could end here with commas
|
|
*/
|
|
comma = (RFC822Token)this.tokens.elementAt(indx);
|
|
if (comma.isSpecialChar(':') == true)
|
|
{
|
|
indx++;
|
|
break;
|
|
}
|
|
|
|
tmp_mbox = collectMailbox(indx);
|
|
// if returns null error code already set.
|
|
if (tmp_mbox == null)
|
|
{
|
|
return(null);
|
|
}
|
|
mailboxes.insertElementAt((Object)tmp_mbox, 0);
|
|
|
|
indx = tmp_mbox.getTokenStart();
|
|
comma = (RFC822Token)this.tokens.elementAt(indx - 1);
|
|
}
|
|
if (comma.isSpecialChar(':') == false)
|
|
{
|
|
// ERROR: group contained more than
|
|
// a comma separated list of
|
|
// mailboxes with the
|
|
// ':' to ';' pair.
|
|
this.error_code = EXTRA_TOKENS_IN_MAILBOX_LIST;
|
|
return(null);
|
|
}
|
|
colon_at = indx - 1;
|
|
}
|
|
/*
|
|
* This is the case of an empty mailbox list
|
|
* in the group.
|
|
*/
|
|
else
|
|
{
|
|
colon_at = indx;
|
|
}
|
|
|
|
phrase = collectPhrase(colon_at);
|
|
// if returns null error code already set.
|
|
if (phrase == null)
|
|
{
|
|
return(null);
|
|
}
|
|
group = new GroupToken(phrase, mailboxes, indx2);
|
|
return(group);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function to skip arbitrary number of commas.
|
|
* Needed because of the RFC's stupid comma separated list
|
|
* rules.
|
|
* Passed the index of possibly the first comma,
|
|
* returns the index of the first non-comma.
|
|
*/
|
|
private int skipCommas(int indx)
|
|
{
|
|
RFC822Token comma;
|
|
|
|
if (indx >= this.tokens.size())
|
|
{
|
|
return(indx);
|
|
}
|
|
|
|
comma = (RFC822Token)this.tokens.elementAt(indx);
|
|
while(comma.isSpecialChar(',') == true)
|
|
{
|
|
indx--;
|
|
if (indx < 0)
|
|
{
|
|
break;
|
|
}
|
|
comma = (RFC822Token)this.tokens.elementAt(indx);
|
|
}
|
|
return(indx);
|
|
}
|
|
|
|
|
|
/*
|
|
* Like skipCommas above but skipping forward
|
|
* instead of backwards.
|
|
*/
|
|
private int skipForwardCommas(int indx, int end)
|
|
{
|
|
RFC822Token comma;
|
|
|
|
comma = (RFC822Token)this.tokens.elementAt(indx);
|
|
while(comma.isSpecialChar(',') == true)
|
|
{
|
|
indx++;
|
|
if (indx > end)
|
|
{
|
|
break;
|
|
}
|
|
comma = (RFC822Token)this.tokens.elementAt(indx);
|
|
}
|
|
return(indx);
|
|
}
|
|
|
|
|
|
/*
|
|
* Passing in the final '>' token of a route-addr, work backwards
|
|
* through the tokens to the matching opening '<' token.
|
|
*/
|
|
private int findBeginRouteAddr(int end)
|
|
{
|
|
int indx = end - 1;
|
|
|
|
while (indx >= 0)
|
|
{
|
|
RFC822Token token;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
if (token.isSpecialChar('<') == true)
|
|
{
|
|
break;
|
|
}
|
|
indx--;
|
|
}
|
|
|
|
if (indx >= 0)
|
|
{
|
|
return(indx);
|
|
}
|
|
|
|
// ERROR: No matching '<' to '>' pair.
|
|
return(NO_ROUTE_ADDR);
|
|
}
|
|
|
|
|
|
/*
|
|
* Starting at the suspected terminal sub-domain token
|
|
* parse up to the beginning of the domain.
|
|
* All legal domains begin with an '@' symbol.
|
|
* To support the illegal but very common case of a local-part
|
|
* with no domain, need to return a special error for
|
|
* a missing domain.
|
|
*/
|
|
private int findBeginDomain(int end)
|
|
{
|
|
int indx = end;
|
|
boolean need_dot = false;
|
|
|
|
while (indx >= 0)
|
|
{
|
|
RFC822Token token;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
if (token.isSpecialChar('@') == true)
|
|
{
|
|
break;
|
|
}
|
|
/*
|
|
* Must be a '.' separated list of sub-domains.
|
|
*/
|
|
else if ((token.isSpecialChar('.') == true)&&
|
|
(need_dot == true))
|
|
{
|
|
need_dot = false;
|
|
}
|
|
/*
|
|
* Between '.' symbols, only atoms or domain-literals
|
|
* make legal sub-domains.
|
|
*/
|
|
else if (((token.getType() == RFC822Token.ATOM)||
|
|
(token.getType() == RFC822Token.DOMAIN_LITERAL))&&
|
|
(need_dot == false))
|
|
{
|
|
need_dot = true;
|
|
}
|
|
else
|
|
{
|
|
// ERROR: Encountered some token out of
|
|
// position to make a legal domain.
|
|
return(NO_DOMAIN);
|
|
}
|
|
indx--;
|
|
}
|
|
|
|
/*
|
|
* Must contain at least one sub-domain after the '@'
|
|
* symbol to be a legal domain.
|
|
*/
|
|
if ((indx >= 0)&&(indx < end))
|
|
{
|
|
return(indx);
|
|
}
|
|
|
|
// ERROR: No legal domain.
|
|
return(NO_DOMAIN);
|
|
}
|
|
|
|
|
|
/*
|
|
* A local-part looks a lot like a domain. The difference
|
|
* being it can have quoted-strings as well as atoms between the '.'
|
|
* symbols, while the domain can have domain-literals as well
|
|
* as atoms between the '.' symbols.
|
|
* So for a list of just '.' separated atoms, you depend
|
|
* on the '@' symbol to know which you are.
|
|
*
|
|
* It is illegal but common for the domain to be ommitted.
|
|
* We will treat this as legal.
|
|
*/
|
|
private int findBeginLocalPart(int end)
|
|
{
|
|
int indx = end;
|
|
boolean need_dot = false;
|
|
|
|
while (indx >= 0)
|
|
{
|
|
RFC822Token token;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
if ((token.isSpecialChar('.') == true)&&
|
|
(need_dot == true))
|
|
{
|
|
need_dot = false;
|
|
}
|
|
else if (((token.getType() == RFC822Token.QUOTED_STRING)||
|
|
(token.getType() == RFC822Token.ATOM))&&
|
|
(need_dot == false))
|
|
{
|
|
need_dot = true;
|
|
}
|
|
/*
|
|
* Break the local part on the first non-matching
|
|
* token you find.
|
|
*/
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
indx--;
|
|
}
|
|
|
|
/*
|
|
* Since we broke on the terminating token, increment
|
|
* to get to the start of the local-part.
|
|
*/
|
|
indx++;
|
|
|
|
/*
|
|
* Must contain at least one word.
|
|
* Cannot start with a '.' symbol.
|
|
*/
|
|
if ((need_dot == true)&&(indx >= 0)&&(indx <= end))
|
|
{
|
|
return(indx);
|
|
}
|
|
|
|
// ERROR: Not a legal local-part.
|
|
return(NO_LOCAL_PART);
|
|
}
|
|
|
|
|
|
/*
|
|
* A legal phrase is always followed by a special symbol
|
|
* (either ':' or '<'). The passed index is assumed to be that
|
|
* symbol, and we search back for the start of the phrase.
|
|
*/
|
|
private int findBeginPhrase(int end)
|
|
{
|
|
int indx = end - 1;
|
|
|
|
while (indx >= 0)
|
|
{
|
|
RFC822Token token;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
if ((token.getType() == RFC822Token.QUOTED_STRING)||
|
|
(token.getType() == RFC822Token.ATOM))
|
|
{
|
|
// OK
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
indx--;
|
|
}
|
|
|
|
/*
|
|
* Since we broke on the terminating token, increment
|
|
* to get to the start of the phrase.
|
|
*/
|
|
indx++;
|
|
|
|
/*
|
|
* A legal index must have at least one word in
|
|
* the phrase.
|
|
*/
|
|
if ((indx >= 0)&&(indx < end))
|
|
{
|
|
return(indx);
|
|
}
|
|
|
|
// ERROR: Illegal phrase.
|
|
return(NO_PHRASE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Having been passed the rightmost token, collect the
|
|
* phrase and pass it back as an address token.
|
|
* On an error return null and set an error code.
|
|
*/
|
|
private AddressToken collectPhrase(int indx2)
|
|
{
|
|
int indx1 = findBeginPhrase(indx2);
|
|
AddressToken addr_token;
|
|
|
|
if (indx1 == NO_PHRASE)
|
|
{
|
|
this.error_code = NO_PHRASE;
|
|
return(null);
|
|
}
|
|
|
|
addr_token = new AddressToken(indx1, indx2 - 1, AddressToken.PHRASE);
|
|
return(addr_token);
|
|
}
|
|
|
|
|
|
/*
|
|
* Having been passed the rightmost token, collect the
|
|
* route-addr and pass it back as an address token.
|
|
* On an error return null and set an error code.
|
|
*/
|
|
private AddressToken collectRouteAddr(int indx2)
|
|
{
|
|
AddressToken addr_token;
|
|
RFC822Token token;
|
|
int indx;
|
|
int indx1 = findBeginRouteAddr(indx2);
|
|
|
|
if ((indx1 == NO_ROUTE_ADDR)||(indx1 >= (indx2 - 1)))
|
|
{
|
|
// ERROR: Illegal to have a route-addr with
|
|
// an empty body.
|
|
this.error_code = NO_ROUTE_ADDR;
|
|
return(null);
|
|
}
|
|
addr_token = collectAddrSpec(indx2 - 1);
|
|
// if returns null error code already set.
|
|
if (addr_token == null)
|
|
{
|
|
return(null);
|
|
}
|
|
indx = addr_token.getTokenStart();
|
|
|
|
/*
|
|
* The route is optional, look for the closing ':'
|
|
* symbol to see if we have one.
|
|
*/
|
|
token = (RFC822Token)this.tokens.elementAt(indx - 1);
|
|
if (token.isSpecialChar(':') == true)
|
|
{
|
|
AddressToken route = collectRoute(indx - 1);
|
|
|
|
// if returns null error code already set.
|
|
if (route == null)
|
|
{
|
|
return(null);
|
|
}
|
|
indx = route.getTokenStart();
|
|
}
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx - 1);
|
|
if (token.isSpecialChar('<') == false)
|
|
{
|
|
// ERROR: More in the route-addr than a legal
|
|
// addr-spec and an optional route.
|
|
this.error_code = EXTRA_TOKENS_IN_ROUTE_ADDR;
|
|
return(null);
|
|
}
|
|
addr_token = new AddressToken(indx - 1, indx2, AddressToken.ROUTE_ADDR);
|
|
return(addr_token);
|
|
}
|
|
|
|
|
|
/*
|
|
* Having been passed the rightmost token, collect the
|
|
* addr-spec and pass it back as an address token.
|
|
* On an error return null and set an error code.
|
|
*/
|
|
private AddressToken collectAddrSpec(int indx2)
|
|
{
|
|
int at_sign = findBeginDomain(indx2);
|
|
int indx1;
|
|
AddressToken addr_token;
|
|
|
|
if (at_sign == NO_DOMAIN)
|
|
{
|
|
indx1 = findBeginLocalPart(indx2);
|
|
}
|
|
else
|
|
{
|
|
indx1 = findBeginLocalPart(at_sign - 1);
|
|
}
|
|
|
|
if (indx1 == NO_LOCAL_PART)
|
|
{
|
|
this.error_code = NO_ADDR_SPEC;
|
|
return(null);
|
|
}
|
|
|
|
addr_token = new AddressToken(indx1, indx2, AddressToken.ADDR_SPEC);
|
|
|
|
return(addr_token);
|
|
}
|
|
|
|
/*
|
|
* Having been passed the rightmost token, collect the
|
|
* route and pass it back as an address token.
|
|
* On an error return null and set an error code.
|
|
*/
|
|
private AddressToken collectRoute(int indx2)
|
|
{
|
|
AddressToken addr_token;
|
|
RFC822Token token;
|
|
int at_sign;
|
|
int indx;
|
|
|
|
token = (RFC822Token)this.tokens.elementAt(indx2);
|
|
if (token.isSpecialChar(':') == false)
|
|
{
|
|
// ERROR: Not a legal route, not ':' terminated.
|
|
this.error_code = BAD_ROUTE;
|
|
return(null);
|
|
}
|
|
|
|
/*
|
|
* Stupid RFC allows many commas at end of list.
|
|
*/
|
|
indx = skipCommas(indx2 - 1);
|
|
|
|
/*
|
|
* Must be at least one domain, look for the '@' symbol
|
|
* that starts it.
|
|
*/
|
|
at_sign = findBeginDomain(indx);
|
|
if (at_sign < 1)
|
|
{
|
|
// ERROR: Must always be room to move back from
|
|
// the start of a route because it is
|
|
// always enclosed in a route-addr.
|
|
// So it is always ok to look for the ','
|
|
// In the optional comma separated list.
|
|
this.error_code = BAD_ROUTE_DOMAIN;
|
|
return(null);
|
|
}
|
|
|
|
/*
|
|
* If there is a comma, there are one or more domains to
|
|
* skip over.
|
|
*/
|
|
token = (RFC822Token)this.tokens.elementAt(at_sign - 1);
|
|
while (token.isSpecialChar(',') == true)
|
|
{
|
|
/*
|
|
* Again RFC allows many commas
|
|
*/
|
|
indx = skipCommas(at_sign - 1);
|
|
/*
|
|
* These commas may have been the end of the list
|
|
*/
|
|
token = (RFC822Token)this.tokens.elementAt(indx);
|
|
if (token.isSpecialChar('<') == true)
|
|
{
|
|
at_sign = indx + 1;
|
|
break;
|
|
}
|
|
|
|
at_sign = findBeginDomain(indx);
|
|
if (at_sign < 1)
|
|
{
|
|
// ERROR: Must always be room to move back from
|
|
// the start of a route because it is
|
|
// always enclosed in a route-addr.
|
|
// So it is always ok to look for the','
|
|
// In the optional comma separated list.
|
|
this.error_code = BAD_ROUTE_DOMAIN;
|
|
return(null);
|
|
}
|
|
token = (RFC822Token)this.tokens.elementAt(at_sign - 1);
|
|
}
|
|
|
|
addr_token = new AddressToken(at_sign, indx2, AddressToken.ROUTE);
|
|
|
|
return(addr_token);
|
|
}
|
|
|
|
|
|
/*
|
|
* Having been passed the rightmost token, collect the
|
|
* mailbox and pass it back as a mailbox token.
|
|
* On an error return null and set an error code.
|
|
*/
|
|
private MailboxToken collectMailbox(int indx2) throws RFC822ParserException
|
|
{
|
|
MailboxToken mailbox = null;
|
|
AddressToken addr_token = null;
|
|
AddressToken phrase;
|
|
RFC822Token token;
|
|
int indx;
|
|
|
|
/*
|
|
* A legal mailbox either ends in a '>'
|
|
* symbol and is a phrase route-addr pair,
|
|
* or it is a simple addr-spec.
|
|
*/
|
|
token = (RFC822Token)this.tokens.elementAt(indx2);
|
|
/*
|
|
* Get the phrase route-addr pair.
|
|
*/
|
|
if (token.isSpecialChar('>') == true)
|
|
{
|
|
addr_token = collectRouteAddr(indx2);
|
|
// if returns null error code already set.
|
|
if (addr_token == null)
|
|
{
|
|
return(null);
|
|
}
|
|
indx = addr_token.getTokenStart();
|
|
phrase = collectPhrase(indx);
|
|
if (this.error_code == NO_PHRASE)
|
|
{
|
|
throw new RouteAddrNoPhraseException("No valid phrase with this route address.", addr_token, this.tokens);
|
|
}
|
|
// if returns null error code already set.
|
|
if (phrase == null)
|
|
{
|
|
return(null);
|
|
}
|
|
indx = phrase.getTokenStart();
|
|
mailbox = new MailboxToken(phrase, addr_token);
|
|
}
|
|
/*
|
|
* Get the addr-spec.
|
|
*/
|
|
else
|
|
{
|
|
addr_token = collectAddrSpec(indx2);
|
|
// if returns null error code already set.
|
|
if (addr_token == null)
|
|
{
|
|
return(null);
|
|
}
|
|
indx = addr_token.getTokenStart();
|
|
mailbox = new MailboxToken(addr_token);
|
|
}
|
|
|
|
return(mailbox);
|
|
}
|
|
}
|
|
|