Advertisement
Guest User

Java's Internet Email Address parsing method

a guest
Sep 18th, 2014
252
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.96 KB | None | 0 0
  1.     /*
  2.      * RFC822 Address parser.
  3.      *
  4.      * XXX - This is complex enough that it ought to be a real parser,
  5.      *       not this ad-hoc mess, and because of that, this is not perfect.
  6.      *
  7.      * XXX - Deal with encoded Headers too.
  8.      */
  9.     private static InternetAddress[] parse(String s, boolean strict,
  10.                     boolean parseHdr) throws AddressException {
  11.     int start, end, index, nesting;
  12.     int start_personal = -1, end_personal = -1;
  13.     int length = s.length();
  14.     boolean ignoreErrors = parseHdr && !strict;
  15.     boolean in_group = false;   // we're processing a group term
  16.     boolean route_addr = false; // address came from route-addr term
  17.     boolean rfc822 = false;     // looks like an RFC822 address
  18.     char c;
  19.     List v = new ArrayList();
  20.     InternetAddress ma;
  21.  
  22.     for (start = end = -1, index = 0; index < length; index++) {
  23.             c = s.charAt(index);
  24.  
  25.         switch (c) {
  26.         case '(': // We are parsing a Comment. Ignore everything inside.
  27.         // XXX - comment fields should be parsed as whitespace,
  28.         //   more than one allowed per address
  29.         rfc822 = true;
  30.         if (start >= 0 && end == -1)
  31.             end = index;
  32.         int pindex = index;
  33.         for (index++, nesting = 1; index < length && nesting > 0;
  34.           index++) {
  35.             c = s.charAt(index);
  36.             switch (c) {
  37.             case '\\':
  38.             index++; // skip both '\' and the escaped char
  39.             break;
  40.             case '(':
  41.             nesting++;
  42.             break;
  43.             case ')':
  44.             nesting--;
  45.             break;
  46.             default:
  47.             break;
  48.             }
  49.         }
  50.         if (nesting > 0) {
  51.             if (!ignoreErrors)
  52.             throw new AddressException("Missing ')'", s, index);
  53.             // pretend the first paren was a regular character and
  54.             // continue parsing after it
  55.             index = pindex + 1;
  56.             break;
  57.         }
  58.         index--;    // point to closing paren
  59.         if (start_personal == -1)
  60.             start_personal = pindex + 1;
  61.         if (end_personal == -1)
  62.             end_personal = index;
  63.         break;
  64.  
  65.         case ')':
  66.         if (!ignoreErrors)
  67.             throw new AddressException("Missing '('", s, index);
  68.         // pretend the left paren was a regular character and
  69.         // continue parsing
  70.         if (start == -1)
  71.             start = index;
  72.         break;
  73.  
  74.         case '<':
  75.         rfc822 = true;
  76.         if (route_addr) {
  77.             if (!ignoreErrors)
  78.             throw new AddressException(
  79.                         "Extra route-addr", s, index);
  80.  
  81.             // assume missing comma between addresses
  82.             if (start == -1) {
  83.             route_addr = false;
  84.             rfc822 = false;
  85.             start = end = -1;
  86.             break;  // nope, nothing there
  87.             }
  88.             if (!in_group) {
  89.             // got a token, add this to our InternetAddress vector
  90.             if (end == -1)  // should never happen
  91.                 end = index;
  92.             String addr = s.substring(start, end).trim();
  93.  
  94.             ma = new InternetAddress();
  95.             ma.setAddress(addr);
  96.             if (start_personal >= 0) {
  97.                 ma.encodedPersonal = unquote(
  98.                 s.substring(start_personal, end_personal).
  99.                                 trim());
  100.             }
  101.             v.add(ma);
  102.  
  103.             route_addr = false;
  104.             rfc822 = false;
  105.             start = end = -1;
  106.             start_personal = end_personal = -1;
  107.             // continue processing this new address...
  108.             }
  109.         }
  110.  
  111.         int rindex = index;
  112.         boolean inquote = false;
  113.           outf:
  114.         for (index++; index < length; index++) {
  115.             c = s.charAt(index);
  116.             switch (c) {
  117.             case '\\':  // XXX - is this needed?
  118.             index++; // skip both '\' and the escaped char
  119.             break;
  120.             case '"':
  121.             inquote = !inquote;
  122.             break;
  123.             case '>':
  124.             if (inquote)
  125.                 continue;
  126.             break outf; // out of for loop
  127.             default:
  128.             break;
  129.             }
  130.         }
  131.  
  132.         // did we find a matching quote?
  133.         if (inquote) {
  134.             if (!ignoreErrors)
  135.             throw new AddressException("Missing '\"'", s, index);
  136.             // didn't find matching quote, try again ignoring quotes
  137.             // (e.g., ``<"@foo.com>'')
  138.           outq:
  139.             for (index = rindex + 1; index < length; index++) {
  140.             c = s.charAt(index);
  141.             if (c == '\\')  // XXX - is this needed?
  142.                 index++;    // skip both '\' and the escaped char
  143.             else if (c == '>')
  144.                 break;
  145.             }
  146.         }
  147.  
  148.         // did we find a terminating '>'?
  149.         if (index >= length) {
  150.             if (!ignoreErrors)
  151.             throw new AddressException("Missing '>'", s, index);
  152.             // pretend the "<" was a regular character and
  153.             // continue parsing after it (e.g., ``<@foo.com'')
  154.             index = rindex + 1;
  155.             if (start == -1)
  156.             start = rindex; // back up to include "<"
  157.             break;
  158.         }
  159.  
  160.         if (!in_group) {
  161.             start_personal = start;
  162.             if (start_personal >= 0)
  163.             end_personal = rindex;
  164.             start = rindex + 1;
  165.         }
  166.         route_addr = true;
  167.         end = index;
  168.         break;
  169.  
  170.         case '>':
  171.         if (!ignoreErrors)
  172.             throw new AddressException("Missing '<'", s, index);
  173.         // pretend the ">" was a regular character and
  174.         // continue parsing (e.g., ``>@foo.com'')
  175.         if (start == -1)
  176.             start = index;
  177.         break;
  178.  
  179.         case '"':   // parse quoted string
  180.         int qindex = index;
  181.         rfc822 = true;
  182.         if (start == -1)
  183.             start = index;
  184.           outq:
  185.         for (index++; index < length; index++) {
  186.             c = s.charAt(index);
  187.             switch (c) {
  188.             case '\\':
  189.             index++; // skip both '\' and the escaped char
  190.             break;
  191.             case '"':
  192.             break outq; // out of for loop
  193.             default:
  194.             break;
  195.             }
  196.         }
  197.         if (index >= length) {
  198.             if (!ignoreErrors)
  199.             throw new AddressException("Missing '\"'", s, index);
  200.             // pretend the quote was a regular character and
  201.             // continue parsing after it (e.g., ``"@foo.com'')
  202.             index = qindex + 1;
  203.         }
  204.         break;
  205.  
  206.         case '[':   // a domain-literal, probably
  207.         rfc822 = true;
  208.         int lindex = index;
  209.           outb:
  210.         for (index++; index < length; index++) {
  211.             c = s.charAt(index);
  212.             switch (c) {
  213.             case '\\':
  214.             index++; // skip both '\' and the escaped char
  215.             break;
  216.             case ']':
  217.             break outb; // out of for loop
  218.             default:
  219.             break;
  220.             }
  221.         }
  222.         if (index >= length) {
  223.             if (!ignoreErrors)
  224.             throw new AddressException("Missing ']'", s, index);
  225.             // pretend the "[" was a regular character and
  226.             // continue parsing after it (e.g., ``[@foo.com'')
  227.             index = lindex + 1;
  228.         }
  229.         break;
  230.  
  231.         case ';':
  232.         if (start == -1) {
  233.             route_addr = false;
  234.             rfc822 = false;
  235.             start = end = -1;
  236.             break;  // nope, nothing there
  237.         }
  238.         if (in_group) {
  239.             in_group = false;
  240.             /*
  241.              * If parsing headers, but not strictly, peek ahead.
  242.              * If next char is "@", treat the group name
  243.              * like the local part of the address, e.g.,
  244.              * "Undisclosed-Recipient:;@java.sun.com".
  245.              */
  246.             if (parseHdr && !strict &&
  247.                 index + 1 < length && s.charAt(index + 1) == '@')
  248.             break;
  249.             ma = new InternetAddress();
  250.             end = index + 1;
  251.             ma.setAddress(s.substring(start, end).trim());
  252.             v.add(ma);
  253.  
  254.             route_addr = false;
  255.             rfc822 = false;
  256.             start = end = -1;
  257.             start_personal = end_personal = -1;
  258.             break;
  259.         }
  260.         if (!ignoreErrors)
  261.             throw new AddressException(
  262.                 "Illegal semicolon, not in group", s, index);
  263.  
  264.         // otherwise, parsing a header; treat semicolon like comma
  265.         // fall through to comma case...
  266.  
  267.         case ',':   // end of an address, probably
  268.         if (start == -1) {
  269.             route_addr = false;
  270.             rfc822 = false;
  271.             start = end = -1;
  272.             break;  // nope, nothing there
  273.         }
  274.         if (in_group) {
  275.             route_addr = false;
  276.             break;
  277.         }
  278.         // got a token, add this to our InternetAddress vector
  279.         if (end == -1)
  280.             end = index;
  281.  
  282.         String addr = s.substring(start, end).trim();
  283.         String pers = null;
  284.         if (rfc822 && start_personal >= 0) {
  285.             pers = unquote(
  286.                 s.substring(start_personal, end_personal).trim());
  287.             if (pers.trim().length() == 0)
  288.             pers = null;
  289.         }
  290.  
  291.         /*
  292.          * If the personal name field has an "@" and the address
  293.          * field does not, assume they were reversed, e.g.,
  294.          * ``"joe doe" (john.doe@example.com)''.
  295.          */
  296.         if (parseHdr && !strict && pers != null &&
  297.             pers.indexOf('@') >= 0 &&
  298.             addr.indexOf('@') < 0 && addr.indexOf('!') < 0) {
  299.             String tmp = addr;
  300.             addr = pers;
  301.             pers = tmp;
  302.         }
  303.         if (rfc822 || strict || parseHdr) {
  304.             if (!ignoreErrors)
  305.             checkAddress(addr, route_addr, false);
  306.             ma = new InternetAddress();
  307.             ma.setAddress(addr);
  308.             if (pers != null)
  309.             ma.encodedPersonal = pers;
  310.             v.add(ma);
  311.         } else {
  312.             // maybe we passed over more than one space-separated addr
  313.             StringTokenizer st = new StringTokenizer(addr);
  314.             while (st.hasMoreTokens()) {
  315.             String a = st.nextToken();
  316.             checkAddress(a, false, false);
  317.             ma = new InternetAddress();
  318.             ma.setAddress(a);
  319.             v.add(ma);
  320.             }
  321.         }
  322.  
  323.         route_addr = false;
  324.         rfc822 = false;
  325.         start = end = -1;
  326.         start_personal = end_personal = -1;
  327.         break;
  328.  
  329.         case ':':
  330.         rfc822 = true;
  331.         if (in_group)
  332.             if (!ignoreErrors)
  333.             throw new AddressException("Nested group", s, index);
  334.         if (start == -1)
  335.             start = index;
  336.         if (parseHdr && !strict) {
  337.             /*
  338.              * If next char is a special character that can't occur at
  339.              * the start of a valid address, treat the group name
  340.              * as the entire address, e.g., "Date:, Tue", "Re:@foo".
  341.              */
  342.             if (index + 1 < length) {
  343.             String addressSpecials = ")>[]:@\\,.";
  344.             char nc = s.charAt(index + 1);
  345.             if (addressSpecials.indexOf(nc) >= 0) {
  346.                 if (nc != '@')
  347.                 break;  // don't change in_group
  348.                 /*
  349.                  * Handle a common error:
  350.                  * ``Undisclosed-Recipient:@example.com;''
  351.                  *
  352.                  * Scan ahead.  If we find a semicolon before
  353.                  * one of these other special characters,
  354.                  * consider it to be a group after all.
  355.                  */
  356.                 for (int i = index + 2; i < length; i++) {
  357.                 nc = s.charAt(i);
  358.                 if (nc == ';')
  359.                     break;
  360.                 if (addressSpecials.indexOf(nc) >= 0)
  361.                     break;
  362.                 }
  363.                 if (nc == ';')
  364.                 break;  // don't change in_group
  365.             }
  366.             }
  367.  
  368.             // ignore bogus "mailto:" prefix in front of an address,
  369.             // or bogus mail header name included in the address field
  370.             String gname = s.substring(start, index);
  371.             if (ignoreBogusGroupName &&
  372.             (gname.equalsIgnoreCase("mailto") ||
  373.             gname.equalsIgnoreCase("From") ||
  374.             gname.equalsIgnoreCase("To") ||
  375.             gname.equalsIgnoreCase("Cc") ||
  376.             gname.equalsIgnoreCase("Subject") ||
  377.             gname.equalsIgnoreCase("Re")))
  378.             start = -1; // we're not really in a group
  379.             else
  380.             in_group = true;
  381.         } else
  382.             in_group = true;
  383.         break;
  384.  
  385.         // Ignore whitespace
  386.         case ' ':
  387.         case '\t':
  388.         case '\r':
  389.         case '\n':
  390.         break;
  391.  
  392.         default:
  393.         if (start == -1)
  394.             start = index;
  395.         break;
  396.          }
  397.     }
  398.  
  399.     if (start >= 0) {
  400.         /*
  401.          * The last token, add this to our InternetAddress vector.
  402.          * Note that this block of code should be identical to the
  403.          * block above for "case ','".
  404.          */
  405.         if (end == -1)
  406.         end = length;
  407.  
  408.         String addr = s.substring(start, end).trim();
  409.         String pers = null;
  410.         if (rfc822 && start_personal >= 0) {
  411.         pers = unquote(
  412.             s.substring(start_personal, end_personal).trim());
  413.         if (pers.trim().length() == 0)
  414.             pers = null;
  415.         }
  416.  
  417.         /*
  418.          * If the personal name field has an "@" and the address
  419.          * field does not, assume they were reversed, e.g.,
  420.          * ``"joe doe" (john.doe@example.com)''.
  421.          */
  422.         if (parseHdr && !strict &&
  423.             pers != null && pers.indexOf('@') >= 0 &&
  424.             addr.indexOf('@') < 0 && addr.indexOf('!') < 0) {
  425.         String tmp = addr;
  426.         addr = pers;
  427.         pers = tmp;
  428.         }
  429.         if (rfc822 || strict || parseHdr) {
  430.         if (!ignoreErrors)
  431.             checkAddress(addr, route_addr, false);
  432.         ma = new InternetAddress();
  433.         ma.setAddress(addr);
  434.         if (pers != null)
  435.             ma.encodedPersonal = pers;
  436.         v.add(ma);
  437.         } else {
  438.         // maybe we passed over more than one space-separated addr
  439.         StringTokenizer st = new StringTokenizer(addr);
  440.         while (st.hasMoreTokens()) {
  441.             String a = st.nextToken();
  442.             checkAddress(a, false, false);
  443.             ma = new InternetAddress();
  444.             ma.setAddress(a);
  445.             v.add(ma);
  446.         }
  447.         }
  448.     }
  449.  
  450.     InternetAddress[] a = new InternetAddress[v.size()];
  451.     v.toArray(a);
  452.     return a;
  453.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement