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.midp.io.j2me.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:
0359: int len = s.length();
0360:
0361: /*
0362: * Empty string or string like ":" are not allowed.
0363: * Only 32 characters are allowed.
0364: * Note: When checking appID length, keep in mind that
0365: * i is pointing to ":"
0366: */
0367: if (i <= 0 || i >= len || (len - i - 1) > 32
0368: || (len - i - 1) < 0 || s.charAt(i) != ':') {
0369: return false;
0370: }
0371:
0372: i++; // to skip ':'
0373:
0374: char c;
0375: int initI = i;
0376:
0377: for (int j = 0; i < len; i++) {
0378:
0379: c = s.charAt(i);
0380:
0381: for (j = 0; i < len; i++, j++) {
0382: c = s.charAt(i);
0383:
0384: if ((c != '_') && /* dot is also allowed in the spec ??? */
0385: !digit(c) && !alpha(c)) {
0386: break;
0387: }
0388: }
0389:
0390: if (i == len) {
0391: if (mmsAddress.type == INVALID_ADDRESS) {
0392: mmsAddress.set(null, s.substring(initI), APP_ID);
0393: } else {
0394: mmsAddress.setAppid(s.substring(initI));
0395: }
0396: return true;
0397: }
0398:
0399: // there has to be at least one digit or letter and
0400: // nondigit and nonalpha char can be only dot (one dot only)
0401: if (j == 0 || c != '.') {
0402: return false;
0403: }
0404: }
0405:
0406: return false;
0407: }
0408:
0409: // ------------------------------- E-mail validation ----------------
0410: /**
0411: * Determines whether a character represents a special e-mail character.
0412: * @param c The character to check.
0413: * @return <code>true </code> if <code>c</code> is a special character.
0414: * <code> false </code> otherwise
0415: */
0416: private static boolean specials(char c) {
0417: if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@'
0418: || c == ',' || c == ';' || c == ':' || c == '\\'
0419: || c == '"' || c == '.' || c == '[' || c == ']') {
0420: return true;
0421: }
0422: return false;
0423: }
0424:
0425: /**
0426: * Determines whether a character represents a character.
0427: * @param c The character to check.
0428: * @return <code>true </code> if <code>c</code> is a character.
0429: * <code> false </code> otherwise
0430: */
0431: private static boolean isChar(char c) {
0432: return ((int) c) >= 0 && ((int) c) <= 127;
0433: }
0434:
0435: /**
0436: * Determines whether a character represents an ascii control
0437: * character.
0438: * @param c The character to check.
0439: * @return <code>true </code> if <code>c</code> is a character.
0440: * <code> false </code> otherwise
0441: */
0442: private static boolean asciiControl(char c) {
0443: return ((int) c >= 0 && (int) c <= 31);
0444: }
0445:
0446: /**
0447: * Determines whether a character represents an atom character.
0448: * as specified in WMA 2.0 specification.
0449: * @param c The character to check.
0450: * @return <code>true </code> if <code>c</code> is a special character.
0451: * <code> false </code> otherwise
0452: */
0453: private static boolean atomChar(char c) {
0454: return ((int) c <= 127 && (int) c > 32 && !specials(c));
0455: }
0456:
0457: /**
0458: * Determines first not linear white space character
0459: * in <code>s</code> after character with index <code>i</code> but
0460: * before character with index <code>n</code>.
0461: *
0462: * @param s The string to check.
0463: * @param i The index of the string at which to start the check.
0464: * <code> 0 <= i <= s.length() </code>
0465: * @param n The index of the last character to be checked in
0466: * <code>s</code>.
0467: * @return The first non linear white character after position
0468: * <code>i</code>, it could the the same as passed in
0469: * <code>i</code> value if there are no linear white
0470: * space characters.
0471: */
0472: private static int linearWhiteSpaceSeq(String s, int i, int n) {
0473: char c;
0474: for (; i < n; i++) {
0475: c = s.charAt(i);
0476: if (c == ' ' || c == '\t') {
0477: continue;
0478: }
0479: if (c == '\r') {
0480: // only CR LF one after another are allowed
0481: // CR by itself is not allowed
0482: if (i == n - 1 || s.charAt(i + 1) != '\n') {
0483: return i;
0484: }
0485: i++;
0486: } else {
0487: return i;
0488: }
0489: }
0490:
0491: return i;
0492: }
0493:
0494: /**
0495: * Determines whether a string represents a valid e-mail address
0496: * starting from an index. If the address string is a valid
0497: * e-mail address representation it is parsed into the fields of
0498: * <code>MMSAddress </code>object (address and null for appId).
0499: * E-mail syntax is specified in WMA 2.0 spec.
0500: *
0501: * @param s The string to check.
0502: * @param i The index of the string at which to start the check.
0503: * <code> 0 <= i <= s.length() </code>
0504: * @param mmsAddress The return <code>MMSAddress</code> object which
0505: * fields are filled with parsed address and
0506: * application id (null in this case), its type is set to EMAIL.
0507: *
0508: * @return <code>true </code> if <code>s</code> represents a valid
0509: * e-mail address starting from index <code>i</code>;
0510: * <code> false </code> otherwise and if index <code>i</code> is
0511: * invalide value
0512: */
0513: static boolean parseEmail(String s, int i, MMSAddress mmsAddress) {
0514:
0515: int len = s.length();
0516: if (i <= 0 || i >= len) {
0517: return false;
0518: }
0519:
0520: int initI = i;
0521:
0522: // email can be on of the following:
0523: // emial ::== mailbox | phrase : [#mailbox] ;
0524:
0525: // if there is a ';' at the end then there should be a phrase
0526: // at the beginning
0527: if (s.charAt(len - 1) == ';') {
0528:
0529: if ((i = isPhrase(s, i, len - 1)) <= 0) {
0530: return false;
0531: }
0532:
0533: // there was no ":"
0534: if (i >= len - 1 || s.charAt(i) != ':') {
0535: return false;
0536: }
0537:
0538: i++; // move after ':'
0539:
0540: if (i == len - 1) {
0541: mmsAddress.set(s.substring(initI), null, EMAIL);
0542: return true; // phrase : ; is allowed string
0543: }
0544:
0545: if ((i = isMailbox(s, i, len - 1)) <= 0) {
0546: return false;
0547: }
0548:
0549: while (i < len - 1) {
0550:
0551: // there has to be a ',' sep
0552: if (i >= len - 2 && s.charAt(i) != ',') {
0553: return false;
0554: }
0555:
0556: if ((i = isMailbox(s, i + 1, len - 1)) <= 0) {
0557: return false;
0558: }
0559: }
0560: if (i == len - 1) {
0561: mmsAddress.set(s.substring(initI), null, EMAIL);
0562: return true;
0563: }
0564: return false;
0565: }
0566:
0567: if (isMailbox(s, i, len) == len) {
0568: mmsAddress.set(s.substring(initI), null, EMAIL);
0569: return true;
0570: }
0571: return false;
0572: }
0573:
0574: /**
0575: * Determines whether a string represents a valid mailbox address
0576: * starting from an index.
0577: * Mailbox syntax is specified in WMA 2.0 spec as
0578: * local-part"@"domain or as
0579: * [phrase] "<"[("@"domain) [*("," ("@"domain))]] local-part"@"domain">"
0580: *
0581: * @param s The string to check.
0582: * @param i The index of the string at which to start the check.
0583: * <code> 0 <= i <= s.length() </code>
0584: * @param n The index of the last character to be checked in
0585: * <code>s</code>.
0586: *
0587: * @return index till which
0588: * <code>s</code> contains valid mailbox specification;
0589: * <code> -1 </code> if index <code>i</code> is invalid or
0590: * if characters in <code>s</code> starting from <code>i</code>
0591: * do not comply with mailbox specification
0592: */
0593: private static int isMailbox(String s, int i, int n) {
0594: if (n > s.length()) {
0595: n = s.length();
0596: }
0597: if (i >= n) {
0598: return -1;
0599: }
0600:
0601: int initI = i;
0602:
0603: if ((i = isLocalAtDomain(s, initI, n)) > 0) {
0604: return i;
0605: }
0606:
0607: i = initI;
0608:
0609: // check for [phrase] <[1#(@ domain):] local @ domain>
0610: // note phrase is optional
0611: if ((s.charAt(i) != '<') && (i = isPhrase(s, initI, n)) < 0) {
0612: return -1;
0613: }
0614:
0615: if (i == n || s.charAt(i) != '<') {
0616: return -1;
0617: }
0618:
0619: i++;
0620:
0621: if (i == n) {
0622: return -1;
0623: }
0624:
0625: // there can be a list of domains:
0626: // @ domain, @domain, @domain :
0627: boolean atLeastOneDomain = false;
0628: while (s.charAt(i) == '@') {
0629: atLeastOneDomain = true;
0630:
0631: if ((i = isDomain(s, i + 1, n)) < 0) {
0632: return -1;
0633: }
0634:
0635: if (i == n) {
0636: return -1;
0637: }
0638:
0639: if (s.charAt(i) == ':') {
0640: break;
0641: }
0642:
0643: // @domain,,@domain is allowed
0644: while (s.charAt(i) == ',') {
0645: i++;
0646: if (i == n || s.charAt(i) == ':') {
0647: return -1;
0648: }
0649: }
0650: }
0651:
0652: if (atLeastOneDomain) {
0653: if (i == n || s.charAt(i) != ':') {
0654: return -1;
0655: }
0656: i++;
0657: }
0658:
0659: // local @ domain
0660: if ((i = isLocalAtDomain(s, i, n)) <= 0) {
0661: return -1;
0662: }
0663:
0664: if (i < n && s.charAt(i) == '>') {
0665: return i + 1;
0666: }
0667: return -1;
0668: }
0669:
0670: /**
0671: * Determines whether a string represents a valid "local-part"
0672: * starting from an index.
0673: * Local-part syntax is specified in WMA 2.0 spec as
0674: * (atom | "\"alpha) *("." (atom | "\"alpha)) where
0675: * atom is represented by at least one char which is
0676: * not a space or a special or control character.
0677: *
0678: * @param s The string to check.
0679: * @param i The index of the string at which to start the check
0680: * <code> 0 <= i <= s.length() </code>
0681: * @param n The index of the last character to be checked in
0682: * <code>s</code>.
0683: *
0684: * @return index till which
0685: * <code>s</code> contains valid local-part specification;
0686: * <code> -1 </code> if index <code>i</code> is invalid or
0687: * if characters in <code>s</code> starting from <code>i</code>
0688: * do not comply with local-part specification
0689: */
0690: private static int isLocalPart(String s, int i, int n) {
0691: return isSequenceOfAtomAndText(s, i, n, '"', '"', true);
0692: }
0693:
0694: /**
0695: * Determines whether a string represents a valid "phrase"
0696: * starting from an index.
0697: * Phrase syntax is specified in WMA 2.0 spec as
0698: * 1*(atom | """ *(text w/o ",\,\r but with linear space | "\"alpha) """)
0699: * where atom is represented by at least one char or more which is
0700: * not a space or a special or control character.
0701: *
0702: * @param s The string to check.
0703: * @param i The index of the string at which to start the check
0704: * <code> 0 <= i <= s.length() </code>
0705: * @param n The index of the last character to be checked in
0706: * <code>s</code>.
0707: *
0708: * @return index till which
0709: * <code>s</code> contains valid phrase specification;
0710: * <code> -1 </code> if index <code>i</code> is invalid or
0711: * if characters in <code>s</code> starting from <code>i</code>
0712: * do not comply with phrase specification
0713: */
0714: private static int isPhrase(String s, int i, int n) {
0715: return isSequenceOfAtomAndText(s, i, n, '"', '"', false);
0716: }
0717:
0718: /**
0719: * Determines whether a string represents a valid "domain"
0720: * starting from an index.
0721: * Domain syntax is specified in WMA 2.0 spec as
0722: * 1*(atom | "[" *(text w/o ",[,],\r but with linear space | "\"alpha) "]")
0723: * where atom is represented by at least one char or more which is
0724: * not a space or a special or control character.
0725: *
0726: * @param s The string to check.
0727: * @param i The index of the string at which to start the check
0728: * <code> 0 <= i <= s.length() </code>
0729: * @param n The index of the last character to be checked in
0730: * <code>s</code>.
0731: *
0732: * @return index till which
0733: * <code>s</code> contains valid domain specification;
0734: * <code> -1 </code> if index <code>i</code> is invalid or
0735: * if characters in <code>s</code> starting from <code>i</code>
0736: * do not comply with domain specification
0737: */
0738: private static int isDomain(String s, int i, int n) {
0739: return isSequenceOfAtomAndText(s, i, n, '[', ']', true);
0740: }
0741:
0742: /**
0743: * Determines whether a string starting from an index
0744: * represents a valid sequence that satisfies the following
0745: * syntax:
0746: * text ::== <begSep>
0747: * *(1*(<any char w/o begSep and eSep incl LWSP>|\ALPHA))
0748: * <endSep>
0749: * sequence ::== 1*(atom | text) | (atom | text) *("." (atom | text))
0750: * where atom is represented by at least one or more characters that
0751: * are not space or special or control characters
0752: * and begSep/endSep are separators that are placed around text.
0753: * LWSP is a linear white space (space and tabs and \r\n but not \r).
0754: *
0755: * @param s The string to check.
0756: * @param i The index of the string at which to start the check
0757: * <code> 0 <= i <= s.length() </code>
0758: * @param n The index of the last character to be checked in
0759: * <code>s</code>.
0760: * @param begSep The character marking beginning of quoted text
0761: * @param endSep The character marking the end of the text
0762: * @param dotSeparated If true atom and quoted text should be
0763: * separated by a "." character; ".." is not allowed
0764: *
0765: * @return index till which
0766: * <code>s</code> contains valid sequence;
0767: * <code> -1 </code> if index <code>i</code> is invalid or
0768: * if characters in <code>s</code> starting from <code>i</code>
0769: * do not comply with the sequence specification.
0770: */
0771: private static int isSequenceOfAtomAndText(String s, int i, int n,
0772: char begSep, char endSep, boolean dotSeparated) {
0773: if (i >= s.length()) {
0774: return -1;
0775: }
0776: if (n > s.length()) {
0777: n = s.length();
0778: }
0779:
0780: // see if there is at least one word
0781: boolean withinQuotation = false;
0782: boolean atLeastOneWord = false;
0783:
0784: char c;
0785: int initI = i;
0786:
0787: for (; i < n; i++) {
0788: c = s.charAt(i);
0789: if (c == begSep) {
0790: if (begSep == endSep) {
0791: withinQuotation = !withinQuotation;
0792: } else if (withinQuotation) {
0793: return -1;
0794: } else {
0795: withinQuotation = true;
0796: }
0797: atLeastOneWord = true;
0798: } else if (c == endSep) {
0799: if (!withinQuotation) {
0800: return -1;
0801: }
0802: withinQuotation = false;
0803: } else if (c == '\\') {
0804: // chars can be quoated only within separators
0805: // and there has to be another char after '\'
0806: if (!withinQuotation || i == n - 1) {
0807: return -1;
0808: }
0809: i++;
0810: c = s.charAt(i);
0811: // only ALPHA character can be quoted
0812: if (!alpha(c)) {
0813: return -1;
0814: }
0815:
0816: } else if (dotSeparated && c == '.') {
0817: if (i == initI
0818: || ((i - initI) > 0 && s.charAt(i - 1) == '.')) {
0819: // empty word
0820: // string starts with .
0821: return -1;
0822: }
0823: } else if (withinQuotation) {
0824: // within quotation \r is not allowed but
0825: // \r is allowed as part of linear-white-space
0826: if (c == ' ' || c == '\t' || c == '\r') {
0827: if ((i = linearWhiteSpaceSeq(s, i, n)) < 0) {
0828: return -1;
0829: }
0830: i--;
0831: }
0832: } else if (atomChar(c)) {
0833: atLeastOneWord = true;
0834: } else {
0835: // this is supposed to be an atom
0836: break;
0837: }
0838: }
0839:
0840: // open quotation or no words or
0841: // dot separator is the last char
0842: if (withinQuotation
0843: || !atLeastOneWord
0844: || (dotSeparated && i > initI && s.charAt(i - 1) == '.')) {
0845: return -1;
0846: }
0847:
0848: return i;
0849: }
0850:
0851: /**
0852: * Determines whether a string starting from an index satisfies the
0853: * following syntax: local-part"@"domain
0854: *
0855: * @param s The string to check.
0856: * @param i The index of the string at which to start the check.
0857: * <code> 0 <= i <= s.length() </code>
0858: * @param n The index of the last character to be checked in
0859: * <code>s</code>.
0860: *
0861: * @return index till which
0862: * <code>s</code> contains valid mailbox specification;
0863: * <code> -1 </code> if index <code>i</code> is invalid or
0864: * if characters in <code>s</code> starting from <code>i</code>
0865: * is not in the form of local-part"@"domain
0866: */
0867: private static int isLocalAtDomain(String s, int i, int n) {
0868: if (n > s.length()) {
0869: n = s.length();
0870: }
0871: if (i >= n) {
0872: return -1;
0873: }
0874:
0875: // local
0876: if ((i = isLocalPart(s, i, n)) <= 0) {
0877: return -1;
0878: }
0879:
0880: if (i == n || s.charAt(i) != '@') {
0881: return -1;
0882: }
0883:
0884: i++;
0885:
0886: // domain
0887: return isDomain(s, i, n);
0888: }
0889:
0890: /**
0891: * Determines whether a string represents a valid mms address
0892: * as specified in WMA 2.0 specification and parses the
0893: * incoming string into address and application id strings.
0894: *
0895: * @param addressStr The string to check.
0896: *
0897: * @return newly created <code>MMSAddress </code> object
0898: * if <code>addressStr</code> is a valid mms address;
0899: * <code> null </code> otherwise
0900: */
0901: public static MMSAddress getParsedMMSAddress(String addressStr) {
0902: return getParsedMMSAddress(addressStr, null);
0903: }
0904:
0905: /**
0906: * Determines whether a string represents a valid mms address
0907: * as specified in WMA 2.0 specification and parses the
0908: * incoming string into address and application id strings.
0909: *
0910: * @param addressStr The string to check.
0911: * @param mmsAddress The return <code>MMSAddress</code> object which
0912: * fields will be filled with parsed address and
0913: * application id, and correspoinding type (EMAIL, GLOBAL_PHONE_NUMBER,
0914: * IPV4, IPV6, SHORTCODE, APP_ID).
0915: *
0916: * @return <code>MMSAddress </code> object if <code>addressStr</code>
0917: * is a valid mms address; <code> null </code> otherwise
0918: */
0919: public static MMSAddress getParsedMMSAddress(String addressStr,
0920: MMSAddress mmsAddress) {
0921:
0922: if (addressStr == null || !addressStr.startsWith("mms://")) {
0923: return null;
0924: }
0925:
0926: if (mmsAddress == null) {
0927: mmsAddress = new MMSAddress();
0928: }
0929:
0930: if (parsePhoneNumber(addressStr, 6, mmsAddress)
0931: || parseIpv4(addressStr, 6, mmsAddress)
0932: || parseIpv6(addressStr, 6, mmsAddress)
0933: || parseEmail(addressStr, 6, mmsAddress)
0934: || parseShortCode(addressStr, 6, mmsAddress)
0935: || parseApplicationId(addressStr, 6, mmsAddress)) {
0936: return mmsAddress;
0937: }
0938:
0939: return null;
0940: }
0941:
0942: /** Type corresponding to invalid address. */
0943: public static final int INVALID_ADDRESS = -1;
0944:
0945: /** Type corresponding to the e-mail address. */
0946: public static final int EMAIL = 0;
0947:
0948: /** Type corresponding to global phone number address. */
0949: public static final int GLOBAL_PHONE_NUMBER = 1;
0950:
0951: /** Type corresponding to the ipv4 address. */
0952: public static final int IPV4 = 2;
0953:
0954: /** Type corresponding to the ipv6 address. */
0955: public static final int IPV6 = 3;
0956:
0957: /** Type corresponding to the shortcode address. */
0958: public static final int SHORTCODE = 4;
0959:
0960: /** Type corresponding to the application id address. */
0961: public static final int APP_ID = 5;
0962:
0963: /**
0964: * Field that holds address part of the mms address
0965: * (without "mms://" and ":" separator before app id).
0966: */
0967: public String address;
0968:
0969: /**
0970: * Field that holds application id part of the mms address
0971: * which appears after "mms://:" or
0972: * after phone number, ipv4, or ipv6 followed by ":" .
0973: */
0974: public String appId;
0975:
0976: /**
0977: * Type of this MMSAddress instance,
0978: * <code>INVALID_ADDRESS</code> when uninitialized .
0979: */
0980: public int type;
0981:
0982: /**
0983: * MMSAddress constructor to create uninitialized instance.
0984: */
0985: MMSAddress() {
0986: clear();
0987: }
0988:
0989: /**
0990: * MMSAddress constructor to create initialized instance.
0991: * @param address The address part of the mms address
0992: * @param appId The application id part of the mms address
0993: * @param type The type of this mms addreess
0994: * (EMAIL, GLOBAL_PHONE_NUMBER, IPV4, IPV6, SHORTCODE, APP_ID)
0995: */
0996: MMSAddress(String address, String appId, int type) {
0997: set(address, appId, type);
0998: }
0999:
1000: /**
1001: * Clears <code>MMSAddress</code> fields.
1002: * Type is set to INVALID_ADDRESS while
1003: * address and appId are set to null.
1004: */
1005: void clear() {
1006: address = appId = null;
1007: type = INVALID_ADDRESS;
1008: }
1009:
1010: /**
1011: * Sets <code>MMSAddress</code> fields to the passed in values.
1012: * @param address The address part of the mms address
1013: * @param appId The application id part of the mms address
1014: * @param type The type of this mms addreess
1015: * (EMAIL, GLOBAL_PHONE_NUMBER, IPV4, IPV6, SHORTCODE, APP_ID)
1016: */
1017: void set(String address, String appId, int type) {
1018: this .address = address;
1019: this .appId = appId;
1020: this .type = type;
1021: }
1022:
1023: /**
1024: * Sets <code>MMSAddress</code> application id field to
1025: * the passed in value.
1026: * @param appId The application id part of the mms address
1027: */
1028: void setAppid(String appId) {
1029: this .appId = appId;
1030: }
1031:
1032: /**
1033: * Creates a valid mms address corresponding to the values
1034: * in this <code>MMSAddress</code> object.
1035: * If the object is not intialized null is returned.
1036: * @return mms address corresponding to this MMSAddress object.
1037: */
1038: String getMMSAddressString() {
1039:
1040: if (type == INVALID_ADDRESS
1041: || (address == null && appId == null)) {
1042: return null;
1043: }
1044:
1045: StringBuffer mmsAddr = new StringBuffer("mms://");
1046: if (address != null)
1047: mmsAddr.append(address);
1048: if (appId != null)
1049: mmsAddr.append(":").append(appId);
1050: return mmsAddr.toString();
1051: }
1052: }
|