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