source: trunk/library/Zend/Validate/Hostname.php @ 5146

Revision 5146, 34.4 KB checked in by wmerlotto, 12 years ago (diff)

Ticket #2305 - Enviando alteracoes, desenvolvidas internamente na Prognus. Library: adicionando arquivos.

Line 
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 23488 2010-12-10 07:22:55Z mjh_ca $
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 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        // RFC3986 3.2.2 states:
516        //
517        //     The rightmost domain label of a fully qualified domain name
518        //     in DNS may be followed by a single "." and should be if it is
519        //     necessary to distinguish between the complete domain name and
520        //     some local domain.
521        //     
522        // Strip trailing '.' since it is not necessary to validate a non-IP
523        // hostname.
524        //
525        // (see ZF-6363)
526        if (substr($value, -1) === '.') {
527            $value = substr($value, 0, strlen($value)-1);
528        }
529       
530        // Check input against DNS hostname schema
531        $domainParts = explode('.', $value);
532        if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
533            $status = false;
534
535            $origenc = iconv_get_encoding('internal_encoding');
536            iconv_set_encoding('internal_encoding', 'UTF-8');
537            do {
538                // First check TLD
539                $matches = array();
540                if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
541                    (end($domainParts) == 'ایران') || (end($domainParts) == '䞭囜') ||
542                    (end($domainParts) == '公叞') || (end($domainParts) == '眑络')) {
543
544                    reset($domainParts);
545
546                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
547                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
548                    // id-prefix: alpha / digit
549                    // ldh: alpha / digit / dash
550
551                    // Match TLD against known list
552                    $this->_tld = strtolower($matches[1]);
553                    if ($this->_options['tld']) {
554                        if (!in_array($this->_tld, $this->_validTlds)) {
555                            $this->_error(self::UNKNOWN_TLD);
556                            $status = false;
557                            break;
558                        }
559                    }
560
561                    /**
562                     * Match against IDN hostnames
563                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
564                     * @see Zend_Validate_Hostname_Interface
565                     */
566                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
567                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
568                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
569                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
570                        } else {
571                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
572                        }
573                    }
574
575                    // Check each hostname part
576                    $check = 0;
577                    foreach ($domainParts as $domainPart) {
578                        // Decode Punycode domainnames to IDN
579                        if (strpos($domainPart, 'xn--') === 0) {
580                            $domainPart = $this->decodePunycode(substr($domainPart, 4));
581                            if ($domainPart === false) {
582                                return false;
583                            }
584                        }
585
586                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
587                        if ((strpos($domainPart, '-') === 0)
588                            || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
589                            || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
590                                $this->_error(self::INVALID_DASH);
591                            $status = false;
592                            break 2;
593                        }
594
595                        // Check each domain part
596                        $checked = false;
597                        foreach($regexChars as $regexKey => $regexChar) {
598                            $status = @preg_match($regexChar, $domainPart);
599                            if ($status > 0) {
600                                $length = 63;
601                                if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
602                                    && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
603                                    $length = $this->_idnLength[strtoupper($this->_tld)];
604                                }
605
606                                if (iconv_strlen($domainPart, 'UTF-8') > $length) {
607                                    $this->_error(self::INVALID_HOSTNAME);
608                                } else {
609                                    $checked = true;
610                                    break;
611                                }
612                            }
613                        }
614
615                        if ($checked) {
616                            ++$check;
617                        }
618                    }
619
620                    // If one of the labels doesn't match, the hostname is invalid
621                    if ($check !== count($domainParts)) {
622                        $this->_error(self::INVALID_HOSTNAME_SCHEMA);
623                        $status = false;
624                    }
625                } else {
626                    // Hostname not long enough
627                    $this->_error(self::UNDECIPHERABLE_TLD);
628                    $status = false;
629                }
630            } while (false);
631
632            iconv_set_encoding('internal_encoding', $origenc);
633            // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
634            // passes validation
635            if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
636                return true;
637            }
638        } else if ($this->_options['allow'] & self::ALLOW_DNS) {
639            $this->_error(self::INVALID_HOSTNAME);
640        }
641
642        // Check for URI Syntax (RFC3986)
643        if ($this->_options['allow'] & self::ALLOW_URI) {
644            if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
645                return true;
646            } else {
647                $this->_error(self::INVALID_URI);
648            }
649        }
650
651        // Check input against local network name schema; last chance to pass validation
652        $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}){1,254}$/';
653        $status = @preg_match($regexLocal, $value);
654
655        // If the input passes as a local network name, and local network names are allowed, then the
656        // hostname passes validation
657        $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
658        if ($status && $allowLocal) {
659            return true;
660        }
661
662        // If the input does not pass as a local network name, add a message
663        if (!$status) {
664            $this->_error(self::INVALID_LOCAL_NAME);
665        }
666
667        // If local network names are not allowed, add a message
668        if ($status && !$allowLocal) {
669            $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
670        }
671
672        return false;
673    }
674
675    /**
676     * Decodes a punycode encoded string to it's original utf8 string
677     * In case of a decoding failure the original string is returned
678     *
679     * @param  string $encoded Punycode encoded string to decode
680     * @return string
681     */
682    protected function decodePunycode($encoded)
683    {
684        $found = preg_match('/([^a-z0-9\x2d]{1,10})$/i', $encoded);
685        if (empty($encoded) || ($found > 0)) {
686            // no punycode encoded string, return as is
687            $this->_error(self::CANNOT_DECODE_PUNYCODE);
688            return false;
689        }
690
691        $separator = strrpos($encoded, '-');
692        if ($separator > 0) {
693            for ($x = 0; $x < $separator; ++$x) {
694                // prepare decoding matrix
695                $decoded[] = ord($encoded[$x]);
696            }
697        } else {
698            $this->_error(self::CANNOT_DECODE_PUNYCODE);
699            return false;
700        }
701
702        $lengthd = count($decoded);
703        $lengthe = strlen($encoded);
704
705        // decoding
706        $init  = true;
707        $base  = 72;
708        $index = 0;
709        $char  = 0x80;
710
711        for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
712            for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
713                $hex   = ord($encoded[$indexe++]);
714                $digit = ($hex - 48 < 10) ? $hex - 22
715                       : (($hex - 65 < 26) ? $hex - 65
716                       : (($hex - 97 < 26) ? $hex - 97
717                       : 36));
718
719                $index += $digit * $pos;
720                $tag    = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
721                if ($digit < $tag) {
722                    break;
723                }
724
725                $pos = (int) ($pos * (36 - $tag));
726            }
727
728            $delta   = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
729            $delta  += intval($delta / ($lengthd + 1));
730            for ($key = 0; $delta > 910 / 2; $key += 36) {
731                $delta = intval($delta / 35);
732            }
733
734            $base   = intval($key + 36 * $delta / ($delta + 38));
735            $init   = false;
736            $char  += (int) ($index / ($lengthd + 1));
737            $index %= ($lengthd + 1);
738            if ($lengthd > 0) {
739                for ($i = $lengthd; $i > $index; $i--) {
740                    $decoded[$i] = $decoded[($i - 1)];
741                }
742            }
743
744            $decoded[$index++] = $char;
745        }
746
747        // convert decoded ucs4 to utf8 string
748        foreach ($decoded as $key => $value) {
749            if ($value < 128) {
750                $decoded[$key] = chr($value);
751            } elseif ($value < (1 << 11)) {
752                $decoded[$key]  = chr(192 + ($value >> 6));
753                $decoded[$key] .= chr(128 + ($value & 63));
754            } elseif ($value < (1 << 16)) {
755                $decoded[$key]  = chr(224 + ($value >> 12));
756                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
757                $decoded[$key] .= chr(128 + ($value & 63));
758            } elseif ($value < (1 << 21)) {
759                $decoded[$key]  = chr(240 + ($value >> 18));
760                $decoded[$key] .= chr(128 + (($value >> 12) & 63));
761                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
762                $decoded[$key] .= chr(128 + ($value & 63));
763            } else {
764                $this->_error(self::CANNOT_DECODE_PUNYCODE);
765                return false;
766            }
767        }
768
769        return implode($decoded);
770    }
771}
Note: See TracBrowser for help on using the repository browser.