0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.tck.wma.mms;
0028:
0029: /**
0030: * MMS address parsing and validation.
0031: * MMS address is validated according to Table D-2 in WMA2.0 specification.
0032: * In addition address is parsed into the fields of <code>MMSAddress </code>
0033: * object.
0034: */
0035: public class MMSAddress {
0036:
0037: /**
0038: * Determines whether a character represents a hexadecimal number.
0039: * ('0' - '9' and 'A' - 'F')
0040: * @param c The character to check.
0041: * @return <code>true </code> if <code>c</code> is a hexadecimal number;
0042: * <code> false </code> otherwise
0043: */
0044: private static boolean hex(char c) {
0045: return (digit(c) || (c >= 'A' && c <= 'F'));
0046: }
0047:
0048: /**
0049: * Determines whether a character is a letter.
0050: * @param c The character to check.
0051: * @return <code>true </code> if <code>c</code> is a letter;
0052: * <code> false </code> otherwise
0053: */
0054: private static boolean alpha(char c) {
0055: return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
0056: }
0057:
0058: /**
0059: * Determines whether a character represents a digit.
0060: * @param c The character to check.
0061: * @return <code>true </code> if <code>c</code> is a digit;
0062: * <code> false </code> otherwise
0063: */
0064: private static boolean digit(char c) {
0065: return (c >= '0' && c <= '9');
0066: }
0067:
0068: /**
0069: * Determines whether a string represents a valid phone number
0070: * starting from an index. If the address string is a valid
0071: * phone number representation it is parsed into the fields of
0072: * <code>MMSAddress </code>object (address and application id).
0073: * General phone number syntax is specified in WMA 2.0 spec as :
0074: * ("+" 1*digit | 1*digit) [applicationId]
0075: *
0076: * @param s The string to check.
0077: * @param mmsAddress The return <code>MMSAddress</code> object which
0078: * fields are filled with parsed address and
0079: * application id (which can be null),
0080: * its type is set to GLOBAL_PHONE_NUMBER.
0081: * @param i The index of the string at which to start the check.
0082: * <code> 0 <= i <= s.length() </code>
0083: *
0084: * @return <code>true </code> if <code>s</code> represents a valid
0085: * phone number starting from index <code>i</code>;
0086: * <code> false </code> otherwise and if index <code>i</code> is
0087: * invalide value
0088: */
0089: static boolean parsePhoneNumber(String s, int i,
0090: MMSAddress mmsAddress) {
0091:
0092: int len = s.length();
0093: if (i < 0 || i >= len) {
0094: return false;
0095: }
0096:
0097: int initI = i;
0098: char c = s.charAt(i);
0099:
0100: if (c == '+') {
0101: i++;
0102: if (len == i) {
0103: return false;
0104: }
0105: }
0106:
0107: int j = 0;
0108: for (; i < len; j++, i++) {
0109: c = s.charAt(i);
0110: if (!digit(c)) {
0111: break;
0112: }
0113: }
0114:
0115: if (j == 0) {
0116: return false;
0117: }
0118:
0119: if (i == len) {
0120: mmsAddress.set(s.substring(initI), null,
0121: GLOBAL_PHONE_NUMBER);
0122: return true;
0123: }
0124:
0125: if (c == ':') {
0126: mmsAddress.set(s.substring(initI, i), null,
0127: GLOBAL_PHONE_NUMBER);
0128: return parseApplicationId(s, i, mmsAddress);
0129: }
0130:
0131: return false;
0132: }
0133:
0134: /**
0135: * Determines whether a string represents a valid ipv4 address
0136: * starting from an index. If the address string is a valid
0137: * ipv4 representation it is parsed into the fields of
0138: * <code>MMSAddress </code>object (address and application id).
0139: * ipv4 syntax is specified in WMA 2.0 spec as :
0140: * 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit [applicationId]
0141: *
0142: * @param s The string to check.
0143: * @param mmsAddress The return <code>MMSAddress</code> object which
0144: * fields are filled with parsed address and
0145: * application id (which can be null), its type is set to IPV4.
0146: * @param i The index of the string at which to start the check.
0147: * <code> 0 <= i <= s.length() </code>
0148: *
0149: * @return <code>true </code> if <code>s</code> represents a valid
0150: * ipv4 address starting from index <code>i</code>;
0151: * <code> false </code> otherwise and if index <code>i</code> is
0152: * invalide value
0153: */
0154: static boolean parseIpv4(String s, int i, MMSAddress mmsAddress) {
0155: int len = s.length();
0156: if (i <= 0 || i >= len) {
0157: return false;
0158: }
0159:
0160: char c;
0161: int j;
0162: int initI = i;
0163:
0164: for (int num = 1; num < 5; num++) {
0165:
0166: for (j = 0; j < 3 && i < len; i++, j++) {
0167:
0168: c = s.charAt(i);
0169:
0170: if (!digit(c)) {
0171: break;
0172: }
0173: }
0174:
0175: // there should be at least one digit and
0176: // the number between dots should be less then 256
0177: if (j == 0
0178: || (j == 3 && (((s.charAt(i - 3) - '0') * 10 + (s
0179: .charAt(i - 2) - '0')) * 10 + (s
0180: .charAt(i - 1) - '0')) > 255)) {
0181: return false;
0182: }
0183:
0184: // check if this is the end of the string
0185: if (i == len) {
0186: if (num == 4) {
0187: mmsAddress.set(s.substring(initI), null, IPV4);
0188: return true;
0189: }
0190: return false;
0191: }
0192:
0193: c = s.charAt(i);
0194:
0195: if (c == ':') {
0196: mmsAddress.set(s.substring(initI, i), null, IPV4);
0197: return parseApplicationId(s, i, mmsAddress);
0198: }
0199:
0200: if (c != '.') {
0201: return false; // 4th character after beg (or dot)
0202: // should be dot;
0203: // only dots are allowed as non digit char
0204: }
0205:
0206: // allowed '.' => continue
0207: i++;
0208: }
0209:
0210: return false;
0211: }
0212:
0213: /**
0214: * Determines whether a string represents a valid ipv6 address
0215: * starting from an index. If the address string is a valid
0216: * ipv6 representation it is parsed into the fields of
0217: * <code>MMSAddress </code>object (address and application id).
0218: * ipv6 syntax is specified in WMA 2.0 spec as :
0219: * ipv6-atom ":" ipv6-atom ":" ipv6-atom ":" ipv6-atom ":"
0220: * ipv6-atom ":" ipv6-atom ":" ipv6-atom ":" ipv6-atom [appId]
0221: * where ipv6-atom is 1*4(digit | hex-alpha).
0222: *
0223: * @param s The string to check.
0224: * @param mmsAddress The return <code>MMSAddress</code> object which
0225: * fields are filled with parsed address and
0226: * application id (which can be null), its type is set to IPV6.
0227: * @param i The index of the string at which to start the check.
0228: * <code> 0 <= i <= s.length() </code>
0229: *
0230: * @return <code>true </code> if <code>s</code> represents a valid
0231: * ipv6 address starting from index <code>i</code>;
0232: * <code> false </code> otherwise and if index <code>i</code> is
0233: * invalide value
0234: */
0235: static boolean parseIpv6(String s, int i, MMSAddress mmsAddress) {
0236: int len = s.length();
0237: if (i <= 0 || i >= len) {
0238: return false;
0239: }
0240:
0241: char c;
0242: int j;
0243: int initI = i;
0244:
0245: for (int num = 1; num < 9; num++) {
0246:
0247: for (j = 0; j < 4 && i < len; i++, j++) {
0248:
0249: c = s.charAt(i);
0250:
0251: if (!hex(c)) {
0252: break;
0253: }
0254: }
0255:
0256: // there should be at least one digit
0257: if (j == 0) {
0258: return false;
0259: }
0260:
0261: // check if this is the end of the string
0262: if (i == len) {
0263: if (num == 8) {
0264: mmsAddress.set(s.substring(initI), null, IPV6);
0265: return true;
0266: }
0267: return false;
0268: }
0269:
0270: c = s.charAt(i);
0271:
0272: if (c == ':') {
0273: if (num == 8) {
0274: mmsAddress.set(s.substring(initI, i), null, IPV6);
0275: return parseApplicationId(s, i, mmsAddress);
0276: }
0277: } else {
0278: return false; // 5th character after beg (or :)
0279: // should be :
0280: // only : are allowed as non digit char
0281: }
0282:
0283: // allowed ':' => continue
0284: i++;
0285: }
0286:
0287: return false;
0288: }
0289:
0290: /**
0291: * Determines whether a string represents a valid shortcode
0292: * starting from an index. If the address string is a valid
0293: * shortcode representation it is parsed into the fields of
0294: * <code>MMSAddress </code>object (address and null for appId).
0295: * Shortcode syntax is specified in WMA 2.0 spec as :
0296: * *(digit | alpha)
0297: *
0298: * @param s The string to check.
0299: * @param mmsAddress The return <code>MMSAddress</code> object which
0300: * fields are filled with parsed address and application id (null in
0301: * this case), its type is set to SHORTCODE.
0302: * @param i The index of the string at which to start the check.
0303: * <code> 0 <= i <= s.length() </code>
0304: *
0305: * @return <code>true </code> if <code>s</code> represents a valid
0306: * shortcode starting from index <code>i</code>;
0307: * <code> false </code> otherwise and if index <code>i</code> is
0308: * invalide value
0309: */
0310: static boolean parseShortCode(String s, int i, MMSAddress mmsAddress) {
0311: int len = s.length();
0312: if (i > len) {
0313: return false;
0314: }
0315:
0316: if (i == len) {
0317: return false; // Even though spec allows "mms://" as a valid
0318: // shortcode, we disallow it here
0319: }
0320: int initI = i;
0321: char c;
0322:
0323: for (; i < s.length(); i++) {
0324: c = s.charAt(i);
0325:
0326: if (!digit(c) && !alpha(c)) {
0327: return false;
0328: }
0329: }
0330: mmsAddress.set(s.substring(initI), null, SHORTCODE);
0331: return true;
0332: }
0333:
0334: /**
0335: * Determines whether a string represents a valid application ID
0336: * starting from an index.If the address string is a valid
0337: * application id representation it is parsed into the fields of
0338: * <code>MMSAddress </code>object (application id and null for the
0339: * address).
0340: * Application ID syntax is specified in WMA 2.0 spec as :
0341: * ":"[*(1*(alpha | digit | "." | "_") ".")]1*(alpha | digit | "." | "_").
0342: * The number of characters in application ID must not exceed 32.
0343: *
0344: * @param s The string to check.
0345: * @param mmsAddress The return <code>MMSAddress</code> object which
0346: * fields are filled with parsed address (null in this case) and
0347: * application id, its type is set to APP_ID.
0348: * @param i The index of the string at which to start the check.
0349: * <code> 0 <= i <= s.length() </code>
0350: *
0351: * @return <code>true </code> if <code>s</code> represents a valid
0352: * application ID starting from index <code>i</code>;
0353: * <code> false </code> otherwise and if index <code>i</code> is
0354: * invalide value
0355: */
0356: static boolean parseApplicationId(String s, int i,
0357: MMSAddress mmsAddress) {
0358: int len = s.length();
0359:
0360: // empty string or string like ":" are not allowed
0361: // only 32 characters is allowed
0362: if (i <= 0 || i >= len || (len - i) > 32 || (len - i) < 1
0363: || s.charAt(i) != ':') {
0364: return false;
0365: }
0366:
0367: i++; // to skip ':'
0368:
0369: char c;
0370: int initI = i;
0371:
0372: for (int j = 0; i < len; i++) {
0373:
0374: c = s.charAt(i);
0375:
0376: for (j = 0; i < len; i++, j++) {
0377: c = s.charAt(i);
0378:
0379: if ((c != '_') && /* dot is also allowed in the spec ??? */
0380: !digit(c) && !alpha(c)) {
0381: break;
0382: }
0383: }
0384:
0385: if (i == len) {
0386: if (mmsAddress.type == INVALID_ADDRESS) {
0387: mmsAddress.set(null, s.substring(initI), APP_ID);
0388: } else {
0389: mmsAddress.setAppid(s.substring(initI));
0390: }
0391: return true;
0392: }
0393:
0394: // there has to be at least one digit or letter and
0395: // nondigit and nonalpha char can be only dot (one dot only)
0396: if (j == 0 || c != '.') {
0397: return false;
0398: }
0399: }
0400:
0401: return false;
0402: }
0403:
0404: // ------------------------------- E-mail validation ----------------
0405: /**
0406: * Determines whether a character represents a special e-mail character.
0407: * @param c The character to check.
0408: * @return <code>true </code> if <code>c</code> is a special character.
0409: * <code> false </code> otherwise
0410: */
0411: private static boolean specials(char c) {
0412: if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@'
0413: || c == ',' || c == ';' || c == ':' || c == '\\'
0414: || c == '"' || c == '.' || c == '[' || c == ']') {
0415: return true;
0416: }
0417: return false;
0418: }
0419:
0420: /**
0421: * Determines whether a character represents a character.
0422: * @param c The character to check.
0423: * @return <code>true </code> if <code>c</code> is a character.
0424: * <code> false </code> otherwise
0425: */
0426: private static boolean isChar(char c) {
0427: return ((int) c) >= 0 && ((int) c) <= 127;
0428: }
0429:
0430: /**
0431: * Determines whether a character represents an ascii control
0432: * character.
0433: * @param c The character to check.
0434: * @return <code>true </code> if <code>c</code> is a character.
0435: * <code> false </code> otherwise
0436: */
0437: private static boolean asciiControl(char c) {
0438: return ((int) c >= 0 && (int) c <= 31);
0439: }
0440:
0441: /**
0442: * Determines whether a character represents an atom character.
0443: * as specified in WMA 2.0 specification.
0444: * @param c The character to check.
0445: * @return <code>true </code> if <code>c</code> is a special character.
0446: * <code> false </code> otherwise
0447: */
0448: private static boolean atomChar(char c) {
0449: return ((int) c <= 127 && (int) c > 32 && !specials(c));
0450: }
0451:
0452: /**
0453: * Determines first not linear white space character
0454: * in <code>s</code> after character with index <code>i</code> but
0455: * before character with index <code>n</code>.
0456: *
0457: * @param s The string to check.
0458: * @param i The index of the string at which to start the check.
0459: * <code> 0 <= i <= s.length() </code>
0460: * @param n The index of the last character to be checked in
0461: * <code>s</code>.
0462: * @return The first non linear white character after position
0463: * <code>i</code>, it could the the same as passed in
0464: * <code>i</code> value if there are no linear white
0465: * space characters.
0466: */
0467: private static int linearWhiteSpaceSeq(String s, int i, int n) {
0468: char c;
0469: for (; i < n; i++) {
0470: c = s.charAt(i);
0471: if (c == ' ' || c == '\t') {
0472: continue;
0473: }
0474: if (c == '\r') {
0475: // only CR LF one after another are allowed
0476: // CR by itself is not allowed
0477: if (i == n - 1 || s.charAt(i + 1) != '\n') {
0478: return i;
0479: }
0480: i++;
0481: } else {
0482: return i;
0483: }
0484: }
0485:
0486: return i;
0487: }
0488:
0489: /**
0490: * Determines whether a string represents a valid e-mail address
0491: * starting from an index. If the address string is a valid
0492: * e-mail address representation it is parsed into the fields of
0493: * <code>MMSAddress </code>object (address and null for appId).
0494: * E-mail syntax is specified in WMA 2.0 spec.
0495: *
0496: * @param s The string to check.
0497: * @param i The index of the string at which to start the check.
0498: * <code> 0 <= i <= s.length() </code>
0499: * @param mmsAddress The return <code>MMSAddress</code> object which
0500: * fields are filled with parsed address and
0501: * application id (null in this case), its type is set to EMAIL.
0502: *
0503: * @return <code>true </code> if <code>s</code> represents a valid
0504: * e-mail address starting from index <code>i</code>;
0505: * <code> false </code> otherwise and if index <code>i</code> is
0506: * invalide value
0507: */
0508: static boolean parseEmail(String s, int i, MMSAddress mmsAddress) {
0509:
0510: int len = s.length();
0511: if (i <= 0 || i >= len) {
0512: return false;
0513: }
0514:
0515: int initI = i;
0516:
0517: // email can be on of the following:
0518: // emial ::== mailbox | phrase : [#mailbox] ;
0519:
0520: // if there is a ';' at the end then there should be a phrase
0521: // at the beginning
0522: if (s.charAt(len - 1) == ';') {
0523:
0524: if ((i = isPhrase(s, i, len - 1)) <= 0) {
0525: return false;
0526: }
0527:
0528: // there was no ":"
0529: if (i >= len - 1 || s.charAt(i) != ':') {
0530: return false;
0531: }
0532:
0533: i++; // move after ':'
0534:
0535: if (i == len - 1) {
0536: mmsAddress.set(s.substring(initI), null, EMAIL);
0537: return true; // phrase : ; is allowed string
0538: }
0539:
0540: if ((i = isMailbox(s, i, len - 1)) <= 0) {
0541: return false;
0542: }
0543:
0544: while (i < len - 1) {
0545:
0546: // there has to be a ',' sep
0547: if (i >= len - 2 && s.charAt(i) != ',') {
0548: return false;
0549: }
0550:
0551: if ((i = isMailbox(s, i + 1, len - 1)) <= 0) {
0552: return false;
0553: }
0554: }
0555: if (i == len - 1) {
0556: mmsAddress.set(s.substring(initI), null, EMAIL);
0557: return true;
0558: }
0559: return false;
0560: }
0561:
0562: if (isMailbox(s, i, len) == len) {
0563: mmsAddress.set(s.substring(initI), null, EMAIL);
0564: return true;
0565: }
0566: return false;
0567: }
0568:
0569: /**
0570: * Determines whether a string represents a valid mailbox address
0571: * starting from an index.
0572: * Mailbox syntax is specified in WMA 2.0 spec as
0573: * local-part"@"domain or as
0574: * [phrase] "<"[("@"domain) [*("," ("@"domain))]] local-part"@"domain">"
0575: *
0576: * @param s The string to check.
0577: * @param i The index of the string at which to start the check.
0578: * <code> 0 <= i <= s.length() </code>
0579: * @param n The index of the last character to be checked in
0580: * <code>s</code>.
0581: *
0582: * @return index till which
0583: * <code>s</code> contains valid mailbox specification;
0584: * <code> -1 </code> if index <code>i</code> is invalid or
0585: * if characters in <code>s</code> starting from <code>i</code>
0586: * do not comply with mailbox specification
0587: */
0588: private static int isMailbox(String s, int i, int n) {
0589: if (n > s.length()) {
0590: n = s.length();
0591: }
0592: if (i >= n) {
0593: return -1;
0594: }
0595:
0596: int initI = i;
0597:
0598: if ((i = isLocalAtDomain(s, initI, n)) > 0) {
0599: return i;
0600: }
0601:
0602: i = initI;
0603:
0604: // check for [phrase] <[1#(@ domain):] local @ domain>
0605: // note phrase is optional
0606: if ((s.charAt(i) != '<') && (i = isPhrase(s, initI, n)) < 0) {
0607: return -1;
0608: }
0609:
0610: if (i == n || s.charAt(i) != '<') {
0611: return -1;
0612: }
0613:
0614: i++;
0615:
0616: if (i == n) {
0617: return -1;
0618: }
0619:
0620: // there can be a list of domains:
0621: // @ domain, @domain, @domain :
0622: boolean atLeastOneDomain = false;
0623: while (s.charAt(i) == '@') {
0624: atLeastOneDomain = true;
0625:
0626: if ((i = isDomain(s, i + 1, n)) < 0) {
0627: return -1;
0628: }
0629:
0630: if (i == n) {
0631: return -1;
0632: }
0633:
0634: if (s.charAt(i) == ':') {
0635: break;
0636: }
0637:
0638: // @domain,,@domain is allowed
0639: while (s.charAt(i) == ',') {
0640: i++;
0641: if (i == n || s.charAt(i) == ':') {
0642: return -1;
0643: }
0644: }
0645: }
0646:
0647: if (atLeastOneDomain) {
0648: if (i == n || s.charAt(i) != ':') {
0649: return -1;
0650: }
0651: i++;
0652: }
0653:
0654: // local @ domain
0655: if ((i = isLocalAtDomain(s, i, n)) <= 0) {
0656: return -1;
0657: }
0658:
0659: if (i < n && s.charAt(i) == '>') {
0660: return i + 1;
0661: }
0662: return -1;
0663: }
0664:
0665: /**
0666: * Determines whether a string represents a valid "local-part"
0667: * starting from an index.
0668: * Local-part syntax is specified in WMA 2.0 spec as
0669: * (atom | "\"alpha) *("." (atom | "\"alpha)) where
0670: * atom is represented by at least one char which is
0671: * not a space or a special or control character.
0672: *
0673: * @param s The string to check.
0674: * @param i The index of the string at which to start the check
0675: * <code> 0 <= i <= s.length() </code>
0676: * @param n The index of the last character to be checked in
0677: * <code>s</code>.
0678: *
0679: * @return index till which
0680: * <code>s</code> contains valid local-part specification;
0681: * <code> -1 </code> if index <code>i</code> is invalid or
0682: * if characters in <code>s</code> starting from <code>i</code>
0683: * do not comply with local-part specification
0684: */
0685: private static int isLocalPart(String s, int i, int n) {
0686: return isSequenceOfAtomAndText(s, i, n, '"', '"', true);
0687: }
0688:
0689: /**
0690: * Determines whether a string represents a valid "phrase"
0691: * starting from an index.
0692: * Phrase syntax is specified in WMA 2.0 spec as
0693: * 1*(atom | """ *(text w/o ",\,\r but with linear space | "\"alpha) """)
0694: * where atom is represented by at least one char or more which is
0695: * not a space or a special or control character.
0696: *
0697: * @param s The string to check.
0698: * @param i The index of the string at which to start the check
0699: * <code> 0 <= i <= s.length() </code>
0700: * @param n The index of the last character to be checked in
0701: * <code>s</code>.
0702: *
0703: * @return index till which
0704: * <code>s</code> contains valid phrase specification;
0705: * <code> -1 </code> if index <code>i</code> is invalid or
0706: * if characters in <code>s</code> starting from <code>i</code>
0707: * do not comply with phrase specification
0708: */
0709: private static int isPhrase(String s, int i, int n) {
0710: return isSequenceOfAtomAndText(s, i, n, '"', '"', false);
0711: }
0712:
0713: /**
0714: * Determines whether a string represents a valid "domain"
0715: * starting from an index.
0716: * Domain syntax is specified in WMA 2.0 spec as
0717: * 1*(atom | "[" *(text w/o ",[,],\r but with linear space | "\"alpha) "]")
0718: * where atom is represented by at least one char or more which is
0719: * not a space or a special or control character.
0720: *
0721: * @param s The string to check.
0722: * @param i The index of the string at which to start the check
0723: * <code> 0 <= i <= s.length() </code>
0724: * @param n The index of the last character to be checked in
0725: * <code>s</code>.
0726: *
0727: * @return index till which
0728: * <code>s</code> contains valid domain specification;
0729: * <code> -1 </code> if index <code>i</code> is invalid or
0730: * if characters in <code>s</code> starting from <code>i</code>
0731: * do not comply with domain specification
0732: */
0733: private static int isDomain(String s, int i, int n) {
0734: return isSequenceOfAtomAndText(s, i, n, '[', ']', true);
0735: }
0736:
0737: /**
0738: * Determines whether a string starting from an index
0739: * represents a valid sequence that satisfies the following
0740: * syntax:
0741: * text ::== <begSep>
0742: * *(1*(<any char w/o begSep and eSep incl LWSP>|\ALPHA))
0743: * <endSep>
0744: * sequence ::== 1*(atom | text) | (atom | text) *("." (atom | text))
0745: * where atom is represented by at least one or more characters that
0746: * are not space or special or control characters
0747: * and begSep/endSep are separators that are placed around text.
0748: * LWSP is a linear white space (space and tabs and \r\n but not \r).
0749: *
0750: * @param s The string to check.
0751: * @param i The index of the string at which to start the check
0752: * <code> 0 <= i <= s.length() </code>
0753: * @param n The index of the last character to be checked in
0754: * <code>s</code>.
0755: * @param begSep The character marking beginning of quoted text
0756: * @param endSep The character marking the end of the text
0757: * @param dotSeparated If true atom and quoted text should be
0758: * separated by a "." character; ".." is not allowed
0759: *
0760: * @return index till which
0761: * <code>s</code> contains valid sequence;
0762: * <code> -1 </code> if index <code>i</code> is invalid or
0763: * if characters in <code>s</code> starting from <code>i</code>
0764: * do not comply with the sequence specification.
0765: */
0766: private static int isSequenceOfAtomAndText(String s, int i, int n,
0767: char begSep, char endSep, boolean dotSeparated) {
0768: if (i >= s.length()) {
0769: return -1;
0770: }
0771: if (n > s.length()) {
0772: n = s.length();
0773: }
0774:
0775: // see if there is at least one word
0776: boolean withinQuotation = false;
0777: boolean atLeastOneWord = false;
0778:
0779: char c;
0780: int initI = i;
0781:
0782: for (; i < n; i++) {
0783: c = s.charAt(i);
0784: if (c == begSep) {
0785: if (begSep == endSep) {
0786: withinQuotation = !withinQuotation;
0787: } else if (withinQuotation) {
0788: return -1;
0789: } else {
0790: withinQuotation = true;
0791: }
0792: atLeastOneWord = true;
0793: } else if (c == endSep) {
0794: if (!withinQuotation) {
0795: return -1;
0796: }
0797: withinQuotation = false;
0798: } else if (c == '\\') {
0799: // chars can be quoated only within separators
0800: // and there has to be another char after '\'
0801: if (!withinQuotation || i == n - 1) {
0802: return -1;
0803: }
0804: i++;
0805: c = s.charAt(i);
0806: // only ALPHA character can be quoted
0807: if (!alpha(c)) {
0808: return -1;
0809: }
0810:
0811: } else if (dotSeparated && c == '.') {
0812: if (i == initI
0813: || ((i - initI) > 0 && s.charAt(i - 1) == '.')) {
0814: // empty word
0815: // string starts with .
0816: return -1;
0817: }
0818: } else if (withinQuotation) {
0819: // within quotation \r is not allowed but
0820: // \r is allowed as part of linear-white-space
0821: if (c == ' ' || c == '\t' || c == '\r') {
0822: if ((i = linearWhiteSpaceSeq(s, i, n)) < 0) {
0823: return -1;
0824: }
0825: i--;
0826: }
0827: } else if (atomChar(c)) {
0828: atLeastOneWord = true;
0829: } else {
0830: // this is supposed to be an atom
0831: break;
0832: }
0833: }
0834:
0835: // open quotation or no words or
0836: // dot separator is the last char
0837: if (withinQuotation
0838: || !atLeastOneWord
0839: || (dotSeparated && i > initI && s.charAt(i - 1) == '.')) {
0840: return -1;
0841: }
0842:
0843: return i;
0844: }
0845:
0846: /**
0847: * Determines whether a string starting from an index satisfies the
0848: * following syntax: local-part"@"domain
0849: *
0850: * @param s The string to check.
0851: * @param i The index of the string at which to start the check.
0852: * <code> 0 <= i <= s.length() </code>
0853: * @param n The index of the last character to be checked in
0854: * <code>s</code>.
0855: *
0856: * @return index till which
0857: * <code>s</code> contains valid mailbox specification;
0858: * <code> -1 </code> if index <code>i</code> is invalid or
0859: * if characters in <code>s</code> starting from <code>i</code>
0860: * is not in the form of local-part"@"domain
0861: */
0862: private static int isLocalAtDomain(String s, int i, int n) {
0863: if (n > s.length()) {
0864: n = s.length();
0865: }
0866: if (i >= n) {
0867: return -1;
0868: }
0869:
0870: // local
0871: if ((i = isLocalPart(s, i, n)) <= 0) {
0872: return -1;
0873: }
0874:
0875: if (i == n || s.charAt(i) != '@') {
0876: return -1;
0877: }
0878:
0879: i++;
0880:
0881: // domain
0882: return isDomain(s, i, n);
0883: }
0884:
0885: /**
0886: * Determines whether a string represents a valid mms address
0887: * as specified in WMA 2.0 specification and parses the
0888: * incoming string into address and application id strings.
0889: *
0890: * @param addressStr The string to check.
0891: *
0892: * @return newly created <code>MMSAddress </code> object
0893: * if <code>addressStr</code> is a valid mms address;
0894: * <code> null </code> otherwise
0895: */
0896: public static MMSAddress getParsedMMSAddress(String addressStr) {
0897: return getParsedMMSAddress(addressStr, null);
0898: }
0899:
0900: /**
0901: * Determines whether a string represents a valid mms address
0902: * as specified in WMA 2.0 specification and parses the
0903: * incoming string into address and application id strings.
0904: *
0905: * @param addressStr The string to check.
0906: * @param mmsAddress The return <code>MMSAddress</code> object which
0907: * fields will be filled with parsed address and
0908: * application id, and correspoinding type (EMAIL, GLOBAL_PHONE_NUMBER,
0909: * IPV4, IPV6, SHORTCODE, APP_ID).
0910: *
0911: * @return <code>MMSAddress </code> object if <code>addressStr</code>
0912: * is a valid mms address; <code> null </code> otherwise
0913: */
0914: public static MMSAddress getParsedMMSAddress(String addressStr,
0915: MMSAddress mmsAddress) {
0916:
0917: if (addressStr == null || !addressStr.startsWith("mms://")) {
0918: return null;
0919: }
0920:
0921: if (mmsAddress == null) {
0922: mmsAddress = new MMSAddress();
0923: }
0924:
0925: if (parsePhoneNumber(addressStr, 6, mmsAddress)
0926: || parseIpv4(addressStr, 6, mmsAddress)
0927: || parseIpv6(addressStr, 6, mmsAddress)
0928: || parseEmail(addressStr, 6, mmsAddress)
0929: || parseShortCode(addressStr, 6, mmsAddress)
0930: || parseApplicationId(addressStr, 6, mmsAddress)) {
0931: return mmsAddress;
0932: }
0933:
0934: return null;
0935: }
0936:
0937: /** Type corresponding to invalid address. */
0938: public static final int INVALID_ADDRESS = -1;
0939:
0940: /** Type corresponding to the e-mail address. */
0941: public static final int EMAIL = 0;
0942:
0943: /** Type corresponding to global phone number address. */
0944: public static final int GLOBAL_PHONE_NUMBER = 1;
0945:
0946: /** Type corresponding to the ipv4 address. */
0947: public static final int IPV4 = 2;
0948:
0949: /** Type corresponding to the ipv6 address. */
0950: public static final int IPV6 = 3;
0951:
0952: /** Type corresponding to the shortcode address. */
0953: public static final int SHORTCODE = 4;
0954:
0955: /** Type corresponding to the application id address. */
0956: public static final int APP_ID = 5;
0957:
0958: /**
0959: * Field that holds address part of the mms address
0960: * (without "mms://" and ":" separator before app id).
0961: */
0962: public String address;
0963:
0964: /**
0965: * Field that holds application id part of the mms address
0966: * which appears after "mms://:" or
0967: * after phone number, ipv4, or ipv6 followed by ":" .
0968: */
0969: public String appId;
0970:
0971: /**
0972: * Type of this MMSAddress instance,
0973: * <code>INVALID_ADDRESS</code> when uninitialized .
0974: */
0975: public int type;
0976:
0977: /**
0978: * MMSAddress constructor to create uninitialized instance.
0979: */
0980: MMSAddress() {
0981: clear();
0982: }
0983:
0984: /**
0985: * MMSAddress constructor to create initialized instance.
0986: * @param address The address part of the mms address
0987: * @param appId The application id part of the mms address
0988: * @param type The type of this mms addreess
0989: * (EMAIL, GLOBAL_PHONE_NUMBER, IPV4, IPV6, SHORTCODE, APP_ID)
0990: */
0991: MMSAddress(String address, String appId, int type) {
0992: set(address, appId, type);
0993: }
0994:
0995: /**
0996: * Clears <code>MMSAddress</code> fields.
0997: * Type is set to INVALID_ADDRESS while
0998: * address and appId are set to null.
0999: */
1000: void clear() {
1001: address = appId = null;
1002: type = INVALID_ADDRESS;
1003: }
1004:
1005: /**
1006: * Sets <code>MMSAddress</code> fields to the passed in values.
1007: * @param address The address part of the mms address
1008: * @param appId The application id part of the mms address
1009: * @param type The type of this mms addreess
1010: * (EMAIL, GLOBAL_PHONE_NUMBER, IPV4, IPV6, SHORTCODE, APP_ID)
1011: */
1012: void set(String address, String appId, int type) {
1013: this .address = address;
1014: this .appId = appId;
1015: this .type = type;
1016: }
1017:
1018: /**
1019: * Sets <code>MMSAddress</code> application id field to
1020: * the passed in value.
1021: * @param appId The application id part of the mms address
1022: */
1023: void setAppid(String appId) {
1024: this .appId = appId;
1025: }
1026:
1027: /**
1028: * Creates a valid mms address corresponding to the values
1029: * in this <code>MMSAddress</code> object.
1030: * If the object is not intialized null is returned.
1031: * @return mms address corresponding to this MMSAddress object.
1032: */
1033: String getMMSAddressString() {
1034:
1035: if (type == INVALID_ADDRESS
1036: || (address == null && appId == null)) {
1037: return null;
1038: }
1039:
1040: StringBuffer mmsAddr = new StringBuffer("mms://");
1041: if (address != null)
1042: mmsAddr.append(address);
1043: if (appId != null)
1044: mmsAddr.append(":").append(appId);
1045: return mmsAddr.toString();
1046: }
1047: }
|