PageRenderTime 59ms CodeModel.GetById 22ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/blog/www/system/library/Zend/Validate/Hostname.php

https://bitbucket.org/vmihailenco/vladimirwebdev
PHP | 734 lines | 439 code | 66 blank | 229 comment | 73 complexity | fa0c0e3cb0e313e965e024b698cf91df MD5 | raw file
  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 20096 2010-01-06 02:05:09Z bkarwin $
 20 */
 21
 22/**
 23 * @see Zend_Validate_Abstract
 24 */
 25require_once 'Zend/Validate/Abstract.php';
 26
 27/**
 28 * @see Zend_Validate_Ip
 29 */
 30require_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 */
 47class Zend_Validate_Hostname extends Zend_Validate_Abstract
 48{
 49    const INVALID                 = 'hostnameInvalid';
 50    const IP_ADDRESS_NOT_ALLOWED  = 'hostnameIpAddressNotAllowed';
 51    const UNKNOWN_TLD             = 'hostnameUnknownTld';
 52    const INVALID_DASH            = 'hostnameDashCharacter';
 53    const INVALID_HOSTNAME_SCHEMA = 'hostnameInvalidHostnameSchema';
 54    const UNDECIPHERABLE_TLD      = 'hostnameUndecipherableTld';
 55    const INVALID_HOSTNAME        = 'hostnameInvalidHostname';
 56    const INVALID_LOCAL_NAME      = 'hostnameInvalidLocalName';
 57    const LOCAL_NAME_NOT_ALLOWED  = 'hostnameLocalNameNotAllowed';
 58    const CANNOT_DECODE_PUNYCODE  = 'hostnameCannotDecodePunycode';
 59
 60    /**
 61     * @var array
 62     */
 63    protected $_messageTemplates = array(
 64        self::INVALID                 => "Invalid type given, value should be a string",
 65        self::IP_ADDRESS_NOT_ALLOWED  => "'%value%' appears to be an IP address, but IP addresses are not allowed",
 66        self::UNKNOWN_TLD             => "'%value%' appears to be a DNS hostname but cannot match TLD against known list",
 67        self::INVALID_DASH            => "'%value%' appears to be a DNS hostname but contains a dash in an invalid position",
 68        self::INVALID_HOSTNAME_SCHEMA => "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'",
 69        self::UNDECIPHERABLE_TLD      => "'%value%' appears to be a DNS hostname but cannot extract TLD part",
 70        self::INVALID_HOSTNAME        => "'%value%' does not match the expected structure for a DNS hostname",
 71        self::INVALID_LOCAL_NAME      => "'%value%' does not appear to be a valid local network name",
 72        self::LOCAL_NAME_NOT_ALLOWED  => "'%value%' appears to be a local network name but local network names are not allowed",
 73        self::CANNOT_DECODE_PUNYCODE  => "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded"
 74    );
 75
 76    /**
 77     * @var array
 78     */
 79    protected $_messageVariables = array(
 80        'tld' => '_tld'
 81    );
 82
 83    /**
 84     * Allows Internet domain names (e.g., example.com)
 85     */
 86    const ALLOW_DNS   = 1;
 87
 88    /**
 89     * Allows IP addresses
 90     */
 91    const ALLOW_IP    = 2;
 92
 93    /**
 94     * Allows local network names (e.g., localhost, www.localdomain)
 95     */
 96    const ALLOW_LOCAL = 4;
 97
 98    /**
 99     * Allows all types of hostnames
100     */
101    const ALLOW_ALL   = 7;
102
103    /**
104     * Array of valid top-level-domains
105     *
106     * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt  List of all TLDs by domain
107     * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs
108     * @var array
109     */
110    protected $_validTlds = array(
111        'ac', 'ad', 'ae', 'aero', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'arpa',
112        'as', 'asia', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi',
113        'biz', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cat', 'cc',
114        'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'com', 'coop', 'cr', 'cu',
115        'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'edu', 'ee', 'eg', 'er',
116        'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg',
117        'gh', 'gi', 'gl', 'gm', 'gn', 'gov', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk',
118        'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'info', 'int', 'io', 'iq',
119        'ir', 'is', 'it', 'je', 'jm', 'jo', 'jobs', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp',
120        'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly',
121        'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mil', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'mp',
122        'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'name', 'nc',
123        'ne', 'net', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'org', 'pa', 'pe',
124        'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'pro', 'ps', 'pt', 'pw', 'py', 'qa', 're',
125        'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl',
126        'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tel', 'tf', 'tg', 'th',
127        'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'travel', 'tt', 'tv', 'tw', 'tz', 'ua',
128        'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws',
129        'ye', 'yt', 'yu', 'za', 'zm', 'zw'
130    );
131
132    /**
133     * @var string
134     */
135    protected $_tld;
136
137    /**
138     * Array for valid Idns
139     * @see http://www.iana.org/domains/idn-tables/ Official list of supported IDN Chars
140     * (.AC) Ascension Island http://www.nic.ac/pdf/AC-IDN-Policy.pdf
141     * (.AR) Argentinia http://www.nic.ar/faqidn.html
142     * (.AS) American Samoa http://www.nic.as/idn/chars.cfm
143     * (.AT) Austria http://www.nic.at/en/service/technical_information/idn/charset_converter/
144     * (.BIZ) International http://www.iana.org/domains/idn-tables/
145     * (.BR) Brazil http://registro.br/faq/faq6.html
146     * (.BV) Bouvett Island http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
147     * (.CAT) Catalan http://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html
148     * (.CH) Switzerland https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
149     * (.CL) Chile http://www.iana.org/domains/idn-tables/tables/cl_latn_1.0.html
150     * (.COM) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
151     * (.DE) Germany http://www.denic.de/en/domains/idns/liste.html
152     * (.DK) Danmark http://www.dk-hostmaster.dk/index.php?id=151
153     * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf
154     * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html
155     * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp
156     * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html
157     * (.INFO) International http://www.nic.info/info/idn
158     * (.IO) British Indian Ocean Territory http://www.nic.io/IO-IDN-Policy.pdf
159     * (.IR) Iran http://www.nic.ir/Allowable_Characters_dot-iran
160     * (.IS) Iceland http://www.isnic.is/domain/rules.php
161     * (.KR) Korea http://www.iana.org/domains/idn-tables/tables/kr_ko-kr_1.0.html
162     * (.LI) Liechtenstein https://nic.switch.ch/reg/ocView.action?res=EF6GW2JBPVTG67DLNIQXU234MN6SC33JNQQGI7L6#anhang1
163     * (.LT) Lithuania http://www.domreg.lt/static/doc/public/idn_symbols-en.pdf
164     * (.MD) Moldova http://www.register.md/
165     * (.MUSEUM) International http://www.iana.org/domains/idn-tables/tables/museum_latn_1.0.html
166     * (.NET) International http://www.verisign.com/information-services/naming-services/internationalized-domain-names/index.html
167     * (.NO) Norway http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
168     * (.NU) Niue http://www.worldnames.net/
169     * (.ORG) International http://www.pir.org/index.php?db=content/FAQs&tbl=FAQs_Registrant&id=2
170     * (.PE) Peru https://www.nic.pe/nuevas_politicas_faq_2.php
171     * (.PL) Poland http://www.dns.pl/IDN/allowed_character_sets.pdf
172     * (.PR) Puerto Rico http://www.nic.pr/idn_rules.asp
173     * (.PT) Portugal https://online.dns.pt/dns_2008/do?com=DS;8216320233;111;+PAGE(4000058)+K-CAT-CODIGO(C.125)+RCNT(100);
174     * (.RU) Russia http://www.iana.org/domains/idn-tables/tables/ru_ru-ru_1.0.html
175     * (.SA) Saudi Arabia http://www.iana.org/domains/idn-tables/tables/sa_ar_1.0.html
176     * (.SE) Sweden http://www.iis.se/english/IDN_campaignsite.shtml?lang=en
177     * (.SH) Saint Helena http://www.nic.sh/SH-IDN-Policy.pdf
178     * (.SJ) Svalbard and Jan Mayen http://www.norid.no/domeneregistrering/idn/idn_nyetegn.en.html
179     * (.TH) Thailand http://www.iana.org/domains/idn-tables/tables/th_th-th_1.0.html
180     * (.TM) Turkmenistan http://www.nic.tm/TM-IDN-Policy.pdf
181     * (.TR) Turkey https://www.nic.tr/index.php
182     * (.VE) Venice http://www.iana.org/domains/idn-tables/tables/ve_es_1.0.html
183     * (.VN) Vietnam http://www.vnnic.vn/english/5-6-300-2-2-04-20071115.htm#1.%20Introduction
184     *
185     * @var array
186     */
187    protected $_validIdns = array(
188        'AC'  => array(1 => '/^[\x{002d}0-9a-z�-��-�????????????????????????????????�??????�????????????�]{1,63}$/iu'),
189        'AR'  => array(1 => '/^[\x{002d}0-9a-z�-��-����-��]{1,63}$/iu'),
190        'AS'  => array(1 => '/^[\x{002d}0-9a-z�-��-�???????????????????????????????????????�??????�?????????????]{1,63}$/iu'),
191        'AT'  => array(1 => '/^[\x{002d}0-9a-z�-��-����]{1,63}$/iu'),
192        'BIZ' => 'Hostname/Biz.php',
193        'BR'  => array(1 => '/^[\x{002d}0-9a-z�-�����-���]{1,63}$/iu'),
194        'BV'  => array(1 => '/^[\x{002d}0-9a-z���-���-����????�?�]{1,63}$/iu'),
195        'CAT' => array(1 => '/^[\x{002d}0-9a-z���-�������]{1,63}$/iu'),
196        'CH'  => array(1 => '/^[\x{002d}0-9a-z�-��-��]{1,63}$/iu'),
197        'CL'  => array(1 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu'),
198        'CN'  => 'Hostname/Cn.php',
199        'COM' => 'Zend/Validate/Hostname/Com.php',
200        'DE'  => array(1 => '/^[\x{002d}0-9a-z�-��-�??????????????????????????????????????�??????�?????????????�?]{1,63}$/iu'),
201        'DK'  => array(1 => '/^[\x{002d}0-9a-z����]{1,63}$/iu'),
202        'ES'  => array(1 => '/^[\x{002d}0-9a-z�������������]{1,63}$/iu'),
203        'FI'  => array(1 => '/^[\x{002d}0-9a-z���]{1,63}$/iu'),
204        'GR'  => array(1 => '/^[\x{002d}0-9a-z??????-??-??-??-??-??-??-?????-??-??-?????-??-??-??-?????-?]{1,63}$/iu'),
205        'HK'  => 'Zend/Validate/Hostname/Cn.php',
206        'HU'  => array(1 => '/^[\x{002d}0-9a-z�������??]{1,63}$/iu'),
207        'INFO'=> array(1 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu',
208            2 => '/^[\x{002d}0-9a-z�������??]{1,63}$/iu',
209            3 => '/^[\x{002d}0-9a-z����������]{1,63}$/iu',
210            4 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
211            5 => '/^[\x{002d}0-9a-z??????????�?�]{1,63}$/iu',
212            6 => '/^[\x{002d}0-9a-z?????�??�]{1,63}$/iu',
213            7 => '/^[\x{002d}0-9a-z�????????]{1,63}$/iu',
214            8 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu'),
215        'IO'  => array(1 => '/^[\x{002d}0-9a-z�-��-�??????????????????????????????????????�??????�?????????????�?]{1,63}$/iu'),
216        'IS'  => array(1 => '/^[\x{002d}0-9a-z����������]{1,63}$/iu'),
217        'JP'  => 'Zend/Validate/Hostname/Jp.php',
218        'KR'  => array(1 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu'),
219        'LI'  => array(1 => '/^[\x{002d}0-9a-z�-��-��]{1,63}$/iu'),
220        'LT'  => array(1 => '/^[\x{002d}0-9?????�??�]{1,63}$/iu'),
221        'MD'  => array(1 => '/^[\x{002d}0-9?��??]{1,63}$/iu'),
222        'MUSEUM' => array(1 => '/^[\x{002d}0-9a-z�-��-�??????????????????????????????�?????�???????????�????\x{01E5}\x{01E7}\x{01E9}\x{01EF}?\x{0292}????]{1,63}$/iu'),
223        'NET' => 'Zend/Validate/Hostname/Com.php',
224        'NO'  => array(1 => '/^[\x{002d}0-9a-z���-���-����????�?�]{1,63}$/iu'),
225        'NU'  => 'Zend/Validate/Hostname/Com.php',
226        'ORG' => array(1 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu',
227            2 => '/^[\x{002d}0-9a-z�????????]{1,63}$/iu',
228            3 => '/^[\x{002d}0-9a-z���������������]{1,63}$/iu',
229            4 => '/^[\x{002d}0-9a-z�������??]{1,63}$/iu',
230            5 => '/^[\x{002d}0-9a-z?????�??�]{1,63}$/iu',
231            6 => '/^[\x{AC00}-\x{D7A3}]{1,17}$/iu',
232            7 => '/^[\x{002d}0-9a-z??????????�?�]{1,63}$/iu'),
233        'PE'  => array(1 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu'),
234        'PL'  => array(1 => '/^[\x{002d}0-9a-z??????????�?�]{1,63}$/iu',
235            2 => '/^[\x{002d}?-??-?\x{0450}???????]{1,63}$/iu',
236            3 => '/^[\x{002d}0-9a-z��???]{1,63}$/iu',
237            4 => '/^[\x{002d}0-9?-??\x{04C2}]{1,63}$/iu',
238            5 => '/^[\x{002d}0-9a-z���������������????]{1,63}$/iu',
239            6 => '/^[\x{002d}0-9a-z������������]{1,63}$/iu',
240            7 => '/^[\x{002d}0-9a-z�????????]{1,63}$/iu',
241            8 => '/^[\x{002d}0-9a-z��������������]{1,63}$/iu',
242            9 => '/^[\x{002d}0-9a-z��???]{1,63}$/iu',
243            10=> '/^[\x{002d}0-9a-z��������??????�?�]{1,63}$/iu',
244            11=> '/^[\x{002d}0-9a-z��]{1,63}$/iu',
245            12=> '/^[\x{002d}0-9?-??-???????]{1,63}$/iu',
246            13=> '/^[\x{002d}0-9a-z???��]{1,63}$/iu',
247            14=> '/^[\x{002d}0-9a-z�����???]{1,63}$/iu',
248            15=> '/^[\x{002d}0-9a-z�������]{1,63}$/iu',
249            16=> '/^[\x{002d}0-9a-z������]{1,63}$/iu',
250            17=> '/^[\x{002d}0-9a-z??????]{1,63}$/iu',
251            18=> '/^[\x{002d}0-9a-z������]{1,63}$/iu',
252            19=> '/^[\x{002d}0-9a-z��������������������������??????�]{1,63}$/iu',
253            20=> '/^[\x{002d}0-9a-z���������]{1,63}$/iu',
254            21=> '/^[\x{002d}0-9a-z�����������]{1,63}$/iu',
255            22=> '/^[\x{002d}0-9a-z��������??]{1,63}$/iu',
256            23=> '/^[\x{002d}0-9??-?]{1,63}$/iu',
257            24=> '/^[\x{002d}0-9a-z������������������]{1,63}$/iu',
258            25=> '/^[\x{002d}0-9a-z���������?????�??�]{1,63}$/iu',
259            26=> '/^[\x{002d}0-9a-z�����������]{1,63}$/iu',
260            27=> '/^[\x{002d}0-9?-????\x{0450}\x{045D}]{1,63}$/iu',
261            28=> '/^[\x{002d}0-9?-????]{1,63}$/iu',
262            29=> '/^[\x{002d}0-9a-z?????�??�]{1,63}$/iu',
263            30=> '/^[\x{002d}0-9a-z���������������]{1,63}$/iu',
264            31=> '/^[\x{002d}0-9a-z�����������������]{1,63}$/iu',
265            32=> '/^[\x{002d}0-9?-????????????]{1,63}$/iu',
266            33=> '/^[\x{002d}0-9?-?]{1,63}$/iu'),
267        'PR'  => array(1 => '/^[\x{002d}0-9a-z������������������������]{1,63}$/iu'),
268        'PT'  => array(1 => '/^[\x{002d}0-9a-z������������]{1,63}$/iu'),
269        'RU'  => array(1 => '/^[\x{002d}0-9?-??]{1,63}$/iu'),
270        'SA'  => array(1 => '/^[\x{002d}.0-9\x{0621}-\x{063A}\x{0641}-\x{064A}\x{0660}-\x{0669}]{1,63}$/iu'),
271        'SE'  => array(1 => '/^[\x{002d}0-9a-z�����]{1,63}$/iu'),
272        'SH'  => array(1 => '/^[\x{002d}0-9a-z�-��-�??????????????????????????????????????�??????�?????????????�?]{1,63}$/iu'),
273        'SJ'  => array(1 => '/^[\x{002d}0-9a-z���-���-����????�?�]{1,63}$/iu'),
274        'TH'  => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
275        'TM'  => array(1 => '/^[\x{002d}0-9a-z�-��-�????????????????????????????????�??????�????????????�]{1,63}$/iu'),
276        'TW'  => 'Zend/Validate/Hostname/Cn.php',
277        'TR'  => array(1 => '/^[\x{002d}0-9a-z??�?��]{1,63}$/iu'),
278        'VE'  => array(1 => '/^[\x{002d}0-9a-z�������]{1,63}$/iu'),
279        'VN'  => array(1 => '/^[��������������������������������????????????\x{1EA0}-\x{1EF9}]{1,63}$/iu'),
280        '?????' => 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'),
281        '??' => 'Zend/Validate/Hostname/Cn.php',
282        '??' => 'Zend/Validate/Hostname/Cn.php',
283        '??' => 'Zend/Validate/Hostname/Cn.php'
284    );
285
286    protected $_idnLength = array(
287        'BIZ' => array(5 => 17, 11 => 15, 12 => 20),
288        'CN'  => array(1 => 20),
289        'COM' => array(3 => 17, 5 => 20),
290        'HK'  => array(1 => 15),
291        'INFO'=> array(4 => 17),
292        'KR'  => array(1 => 17),
293        'NET' => array(3 => 17, 5 => 20),
294        'ORG' => array(6 => 17),
295        'TW'  => array(1 => 20),
296        '?????' => array(1 => 30),
297        '??' => array(1 => 20),
298        '??' => array(1 => 20),
299        '??' => array(1 => 20),
300    );
301
302    protected $_options = array(
303        'allow' => self::ALLOW_DNS,
304        'idn'   => true,
305        'tld'   => true,
306        'ip'    => null
307    );
308
309    /**
310     * Sets validator options
311     *
312     * @param integer          $allow       OPTIONAL Set what types of hostname to allow (default ALLOW_DNS)
313     * @param boolean          $validateIdn OPTIONAL Set whether IDN domains are validated (default true)
314     * @param boolean          $validateTld OPTIONAL Set whether the TLD element of a hostname is validated (default true)
315     * @param Zend_Validate_Ip $ipValidator OPTIONAL
316     * @return void
317     * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm  Technical Specifications for ccTLDs
318     */
319    public function __construct($options = array())
320    {
321        if ($options instanceof Zend_Config) {
322            $options = $options->toArray();
323        } else if (!is_array($options)) {
324            $options = func_get_args();
325            $temp['allow'] = array_shift($options);
326            if (!empty($options)) {
327                $temp['idn'] = array_shift($options);
328            }
329
330            if (!empty($options)) {
331                $temp['tld'] = array_shift($options);
332            }
333
334            if (!empty($options)) {
335                $temp['ip'] = array_shift($options);
336            }
337
338            $options = $temp;
339        }
340
341        $options += $this->_options;
342        $this->setOptions($options);
343    }
344
345    /**
346     * Returns all set options
347     *
348     * @return array
349     */
350    public function getOptions()
351    {
352        return $this->_options;
353    }
354
355    /**
356     * Sets the options for this validator
357     *
358     * @param array $options
359     * @return Zend_Validate_Hostname
360     */
361    public function setOptions($options)
362    {
363        if (array_key_exists('allow', $options)) {
364            $this->setAllow($options['allow']);
365        }
366
367        if (array_key_exists('idn', $options)) {
368            $this->setValidateIdn($options['idn']);
369        }
370
371        if (array_key_exists('tld', $options)) {
372            $this->setValidateTld($options['tld']);
373        }
374
375        if (array_key_exists('ip', $options)) {
376            $this->setIpValidator($options['ip']);
377        }
378
379        return $this;
380    }
381
382    /**
383     * Returns the set ip validator
384     *
385     * @return Zend_Validate_Ip
386     */
387    public function getIpValidator()
388    {
389        return $this->_options['ip'];
390    }
391
392    /**
393     * @param Zend_Validate_Ip $ipValidator OPTIONAL
394     * @return void;
395     */
396    public function setIpValidator(Zend_Validate_Ip $ipValidator = null)
397    {
398        if ($ipValidator === null) {
399            $ipValidator = new Zend_Validate_Ip();
400        }
401
402        $this->_options['ip'] = $ipValidator;
403        return $this;
404    }
405
406    /**
407     * Returns the allow option
408     *
409     * @return integer
410     */
411    public function getAllow()
412    {
413        return $this->_options['allow'];
414    }
415
416    /**
417     * Sets the allow option
418     *
419     * @param  integer $allow
420     * @return Zend_Validate_Hostname Provides a fluent interface
421     */
422    public function setAllow($allow)
423    {
424        $this->_options['allow'] = $allow;
425        return $this;
426    }
427
428    /**
429     * Returns the set idn option
430     *
431     * @return boolean
432     */
433    public function getValidateIdn()
434    {
435        return $this->_options['idn'];
436    }
437
438    /**
439     * Set whether IDN domains are validated
440     *
441     * This only applies when DNS hostnames are validated
442     *
443     * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
444     */
445    public function setValidateIdn ($allowed)
446    {
447        $this->_options['idn'] = (bool) $allowed;
448        return $this;
449    }
450
451    /**
452     * Returns the set tld option
453     *
454     * @return boolean
455     */
456    public function getValidateTld()
457    {
458        return $this->_options['tld'];
459    }
460
461    /**
462     * Set whether the TLD element of a hostname is validated
463     *
464     * This only applies when DNS hostnames are validated
465     *
466     * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
467     */
468    public function setValidateTld ($allowed)
469    {
470        $this->_options['tld'] = (bool) $allowed;
471        return $this;
472    }
473
474    /**
475     * Defined by Zend_Validate_Interface
476     *
477     * Returns true if and only if the $value is a valid hostname with respect to the current allow option
478     *
479     * @param  string $value
480     * @throws Zend_Validate_Exception if a fatal error occurs for validation process
481     * @return boolean
482     */
483    public function isValid($value)
484    {
485        if (!is_string($value)) {
486            $this->_error(self::INVALID);
487            return false;
488        }
489
490        $this->_setValue($value);
491        // Check input against IP address schema
492        if (preg_match('/^[0-9.a-e:.]*$/i', $value) &&
493            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
494            if (!($this->_options['allow'] & self::ALLOW_IP)) {
495                $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
496                return false;
497            } else {
498                return true;
499            }
500        }
501
502        // Check input against DNS hostname schema
503        $domainParts = explode('.', $value);
504        if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
505            $status = false;
506
507            $origenc = iconv_get_encoding('internal_encoding');
508            iconv_set_encoding('internal_encoding', 'UTF-8');
509            do {
510                // First check TLD
511                $matches = array();
512                if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
513                    (end($domainParts) == '?????') || (end($domainParts) == '??') ||
514                    (end($domainParts) == '??') || (end($domainParts) == '??')) {
515
516                    reset($domainParts);
517
518                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
519                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
520                    // id-prefix: alpha / digit
521                    // ldh: alpha / digit / dash
522
523                    // Match TLD against known list
524                    $this->_tld = strtolower($matches[1]);
525                    if ($this->_options['tld']) {
526                        if (!in_array($this->_tld, $this->_validTlds)) {
527                            $this->_error(self::UNKNOWN_TLD);
528                            $status = false;
529                            break;
530                        }
531                    }
532
533                    /**
534                     * Match against IDN hostnames
535                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
536                     * @see Zend_Validate_Hostname_Interface
537                     */
538                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
539                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
540                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
541                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
542                        } else {
543                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
544                        }
545                    }
546
547                    // Check each hostname part
548                    $check = 0;
549                    foreach ($domainParts as $domainPart) {
550                        // Decode Punycode domainnames to IDN
551                        if (strpos($domainPart, 'xn--') === 0) {
552                            $domainPart = $this->decodePunycode(substr($domainPart, 4));
553                            if ($domainPart === false) {
554                                return false;
555                            }
556                        }
557
558                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
559                        if ((strpos($domainPart, '-') === 0)
560                            || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
561                            || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
562                                $this->_error(self::INVALID_DASH);
563                            $status = false;
564                            break 2;
565                        }
566
567                        // Check each domain part
568                        $checked = false;
569                        foreach($regexChars as $regexKey => $regexChar) {
570                            $status = @preg_match($regexChar, $domainPart);
571                            if ($status > 0) {
572                                $length = 63;
573                                if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
574                                    && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
575                                    $length = $this->_idnLength[strtoupper($this->_tld)];
576                                }
577
578                                if (iconv_strlen($domainPart, 'UTF-8') > $length) {
579                                    $this->_error(self::INVALID_HOSTNAME);
580                                } else {
581                                    $checked = true;
582                                    break;
583                                }
584                            }
585                        }
586
587                        if ($checked) {
588                            ++$check;
589                        }
590                    }
591
592                    // If one of the labels doesn't match, the hostname is invalid
593                    if ($check !== count($domainParts)) {
594                        $this->_error(self::INVALID_HOSTNAME_SCHEMA);
595                        $status = false;
596                    }
597                } else {
598                    // Hostname not long enough
599                    $this->_error(self::UNDECIPHERABLE_TLD);
600                    $status = false;
601                }
602            } while (false);
603
604            iconv_set_encoding('internal_encoding', $origenc);
605            // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
606            // passes validation
607            if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
608                return true;
609            }
610        } else if ($this->_options['allow'] & self::ALLOW_DNS) {
611            $this->_error(self::INVALID_HOSTNAME);
612        }
613
614        // Check input against local network name schema; last chance to pass validation
615        $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}){1,254}$/';
616        $status = @preg_match($regexLocal, $value);
617
618        // If the input passes as a local network name, and local network names are allowed, then the
619        // hostname passes validation
620        $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
621        if ($status && $allowLocal) {
622            return true;
623        }
624
625        // If the input does not pass as a local network name, add a message
626        if (!$status) {
627            $this->_error(self::INVALID_LOCAL_NAME);
628        }
629
630        // If local network names are not allowed, add a message
631        if ($status && !$allowLocal) {
632            $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
633        }
634
635        return false;
636    }
637
638    /**
639     * Decodes a punycode encoded string to it's original utf8 string
640     * In case of a decoding failure the original string is returned
641     *
642     * @param  string $encoded Punycode encoded string to decode
643     * @return string
644     */
645    protected function decodePunycode($encoded)
646    {
647        $found = preg_match('/([^a-z0-9\x2d]{1,10})$/i', $encoded);
648        if (empty($encoded) || ($found > 0)) {
649            // no punycode encoded string, return as is
650            $this->_error(self::CANNOT_DECODE_PUNYCODE);
651            return false;
652        }
653
654        $separator = strrpos($encoded, '-');
655        if ($separator > 0) {
656            for ($x = 0; $x < $separator; ++$x) {
657                // prepare decoding matrix
658                $decoded[] = ord($encoded[$x]);
659            }
660        } else {
661            $this->_error(self::CANNOT_DECODE_PUNYCODE);
662            return false;
663        }
664
665        $lengthd = count($decoded);
666        $lengthe = strlen($encoded);
667
668        // decoding
669        $init  = true;
670        $base  = 72;
671        $index = 0;
672        $char  = 0x80;
673
674        for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
675            for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
676                $hex   = ord($encoded[$indexe++]);
677                $digit = ($hex - 48 < 10) ? $hex - 22
678                       : (($hex - 65 < 26) ? $hex - 65
679                       : (($hex - 97 < 26) ? $hex - 97
680                       : 36));
681
682                $index += $digit * $pos;
683                $tag    = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
684                if ($digit < $tag) {
685                    break;
686                }
687
688                $pos = (int) ($pos * (36 - $tag));
689            }
690
691            $delta   = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
692            $delta  += intval($delta / ($lengthd + 1));
693            for ($key = 0; $delta > 910 / 2; $key += 36) {
694                $delta = intval($delta / 35);
695            }
696
697            $base   = intval($key + 36 * $delta / ($delta + 38));
698            $init   = false;
699            $char  += (int) ($index / ($lengthd + 1));
700            $index %= ($lengthd + 1);
701            if ($lengthd > 0) {
702                for ($i = $lengthd; $i > $index; $i--) {
703                    $decoded[$i] = $decoded[($i - 1)];
704                }
705            }
706
707            $decoded[$index++] = $char;
708        }
709
710        // convert decoded ucs4 to utf8 string
711        foreach ($decoded as $key => $value) {
712            if ($value < 128) {
713                $decoded[$key] = chr($value);
714            } elseif ($value < (1 << 11)) {
715                $decoded[$key]  = chr(192 + ($value >> 6));
716                $decoded[$key] .= chr(128 + ($value & 63));
717            } elseif ($value < (1 << 16)) {
718                $decoded[$key]  = chr(224 + ($value >> 12));
719                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
720                $decoded[$key] .= chr(128 + ($value & 63));
721            } elseif ($value < (1 << 21)) {
722                $decoded[$key]  = chr(240 + ($value >> 18));
723                $decoded[$key] .= chr(128 + (($value >> 12) & 63));
724                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
725                $decoded[$key] .= chr(128 + ($value & 63));
726            } else {
727                $this->_error(self::CANNOT_DECODE_PUNYCODE);
728                return false;
729            }
730        }
731
732        return implode($decoded);
733    }
734}