web/lib/Zend/Validate/Hostname.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Validate
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version    $Id: Hostname.php 22830 2010-08-12 16:05:09Z thomas $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_Validate_Abstract
       
    24  */
       
    25 require_once 'Zend/Validate/Abstract.php';
       
    26 
       
    27 /**
       
    28  * @see Zend_Validate_Ip
       
    29  */
       
    30 require_once 'Zend/Validate/Ip.php';
       
    31 
       
    32 /**
       
    33  * Please note there are two standalone test scripts for testing IDN characters due to problems
       
    34  * with file encoding.
       
    35  *
       
    36  * The first is tests/Zend/Validate/HostnameTestStandalone.php which is designed to be run on
       
    37  * the command line.
       
    38  *
       
    39  * The second is tests/Zend/Validate/HostnameTestForm.php which is designed to be run via HTML
       
    40  * to allow users to test entering UTF-8 characters in a form.
       
    41  *
       
    42  * @category   Zend
       
    43  * @package    Zend_Validate
       
    44  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    45  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    46  */
       
    47 class Zend_Validate_Hostname extends Zend_Validate_Abstract
       
    48 {
       
    49     const CANNOT_DECODE_PUNYCODE  = 'hostnameCannotDecodePunycode';
       
    50     const INVALID                 = 'hostnameInvalid';
       
    51     const INVALID_DASH            = 'hostnameDashCharacter';
       
    52     const INVALID_HOSTNAME        = 'hostnameInvalidHostname';
       
    53     const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
       
    54     const INVALID_LOCAL_NAME      = 'hostnameInvalidLocalName';
       
    55     const INVALID_URI             = 'hostnameInvalidUri';
       
    56     const IP_ADDRESS_NOT_ALLOWED  = 'hostnameIpAddressNotAllowed';
       
    57     const LOCAL_NAME_NOT_ALLOWED  = 'hostnameLocalNameNotAllowed';
       
    58     const UNDECIPHERABLE_TLD      = 'hostnameUndecipherableTld';
       
    59     const UNKNOWN_TLD             = 'hostnameUnknownTld';
       
    60 
       
    61     /**
       
    62      * @var array
       
    63      */
       
    64     protected $_messageTemplates = array(
       
    65         self::CANNOT_DECODE_PUNYCODE  => "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded",
       
    66         self::INVALID                 => "Invalid type given. String expected",
       
    67         self::INVALID_DASH            => "'%value%' appears to be a DNS hostname but contains a dash in an invalid position",
       
    68         self::INVALID_HOSTNAME        => "'%value%' does not match the expected structure for a DNS hostname",
       
    69         self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
       
    70         self::INVALID_LOCAL_NAME      => "'%value%' does not appear to be a valid local network name",
       
    71         self::INVALID_URI             => "'%value%' does not appear to be a valid URI hostname",
       
    72         self::IP_ADDRESS_NOT_ALLOWED  => "'%value%' appears to be an IP address, but IP addresses are not allowed",
       
    73         self::LOCAL_NAME_NOT_ALLOWED  => "'%value%' appears to be a local network name but local network names are not allowed",
       
    74         self::UNDECIPHERABLE_TLD      => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
       
    75         self::UNKNOWN_TLD             => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
       
    76     );
       
    77 
       
    78     /**
       
    79      * @var array
       
    80      */
       
    81     protected $_messageVariables = array(
       
    82         'tld' => '_tld'
       
    83     );
       
    84 
       
    85     /**
       
    86      * Allows Internet domain names (e.g., example.com)
       
    87      */
       
    88     const ALLOW_DNS   = 1;
       
    89 
       
    90     /**
       
    91      * Allows IP addresses
       
    92      */
       
    93     const ALLOW_IP    = 2;
       
    94 
       
    95     /**
       
    96      * Allows local network names (e.g., localhost, www.localdomain)
       
    97      */
       
    98     const ALLOW_LOCAL = 4;
       
    99 
       
   100     /**
       
   101      * Allows all types of hostnames
       
   102      */
       
   103     const ALLOW_ALL   = 7;
       
   104 
       
   105     /**
       
   106      * Allows all types of hostnames
       
   107      */
       
   108     const ALLOW_URI = 8;
       
   109 
       
   110     /**
       
   111      * Array of valid top-level-domains
       
   112      *
       
   113      * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt  List of all TLDs by domain
       
   114      * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs
       
   115      * @var array
       
   116      */
       
   117     protected $_validTlds = array(
       
   118         'ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'arpa',
       
   119         'as', 'asia', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi',
       
   120         'biz', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cat', 'cc',
       
   121         'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop', 'cr', 'cu',
       
   122         'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'edu', 'ee', 'eg', 'er',
       
   123         'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg',
       
   124         'gh', 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk',
       
   125         'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'info', 'int', 'io', 'iq',
       
   126         'ir', 'is', 'it', 'je', 'jm', 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp',
       
   127         'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly',
       
   128         'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'mp',
       
   129         'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc',
       
   130         'ne', 'net', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', 'pa', 'pe',
       
   131         'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'pro', 'ps', 'pt', 'pw', 'py', 'qa', 're',
       
   132         'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl',
       
   133         'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th',
       
   134         'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw', 'tz', 'ua',
       
   135         'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws',
       
   136         'ye', 'yt', 'yu', 'za', 'zm', 'zw'
       
   137     );
       
   138 
       
   139     /**
       
   140      * @var string
       
   141      */
       
   142     protected $_tld;
       
   143 
       
   144     /**
       
   145      * Array for valid Idns
       
   146      * @see http://www.iana.org/domains/idn-tables/ Official list of supported IDN Chars
       
   147      * (.AC) Ascension Island http://www.nic.ac/pdf/AC-IDN-Policy.pdf
       
   148      * (.AR) Argentinia http://www.nic.ar/faqidn.html
       
   149      * (.AS) American Samoa http://www.nic.as/idn/chars.cfm
       
   150      * (.AT) Austria http://www.nic.at/en/service/technical_information/idn/charset_converter/
       
   151      * (.BIZ) International http://www.iana.org/domains/idn-tables/
       
   152      * (.BR) Brazil http://registro.br/faq/faq6.html
       
   153      * (.BV) Bouvett Island http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
       
   154      * (.CAT) Catalan http://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html
       
   155      * (.CH) Switzerland https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
       
   156      * (.CL) Chile http://www.iana.org/domains/idn-tables/tables/cl_latn_1.0.html
       
   157      * (.COM) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
       
   158      * (.DE) Germany http://www.denic.de/en/domains/idns/liste.html
       
   159      * (.DK) Danmark http://www.dk-hostmaster.dk/index.php?id=151
       
   160      * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf
       
   161      * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html
       
   162      * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp
       
   163      * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html
       
   164      * (.INFO) International http://www.nic.info/info/idn
       
   165      * (.IO) British Indian Ocean Territory http://www.nic.io/IO-IDN-Policy.pdf
       
   166      * (.IR) Iran http://www.nic.ir/Allowable_Characters_dot-iran
       
   167      * (.IS) Iceland http://www.isnic.is/domain/rules.php
       
   168      * (.KR) Korea http://www.iana.org/domains/idn-tables/tables/kr_ko-kr_1.0.html
       
   169      * (.LI) Liechtenstein https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
       
   170      * (.LT) Lithuania http://www.domreg.lt/static/doc/public/idn_symbols-en.pdf
       
   171      * (.MD) Moldova http://www.register.md/
       
   172      * (.MUSEUM) International http://www.iana.org/domains/idn-tables/tables/museum_latn_1.0.html
       
   173      * (.NET) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
       
   174      * (.NO) Norway http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
       
   175      * (.NU) Niue http://www.worldnames.net/
       
   176      * (.ORG) International http://www.pir.org/index.php?db=content/FAQs&tbl=FAQs_Registrant&id=2
       
   177      * (.PE) Peru https://www.nic.pe/nuevas_politicas_faq_2.php
       
   178      * (.PL) Poland http://www.dns.pl/IDN/allowed_character_sets.pdf
       
   179      * (.PR) Puerto Rico http://www.nic.pr/idn_rules.asp
       
   180      * (.PT) Portugal https://online.dns.pt/dns_2008/do?com=DS;8216320233;111;+PAGE(4000058)+K-CAT-CODIGO(C.125)+RCNT(100);
       
   181      * (.RU) Russia http://www.iana.org/domains/idn-tables/tables/ru_ru-ru_1.0.html
       
   182      * (.SA) Saudi Arabia http://www.iana.org/domains/idn-tables/tables/sa_ar_1.0.html
       
   183      * (.SE) Sweden http://www.iis.se/english/IDN_campaignsite.shtml?lang=en
       
   184      * (.SH) Saint Helena http://www.nic.sh/SH-IDN-Policy.pdf
       
   185      * (.SJ) Svalbard and Jan Mayen http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
       
   186      * (.TH) Thailand http://www.iana.org/domains/idn-tables/tables/th_th-th_1.0.html
       
   187      * (.TM) Turkmenistan http://www.nic.tm/TM-IDN-Policy.pdf
       
   188      * (.TR) Turkey https://www.nic.tr/index.php
       
   189      * (.VE) Venice http://www.iana.org/domains/idn-tables/tables/ve_es_1.0.html
       
   190      * (.VN) Vietnam http://www.vnnic.vn/english/5-6-300-2-2-04-20071115.htm#1.%20Introduction
       
   191      *
       
   192      * @var array
       
   193      */
       
   194     protected $_validIdns = array(
       
   195         'AC'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu'),
       
   196         'AR'  => array(1 => '/^[\x{002d}0-9a-zà-ãç-êìíñ-õü]{1,63}$/iu'),
       
   197         'AS'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$/iu'),
       
   198         'AT'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœšž]{1,63}$/iu'),
       
   199         'BIZ' => 'Hostname/Biz.php',
       
   200         'BR'  => array(1 => '/^[\x{002d}0-9a-zà-ãçéíó-õúü]{1,63}$/iu'),
       
   201         'BV'  => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
       
   202         'CAT' => array(1 => '/^[\x{002d}0-9a-z·àç-éíïòóúü]{1,63}$/iu'),
       
   203         'CH'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu'),
       
   204         'CL'  => array(1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu'),
       
   205         'CN'  => 'Hostname/Cn.php',
       
   206         'COM' => 'Zend/Validate/Hostname/Com.php',
       
   207         'DE'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
       
   208         'DK'  => array(1 => '/^[\x{002d}0-9a-zäéöü]{1,63}$/iu'),
       
   209         'ES'  => array(1 => '/^[\x{002d}0-9a-zàáçèéíïñòóúü·]{1,63}$/iu'),
       
   210         'EU'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
       
   211             2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
       
   212             3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu',
       
   213             4 => '/^[\x{002d}0-9a-zΐάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ]{1,63}$/iu',
       
   214             5 => '/^[\x{002d}0-9a-zабвгдежзийклмнопрстуфхцчшщъыьэюя]{1,63}$/iu',
       
   215             6 => '/^[\x{002d}0-9a-zἀ-ἇἐ-ἕἠ-ἧἰ-ἷὀ-ὅὐ-ὗὠ-ὧὰ-ώᾀ-ᾇᾐ-ᾗᾠ-ᾧᾰ-ᾴᾶᾷῂῃῄῆῇῐ-ΐῖῗῠ-ῧῲῳῴῶῷ]{1,63}$/iu'),
       
   216         'FI'  => array(1 => '/^[\x{002d}0-9a-zäåö]{1,63}$/iu'),
       
   217         'GR'  => array(1 => '/^[\x{002d}0-9a-zΆΈΉΊΌΎ-ΡΣ-ώἀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼῂῃῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲῳῴῶ-ῼ]{1,63}$/iu'),
       
   218         'HK'  => 'Zend/Validate/Hostname/Cn.php',
       
   219         'HU'  => array(1 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu'),
       
   220         'INFO'=> array(1 => '/^[\x{002d}0-9a-zäåæéöøü]{1,63}$/iu',
       
   221             2 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
       
   222             3 => '/^[\x{002d}0-9a-záæéíðóöúýþ]{1,63}$/iu',
       
   223             4 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
       
   224             5 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
       
   225             6 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
       
   226             7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
       
   227             8 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu'),
       
   228         'IO'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
       
   229         'IS'  => array(1 => '/^[\x{002d}0-9a-záéýúíóþæöð]{1,63}$/iu'),
       
   230         'JP'  => 'Zend/Validate/Hostname/Jp.php',
       
   231         'KR'  => array(1 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu'),
       
   232         'LI'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿœ]{1,63}$/iu'),
       
   233         'LT'  => array(1 => '/^[\x{002d}0-9ąčęėįšųūž]{1,63}$/iu'),
       
   234         'MD'  => array(1 => '/^[\x{002d}0-9ăâîşţ]{1,63}$/iu'),
       
   235         'MUSEUM' => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćċčďđēėęěğġģħīįıķĺļľłńņňŋōőœŕŗřśşšţťŧūůűųŵŷźżžǎǐǒǔ\x{01E5}\x{01E7}\x{01E9}\x{01EF}ə\x{0292}ẁẃẅỳ]{1,63}$/iu'),
       
   236         'NET' => 'Zend/Validate/Hostname/Com.php',
       
   237         'NO'  => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
       
   238         'NU'  => 'Zend/Validate/Hostname/Com.php',
       
   239         'ORG' => array(1 => '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
       
   240             2 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
       
   241             3 => '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
       
   242             4 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu',
       
   243             5 => '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
       
   244             6 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
       
   245             7 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu'),
       
   246         'PE'  => array(1 => '/^[\x{002d}0-9a-zñáéíóúü]{1,63}$/iu'),
       
   247         'PL'  => array(1 => '/^[\x{002d}0-9a-zāčēģīķļņōŗšūž]{1,63}$/iu',
       
   248             2 => '/^[\x{002d}а-ик-ш\x{0450}ѓѕјљњќџ]{1,63}$/iu',
       
   249             3 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
       
   250             4 => '/^[\x{002d}0-9а-яё\x{04C2}]{1,63}$/iu',
       
   251             5 => '/^[\x{002d}0-9a-zàáâèéêìíîòóôùúûċġħż]{1,63}$/iu',
       
   252             6 => '/^[\x{002d}0-9a-zàäåæéêòóôöøü]{1,63}$/iu',
       
   253             7 => '/^[\x{002d}0-9a-zóąćęłńśźż]{1,63}$/iu',
       
   254             8 => '/^[\x{002d}0-9a-zàáâãçéêíòóôõúü]{1,63}$/iu',
       
   255             9 => '/^[\x{002d}0-9a-zâîăşţ]{1,63}$/iu',
       
   256             10=> '/^[\x{002d}0-9a-záäéíóôúýčďĺľňŕšťž]{1,63}$/iu',
       
   257             11=> '/^[\x{002d}0-9a-zçë]{1,63}$/iu',
       
   258             12=> '/^[\x{002d}0-9а-ик-шђјљњћџ]{1,63}$/iu',
       
   259             13=> '/^[\x{002d}0-9a-zćčđšž]{1,63}$/iu',
       
   260             14=> '/^[\x{002d}0-9a-zâçöûüğış]{1,63}$/iu',
       
   261             15=> '/^[\x{002d}0-9a-záéíñóúü]{1,63}$/iu',
       
   262             16=> '/^[\x{002d}0-9a-zäõöüšž]{1,63}$/iu',
       
   263             17=> '/^[\x{002d}0-9a-zĉĝĥĵŝŭ]{1,63}$/iu',
       
   264             18=> '/^[\x{002d}0-9a-zâäéëîô]{1,63}$/iu',
       
   265             19=> '/^[\x{002d}0-9a-zàáâäåæçèéêëìíîïðñòôöøùúûüýćčłńřśš]{1,63}$/iu',
       
   266             20=> '/^[\x{002d}0-9a-zäåæõöøüšž]{1,63}$/iu',
       
   267             21=> '/^[\x{002d}0-9a-zàáçèéìíòóùú]{1,63}$/iu',
       
   268             22=> '/^[\x{002d}0-9a-zàáéíóöúüőű]{1,63}$/iu',
       
   269             23=> '/^[\x{002d}0-9ΐά-ώ]{1,63}$/iu',
       
   270             24=> '/^[\x{002d}0-9a-zàáâåæçèéêëðóôöøüþœ]{1,63}$/iu',
       
   271             25=> '/^[\x{002d}0-9a-záäéíóöúüýčďěňřšťůž]{1,63}$/iu',
       
   272             26=> '/^[\x{002d}0-9a-z·àçèéíïòóúü]{1,63}$/iu',
       
   273             27=> '/^[\x{002d}0-9а-ъьюя\x{0450}\x{045D}]{1,63}$/iu',
       
   274             28=> '/^[\x{002d}0-9а-яёіў]{1,63}$/iu',
       
   275             29=> '/^[\x{002d}0-9a-ząčėęįšūųž]{1,63}$/iu',
       
   276             30=> '/^[\x{002d}0-9a-záäåæéëíðóöøúüýþ]{1,63}$/iu',
       
   277             31=> '/^[\x{002d}0-9a-zàâæçèéêëîïñôùûüÿœ]{1,63}$/iu',
       
   278             32=> '/^[\x{002d}0-9а-щъыьэюяёєіїґ]{1,63}$/iu',
       
   279             33=> '/^[\x{002d}0-9א-ת]{1,63}$/iu'),
       
   280         'PR'  => array(1 => '/^[\x{002d}0-9a-záéíóúñäëïüöâêîôûàèùæçœãõ]{1,63}$/iu'),
       
   281         'PT'  => array(1 => '/^[\x{002d}0-9a-záàâãçéêíóôõú]{1,63}$/iu'),
       
   282         'RU'  => array(1 => '/^[\x{002d}0-9а-яё]{1,63}$/iu'),
       
   283         'SA'  => array(1 => '/^[\x{002d}.0-9\x{0621}-\x{063A}\x{0641}-\x{064A}\x{0660}-\x{0669}]{1,63}$/iu'),
       
   284         'SE'  => array(1 => '/^[\x{002d}0-9a-zäåéöü]{1,63}$/iu'),
       
   285         'SH'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿăąāćĉčċďđĕěėęēğĝġģĥħĭĩįīıĵķĺľļłńňņŋŏőōœĸŕřŗśŝšşťţŧŭůűũųūŵŷźžż]{1,63}$/iu'),
       
   286         'SJ'  => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
       
   287         'TH'  => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
       
   288         'TM'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu'),
       
   289         'TW'  => 'Zend/Validate/Hostname/Cn.php',
       
   290         'TR'  => array(1 => '/^[\x{002d}0-9a-zğıüşöç]{1,63}$/iu'),
       
   291         'VE'  => array(1 => '/^[\x{002d}0-9a-záéíóúüñ]{1,63}$/iu'),
       
   292         'VN'  => array(1 => '/^[ÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚÝàáâãèéêìíòóôõùúýĂăĐđĨĩŨũƠơƯư\x{1EA0}-\x{1EF9}]{1,63}$/iu'),
       
   293         'ایران' => array(1 => '/^[\x{0621}-\x{0624}\x{0626}-\x{063A}\x{0641}\x{0642}\x{0644}-\x{0648}\x{067E}\x{0686}\x{0698}\x{06A9}\x{06AF}\x{06CC}\x{06F0}-\x{06F9}]{1,30}$/iu'),
       
   294         '中国' => 'Zend/Validate/Hostname/Cn.php',
       
   295         '公司' => 'Zend/Validate/Hostname/Cn.php',
       
   296         '网络' => 'Zend/Validate/Hostname/Cn.php'
       
   297     );
       
   298 
       
   299     protected $_idnLength = array(
       
   300         'BIZ' => array(5 => 17, 11 => 15, 12 => 20),
       
   301         'CN'  => array(1 => 20),
       
   302         'COM' => array(3 => 17, 5 => 20),
       
   303         'HK'  => array(1 => 15),
       
   304         'INFO'=> array(4 => 17),
       
   305         'KR'  => array(1 => 17),
       
   306         'NET' => array(3 => 17, 5 => 20),
       
   307         'ORG' => array(6 => 17),
       
   308         'TW'  => array(1 => 20),
       
   309         'ایران' => array(1 => 30),
       
   310         '中国' => array(1 => 20),
       
   311         '公司' => array(1 => 20),
       
   312         '网络' => array(1 => 20),
       
   313     );
       
   314 
       
   315     protected $_options = array(
       
   316         'allow' => self::ALLOW_DNS,
       
   317         'idn'   => true,
       
   318         'tld'   => true,
       
   319         'ip'    => null
       
   320     );
       
   321 
       
   322     /**
       
   323      * Sets validator options
       
   324      *
       
   325      * @param integer          $allow       OPTIONAL Set what types of hostname to allow (default ALLOW_DNS)
       
   326      * @param boolean          $validateIdn OPTIONAL Set whether IDN domains are validated (default true)
       
   327      * @param boolean          $validateTld OPTIONAL Set whether the TLD element of a hostname is validated (default true)
       
   328      * @param Zend_Validate_Ip $ipValidator OPTIONAL
       
   329      * @return void
       
   330      * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm  Technical Specifications for ccTLDs
       
   331      */
       
   332     public function __construct($options = array())
       
   333     {
       
   334         if ($options instanceof Zend_Config) {
       
   335             $options = $options->toArray();
       
   336         } else if (!is_array($options)) {
       
   337             $options = func_get_args();
       
   338             $temp['allow'] = array_shift($options);
       
   339             if (!empty($options)) {
       
   340                 $temp['idn'] = array_shift($options);
       
   341             }
       
   342 
       
   343             if (!empty($options)) {
       
   344                 $temp['tld'] = array_shift($options);
       
   345             }
       
   346 
       
   347             if (!empty($options)) {
       
   348                 $temp['ip'] = array_shift($options);
       
   349             }
       
   350 
       
   351             $options = $temp;
       
   352         }
       
   353 
       
   354         $options += $this->_options;
       
   355         $this->setOptions($options);
       
   356     }
       
   357 
       
   358     /**
       
   359      * Returns all set options
       
   360      *
       
   361      * @return array
       
   362      */
       
   363     public function getOptions()
       
   364     {
       
   365         return $this->_options;
       
   366     }
       
   367 
       
   368     /**
       
   369      * Sets the options for this validator
       
   370      *
       
   371      * @param array $options
       
   372      * @return Zend_Validate_Hostname
       
   373      */
       
   374     public function setOptions($options)
       
   375     {
       
   376         if (array_key_exists('allow', $options)) {
       
   377             $this->setAllow($options['allow']);
       
   378         }
       
   379 
       
   380         if (array_key_exists('idn', $options)) {
       
   381             $this->setValidateIdn($options['idn']);
       
   382         }
       
   383 
       
   384         if (array_key_exists('tld', $options)) {
       
   385             $this->setValidateTld($options['tld']);
       
   386         }
       
   387 
       
   388         if (array_key_exists('ip', $options)) {
       
   389             $this->setIpValidator($options['ip']);
       
   390         }
       
   391 
       
   392         return $this;
       
   393     }
       
   394 
       
   395     /**
       
   396      * Returns the set ip validator
       
   397      *
       
   398      * @return Zend_Validate_Ip
       
   399      */
       
   400     public function getIpValidator()
       
   401     {
       
   402         return $this->_options['ip'];
       
   403     }
       
   404 
       
   405     /**
       
   406      * @param Zend_Validate_Ip $ipValidator OPTIONAL
       
   407      * @return void;
       
   408      */
       
   409     public function setIpValidator(Zend_Validate_Ip $ipValidator = null)
       
   410     {
       
   411         if ($ipValidator === null) {
       
   412             $ipValidator = new Zend_Validate_Ip();
       
   413         }
       
   414 
       
   415         $this->_options['ip'] = $ipValidator;
       
   416         return $this;
       
   417     }
       
   418 
       
   419     /**
       
   420      * Returns the allow option
       
   421      *
       
   422      * @return integer
       
   423      */
       
   424     public function getAllow()
       
   425     {
       
   426         return $this->_options['allow'];
       
   427     }
       
   428 
       
   429     /**
       
   430      * Sets the allow option
       
   431      *
       
   432      * @param  integer $allow
       
   433      * @return Zend_Validate_Hostname Provides a fluent interface
       
   434      */
       
   435     public function setAllow($allow)
       
   436     {
       
   437         $this->_options['allow'] = $allow;
       
   438         return $this;
       
   439     }
       
   440 
       
   441     /**
       
   442      * Returns the set idn option
       
   443      *
       
   444      * @return boolean
       
   445      */
       
   446     public function getValidateIdn()
       
   447     {
       
   448         return $this->_options['idn'];
       
   449     }
       
   450 
       
   451     /**
       
   452      * Set whether IDN domains are validated
       
   453      *
       
   454      * This only applies when DNS hostnames are validated
       
   455      *
       
   456      * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
       
   457      */
       
   458     public function setValidateIdn ($allowed)
       
   459     {
       
   460         $this->_options['idn'] = (bool) $allowed;
       
   461         return $this;
       
   462     }
       
   463 
       
   464     /**
       
   465      * Returns the set tld option
       
   466      *
       
   467      * @return boolean
       
   468      */
       
   469     public function getValidateTld()
       
   470     {
       
   471         return $this->_options['tld'];
       
   472     }
       
   473 
       
   474     /**
       
   475      * Set whether the TLD element of a hostname is validated
       
   476      *
       
   477      * This only applies when DNS hostnames are validated
       
   478      *
       
   479      * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
       
   480      */
       
   481     public function setValidateTld ($allowed)
       
   482     {
       
   483         $this->_options['tld'] = (bool) $allowed;
       
   484         return $this;
       
   485     }
       
   486 
       
   487     /**
       
   488      * Defined by Zend_Validate_Interface
       
   489      *
       
   490      * Returns true if and only if the $value is a valid hostname with respect to the current allow option
       
   491      *
       
   492      * @param  string $value
       
   493      * @throws Zend_Validate_Exception if a fatal error occurs for validation process
       
   494      * @return boolean
       
   495      */
       
   496     public function isValid($value)
       
   497     {
       
   498         if (!is_string($value)) {
       
   499             $this->_error(self::INVALID);
       
   500             return false;
       
   501         }
       
   502 
       
   503         $this->_setValue($value);
       
   504         // Check input against IP address schema
       
   505         if (preg_match('/^[0-9.a-e:.]*$/i', $value) &&
       
   506             $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
       
   507             if (!($this->_options['allow'] & self::ALLOW_IP)) {
       
   508                 $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
       
   509                 return false;
       
   510             } else {
       
   511                 return true;
       
   512             }
       
   513         }
       
   514 
       
   515         // Check input against DNS hostname schema
       
   516         $domainParts = explode('.', $value);
       
   517         if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
       
   518             $status = false;
       
   519 
       
   520             $origenc = iconv_get_encoding('internal_encoding');
       
   521             iconv_set_encoding('internal_encoding', 'UTF-8');
       
   522             do {
       
   523                 // First check TLD
       
   524                 $matches = array();
       
   525                 if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
       
   526                     (end($domainParts) == 'ایران') || (end($domainParts) == '中国') ||
       
   527                     (end($domainParts) == '公司') || (end($domainParts) == '网络')) {
       
   528 
       
   529                     reset($domainParts);
       
   530 
       
   531                     // Hostname characters are: *(label dot)(label dot label); max 254 chars
       
   532                     // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
       
   533                     // id-prefix: alpha / digit
       
   534                     // ldh: alpha / digit / dash
       
   535 
       
   536                     // Match TLD against known list
       
   537                     $this->_tld = strtolower($matches[1]);
       
   538                     if ($this->_options['tld']) {
       
   539                         if (!in_array($this->_tld, $this->_validTlds)) {
       
   540                             $this->_error(self::UNKNOWN_TLD);
       
   541                             $status = false;
       
   542                             break;
       
   543                         }
       
   544                     }
       
   545 
       
   546                     /**
       
   547                      * Match against IDN hostnames
       
   548                      * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
       
   549                      * @see Zend_Validate_Hostname_Interface
       
   550                      */
       
   551                     $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
       
   552                     if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
       
   553                         if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
       
   554                             $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
       
   555                         } else {
       
   556                             $regexChars += $this->_validIdns[strtoupper($this->_tld)];
       
   557                         }
       
   558                     }
       
   559 
       
   560                     // Check each hostname part
       
   561                     $check = 0;
       
   562                     foreach ($domainParts as $domainPart) {
       
   563                         // Decode Punycode domainnames to IDN
       
   564                         if (strpos($domainPart, 'xn--') === 0) {
       
   565                             $domainPart = $this->decodePunycode(substr($domainPart, 4));
       
   566                             if ($domainPart === false) {
       
   567                                 return false;
       
   568                             }
       
   569                         }
       
   570 
       
   571                         // Check dash (-) does not start, end or appear in 3rd and 4th positions
       
   572                         if ((strpos($domainPart, '-') === 0)
       
   573                             || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
       
   574                             || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
       
   575                                 $this->_error(self::INVALID_DASH);
       
   576                             $status = false;
       
   577                             break 2;
       
   578                         }
       
   579 
       
   580                         // Check each domain part
       
   581                         $checked = false;
       
   582                         foreach($regexChars as $regexKey => $regexChar) {
       
   583                             $status = @preg_match($regexChar, $domainPart);
       
   584                             if ($status > 0) {
       
   585                                 $length = 63;
       
   586                                 if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
       
   587                                     && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
       
   588                                     $length = $this->_idnLength[strtoupper($this->_tld)];
       
   589                                 }
       
   590 
       
   591                                 if (iconv_strlen($domainPart, 'UTF-8') > $length) {
       
   592                                     $this->_error(self::INVALID_HOSTNAME);
       
   593                                 } else {
       
   594                                     $checked = true;
       
   595                                     break;
       
   596                                 }
       
   597                             }
       
   598                         }
       
   599 
       
   600                         if ($checked) {
       
   601                             ++$check;
       
   602                         }
       
   603                     }
       
   604 
       
   605                     // If one of the labels doesn't match, the hostname is invalid
       
   606                     if ($check !== count($domainParts)) {
       
   607                         $this->_error(self::INVALID_HOSTNAME_SCHEMA);
       
   608                         $status = false;
       
   609                     }
       
   610                 } else {
       
   611                     // Hostname not long enough
       
   612                     $this->_error(self::UNDECIPHERABLE_TLD);
       
   613                     $status = false;
       
   614                 }
       
   615             } while (false);
       
   616 
       
   617             iconv_set_encoding('internal_encoding', $origenc);
       
   618             // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
       
   619             // passes validation
       
   620             if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
       
   621                 return true;
       
   622             }
       
   623         } else if ($this->_options['allow'] & self::ALLOW_DNS) {
       
   624             $this->_error(self::INVALID_HOSTNAME);
       
   625         }
       
   626 
       
   627         // Check for URI Syntax (RFC3986)
       
   628         if ($this->_options['allow'] & self::ALLOW_URI) {
       
   629             if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
       
   630                 return true;
       
   631             } else {
       
   632                 $this->_error(self::INVALID_URI);
       
   633             }
       
   634         }
       
   635 
       
   636         // Check input against local network name schema; last chance to pass validation
       
   637         $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}){1,254}$/';
       
   638         $status = @preg_match($regexLocal, $value);
       
   639 
       
   640         // If the input passes as a local network name, and local network names are allowed, then the
       
   641         // hostname passes validation
       
   642         $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
       
   643         if ($status && $allowLocal) {
       
   644             return true;
       
   645         }
       
   646 
       
   647         // If the input does not pass as a local network name, add a message
       
   648         if (!$status) {
       
   649             $this->_error(self::INVALID_LOCAL_NAME);
       
   650         }
       
   651 
       
   652         // If local network names are not allowed, add a message
       
   653         if ($status && !$allowLocal) {
       
   654             $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
       
   655         }
       
   656 
       
   657         return false;
       
   658     }
       
   659 
       
   660     /**
       
   661      * Decodes a punycode encoded string to it's original utf8 string
       
   662      * In case of a decoding failure the original string is returned
       
   663      *
       
   664      * @param  string $encoded Punycode encoded string to decode
       
   665      * @return string
       
   666      */
       
   667     protected function decodePunycode($encoded)
       
   668     {
       
   669         $found = preg_match('/([^a-z0-9\x2d]{1,10})$/i', $encoded);
       
   670         if (empty($encoded) || ($found > 0)) {
       
   671             // no punycode encoded string, return as is
       
   672             $this->_error(self::CANNOT_DECODE_PUNYCODE);
       
   673             return false;
       
   674         }
       
   675 
       
   676         $separator = strrpos($encoded, '-');
       
   677         if ($separator > 0) {
       
   678             for ($x = 0; $x < $separator; ++$x) {
       
   679                 // prepare decoding matrix
       
   680                 $decoded[] = ord($encoded[$x]);
       
   681             }
       
   682         } else {
       
   683             $this->_error(self::CANNOT_DECODE_PUNYCODE);
       
   684             return false;
       
   685         }
       
   686 
       
   687         $lengthd = count($decoded);
       
   688         $lengthe = strlen($encoded);
       
   689 
       
   690         // decoding
       
   691         $init  = true;
       
   692         $base  = 72;
       
   693         $index = 0;
       
   694         $char  = 0x80;
       
   695 
       
   696         for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
       
   697             for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
       
   698                 $hex   = ord($encoded[$indexe++]);
       
   699                 $digit = ($hex - 48 < 10) ? $hex - 22
       
   700                        : (($hex - 65 < 26) ? $hex - 65
       
   701                        : (($hex - 97 < 26) ? $hex - 97
       
   702                        : 36));
       
   703 
       
   704                 $index += $digit * $pos;
       
   705                 $tag    = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
       
   706                 if ($digit < $tag) {
       
   707                     break;
       
   708                 }
       
   709 
       
   710                 $pos = (int) ($pos * (36 - $tag));
       
   711             }
       
   712 
       
   713             $delta   = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
       
   714             $delta  += intval($delta / ($lengthd + 1));
       
   715             for ($key = 0; $delta > 910 / 2; $key += 36) {
       
   716                 $delta = intval($delta / 35);
       
   717             }
       
   718 
       
   719             $base   = intval($key + 36 * $delta / ($delta + 38));
       
   720             $init   = false;
       
   721             $char  += (int) ($index / ($lengthd + 1));
       
   722             $index %= ($lengthd + 1);
       
   723             if ($lengthd > 0) {
       
   724                 for ($i = $lengthd; $i > $index; $i--) {
       
   725                     $decoded[$i] = $decoded[($i - 1)];
       
   726                 }
       
   727             }
       
   728 
       
   729             $decoded[$index++] = $char;
       
   730         }
       
   731 
       
   732         // convert decoded ucs4 to utf8 string
       
   733         foreach ($decoded as $key => $value) {
       
   734             if ($value < 128) {
       
   735                 $decoded[$key] = chr($value);
       
   736             } elseif ($value < (1 << 11)) {
       
   737                 $decoded[$key]  = chr(192 + ($value >> 6));
       
   738                 $decoded[$key] .= chr(128 + ($value & 63));
       
   739             } elseif ($value < (1 << 16)) {
       
   740                 $decoded[$key]  = chr(224 + ($value >> 12));
       
   741                 $decoded[$key] .= chr(128 + (($value >> 6) & 63));
       
   742                 $decoded[$key] .= chr(128 + ($value & 63));
       
   743             } elseif ($value < (1 << 21)) {
       
   744                 $decoded[$key]  = chr(240 + ($value >> 18));
       
   745                 $decoded[$key] .= chr(128 + (($value >> 12) & 63));
       
   746                 $decoded[$key] .= chr(128 + (($value >> 6) & 63));
       
   747                 $decoded[$key] .= chr(128 + ($value & 63));
       
   748             } else {
       
   749                 $this->_error(self::CANNOT_DECODE_PUNYCODE);
       
   750                 return false;
       
   751             }
       
   752         }
       
   753 
       
   754         return implode($decoded);
       
   755     }
       
   756 }