source: extensions/Google2Piwigo/include/Zend/Validate/Hostname.php @ 17475

Revision 17475, 35.3 KB checked in by mistic100, 7 years ago (diff)

new extension: Google2Piwigo

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-2012 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 24632 2012-02-24 17:46:13Z adamlundrigan $
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-2012 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_URI = 8;
104
105    /**
106     * Allows all types of hostnames
107     */
108    const ALLOW_ALL = 15;
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        'xxx', '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        'SI'  => array(
287            1 => '/^[\x{002d}0-9a-zà-öø-ÿ]{1,63}$/iu',
288            2 => '/^[\x{002d}0-9a-zāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĺļľŀłńņňʼnŋōŏőœŕŗřśŝšťŧũūŭůűųŵŷźżž]{1,63}$/iu',
289            3 => '/^[\x{002d}0-9a-zșț]{1,63}$/iu'),
290        'SJ'  => array(1 => '/^[\x{002d}0-9a-zàáä-éêñ-ôöøüčđńŋšŧž]{1,63}$/iu'),
291        'TH'  => array(1 => '/^[\x{002d}0-9a-z\x{0E01}-\x{0E3A}\x{0E40}-\x{0E4D}\x{0E50}-\x{0E59}]{1,63}$/iu'),
292        'TM'  => array(1 => '/^[\x{002d}0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$/iu'),
293        'TW'  => 'Zend/Validate/Hostname/Cn.php',
294        'TR'  => array(1 => '/^[\x{002d}0-9a-zğıüşöç]{1,63}$/iu'),
295        'VE'  => array(1 => '/^[\x{002d}0-9a-záéíóúüñ]{1,63}$/iu'),
296        'VN'  => array(1 => '/^[ÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚÝàáâãèéêìíòóôõùúýĂăĐđĨĩŨũƠơƯư\x{1EA0}-\x{1EF9}]{1,63}$/iu'),
297        'ایران' => 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'),
298        '中国' => 'Zend/Validate/Hostname/Cn.php',
299        '公司' => 'Zend/Validate/Hostname/Cn.php',
300        '网络' => 'Zend/Validate/Hostname/Cn.php'
301    );
302
303    protected $_idnLength = array(
304        'BIZ' => array(5 => 17, 11 => 15, 12 => 20),
305        'CN'  => array(1 => 20),
306        'COM' => array(3 => 17, 5 => 20),
307        'HK'  => array(1 => 15),
308        'INFO'=> array(4 => 17),
309        'KR'  => array(1 => 17),
310        'NET' => array(3 => 17, 5 => 20),
311        'ORG' => array(6 => 17),
312        'TW'  => array(1 => 20),
313        'ایران' => array(1 => 30),
314        '中国' => array(1 => 20),
315        '公司' => array(1 => 20),
316        '网络' => array(1 => 20),
317    );
318
319    protected $_options = array(
320        'allow' => self::ALLOW_DNS,
321        'idn'   => true,
322        'tld'   => true,
323        'ip'    => null
324    );
325
326    /**
327     * Sets validator options
328     *
329     * @param integer          $allow       OPTIONAL Set what types of hostname to allow (default ALLOW_DNS)
330     * @param boolean          $validateIdn OPTIONAL Set whether IDN domains are validated (default true)
331     * @param boolean          $validateTld OPTIONAL Set whether the TLD element of a hostname is validated (default true)
332     * @param Zend_Validate_Ip $ipValidator OPTIONAL
333     * @return void
334     * @see http://www.iana.org/cctld/specifications-policies-cctlds-01apr02.htm  Technical Specifications for ccTLDs
335     */
336    public function __construct($options = array())
337    {
338        if ($options instanceof Zend_Config) {
339            $options = $options->toArray();
340        } else if (!is_array($options)) {
341            $options = func_get_args();
342            $temp['allow'] = array_shift($options);
343            if (!empty($options)) {
344                $temp['idn'] = array_shift($options);
345            }
346
347            if (!empty($options)) {
348                $temp['tld'] = array_shift($options);
349            }
350
351            if (!empty($options)) {
352                $temp['ip'] = array_shift($options);
353            }
354
355            $options = $temp;
356        }
357
358        $options += $this->_options;
359        $this->setOptions($options);
360    }
361
362    /**
363     * Returns all set options
364     *
365     * @return array
366     */
367    public function getOptions()
368    {
369        return $this->_options;
370    }
371
372    /**
373     * Sets the options for this validator
374     *
375     * @param array $options
376     * @return Zend_Validate_Hostname
377     */
378    public function setOptions($options)
379    {
380        if (array_key_exists('allow', $options)) {
381            $this->setAllow($options['allow']);
382        }
383
384        if (array_key_exists('idn', $options)) {
385            $this->setValidateIdn($options['idn']);
386        }
387
388        if (array_key_exists('tld', $options)) {
389            $this->setValidateTld($options['tld']);
390        }
391
392        if (array_key_exists('ip', $options)) {
393            $this->setIpValidator($options['ip']);
394        }
395
396        return $this;
397    }
398
399    /**
400     * Returns the set ip validator
401     *
402     * @return Zend_Validate_Ip
403     */
404    public function getIpValidator()
405    {
406        return $this->_options['ip'];
407    }
408
409    /**
410     * @param Zend_Validate_Ip $ipValidator OPTIONAL
411     * @return void;
412     */
413    public function setIpValidator(Zend_Validate_Ip $ipValidator = null)
414    {
415        if ($ipValidator === null) {
416            $ipValidator = new Zend_Validate_Ip();
417        }
418
419        $this->_options['ip'] = $ipValidator;
420        return $this;
421    }
422
423    /**
424     * Returns the allow option
425     *
426     * @return integer
427     */
428    public function getAllow()
429    {
430        return $this->_options['allow'];
431    }
432
433    /**
434     * Sets the allow option
435     *
436     * @param  integer $allow
437     * @return Zend_Validate_Hostname Provides a fluent interface
438     */
439    public function setAllow($allow)
440    {
441        $this->_options['allow'] = $allow;
442        return $this;
443    }
444
445    /**
446     * Returns the set idn option
447     *
448     * @return boolean
449     */
450    public function getValidateIdn()
451    {
452        return $this->_options['idn'];
453    }
454
455    /**
456     * Set whether IDN domains are validated
457     *
458     * This only applies when DNS hostnames are validated
459     *
460     * @param boolean $allowed Set allowed to true to validate IDNs, and false to not validate them
461     */
462    public function setValidateIdn ($allowed)
463    {
464        $this->_options['idn'] = (bool) $allowed;
465        return $this;
466    }
467
468    /**
469     * Returns the set tld option
470     *
471     * @return boolean
472     */
473    public function getValidateTld()
474    {
475        return $this->_options['tld'];
476    }
477
478    /**
479     * Set whether the TLD element of a hostname is validated
480     *
481     * This only applies when DNS hostnames are validated
482     *
483     * @param boolean $allowed Set allowed to true to validate TLDs, and false to not validate them
484     */
485    public function setValidateTld ($allowed)
486    {
487        $this->_options['tld'] = (bool) $allowed;
488        return $this;
489    }
490
491    /**
492     * Defined by Zend_Validate_Interface
493     *
494     * Returns true if and only if the $value is a valid hostname with respect to the current allow option
495     *
496     * @param  string $value
497     * @throws Zend_Validate_Exception if a fatal error occurs for validation process
498     * @return boolean
499     */
500    public function isValid($value)
501    {
502        if (!is_string($value)) {
503            $this->_error(self::INVALID);
504            return false;
505        }
506
507        $this->_setValue($value);
508        // Check input against IP address schema
509        if (preg_match('/^[0-9a-f:.]*$/i', $value) &&
510            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
511            if (!($this->_options['allow'] & self::ALLOW_IP)) {
512                $this->_error(self::IP_ADDRESS_NOT_ALLOWED);
513                return false;
514            } else {
515                return true;
516            }
517        }
518
519        // RFC3986 3.2.2 states:
520        //
521        //     The rightmost domain label of a fully qualified domain name
522        //     in DNS may be followed by a single "." and should be if it is
523        //     necessary to distinguish between the complete domain name and
524        //     some local domain.
525        //     
526        // (see ZF-6363)
527       
528        // Local hostnames are allowed to be partitial (ending '.')
529        if ($this->_options['allow'] & self::ALLOW_LOCAL) {
530            if (substr($value, -1) === '.') {
531                $value = substr($value, 0, -1);
532                if (substr($value, -1) === '.') {
533                    // Empty hostnames (ending '..') are not allowed
534                    $this->_error(self::INVALID_LOCAL_NAME);
535                    return false;
536                }
537            }
538        }
539
540        $domainParts = explode('.', $value);
541
542        // Prevent partitial IP V4 adresses (ending '.')
543        if ((count($domainParts) == 4) && preg_match('/^[0-9.a-e:.]*$/i', $value) &&
544            $this->_options['ip']->setTranslator($this->getTranslator())->isValid($value)) {
545            $this->_error(self::INVALID_LOCAL_NAME);
546        }
547
548        // Check input against DNS hostname schema
549        if ((count($domainParts) > 1) && (strlen($value) >= 4) && (strlen($value) <= 254)) {
550            $status = false;
551
552            $origenc = iconv_get_encoding('internal_encoding');
553            iconv_set_encoding('internal_encoding', 'UTF-8');
554            do {
555                // First check TLD
556                $matches = array();
557                if (preg_match('/([^.]{2,10})$/i', end($domainParts), $matches) ||
558                    (end($domainParts) == 'ایران') || (end($domainParts) == '中国') ||
559                    (end($domainParts) == '公司') || (end($domainParts) == '网络')) {
560
561                    reset($domainParts);
562
563                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
564                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
565                    // id-prefix: alpha / digit
566                    // ldh: alpha / digit / dash
567
568                    // Match TLD against known list
569                    $this->_tld = strtolower($matches[1]);
570                    if ($this->_options['tld']) {
571                        if (!in_array($this->_tld, $this->_validTlds)) {
572                            $this->_error(self::UNKNOWN_TLD);
573                            $status = false;
574                            break;
575                        }
576                    }
577
578                    /**
579                     * Match against IDN hostnames
580                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
581                     * @see Zend_Validate_Hostname_Interface
582                     */
583                    $regexChars = array(0 => '/^[a-z0-9\x2d]{1,63}$/i');
584                    if ($this->_options['idn'] &&  isset($this->_validIdns[strtoupper($this->_tld)])) {
585                        if (is_string($this->_validIdns[strtoupper($this->_tld)])) {
586                            $regexChars += include($this->_validIdns[strtoupper($this->_tld)]);
587                        } else {
588                            $regexChars += $this->_validIdns[strtoupper($this->_tld)];
589                        }
590                    }
591
592                    // Check each hostname part
593                    $check = 0;
594                    foreach ($domainParts as $domainPart) {
595                        // Decode Punycode domainnames to IDN
596                        if (strpos($domainPart, 'xn--') === 0) {
597                            $domainPart = $this->decodePunycode(substr($domainPart, 4));
598                            if ($domainPart === false) {
599                                return false;
600                            }
601                        }
602
603                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
604                        if ((strpos($domainPart, '-') === 0)
605                            || ((strlen($domainPart) > 2) && (strpos($domainPart, '-', 2) == 2) && (strpos($domainPart, '-', 3) == 3))
606                            || (strpos($domainPart, '-') === (strlen($domainPart) - 1))) {
607                                $this->_error(self::INVALID_DASH);
608                            $status = false;
609                            break 2;
610                        }
611
612                        // Check each domain part
613                        $checked = false;
614                        foreach($regexChars as $regexKey => $regexChar) {
615                            $status = @preg_match($regexChar, $domainPart);
616                            if ($status > 0) {
617                                $length = 63;
618                                if (array_key_exists(strtoupper($this->_tld), $this->_idnLength)
619                                    && (array_key_exists($regexKey, $this->_idnLength[strtoupper($this->_tld)]))) {
620                                    $length = $this->_idnLength[strtoupper($this->_tld)];
621                                }
622
623                                if (iconv_strlen($domainPart, 'UTF-8') > $length) {
624                                    $this->_error(self::INVALID_HOSTNAME);
625                                } else {
626                                    $checked = true;
627                                    break;
628                                }
629                            }
630                        }
631
632                        if ($checked) {
633                            ++$check;
634                        }
635                    }
636
637                    // If one of the labels doesn't match, the hostname is invalid
638                    if ($check !== count($domainParts)) {
639                        $this->_error(self::INVALID_HOSTNAME_SCHEMA);
640                        $status = false;
641                    }
642                } else {
643                    // Hostname not long enough
644                    $this->_error(self::UNDECIPHERABLE_TLD);
645                    $status = false;
646                }
647            } while (false);
648
649            iconv_set_encoding('internal_encoding', $origenc);
650            // If the input passes as an Internet domain name, and domain names are allowed, then the hostname
651            // passes validation
652            if ($status && ($this->_options['allow'] & self::ALLOW_DNS)) {
653                return true;
654            }
655        } else if ($this->_options['allow'] & self::ALLOW_DNS) {
656            $this->_error(self::INVALID_HOSTNAME);
657        }
658
659        // Check for URI Syntax (RFC3986)
660        if ($this->_options['allow'] & self::ALLOW_URI) {
661            if (preg_match("/^([a-zA-Z0-9-._~!$&\'()*+,;=]|%[[:xdigit:]]{2}){1,254}$/i", $value)) {
662                return true;
663            } else {
664                $this->_error(self::INVALID_URI);
665            }
666        }
667
668        // Check input against local network name schema; last chance to pass validation
669        $regexLocal = '/^(([a-zA-Z0-9\x2d]{1,63}\x2e)*[a-zA-Z0-9\x2d]{1,63}[\x2e]{0,1}){1,254}$/';
670        $status = @preg_match($regexLocal, $value);
671
672        // If the input passes as a local network name, and local network names are allowed, then the
673        // hostname passes validation
674        $allowLocal = $this->_options['allow'] & self::ALLOW_LOCAL;
675        if ($status && $allowLocal) {
676            return true;
677        }
678
679        // If the input does not pass as a local network name, add a message
680        if (!$status) {
681            $this->_error(self::INVALID_LOCAL_NAME);
682        }
683
684        // If local network names are not allowed, add a message
685        if ($status && !$allowLocal) {
686            $this->_error(self::LOCAL_NAME_NOT_ALLOWED);
687        }
688
689        return false;
690    }
691
692    /**
693     * Decodes a punycode encoded string to it's original utf8 string
694     * In case of a decoding failure the original string is returned
695     *
696     * @param  string $encoded Punycode encoded string to decode
697     * @return string
698     */
699    protected function decodePunycode($encoded)
700    {
701        $found = preg_match('/([^a-z0-9\x2d]{1,10})$/i', $encoded);
702        if (empty($encoded) || ($found > 0)) {
703            // no punycode encoded string, return as is
704            $this->_error(self::CANNOT_DECODE_PUNYCODE);
705            return false;
706        }
707
708        $separator = strrpos($encoded, '-');
709        if ($separator > 0) {
710            for ($x = 0; $x < $separator; ++$x) {
711                // prepare decoding matrix
712                $decoded[] = ord($encoded[$x]);
713            }
714        } else {
715            $this->_error(self::CANNOT_DECODE_PUNYCODE);
716            return false;
717        }
718
719        $lengthd = count($decoded);
720        $lengthe = strlen($encoded);
721
722        // decoding
723        $init  = true;
724        $base  = 72;
725        $index = 0;
726        $char  = 0x80;
727
728        for ($indexe = ($separator) ? ($separator + 1) : 0; $indexe < $lengthe; ++$lengthd) {
729            for ($old_index = $index, $pos = 1, $key = 36; 1 ; $key += 36) {
730                $hex   = ord($encoded[$indexe++]);
731                $digit = ($hex - 48 < 10) ? $hex - 22
732                       : (($hex - 65 < 26) ? $hex - 65
733                       : (($hex - 97 < 26) ? $hex - 97
734                       : 36));
735
736                $index += $digit * $pos;
737                $tag    = ($key <= $base) ? 1 : (($key >= $base + 26) ? 26 : ($key - $base));
738                if ($digit < $tag) {
739                    break;
740                }
741
742                $pos = (int) ($pos * (36 - $tag));
743            }
744
745            $delta   = intval($init ? (($index - $old_index) / 700) : (($index - $old_index) / 2));
746            $delta  += intval($delta / ($lengthd + 1));
747            for ($key = 0; $delta > 910 / 2; $key += 36) {
748                $delta = intval($delta / 35);
749            }
750
751            $base   = intval($key + 36 * $delta / ($delta + 38));
752            $init   = false;
753            $char  += (int) ($index / ($lengthd + 1));
754            $index %= ($lengthd + 1);
755            if ($lengthd > 0) {
756                for ($i = $lengthd; $i > $index; $i--) {
757                    $decoded[$i] = $decoded[($i - 1)];
758                }
759            }
760
761            $decoded[$index++] = $char;
762        }
763
764        // convert decoded ucs4 to utf8 string
765        foreach ($decoded as $key => $value) {
766            if ($value < 128) {
767                $decoded[$key] = chr($value);
768            } elseif ($value < (1 << 11)) {
769                $decoded[$key]  = chr(192 + ($value >> 6));
770                $decoded[$key] .= chr(128 + ($value & 63));
771            } elseif ($value < (1 << 16)) {
772                $decoded[$key]  = chr(224 + ($value >> 12));
773                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
774                $decoded[$key] .= chr(128 + ($value & 63));
775            } elseif ($value < (1 << 21)) {
776                $decoded[$key]  = chr(240 + ($value >> 18));
777                $decoded[$key] .= chr(128 + (($value >> 12) & 63));
778                $decoded[$key] .= chr(128 + (($value >> 6) & 63));
779                $decoded[$key] .= chr(128 + ($value & 63));
780            } else {
781                $this->_error(self::CANNOT_DECODE_PUNYCODE);
782                return false;
783            }
784        }
785
786        return implode($decoded);
787    }
788}
Note: See TracBrowser for help on using the repository browser.