diff options
author | Thomas Bruederli <thomas@roundcube.net> | 2014-08-27 17:45:21 +0200 |
---|---|---|
committer | Thomas Bruederli <thomas@roundcube.net> | 2014-08-27 17:45:21 +0200 |
commit | a98a4f8bb56eacffff1765ff09dd29af26e5fc12 (patch) | |
tree | 68101a1906303dbd0255c77e61fb058a52296294 /program/lib/Net | |
parent | 812f37c5d1baa077f22a35240c5488f65d054260 (diff) |
Remove 3rd party libs from our repository and define the dependencies in composer.json-dist.
Also remove the ancient utf8 lib and replace it with 'Patchwork UTF-8 for PHP'.
For direct git checkouts, copy composer.json-dist into composer.json and run
`php composer.phar install` to install the dependencies.
Diffstat (limited to 'program/lib/Net')
-rw-r--r-- | program/lib/Net/IDNA2.php | 3402 | ||||
-rw-r--r-- | program/lib/Net/IDNA2/Exception.php | 4 | ||||
-rw-r--r-- | program/lib/Net/IDNA2/Exception/Nameprep.php | 6 | ||||
-rw-r--r-- | program/lib/Net/LDAP3.php | 2618 | ||||
-rw-r--r-- | program/lib/Net/LDAP3/Result.php | 152 | ||||
-rw-r--r-- | program/lib/Net/SMTP.php | 1338 | ||||
-rw-r--r-- | program/lib/Net/Sieve.php | 1274 | ||||
-rw-r--r-- | program/lib/Net/Socket.php | 686 |
8 files changed, 0 insertions, 9480 deletions
diff --git a/program/lib/Net/IDNA2.php b/program/lib/Net/IDNA2.php deleted file mode 100644 index 8c366fb8a..000000000 --- a/program/lib/Net/IDNA2.php +++ /dev/null @@ -1,3402 +0,0 @@ -<?php - -// {{{ license - -/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */ -// -// +----------------------------------------------------------------------+ -// | This library is free software; you can redistribute it and/or modify | -// | it under the terms of the GNU Lesser General Public License as | -// | published by the Free Software Foundation; either version 2.1 of the | -// | License, or (at your option) any later version. | -// | | -// | This library is distributed in the hope that it will be useful, but | -// | WITHOUT ANY WARRANTY; without even the implied warranty of | -// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | -// | Lesser General Public License for more details. | -// | | -// | You should have received a copy of the GNU Lesser General Public | -// | License along with this library; if not, write to the Free Software | -// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | -// | USA. | -// +----------------------------------------------------------------------+ -// - -// }}} -require_once 'Net/IDNA2/Exception.php'; -require_once 'Net/IDNA2/Exception/Nameprep.php'; - -/** - * Encode/decode Internationalized Domain Names. - * - * The class allows to convert internationalized domain names - * (see RFC 3490 for details) as they can be used with various registries worldwide - * to be translated between their original (localized) form and their encoded form - * as it will be used in the DNS (Domain Name System). - * - * The class provides two public methods, encode() and decode(), which do exactly - * what you would expect them to do. You are allowed to use complete domain names, - * simple strings and complete email addresses as well. That means, that you might - * use any of the following notations: - * - * - www.n�rgler.com - * - xn--nrgler-wxa - * - xn--brse-5qa.xn--knrz-1ra.info - * - * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4 - * array. Unicode output is available in the same formats. - * You can select your preferred format via {@link set_paramter()}. - * - * ACE input and output is always expected to be ASCII. - * - * @package Net - * @author Markus Nix <mnix@docuverse.de> - * @author Matthias Sommerfeld <mso@phlylabs.de> - * @author Stefan Neufeind <pear.neufeind@speedpartner.de> - * @version $Id: IDNA2.php 305344 2010-11-14 23:52:42Z neufeind $ - */ -class Net_IDNA2 -{ - // {{{ npdata - /** - * These Unicode codepoints are - * mapped to nothing, See RFC3454 for details - * - * @static - * @var array - * @access private - */ - private static $_np_map_nothing = array( - 0xAD, - 0x34F, - 0x1806, - 0x180B, - 0x180C, - 0x180D, - 0x200B, - 0x200C, - 0x200D, - 0x2060, - 0xFE00, - 0xFE01, - 0xFE02, - 0xFE03, - 0xFE04, - 0xFE05, - 0xFE06, - 0xFE07, - 0xFE08, - 0xFE09, - 0xFE0A, - 0xFE0B, - 0xFE0C, - 0xFE0D, - 0xFE0E, - 0xFE0F, - 0xFEFF - ); - - /** - * Prohibited codepints - * - * @static - * @var array - * @access private - */ - private static $_general_prohibited = array( - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 0xA, - 0xB, - 0xC, - 0xD, - 0xE, - 0xF, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21, - 0x22, - 0x23, - 0x24, - 0x25, - 0x26, - 0x27, - 0x28, - 0x29, - 0x2A, - 0x2B, - 0x2C, - 0x2F, - 0x3B, - 0x3C, - 0x3D, - 0x3E, - 0x3F, - 0x40, - 0x5B, - 0x5C, - 0x5D, - 0x5E, - 0x5F, - 0x60, - 0x7B, - 0x7C, - 0x7D, - 0x7E, - 0x7F, - 0x3002 - ); - - /** - * Codepints prohibited by Nameprep - * @static - * @var array - * @access private - */ - private static $_np_prohibit = array( - 0xA0, - 0x1680, - 0x2000, - 0x2001, - 0x2002, - 0x2003, - 0x2004, - 0x2005, - 0x2006, - 0x2007, - 0x2008, - 0x2009, - 0x200A, - 0x200B, - 0x202F, - 0x205F, - 0x3000, - 0x6DD, - 0x70F, - 0x180E, - 0x200C, - 0x200D, - 0x2028, - 0x2029, - 0xFEFF, - 0xFFF9, - 0xFFFA, - 0xFFFB, - 0xFFFC, - 0xFFFE, - 0xFFFF, - 0x1FFFE, - 0x1FFFF, - 0x2FFFE, - 0x2FFFF, - 0x3FFFE, - 0x3FFFF, - 0x4FFFE, - 0x4FFFF, - 0x5FFFE, - 0x5FFFF, - 0x6FFFE, - 0x6FFFF, - 0x7FFFE, - 0x7FFFF, - 0x8FFFE, - 0x8FFFF, - 0x9FFFE, - 0x9FFFF, - 0xAFFFE, - 0xAFFFF, - 0xBFFFE, - 0xBFFFF, - 0xCFFFE, - 0xCFFFF, - 0xDFFFE, - 0xDFFFF, - 0xEFFFE, - 0xEFFFF, - 0xFFFFE, - 0xFFFFF, - 0x10FFFE, - 0x10FFFF, - 0xFFF9, - 0xFFFA, - 0xFFFB, - 0xFFFC, - 0xFFFD, - 0x340, - 0x341, - 0x200E, - 0x200F, - 0x202A, - 0x202B, - 0x202C, - 0x202D, - 0x202E, - 0x206A, - 0x206B, - 0x206C, - 0x206D, - 0x206E, - 0x206F, - 0xE0001 - ); - - /** - * Codepoint ranges prohibited by nameprep - * - * @static - * @var array - * @access private - */ - private static $_np_prohibit_ranges = array( - array(0x80, 0x9F ), - array(0x2060, 0x206F ), - array(0x1D173, 0x1D17A ), - array(0xE000, 0xF8FF ), - array(0xF0000, 0xFFFFD ), - array(0x100000, 0x10FFFD), - array(0xFDD0, 0xFDEF ), - array(0xD800, 0xDFFF ), - array(0x2FF0, 0x2FFB ), - array(0xE0020, 0xE007F ) - ); - - /** - * Replacement mappings (casemapping, replacement sequences, ...) - * - * @static - * @var array - * @access private - */ - private static $_np_replacemaps = array( - 0x41 => array(0x61), - 0x42 => array(0x62), - 0x43 => array(0x63), - 0x44 => array(0x64), - 0x45 => array(0x65), - 0x46 => array(0x66), - 0x47 => array(0x67), - 0x48 => array(0x68), - 0x49 => array(0x69), - 0x4A => array(0x6A), - 0x4B => array(0x6B), - 0x4C => array(0x6C), - 0x4D => array(0x6D), - 0x4E => array(0x6E), - 0x4F => array(0x6F), - 0x50 => array(0x70), - 0x51 => array(0x71), - 0x52 => array(0x72), - 0x53 => array(0x73), - 0x54 => array(0x74), - 0x55 => array(0x75), - 0x56 => array(0x76), - 0x57 => array(0x77), - 0x58 => array(0x78), - 0x59 => array(0x79), - 0x5A => array(0x7A), - 0xB5 => array(0x3BC), - 0xC0 => array(0xE0), - 0xC1 => array(0xE1), - 0xC2 => array(0xE2), - 0xC3 => array(0xE3), - 0xC4 => array(0xE4), - 0xC5 => array(0xE5), - 0xC6 => array(0xE6), - 0xC7 => array(0xE7), - 0xC8 => array(0xE8), - 0xC9 => array(0xE9), - 0xCA => array(0xEA), - 0xCB => array(0xEB), - 0xCC => array(0xEC), - 0xCD => array(0xED), - 0xCE => array(0xEE), - 0xCF => array(0xEF), - 0xD0 => array(0xF0), - 0xD1 => array(0xF1), - 0xD2 => array(0xF2), - 0xD3 => array(0xF3), - 0xD4 => array(0xF4), - 0xD5 => array(0xF5), - 0xD6 => array(0xF6), - 0xD8 => array(0xF8), - 0xD9 => array(0xF9), - 0xDA => array(0xFA), - 0xDB => array(0xFB), - 0xDC => array(0xFC), - 0xDD => array(0xFD), - 0xDE => array(0xFE), - 0xDF => array(0x73, 0x73), - 0x100 => array(0x101), - 0x102 => array(0x103), - 0x104 => array(0x105), - 0x106 => array(0x107), - 0x108 => array(0x109), - 0x10A => array(0x10B), - 0x10C => array(0x10D), - 0x10E => array(0x10F), - 0x110 => array(0x111), - 0x112 => array(0x113), - 0x114 => array(0x115), - 0x116 => array(0x117), - 0x118 => array(0x119), - 0x11A => array(0x11B), - 0x11C => array(0x11D), - 0x11E => array(0x11F), - 0x120 => array(0x121), - 0x122 => array(0x123), - 0x124 => array(0x125), - 0x126 => array(0x127), - 0x128 => array(0x129), - 0x12A => array(0x12B), - 0x12C => array(0x12D), - 0x12E => array(0x12F), - 0x130 => array(0x69, 0x307), - 0x132 => array(0x133), - 0x134 => array(0x135), - 0x136 => array(0x137), - 0x139 => array(0x13A), - 0x13B => array(0x13C), - 0x13D => array(0x13E), - 0x13F => array(0x140), - 0x141 => array(0x142), - 0x143 => array(0x144), - 0x145 => array(0x146), - 0x147 => array(0x148), - 0x149 => array(0x2BC, 0x6E), - 0x14A => array(0x14B), - 0x14C => array(0x14D), - 0x14E => array(0x14F), - 0x150 => array(0x151), - 0x152 => array(0x153), - 0x154 => array(0x155), - 0x156 => array(0x157), - 0x158 => array(0x159), - 0x15A => array(0x15B), - 0x15C => array(0x15D), - 0x15E => array(0x15F), - 0x160 => array(0x161), - 0x162 => array(0x163), - 0x164 => array(0x165), - 0x166 => array(0x167), - 0x168 => array(0x169), - 0x16A => array(0x16B), - 0x16C => array(0x16D), - 0x16E => array(0x16F), - 0x170 => array(0x171), - 0x172 => array(0x173), - 0x174 => array(0x175), - 0x176 => array(0x177), - 0x178 => array(0xFF), - 0x179 => array(0x17A), - 0x17B => array(0x17C), - 0x17D => array(0x17E), - 0x17F => array(0x73), - 0x181 => array(0x253), - 0x182 => array(0x183), - 0x184 => array(0x185), - 0x186 => array(0x254), - 0x187 => array(0x188), - 0x189 => array(0x256), - 0x18A => array(0x257), - 0x18B => array(0x18C), - 0x18E => array(0x1DD), - 0x18F => array(0x259), - 0x190 => array(0x25B), - 0x191 => array(0x192), - 0x193 => array(0x260), - 0x194 => array(0x263), - 0x196 => array(0x269), - 0x197 => array(0x268), - 0x198 => array(0x199), - 0x19C => array(0x26F), - 0x19D => array(0x272), - 0x19F => array(0x275), - 0x1A0 => array(0x1A1), - 0x1A2 => array(0x1A3), - 0x1A4 => array(0x1A5), - 0x1A6 => array(0x280), - 0x1A7 => array(0x1A8), - 0x1A9 => array(0x283), - 0x1AC => array(0x1AD), - 0x1AE => array(0x288), - 0x1AF => array(0x1B0), - 0x1B1 => array(0x28A), - 0x1B2 => array(0x28B), - 0x1B3 => array(0x1B4), - 0x1B5 => array(0x1B6), - 0x1B7 => array(0x292), - 0x1B8 => array(0x1B9), - 0x1BC => array(0x1BD), - 0x1C4 => array(0x1C6), - 0x1C5 => array(0x1C6), - 0x1C7 => array(0x1C9), - 0x1C8 => array(0x1C9), - 0x1CA => array(0x1CC), - 0x1CB => array(0x1CC), - 0x1CD => array(0x1CE), - 0x1CF => array(0x1D0), - 0x1D1 => array(0x1D2), - 0x1D3 => array(0x1D4), - 0x1D5 => array(0x1D6), - 0x1D7 => array(0x1D8), - 0x1D9 => array(0x1DA), - 0x1DB => array(0x1DC), - 0x1DE => array(0x1DF), - 0x1E0 => array(0x1E1), - 0x1E2 => array(0x1E3), - 0x1E4 => array(0x1E5), - 0x1E6 => array(0x1E7), - 0x1E8 => array(0x1E9), - 0x1EA => array(0x1EB), - 0x1EC => array(0x1ED), - 0x1EE => array(0x1EF), - 0x1F0 => array(0x6A, 0x30C), - 0x1F1 => array(0x1F3), - 0x1F2 => array(0x1F3), - 0x1F4 => array(0x1F5), - 0x1F6 => array(0x195), - 0x1F7 => array(0x1BF), - 0x1F8 => array(0x1F9), - 0x1FA => array(0x1FB), - 0x1FC => array(0x1FD), - 0x1FE => array(0x1FF), - 0x200 => array(0x201), - 0x202 => array(0x203), - 0x204 => array(0x205), - 0x206 => array(0x207), - 0x208 => array(0x209), - 0x20A => array(0x20B), - 0x20C => array(0x20D), - 0x20E => array(0x20F), - 0x210 => array(0x211), - 0x212 => array(0x213), - 0x214 => array(0x215), - 0x216 => array(0x217), - 0x218 => array(0x219), - 0x21A => array(0x21B), - 0x21C => array(0x21D), - 0x21E => array(0x21F), - 0x220 => array(0x19E), - 0x222 => array(0x223), - 0x224 => array(0x225), - 0x226 => array(0x227), - 0x228 => array(0x229), - 0x22A => array(0x22B), - 0x22C => array(0x22D), - 0x22E => array(0x22F), - 0x230 => array(0x231), - 0x232 => array(0x233), - 0x345 => array(0x3B9), - 0x37A => array(0x20, 0x3B9), - 0x386 => array(0x3AC), - 0x388 => array(0x3AD), - 0x389 => array(0x3AE), - 0x38A => array(0x3AF), - 0x38C => array(0x3CC), - 0x38E => array(0x3CD), - 0x38F => array(0x3CE), - 0x390 => array(0x3B9, 0x308, 0x301), - 0x391 => array(0x3B1), - 0x392 => array(0x3B2), - 0x393 => array(0x3B3), - 0x394 => array(0x3B4), - 0x395 => array(0x3B5), - 0x396 => array(0x3B6), - 0x397 => array(0x3B7), - 0x398 => array(0x3B8), - 0x399 => array(0x3B9), - 0x39A => array(0x3BA), - 0x39B => array(0x3BB), - 0x39C => array(0x3BC), - 0x39D => array(0x3BD), - 0x39E => array(0x3BE), - 0x39F => array(0x3BF), - 0x3A0 => array(0x3C0), - 0x3A1 => array(0x3C1), - 0x3A3 => array(0x3C3), - 0x3A4 => array(0x3C4), - 0x3A5 => array(0x3C5), - 0x3A6 => array(0x3C6), - 0x3A7 => array(0x3C7), - 0x3A8 => array(0x3C8), - 0x3A9 => array(0x3C9), - 0x3AA => array(0x3CA), - 0x3AB => array(0x3CB), - 0x3B0 => array(0x3C5, 0x308, 0x301), - 0x3C2 => array(0x3C3), - 0x3D0 => array(0x3B2), - 0x3D1 => array(0x3B8), - 0x3D2 => array(0x3C5), - 0x3D3 => array(0x3CD), - 0x3D4 => array(0x3CB), - 0x3D5 => array(0x3C6), - 0x3D6 => array(0x3C0), - 0x3D8 => array(0x3D9), - 0x3DA => array(0x3DB), - 0x3DC => array(0x3DD), - 0x3DE => array(0x3DF), - 0x3E0 => array(0x3E1), - 0x3E2 => array(0x3E3), - 0x3E4 => array(0x3E5), - 0x3E6 => array(0x3E7), - 0x3E8 => array(0x3E9), - 0x3EA => array(0x3EB), - 0x3EC => array(0x3ED), - 0x3EE => array(0x3EF), - 0x3F0 => array(0x3BA), - 0x3F1 => array(0x3C1), - 0x3F2 => array(0x3C3), - 0x3F4 => array(0x3B8), - 0x3F5 => array(0x3B5), - 0x400 => array(0x450), - 0x401 => array(0x451), - 0x402 => array(0x452), - 0x403 => array(0x453), - 0x404 => array(0x454), - 0x405 => array(0x455), - 0x406 => array(0x456), - 0x407 => array(0x457), - 0x408 => array(0x458), - 0x409 => array(0x459), - 0x40A => array(0x45A), - 0x40B => array(0x45B), - 0x40C => array(0x45C), - 0x40D => array(0x45D), - 0x40E => array(0x45E), - 0x40F => array(0x45F), - 0x410 => array(0x430), - 0x411 => array(0x431), - 0x412 => array(0x432), - 0x413 => array(0x433), - 0x414 => array(0x434), - 0x415 => array(0x435), - 0x416 => array(0x436), - 0x417 => array(0x437), - 0x418 => array(0x438), - 0x419 => array(0x439), - 0x41A => array(0x43A), - 0x41B => array(0x43B), - 0x41C => array(0x43C), - 0x41D => array(0x43D), - 0x41E => array(0x43E), - 0x41F => array(0x43F), - 0x420 => array(0x440), - 0x421 => array(0x441), - 0x422 => array(0x442), - 0x423 => array(0x443), - 0x424 => array(0x444), - 0x425 => array(0x445), - 0x426 => array(0x446), - 0x427 => array(0x447), - 0x428 => array(0x448), - 0x429 => array(0x449), - 0x42A => array(0x44A), - 0x42B => array(0x44B), - 0x42C => array(0x44C), - 0x42D => array(0x44D), - 0x42E => array(0x44E), - 0x42F => array(0x44F), - 0x460 => array(0x461), - 0x462 => array(0x463), - 0x464 => array(0x465), - 0x466 => array(0x467), - 0x468 => array(0x469), - 0x46A => array(0x46B), - 0x46C => array(0x46D), - 0x46E => array(0x46F), - 0x470 => array(0x471), - 0x472 => array(0x473), - 0x474 => array(0x475), - 0x476 => array(0x477), - 0x478 => array(0x479), - 0x47A => array(0x47B), - 0x47C => array(0x47D), - 0x47E => array(0x47F), - 0x480 => array(0x481), - 0x48A => array(0x48B), - 0x48C => array(0x48D), - 0x48E => array(0x48F), - 0x490 => array(0x491), - 0x492 => array(0x493), - 0x494 => array(0x495), - 0x496 => array(0x497), - 0x498 => array(0x499), - 0x49A => array(0x49B), - 0x49C => array(0x49D), - 0x49E => array(0x49F), - 0x4A0 => array(0x4A1), - 0x4A2 => array(0x4A3), - 0x4A4 => array(0x4A5), - 0x4A6 => array(0x4A7), - 0x4A8 => array(0x4A9), - 0x4AA => array(0x4AB), - 0x4AC => array(0x4AD), - 0x4AE => array(0x4AF), - 0x4B0 => array(0x4B1), - 0x4B2 => array(0x4B3), - 0x4B4 => array(0x4B5), - 0x4B6 => array(0x4B7), - 0x4B8 => array(0x4B9), - 0x4BA => array(0x4BB), - 0x4BC => array(0x4BD), - 0x4BE => array(0x4BF), - 0x4C1 => array(0x4C2), - 0x4C3 => array(0x4C4), - 0x4C5 => array(0x4C6), - 0x4C7 => array(0x4C8), - 0x4C9 => array(0x4CA), - 0x4CB => array(0x4CC), - 0x4CD => array(0x4CE), - 0x4D0 => array(0x4D1), - 0x4D2 => array(0x4D3), - 0x4D4 => array(0x4D5), - 0x4D6 => array(0x4D7), - 0x4D8 => array(0x4D9), - 0x4DA => array(0x4DB), - 0x4DC => array(0x4DD), - 0x4DE => array(0x4DF), - 0x4E0 => array(0x4E1), - 0x4E2 => array(0x4E3), - 0x4E4 => array(0x4E5), - 0x4E6 => array(0x4E7), - 0x4E8 => array(0x4E9), - 0x4EA => array(0x4EB), - 0x4EC => array(0x4ED), - 0x4EE => array(0x4EF), - 0x4F0 => array(0x4F1), - 0x4F2 => array(0x4F3), - 0x4F4 => array(0x4F5), - 0x4F8 => array(0x4F9), - 0x500 => array(0x501), - 0x502 => array(0x503), - 0x504 => array(0x505), - 0x506 => array(0x507), - 0x508 => array(0x509), - 0x50A => array(0x50B), - 0x50C => array(0x50D), - 0x50E => array(0x50F), - 0x531 => array(0x561), - 0x532 => array(0x562), - 0x533 => array(0x563), - 0x534 => array(0x564), - 0x535 => array(0x565), - 0x536 => array(0x566), - 0x537 => array(0x567), - 0x538 => array(0x568), - 0x539 => array(0x569), - 0x53A => array(0x56A), - 0x53B => array(0x56B), - 0x53C => array(0x56C), - 0x53D => array(0x56D), - 0x53E => array(0x56E), - 0x53F => array(0x56F), - 0x540 => array(0x570), - 0x541 => array(0x571), - 0x542 => array(0x572), - 0x543 => array(0x573), - 0x544 => array(0x574), - 0x545 => array(0x575), - 0x546 => array(0x576), - 0x547 => array(0x577), - 0x548 => array(0x578), - 0x549 => array(0x579), - 0x54A => array(0x57A), - 0x54B => array(0x57B), - 0x54C => array(0x57C), - 0x54D => array(0x57D), - 0x54E => array(0x57E), - 0x54F => array(0x57F), - 0x550 => array(0x580), - 0x551 => array(0x581), - 0x552 => array(0x582), - 0x553 => array(0x583), - 0x554 => array(0x584), - 0x555 => array(0x585), - 0x556 => array(0x586), - 0x587 => array(0x565, 0x582), - 0x1E00 => array(0x1E01), - 0x1E02 => array(0x1E03), - 0x1E04 => array(0x1E05), - 0x1E06 => array(0x1E07), - 0x1E08 => array(0x1E09), - 0x1E0A => array(0x1E0B), - 0x1E0C => array(0x1E0D), - 0x1E0E => array(0x1E0F), - 0x1E10 => array(0x1E11), - 0x1E12 => array(0x1E13), - 0x1E14 => array(0x1E15), - 0x1E16 => array(0x1E17), - 0x1E18 => array(0x1E19), - 0x1E1A => array(0x1E1B), - 0x1E1C => array(0x1E1D), - 0x1E1E => array(0x1E1F), - 0x1E20 => array(0x1E21), - 0x1E22 => array(0x1E23), - 0x1E24 => array(0x1E25), - 0x1E26 => array(0x1E27), - 0x1E28 => array(0x1E29), - 0x1E2A => array(0x1E2B), - 0x1E2C => array(0x1E2D), - 0x1E2E => array(0x1E2F), - 0x1E30 => array(0x1E31), - 0x1E32 => array(0x1E33), - 0x1E34 => array(0x1E35), - 0x1E36 => array(0x1E37), - 0x1E38 => array(0x1E39), - 0x1E3A => array(0x1E3B), - 0x1E3C => array(0x1E3D), - 0x1E3E => array(0x1E3F), - 0x1E40 => array(0x1E41), - 0x1E42 => array(0x1E43), - 0x1E44 => array(0x1E45), - 0x1E46 => array(0x1E47), - 0x1E48 => array(0x1E49), - 0x1E4A => array(0x1E4B), - 0x1E4C => array(0x1E4D), - 0x1E4E => array(0x1E4F), - 0x1E50 => array(0x1E51), - 0x1E52 => array(0x1E53), - 0x1E54 => array(0x1E55), - 0x1E56 => array(0x1E57), - 0x1E58 => array(0x1E59), - 0x1E5A => array(0x1E5B), - 0x1E5C => array(0x1E5D), - 0x1E5E => array(0x1E5F), - 0x1E60 => array(0x1E61), - 0x1E62 => array(0x1E63), - 0x1E64 => array(0x1E65), - 0x1E66 => array(0x1E67), - 0x1E68 => array(0x1E69), - 0x1E6A => array(0x1E6B), - 0x1E6C => array(0x1E6D), - 0x1E6E => array(0x1E6F), - 0x1E70 => array(0x1E71), - 0x1E72 => array(0x1E73), - 0x1E74 => array(0x1E75), - 0x1E76 => array(0x1E77), - 0x1E78 => array(0x1E79), - 0x1E7A => array(0x1E7B), - 0x1E7C => array(0x1E7D), - 0x1E7E => array(0x1E7F), - 0x1E80 => array(0x1E81), - 0x1E82 => array(0x1E83), - 0x1E84 => array(0x1E85), - 0x1E86 => array(0x1E87), - 0x1E88 => array(0x1E89), - 0x1E8A => array(0x1E8B), - 0x1E8C => array(0x1E8D), - 0x1E8E => array(0x1E8F), - 0x1E90 => array(0x1E91), - 0x1E92 => array(0x1E93), - 0x1E94 => array(0x1E95), - 0x1E96 => array(0x68, 0x331), - 0x1E97 => array(0x74, 0x308), - 0x1E98 => array(0x77, 0x30A), - 0x1E99 => array(0x79, 0x30A), - 0x1E9A => array(0x61, 0x2BE), - 0x1E9B => array(0x1E61), - 0x1EA0 => array(0x1EA1), - 0x1EA2 => array(0x1EA3), - 0x1EA4 => array(0x1EA5), - 0x1EA6 => array(0x1EA7), - 0x1EA8 => array(0x1EA9), - 0x1EAA => array(0x1EAB), - 0x1EAC => array(0x1EAD), - 0x1EAE => array(0x1EAF), - 0x1EB0 => array(0x1EB1), - 0x1EB2 => array(0x1EB3), - 0x1EB4 => array(0x1EB5), - 0x1EB6 => array(0x1EB7), - 0x1EB8 => array(0x1EB9), - 0x1EBA => array(0x1EBB), - 0x1EBC => array(0x1EBD), - 0x1EBE => array(0x1EBF), - 0x1EC0 => array(0x1EC1), - 0x1EC2 => array(0x1EC3), - 0x1EC4 => array(0x1EC5), - 0x1EC6 => array(0x1EC7), - 0x1EC8 => array(0x1EC9), - 0x1ECA => array(0x1ECB), - 0x1ECC => array(0x1ECD), - 0x1ECE => array(0x1ECF), - 0x1ED0 => array(0x1ED1), - 0x1ED2 => array(0x1ED3), - 0x1ED4 => array(0x1ED5), - 0x1ED6 => array(0x1ED7), - 0x1ED8 => array(0x1ED9), - 0x1EDA => array(0x1EDB), - 0x1EDC => array(0x1EDD), - 0x1EDE => array(0x1EDF), - 0x1EE0 => array(0x1EE1), - 0x1EE2 => array(0x1EE3), - 0x1EE4 => array(0x1EE5), - 0x1EE6 => array(0x1EE7), - 0x1EE8 => array(0x1EE9), - 0x1EEA => array(0x1EEB), - 0x1EEC => array(0x1EED), - 0x1EEE => array(0x1EEF), - 0x1EF0 => array(0x1EF1), - 0x1EF2 => array(0x1EF3), - 0x1EF4 => array(0x1EF5), - 0x1EF6 => array(0x1EF7), - 0x1EF8 => array(0x1EF9), - 0x1F08 => array(0x1F00), - 0x1F09 => array(0x1F01), - 0x1F0A => array(0x1F02), - 0x1F0B => array(0x1F03), - 0x1F0C => array(0x1F04), - 0x1F0D => array(0x1F05), - 0x1F0E => array(0x1F06), - 0x1F0F => array(0x1F07), - 0x1F18 => array(0x1F10), - 0x1F19 => array(0x1F11), - 0x1F1A => array(0x1F12), - 0x1F1B => array(0x1F13), - 0x1F1C => array(0x1F14), - 0x1F1D => array(0x1F15), - 0x1F28 => array(0x1F20), - 0x1F29 => array(0x1F21), - 0x1F2A => array(0x1F22), - 0x1F2B => array(0x1F23), - 0x1F2C => array(0x1F24), - 0x1F2D => array(0x1F25), - 0x1F2E => array(0x1F26), - 0x1F2F => array(0x1F27), - 0x1F38 => array(0x1F30), - 0x1F39 => array(0x1F31), - 0x1F3A => array(0x1F32), - 0x1F3B => array(0x1F33), - 0x1F3C => array(0x1F34), - 0x1F3D => array(0x1F35), - 0x1F3E => array(0x1F36), - 0x1F3F => array(0x1F37), - 0x1F48 => array(0x1F40), - 0x1F49 => array(0x1F41), - 0x1F4A => array(0x1F42), - 0x1F4B => array(0x1F43), - 0x1F4C => array(0x1F44), - 0x1F4D => array(0x1F45), - 0x1F50 => array(0x3C5, 0x313), - 0x1F52 => array(0x3C5, 0x313, 0x300), - 0x1F54 => array(0x3C5, 0x313, 0x301), - 0x1F56 => array(0x3C5, 0x313, 0x342), - 0x1F59 => array(0x1F51), - 0x1F5B => array(0x1F53), - 0x1F5D => array(0x1F55), - 0x1F5F => array(0x1F57), - 0x1F68 => array(0x1F60), - 0x1F69 => array(0x1F61), - 0x1F6A => array(0x1F62), - 0x1F6B => array(0x1F63), - 0x1F6C => array(0x1F64), - 0x1F6D => array(0x1F65), - 0x1F6E => array(0x1F66), - 0x1F6F => array(0x1F67), - 0x1F80 => array(0x1F00, 0x3B9), - 0x1F81 => array(0x1F01, 0x3B9), - 0x1F82 => array(0x1F02, 0x3B9), - 0x1F83 => array(0x1F03, 0x3B9), - 0x1F84 => array(0x1F04, 0x3B9), - 0x1F85 => array(0x1F05, 0x3B9), - 0x1F86 => array(0x1F06, 0x3B9), - 0x1F87 => array(0x1F07, 0x3B9), - 0x1F88 => array(0x1F00, 0x3B9), - 0x1F89 => array(0x1F01, 0x3B9), - 0x1F8A => array(0x1F02, 0x3B9), - 0x1F8B => array(0x1F03, 0x3B9), - 0x1F8C => array(0x1F04, 0x3B9), - 0x1F8D => array(0x1F05, 0x3B9), - 0x1F8E => array(0x1F06, 0x3B9), - 0x1F8F => array(0x1F07, 0x3B9), - 0x1F90 => array(0x1F20, 0x3B9), - 0x1F91 => array(0x1F21, 0x3B9), - 0x1F92 => array(0x1F22, 0x3B9), - 0x1F93 => array(0x1F23, 0x3B9), - 0x1F94 => array(0x1F24, 0x3B9), - 0x1F95 => array(0x1F25, 0x3B9), - 0x1F96 => array(0x1F26, 0x3B9), - 0x1F97 => array(0x1F27, 0x3B9), - 0x1F98 => array(0x1F20, 0x3B9), - 0x1F99 => array(0x1F21, 0x3B9), - 0x1F9A => array(0x1F22, 0x3B9), - 0x1F9B => array(0x1F23, 0x3B9), - 0x1F9C => array(0x1F24, 0x3B9), - 0x1F9D => array(0x1F25, 0x3B9), - 0x1F9E => array(0x1F26, 0x3B9), - 0x1F9F => array(0x1F27, 0x3B9), - 0x1FA0 => array(0x1F60, 0x3B9), - 0x1FA1 => array(0x1F61, 0x3B9), - 0x1FA2 => array(0x1F62, 0x3B9), - 0x1FA3 => array(0x1F63, 0x3B9), - 0x1FA4 => array(0x1F64, 0x3B9), - 0x1FA5 => array(0x1F65, 0x3B9), - 0x1FA6 => array(0x1F66, 0x3B9), - 0x1FA7 => array(0x1F67, 0x3B9), - 0x1FA8 => array(0x1F60, 0x3B9), - 0x1FA9 => array(0x1F61, 0x3B9), - 0x1FAA => array(0x1F62, 0x3B9), - 0x1FAB => array(0x1F63, 0x3B9), - 0x1FAC => array(0x1F64, 0x3B9), - 0x1FAD => array(0x1F65, 0x3B9), - 0x1FAE => array(0x1F66, 0x3B9), - 0x1FAF => array(0x1F67, 0x3B9), - 0x1FB2 => array(0x1F70, 0x3B9), - 0x1FB3 => array(0x3B1, 0x3B9), - 0x1FB4 => array(0x3AC, 0x3B9), - 0x1FB6 => array(0x3B1, 0x342), - 0x1FB7 => array(0x3B1, 0x342, 0x3B9), - 0x1FB8 => array(0x1FB0), - 0x1FB9 => array(0x1FB1), - 0x1FBA => array(0x1F70), - 0x1FBB => array(0x1F71), - 0x1FBC => array(0x3B1, 0x3B9), - 0x1FBE => array(0x3B9), - 0x1FC2 => array(0x1F74, 0x3B9), - 0x1FC3 => array(0x3B7, 0x3B9), - 0x1FC4 => array(0x3AE, 0x3B9), - 0x1FC6 => array(0x3B7, 0x342), - 0x1FC7 => array(0x3B7, 0x342, 0x3B9), - 0x1FC8 => array(0x1F72), - 0x1FC9 => array(0x1F73), - 0x1FCA => array(0x1F74), - 0x1FCB => array(0x1F75), - 0x1FCC => array(0x3B7, 0x3B9), - 0x1FD2 => array(0x3B9, 0x308, 0x300), - 0x1FD3 => array(0x3B9, 0x308, 0x301), - 0x1FD6 => array(0x3B9, 0x342), - 0x1FD7 => array(0x3B9, 0x308, 0x342), - 0x1FD8 => array(0x1FD0), - 0x1FD9 => array(0x1FD1), - 0x1FDA => array(0x1F76), - 0x1FDB => array(0x1F77), - 0x1FE2 => array(0x3C5, 0x308, 0x300), - 0x1FE3 => array(0x3C5, 0x308, 0x301), - 0x1FE4 => array(0x3C1, 0x313), - 0x1FE6 => array(0x3C5, 0x342), - 0x1FE7 => array(0x3C5, 0x308, 0x342), - 0x1FE8 => array(0x1FE0), - 0x1FE9 => array(0x1FE1), - 0x1FEA => array(0x1F7A), - 0x1FEB => array(0x1F7B), - 0x1FEC => array(0x1FE5), - 0x1FF2 => array(0x1F7C, 0x3B9), - 0x1FF3 => array(0x3C9, 0x3B9), - 0x1FF4 => array(0x3CE, 0x3B9), - 0x1FF6 => array(0x3C9, 0x342), - 0x1FF7 => array(0x3C9, 0x342, 0x3B9), - 0x1FF8 => array(0x1F78), - 0x1FF9 => array(0x1F79), - 0x1FFA => array(0x1F7C), - 0x1FFB => array(0x1F7D), - 0x1FFC => array(0x3C9, 0x3B9), - 0x20A8 => array(0x72, 0x73), - 0x2102 => array(0x63), - 0x2103 => array(0xB0, 0x63), - 0x2107 => array(0x25B), - 0x2109 => array(0xB0, 0x66), - 0x210B => array(0x68), - 0x210C => array(0x68), - 0x210D => array(0x68), - 0x2110 => array(0x69), - 0x2111 => array(0x69), - 0x2112 => array(0x6C), - 0x2115 => array(0x6E), - 0x2116 => array(0x6E, 0x6F), - 0x2119 => array(0x70), - 0x211A => array(0x71), - 0x211B => array(0x72), - 0x211C => array(0x72), - 0x211D => array(0x72), - 0x2120 => array(0x73, 0x6D), - 0x2121 => array(0x74, 0x65, 0x6C), - 0x2122 => array(0x74, 0x6D), - 0x2124 => array(0x7A), - 0x2126 => array(0x3C9), - 0x2128 => array(0x7A), - 0x212A => array(0x6B), - 0x212B => array(0xE5), - 0x212C => array(0x62), - 0x212D => array(0x63), - 0x2130 => array(0x65), - 0x2131 => array(0x66), - 0x2133 => array(0x6D), - 0x213E => array(0x3B3), - 0x213F => array(0x3C0), - 0x2145 => array(0x64), - 0x2160 => array(0x2170), - 0x2161 => array(0x2171), - 0x2162 => array(0x2172), - 0x2163 => array(0x2173), - 0x2164 => array(0x2174), - 0x2165 => array(0x2175), - 0x2166 => array(0x2176), - 0x2167 => array(0x2177), - 0x2168 => array(0x2178), - 0x2169 => array(0x2179), - 0x216A => array(0x217A), - 0x216B => array(0x217B), - 0x216C => array(0x217C), - 0x216D => array(0x217D), - 0x216E => array(0x217E), - 0x216F => array(0x217F), - 0x24B6 => array(0x24D0), - 0x24B7 => array(0x24D1), - 0x24B8 => array(0x24D2), - 0x24B9 => array(0x24D3), - 0x24BA => array(0x24D4), - 0x24BB => array(0x24D5), - 0x24BC => array(0x24D6), - 0x24BD => array(0x24D7), - 0x24BE => array(0x24D8), - 0x24BF => array(0x24D9), - 0x24C0 => array(0x24DA), - 0x24C1 => array(0x24DB), - 0x24C2 => array(0x24DC), - 0x24C3 => array(0x24DD), - 0x24C4 => array(0x24DE), - 0x24C5 => array(0x24DF), - 0x24C6 => array(0x24E0), - 0x24C7 => array(0x24E1), - 0x24C8 => array(0x24E2), - 0x24C9 => array(0x24E3), - 0x24CA => array(0x24E4), - 0x24CB => array(0x24E5), - 0x24CC => array(0x24E6), - 0x24CD => array(0x24E7), - 0x24CE => array(0x24E8), - 0x24CF => array(0x24E9), - 0x3371 => array(0x68, 0x70, 0x61), - 0x3373 => array(0x61, 0x75), - 0x3375 => array(0x6F, 0x76), - 0x3380 => array(0x70, 0x61), - 0x3381 => array(0x6E, 0x61), - 0x3382 => array(0x3BC, 0x61), - 0x3383 => array(0x6D, 0x61), - 0x3384 => array(0x6B, 0x61), - 0x3385 => array(0x6B, 0x62), - 0x3386 => array(0x6D, 0x62), - 0x3387 => array(0x67, 0x62), - 0x338A => array(0x70, 0x66), - 0x338B => array(0x6E, 0x66), - 0x338C => array(0x3BC, 0x66), - 0x3390 => array(0x68, 0x7A), - 0x3391 => array(0x6B, 0x68, 0x7A), - 0x3392 => array(0x6D, 0x68, 0x7A), - 0x3393 => array(0x67, 0x68, 0x7A), - 0x3394 => array(0x74, 0x68, 0x7A), - 0x33A9 => array(0x70, 0x61), - 0x33AA => array(0x6B, 0x70, 0x61), - 0x33AB => array(0x6D, 0x70, 0x61), - 0x33AC => array(0x67, 0x70, 0x61), - 0x33B4 => array(0x70, 0x76), - 0x33B5 => array(0x6E, 0x76), - 0x33B6 => array(0x3BC, 0x76), - 0x33B7 => array(0x6D, 0x76), - 0x33B8 => array(0x6B, 0x76), - 0x33B9 => array(0x6D, 0x76), - 0x33BA => array(0x70, 0x77), - 0x33BB => array(0x6E, 0x77), - 0x33BC => array(0x3BC, 0x77), - 0x33BD => array(0x6D, 0x77), - 0x33BE => array(0x6B, 0x77), - 0x33BF => array(0x6D, 0x77), - 0x33C0 => array(0x6B, 0x3C9), - 0x33C1 => array(0x6D, 0x3C9), - /* 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ - 0x33C3 => array(0x62, 0x71), - 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), - 0x33C7 => array(0x63, 0x6F, 0x2E), - 0x33C8 => array(0x64, 0x62), - 0x33C9 => array(0x67, 0x79), - 0x33CB => array(0x68, 0x70), - 0x33CD => array(0x6B, 0x6B), - 0x33CE => array(0x6B, 0x6D), - 0x33D7 => array(0x70, 0x68), - 0x33D9 => array(0x70, 0x70, 0x6D), - 0x33DA => array(0x70, 0x72), - 0x33DC => array(0x73, 0x76), - 0x33DD => array(0x77, 0x62), - 0xFB00 => array(0x66, 0x66), - 0xFB01 => array(0x66, 0x69), - 0xFB02 => array(0x66, 0x6C), - 0xFB03 => array(0x66, 0x66, 0x69), - 0xFB04 => array(0x66, 0x66, 0x6C), - 0xFB05 => array(0x73, 0x74), - 0xFB06 => array(0x73, 0x74), - 0xFB13 => array(0x574, 0x576), - 0xFB14 => array(0x574, 0x565), - 0xFB15 => array(0x574, 0x56B), - 0xFB16 => array(0x57E, 0x576), - 0xFB17 => array(0x574, 0x56D), - 0xFF21 => array(0xFF41), - 0xFF22 => array(0xFF42), - 0xFF23 => array(0xFF43), - 0xFF24 => array(0xFF44), - 0xFF25 => array(0xFF45), - 0xFF26 => array(0xFF46), - 0xFF27 => array(0xFF47), - 0xFF28 => array(0xFF48), - 0xFF29 => array(0xFF49), - 0xFF2A => array(0xFF4A), - 0xFF2B => array(0xFF4B), - 0xFF2C => array(0xFF4C), - 0xFF2D => array(0xFF4D), - 0xFF2E => array(0xFF4E), - 0xFF2F => array(0xFF4F), - 0xFF30 => array(0xFF50), - 0xFF31 => array(0xFF51), - 0xFF32 => array(0xFF52), - 0xFF33 => array(0xFF53), - 0xFF34 => array(0xFF54), - 0xFF35 => array(0xFF55), - 0xFF36 => array(0xFF56), - 0xFF37 => array(0xFF57), - 0xFF38 => array(0xFF58), - 0xFF39 => array(0xFF59), - 0xFF3A => array(0xFF5A), - 0x10400 => array(0x10428), - 0x10401 => array(0x10429), - 0x10402 => array(0x1042A), - 0x10403 => array(0x1042B), - 0x10404 => array(0x1042C), - 0x10405 => array(0x1042D), - 0x10406 => array(0x1042E), - 0x10407 => array(0x1042F), - 0x10408 => array(0x10430), - 0x10409 => array(0x10431), - 0x1040A => array(0x10432), - 0x1040B => array(0x10433), - 0x1040C => array(0x10434), - 0x1040D => array(0x10435), - 0x1040E => array(0x10436), - 0x1040F => array(0x10437), - 0x10410 => array(0x10438), - 0x10411 => array(0x10439), - 0x10412 => array(0x1043A), - 0x10413 => array(0x1043B), - 0x10414 => array(0x1043C), - 0x10415 => array(0x1043D), - 0x10416 => array(0x1043E), - 0x10417 => array(0x1043F), - 0x10418 => array(0x10440), - 0x10419 => array(0x10441), - 0x1041A => array(0x10442), - 0x1041B => array(0x10443), - 0x1041C => array(0x10444), - 0x1041D => array(0x10445), - 0x1041E => array(0x10446), - 0x1041F => array(0x10447), - 0x10420 => array(0x10448), - 0x10421 => array(0x10449), - 0x10422 => array(0x1044A), - 0x10423 => array(0x1044B), - 0x10424 => array(0x1044C), - 0x10425 => array(0x1044D), - 0x1D400 => array(0x61), - 0x1D401 => array(0x62), - 0x1D402 => array(0x63), - 0x1D403 => array(0x64), - 0x1D404 => array(0x65), - 0x1D405 => array(0x66), - 0x1D406 => array(0x67), - 0x1D407 => array(0x68), - 0x1D408 => array(0x69), - 0x1D409 => array(0x6A), - 0x1D40A => array(0x6B), - 0x1D40B => array(0x6C), - 0x1D40C => array(0x6D), - 0x1D40D => array(0x6E), - 0x1D40E => array(0x6F), - 0x1D40F => array(0x70), - 0x1D410 => array(0x71), - 0x1D411 => array(0x72), - 0x1D412 => array(0x73), - 0x1D413 => array(0x74), - 0x1D414 => array(0x75), - 0x1D415 => array(0x76), - 0x1D416 => array(0x77), - 0x1D417 => array(0x78), - 0x1D418 => array(0x79), - 0x1D419 => array(0x7A), - 0x1D434 => array(0x61), - 0x1D435 => array(0x62), - 0x1D436 => array(0x63), - 0x1D437 => array(0x64), - 0x1D438 => array(0x65), - 0x1D439 => array(0x66), - 0x1D43A => array(0x67), - 0x1D43B => array(0x68), - 0x1D43C => array(0x69), - 0x1D43D => array(0x6A), - 0x1D43E => array(0x6B), - 0x1D43F => array(0x6C), - 0x1D440 => array(0x6D), - 0x1D441 => array(0x6E), - 0x1D442 => array(0x6F), - 0x1D443 => array(0x70), - 0x1D444 => array(0x71), - 0x1D445 => array(0x72), - 0x1D446 => array(0x73), - 0x1D447 => array(0x74), - 0x1D448 => array(0x75), - 0x1D449 => array(0x76), - 0x1D44A => array(0x77), - 0x1D44B => array(0x78), - 0x1D44C => array(0x79), - 0x1D44D => array(0x7A), - 0x1D468 => array(0x61), - 0x1D469 => array(0x62), - 0x1D46A => array(0x63), - 0x1D46B => array(0x64), - 0x1D46C => array(0x65), - 0x1D46D => array(0x66), - 0x1D46E => array(0x67), - 0x1D46F => array(0x68), - 0x1D470 => array(0x69), - 0x1D471 => array(0x6A), - 0x1D472 => array(0x6B), - 0x1D473 => array(0x6C), - 0x1D474 => array(0x6D), - 0x1D475 => array(0x6E), - 0x1D476 => array(0x6F), - 0x1D477 => array(0x70), - 0x1D478 => array(0x71), - 0x1D479 => array(0x72), - 0x1D47A => array(0x73), - 0x1D47B => array(0x74), - 0x1D47C => array(0x75), - 0x1D47D => array(0x76), - 0x1D47E => array(0x77), - 0x1D47F => array(0x78), - 0x1D480 => array(0x79), - 0x1D481 => array(0x7A), - 0x1D49C => array(0x61), - 0x1D49E => array(0x63), - 0x1D49F => array(0x64), - 0x1D4A2 => array(0x67), - 0x1D4A5 => array(0x6A), - 0x1D4A6 => array(0x6B), - 0x1D4A9 => array(0x6E), - 0x1D4AA => array(0x6F), - 0x1D4AB => array(0x70), - 0x1D4AC => array(0x71), - 0x1D4AE => array(0x73), - 0x1D4AF => array(0x74), - 0x1D4B0 => array(0x75), - 0x1D4B1 => array(0x76), - 0x1D4B2 => array(0x77), - 0x1D4B3 => array(0x78), - 0x1D4B4 => array(0x79), - 0x1D4B5 => array(0x7A), - 0x1D4D0 => array(0x61), - 0x1D4D1 => array(0x62), - 0x1D4D2 => array(0x63), - 0x1D4D3 => array(0x64), - 0x1D4D4 => array(0x65), - 0x1D4D5 => array(0x66), - 0x1D4D6 => array(0x67), - 0x1D4D7 => array(0x68), - 0x1D4D8 => array(0x69), - 0x1D4D9 => array(0x6A), - 0x1D4DA => array(0x6B), - 0x1D4DB => array(0x6C), - 0x1D4DC => array(0x6D), - 0x1D4DD => array(0x6E), - 0x1D4DE => array(0x6F), - 0x1D4DF => array(0x70), - 0x1D4E0 => array(0x71), - 0x1D4E1 => array(0x72), - 0x1D4E2 => array(0x73), - 0x1D4E3 => array(0x74), - 0x1D4E4 => array(0x75), - 0x1D4E5 => array(0x76), - 0x1D4E6 => array(0x77), - 0x1D4E7 => array(0x78), - 0x1D4E8 => array(0x79), - 0x1D4E9 => array(0x7A), - 0x1D504 => array(0x61), - 0x1D505 => array(0x62), - 0x1D507 => array(0x64), - 0x1D508 => array(0x65), - 0x1D509 => array(0x66), - 0x1D50A => array(0x67), - 0x1D50D => array(0x6A), - 0x1D50E => array(0x6B), - 0x1D50F => array(0x6C), - 0x1D510 => array(0x6D), - 0x1D511 => array(0x6E), - 0x1D512 => array(0x6F), - 0x1D513 => array(0x70), - 0x1D514 => array(0x71), - 0x1D516 => array(0x73), - 0x1D517 => array(0x74), - 0x1D518 => array(0x75), - 0x1D519 => array(0x76), - 0x1D51A => array(0x77), - 0x1D51B => array(0x78), - 0x1D51C => array(0x79), - 0x1D538 => array(0x61), - 0x1D539 => array(0x62), - 0x1D53B => array(0x64), - 0x1D53C => array(0x65), - 0x1D53D => array(0x66), - 0x1D53E => array(0x67), - 0x1D540 => array(0x69), - 0x1D541 => array(0x6A), - 0x1D542 => array(0x6B), - 0x1D543 => array(0x6C), - 0x1D544 => array(0x6D), - 0x1D546 => array(0x6F), - 0x1D54A => array(0x73), - 0x1D54B => array(0x74), - 0x1D54C => array(0x75), - 0x1D54D => array(0x76), - 0x1D54E => array(0x77), - 0x1D54F => array(0x78), - 0x1D550 => array(0x79), - 0x1D56C => array(0x61), - 0x1D56D => array(0x62), - 0x1D56E => array(0x63), - 0x1D56F => array(0x64), - 0x1D570 => array(0x65), - 0x1D571 => array(0x66), - 0x1D572 => array(0x67), - 0x1D573 => array(0x68), - 0x1D574 => array(0x69), - 0x1D575 => array(0x6A), - 0x1D576 => array(0x6B), - 0x1D577 => array(0x6C), - 0x1D578 => array(0x6D), - 0x1D579 => array(0x6E), - 0x1D57A => array(0x6F), - 0x1D57B => array(0x70), - 0x1D57C => array(0x71), - 0x1D57D => array(0x72), - 0x1D57E => array(0x73), - 0x1D57F => array(0x74), - 0x1D580 => array(0x75), - 0x1D581 => array(0x76), - 0x1D582 => array(0x77), - 0x1D583 => array(0x78), - 0x1D584 => array(0x79), - 0x1D585 => array(0x7A), - 0x1D5A0 => array(0x61), - 0x1D5A1 => array(0x62), - 0x1D5A2 => array(0x63), - 0x1D5A3 => array(0x64), - 0x1D5A4 => array(0x65), - 0x1D5A5 => array(0x66), - 0x1D5A6 => array(0x67), - 0x1D5A7 => array(0x68), - 0x1D5A8 => array(0x69), - 0x1D5A9 => array(0x6A), - 0x1D5AA => array(0x6B), - 0x1D5AB => array(0x6C), - 0x1D5AC => array(0x6D), - 0x1D5AD => array(0x6E), - 0x1D5AE => array(0x6F), - 0x1D5AF => array(0x70), - 0x1D5B0 => array(0x71), - 0x1D5B1 => array(0x72), - 0x1D5B2 => array(0x73), - 0x1D5B3 => array(0x74), - 0x1D5B4 => array(0x75), - 0x1D5B5 => array(0x76), - 0x1D5B6 => array(0x77), - 0x1D5B7 => array(0x78), - 0x1D5B8 => array(0x79), - 0x1D5B9 => array(0x7A), - 0x1D5D4 => array(0x61), - 0x1D5D5 => array(0x62), - 0x1D5D6 => array(0x63), - 0x1D5D7 => array(0x64), - 0x1D5D8 => array(0x65), - 0x1D5D9 => array(0x66), - 0x1D5DA => array(0x67), - 0x1D5DB => array(0x68), - 0x1D5DC => array(0x69), - 0x1D5DD => array(0x6A), - 0x1D5DE => array(0x6B), - 0x1D5DF => array(0x6C), - 0x1D5E0 => array(0x6D), - 0x1D5E1 => array(0x6E), - 0x1D5E2 => array(0x6F), - 0x1D5E3 => array(0x70), - 0x1D5E4 => array(0x71), - 0x1D5E5 => array(0x72), - 0x1D5E6 => array(0x73), - 0x1D5E7 => array(0x74), - 0x1D5E8 => array(0x75), - 0x1D5E9 => array(0x76), - 0x1D5EA => array(0x77), - 0x1D5EB => array(0x78), - 0x1D5EC => array(0x79), - 0x1D5ED => array(0x7A), - 0x1D608 => array(0x61), - 0x1D609 => array(0x62), - 0x1D60A => array(0x63), - 0x1D60B => array(0x64), - 0x1D60C => array(0x65), - 0x1D60D => array(0x66), - 0x1D60E => array(0x67), - 0x1D60F => array(0x68), - 0x1D610 => array(0x69), - 0x1D611 => array(0x6A), - 0x1D612 => array(0x6B), - 0x1D613 => array(0x6C), - 0x1D614 => array(0x6D), - 0x1D615 => array(0x6E), - 0x1D616 => array(0x6F), - 0x1D617 => array(0x70), - 0x1D618 => array(0x71), - 0x1D619 => array(0x72), - 0x1D61A => array(0x73), - 0x1D61B => array(0x74), - 0x1D61C => array(0x75), - 0x1D61D => array(0x76), - 0x1D61E => array(0x77), - 0x1D61F => array(0x78), - 0x1D620 => array(0x79), - 0x1D621 => array(0x7A), - 0x1D63C => array(0x61), - 0x1D63D => array(0x62), - 0x1D63E => array(0x63), - 0x1D63F => array(0x64), - 0x1D640 => array(0x65), - 0x1D641 => array(0x66), - 0x1D642 => array(0x67), - 0x1D643 => array(0x68), - 0x1D644 => array(0x69), - 0x1D645 => array(0x6A), - 0x1D646 => array(0x6B), - 0x1D647 => array(0x6C), - 0x1D648 => array(0x6D), - 0x1D649 => array(0x6E), - 0x1D64A => array(0x6F), - 0x1D64B => array(0x70), - 0x1D64C => array(0x71), - 0x1D64D => array(0x72), - 0x1D64E => array(0x73), - 0x1D64F => array(0x74), - 0x1D650 => array(0x75), - 0x1D651 => array(0x76), - 0x1D652 => array(0x77), - 0x1D653 => array(0x78), - 0x1D654 => array(0x79), - 0x1D655 => array(0x7A), - 0x1D670 => array(0x61), - 0x1D671 => array(0x62), - 0x1D672 => array(0x63), - 0x1D673 => array(0x64), - 0x1D674 => array(0x65), - 0x1D675 => array(0x66), - 0x1D676 => array(0x67), - 0x1D677 => array(0x68), - 0x1D678 => array(0x69), - 0x1D679 => array(0x6A), - 0x1D67A => array(0x6B), - 0x1D67B => array(0x6C), - 0x1D67C => array(0x6D), - 0x1D67D => array(0x6E), - 0x1D67E => array(0x6F), - 0x1D67F => array(0x70), - 0x1D680 => array(0x71), - 0x1D681 => array(0x72), - 0x1D682 => array(0x73), - 0x1D683 => array(0x74), - 0x1D684 => array(0x75), - 0x1D685 => array(0x76), - 0x1D686 => array(0x77), - 0x1D687 => array(0x78), - 0x1D688 => array(0x79), - 0x1D689 => array(0x7A), - 0x1D6A8 => array(0x3B1), - 0x1D6A9 => array(0x3B2), - 0x1D6AA => array(0x3B3), - 0x1D6AB => array(0x3B4), - 0x1D6AC => array(0x3B5), - 0x1D6AD => array(0x3B6), - 0x1D6AE => array(0x3B7), - 0x1D6AF => array(0x3B8), - 0x1D6B0 => array(0x3B9), - 0x1D6B1 => array(0x3BA), - 0x1D6B2 => array(0x3BB), - 0x1D6B3 => array(0x3BC), - 0x1D6B4 => array(0x3BD), - 0x1D6B5 => array(0x3BE), - 0x1D6B6 => array(0x3BF), - 0x1D6B7 => array(0x3C0), - 0x1D6B8 => array(0x3C1), - 0x1D6B9 => array(0x3B8), - 0x1D6BA => array(0x3C3), - 0x1D6BB => array(0x3C4), - 0x1D6BC => array(0x3C5), - 0x1D6BD => array(0x3C6), - 0x1D6BE => array(0x3C7), - 0x1D6BF => array(0x3C8), - 0x1D6C0 => array(0x3C9), - 0x1D6D3 => array(0x3C3), - 0x1D6E2 => array(0x3B1), - 0x1D6E3 => array(0x3B2), - 0x1D6E4 => array(0x3B3), - 0x1D6E5 => array(0x3B4), - 0x1D6E6 => array(0x3B5), - 0x1D6E7 => array(0x3B6), - 0x1D6E8 => array(0x3B7), - 0x1D6E9 => array(0x3B8), - 0x1D6EA => array(0x3B9), - 0x1D6EB => array(0x3BA), - 0x1D6EC => array(0x3BB), - 0x1D6ED => array(0x3BC), - 0x1D6EE => array(0x3BD), - 0x1D6EF => array(0x3BE), - 0x1D6F0 => array(0x3BF), - 0x1D6F1 => array(0x3C0), - 0x1D6F2 => array(0x3C1), - 0x1D6F3 => array(0x3B8), - 0x1D6F4 => array(0x3C3), - 0x1D6F5 => array(0x3C4), - 0x1D6F6 => array(0x3C5), - 0x1D6F7 => array(0x3C6), - 0x1D6F8 => array(0x3C7), - 0x1D6F9 => array(0x3C8), - 0x1D6FA => array(0x3C9), - 0x1D70D => array(0x3C3), - 0x1D71C => array(0x3B1), - 0x1D71D => array(0x3B2), - 0x1D71E => array(0x3B3), - 0x1D71F => array(0x3B4), - 0x1D720 => array(0x3B5), - 0x1D721 => array(0x3B6), - 0x1D722 => array(0x3B7), - 0x1D723 => array(0x3B8), - 0x1D724 => array(0x3B9), - 0x1D725 => array(0x3BA), - 0x1D726 => array(0x3BB), - 0x1D727 => array(0x3BC), - 0x1D728 => array(0x3BD), - 0x1D729 => array(0x3BE), - 0x1D72A => array(0x3BF), - 0x1D72B => array(0x3C0), - 0x1D72C => array(0x3C1), - 0x1D72D => array(0x3B8), - 0x1D72E => array(0x3C3), - 0x1D72F => array(0x3C4), - 0x1D730 => array(0x3C5), - 0x1D731 => array(0x3C6), - 0x1D732 => array(0x3C7), - 0x1D733 => array(0x3C8), - 0x1D734 => array(0x3C9), - 0x1D747 => array(0x3C3), - 0x1D756 => array(0x3B1), - 0x1D757 => array(0x3B2), - 0x1D758 => array(0x3B3), - 0x1D759 => array(0x3B4), - 0x1D75A => array(0x3B5), - 0x1D75B => array(0x3B6), - 0x1D75C => array(0x3B7), - 0x1D75D => array(0x3B8), - 0x1D75E => array(0x3B9), - 0x1D75F => array(0x3BA), - 0x1D760 => array(0x3BB), - 0x1D761 => array(0x3BC), - 0x1D762 => array(0x3BD), - 0x1D763 => array(0x3BE), - 0x1D764 => array(0x3BF), - 0x1D765 => array(0x3C0), - 0x1D766 => array(0x3C1), - 0x1D767 => array(0x3B8), - 0x1D768 => array(0x3C3), - 0x1D769 => array(0x3C4), - 0x1D76A => array(0x3C5), - 0x1D76B => array(0x3C6), - 0x1D76C => array(0x3C7), - 0x1D76D => array(0x3C8), - 0x1D76E => array(0x3C9), - 0x1D781 => array(0x3C3), - 0x1D790 => array(0x3B1), - 0x1D791 => array(0x3B2), - 0x1D792 => array(0x3B3), - 0x1D793 => array(0x3B4), - 0x1D794 => array(0x3B5), - 0x1D795 => array(0x3B6), - 0x1D796 => array(0x3B7), - 0x1D797 => array(0x3B8), - 0x1D798 => array(0x3B9), - 0x1D799 => array(0x3BA), - 0x1D79A => array(0x3BB), - 0x1D79B => array(0x3BC), - 0x1D79C => array(0x3BD), - 0x1D79D => array(0x3BE), - 0x1D79E => array(0x3BF), - 0x1D79F => array(0x3C0), - 0x1D7A0 => array(0x3C1), - 0x1D7A1 => array(0x3B8), - 0x1D7A2 => array(0x3C3), - 0x1D7A3 => array(0x3C4), - 0x1D7A4 => array(0x3C5), - 0x1D7A5 => array(0x3C6), - 0x1D7A6 => array(0x3C7), - 0x1D7A7 => array(0x3C8), - 0x1D7A8 => array(0x3C9), - 0x1D7BB => array(0x3C3), - 0x3F9 => array(0x3C3), - 0x1D2C => array(0x61), - 0x1D2D => array(0xE6), - 0x1D2E => array(0x62), - 0x1D30 => array(0x64), - 0x1D31 => array(0x65), - 0x1D32 => array(0x1DD), - 0x1D33 => array(0x67), - 0x1D34 => array(0x68), - 0x1D35 => array(0x69), - 0x1D36 => array(0x6A), - 0x1D37 => array(0x6B), - 0x1D38 => array(0x6C), - 0x1D39 => array(0x6D), - 0x1D3A => array(0x6E), - 0x1D3C => array(0x6F), - 0x1D3D => array(0x223), - 0x1D3E => array(0x70), - 0x1D3F => array(0x72), - 0x1D40 => array(0x74), - 0x1D41 => array(0x75), - 0x1D42 => array(0x77), - 0x213B => array(0x66, 0x61, 0x78), - 0x3250 => array(0x70, 0x74, 0x65), - 0x32CC => array(0x68, 0x67), - 0x32CE => array(0x65, 0x76), - 0x32CF => array(0x6C, 0x74, 0x64), - 0x337A => array(0x69, 0x75), - 0x33DE => array(0x76, 0x2215, 0x6D), - 0x33DF => array(0x61, 0x2215, 0x6D) - ); - - /** - * Normalization Combining Classes; Code Points not listed - * got Combining Class 0. - * - * @static - * @var array - * @access private - */ - private static $_np_norm_combcls = array( - 0x334 => 1, - 0x335 => 1, - 0x336 => 1, - 0x337 => 1, - 0x338 => 1, - 0x93C => 7, - 0x9BC => 7, - 0xA3C => 7, - 0xABC => 7, - 0xB3C => 7, - 0xCBC => 7, - 0x1037 => 7, - 0x3099 => 8, - 0x309A => 8, - 0x94D => 9, - 0x9CD => 9, - 0xA4D => 9, - 0xACD => 9, - 0xB4D => 9, - 0xBCD => 9, - 0xC4D => 9, - 0xCCD => 9, - 0xD4D => 9, - 0xDCA => 9, - 0xE3A => 9, - 0xF84 => 9, - 0x1039 => 9, - 0x1714 => 9, - 0x1734 => 9, - 0x17D2 => 9, - 0x5B0 => 10, - 0x5B1 => 11, - 0x5B2 => 12, - 0x5B3 => 13, - 0x5B4 => 14, - 0x5B5 => 15, - 0x5B6 => 16, - 0x5B7 => 17, - 0x5B8 => 18, - 0x5B9 => 19, - 0x5BB => 20, - 0x5Bc => 21, - 0x5BD => 22, - 0x5BF => 23, - 0x5C1 => 24, - 0x5C2 => 25, - 0xFB1E => 26, - 0x64B => 27, - 0x64C => 28, - 0x64D => 29, - 0x64E => 30, - 0x64F => 31, - 0x650 => 32, - 0x651 => 33, - 0x652 => 34, - 0x670 => 35, - 0x711 => 36, - 0xC55 => 84, - 0xC56 => 91, - 0xE38 => 103, - 0xE39 => 103, - 0xE48 => 107, - 0xE49 => 107, - 0xE4A => 107, - 0xE4B => 107, - 0xEB8 => 118, - 0xEB9 => 118, - 0xEC8 => 122, - 0xEC9 => 122, - 0xECA => 122, - 0xECB => 122, - 0xF71 => 129, - 0xF72 => 130, - 0xF7A => 130, - 0xF7B => 130, - 0xF7C => 130, - 0xF7D => 130, - 0xF80 => 130, - 0xF74 => 132, - 0x321 => 202, - 0x322 => 202, - 0x327 => 202, - 0x328 => 202, - 0x31B => 216, - 0xF39 => 216, - 0x1D165 => 216, - 0x1D166 => 216, - 0x1D16E => 216, - 0x1D16F => 216, - 0x1D170 => 216, - 0x1D171 => 216, - 0x1D172 => 216, - 0x302A => 218, - 0x316 => 220, - 0x317 => 220, - 0x318 => 220, - 0x319 => 220, - 0x31C => 220, - 0x31D => 220, - 0x31E => 220, - 0x31F => 220, - 0x320 => 220, - 0x323 => 220, - 0x324 => 220, - 0x325 => 220, - 0x326 => 220, - 0x329 => 220, - 0x32A => 220, - 0x32B => 220, - 0x32C => 220, - 0x32D => 220, - 0x32E => 220, - 0x32F => 220, - 0x330 => 220, - 0x331 => 220, - 0x332 => 220, - 0x333 => 220, - 0x339 => 220, - 0x33A => 220, - 0x33B => 220, - 0x33C => 220, - 0x347 => 220, - 0x348 => 220, - 0x349 => 220, - 0x34D => 220, - 0x34E => 220, - 0x353 => 220, - 0x354 => 220, - 0x355 => 220, - 0x356 => 220, - 0x591 => 220, - 0x596 => 220, - 0x59B => 220, - 0x5A3 => 220, - 0x5A4 => 220, - 0x5A5 => 220, - 0x5A6 => 220, - 0x5A7 => 220, - 0x5AA => 220, - 0x655 => 220, - 0x656 => 220, - 0x6E3 => 220, - 0x6EA => 220, - 0x6ED => 220, - 0x731 => 220, - 0x734 => 220, - 0x737 => 220, - 0x738 => 220, - 0x739 => 220, - 0x73B => 220, - 0x73C => 220, - 0x73E => 220, - 0x742 => 220, - 0x744 => 220, - 0x746 => 220, - 0x748 => 220, - 0x952 => 220, - 0xF18 => 220, - 0xF19 => 220, - 0xF35 => 220, - 0xF37 => 220, - 0xFC6 => 220, - 0x193B => 220, - 0x20E8 => 220, - 0x1D17B => 220, - 0x1D17C => 220, - 0x1D17D => 220, - 0x1D17E => 220, - 0x1D17F => 220, - 0x1D180 => 220, - 0x1D181 => 220, - 0x1D182 => 220, - 0x1D18A => 220, - 0x1D18B => 220, - 0x59A => 222, - 0x5AD => 222, - 0x1929 => 222, - 0x302D => 222, - 0x302E => 224, - 0x302F => 224, - 0x1D16D => 226, - 0x5AE => 228, - 0x18A9 => 228, - 0x302B => 228, - 0x300 => 230, - 0x301 => 230, - 0x302 => 230, - 0x303 => 230, - 0x304 => 230, - 0x305 => 230, - 0x306 => 230, - 0x307 => 230, - 0x308 => 230, - 0x309 => 230, - 0x30A => 230, - 0x30B => 230, - 0x30C => 230, - 0x30D => 230, - 0x30E => 230, - 0x30F => 230, - 0x310 => 230, - 0x311 => 230, - 0x312 => 230, - 0x313 => 230, - 0x314 => 230, - 0x33D => 230, - 0x33E => 230, - 0x33F => 230, - 0x340 => 230, - 0x341 => 230, - 0x342 => 230, - 0x343 => 230, - 0x344 => 230, - 0x346 => 230, - 0x34A => 230, - 0x34B => 230, - 0x34C => 230, - 0x350 => 230, - 0x351 => 230, - 0x352 => 230, - 0x357 => 230, - 0x363 => 230, - 0x364 => 230, - 0x365 => 230, - 0x366 => 230, - 0x367 => 230, - 0x368 => 230, - 0x369 => 230, - 0x36A => 230, - 0x36B => 230, - 0x36C => 230, - 0x36D => 230, - 0x36E => 230, - 0x36F => 230, - 0x483 => 230, - 0x484 => 230, - 0x485 => 230, - 0x486 => 230, - 0x592 => 230, - 0x593 => 230, - 0x594 => 230, - 0x595 => 230, - 0x597 => 230, - 0x598 => 230, - 0x599 => 230, - 0x59C => 230, - 0x59D => 230, - 0x59E => 230, - 0x59F => 230, - 0x5A0 => 230, - 0x5A1 => 230, - 0x5A8 => 230, - 0x5A9 => 230, - 0x5AB => 230, - 0x5AC => 230, - 0x5AF => 230, - 0x5C4 => 230, - 0x610 => 230, - 0x611 => 230, - 0x612 => 230, - 0x613 => 230, - 0x614 => 230, - 0x615 => 230, - 0x653 => 230, - 0x654 => 230, - 0x657 => 230, - 0x658 => 230, - 0x6D6 => 230, - 0x6D7 => 230, - 0x6D8 => 230, - 0x6D9 => 230, - 0x6DA => 230, - 0x6DB => 230, - 0x6DC => 230, - 0x6DF => 230, - 0x6E0 => 230, - 0x6E1 => 230, - 0x6E2 => 230, - 0x6E4 => 230, - 0x6E7 => 230, - 0x6E8 => 230, - 0x6EB => 230, - 0x6EC => 230, - 0x730 => 230, - 0x732 => 230, - 0x733 => 230, - 0x735 => 230, - 0x736 => 230, - 0x73A => 230, - 0x73D => 230, - 0x73F => 230, - 0x740 => 230, - 0x741 => 230, - 0x743 => 230, - 0x745 => 230, - 0x747 => 230, - 0x749 => 230, - 0x74A => 230, - 0x951 => 230, - 0x953 => 230, - 0x954 => 230, - 0xF82 => 230, - 0xF83 => 230, - 0xF86 => 230, - 0xF87 => 230, - 0x170D => 230, - 0x193A => 230, - 0x20D0 => 230, - 0x20D1 => 230, - 0x20D4 => 230, - 0x20D5 => 230, - 0x20D6 => 230, - 0x20D7 => 230, - 0x20DB => 230, - 0x20DC => 230, - 0x20E1 => 230, - 0x20E7 => 230, - 0x20E9 => 230, - 0xFE20 => 230, - 0xFE21 => 230, - 0xFE22 => 230, - 0xFE23 => 230, - 0x1D185 => 230, - 0x1D186 => 230, - 0x1D187 => 230, - 0x1D189 => 230, - 0x1D188 => 230, - 0x1D1AA => 230, - 0x1D1AB => 230, - 0x1D1AC => 230, - 0x1D1AD => 230, - 0x315 => 232, - 0x31A => 232, - 0x302C => 232, - 0x35F => 233, - 0x362 => 233, - 0x35D => 234, - 0x35E => 234, - 0x360 => 234, - 0x361 => 234, - 0x345 => 240 - ); - // }}} - - // {{{ properties - /** - * @var string - * @access private - */ - private $_punycode_prefix = 'xn--'; - - /** - * @access private - */ - private $_invalid_ucs = 0x80000000; - - /** - * @access private - */ - private $_max_ucs = 0x10FFFF; - - /** - * @var int - * @access private - */ - private $_base = 36; - - /** - * @var int - * @access private - */ - private $_tmin = 1; - - /** - * @var int - * @access private - */ - private $_tmax = 26; - - /** - * @var int - * @access private - */ - private $_skew = 38; - - /** - * @var int - * @access private - */ - private $_damp = 700; - - /** - * @var int - * @access private - */ - private $_initial_bias = 72; - - /** - * @var int - * @access private - */ - private $_initial_n = 0x80; - - /** - * @var int - * @access private - */ - private $_slast; - - /** - * @access private - */ - private $_sbase = 0xAC00; - - /** - * @access private - */ - private $_lbase = 0x1100; - - /** - * @access private - */ - private $_vbase = 0x1161; - - /** - * @access private - */ - private $_tbase = 0x11a7; - - /** - * @var int - * @access private - */ - private $_lcount = 19; - - /** - * @var int - * @access private - */ - private $_vcount = 21; - - /** - * @var int - * @access private - */ - private $_tcount = 28; - - /** - * vcount * tcount - * - * @var int - * @access private - */ - private $_ncount = 588; - - /** - * lcount * tcount * vcount - * - * @var int - * @access private - */ - private $_scount = 11172; - - /** - * Default encoding for encode()'s input and decode()'s output is UTF-8; - * Other possible encodings are ucs4_string and ucs4_array - * See {@link setParams()} for how to select these - * - * @var bool - * @access private - */ - private $_api_encoding = 'utf8'; - - /** - * Overlong UTF-8 encodings are forbidden - * - * @var bool - * @access private - */ - private $_allow_overlong = false; - - /** - * Behave strict or not - * - * @var bool - * @access private - */ - private $_strict_mode = false; - - /** - * IDNA-version to use - * - * Values are "2003" and "2008". - * Defaults to "2003", since that was the original version and for - * compatibility with previous versions of this library. - * If you need to encode "new" characters like the German "Eszett", - * please switch to 2008 first before encoding. - * - * @var bool - * @access private - */ - private $_version = '2003'; - - /** - * Cached value indicating whether or not mbstring function overloading is - * on for strlen - * - * This is cached for optimal performance. - * - * @var boolean - * @see Net_IDNA2::_byteLength() - */ - private static $_mb_string_overload = null; - // }}} - - - // {{{ constructor - /** - * Constructor - * - * @param array $options Options to initialise the object with - * - * @access public - * @see setParams() - */ - public function __construct($options = null) - { - $this->_slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; - - if (is_array($options)) { - $this->setParams($options); - } - - // populate mbstring overloading cache if not set - if (self::$_mb_string_overload === null) { - self::$_mb_string_overload = (extension_loaded('mbstring') - && (ini_get('mbstring.func_overload') & 0x02) === 0x02); - } - } - // }}} - - - /** - * Sets a new option value. Available options and values: - * - * [utf8 - Use either UTF-8 or ISO-8859-1 as input (true for UTF-8, false - * otherwise); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead] - * - * @param mixed $option Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string $value Value to use (if parameter 1 is a string) - * - * @return boolean true on success, false otherwise - * @access public - */ - public function setParams($option, $value = false) - { - if (!is_array($option)) { - $option = array($option => $value); - } - - foreach ($option as $k => $v) { - switch ($k) { - case 'encoding': - switch ($v) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - $this->_api_encoding = $v; - break; - - default: - throw new InvalidArgumentException('Set Parameter: Unknown parameter '.$v.' for option '.$k); - } - - break; - - case 'overlong': - $this->_allow_overlong = ($v) ? true : false; - break; - - case 'strict': - $this->_strict_mode = ($v) ? true : false; - break; - - case 'version': - if (in_array($v, array('2003', '2008'))) { - $this->_version = $v; - } else { - throw new InvalidArgumentException('Set Parameter: Invalid parameter '.$v.' for option '.$k); - } - break; - - default: - return false; - } - } - - return true; - } - - /** - * Encode a given UTF-8 domain name. - * - * @param string $decoded Domain name (UTF-8 or UCS-4) - * @param string $one_time_encoding Desired input encoding, see {@link set_parameter} - * If not given will use default-encoding - * - * @return string Encoded Domain name (ACE string) - * @return mixed processed string - * @throws Exception - * @access public - */ - public function encode($decoded, $one_time_encoding = false) - { - // Forcing conversion of input to UCS4 array - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - $decoded = $this->_utf8_to_ucs4($decoded); - break; - case 'ucs4_string': - $decoded = $this->_ucs4_string_to_ucs4($decoded); - case 'ucs4_array': // No break; before this line. Catch case, but do nothing - break; - default: - throw new InvalidArgumentException('Unsupported input format'); - } - - // No input, no output, what else did you expect? - if (empty($decoded)) return ''; - - // Anchors for iteration - $last_begin = 0; - // Output string - $output = ''; - - foreach ($decoded as $k => $v) { - // Make sure to use just the plain dot - switch($v) { - case 0x3002: - case 0xFF0E: - case 0xFF61: - $decoded[$k] = 0x2E; - // It's right, no break here - // The codepoints above have to be converted to dots anyway - - // Stumbling across an anchoring character - case 0x2E: - case 0x2F: - case 0x3A: - case 0x3F: - case 0x40: - // Neither email addresses nor URLs allowed in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Neither email addresses nor URLs are allowed in strict mode.'); - } - // Skip first char - if ($k) { - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); - } - $output .= chr($decoded[$k]); - } - $last_begin = $k + 1; - } - } - // Catch the rest of the string - if ($last_begin) { - $inp_len = sizeof($decoded); - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - } - return $output; - } - - if ($output = $this->_encode($decoded)) { - return $output; - } - - return $this->_ucs4_to_utf8($decoded); - } - - /** - * Decode a given ACE domain name. - * - * @param string $input Domain name (ACE string) - * @param string $one_time_encoding Desired output encoding, see {@link set_parameter} - * - * @return string Decoded Domain name (UTF-8 or UCS-4) - * @throws Exception - * @access public - */ - public function decode($input, $one_time_encoding = false) - { - // Optionally set - if ($one_time_encoding) { - switch ($one_time_encoding) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - break; - default: - throw new InvalidArgumentException('Unknown encoding '.$one_time_encoding); - } - } - // Make sure to drop any newline characters around - $input = trim($input); - - // Negotiate input and try to determine, wether it is a plain string, - // an email address or something like a complete URL - if (strpos($input, '@')) { // Maybe it is an email address - // No no in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode'); - } - list($email_pref, $input) = explode('@', $input, 2); - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $return = $email_pref . '@' . join('.', $arr); - } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) - // No no in strict mode - if ($this->_strict_mode) { - throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode'); - } - - $parsed = parse_url($input); - if (isset($parsed['host'])) { - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $parsed['host'] = join('.', $arr); - if (isset($parsed['scheme'])) { - $parsed['scheme'] .= (strtolower($parsed['scheme']) == 'mailto') ? ':' : '://'; - } - $return = $this->_unparse_url($parsed); - } else { // parse_url seems to have failed, try without it - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $return = join('.', $arr); - } - } else { // Otherwise we consider it being a pure domain name string - $return = $this->_decode($input); - } - // The output is UTF-8 by default, other output formats need conversion here - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - return $return; - break; - case 'ucs4_string': - return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); - break; - case 'ucs4_array': - return $this->_utf8_to_ucs4($return); - break; - default: - throw new InvalidArgumentException('Unsupported output format'); - } - } - - - // {{{ private - /** - * Opposite function to parse_url() - * - * Inspired by code from comments of php.net-documentation for parse_url() - * - * @param array $parts_arr parts (strings) as returned by parse_url() - * - * @return string - * @access private - */ - private function _unparse_url($parts_arr) - { - if (!empty($parts_arr['scheme'])) { - $ret_url = $parts_arr['scheme']; - } - if (!empty($parts_arr['user'])) { - $ret_url .= $parts_arr['user']; - if (!empty($parts_arr['pass'])) { - $ret_url .= ':' . $parts_arr['pass']; - } - $ret_url .= '@'; - } - $ret_url .= $parts_arr['host']; - if (!empty($parts_arr['port'])) { - $ret_url .= ':' . $parts_arr['port']; - } - $ret_url .= $parts_arr['path']; - if (!empty($parts_arr['query'])) { - $ret_url .= '?' . $parts_arr['query']; - } - if (!empty($parts_arr['fragment'])) { - $ret_url .= '#' . $parts_arr['fragment']; - } - return $ret_url; - } - - /** - * The actual encoding algorithm. - * - * @param string $decoded Decoded string which should be encoded - * - * @return string Encoded string - * @throws Exception - * @access private - */ - private function _encode($decoded) - { - // We cannot encode a domain name containing the Punycode prefix - $extract = self::_byteLength($this->_punycode_prefix); - $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); - $check_deco = array_slice($decoded, 0, $extract); - - if ($check_pref == $check_deco) { - throw new InvalidArgumentException('This is already a punycode string'); - } - - // We will not try to encode strings consisting of basic code points only - $encodable = false; - foreach ($decoded as $k => $v) { - if ($v > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - if ($this->_strict_mode) { - throw new InvalidArgumentException('The given string does not contain encodable chars'); - } - - return false; - } - - // Do NAMEPREP - $decoded = $this->_nameprep($decoded); - - $deco_len = count($decoded); - - // Empty array - if (!$deco_len) { - return false; - } - - // How many chars have been consumed - $codecount = 0; - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix; - - $encoded = ''; - // Copy all basic code points to output - for ($i = 0; $i < $deco_len; ++$i) { - $test = $decoded[$i]; - // Will match [0-9a-zA-Z-] - if ((0x2F < $test && $test < 0x40) - || (0x40 < $test && $test < 0x5B) - || (0x60 < $test && $test <= 0x7B) - || (0x2D == $test) - ) { - $encoded .= chr($decoded[$i]); - $codecount++; - } - } - - // All codepoints were basic ones - if ($codecount == $deco_len) { - return $encoded; - } - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix . $encoded; - - // If we have basic code points in output, add an hyphen to the end - if ($codecount) { - $encoded .= '-'; - } - - // Now find and encode all non-basic code points - $is_first = true; - $cur_code = $this->_initial_n; - $bias = $this->_initial_bias; - $delta = 0; - - while ($codecount < $deco_len) { - // Find the smallest code point >= the current code point and - // remember the last ouccrence of it in the input - for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { - if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { - $next_code = $decoded[$i]; - } - } - - $delta += ($next_code - $cur_code) * ($codecount + 1); - $cur_code = $next_code; - - // Scan input again and encode all characters whose code point is $cur_code - for ($i = 0; $i < $deco_len; $i++) { - if ($decoded[$i] < $cur_code) { - $delta++; - } else if ($decoded[$i] == $cur_code) { - for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { - $t = ($k <= $bias)? - $this->_tmin : - (($k >= $bias + $this->_tmax)? $this->_tmax : $k - $bias); - - if ($q < $t) { - break; - } - - $encoded .= $this->_encodeDigit(ceil($t + (($q - $t) % ($this->_base - $t)))); - $q = ($q - $t) / ($this->_base - $t); - } - - $encoded .= $this->_encodeDigit($q); - $bias = $this->_adapt($delta, $codecount + 1, $is_first); - $codecount++; - $delta = 0; - $is_first = false; - } - } - - $delta++; - $cur_code++; - } - - return $encoded; - } - - /** - * The actual decoding algorithm. - * - * @param string $encoded Encoded string which should be decoded - * - * @return string Decoded string - * @throws Exception - * @access private - */ - private function _decode($encoded) - { - // We do need to find the Punycode prefix - if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) { - return false; - } - - $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded); - - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { - return false; - } - - // Find last occurence of the delimiter - $delim_pos = strrpos($encoded, '-'); - - if ($delim_pos > self::_byteLength($this->_punycode_prefix)) { - for ($k = self::_byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) { - $decoded[] = ord($encoded{$k}); - } - } else { - $decoded = array(); - } - - $deco_len = count($decoded); - $enco_len = self::_byteLength($encoded); - - // Wandering through the strings; init - $is_first = true; - $bias = $this->_initial_bias; - $idx = 0; - $char = $this->_initial_n; - - for ($enco_idx = ($delim_pos)? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { - for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { - $digit = $this->_decodeDigit($encoded{$enco_idx++}); - $idx += $digit * $w; - - $t = ($k <= $bias) ? - $this->_tmin : - (($k >= $bias + $this->_tmax)? $this->_tmax : ($k - $bias)); - - if ($digit < $t) { - break; - } - - $w = (int)($w * ($this->_base - $t)); - } - - $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); - $is_first = false; - $char += (int) ($idx / ($deco_len + 1)); - $idx %= ($deco_len + 1); - - if ($deco_len > 0) { - // Make room for the decoded char - for ($i = $deco_len; $i > $idx; $i--) { - $decoded[$i] = $decoded[($i - 1)]; - } - } - - $decoded[$idx++] = $char; - } - - return $this->_ucs4_to_utf8($decoded); - } - - /** - * Adapt the bias according to the current code point and position. - * - * @param int $delta ... - * @param int $npoints ... - * @param boolean $is_first ... - * - * @return int - * @access private - */ - private function _adapt($delta, $npoints, $is_first) - { - $delta = (int) ($is_first ? ($delta / $this->_damp) : ($delta / 2)); - $delta += (int) ($delta / $npoints); - - for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { - $delta = (int) ($delta / ($this->_base - $this->_tmin)); - } - - return (int) ($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); - } - - /** - * Encoding a certain digit. - * - * @param int $d One digit to encode - * - * @return char Encoded digit - * @access private - */ - private function _encodeDigit($d) - { - return chr($d + 22 + 75 * ($d < 26)); - } - - /** - * Decode a certain digit. - * - * @param char $cp One digit (character) to decode - * - * @return int Decoded digit - * @access private - */ - private function _decodeDigit($cp) - { - $cp = ord($cp); - return ($cp - 48 < 10)? $cp - 22 : (($cp - 65 < 26)? $cp - 65 : (($cp - 97 < 26)? $cp - 97 : $this->_base)); - } - - /** - * Do Nameprep according to RFC3491 and RFC3454. - * - * @param array $input Unicode Characters - * - * @return string Unicode Characters, Nameprep'd - * @throws Exception - * @access private - */ - private function _nameprep($input) - { - $output = array(); - - // Walking through the input array, performing the required steps on each of - // the input chars and putting the result into the output array - // While mapping required chars we apply the cannonical ordering - - foreach ($input as $v) { - // Map to nothing == skip that code point - if (in_array($v, self::$_np_map_nothing)) { - continue; - } - - // Try to find prohibited input - if (in_array($v, self::$_np_prohibit) || in_array($v, self::$_general_prohibited)) { - throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v)); - } - - foreach (self::$_np_prohibit_ranges as $range) { - if ($range[0] <= $v && $v <= $range[1]) { - throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v)); - } - } - - // Hangul syllable decomposition - if (0xAC00 <= $v && $v <= 0xD7AF) { - foreach ($this->_hangulDecompose($v) as $out) { - $output[] = $out; - } - } else if (($this->_version == '2003') && isset(self::$_np_replacemaps[$v])) { - // There's a decomposition mapping for that code point - // Decompositions only in version 2003 (original) of IDNA - foreach ($this->_applyCannonicalOrdering(self::$_np_replacemaps[$v]) as $out) { - $output[] = $out; - } - } else { - $output[] = $v; - } - } - - // Combine code points - - $last_class = 0; - $last_starter = 0; - $out_len = count($output); - - for ($i = 0; $i < $out_len; ++$i) { - $class = $this->_getCombiningClass($output[$i]); - - if ((!$last_class || $last_class != $class) && $class) { - // Try to match - $seq_len = $i - $last_starter; - $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); - - // On match: Replace the last starter with the composed character and remove - // the now redundant non-starter(s) - if ($out) { - $output[$last_starter] = $out; - - if (count($out) != $seq_len) { - for ($j = $i + 1; $j < $out_len; ++$j) { - $output[$j - 1] = $output[$j]; - } - - unset($output[$out_len]); - } - - // Rewind the for loop by one, since there can be more possible compositions - $i--; - $out_len--; - $last_class = ($i == $last_starter)? 0 : $this->_getCombiningClass($output[$i - 1]); - - continue; - } - } - - // The current class is 0 - if (!$class) { - $last_starter = $i; - } - - $last_class = $class; - } - - return $output; - } - - /** - * Decomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). - * - * @param integer $char 32bit UCS4 code point - * - * @return array Either Hangul Syllable decomposed or original 32bit - * value as one value array - * @access private - */ - private function _hangulDecompose($char) - { - $sindex = $char - $this->_sbase; - - if ($sindex < 0 || $sindex >= $this->_scount) { - return array($char); - } - - $result = array(); - $T = $this->_tbase + $sindex % $this->_tcount; - $result[] = (int)($this->_lbase + $sindex / $this->_ncount); - $result[] = (int)($this->_vbase + ($sindex % $this->_ncount) / $this->_tcount); - - if ($T != $this->_tbase) { - $result[] = $T; - } - - return $result; - } - - /** - * Ccomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). - * - * @param array $input Decomposed UCS4 sequence - * - * @return array UCS4 sequence with syllables composed - * @access private - */ - private function _hangulCompose($input) - { - $inp_len = count($input); - - if (!$inp_len) { - return array(); - } - - $result = array(); - $last = $input[0]; - $result[] = $last; // copy first char from input to output - - for ($i = 1; $i < $inp_len; ++$i) { - $char = $input[$i]; - - // Find out, wether two current characters from L and V - $lindex = $last - $this->_lbase; - - if (0 <= $lindex && $lindex < $this->_lcount) { - $vindex = $char - $this->_vbase; - - if (0 <= $vindex && $vindex < $this->_vcount) { - // create syllable of form LV - $last = ($this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount); - $out_off = count($result) - 1; - $result[$out_off] = $last; // reset last - - // discard char - continue; - } - } - - // Find out, wether two current characters are LV and T - $sindex = $last - $this->_sbase; - - if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount) == 0) { - $tindex = $char - $this->_tbase; - - if (0 <= $tindex && $tindex <= $this->_tcount) { - // create syllable of form LVT - $last += $tindex; - $out_off = count($result) - 1; - $result[$out_off] = $last; // reset last - - // discard char - continue; - } - } - - // if neither case was true, just add the character - $last = $char; - $result[] = $char; - } - - return $result; - } - - /** - * Returns the combining class of a certain wide char. - * - * @param integer $char Wide char to check (32bit integer) - * - * @return integer Combining class if found, else 0 - * @access private - */ - private function _getCombiningClass($char) - { - return isset(self::$_np_norm_combcls[$char])? self::$_np_norm_combcls[$char] : 0; - } - - /** - * Apllies the cannonical ordering of a decomposed UCS4 sequence. - * - * @param array $input Decomposed UCS4 sequence - * - * @return array Ordered USC4 sequence - * @access private - */ - private function _applyCannonicalOrdering($input) - { - $swap = true; - $size = count($input); - - while ($swap) { - $swap = false; - $last = $this->_getCombiningClass($input[0]); - - for ($i = 0; $i < $size - 1; ++$i) { - $next = $this->_getCombiningClass($input[$i + 1]); - - if ($next != 0 && $last > $next) { - // Move item leftward until it fits - for ($j = $i + 1; $j > 0; --$j) { - if ($this->_getCombiningClass($input[$j - 1]) <= $next) { - break; - } - - $t = $input[$j]; - $input[$j] = $input[$j - 1]; - $input[$j - 1] = $t; - $swap = 1; - } - - // Reentering the loop looking at the old character again - $next = $last; - } - - $last = $next; - } - } - - return $input; - } - - /** - * Do composition of a sequence of starter and non-starter. - * - * @param array $input UCS4 Decomposed sequence - * - * @return array Ordered USC4 sequence - * @access private - */ - private function _combine($input) - { - $inp_len = count($input); - - // Is it a Hangul syllable? - if (1 != $inp_len) { - $hangul = $this->_hangulCompose($input); - - // This place is probably wrong - if (count($hangul) != $inp_len) { - return $hangul; - } - } - - foreach (self::$_np_replacemaps as $np_src => $np_target) { - if ($np_target[0] != $input[0]) { - continue; - } - - if (count($np_target) != $inp_len) { - continue; - } - - $hit = false; - - foreach ($input as $k2 => $v2) { - if ($v2 == $np_target[$k2]) { - $hit = true; - } else { - $hit = false; - break; - } - } - - if ($hit) { - return $np_src; - } - } - - return false; - } - - /** - * This converts an UTF-8 encoded string to its UCS-4 (array) representation - * By talking about UCS-4 we mean arrays of 32bit integers representing - * each of the "chars". This is due to PHP not being able to handle strings with - * bit depth different from 8. This applies to the reverse method _ucs4_to_utf8(), too. - * The following UTF-8 encodings are supported: - * - * bytes bits representation - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * - * Each x represents a bit that can be used to store character data. - * - * @param string $input utf8-encoded string - * - * @return array ucs4-encoded array - * @throws Exception - * @access private - */ - private function _utf8_to_ucs4($input) - { - $output = array(); - $out_len = 0; - $inp_len = self::_byteLength($input, '8bit'); - $mode = 'next'; - $test = 'none'; - for ($k = 0; $k < $inp_len; ++$k) { - $v = ord($input{$k}); // Extract byte from input string - - if ($v < 128) { // We found an ASCII char - put into stirng as is - $output[$out_len] = $v; - ++$out_len; - if ('add' == $mode) { - throw new UnexpectedValueException('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - } - continue; - } - if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char - $start_byte = $v; - $mode = 'add'; - $test = 'range'; - if ($v >> 5 == 6) { // &110xxxxx 10xxxxx - $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left - $v = ($v - 192) << 6; - } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx - $next_byte = 1; - $v = ($v - 224) << 12; - } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 2; - $v = ($v - 240) << 18; - } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 3; - $v = ($v - 248) << 24; - } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 4; - $v = ($v - 252) << 30; - } else { - throw new UnexpectedValueException('This might be UTF-8, but I don\'t understand it at byte '.$k); - } - if ('add' == $mode) { - $output[$out_len] = (int) $v; - ++$out_len; - continue; - } - } - if ('add' == $mode) { - if (!$this->_allow_overlong && $test == 'range') { - $test = 'none'; - if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { - throw new OutOfRangeException('Bogus UTF-8 character detected (out of legal range) at byte '.$k); - } - } - if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx - $v = ($v - 128) << ($next_byte * 6); - $output[($out_len - 1)] += $v; - --$next_byte; - } else { - throw new UnexpectedValueException('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - } - if ($next_byte < 0) { - $mode = 'next'; - } - } - } // for - return $output; - } - - /** - * Convert UCS-4 array into UTF-8 string - * - * @param array $input ucs4-encoded array - * - * @return string utf8-encoded string - * @throws Exception - * @access private - */ - private function _ucs4_to_utf8($input) - { - $output = ''; - - foreach ($input as $v) { - // $v = ord($v); - - if ($v < 128) { - // 7bit are transferred literally - $output .= chr($v); - } else if ($v < 1 << 11) { - // 2 bytes - $output .= chr(192 + ($v >> 6)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 16) { - // 3 bytes - $output .= chr(224 + ($v >> 12)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 21) { - // 4 bytes - $output .= chr(240 + ($v >> 18)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 26) { - // 5 bytes - $output .= chr(248 + ($v >> 24)) - . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else if ($v < 1 << 31) { - // 6 bytes - $output .= chr(252 + ($v >> 30)) - . chr(128 + (($v >> 24) & 63)) - . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } else { - throw new UnexpectedValueException('Conversion from UCS-4 to UTF-8 failed: malformed input'); - } - } - - return $output; - } - - /** - * Convert UCS-4 array into UCS-4 string - * - * @param array $input ucs4-encoded array - * - * @return string ucs4-encoded string - * @throws Exception - * @access private - */ - private function _ucs4_to_ucs4_string($input) - { - $output = ''; - // Take array values and split output to 4 bytes per value - // The bit mask is 255, which reads &11111111 - foreach ($input as $v) { - $output .= ($v & (255 << 24) >> 24) . ($v & (255 << 16) >> 16) . ($v & (255 << 8) >> 8) . ($v & 255); - } - return $output; - } - - /** - * Convert UCS-4 string into UCS-4 array - * - * @param string $input ucs4-encoded string - * - * @return array ucs4-encoded array - * @throws InvalidArgumentException - * @access private - */ - private function _ucs4_string_to_ucs4($input) - { - $output = array(); - - $inp_len = self::_byteLength($input); - // Input length must be dividable by 4 - if ($inp_len % 4) { - throw new InvalidArgumentException('Input UCS4 string is broken'); - } - - // Empty input - return empty output - if (!$inp_len) { - return $output; - } - - for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { - // Increment output position every 4 input bytes - if (!$i % 4) { - $out_len++; - $output[$out_len] = 0; - } - $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); - } - return $output; - } - - /** - * Echo hex representation of UCS4 sequence. - * - * @param array $input UCS4 sequence - * @param boolean $include_bit Include bitmask in output - * - * @return void - * @static - * @access private - */ - private static function _showHex($input, $include_bit = false) - { - foreach ($input as $k => $v) { - echo '[', $k, '] => ', sprintf('%X', $v); - - if ($include_bit) { - echo ' (', Net_IDNA2::_showBitmask($v), ')'; - } - - echo "\n"; - } - } - - /** - * Gives you a bit representation of given Byte (8 bits), Word (16 bits) or DWord (32 bits) - * Output width is automagically determined - * - * @param int $octet ... - * - * @return string Bitmask-representation - * @static - * @access private - */ - private static function _showBitmask($octet) - { - if ($octet >= (1 << 16)) { - $w = 31; - } else if ($octet >= (1 << 8)) { - $w = 15; - } else { - $w = 7; - } - - $return = ''; - - for ($i = $w; $i > -1; $i--) { - $return .= ($octet & (1 << $i))? '1' : '0'; - } - - return $return; - } - - /** - * Gets the length of a string in bytes even if mbstring function - * overloading is turned on - * - * @param string $string the string for which to get the length. - * - * @return integer the length of the string in bytes. - * - * @see Net_IDNA2::$_mb_string_overload - */ - private static function _byteLength($string) - { - if (self::$_mb_string_overload) { - return mb_strlen($string, '8bit'); - } - return strlen((binary)$string); - } - - // }}}} - - // {{{ factory - /** - * Attempts to return a concrete IDNA instance for either php4 or php5. - * - * @param array $params Set of paramaters - * - * @return Net_IDNA2 - * @access public - */ - function getInstance($params = array()) - { - return new Net_IDNA2($params); - } - // }}} - - // {{{ singleton - /** - * Attempts to return a concrete IDNA instance for either php4 or php5, - * only creating a new instance if no IDNA instance with the same - * parameters currently exists. - * - * @param array $params Set of paramaters - * - * @return object Net_IDNA2 - * @access public - */ - function singleton($params = array()) - { - static $instances; - if (!isset($instances)) { - $instances = array(); - } - - $signature = serialize($params); - if (!isset($instances[$signature])) { - $instances[$signature] = Net_IDNA2::getInstance($params); - } - - return $instances[$signature]; - } - // }}} -} - -?> diff --git a/program/lib/Net/IDNA2/Exception.php b/program/lib/Net/IDNA2/Exception.php deleted file mode 100644 index 72cb1ae75..000000000 --- a/program/lib/Net/IDNA2/Exception.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -class Net_IDNA2_Exception extends Exception -{ -} diff --git a/program/lib/Net/IDNA2/Exception/Nameprep.php b/program/lib/Net/IDNA2/Exception/Nameprep.php deleted file mode 100644 index 44cbd6bb9..000000000 --- a/program/lib/Net/IDNA2/Exception/Nameprep.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -require_once 'Net/IDNA2/Exception.php'; - -class Net_IDNA2_Exception_Nameprep extends Net_IDNA2_Exception -{ -} diff --git a/program/lib/Net/LDAP3.php b/program/lib/Net/LDAP3.php deleted file mode 100644 index 3202f5aaf..000000000 --- a/program/lib/Net/LDAP3.php +++ /dev/null @@ -1,2618 +0,0 @@ -<?php -/* - +-----------------------------------------------------------------------+ - | Net/LDAP3.php | - | | - | Based on code created by the Roundcube Webmail team. | - | | - | Copyright (C) 2006-2014, The Roundcube Dev Team | - | Copyright (C) 2012-2014, Kolab Systems AG | - | | - | Licensed under the GNU General Public License version 3 or | - | any later version with exceptions for plugins. | - | See the README file for a full license statement. | - | | - | PURPOSE: | - | Provide advanced functionality for accessing LDAP directories | - | | - +-----------------------------------------------------------------------+ - | Authors: Thomas Bruederli <roundcube@gmail.com> | - | Aleksander Machniak <machniak@kolabsys.com> | - | Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | - +-----------------------------------------------------------------------+ -*/ - -require_once __DIR__ . '/LDAP3/Result.php'; - -/** - * Model class to access a LDAP directories - * - * @package Net_LDAP3 - */ -class Net_LDAP3 -{ - public $conn; - public $vlv_active = false; - - private $attribute_level_rights_map = array( - "r" => "read", - "s" => "search", - "w" => "write", - "o" => "delete", - "c" => "compare", - "W" => "write", - "O" => "delete" - ); - - private $entry_level_rights_map = array( - "a" => "add", - "d" => "delete", - "n" => "modrdn", - "v" => "read" - ); - - /* - * Manipulate configuration through the config_set and config_get methods. - * Available options: - * 'debug' => false, - * 'hosts' => array(), - * 'port' => 389, - * 'use_tls' => false, - * 'ldap_version' => 3, // using LDAPv3 - * 'auth_method' => '', // SASL authentication method (for proxy auth), e.g. DIGEST-MD5 - * 'numsub_filter' => '(objectClass=organizationalUnit)', // with VLV, we also use numSubOrdinates to query the total number of records. Set this filter to get all numSubOrdinates attributes for counting - * 'referrals' => false, // Sets the LDAP_OPT_REFERRALS option. Mostly used in multi-domain Active Directory setups - * 'network_timeout' => 10, // The timeout (in seconds) for connect + bind arrempts. This is only supported in PHP >= 5.3.0 with OpenLDAP 2.x - * 'sizelimit' => 0, // Enables you to limit the count of entries fetched. Setting this to 0 means no limit. - * 'timelimit' => 0, // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit. - * 'vlv' => false, // force VLV off - * 'config_root_dn' => 'cn=config', // Root DN to read config (e.g. vlv indexes) from - * 'service_bind_dn' => 'uid=kolab-service,ou=Special Users,dc=example,dc=org', - * 'service_bind_pw' => 'Welcome2KolabSystems', - * 'root_dn' => 'dc=example,dc=org', - */ - protected $config = array( - 'sizelimit' => 0, - 'timelimit' => 0, - ); - - protected $debug_level = false; - protected $list_page = 1; - protected $page_size = 10; - protected $cache; - - // Use public method config_set('log_hook', $callback) to have $callback be - // call_user_func'ed instead of the local log functions. - protected $_log_hook; - - // Use public method config_set('config_get_hook', $callback) to have - // $callback be call_user_func'ed instead of the local config_get function. - protected $_config_get_hook; - - // Use public method config_set('config_set_hook', $callback) to have - // $callback be call_user_func'ed instead of the local config_set function. - protected $_config_set_hook; - - // Not Yet Implemented - // Intended to allow hooking in for the purpose of caching. - protected $_result_hook; - - // Runtime. These are not the variables you're looking for. - protected $_current_bind_dn; - protected $_current_bind_pw; - protected $_current_host; - protected $_supported_control = array(); - protected $_vlv_indexes_and_searches; - - /** - * Constructor - * - * @param array $config Configuration parameters that have not already - * been initialized. For configuration parameters - * that have in fact been set, use the config_set() - * method after initialization. - */ - public function __construct($config = array()) - { - if (!empty($config) && is_array($config)) { - foreach ($config as $key => $value) { - if (empty($this->config[$key])) { - $setter = 'config_set_' . $key; - if (method_exists($this, $setter)) { - $this->$setter($value); - } - else if (isset($this->$key)) { - $this->$key = $value; - } - else { - $this->config[$key] = $value; - } - } - } - } - } - - /** - * Add multiple entries to the directory information tree in one go. - */ - public function add_entries($entries, $attributes = array()) - { - // If $entries is an associative array, it's keys are DNs and its - // values are the attributes for that DN. - // - // If $entries is a non-associative array, the attributes are expected - // to be positional in $attributes. - - $result_set = array(); - - if (array_keys($entries) == range(0, count($entries) - 1)) { - // $entries is sequential - if (count($entries) !== count($attributes)) { - $this->_error("Wrong entry/attribute count in " . __FUNCTION__); - return false; - } - - for ($i = 0; $i < count($entries); $i++) { - $result_set[$i] = $this->add_entry($entries[$i], $attributes[$i]); - } - } - else { - // $entries is associative - foreach ($entries as $entry_dn => $entry_attributes) { - if (array_keys($attributes) !== range(0, count($attributes)-1)) { - // $attributes is associative as well, let's merge these - // - // $entry_attributes takes precedence, so is in the second - // position in array_merge() - $entry_attributes = array_merge($attributes, $entry_attributes); - } - - $result_set[$entry_dn] = $this->add_entry($entry_dn, $entry_attributes); - } - } - - return $result_set; - } - - /** - * Add an entry to the directory information tree. - */ - public function add_entry($entry_dn, $attributes) - { - // TODO: - // - Get entry rdn attribute value from entry_dn and see if it exists in - // attributes -> issue warning if so (but not block the operation). - $this->_debug("Entry DN", $entry_dn); - $this->_debug("Attributes", $attributes); - - foreach ($attributes as $attr_name => $attr_value) { - if (empty($attr_value)) { - unset($attributes[$attr_name]); - } - } - - $this->_debug("C: Add $entry_dn: " . json_encode($attributes)); - - if (($add_result = ldap_add($this->conn, $entry_dn, $attributes)) == false) { - $this->_debug("S: " . ldap_error($this->conn)); - $this->_debug("S: Adding entry $entry_dn failed. " . ldap_error($this->conn)); - - return false; - } - - $this->_debug("LDAP: S: OK"); - - return true; - } - - /** - * Add replication agreements and initialize the consumer(s) for - * $domain_root_dn. - * - * Searches the configured replicas for any of the current domain/config - * databases, and uses this information to configure the additional - * replication for the (new) domain database (at $domain_root_dn). - * - * Very specific to Netscape-based directory servers, and currently also - * very specific to multi-master replication. - */ - public function add_replication_agreements($domain_root_dn) - { - $replica_hosts = $this->list_replicas(); - - if (empty($replica_hosts)) { - return; - } - - $result = $this->search($this->config_get('config_root_dn'), "(&(objectclass=nsDS5Replica)(nsDS5ReplicaType=3))", "sub"); - - if (!$result) { - $this->_debug("No replication configuration found."); - return; - } - - // Preserve the number of replicated databases we have, because the replication ID - // can be calculated from the number of databases replicated, and the number of - // servers. - $num_replica_dbs = $result->count(); - $replicas = $result->entries(true); - $max_replica_agreements = 0; - - foreach ($replicas as $replica_dn => $replica_attrs) { - $result = $this->search($replica_dn, "(objectclass=nsDS5ReplicationAgreement)", "sub"); - if ($result) { - if ($max_replica_agreements < $result->count()) { - $max_replica_agreements = $result->count(); - $max_replica_agreements_dn = $replica_dn; - } - } - } - - $max_repl_id = $num_replica_dbs * count($replica_hosts); - - $this->_debug("The current maximum replication ID is $max_repl_id"); - $this->_debug("The current maximum number of replication agreements for any database is $max_replica_agreements (for $max_replica_agreements_dn)"); - $this->_debug("With " . count($replica_hosts) . " replicas, the next is " . ($max_repl_id + 1) . " and the last one is " . ($max_repl_id + count($replica_hosts))); - - // Then add the replication agreements - foreach ($replica_hosts as $num => $replica_host) { - $ldap = new Net_LDAP3($this->config); - $ldap->config_set('hosts', array($replica_host)); - $ldap->connect(); - $ldap->bind($this->_current_bind_dn, $this->_current_bind_pw); - - $replica_attrs = array( - 'cn' => 'replica', - 'objectclass' => array( - 'top', - 'nsds5replica', - 'extensibleobject', - ), - 'nsDS5ReplicaBindDN' => $ldap->get_entry_attribute($replica_dn, "nsDS5ReplicaBindDN"), - 'nsDS5ReplicaId' => ($max_repl_id + $num + 1), - 'nsDS5ReplicaRoot' => $domain_root_dn, - 'nsDS5ReplicaType' => $ldap->get_entry_attribute($replica_dn, "nsDS5ReplicaType"), - 'nsds5ReplicaPurgeDelay' => $ldap->get_entry_attribute($replica_dn, "nsds5ReplicaPurgeDelay"), - 'nsDS5Flags' => $ldap->get_entry_attribute($replica_dn, "nsDS5Flags") - ); - - $new_replica_dn = 'cn=replica,cn="' . $domain_root_dn . '",cn=mapping tree,cn=config'; - - $this->_debug("Adding $new_replica_dn to $replica_host with attributes: " . var_export($replica_attrs, true)); - - $result = $ldap->add_entry($new_replica_dn, $replica_attrs); - - if (!$result) { - $this->_error("Could not add replication configuration to database for $domain_root_dn on $replica_host"); - continue; - } - - $result = $ldap->search($replica_dn, "(objectclass=nsDS5ReplicationAgreement)", "sub"); - - if (!$result) { - $this->_error("Host $replica_host does not have any replication agreements"); - continue; - } - - $entries = $result->entries(true); - $replica_agreement_tpl_dn = key($entries); - - $this->_debug("Using " . var_export($replica_agreement_tpl_dn, true) . " as the template for new replication agreements"); - - foreach ($replica_hosts as $replicate_to_host) { - // Skip the current server - if ($replicate_to_host == $replica_host) { - continue; - } - - $this->_debug("Adding a replication agreement for $domain_root_dn to $replicate_to_host on " . $replica_host); - - $attrs = array( - 'objectclass', - 'nsDS5ReplicaBindDN', - 'nsDS5ReplicaCredentials', - 'nsDS5ReplicaTransportInfo', - 'nsDS5ReplicaBindMethod', - 'nsDS5ReplicaHost', - 'nsDS5ReplicaPort' - ); - - $replica_agreement_attrs = $ldap->get_entry_attributes($replica_agreement_tpl_dn, $attrs); - $replica_agreement_attrs['cn'] = array_shift(explode('.', $replicate_to_host)) . str_replace(array('dc=',','), array('_',''), $domain_root_dn); - $replica_agreement_attrs['nsDS5ReplicaRoot'] = $domain_root_dn; - $replica_agreement_dn = "cn=" . $replica_agreement_attrs['cn'] . "," . $new_replica_dn; - - $this->_debug("Adding $replica_agreement_dn to $replica_host with attributes: " . var_export($replica_agreement_attrs, true)); - - $result = $ldap->add_entry($replica_agreement_dn, $replica_agreement_attrs); - - if (!$result) { - $this->_error("Failed adding $replica_agreement_dn"); - } - } - } - - $server_id = implode('', array_diff($replica_hosts, $this->_server_id_not)); - - $this->_debug("About to trigger consumer initialization for replicas on current 'parent': $server_id"); - - $result = $this->search($this->config_get('config_root_dn'), "(&(objectclass=nsDS5ReplicationAgreement)(nsds5replicaroot=$domain_root_dn))", "sub"); - - if ($result) { - foreach ($result->entries(true) as $agreement_dn => $agreement_attrs) { - $this->modify_entry_attributes( - $agreement_dn, - array( - 'replace' => array( - 'nsds5BeginReplicaRefresh' => 'start', - ), - ) - ); - } - } - } - - public function attribute_details($attributes = array()) - { - $schema = $this->init_schema(); - - if (!$schema) { - return array(); - } - - $attribs = $schema->getAll('attributes'); - - $attributes_details = array(); - - foreach ($attributes as $attribute) { - if (array_key_exists($attribute, $attribs)) { - $attrib_details = $attribs[$attribute]; - - if (!empty($attrib_details['sup'])) { - foreach ($attrib_details['sup'] as $super_attrib) { - $_attrib_details = $attribs[$super_attrib]; - if (is_array($_attrib_details)) { - $attrib_details = array_merge($_attrib_details, $attrib_details); - } - } - } - } - else if (array_key_exists(strtolower($attribute), $attribs)) { - $attrib_details = $attribs[strtolower($attribute)]; - - if (!empty($attrib_details['sup'])) { - foreach ($attrib_details['sup'] as $super_attrib) { - $_attrib_details = $attribs[$super_attrib]; - if (is_array($_attrib_details)) { - $attrib_details = array_merge($_attrib_details, $attrib_details); - } - } - } - } - else { - $this->_warning("LDAP: No schema details exist for attribute $attribute (which is strange)"); - } - - // The relevant parts only, please - $attributes_details[$attribute] = array( - 'type' => !empty($attrib_details['single-value']) ? 'text' : 'list', - 'description' => $attrib_details['desc'], - 'syntax' => $attrib_details['syntax'], - 'max-length' => $attrib_details['max-length'] ?: false, - ); - } - - return $attributes_details; - } - - public function attributes_allowed($objectclasses = array()) - { - $this->_debug("Listing allowed_attributes for objectclasses", $objectclasses); - - if (!is_array($objectclasses) || empty($objectclasses)) { - return false; - } - - $schema = $this->init_schema(); - if (!$schema) { - return false; - } - - $may = array(); - $must = array(); - $superclasses = array(); - - foreach ($objectclasses as $objectclass) { - $superclass = $schema->superclass($objectclass); - if (!empty($superclass)) { - $superclasses = array_merge($superclass, $superclasses); - } - - $_may = $schema->may($objectclass); - $_must = $schema->must($objectclass); - - if (is_array($_may)) { - $may = array_merge($may, $_may); - } - if (is_array($_must)) { - $must = array_merge($must, $_must); - } - } - - $may = array_unique($may); - $must = array_unique($must); - $superclasses = array_unique($superclasses); - - return array('may' => $may, 'must' => $must, 'super' => $superclasses); - } - - public function classes_allowed() - { - $schema = $this->init_schema(); - if (!$schema) { - return false; - } - - $list = $schema->getAll('objectclasses'); - $classes = array(); - - foreach ($list as $class) { - $classes[] = $class['name']; - } - - return $classes; - } - - /** - * Bind connection with DN and password - * - * @param string $dn Bind DN - * @param string $pass Bind password - * - * @return boolean True on success, False on error - */ - public function bind($bind_dn, $bind_pw) - { - if (!$this->conn) { - return false; - } - - if ($bind_dn == $this->_current_bind_dn) { - return true; - } - - $this->_debug("C: Bind [dn: $bind_dn]"); - - if (@ldap_bind($this->conn, $bind_dn, $bind_pw)) { - $this->_debug("S: OK"); - $this->_current_bind_dn = $bind_dn; - $this->_current_bind_pw = $bind_pw; - - return true; - } - - $this->_debug("S: ".ldap_error($this->conn)); - $this->_error("Bind failed for dn=$bind_dn: ".ldap_error($this->conn)); - - return false; - } - - /** - * Close connection to LDAP server - */ - public function close() - { - if ($this->conn) { - $this->_debug("C: Close"); - ldap_unbind($this->conn); - - $this->_current_bind_dn = null; - $this->_current_bind_pw = null; - $this->conn = null; - } - } - - /** - * Get the value of a configuration item. - * - * @param string $key Configuration key - * @param mixed $default Default value to return - */ - public function config_get($key, $default = null) - { - if (!empty($this->_config_get_hook)) { - return call_user_func_array($this->_config_get_hook, array($key, $value)); - } - else if (method_exists($this, "config_get_{$key}")) { - return call_user_func(array($this, "config_get_$key"), $value); - } - else if (!isset($this->config[$key])) { - return $default; - } - else { - return $this->config[$key]; - } - } - - /** - * Set a configuration item to value. - * - * @param string $key Configuration key - * @param mixed $value Configuration value - */ - public function config_set($key, $value = null) - { - if (is_array($key)) { - foreach ($key as $k => $v) { - $this->config_set($k, $v); - } - return; - } - - if (!empty($this->_config_set_hook)) { - call_user_func($this->_config_set_hook, array($key, $value)); - } - else if (method_exists($this, "config_set_{$key}")) { - call_user_func_array(array($this, "config_set_$key"), array($value)); - } - else if (isset($this->$key)) { - $this->$key = $value; - } - else { - // 'host' option is deprecated - if ($key == 'host') { - $this->config['hosts'] = (array) $value; - } - else { - $this->config[$key] = $value; - } - } - } - - /** - * Establish a connection to the LDAP server - */ - public function connect($host = null) - { - if (!function_exists('ldap_connect')) { - $this->_error("No ldap support in this PHP installation"); - return false; - } - - if (is_resource($this->conn)) { - $this->_debug("Connection already exists"); - return true; - } - - $hosts = !empty($host) ? $host : $this->config_get('hosts', array()); - $port = $this->config_get('port', 389); - - foreach ((array) $hosts as $host) { - $this->_debug("C: Connect [$host:$port]"); - - if ($lc = @ldap_connect($host, $port)) { - if ($this->config_get('use_tls', false) === true) { - if (!ldap_start_tls($lc)) { - $this->_debug("S: Could not start TLS. " . ldap_error($lc)); - continue; - } - } - - $this->_debug("S: OK"); - - $ldap_version = $this->config_get('ldap_version', 3); - $timeout = $this->config_get('network_timeout'); - $referrals = $this->config_get('referrals'); - - ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $ldap_version); - - if ($timeout) { - ldap_set_option($lc, LDAP_OPT_NETWORK_TIMEOUT, $timeout); - } - - if ($referrals !== null) { - ldap_set_option($lc, LDAP_OPT_REFERRALS, (bool) $referrals); - } - - $this->_current_host = $host; - $this->conn = $lc; - - break; - } - - $this->_debug("S: NOT OK"); - } - - if (!is_resource($this->conn)) { - $this->_error("Could not connect to LDAP"); - return false; - } - - return true; - } - - /** - * Shortcut to ldap_delete() - */ - public function delete_entry($entry_dn) - { - $this->_debug("LDAP: C: Delete $entry_dn"); - - if (ldap_delete($this->conn, $entry_dn) === false) { - $this->_debug("LDAP: S: " . ldap_error($this->conn)); - return false; - } - - $this->_debug("LDAP: S: OK"); - - return true; - } - - /** - * Deletes specified entry and all entries in the tree - */ - public function delete_entry_recursive($entry_dn) - { - // searching for sub entries, but not scope sub, just one level - $result = $this->search($entry_dn, '(objectclass=*)', 'one'); - - if ($result) { - $entries = $result->entries(true); - - foreach (array_keys($entries) as $sub_dn) { - if (!$this->delete_entry_recursive($sub_dn)) { - return false; - } - } - } - - if (!$this->delete_entry($entry_dn)) { - return false; - } - - return true; - } - - public function effective_rights($subject) - { - $effective_rights_control_oid = "1.3.6.1.4.1.42.2.27.9.5.2"; - - $supported_controls = $this->supported_controls(); - - if (!in_array($effective_rights_control_oid, $supported_controls)) { - $this->_debug("LDAP: No getEffectiveRights control in supportedControls"); - return false; - } - - $attributes = array( - 'attributeLevelRights' => array(), - 'entryLevelRights' => array(), - ); - - $output = array(); - $entry_dn = $this->entry_dn($subject); - - if (!$entry_dn) { - $entry_dn = $this->config_get($subject . "_base_dn"); - } - if (!$entry_dn) { - $entry_dn = $this->config_get("base_dn"); - } - if (!$entry_dn) { - $entry_dn = $this->config_get("root_dn"); - } - - $this->_debug("effective_rights for subject $subject resolves to entry dn $entry_dn"); - - $moz_ldapsearch = "/usr/lib64/mozldap/ldapsearch"; - if (!is_file($moz_ldapsearch)) { - $moz_ldapsearch = "/usr/lib/mozldap/ldapsearch"; - } - if (!is_file($moz_ldapsearch)) { - $moz_ldapsearch = null; - } - - if (empty($moz_ldapsearch)) { - $this->_error("Mozilla LDAP C SDK binary ldapsearch not found, cannot get effective rights on subject $subject"); - return null; - } - - $command = array( - $moz_ldapsearch, - '-x', - '-h', - $this->_ldap_server, - '-p', - $this->_ldap_port, - '-b', - escapeshellarg($entry_dn), - '-D', - escapeshellarg($this->_current_bind_dn), - '-w', - escapeshellarg($this->_current_bind_pw), - '-J', - escapeshellarg(implode(':', array( - $effective_rights_control_oid, // OID - 'true', // Criticality - 'dn:' . $this->_current_bind_dn // User DN - ))), - '-s', - 'base', - '"(objectclass=*)"', - '"*"', - ); - - $command = implode(' ', $command); - - $this->_debug("LDAP: Executing command: $command"); - - exec($command, $output, $return_code); - - $this->_debug("LDAP: Command output:" . var_export($output, true)); - $this->_debug("Return code: " . $return_code); - - if ($return_code) { - $this->_error("Command $moz_ldapsearch returned error code: $return_code"); - return null; - } - - $lines = array(); - foreach ($output as $line_num => $line) { - if (substr($line, 0, 1) == " ") { - $lines[count($lines)-1] .= trim($line); - } - else { - $lines[] = trim($line); - } - } - - foreach ($lines as $line) { - $line_components = explode(':', $line); - $attribute_name = array_shift($line_components); - $attribute_value = trim(implode(':', $line_components)); - - switch ($attribute_name) { - case "attributeLevelRights": - $attributes[$attribute_name] = $this->parse_attribute_level_rights($attribute_value); - break; - case "dn": - $attributes[$attribute_name] = $attribute_value; - break; - case "entryLevelRights": - $attributes[$attribute_name] = $this->parse_entry_level_rights($attribute_value); - break; - - default: - break; - } - } - - return $attributes; - } - - /** - * Resolve entry data to entry DN - * - * @param string $subject Entry string (e.g. entry DN or unique attribute value) - * @param array $attributes Additional attributes - * @param string $base_dn Optional base DN - * - * @return string Entry DN string - */ - public function entry_dn($subject, $attributes = array(), $base_dn = null) - { - $this->_debug("entry_dn on subject $subject"); - $is_dn = ldap_explode_dn($subject, 1); - - if (is_array($is_dn) && array_key_exists("count", $is_dn) && $is_dn["count"] > 0) { - $this->_debug("$subject is a dn"); - return $subject; - } - - $this->_debug("$subject is not a dn"); - - if (strlen($subject) < 32 || preg_match('/[^a-fA-F0-9-]/', $subject)) { - $this->_debug("$subject is not a unique identifier"); - return; - } - - $unique_attr = $this->config_get('unique_attribute', 'nsuniqueid'); - - $this->_debug("Using unique_attribute " . var_export($unique_attr, true) . " at " . __FILE__ . ":" . __LINE__); - - $attributes = array_merge(array($unique_attr => $subject), (array)$attributes); - $subject = $this->entry_find_by_attribute($attributes, $base_dn); - - if (!empty($subject)) { - return key($subject); - } - } - - public function entry_find_by_attribute($attributes, $base_dn = null) - { - $this->_debug("Net_LDAP3::entry_find_by_attribute(\$attributes, \$base_dn) called with base_dn", $base_dn, "and attributes", $attributes); - - if (empty($attributes) || !is_array($attributes)) { - return false; - } - - if (empty($attributes[key($attributes)])) { - return false; - } - - $filter = count($attributes) ? "(&" : ""; - - foreach ($attributes as $key => $value) { - $filter .= "(" . $key . "=" . $value . ")"; - } - - $filter .= count($attributes) ? ")" : ""; - - if (empty($base_dn)) { - $base_dn = $this->config_get('root_dn'); - $this->_debug("Using base_dn from domain " . $this->domain . ": " . $base_dn); - } - - $result = $this->search($base_dn, $filter, 'sub', array_keys($attributes)); - - if ($result && $result->count() > 0) { - $this->_debug("Results found: " . implode(', ', array_keys($result->entries(true)))); - return $result->entries(true); - } - else { - $this->_debug("No result"); - return false; - } - } - - public function find_user_groups($member_dn) - { - $groups = array(); - $root_dn = $this->config_get('root_dn'); - - // TODO: Do not query for both, it's either one or the other - $entries = $this->search($root_dn, "(|" . - "(&(objectclass=groupofnames)(member=$member_dn))" . - "(&(objectclass=groupofuniquenames)(uniquemember=$member_dn))" . - ")" - ); - - if ($entries) { - $groups = array_keys($entries->entries(true)); - } - - return $groups; - } - - public function get_entry_attribute($subject_dn, $attribute) - { - $entry = $this->get_entry_attributes($subject_dn, (array)$attribute); - - return $entry[strtolower($attribute)]; - } - - public function get_entry_attributes($subject_dn, $attributes) - { - // @TODO: use get_entry? - $result = $this->search($subject_dn, '(objectclass=*)', 'base', $attributes); - - if (!$result) { - return array(); - } - - $entries = $result->entries(true); - $entry_dn = key($entries); - $entry = $entries[$entry_dn]; - - return $entry; - } - - /** - * Get a specific LDAP entry, identified by its DN - * - * @param string $dn Record identifier - * @param array $attributes Attributes to return - * - * @return array Hash array - */ - public function get_entry($dn, $attributes = array()) - { - $rec = null; - - if ($this->conn && $dn) { - $this->_debug("C: Read [dn: $dn] [(objectclass=*)]"); - - if ($ldap_result = @ldap_read($this->conn, $dn, '(objectclass=*)', $attributes)) { - $this->_debug("S: OK"); - - if ($entry = ldap_first_entry($this->conn, $ldap_result)) { - $rec = ldap_get_attributes($this->conn, $entry); - } - } - else { - $this->_debug("S: ".ldap_error($this->conn)); - } - - if (!empty($rec)) { - $rec['dn'] = $dn; // Add in the dn for the entry. - } - } - - return $rec; - } - - public function list_replicas() - { - $this->_debug("Finding replicas for this server."); - - // Search any host that is a replica for the current host - $replica_hosts = $this->config_get('replica_hosts', array()); - $root_dn = $this->config_get('config_root_dn'); - - if (!empty($replica_hosts)) { - return $replica_hosts; - } - - $ldap = new Net_LDAP3($this->config); - $ldap->connect(); - $ldap->bind($this->_current_bind_dn, $this->_current_bind_pw); - - $result = $ldap->search($root_dn, '(objectclass=nsds5replicationagreement)', 'sub', array('nsds5replicahost')); - - if (!$result) { - $this->_debug("No replicas configured"); - return $replica_hosts; - } - - $this->_debug("Replication agreements found: " . var_export($result->entries(true), true)); - - foreach ($result->entries(true) as $dn => $attrs) { - if (!in_array($attrs['nsds5replicahost'], $replica_hosts)) { - $replica_hosts[] = $attrs['nsds5replicahost']; - } - } - - // $replica_hosts now holds the IDs of servers we are currently NOT - // connected to. We might need this later in order to set - $this->_server_id_not = $replica_hosts; - - $this->_debug("So far, we have the following replicas: " . var_export($replica_hosts, true)); - - $ldap->close(); - - foreach ($replica_hosts as $replica_host) { - $ldap->config_set('hosts', array($replica_host)); - $ldap->connect(); - $ldap->bind($this->_current_bind_dn, $this->_current_bind_pw); - - $result = $ldap->search($root_dn, '(objectclass=nsds5replicationagreement)', 'sub', array('nsds5replicahost')); - if (!$result) { - $this->_debug("No replicas configured"); - } - - foreach ($result->entries(true) as $dn => $attrs) { - if (!in_array($attrs['nsds5replicahost'], $replica_hosts)) { - $replica_hosts[] = $attrs['nsds5replicahost']; - } - } - - $ldap->close(); - } - - $this->config_set('replica_hosts', $replica_hosts); - - return $replica_hosts; - } - - public function login($username, $password, $domain = null) - { - $this->_debug("Net_LDAP3::login(\$username = '" . $username . "', \$password = '****', \$domain = '" . $domain . "')"); - - $_bind_dn = $this->config_get('service_bind_dn'); - $_bind_pw = $this->config_get('service_bind_pw'); - - if (empty($_bind_dn)) { - $this->_debug("No valid service bind dn found."); - return null; - } - - if (empty($_bind_pw)) { - $this->_debug("No valid service bind password found."); - return null; - } - - $bound = $this->bind($_bind_dn, $_bind_pw); - - if (!$bound) { - $this->_debug("Could not bind with service bind credentials."); - return null; - } - - $entry_dn = $this->entry_dn($username); - - if (!empty($entry_dn)) { - $bound = $this->bind($entry_dn, $password); - - if (!$bound) { - $this->_error("Could not bind with " . $entry_dn); - return null; - } - - return $entry_dn; - } - - $base_dn = $this->config_get('root_dn'); - - if (empty($base_dn)) { - $this->_debug("Could not get a valid base dn to search."); - return null; - } - - $localpart = $username; - - if (empty($domain) ) { - if (count(explode('@', $username)) > 1) { - $_parts = explode('@', $username); - $localpart = $_parts[0]; - $domain = $_parts[1]; - } - else { - $localpart = $username; - $domain = ''; - } - } - - $realm = $domain; - $filter = $this->config_get("login_filter", null); - - if (empty($filter)) { - $filter = $this->config_get("filter", null); - } - if (empty($filter)) { - $filter = "(&(|(mail=%s)(mail=%U@%d)(alias=%s)(alias=%U@%d)(uid=%s))(objectclass=inetorgperson))"; - } - - $this->_debug("Net::LDAP3::login() original filter: " . $filter); - - $replace_patterns = array( - '/%s/' => $username, - '/%d/' => $domain, - '/%U/' => $localpart, - '/%r/' => $realm - ); - - $filter = preg_replace(array_keys($replace_patterns), array_values($replace_patterns), $filter); - - $this->_debug("Net::LDAP3::login() actual filter: " . $filter); - - $result = $this->search($base_dn, $filter, 'sub'); - - if (!$result) { - $this->_debug("Could not search $base_dn with $filter"); - return null; - } - - if ($result->count() > 1) { - $this->_debug("Multiple entries found."); - return null; - } - else if ($result->count() < 1) { - $this->_debug("No entries found."); - return null; - } - - $entries = $result->entries(); - $entry = self::normalize_result($entries); - $entry_dn = key($entry); - - $bound = $this->bind($entry_dn, $password); - - if (!$bound) { - $this->_debug("Could not bind with " . $entry_dn); - return null; - } - - return $entry_dn; - } - - public function list_group_members($dn, $entry = null, $recurse = true) - { - $this->_debug("Called list_group_members(" . $dn . ")"); - - if (is_array($entry) && in_array('objectclass', $entry)) { - if (!in_array(array('groupofnames', 'groupofuniquenames', 'groupofurls'), $entry['objectclass'])) { - $this->_debug("Called list_group_members on a non-group!"); - return array(); - } - } - else { - $entry = $this->get_entry($dn, array('member', 'uniquemember', 'memberurl', 'objectclass')); - - if (!$entry) { - return array(); - } - } - - $group_members = array(); - - foreach ((array)$entry['objectclass'] as $objectclass) { - switch (strtolower($objectclass)) { - case "groupofnames": - case "kolabgroupofnames": - $group_members = array_merge($group_members, $this->list_group_member($dn, $entry['member'], $recurse)); - break; - case "groupofuniquenames": - case "kolabgroupofuniquenames": - $group_members = array_merge($group_members, $this->list_group_uniquemember($dn, $entry['uniquemember'], $recurse)); - break; - case "groupofurls": - $group_members = array_merge($group_members, $this->list_group_memberurl($dn, $entry['memberurl'], $recurse)); - break; - } - } - - return array_values(array_filter($group_members)); - } - - public function modify_entry($subject_dn, $old_attrs, $new_attrs) - { - $this->_debug("OLD ATTRIBUTES", $old_attrs); - $this->_debug("NEW ATTRIBUTES", $new_attrs); - - // TODO: Get $rdn_attr - we have type_id in $new_attrs - $dn_components = ldap_explode_dn($subject_dn, 0); - $rdn_components = explode('=', $dn_components[0]); - $rdn_attr = $rdn_components[0]; - - $this->_debug("Net_LDAP3::modify_entry() using rdn attribute: " . $rdn_attr); - - $mod_array = array( - 'add' => array(), // For use with ldap_mod_add() - 'del' => array(), // For use with ldap_mod_del() - 'replace' => array(), // For use with ldap_mod_replace() - 'rename' => array(), // For use with ldap_rename() - ); - - // This is me cheating. Remove this special attribute. - if (array_key_exists('ou', $old_attrs) || array_key_exists('ou', $new_attrs)) { - $old_ou = $old_attrs['ou']; - $new_ou = $new_attrs['ou']; - unset($old_attrs['ou']); - unset($new_attrs['ou']); - } - else { - $old_ou = null; - $new_ou = null; - } - - // Compare each attribute value of the old attrs with the corresponding value - // in the new attrs, if any. - foreach ($old_attrs as $attr => $old_attr_value) { - if (is_array($old_attr_value)) { - if (count($old_attr_value) == 1) { - $old_attrs[$attr] = $old_attr_value[0]; - $old_attr_value = $old_attrs[$attr]; - } - } - - if (array_key_exists($attr, $new_attrs)) { - if (is_array($new_attrs[$attr])) { - if (count($new_attrs[$attr]) == 1) { - $new_attrs[$attr] = $new_attrs[$attr][0]; - } - } - - if (is_array($old_attrs[$attr]) && is_array($new_attrs[$attr])) { - $_sort1 = $new_attrs[$attr]; - sort($_sort1); - $_sort2 = $old_attr_value; - sort($_sort2); - } - else { - $_sort1 = true; - $_sort2 = false; - } - - if ($new_attrs[$attr] !== $old_attr_value && $_sort1 !== $_sort2) { - $this->_debug("Attribute $attr changed from " . var_export($old_attr_value, true) . " to " . var_export($new_attrs[$attr], true)); - if ($attr === $rdn_attr) { - $this->_debug("This attribute is the RDN attribute. Let's see if it is multi-valued, and if the original still exists in the new value."); - if (is_array($old_attrs[$attr])) { - if (!is_array($new_attrs[$attr])) { - if (in_array($new_attrs[$attr], $old_attrs[$attr])) { - // TODO: Need to remove all $old_attrs[$attr] values not equal to $new_attrs[$attr], and not equal to the current $rdn_attr value [0] - - $this->_debug("old attrs. is array, new attrs. is not array. new attr. exists in old attrs."); - - $rdn_attr_value = array_shift($old_attrs[$attr]); - $_attr_to_remove = array(); - - foreach ($old_attrs[$attr] as $value) { - if (strtolower($value) != strtolower($new_attrs[$attr])) { - $_attr_to_remove[] = $value; - } - } - - $this->_debug("Adding to delete attribute $attr values:" . implode(', ', $_attr_to_remove)); - - $mod_array['del'][$attr] = $_attr_to_remove; - - if (strtolower($new_attrs[$attr]) !== strtolower($rdn_attr_value)) { - $this->_debug("new attrs is not the same as the old rdn value, issuing a rename"); - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0]; - } - } - else { - $this->_debug("new attrs is not the same as any of the old rdn value, issuing a full rename"); - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr]; - } - } - else { - // TODO: See if the rdn attr. value is still in $new_attrs[$attr] - if (in_array($old_attrs[$attr][0], $new_attrs[$attr])) { - $this->_debug("Simply replacing attr $attr as rdn attr value is preserved."); - $mod_array['replace'][$attr] = $new_attrs[$attr]; - } - else { - // TODO: This fails. - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr][0]; - $mod_array['del'][$attr] = $old_attrs[$attr][0]; - } - } - } - else { - if (!is_array($new_attrs[$attr])) { - $this->_debug("Renaming " . $old_attrs[$attr] . " to " . $new_attrs[$attr]); - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$attr]; - } - else { - $this->_debug("Adding to replace"); - // An additional attribute value is being supplied. Just replace and continue. - $mod_array['replace'][$attr] = $new_attrs[$attr]; - continue; - } - } - - } - else { - if (!isset($new_attrs[$attr]) || $new_attrs[$attr] === '' || (is_array($new_attrs[$attr]) && empty($new_attrs[$attr]))) { - switch ($attr) { - case "userpassword": - break; - default: - $this->_debug("Adding to del: $attr"); - $mod_array['del'][$attr] = (array)($old_attr_value); - break; - } - } - else { - $this->_debug("Adding to replace: $attr"); - $mod_array['replace'][$attr] = (array)($new_attrs[$attr]); - } - } - } - else { - $this->_debug("Attribute $attr unchanged"); - } - } - else { - // TODO: Since we're not shipping the entire object back and forth, and only post - // part of the data... we don't know what is actually removed (think modifiedtimestamp, etc.) - $this->_debug("Group attribute $attr not mentioned in \$new_attrs..., but not explicitly removed... by assumption"); - } - } - - foreach ($new_attrs as $attr => $value) { - // OU's parent base dn - if ($attr == 'base_dn') { - continue; - } - - if (is_array($value)) { - if (count($value) == 1) { - $new_attrs[$attr] = $value[0]; - $value = $new_attrs[$attr]; - } - } - - if (array_key_exists($attr, $old_attrs)) { - if (is_array($old_attrs[$attr])) { - if (count($old_attrs[$attr]) == 1) { - $old_attrs[$attr] = $old_attrs[$attr][0]; - } - } - - if (is_array($new_attrs[$attr]) && is_array($old_attrs[$attr])) { - $_sort1 = $old_attrs[$attr]; - sort($_sort1); - $_sort2 = $value; - sort($_sort2); - } - else { - $_sort1 = true; - $_sort2 = false; - } - - if ($value === null || $value === '' || (is_array($value) && empty($value))) { - if (!array_key_exists($attr, $mod_array['del'])) { - switch ($attr) { - case 'userpassword': - break; - default: - $this->_debug("Adding to del(2): $attr"); - $mod_array['del'][$attr] = (array)($old_attrs[$attr]); - break; - } - } - } - else { - if (!($old_attrs[$attr] === $value) && !($attr === $rdn_attr) && !($_sort1 === $_sort2)) { - if (!array_key_exists($attr, $mod_array['replace'])) { - $this->_debug("Adding to replace(2): $attr"); - $mod_array['replace'][$attr] = $value; - } - } - } - } - else { - if (!empty($value)) { - $mod_array['add'][$attr] = $value; - } - } - } - - if (empty($old_ou)) { - $subject_dn_components = ldap_explode_dn($subject_dn, 0); - unset($subject_dn_components["count"]); - $subject_rdn = array_shift($subject_dn_components); - $old_ou = implode(',', $subject_dn_components); - } - - // object is an organizational unit - if (strpos($subject_dn, 'ou=' . $old_ou) === 0) { - $root = substr($subject_dn, strlen($old_ou) + 4); // remove ou=*, - - if ((!empty($new_attrs['base_dn']) && strtolower($new_attrs['base_dn']) !== strtolower($root)) - || (strtolower($old_ou) !== strtolower($new_ou)) - ) { - if (!empty($new_attrs['base_dn'])) { - $root = $new_attrs['base_dn']; - } - - $mod_array['rename']['new_parent'] = $root; - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = 'ou=' . $new_ou; - } - } - // not OU object, but changed ou attribute - else if ((!empty($old_ou) && !empty($new_ou)) && strtolower($old_ou) !== strtolower($new_ou)) { - $mod_array['rename']['new_parent'] = $new_ou; - if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) { - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$rdn_attr]; - } - } - - $this->_debug($mod_array); - - $result = $this->modify_entry_attributes($subject_dn, $mod_array); - - if ($result) { - return $mod_array; - } - } - - /** - * Bind connection with (SASL-) user and password - * - * @param string $authc Authentication user - * @param string $pass Bind password - * @param string $authz Autorization user - * - * @return boolean True on success, False on error - */ - public function sasl_bind($authc, $pass, $authz=null) - { - if (!$this->conn) { - return false; - } - - if (!function_exists('ldap_sasl_bind')) { - $this->_error("Unable to bind: ldap_sasl_bind() not exists"); - return false; - } - - if (!empty($authz)) { - $authz = 'u:' . $authz; - } - - $method = $this->config_get('auth_method'); - if (empty($method)) { - $method = 'DIGEST-MD5'; - } - - $this->_debug("C: Bind [mech: $method, authc: $authc, authz: $authz]"); - - if (ldap_sasl_bind($this->conn, null, $pass, $method, null, $authc, $authz)) { - $this->_debug("S: OK"); - return true; - } - - $this->_debug("S: ".ldap_error($this->conn)); - $this->_error("Bind failed for authcid=$authc ".ldap_error($this->conn)); - - return false; - } - - /** - * Execute LDAP search - * - * @param string $base_dn Base DN to use for searching - * @param string $filter Filter string to query - * @param string $scope The LDAP scope (list|sub|base) - * @param array $attrs List of entry attributes to read - * @param array $prop Hash array with query configuration properties: - * - sort: array of sort attributes (has to be in sync with the VLV index) - * - search: search string used for VLV controls - * @param bool $count_only Set to true if only entry count is requested - * - * @return mixed Net_LDAP3_Result object or number of entries (if $count_only=true) or False on failure - */ - public function search($base_dn, $filter = '(objectclass=*)', $scope = 'sub', $attrs = array('dn'), $props = array(), $count_only = false) - { - if (!$this->conn) { - $this->_debug("No active connection for " . __CLASS__ . "::" . __FUNCTION__); - return false; - } - - $this->_debug("C: Search base dn: [$base_dn] scope [$scope] with filter [$filter]"); - - // make sure attributes list is not empty - if (empty($attrs)) { - $attrs = array('dn'); - } - - if (!$count_only && ($sort = $this->find_vlv($base_dn, $filter, $scope, $props['sort']))) { - // when using VLV, we get the total count by... - // ...either reading numSubOrdinates attribute - if (($sub_filter = $this->config_get('numsub_filter')) && - ($result_count = @$ns_function($this->conn, $base_dn, $sub_filter, array('numSubOrdinates'), 0, 0, 0)) - ) { - $counts = ldap_get_entries($this->conn, $result_count); - for ($vlv_count = $j = 0; $j < $counts['count']; $j++) { - $vlv_count += $counts[$j]['numsubordinates'][0]; - } - $this->_debug("D: total numsubordinates = " . $vlv_count); - } - // ...or by fetching all records dn and count them - else if (!function_exists('ldap_parse_virtuallist_control')) { - $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $props, true); - } - - $this->vlv_active = $this->_vlv_set_controls($sort, $this->list_page, $this->page_size, - $this->_vlv_search($sort, $props['search'])); - } - else { - $this->vlv_active = false; - } - - $function = self::scope_to_function($scope, $ns_function); - $sizelimit = (int) $this->config['sizelimit']; - $timelimit = (int) $this->config['timelimit']; - - $this->_debug("Using function $function on scope $scope (\$ns_function is $ns_function)"); - - if ($this->vlv_active) { - if (!empty($this->additional_filter)) { - $filter = "(&" . $filter . $this->additional_filter . ")"; - $this->_debug("C: (With VLV) Setting a filter (with additional filter) of " . $filter); - } - else { - $this->_debug("C: (With VLV) Setting a filter (without additional filter) of " . $filter); - } - } - else { - if (!empty($this->additional_filter)) { - $filter = "(&" . $filter . $this->additional_filter . ")"; - } - $this->_debug("C: (Without VLV) Setting a filter of " . $filter); - } - - $this->_debug("Executing search with return attributes: " . var_export($attrs, true)); - - $ldap_result = @$function($this->conn, $base_dn, $filter, $attrs, 0, $sizelimit, $timelimit); - - if (!$ldap_result) { - $this->_debug("$function failed for dn=$base_dn: ".ldap_error($this->conn)); - return false; - } - - // when running on a patched PHP we can use the extended functions - // to retrieve the total count from the LDAP search result - if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) { - if (ldap_parse_result($this->conn, $ldap_result, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)) { - ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $vlv_count, $vresult); - $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$vlv_count"); - } - else { - $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn))); - } - } - else if ($this->debug) { - $this->_debug("S: ".ldap_count_entries($this->conn, $ldap_result)." record(s) found"); - } - - $result = new Net_LDAP3_Result($this->conn, $base_dn, $filter, $scope, $ldap_result); - $result->set('offset', $last_offset); - $result->set('count', $vlv_count); - $result->set('vlv', true); - - return $count_only ? $result->count() : $result; - } - - /** - * Similar to Net_LDAP3::search() but using a search array with multiple - * keys and values that to continue to use the VLV but with an original - * filter adding the search stuff to an additional filter. - * - * @see Net_LDAP3::search() - */ - public function search_entries($base_dn, $filter = '(objectclass=*)', $scope = 'sub', $attrs = array('dn'), $props = array()) - { - $this->_debug("Net_LDAP3::search_entries with search " . var_export($props, true)); - - if (is_array($props['search']) && array_key_exists('params', $props['search'])) { - $_search = $this->search_filter($props['search']); - $this->_debug("C: Search filter: $_search"); - - if (!empty($_search)) { - $this->additional_filter = $_search; - } - else { - $this->additional_filter = "(|"; - - foreach ($props['search'] as $attr => $value) { - $this->additional_filter .= "(" . $attr . "=" . $this->_fuzzy_search_prefix() . $value . $this->_fuzzy_search_suffix() . ")"; - } - - $this->additional_filter .= ")"; - } - - $this->_debug("C: Setting an additional filter " . $this->additional_filter); - } - - $search = $this->search($base_dn, $filter, $scope, $attrs, $props); - - $this->additional_filter = null; - - if (!$search) { - $this->_debug("Net_LDAP3: Search did not succeed!"); - return false; - } - - return $search; - } - - /** - * Create LDAP search filter string according to defined parameters. - */ - public function search_filter($search) - { - if (empty($search) || !is_array($search) || empty($search['params'])) { - return null; - } - - $operators = array('=', '~=', '>=', '<='); - $filter = ''; - - foreach ((array) $search['params'] as $field => $param) { - switch ((string)$param['type']) { - case 'prefix': - $prefix = ''; - $suffix = '*'; - break; - - case 'suffix': - $prefix = '*'; - $suffix = ''; - break; - - case 'exact': - case '=': - case '~=': - case '>=': - case '<=': - $prefix = ''; - $suffix = ''; - break; - - case 'exists': - $prefix = '*'; - $suffix = ''; - $param['value'] = ''; - break; - - case 'both': - default: - $prefix = '*'; - $suffix = '*'; - break; - } - - $operator = $param['type'] && in_array($param['type'], $operators) ? $param['type'] : '='; - - if (is_array($param['value'])) { - $val_filter = array(); - foreach ($param['value'] as $val) { - $value = self::quote_string($val); - $val_filter[] = "(" . $field . $operator . $prefix . $value . $suffix . ")"; - } - $filter .= "(|" . implode($val_filter, '') . ")"; - } - else { - $value = self::quote_string($param['value']); - $filter .= "(" . $field . $operator . $prefix . $value . $suffix . ")"; - } - } - - // join search parameters with specified operator ('OR' or 'AND') - if (count($search['params']) > 1) { - $filter = '(' . ($search['operator'] == 'AND' ? '&' : '|') . $filter . ')'; - } - - return $filter; - } - - /** - * Set properties for VLV-based paging - * - * @param number $page Page number to list (starting at 1) - * @param number $size Number of entries to display on one page - */ - public function set_vlv_page($page, $size = 10) - { - $this->list_page = $page; - $this->page_size = $size; - } - - /** - * Turn an LDAP entry into a regular PHP array with attributes as keys. - * - * @param array $entry Attributes array as retrieved from ldap_get_attributes() or ldap_get_entries() - * - * @return array Hash array with attributes as keys - */ - public static function normalize_entry($entry) - { - $rec = array(); - for ($i=0; $i < $entry['count']; $i++) { - $attr = $entry[$i]; - for ($j=0; $j < $entry[$attr]['count']; $j++) { - $rec[$attr][$j] = $entry[$attr][$j]; - } - } - - return $rec; - } - - /** - * Normalize a ldap result by converting entry attribute arrays into single values - */ - public static function normalize_result($_result) - { - if (!is_array($_result)) { - return array(); - } - - $result = array(); - - for ($x = 0; $x < $_result['count']; $x++) { - $dn = $_result[$x]['dn']; - $result[$dn] = array(); - for ($y = 0; $y < $_result[$x]['count']; $y++) { - $attr = $_result[$x][$y]; - if ($_result[$x][$attr]['count'] == 1) { - switch ($attr) { - case 'objectclass': - $result[$dn][$attr] = array(strtolower($_result[$x][$attr][0])); - break; - default: - $result[$dn][$attr] = $_result[$x][$attr][0]; - break; - } - } - else { - $result[$dn][$attr] = array(); - for ($z = 0; $z < $_result[$x][$attr]['count']; $z++) { - switch ($attr) { - case 'objectclass': - $result[$dn][$attr][] = strtolower($_result[$x][$attr][$z]); - break; - default: - $result[$dn][$attr][] = $_result[$x][$attr][$z]; - break; - } - } - } - } - } - - return $result; - } - - public static function scopeint2str($scope) - { - switch ($scope) { - case 2: - return 'sub'; - break; - case 1: - return 'one'; - break; - case 0: - return 'base'; - break; - default: - $this->_debug("Scope $scope is not a valid scope integer"); - break; - } - } - - /** - * Choose the right PHP function according to scope property - * - * @param string $scope The LDAP scope (sub|base|list) - * @param string $ns_function Function to be used for numSubOrdinates queries - * @return string PHP function to be used to query directory - */ - public static function scope_to_function($scope, &$ns_function = null) - { - switch ($scope) { - case 'sub': - $function = $ns_function = 'ldap_search'; - break; - case 'base': - $function = $ns_function = 'ldap_read'; - break; - case 'one': - case 'list': - default: - $function = 'ldap_list'; - $ns_function = 'ldap_read'; - break; - } - - return $function; - } - - private function config_set_config_get_hook($callback) - { - $this->_config_get_hook = $callback; - } - - private function config_set_config_set_hook($callback) - { - $this->_config_set_hook = $callback; - } - - /** - * Sets the debug level both for this class and the ldap connection. - */ - private function config_set_debug($value) - { - $this->config['debug'] = (bool) $value; - if ((int)($value) > 0) { - ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, (int)($value)); - } - } - - /** - * Sets a log hook that is called with every log message in this module. - */ - private function config_set_log_hook($callback) - { - $this->_log_hook = $callback; - } - - /** - * Find a matching VLV - */ - protected function find_vlv($base_dn, $filter, $scope, $sort_attrs = null) - { - if ($scope == 'base') { - return false; - } - - $vlv_indexes = $this->find_vlv_indexes_and_searches(); - - if (empty($vlv_indexes)) { - return false; - } - - $this->_debug("Existing vlv index and search information", $vlv_indexes); - - $filter = strtolower($filter); - - foreach ($vlv_indexes as $vlv_index) { - if (!empty($vlv_index[$base_dn])) { - $this->_debug("Found a VLV for base_dn: " . $base_dn); - if ($vlv_index[$base_dn]['filter'] == $filter) { - if ($vlv_index[$base_dn]['scope'] == $scope) { - $this->_debug("Scope and filter matches"); - - // Not passing any sort attributes means you don't care - if (!empty($sort_attrs)) { - $sort_attrs = (array) $sort_attrs; - foreach ($vlv_index[$base_dn]['sort'] as $sss_config) { - if (count(array_intersect($sort_attrs, $sss_config)) == count($sort_attrs)) { - return $sort_attrs; - } - } - - $this->_error("The requested sorting does not match any server-side sorting configuration"); - - return false; - } - else { - return $vlv_index[$base_dn]['sort'][0]; - } - } - else { - $this->_debug("Scope does not match. VLV: " . var_export($vlv_index[$base_dn]['scope'], true) - . " while looking for " . var_export($scope, true)); - return false; - } - } - else { - $this->_debug("Filter does not match"); - } - } - } - - return false; - } - - /** - * Return VLV indexes and searches including necessary configuration - * details. - */ - protected function find_vlv_indexes_and_searches() - { - if ($this->config['vlv'] === false) { - return false; - } - - if (is_array($this->config['vlv'])) { - return $this->config['vlv']; - } - - if ($this->_vlv_indexes_and_searches !== null) { - return $this->_vlv_indexes_and_searches; - } - - $this->_vlv_indexes_and_searches = array(); - - $config_root_dn = $this->config_get('config_root_dn'); - - if (empty($config_root_dn)) { - return array(); - } - - if ($this->cache && ($cached_config = $this->cache->get('vlvconfig'))) { - $this->_vlv_indexes_and_searches = $cached_config; - return $this->_vlv_indexes_and_searches; - } - - $this->_debug("No VLV information available yet, refreshing"); - - $search_filter = '(objectclass=vlvsearch)'; - $search_result = ldap_search($this->conn, $config_root_dn, $search_filter, array('*'), 0, 0, 0); - - if ($search_result === false) { - $this->_debug("Search for '$search_filter' on '$config_root_dn' failed:".ldap_error($this->conn)); - return; - } - - $vlv_searches = new Net_LDAP3_Result($this->conn, $config_root_dn, $search_filter, 'sub', $search_result); - - if ($vlv_searches->count() < 1) { - $this->_debug("Empty result from search for '(objectclass=vlvsearch)' on '$config_root_dn'"); - return; - } - - $index_filter = '(objectclass=vlvindex)'; - - foreach ($vlv_searches->entries(true) as $vlv_search_dn => $vlv_search_attrs) { - // The attributes we are interested in are as follows: - $_vlv_base_dn = $vlv_search_attrs['vlvbase']; - $_vlv_scope = $vlv_search_attrs['vlvscope']; - $_vlv_filter = $vlv_search_attrs['vlvfilter']; - - // Multiple indexes may exist - $index_result = ldap_search($this->conn, $vlv_search_dn, $index_filter, array('*'), 0, 0, 0); - - if ($index_result === false) { - $this->_debug("Search for '$index_filter' on '$vlv_search_dn' failed:".ldap_error($this->conn)); - continue; - } - - $vlv_indexes = new Net_LDAP3_Result($this->conn, $vlv_search_dn, $index_filter, 'sub', $index_result); - $vlv_indexes = $vlv_indexes->entries(true); - - // Reset this one for each VLV search. - $_vlv_sort = array(); - - foreach ($vlv_indexes as $vlv_index_dn => $vlv_index_attrs) { - $_vlv_sort[] = explode(' ', $vlv_index_attrs['vlvsort']); - } - - $this->_vlv_indexes_and_searches[] = array( - $_vlv_base_dn => array( - 'scope' => self::scopeint2str($_vlv_scope), - 'filter' => strtolower($_vlv_filter), - 'sort' => $_vlv_sort, - ), - ); - } - - // cache this - if ($this->cache) { - $this->cache->set('vlvconfig', $this->_vlv_indexes_and_searches); - } - - return $this->_vlv_indexes_and_searches; - } - - private function init_schema() - { - // use PEAR include if autoloading failed - if (!class_exists('Net_LDAP2')) { - require_once('Net/LDAP2.php'); - } - - $port = $this->config_get('port', 389); - $tls = $this->config_get('use_tls', false); - - foreach ((array) $this->config_get('hosts') as $host) { - $this->_debug("C: Connect [$host:$port]"); - - $_ldap_cfg = array( - 'host' => $host, - 'port' => $port, - 'tls' => $tls, - 'version' => 3, - 'binddn' => $this->config_get('service_bind_dn'), - 'bindpw' => $this->config_get('service_bind_pw') - ); - - $_ldap_schema_cache_cfg = array( - 'path' => "/tmp/" . $host . ":" . ($port ? $port : '389') . "-Net_LDAP2_Schema.cache", - 'max_age' => 86400, - ); - - $_ldap = Net_LDAP2::connect($_ldap_cfg); - - if (!is_a($_ldap, 'Net_LDAP2_Error')) { - $this->_debug("S: OK"); - break; - } - - $this->_debug("S: NOT OK"); - $this->_debug($_ldap->getMessage()); - } - - if (is_a($_ldap, 'Net_LDAP2_Error')) { - return null; - } - - $_ldap_schema_cache = new Net_LDAP2_SimpleFileSchemaCache($_ldap_schema_cache_cfg); - - $_ldap->registerSchemaCache($_ldap_schema_cache); - - // TODO: We should learn what LDAP tech. we're running against. - // Perhaps with a scope base objectclass recognize rootdse entry - $schema_root_dn = $this->config_get('schema_root_dn'); - - if (!$schema_root_dn) { - $_schema = $_ldap->schema(); - } - - return $_schema; - } - - private function list_group_member($dn, $members, $recurse = true) - { - $this->_debug("Called list_group_member(" . $dn . ")"); - - $members = (array) $members; - $group_members = array(); - - // remove possible 'count' item - unset($members['count']); - - // Use the member attributes to return an array of member ldap objects - // NOTE that the member attribute is supposed to contain a DN - foreach ($members as $member) { - $member_entry = $this->get_entry($member, array('member', 'uniquemember', 'memberurl', 'objectclass')); - - if (empty($member_entry)) { - continue; - } - - $group_members[$member] = $member; - - if ($recurse) { - // Nested groups - $group_group_members = $this->list_group_members($member, $member_entry); - if ($group_group_members) { - $group_members = array_merge($group_group_members, $group_members); - } - } - } - - return array_filter($group_members); - } - - private function list_group_uniquemember($dn, $uniquemembers, $recurse = true) - { - $this->_debug("Called list_group_uniquemember(" . $dn . ")", $entry); - - $uniquemembers = (array)($uniquemembers); - $group_members = array(); - - // remove possible 'count' item - unset($uniquemembers['count']); - - foreach ($uniquemembers as $member) { - $member_entry = $this->get_entry($member, array('member', 'uniquemember', 'memberurl', 'objectclass')); - - if (empty($member_entry)) { - continue; - } - - $group_members[$member] = $member; - - if ($recurse) { - // Nested groups - $group_group_members = $this->list_group_members($member, $member_entry); - if ($group_group_members) { - $group_members = array_merge($group_group_members, $group_members); - } - } - } - - return array_filter($group_members); - } - - private function list_group_memberurl($dn, $memberurls, $recurse = true) - { - $this->_debug("Called list_group_memberurl(" . $dn . ")"); - - $group_members = array(); - $memberurls = (array) $memberurls; - $attributes = array('member', 'uniquemember', 'memberurl', 'objectclass'); - - // remove possible 'count' item - unset($memberurls['count']); - - foreach ($memberurls as $url) { - $ldap_uri = $this->parse_memberurl($url); - $result = $this->search($ldap_uri[3], $ldap_uri[6], 'sub', $attributes); - - if (!$result) { - continue; - } - - foreach ($result->entries(true) as $entry_dn => $_entry) { - $group_members[$entry_dn] = $entry_dn; - $this->_debug("Found " . $entry_dn); - - if ($recurse) { - // Nested group - $group_group_members = $this->list_group_members($entry_dn, $_entry); - if ($group_group_members) { - $group_members = array_merge($group_members, $group_group_members); - } - } - } - } - - return array_filter($group_members); - } - - /** - * memberUrl attribute parser - * - * @param string $url URL string - * - * @return array URL elements - */ - private function parse_memberurl($url) - { - preg_match('/(.*):\/\/(.*)\/(.*)\?(.*)\?(.*)\?(.*)/', $url, $matches); - return $matches; - } - - private function modify_entry_attributes($subject_dn, $attributes) - { - // Opportunities to set false include failed ldap commands. - $result = true; - - if (is_array($attributes['rename']) && !empty($attributes['rename'])) { - $olddn = $attributes['rename']['dn']; - $newrdn = $attributes['rename']['new_rdn']; - - if (!empty($attributes['rename']['new_parent'])) { - $new_parent = $attributes['rename']['new_parent']; - } - else { - $new_parent = null; - } - - $this->_debug("LDAP: C: Rename $olddn to $newrdn,$new_parent"); - - $result = ldap_rename($this->conn, $olddn, $newrdn, $new_parent, true); - - if ($result) { - $this->_debug("LDAP: S: OK"); - - if ($new_parent) { - $subject_dn = $newrdn . ',' . $new_parent; - } - else { - $old_parent_dn_components = ldap_explode_dn($olddn, 0); - unset($old_parent_dn_components["count"]); - $old_rdn = array_shift($old_parent_dn_components); - $old_parent_dn = implode(",", $old_parent_dn_components); - $subject_dn = $newrdn . ',' . $old_parent_dn; - } - } - else { - $this->_debug("LDAP: S: " . ldap_error($this->conn)); - $this->_warning("LDAP: Failed to rename $olddn to $newrdn,$new_parent"); - return false; - } - } - - if (is_array($attributes['replace']) && !empty($attributes['replace'])) { - $this->_debug("LDAP: C: Mod-Replace $subject_dn: " . json_encode($attributes['replace'])); - - $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']); - - if ($result) { - $this->_debug("LDAP: S: OK"); - } - else { - $this->_debug("LDAP: S: " . ldap_error($this->conn)); - $this->_warning("LDAP: Failed to replace attributes on $subject_dn: " . json_encode($attributes['replace'])); - return false; - } - } - - if (is_array($attributes['del']) && !empty($attributes['del'])) { - $this->_debug("LDAP: C: Mod-Delete $subject_dn: " . json_encode($attributes['del'])); - - $result = ldap_mod_del($this->conn, $subject_dn, $attributes['del']); - - if ($result) { - $this->_debug("LDAP: S: OK"); - } - else { - $this->_debug("LDAP: S: " . ldap_error($this->conn)); - $this->_warning("LDAP: Failed to delete attributes on $subject_dn: " . json_encode($attributes['del'])); - return false; - } - } - - if (is_array($attributes['add']) && !empty($attributes['add'])) { - $this->_debug("LDAP: C: Mod-Add $subject_dn: " . json_encode($attributes['add'])); - - $result = ldap_mod_add($this->conn, $subject_dn, $attributes['add']); - - if ($result) { - $this->_debug("LDAP: S: OK"); - } - else { - $this->_debug("LDAP: S: " . ldap_error($this->conn)); - $this->_warning("LDAP: Failed to add attributes on $subject_dn: " . json_encode($attributes['add'])); - return false; - } - } - - return true; - } - - private function parse_attribute_level_rights($attribute_value) - { - $attribute_value = str_replace(", ", ",", $attribute_value); - $attribute_values = explode(",", $attribute_value); - $attribute_value = array(); - - foreach ($attribute_values as $access_right) { - $access_right_components = explode(":", $access_right); - $access_attribute = strtolower(array_shift($access_right_components)); - $access_value = array_shift($access_right_components); - - $attribute_value[$access_attribute] = array(); - - for ($i = 0; $i < strlen($access_value); $i++) { - $method = $this->attribute_level_rights_map[substr($access_value, $i, 1)]; - - if (!in_array($method, $attribute_value[$access_attribute])) { - $attribute_value[$access_attribute][] = $method; - } - } - } - - return $attribute_value; - } - - private function parse_entry_level_rights($attribute_value) - { - $_attribute_value = array(); - - for ($i = 0; $i < strlen($attribute_value); $i++) { - $method = $this->entry_level_rights_map[substr($attribute_value, $i, 1)]; - - if (!in_array($method, $_attribute_value)) { - $_attribute_value[] = $method; - } - } - - return $_attribute_value; - } - - private function supported_controls() - { - if (!empty($this->supported_controls)) { - return $this->supported_controls; - } - - $this->_info("Obtaining supported controls"); - - if ($result = $this->search('', '(objectclass=*)', 'base', array('supportedcontrol'))) { - $result = $result->entries(true); - $control = $result['']['supportedcontrol']; - } - else { - $control = array(); - } - - $this->_info("Obtained " . count($control) . " supported controls"); - $this->supported_controls = $control; - - return $control; - } - - protected function _alert() - { - $this->__log(LOG_ALERT, func_get_args()); - } - - protected function _critical() - { - $this->__log(LOG_CRIT, func_get_args()); - } - - protected function _debug() - { - $this->__log(LOG_DEBUG, func_get_args()); - } - - protected function _emergency() - { - $this->__log(LOG_EMERG, func_get_args()); - } - - protected function _error() - { - $this->__log(LOG_ERR, func_get_args()); - } - - protected function _info() - { - $this->__log(LOG_INFO, func_get_args()); - } - - protected function _notice() - { - $this->__log(LOG_NOTICE, func_get_args()); - } - - protected function _warning() - { - $this->__log(LOG_WARNING, func_get_args()); - } - - /** - * Log a message. - */ - private function __log($level, $args) - { - $msg = array(); - - foreach ($args as $arg) { - $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; - } - - if (!empty($this->_log_hook)) { - call_user_func_array($this->_log_hook, array($level, $msg)); - return; - } - - if ($this->debug_level > 0) { - syslog($level, implode("\n", $msg)); - } - } - - /** - * Add BER sequence with correct length and the given identifier - */ - private static function _ber_addseq($str, $identifier) - { - $len = dechex(strlen($str)/2); - if (strlen($len) % 2 != 0) { - $len = '0'.$len; - } - - return $identifier . $len . $str; - } - - /** - * Returns BER encoded integer value in hex format - */ - private static function _ber_encode_int($offset) - { - $val = dechex($offset); - $prefix = ''; - - // check if bit 8 of high byte is 1 - if (preg_match('/^[89abcdef]/', $val)) { - $prefix = '00'; - } - - if (strlen($val)%2 != 0) { - $prefix .= '0'; - } - - return $prefix . $val; - } - - /** - * Quotes attribute value string - * - * @param string $str Attribute value - * @param bool $dn True if the attribute is a DN - * - * @return string Quoted string - */ - public static function quote_string($str, $is_dn = false) - { - // take firt entry if array given - if (is_array($str)) { - $str = reset($str); - } - - if ($is_dn) { - $replace = array( - ',' => '\2c', - '=' => '\3d', - '+' => '\2b', - '<' => '\3c', - '>' => '\3e', - ';' => '\3b', - "\\"=> '\5c', - '"' => '\22', - '#' => '\23' - ); - } - else { - $replace = array( - '*' => '\2a', - '(' => '\28', - ')' => '\29', - "\\" => '\5c', - '/' => '\2f' - ); - } - - return strtr($str, $replace); - } - - /** - * create ber encoding for sort control - * - * @param array List of cols to sort by - * @return string BER encoded option value - */ - private static function _sort_ber_encode($sortcols) - { - $str = ''; - foreach (array_reverse((array)$sortcols) as $col) { - $ber_val = self::_string2hex($col); - - // 30 = ber sequence with a length of octet value - // 04 = octet string with a length of the ascii value - $oct = self::_ber_addseq($ber_val, '04'); - $str = self::_ber_addseq($oct, '30') . $str; - } - - // now tack on sequence identifier and length - $str = self::_ber_addseq($str, '30'); - - return pack('H'.strlen($str), $str); - } - - /** - * Returns ascii string encoded in hex - */ - private static function _string2hex($str) - { - $hex = ''; - for ($i=0; $i < strlen($str); $i++) - $hex .= dechex(ord($str[$i])); - - return $hex; - } - - /** - * Generate BER encoded string for Virtual List View option - * - * @param integer List offset (first record) - * @param integer Records per page - * @return string BER encoded option value - */ - private static function _vlv_ber_encode($offset, $rpp, $search = '') - { - // This string is ber-encoded, php will prefix this value with: - // 04 (octet string) and 10 (length of 16 bytes) - // the code behind this string is broken down as follows: - // 30 = ber sequence with a length of 0e (14) bytes following - // 02 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0) - // 02 = type integer (in two's complement form) with 2 bytes following (afterCount): 01 18 (ie 25-1=24) - // a0 = type context-specific/constructed with a length of 06 (6) bytes following - // 02 = type integer with 2 bytes following (offset): 01 01 (ie 1) - // 02 = type integer with 2 bytes following (contentCount): 01 00 - - // whith a search string present: - // 81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here) - // 81 indicates a user string is present where as a a0 indicates just a offset search - // 81 = type context-specific/constructed with a length of 06 (6) bytes following - - // the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the - // encoding of integer values (note: these values are in - // two-complement form so since offset will never be negative bit 8 of the - // leftmost octet should never by set to 1): - // 8.3.2: If the contents octets of an integer value encoding consist - // of more than one octet, then the bits of the first octet (rightmost) and bit 8 - // of the second (to the left of first octet) octet: - // a) shall not all be ones; and - // b) shall not all be zero - - if ($search) { - $search = preg_replace('/[^-[:alpha:] ,.()0-9]+/', '', $search); - $ber_val = self::_string2hex($search); - $str = self::_ber_addseq($ber_val, '81'); - } - else { - // construct the string from right to left - $str = "020100"; # contentCount - - // returns encoded integer value in hex format - $ber_val = self::_ber_encode_int($offset); - - // calculate octet length of $ber_val - $str = self::_ber_addseq($ber_val, '02') . $str; - - // now compute length over $str - $str = self::_ber_addseq($str, 'a0'); - } - - // now tack on records per page - $str = "020100" . self::_ber_addseq(self::_ber_encode_int($rpp-1), '02') . $str; - - // now tack on sequence identifier and length - $str = self::_ber_addseq($str, '30'); - - return pack('H'.strlen($str), $str); - } - - private function _fuzzy_search_prefix() - { - switch ($this->config_get("fuzzy_search", 2)) { - case 2: - return "*"; - break; - case 1: - case 0: - default: - return ""; - break; - } - } - - private function _fuzzy_search_suffix() - { - switch ($this->config_get("fuzzy_search", 2)) { - case 2: - return "*"; - break; - case 1: - return "*"; - case 0: - default: - return ""; - break; - } - } - - /** - * Return the search string value to be used in VLV controls - */ - private function _vlv_search($sort, $search) - { - if (!empty($this->additional_filter)) { - $this->_debug("Not setting a VLV search filter because we already have a filter"); - return; - } - - if (empty($search)) { - return; - } - - $search_suffix = $this->_fuzzy_search_suffix(); - - foreach ($search as $attr => $value) { - if (!in_array(strtolower($attr), $sort)) { - $this->_debug("Cannot use VLV search using attribute not indexed: $attr (not in " . var_export($sort, true) . ")"); - return; - } - else { - return $value . $search_suffix; - } - } - } - - /** - * Set server controls for Virtual List View (paginated listing) - */ - private function _vlv_set_controls($sort, $list_page, $page_size, $search = null) - { - $sort_ctrl = array( - 'oid' => "1.2.840.113556.1.4.473", - 'value' => self::_sort_ber_encode($sort) - ); - - if (!empty($search)) { - $this->_debug("_vlv_set_controls to include search: " . var_export($search, true)); - } - - $vlv_ctrl = array( - 'oid' => "2.16.840.1.113730.3.4.9", - 'value' => self::_vlv_ber_encode( - $offset = ($list_page-1) * $page_size + 1, - $page_size, - $search - ), - 'iscritical' => true - ); - - $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) - . " (" . implode(',', (array) $sort) . ");" - . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset/$page_size)"); - - if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, array($sort_ctrl, $vlv_ctrl))) { - $this->_debug("S: ".ldap_error($this->conn)); - $this->set_error(self::ERROR_SEARCH, 'vlvnotsupported'); - - return false; - } - - return true; - } - -} diff --git a/program/lib/Net/LDAP3/Result.php b/program/lib/Net/LDAP3/Result.php deleted file mode 100644 index 0759df087..000000000 --- a/program/lib/Net/LDAP3/Result.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php - -/* - +-----------------------------------------------------------------------+ - | Net/LDAP3/Result.php | - | | - | Based on code created by the Roundcube Webmail team. | - | | - | Copyright (C) 2006-2014, The Roundcube Dev Team | - | Copyright (C) 2012-2014, Kolab Systems AG | - | | - | Licensed under the GNU General Public License version 3 or | - | any later version with exceptions for plugins. | - | See the README file for a full license statement. | - | | - | PURPOSE: | - | Provide advanced functionality for accessing LDAP directories | - | | - +-----------------------------------------------------------------------+ - | Authors: Thomas Bruederli <roundcube@gmail.com> | - | Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | - +-----------------------------------------------------------------------+ -*/ - -/** - * Model class representing an LDAP search result - * - * @package LDAP - */ -class Net_LDAP3_Result implements Iterator -{ - protected $conn; - protected $base_dn; - protected $filter; - protected $scope; - - private $count; - private $current; - private $iteratorkey = 0; - - /** - * Default constructor - * - * @param resource $conn LDAP link identifier - * @param string $base_dn Base DN used to get this result - * @param string $filter Filter query used to get this result - * @param string $scope Scope of the result - * @param resource $result LDAP result entry identifier - */ - function __construct($conn, $base_dn, $filter, $scope, $result) - { - $this->conn = $conn; - $this->base_dn = $base_dn; - $this->filter = $filter; - $this->scope = $scope; - $this->result = $result; - } - - public function get($property, $default = null) - { - if (isset($this->$property)) { - return $this->$property; - } else { - return $default; - } - } - - public function set($property, $value) - { - $this->$property = $value; - } - - /** - * Wrapper for ldap_sort() - */ - public function sort($attr) - { - return ldap_sort($this->conn, $this->result, $attr); - } - - /** - * Get entries count - */ - public function count() - { - if (!isset($this->count)) { - $this->count = ldap_count_entries($this->conn, $this->result); - } - - return $this->count; - } - - /** - * Wrapper for ldap_get_entries() - * - * @param bool $normalize Optionally normalize the entries to a list of hash arrays - * - * @return array List of LDAP entries - */ - public function entries($normalize = false) - { - $entries = ldap_get_entries($this->conn, $this->result); - - if ($normalize) { - return Net_LDAP3::normalize_result($entries); - } - - return $entries; - } - - /** - * Wrapper for ldap_get_dn() using the current entry pointer - */ - public function get_dn() - { - return $this->current ? ldap_get_dn($this->conn, $this->current) : null; - } - - - /*** Implement PHP 5 Iterator interface to make foreach work ***/ - - function current() - { - $attrib = ldap_get_attributes($this->conn, $this->current); - $attrib['dn'] = ldap_get_dn($this->conn, $this->current); - - return $attrib; - } - - function key() - { - return $this->iteratorkey; - } - - function rewind() - { - $this->iteratorkey = 0; - $this->current = ldap_first_entry($this->conn, $this->result); - } - - function next() - { - $this->iteratorkey++; - $this->current = ldap_next_entry($this->conn, $this->current); - } - - function valid() - { - return (bool)$this->current; - } - -} diff --git a/program/lib/Net/SMTP.php b/program/lib/Net/SMTP.php deleted file mode 100644 index 2c1ef5c55..000000000 --- a/program/lib/Net/SMTP.php +++ /dev/null @@ -1,1338 +0,0 @@ -<?php -/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */ -// +----------------------------------------------------------------------+ -// | PHP Version 4 | -// +----------------------------------------------------------------------+ -// | Copyright (c) 1997-2003 The PHP Group | -// +----------------------------------------------------------------------+ -// | This source file is subject to version 2.02 of the PHP license, | -// | that is bundled with this package in the file LICENSE, and is | -// | available at through the world-wide-web at | -// | http://www.php.net/license/2_02.txt. | -// | If you did not receive a copy of the PHP license and are unable to | -// | obtain it through the world-wide-web, please send a note to | -// | license@php.net so we can mail you a copy immediately. | -// +----------------------------------------------------------------------+ -// | Authors: Chuck Hagenbuch <chuck@horde.org> | -// | Jon Parise <jon@php.net> | -// | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> | -// +----------------------------------------------------------------------+ - -require_once 'PEAR.php'; -require_once 'Net/Socket.php'; - -/** - * Provides an implementation of the SMTP protocol using PEAR's - * Net_Socket:: class. - * - * @package Net_SMTP - * @author Chuck Hagenbuch <chuck@horde.org> - * @author Jon Parise <jon@php.net> - * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> - * - * @example basic.php A basic implementation of the Net_SMTP package. - */ -class Net_SMTP -{ - /** - * The server to connect to. - * @var string - * @access public - */ - var $host = 'localhost'; - - /** - * The port to connect to. - * @var int - * @access public - */ - var $port = 25; - - /** - * The value to give when sending EHLO or HELO. - * @var string - * @access public - */ - var $localhost = 'localhost'; - - /** - * List of supported authentication methods, in preferential order. - * @var array - * @access public - */ - var $auth_methods = array(); - - /** - * Use SMTP command pipelining (specified in RFC 2920) if the SMTP - * server supports it. - * - * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(), - * somlFrom() and samlFrom() do not wait for a response from the - * SMTP server but return immediately. - * - * @var bool - * @access public - */ - var $pipelining = false; - - /** - * Number of pipelined commands. - * @var int - * @access private - */ - var $_pipelined_commands = 0; - - /** - * Should debugging output be enabled? - * @var boolean - * @access private - */ - var $_debug = false; - - /** - * Debug output handler. - * @var callback - * @access private - */ - var $_debug_handler = null; - - /** - * The socket resource being used to connect to the SMTP server. - * @var resource - * @access private - */ - var $_socket = null; - - /** - * Array of socket options that will be passed to Net_Socket::connect(). - * @see stream_context_create() - * @var array - * @access private - */ - var $_socket_options = null; - - /** - * The socket I/O timeout value in seconds. - * @var int - * @access private - */ - var $_timeout = 0; - - /** - * The most recent server response code. - * @var int - * @access private - */ - var $_code = -1; - - /** - * The most recent server response arguments. - * @var array - * @access private - */ - var $_arguments = array(); - - /** - * Stores the SMTP server's greeting string. - * @var string - * @access private - */ - var $_greeting = null; - - /** - * Stores detected features of the SMTP server. - * @var array - * @access private - */ - var $_esmtp = array(); - - /** - * Instantiates a new Net_SMTP object, overriding any defaults - * with parameters that are passed in. - * - * If you have SSL support in PHP, you can connect to a server - * over SSL using an 'ssl://' prefix: - * - * // 465 is a common smtps port. - * $smtp = new Net_SMTP('ssl://mail.host.com', 465); - * $smtp->connect(); - * - * @param string $host The server to connect to. - * @param integer $port The port to connect to. - * @param string $localhost The value to give when sending EHLO or HELO. - * @param boolean $pipeling Use SMTP command pipelining - * @param integer $timeout Socket I/O timeout in seconds. - * @param array $socket_options Socket stream_context_create() options. - * - * @access public - * @since 1.0 - */ - function Net_SMTP($host = null, $port = null, $localhost = null, - $pipelining = false, $timeout = 0, $socket_options = null) - { - if (isset($host)) { - $this->host = $host; - } - if (isset($port)) { - $this->port = $port; - } - if (isset($localhost)) { - $this->localhost = $localhost; - } - $this->pipelining = $pipelining; - - $this->_socket = new Net_Socket(); - $this->_socket_options = $socket_options; - $this->_timeout = $timeout; - - /* Include the Auth_SASL package. If the package is available, we - * enable the authentication methods that depend upon it. */ - if (@include_once 'Auth/SASL.php') { - $this->setAuthMethod('CRAM-MD5', array($this, '_authCram_MD5')); - $this->setAuthMethod('DIGEST-MD5', array($this, '_authDigest_MD5')); - } - - /* These standard authentication methods are always available. */ - $this->setAuthMethod('LOGIN', array($this, '_authLogin'), false); - $this->setAuthMethod('PLAIN', array($this, '_authPlain'), false); - } - - /** - * Set the socket I/O timeout value in seconds plus microseconds. - * - * @param integer $seconds Timeout value in seconds. - * @param integer $microseconds Additional value in microseconds. - * - * @access public - * @since 1.5.0 - */ - function setTimeout($seconds, $microseconds = 0) { - return $this->_socket->setTimeout($seconds, $microseconds); - } - - /** - * Set the value of the debugging flag. - * - * @param boolean $debug New value for the debugging flag. - * - * @access public - * @since 1.1.0 - */ - function setDebug($debug, $handler = null) - { - $this->_debug = $debug; - $this->_debug_handler = $handler; - } - - /** - * Write the given debug text to the current debug output handler. - * - * @param string $message Debug mesage text. - * - * @access private - * @since 1.3.3 - */ - function _debug($message) - { - if ($this->_debug) { - if ($this->_debug_handler) { - call_user_func_array($this->_debug_handler, - array(&$this, $message)); - } else { - echo "DEBUG: $message\n"; - } - } - } - - /** - * Send the given string of data to the server. - * - * @param string $data The string of data to send. - * - * @return mixed The number of bytes that were actually written, - * or a PEAR_Error object on failure. - * - * @access private - * @since 1.1.0 - */ - function _send($data) - { - $this->_debug("Send: $data"); - - $result = $this->_socket->write($data); - if (!$result || PEAR::isError($result)) { - $msg = ($result) ? $result->getMessage() : "unknown error"; - return PEAR::raiseError("Failed to write to socket: $msg", - null, PEAR_ERROR_RETURN); - } - - return $result; - } - - /** - * Send a command to the server with an optional string of - * arguments. A carriage return / linefeed (CRLF) sequence will - * be appended to each command string before it is sent to the - * SMTP server - an error will be thrown if the command string - * already contains any newline characters. Use _send() for - * commands that must contain newlines. - * - * @param string $command The SMTP command to send to the server. - * @param string $args A string of optional arguments to append - * to the command. - * - * @return mixed The result of the _send() call. - * - * @access private - * @since 1.1.0 - */ - function _put($command, $args = '') - { - if (!empty($args)) { - $command .= ' ' . $args; - } - - if (strcspn($command, "\r\n") !== strlen($command)) { - return PEAR::raiseError('Commands cannot contain newlines', - null, PEAR_ERROR_RETURN); - } - - return $this->_send($command . "\r\n"); - } - - /** - * Read a reply from the SMTP server. The reply consists of a response - * code and a response message. - * - * @param mixed $valid The set of valid response codes. These - * may be specified as an array of integer - * values or as a single integer value. - * @param bool $later Do not parse the response now, but wait - * until the last command in the pipelined - * command group - * - * @return mixed True if the server returned a valid response code or - * a PEAR_Error object is an error condition is reached. - * - * @access private - * @since 1.1.0 - * - * @see getResponse - */ - function _parseResponse($valid, $later = false) - { - $this->_code = -1; - $this->_arguments = array(); - - if ($later) { - $this->_pipelined_commands++; - return true; - } - - for ($i = 0; $i <= $this->_pipelined_commands; $i++) { - while ($line = $this->_socket->readLine()) { - $this->_debug("Recv: $line"); - - /* If we receive an empty line, the connection was closed. */ - if (empty($line)) { - $this->disconnect(); - return PEAR::raiseError('Connection was closed', - null, PEAR_ERROR_RETURN); - } - - /* Read the code and store the rest in the arguments array. */ - $code = substr($line, 0, 3); - $this->_arguments[] = trim(substr($line, 4)); - - /* Check the syntax of the response code. */ - if (is_numeric($code)) { - $this->_code = (int)$code; - } else { - $this->_code = -1; - break; - } - - /* If this is not a multiline response, we're done. */ - if (substr($line, 3, 1) != '-') { - break; - } - } - } - - $this->_pipelined_commands = 0; - - /* Compare the server's response code with the valid code/codes. */ - if (is_int($valid) && ($this->_code === $valid)) { - return true; - } elseif (is_array($valid) && in_array($this->_code, $valid, true)) { - return true; - } - - return PEAR::raiseError('Invalid response code received from server', - $this->_code, PEAR_ERROR_RETURN); - } - - /** - * Issue an SMTP command and verify its response. - * - * @param string $command The SMTP command string or data. - * @param mixed $valid The set of valid response codes. These - * may be specified as an array of integer - * values or as a single integer value. - * - * @return mixed True on success or a PEAR_Error object on failure. - * - * @access public - * @since 1.6.0 - */ - function command($command, $valid) - { - if (PEAR::isError($error = $this->_put($command))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse($valid))) { - return $error; - } - - return true; - } - - /** - * Return a 2-tuple containing the last response from the SMTP server. - * - * @return array A two-element array: the first element contains the - * response code as an integer and the second element - * contains the response's arguments as a string. - * - * @access public - * @since 1.1.0 - */ - function getResponse() - { - return array($this->_code, join("\n", $this->_arguments)); - } - - /** - * Return the SMTP server's greeting string. - * - * @return string A string containing the greeting string, or null if a - * greeting has not been received. - * - * @access public - * @since 1.3.3 - */ - function getGreeting() - { - return $this->_greeting; - } - - /** - * Attempt to connect to the SMTP server. - * - * @param int $timeout The timeout value (in seconds) for the - * socket connection attempt. - * @param bool $persistent Should a persistent socket connection - * be used? - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function connect($timeout = null, $persistent = false) - { - $this->_greeting = null; - $result = $this->_socket->connect($this->host, $this->port, - $persistent, $timeout, - $this->_socket_options); - if (PEAR::isError($result)) { - return PEAR::raiseError('Failed to connect socket: ' . - $result->getMessage()); - } - - /* - * Now that we're connected, reset the socket's timeout value for - * future I/O operations. This allows us to have different socket - * timeout values for the initial connection (our $timeout parameter) - * and all other socket operations. - */ - if ($this->_timeout > 0) { - if (PEAR::isError($error = $this->setTimeout($this->_timeout))) { - return $error; - } - } - - if (PEAR::isError($error = $this->_parseResponse(220))) { - return $error; - } - - /* Extract and store a copy of the server's greeting string. */ - list(, $this->_greeting) = $this->getResponse(); - - if (PEAR::isError($error = $this->_negotiate())) { - return $error; - } - - return true; - } - - /** - * Attempt to disconnect from the SMTP server. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function disconnect() - { - if (PEAR::isError($error = $this->_put('QUIT'))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(221))) { - return $error; - } - if (PEAR::isError($error = $this->_socket->disconnect())) { - return PEAR::raiseError('Failed to disconnect socket: ' . - $error->getMessage()); - } - - return true; - } - - /** - * Attempt to send the EHLO command and obtain a list of ESMTP - * extensions available, and failing that just send HELO. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * - * @access private - * @since 1.1.0 - */ - function _negotiate() - { - if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) { - return $error; - } - - if (PEAR::isError($this->_parseResponse(250))) { - /* If we receive a 503 response, we're already authenticated. */ - if ($this->_code === 503) { - return true; - } - - /* If the EHLO failed, try the simpler HELO command. */ - if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) { - return $error; - } - if (PEAR::isError($this->_parseResponse(250))) { - return PEAR::raiseError('HELO was not accepted: ', $this->_code, - PEAR_ERROR_RETURN); - } - - return true; - } - - foreach ($this->_arguments as $argument) { - $verb = strtok($argument, ' '); - $arguments = substr($argument, strlen($verb) + 1, - strlen($argument) - strlen($verb) - 1); - $this->_esmtp[$verb] = $arguments; - } - - if (!isset($this->_esmtp['PIPELINING'])) { - $this->pipelining = false; - } - - return true; - } - - /** - * Returns the name of the best authentication method that the server - * has advertised. - * - * @return mixed Returns a string containing the name of the best - * supported authentication method or a PEAR_Error object - * if a failure condition is encountered. - * @access private - * @since 1.1.0 - */ - function _getBestAuthMethod() - { - $available_methods = explode(' ', $this->_esmtp['AUTH']); - - foreach ($this->auth_methods as $method => $callback) { - if (in_array($method, $available_methods)) { - return $method; - } - } - - return PEAR::raiseError('No supported authentication methods', - null, PEAR_ERROR_RETURN); - } - - /** - * Attempt to do SMTP authentication. - * - * @param string The userid to authenticate as. - * @param string The password to authenticate with. - * @param string The requested authentication method. If none is - * specified, the best supported method will be used. - * @param bool Flag indicating whether or not TLS should be attempted. - * @param string An optional authorization identifier. If specified, this - * identifier will be used as the authorization proxy. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function auth($uid, $pwd , $method = '', $tls = true, $authz = '') - { - /* We can only attempt a TLS connection if one has been requested, - * we're running PHP 5.1.0 or later, have access to the OpenSSL - * extension, are connected to an SMTP server which supports the - * STARTTLS extension, and aren't already connected over a secure - * (SSL) socket connection. */ - if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') && - extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) && - strncasecmp($this->host, 'ssl://', 6) !== 0) { - /* Start the TLS connection attempt. */ - if (PEAR::isError($result = $this->_put('STARTTLS'))) { - return $result; - } - if (PEAR::isError($result = $this->_parseResponse(220))) { - return $result; - } - if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) { - return $result; - } elseif ($result !== true) { - return PEAR::raiseError('STARTTLS failed'); - } - - /* Send EHLO again to recieve the AUTH string from the - * SMTP server. */ - $this->_negotiate(); - } - - if (empty($this->_esmtp['AUTH'])) { - return PEAR::raiseError('SMTP server does not support authentication'); - } - - /* If no method has been specified, get the name of the best - * supported method advertised by the SMTP server. */ - if (empty($method)) { - if (PEAR::isError($method = $this->_getBestAuthMethod())) { - /* Return the PEAR_Error object from _getBestAuthMethod(). */ - return $method; - } - } else { - $method = strtoupper($method); - if (!array_key_exists($method, $this->auth_methods)) { - return PEAR::raiseError("$method is not a supported authentication method"); - } - } - - if (!isset($this->auth_methods[$method])) { - return PEAR::raiseError("$method is not a supported authentication method"); - } - - if (!is_callable($this->auth_methods[$method], false)) { - return PEAR::raiseError("$method authentication method cannot be called"); - } - - if (is_array($this->auth_methods[$method])) { - list($object, $method) = $this->auth_methods[$method]; - $result = $object->{$method}($uid, $pwd, $authz, $this); - } else { - $func = $this->auth_methods[$method]; - $result = $func($uid, $pwd, $authz, $this); - } - - /* If an error was encountered, return the PEAR_Error object. */ - if (PEAR::isError($result)) { - return $result; - } - - return true; - } - - /** - * Add a new authentication method. - * - * @param string The authentication method name (e.g. 'PLAIN') - * @param mixed The authentication callback (given as the name of a - * function or as an (object, method name) array). - * @param bool Should the new method be prepended to the list of - * available methods? This is the default behavior, - * giving the new method the highest priority. - * - * @return mixed True on success or a PEAR_Error object on failure. - * - * @access public - * @since 1.6.0 - */ - function setAuthMethod($name, $callback, $prepend = true) - { - if (!is_string($name)) { - return PEAR::raiseError('Method name is not a string'); - } - - if (!is_string($callback) && !is_array($callback)) { - return PEAR::raiseError('Method callback must be string or array'); - } - - if (is_array($callback)) { - if (!is_object($callback[0]) || !is_string($callback[1])) - return PEAR::raiseError('Bad mMethod callback array'); - } - - if ($prepend) { - $this->auth_methods = array_merge(array($name => $callback), - $this->auth_methods); - } else { - $this->auth_methods[$name] = $callback; - } - - return true; - } - - /** - * Authenticates the user using the DIGEST-MD5 method. - * - * @param string The userid to authenticate as. - * @param string The password to authenticate with. - * @param string The optional authorization proxy identifier. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access private - * @since 1.1.0 - */ - function _authDigest_MD5($uid, $pwd, $authz = '') - { - if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $challenge = base64_decode($this->_arguments[0]); - $digest = &Auth_SASL::factory('digest-md5'); - $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, - $this->host, "smtp", - $authz)); - - if (PEAR::isError($error = $this->_put($auth_str))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - return $error; - } - - /* We don't use the protocol's third step because SMTP doesn't - * allow subsequent authentication, so we just silently ignore - * it. */ - if (PEAR::isError($error = $this->_put(''))) { - return $error; - } - /* 235: Authentication successful */ - if (PEAR::isError($error = $this->_parseResponse(235))) { - return $error; - } - } - - /** - * Authenticates the user using the CRAM-MD5 method. - * - * @param string The userid to authenticate as. - * @param string The password to authenticate with. - * @param string The optional authorization proxy identifier. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access private - * @since 1.1.0 - */ - function _authCRAM_MD5($uid, $pwd, $authz = '') - { - if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $challenge = base64_decode($this->_arguments[0]); - $cram = &Auth_SASL::factory('cram-md5'); - $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); - - if (PEAR::isError($error = $this->_put($auth_str))) { - return $error; - } - - /* 235: Authentication successful */ - if (PEAR::isError($error = $this->_parseResponse(235))) { - return $error; - } - } - - /** - * Authenticates the user using the LOGIN method. - * - * @param string The userid to authenticate as. - * @param string The password to authenticate with. - * @param string The optional authorization proxy identifier. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access private - * @since 1.1.0 - */ - function _authLogin($uid, $pwd, $authz = '') - { - if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - if (PEAR::isError($error = $this->_put(base64_encode($uid)))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - return $error; - } - - if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) { - return $error; - } - - /* 235: Authentication successful */ - if (PEAR::isError($error = $this->_parseResponse(235))) { - return $error; - } - - return true; - } - - /** - * Authenticates the user using the PLAIN method. - * - * @param string The userid to authenticate as. - * @param string The password to authenticate with. - * @param string The optional authorization proxy identifier. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access private - * @since 1.1.0 - */ - function _authPlain($uid, $pwd, $authz = '') - { - if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) { - return $error; - } - /* 334: Continue authentication request */ - if (PEAR::isError($error = $this->_parseResponse(334))) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd); - - if (PEAR::isError($error = $this->_put($auth_str))) { - return $error; - } - - /* 235: Authentication successful */ - if (PEAR::isError($error = $this->_parseResponse(235))) { - return $error; - } - - return true; - } - - /** - * Send the HELO command. - * - * @param string The domain name to say we are. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function helo($domain) - { - if (PEAR::isError($error = $this->_put('HELO', $domain))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250))) { - return $error; - } - - return true; - } - - /** - * Return the list of SMTP service extensions advertised by the server. - * - * @return array The list of SMTP service extensions. - * @access public - * @since 1.3 - */ - function getServiceExtensions() - { - return $this->_esmtp; - } - - /** - * Send the MAIL FROM: command. - * - * @param string $sender The sender (reverse path) to set. - * @param string $params String containing additional MAIL parameters, - * such as the NOTIFY flags defined by RFC 1891 - * or the VERP protocol. - * - * If $params is an array, only the 'verp' option - * is supported. If 'verp' is true, the XVERP - * parameter is appended to the MAIL command. If - * the 'verp' value is a string, the full - * XVERP=value parameter is appended. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function mailFrom($sender, $params = null) - { - $args = "FROM:<$sender>"; - - /* Support the deprecated array form of $params. */ - if (is_array($params) && isset($params['verp'])) { - /* XVERP */ - if ($params['verp'] === true) { - $args .= ' XVERP'; - - /* XVERP=something */ - } elseif (trim($params['verp'])) { - $args .= ' XVERP=' . $params['verp']; - } - } elseif (is_string($params) && !empty($params)) { - $args .= ' ' . $params; - } - - if (PEAR::isError($error = $this->_put('MAIL', $args))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Send the RCPT TO: command. - * - * @param string $recipient The recipient (forward path) to add. - * @param string $params String containing additional RCPT parameters, - * such as the NOTIFY flags defined by RFC 1891. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * - * @access public - * @since 1.0 - */ - function rcptTo($recipient, $params = null) - { - $args = "TO:<$recipient>"; - if (is_string($params)) { - $args .= ' ' . $params; - } - - if (PEAR::isError($error = $this->_put('RCPT', $args))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Quote the data so that it meets SMTP standards. - * - * This is provided as a separate public function to facilitate - * easier overloading for the cases where it is desirable to - * customize the quoting behavior. - * - * @param string $data The message text to quote. The string must be passed - * by reference, and the text will be modified in place. - * - * @access public - * @since 1.2 - */ - function quotedata(&$data) - { - /* Because a single leading period (.) signifies an end to the - * data, legitimate leading periods need to be "doubled" ('..'). */ - $data = preg_replace('/^\./m', '..', $data); - - /* Change Unix (\n) and Mac (\r) linefeeds into CRLF's (\r\n). */ - $data = preg_replace('/(?:\r\n|\n|\r(?!\n))/', "\r\n", $data); - } - - /** - * Send the DATA command. - * - * @param mixed $data The message data, either as a string or an open - * file resource. - * @param string $headers The message headers. If $headers is provided, - * $data is assumed to contain only body data. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function data($data, $headers = null) - { - /* Verify that $data is a supported type. */ - if (!is_string($data) && !is_resource($data)) { - return PEAR::raiseError('Expected a string or file resource'); - } - - /* Start by considering the size of the optional headers string. We - * also account for the addition 4 character "\r\n\r\n" separator - * sequence. */ - $size = (is_null($headers)) ? 0 : strlen($headers) + 4; - - if (is_resource($data)) { - $stat = fstat($data); - if ($stat === false) { - return PEAR::raiseError('Failed to get file size'); - } - $size += $stat['size']; - } else { - $size += strlen($data); - } - - /* RFC 1870, section 3, subsection 3 states "a value of zero indicates - * that no fixed maximum message size is in force". Furthermore, it - * says that if "the parameter is omitted no information is conveyed - * about the server's fixed maximum message size". */ - $limit = (isset($this->_esmtp['SIZE'])) ? $this->_esmtp['SIZE'] : 0; - if ($limit > 0 && $size >= $limit) { - $this->disconnect(); - return PEAR::raiseError('Message size exceeds server limit'); - } - - /* Initiate the DATA command. */ - if (PEAR::isError($error = $this->_put('DATA'))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(354))) { - return $error; - } - - /* If we have a separate headers string, send it first. */ - if (!is_null($headers)) { - $this->quotedata($headers); - if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) { - return $result; - } - } - - /* Now we can send the message body data. */ - if (is_resource($data)) { - /* Stream the contents of the file resource out over our socket - * connection, line by line. Each line must be run through the - * quoting routine. */ - while (strlen($line = fread($data, 8192)) > 0) { - /* If the last character is an newline, we need to grab the - * next character to check to see if it is a period. */ - while (!feof($data)) { - $char = fread($data, 1); - $line .= $char; - if ($char != "\n") { - break; - } - } - $this->quotedata($line); - if (PEAR::isError($result = $this->_send($line))) { - return $result; - } - } - } else { - /* - * Break up the data by sending one chunk (up to 512k) at a time. - * This approach reduces our peak memory usage. - */ - for ($offset = 0; $offset < $size;) { - $end = $offset + 512000; - - /* - * Ensure we don't read beyond our data size or span multiple - * lines. quotedata() can't properly handle character data - * that's split across two line break boundaries. - */ - if ($end >= $size) { - $end = $size; - } else { - for (; $end < $size; $end++) { - if ($data[$end] != "\n") { - break; - } - } - } - - /* Extract our chunk and run it through the quoting routine. */ - $chunk = substr($data, $offset, $end - $offset); - $this->quotedata($chunk); - - /* If we run into a problem along the way, abort. */ - if (PEAR::isError($result = $this->_send($chunk))) { - return $result; - } - - /* Advance the offset to the end of this chunk. */ - $offset = $end; - } - } - - /* Finally, send the DATA terminator sequence. */ - if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) { - return $result; - } - - /* Verify that the data was successfully received by the server. */ - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Send the SEND FROM: command. - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.2.6 - */ - function sendFrom($path) - { - if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Backwards-compatibility wrapper for sendFrom(). - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * - * @access public - * @since 1.0 - * @deprecated 1.2.6 - */ - function send_from($path) - { - return sendFrom($path); - } - - /** - * Send the SOML FROM: command. - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.2.6 - */ - function somlFrom($path) - { - if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Backwards-compatibility wrapper for somlFrom(). - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * - * @access public - * @since 1.0 - * @deprecated 1.2.6 - */ - function soml_from($path) - { - return somlFrom($path); - } - - /** - * Send the SAML FROM: command. - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.2.6 - */ - function samlFrom($path) - { - if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Backwards-compatibility wrapper for samlFrom(). - * - * @param string The reverse path to send. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * - * @access public - * @since 1.0 - * @deprecated 1.2.6 - */ - function saml_from($path) - { - return samlFrom($path); - } - - /** - * Send the RSET command. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function rset() - { - if (PEAR::isError($error = $this->_put('RSET'))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - return $error; - } - - return true; - } - - /** - * Send the VRFY command. - * - * @param string The string to verify - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function vrfy($string) - { - /* Note: 251 is also a valid response code */ - if (PEAR::isError($error = $this->_put('VRFY', $string))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) { - return $error; - } - - return true; - } - - /** - * Send the NOOP command. - * - * @return mixed Returns a PEAR_Error with an error message on any - * kind of failure, or true on success. - * @access public - * @since 1.0 - */ - function noop() - { - if (PEAR::isError($error = $this->_put('NOOP'))) { - return $error; - } - if (PEAR::isError($error = $this->_parseResponse(250))) { - return $error; - } - - return true; - } - - /** - * Backwards-compatibility method. identifySender()'s functionality is - * now handled internally. - * - * @return boolean This method always return true. - * - * @access public - * @since 1.0 - */ - function identifySender() - { - return true; - } - -} diff --git a/program/lib/Net/Sieve.php b/program/lib/Net/Sieve.php deleted file mode 100644 index 8ebdf0958..000000000 --- a/program/lib/Net/Sieve.php +++ /dev/null @@ -1,1274 +0,0 @@ -<?php -/** - * This file contains the Net_Sieve class. - * - * PHP version 4 - * - * +-----------------------------------------------------------------------+ - * | All rights reserved. | - * | | - * | Redistribution and use in source and binary forms, with or without | - * | modification, are permitted provided that the following conditions | - * | are met: | - * | | - * | o Redistributions of source code must retain the above copyright | - * | notice, this list of conditions and the following disclaimer. | - * | o Redistributions in binary form must reproduce the above copyright | - * | notice, this list of conditions and the following disclaimer in the | - * | documentation and/or other materials provided with the distribution.| - * | | - * | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | - * | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | - * | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | - * | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | - * | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | - * | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | - * | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | - * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | - * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | - * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | - * | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | - * +-----------------------------------------------------------------------+ - * - * @category Networking - * @package Net_Sieve - * @author Richard Heyes <richard@phpguru.org> - * @author Damian Fernandez Sosa <damlists@cnba.uba.ar> - * @author Anish Mistry <amistry@am-productions.biz> - * @author Jan Schneider <jan@horde.org> - * @copyright 2002-2003 Richard Heyes - * @copyright 2006-2008 Anish Mistry - * @license http://www.opensource.org/licenses/bsd-license.php BSD - * @version SVN: $Id$ - * @link http://pear.php.net/package/Net_Sieve - */ - -require_once 'PEAR.php'; -require_once 'Net/Socket.php'; - -/** - * TODO - * - * o supportsAuthMech() - */ - -/** - * Disconnected state - * @const NET_SIEVE_STATE_DISCONNECTED - */ -define('NET_SIEVE_STATE_DISCONNECTED', 1, true); - -/** - * Authorisation state - * @const NET_SIEVE_STATE_AUTHORISATION - */ -define('NET_SIEVE_STATE_AUTHORISATION', 2, true); - -/** - * Transaction state - * @const NET_SIEVE_STATE_TRANSACTION - */ -define('NET_SIEVE_STATE_TRANSACTION', 3, true); - - -/** - * A class for talking to the timsieved server which comes with Cyrus IMAP. - * - * @category Networking - * @package Net_Sieve - * @author Richard Heyes <richard@phpguru.org> - * @author Damian Fernandez Sosa <damlists@cnba.uba.ar> - * @author Anish Mistry <amistry@am-productions.biz> - * @author Jan Schneider <jan@horde.org> - * @copyright 2002-2003 Richard Heyes - * @copyright 2006-2008 Anish Mistry - * @license http://www.opensource.org/licenses/bsd-license.php BSD - * @version Release: 1.3.2 - * @link http://pear.php.net/package/Net_Sieve - * @link http://tools.ietf.org/html/rfc5228 RFC 5228 (Sieve: An Email - * Filtering Language) - * @link http://tools.ietf.org/html/rfc5804 RFC 5804 A Protocol for - * Remotely Managing Sieve Scripts - */ -class Net_Sieve -{ - /** - * The authentication methods this class supports. - * - * Can be overwritten if having problems with certain methods. - * - * @var array - */ - var $supportedAuthMethods = array('DIGEST-MD5', 'CRAM-MD5', 'EXTERNAL', - 'PLAIN' , 'LOGIN'); - - /** - * SASL authentication methods that require Auth_SASL. - * - * @var array - */ - var $supportedSASLAuthMethods = array('DIGEST-MD5', 'CRAM-MD5'); - - /** - * The socket handle. - * - * @var resource - */ - var $_sock; - - /** - * Parameters and connection information. - * - * @var array - */ - var $_data; - - /** - * Current state of the connection. - * - * One of the NET_SIEVE_STATE_* constants. - * - * @var integer - */ - var $_state; - - /** - * Constructor error. - * - * @var PEAR_Error - */ - var $_error; - - /** - * Whether to enable debugging. - * - * @var boolean - */ - var $_debug = false; - - /** - * Debug output handler. - * - * This has to be a valid callback. - * - * @var string|array - */ - var $_debug_handler = null; - - /** - * Whether to pick up an already established connection. - * - * @var boolean - */ - var $_bypassAuth = false; - - /** - * Whether to use TLS if available. - * - * @var boolean - */ - var $_useTLS = true; - - /** - * Additional options for stream_context_create(). - * - * @var array - */ - var $_options = null; - - /** - * Maximum number of referral loops - * - * @var array - */ - var $_maxReferralCount = 15; - - /** - * Constructor. - * - * Sets up the object, connects to the server and logs in. Stores any - * generated error in $this->_error, which can be retrieved using the - * getError() method. - * - * @param string $user Login username. - * @param string $pass Login password. - * @param string $host Hostname of server. - * @param string $port Port of server. - * @param string $logintype Type of login to perform (see - * $supportedAuthMethods). - * @param string $euser Effective user. If authenticating as an - * administrator, login as this user. - * @param boolean $debug Whether to enable debugging (@see setDebug()). - * @param string $bypassAuth Skip the authentication phase. Useful if the - * socket is already open. - * @param boolean $useTLS Use TLS if available. - * @param array $options Additional options for - * stream_context_create(). - * @param mixed $handler A callback handler for the debug output. - */ - function Net_Sieve($user = null, $pass = null, $host = 'localhost', - $port = 2000, $logintype = '', $euser = '', - $debug = false, $bypassAuth = false, $useTLS = true, - $options = null, $handler = null) - { - $this->_state = NET_SIEVE_STATE_DISCONNECTED; - $this->_data['user'] = $user; - $this->_data['pass'] = $pass; - $this->_data['host'] = $host; - $this->_data['port'] = $port; - $this->_data['logintype'] = $logintype; - $this->_data['euser'] = $euser; - $this->_sock = new Net_Socket(); - $this->_bypassAuth = $bypassAuth; - $this->_useTLS = $useTLS; - $this->_options = $options; - $this->setDebug($debug, $handler); - - /* Try to include the Auth_SASL package. If the package is not - * available, we disable the authentication methods that depend upon - * it. */ - if ((@include_once 'Auth/SASL.php') === false) { - $this->_debug('Auth_SASL not present'); - foreach ($this->supportedSASLAuthMethods as $SASLMethod) { - $pos = array_search($SASLMethod, $this->supportedAuthMethods); - $this->_debug('Disabling method ' . $SASLMethod); - unset($this->supportedAuthMethods[$pos]); - } - } - - if (strlen($user) && strlen($pass)) { - $this->_error = $this->_handleConnectAndLogin(); - } - } - - /** - * Returns any error that may have been generated in the constructor. - * - * @return boolean|PEAR_Error False if no error, PEAR_Error otherwise. - */ - function getError() - { - return PEAR::isError($this->_error) ? $this->_error : false; - } - - /** - * Sets the debug state and handler function. - * - * @param boolean $debug Whether to enable debugging. - * @param string $handler A custom debug handler. Must be a valid callback. - * - * @return void - */ - function setDebug($debug = true, $handler = null) - { - $this->_debug = $debug; - $this->_debug_handler = $handler; - } - - /** - * Connects to the server and logs in. - * - * @return boolean True on success, PEAR_Error on failure. - */ - function _handleConnectAndLogin() - { - if (PEAR::isError($res = $this->connect($this->_data['host'], $this->_data['port'], $this->_options, $this->_useTLS))) { - return $res; - } - if ($this->_bypassAuth === false) { - if (PEAR::isError($res = $this->login($this->_data['user'], $this->_data['pass'], $this->_data['logintype'], $this->_data['euser'], $this->_bypassAuth))) { - return $res; - } - } - return true; - } - - /** - * Handles connecting to the server and checks the response validity. - * - * @param string $host Hostname of server. - * @param string $port Port of server. - * @param array $options List of options to pass to - * stream_context_create(). - * @param boolean $useTLS Use TLS if available. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function connect($host, $port, $options = null, $useTLS = true) - { - $this->_data['host'] = $host; - $this->_data['port'] = $port; - $this->_useTLS = $useTLS; - if (is_array($options)) { - $this->_options = array_merge($this->_options, $options); - } - - if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) { - return PEAR::raiseError('Not currently in DISCONNECTED state', 1); - } - - if (PEAR::isError($res = $this->_sock->connect($host, $port, false, 5, $options))) { - return $res; - } - - if ($this->_bypassAuth) { - $this->_state = NET_SIEVE_STATE_TRANSACTION; - } else { - $this->_state = NET_SIEVE_STATE_AUTHORISATION; - if (PEAR::isError($res = $this->_doCmd())) { - return $res; - } - } - - // Explicitly ask for the capabilities in case the connection is - // picked up from an existing connection. - if (PEAR::isError($res = $this->_cmdCapability())) { - return PEAR::raiseError( - 'Failed to connect, server said: ' . $res->getMessage(), 2 - ); - } - - // Check if we can enable TLS via STARTTLS. - if ($useTLS && !empty($this->_capability['starttls']) - && function_exists('stream_socket_enable_crypto') - ) { - if (PEAR::isError($res = $this->_startTLS())) { - return $res; - } - } - - return true; - } - - /** - * Disconnect from the Sieve server. - * - * @param boolean $sendLogoutCMD Whether to send LOGOUT command before - * disconnecting. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function disconnect($sendLogoutCMD = true) - { - return $this->_cmdLogout($sendLogoutCMD); - } - - /** - * Logs into server. - * - * @param string $user Login username. - * @param string $pass Login password. - * @param string $logintype Type of login method to use. - * @param string $euser Effective UID (perform on behalf of $euser). - * @param boolean $bypassAuth Do not perform authentication. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function login($user, $pass, $logintype = null, $euser = '', $bypassAuth = false) - { - $this->_data['user'] = $user; - $this->_data['pass'] = $pass; - $this->_data['logintype'] = $logintype; - $this->_data['euser'] = $euser; - $this->_bypassAuth = $bypassAuth; - - if (NET_SIEVE_STATE_AUTHORISATION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - if (!$bypassAuth ) { - if (PEAR::isError($res = $this->_cmdAuthenticate($user, $pass, $logintype, $euser))) { - return $res; - } - } - $this->_state = NET_SIEVE_STATE_TRANSACTION; - - return true; - } - - /** - * Returns an indexed array of scripts currently on the server. - * - * @return array Indexed array of scriptnames. - */ - function listScripts() - { - if (is_array($scripts = $this->_cmdListScripts())) { - $this->_active = $scripts[1]; - return $scripts[0]; - } else { - return $scripts; - } - } - - /** - * Returns the active script. - * - * @return string The active scriptname. - */ - function getActive() - { - if (!empty($this->_active)) { - return $this->_active; - } - if (is_array($scripts = $this->_cmdListScripts())) { - $this->_active = $scripts[1]; - return $scripts[1]; - } - } - - /** - * Sets the active script. - * - * @param string $scriptname The name of the script to be set as active. - * - * @return boolean True on success, PEAR_Error on failure. - */ - function setActive($scriptname) - { - return $this->_cmdSetActive($scriptname); - } - - /** - * Retrieves a script. - * - * @param string $scriptname The name of the script to be retrieved. - * - * @return string The script on success, PEAR_Error on failure. - */ - function getScript($scriptname) - { - return $this->_cmdGetScript($scriptname); - } - - /** - * Adds a script to the server. - * - * @param string $scriptname Name of the script. - * @param string $script The script content. - * @param boolean $makeactive Whether to make this the active script. - * - * @return boolean True on success, PEAR_Error on failure. - */ - function installScript($scriptname, $script, $makeactive = false) - { - if (PEAR::isError($res = $this->_cmdPutScript($scriptname, $script))) { - return $res; - } - if ($makeactive) { - return $this->_cmdSetActive($scriptname); - } - return true; - } - - /** - * Removes a script from the server. - * - * @param string $scriptname Name of the script. - * - * @return boolean True on success, PEAR_Error on failure. - */ - function removeScript($scriptname) - { - return $this->_cmdDeleteScript($scriptname); - } - - /** - * Checks if the server has space to store the script by the server. - * - * @param string $scriptname The name of the script to mark as active. - * @param integer $size The size of the script. - * - * @return boolean|PEAR_Error True if there is space, PEAR_Error otherwise. - * - * @todo Rename to hasSpace() - */ - function haveSpace($scriptname, $size) - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in TRANSACTION state', 1); - } - - if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE %s %d', $this->_escape($scriptname), $size)))) { - return $res; - } - return true; - } - - /** - * Returns the list of extensions the server supports. - * - * @return array List of extensions or PEAR_Error on failure. - */ - function getExtensions() - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 7); - } - return $this->_capability['extensions']; - } - - /** - * Returns whether the server supports an extension. - * - * @param string $extension The extension to check. - * - * @return boolean Whether the extension is supported or PEAR_Error on - * failure. - */ - function hasExtension($extension) - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 7); - } - - $extension = trim($this->_toUpper($extension)); - if (is_array($this->_capability['extensions'])) { - foreach ($this->_capability['extensions'] as $ext) { - if ($ext == $extension) { - return true; - } - } - } - - return false; - } - - /** - * Returns the list of authentication methods the server supports. - * - * @return array List of authentication methods or PEAR_Error on failure. - */ - function getAuthMechs() - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 7); - } - return $this->_capability['sasl']; - } - - /** - * Returns whether the server supports an authentication method. - * - * @param string $method The method to check. - * - * @return boolean Whether the method is supported or PEAR_Error on - * failure. - */ - function hasAuthMech($method) - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 7); - } - - $method = trim($this->_toUpper($method)); - if (is_array($this->_capability['sasl'])) { - foreach ($this->_capability['sasl'] as $sasl) { - if ($sasl == $method) { - return true; - } - } - } - - return false; - } - - /** - * Handles the authentication using any known method. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $userMethod The method to use. If empty, the class chooses - * the best (strongest) available method. - * @param string $euser The effective uid to authenticate as. - * - * @return void - */ - function _cmdAuthenticate($uid, $pwd, $userMethod = null, $euser = '') - { - if (PEAR::isError($method = $this->_getBestAuthMethod($userMethod))) { - return $method; - } - switch ($method) { - case 'DIGEST-MD5': - return $this->_authDigestMD5($uid, $pwd, $euser); - case 'CRAM-MD5': - $result = $this->_authCRAMMD5($uid, $pwd, $euser); - break; - case 'LOGIN': - $result = $this->_authLOGIN($uid, $pwd, $euser); - break; - case 'PLAIN': - $result = $this->_authPLAIN($uid, $pwd, $euser); - break; - case 'EXTERNAL': - $result = $this->_authEXTERNAL($uid, $pwd, $euser); - break; - default : - $result = PEAR::raiseError( - $method . ' is not a supported authentication method' - ); - break; - } - - if (PEAR::isError($res = $this->_doCmd())) { - return $res; - } - - // Query the server capabilities again now that we are authenticated. - if (PEAR::isError($res = $this->_cmdCapability())) { - return PEAR::raiseError( - 'Failed to connect, server said: ' . $res->getMessage(), 2 - ); - } - - return $result; - } - - /** - * Authenticates the user using the PLAIN method. - * - * @param string $user The userid to authenticate as. - * @param string $pass The password to authenticate with. - * @param string $euser The effective uid to authenticate as. - * - * @return void - */ - function _authPLAIN($user, $pass, $euser) - { - return $this->_sendCmd( - sprintf( - 'AUTHENTICATE "PLAIN" "%s"', - base64_encode($euser . chr(0) . $user . chr(0) . $pass) - ) - ); - } - - /** - * Authenticates the user using the LOGIN method. - * - * @param string $user The userid to authenticate as. - * @param string $pass The password to authenticate with. - * @param string $euser The effective uid to authenticate as. - * - * @return void - */ - function _authLOGIN($user, $pass, $euser) - { - if (PEAR::isError($result = $this->_sendCmd('AUTHENTICATE "LOGIN"'))) { - return $result; - } - if (PEAR::isError($result = $this->_doCmd('"' . base64_encode($user) . '"', true))) { - return $result; - } - return $this->_doCmd('"' . base64_encode($pass) . '"', true); - } - - /** - * Authenticates the user using the CRAM-MD5 method. - * - * @param string $user The userid to authenticate as. - * @param string $pass The password to authenticate with. - * @param string $euser The effective uid to authenticate as. - * - * @return void - */ - function _authCRAMMD5($user, $pass, $euser) - { - if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "CRAM-MD5"', true))) { - return $challenge; - } - - $challenge = base64_decode(trim($challenge)); - $cram = Auth_SASL::factory('crammd5'); - if (PEAR::isError($response = $cram->getResponse($user, $pass, $challenge))) { - return $response; - } - - return $this->_sendStringResponse(base64_encode($response)); - } - - /** - * Authenticates the user using the DIGEST-MD5 method. - * - * @param string $user The userid to authenticate as. - * @param string $pass The password to authenticate with. - * @param string $euser The effective uid to authenticate as. - * - * @return void - */ - function _authDigestMD5($user, $pass, $euser) - { - if (PEAR::isError($challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"', true))) { - return $challenge; - } - - $challenge = base64_decode(trim($challenge)); - $digest = Auth_SASL::factory('digestmd5'); - // @todo Really 'localhost'? - if (PEAR::isError($response = $digest->getResponse($user, $pass, $challenge, 'localhost', 'sieve', $euser))) { - return $response; - } - - if (PEAR::isError($result = $this->_sendStringResponse(base64_encode($response)))) { - return $result; - } - if (PEAR::isError($result = $this->_doCmd('', true))) { - return $result; - } - if ($this->_toUpper(substr($result, 0, 2)) == 'OK') { - return; - } - - /* We don't use the protocol's third step because SIEVE doesn't allow - * subsequent authentication, so we just silently ignore it. */ - if (PEAR::isError($result = $this->_sendStringResponse(''))) { - return $result; - } - - return $this->_doCmd(); - } - - /** - * Authenticates the user using the EXTERNAL method. - * - * @param string $user The userid to authenticate as. - * @param string $pass The password to authenticate with. - * @param string $euser The effective uid to authenticate as. - * - * @return void - * - * @since 1.1.7 - */ - function _authEXTERNAL($user, $pass, $euser) - { - $cmd = sprintf( - 'AUTHENTICATE "EXTERNAL" "%s"', - base64_encode(strlen($euser) ? $euser : $user) - ); - return $this->_sendCmd($cmd); - } - - /** - * Removes a script from the server. - * - * @param string $scriptname Name of the script to delete. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function _cmdDeleteScript($scriptname) - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - if (PEAR::isError($res = $this->_doCmd(sprintf('DELETESCRIPT %s', $this->_escape($scriptname))))) { - return $res; - } - return true; - } - - /** - * Retrieves the contents of the named script. - * - * @param string $scriptname Name of the script to retrieve. - * - * @return string The script if successful, PEAR_Error otherwise. - */ - function _cmdGetScript($scriptname) - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - if (PEAR::isError($res = $this->_doCmd(sprintf('GETSCRIPT %s', $this->_escape($scriptname))))) { - return $res; - } - - return preg_replace('/^{[0-9]+}\r\n/', '', $res); - } - - /** - * Sets the active script, i.e. the one that gets run on new mail by the - * server. - * - * @param string $scriptname The name of the script to mark as active. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function _cmdSetActive($scriptname) - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - if (PEAR::isError($res = $this->_doCmd(sprintf('SETACTIVE %s', $this->_escape($scriptname))))) { - return $res; - } - - $this->_activeScript = $scriptname; - return true; - } - - /** - * Returns the list of scripts on the server. - * - * @return array An array with the list of scripts in the first element - * and the active script in the second element on success, - * PEAR_Error otherwise. - */ - function _cmdListScripts() - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - if (PEAR::isError($res = $this->_doCmd('LISTSCRIPTS'))) { - return $res; - } - - $scripts = array(); - $activescript = null; - $res = explode("\r\n", $res); - foreach ($res as $value) { - if (preg_match('/^"(.*)"( ACTIVE)?$/i', $value, $matches)) { - $script_name = stripslashes($matches[1]); - $scripts[] = $script_name; - if (!empty($matches[2])) { - $activescript = $script_name; - } - } - } - - return array($scripts, $activescript); - } - - /** - * Adds a script to the server. - * - * @param string $scriptname Name of the new script. - * @param string $scriptdata The new script. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function _cmdPutScript($scriptname, $scriptdata) - { - if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { - return PEAR::raiseError('Not currently in AUTHORISATION state', 1); - } - - $stringLength = $this->_getLineLength($scriptdata); - $command = sprintf("PUTSCRIPT %s {%d+}\r\n%s", - $this->_escape($scriptname), - $stringLength, - $scriptdata); - if (PEAR::isError($res = $this->_doCmd($command))) { - return $res; - } - - return true; - } - - /** - * Logs out of the server and terminates the connection. - * - * @param boolean $sendLogoutCMD Whether to send LOGOUT command before - * disconnecting. - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function _cmdLogout($sendLogoutCMD = true) - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 1); - } - - if ($sendLogoutCMD) { - if (PEAR::isError($res = $this->_doCmd('LOGOUT'))) { - return $res; - } - } - - $this->_sock->disconnect(); - $this->_state = NET_SIEVE_STATE_DISCONNECTED; - - return true; - } - - /** - * Sends the CAPABILITY command - * - * @return boolean True on success, PEAR_Error otherwise. - */ - function _cmdCapability() - { - if (NET_SIEVE_STATE_DISCONNECTED == $this->_state) { - return PEAR::raiseError('Not currently connected', 1); - } - if (PEAR::isError($res = $this->_doCmd('CAPABILITY'))) { - return $res; - } - $this->_parseCapability($res); - return true; - } - - /** - * Parses the response from the CAPABILITY command and stores the result - * in $_capability. - * - * @param string $data The response from the capability command. - * - * @return void - */ - function _parseCapability($data) - { - // Clear the cached capabilities. - $this->_capability = array('sasl' => array(), - 'extensions' => array()); - - $data = preg_split('/\r?\n/', $this->_toUpper($data), -1, PREG_SPLIT_NO_EMPTY); - - for ($i = 0; $i < count($data); $i++) { - if (!preg_match('/^"([A-Z]+)"( "(.*)")?$/', $data[$i], $matches)) { - continue; - } - switch ($matches[1]) { - case 'IMPLEMENTATION': - $this->_capability['implementation'] = $matches[3]; - break; - - case 'SASL': - $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]); - break; - - case 'SIEVE': - $this->_capability['extensions'] = preg_split('/\s+/', $matches[3]); - break; - - case 'STARTTLS': - $this->_capability['starttls'] = true; - break; - } - } - } - - /** - * Sends a command to the server - * - * @param string $cmd The command to send. - * - * @return void - */ - function _sendCmd($cmd) - { - $status = $this->_sock->getStatus(); - if (PEAR::isError($status) || $status['eof']) { - return PEAR::raiseError('Failed to write to socket: connection lost'); - } - if (PEAR::isError($error = $this->_sock->write($cmd . "\r\n"))) { - return PEAR::raiseError( - 'Failed to write to socket: ' . $error->getMessage() - ); - } - $this->_debug("C: $cmd"); - } - - /** - * Sends a string response to the server. - * - * @param string $str The string to send. - * - * @return void - */ - function _sendStringResponse($str) - { - return $this->_sendCmd('{' . $this->_getLineLength($str) . "+}\r\n" . $str); - } - - /** - * Receives a single line from the server. - * - * @return string The server response line. - */ - function _recvLn() - { - if (PEAR::isError($lastline = $this->_sock->gets(8192))) { - return PEAR::raiseError( - 'Failed to read from socket: ' . $lastline->getMessage() - ); - } - - $lastline = rtrim($lastline); - $this->_debug("S: $lastline"); - - if ($lastline === '') { - return PEAR::raiseError('Failed to read from socket'); - } - - return $lastline; - } - - /** - * Receives a number of bytes from the server. - * - * @param integer $length Number of bytes to read. - * - * @return string The server response. - */ - function _recvBytes($length) - { - $response = ''; - $response_length = 0; - while ($response_length < $length) { - $response .= $this->_sock->read($length - $response_length); - $response_length = $this->_getLineLength($response); - } - $this->_debug('S: ' . rtrim($response)); - return $response; - } - - /** - * Send a command and retrieves a response from the server. - * - * @param string $cmd The command to send. - * @param boolean $auth Whether this is an authentication command. - * - * @return string|PEAR_Error Reponse string if an OK response, PEAR_Error - * if a NO response. - */ - function _doCmd($cmd = '', $auth = false) - { - $referralCount = 0; - while ($referralCount < $this->_maxReferralCount) { - if (strlen($cmd)) { - if (PEAR::isError($error = $this->_sendCmd($cmd))) { - return $error; - } - } - - $response = ''; - while (true) { - if (PEAR::isError($line = $this->_recvLn())) { - return $line; - } - $uc_line = $this->_toUpper($line); - - if ('OK' == substr($uc_line, 0, 2)) { - $response .= $line; - return rtrim($response); - } - - if ('NO' == substr($uc_line, 0, 2)) { - // Check for string literal error message. - if (preg_match('/{([0-9]+)}$/', $line, $matches)) { - $line = substr($line, 0, -(strlen($matches[1])+2)) - . str_replace( - "\r\n", ' ', $this->_recvBytes($matches[1] + 2) - ); - } - return PEAR::raiseError(trim($response . substr($line, 2)), 3); - } - - if ('BYE' == substr($uc_line, 0, 3)) { - if (PEAR::isError($error = $this->disconnect(false))) { - return PEAR::raiseError( - 'Cannot handle BYE, the error was: ' - . $error->getMessage(), - 4 - ); - } - // Check for referral, then follow it. Otherwise, carp an - // error. - if (preg_match('/^bye \(referral "(sieve:\/\/)?([^"]+)/i', $line, $matches)) { - // Replace the old host with the referral host - // preserving any protocol prefix. - $this->_data['host'] = preg_replace( - '/\w+(?!(\w|\:\/\/)).*/', $matches[2], - $this->_data['host'] - ); - if (PEAR::isError($error = $this->_handleConnectAndLogin())) { - return PEAR::raiseError( - 'Cannot follow referral to ' - . $this->_data['host'] . ', the error was: ' - . $error->getMessage(), - 5 - ); - } - break; - } - return PEAR::raiseError(trim($response . $line), 6); - } - - if (preg_match('/^{([0-9]+)}/', $line, $matches)) { - // Matches literal string responses. - $line = $this->_recvBytes($matches[1] + 2); - if (!$auth) { - // Receive the pending OK only if we aren't - // authenticating since string responses during - // authentication don't need an OK. - $this->_recvLn(); - } - return $line; - } - - if ($auth) { - // String responses during authentication don't need an - // OK. - $response .= $line; - return rtrim($response); - } - - $response .= $line . "\r\n"; - $referralCount++; - } - } - - return PEAR::raiseError('Max referral count (' . $referralCount . ') reached. Cyrus murder loop error?', 7); - } - - /** - * Returns the name of the best authentication method that the server - * has advertised. - * - * @param string $userMethod Only consider this method as available. - * - * @return string The name of the best supported authentication method or - * a PEAR_Error object on failure. - */ - function _getBestAuthMethod($userMethod = null) - { - if (!isset($this->_capability['sasl'])) { - return PEAR::raiseError('This server doesn\'t support any authentication methods. SASL problem?'); - } - if (!$this->_capability['sasl']) { - return PEAR::raiseError('This server doesn\'t support any authentication methods.'); - } - - if ($userMethod) { - if (in_array($userMethod, $this->_capability['sasl'])) { - return $userMethod; - } - return PEAR::raiseError( - sprintf('No supported authentication method found. The server supports these methods: %s, but we want to use: %s', - implode(', ', $this->_capability['sasl']), - $userMethod)); - } - - foreach ($this->supportedAuthMethods as $method) { - if (in_array($method, $this->_capability['sasl'])) { - return $method; - } - } - - return PEAR::raiseError( - sprintf('No supported authentication method found. The server supports these methods: %s, but we only support: %s', - implode(', ', $this->_capability['sasl']), - implode(', ', $this->supportedAuthMethods))); - } - - /** - * Starts a TLS connection. - * - * @return boolean True on success, PEAR_Error on failure. - */ - function _startTLS() - { - if (PEAR::isError($res = $this->_doCmd('STARTTLS'))) { - return $res; - } - - if (!stream_socket_enable_crypto($this->_sock->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { - return PEAR::raiseError('Failed to establish TLS connection', 2); - } - - $this->_debug('STARTTLS negotiation successful'); - - // The server should be sending a CAPABILITY response after - // negotiating TLS. Read it, and ignore if it doesn't. - // Unfortunately old Cyrus versions are broken and don't send a - // CAPABILITY response, thus we would wait here forever. Parse the - // Cyrus version and work around this broken behavior. - if (!preg_match('/^CYRUS TIMSIEVED V([0-9.]+)/', $this->_capability['implementation'], $matches) || - version_compare($matches[1], '2.3.10', '>=')) { - $this->_doCmd(); - } - - // Query the server capabilities again now that we are under - // encryption. - if (PEAR::isError($res = $this->_cmdCapability())) { - return PEAR::raiseError( - 'Failed to connect, server said: ' . $res->getMessage(), 2 - ); - } - - return true; - } - - /** - * Returns the length of a string. - * - * @param string $string A string. - * - * @return integer The length of the string. - */ - function _getLineLength($string) - { - if (extension_loaded('mbstring')) { - return mb_strlen($string, 'latin1'); - } else { - return strlen($string); - } - } - - /** - * Locale independant strtoupper() implementation. - * - * @param string $string The string to convert to lowercase. - * - * @return string The lowercased string, based on ASCII encoding. - */ - function _toUpper($string) - { - $language = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $string = strtoupper($string); - setlocale(LC_CTYPE, $language); - return $string; - } - - /** - * Converts strings into RFC's quoted-string or literal-c2s form. - * - * @param string $string The string to convert. - * - * @return string Result string. - */ - function _escape($string) - { - // Some implementations don't allow UTF-8 characters in quoted-string, - // use literal-c2s. - if (preg_match('/[^\x01-\x09\x0B-\x0C\x0E-\x7F]/', $string)) { - return sprintf("{%d+}\r\n%s", $this->_getLineLength($string), $string); - } - - return '"' . addcslashes($string, '\\"') . '"'; - } - - /** - * Write debug text to the current debug output handler. - * - * @param string $message Debug message text. - * - * @return void - */ - function _debug($message) - { - if ($this->_debug) { - if ($this->_debug_handler) { - call_user_func_array($this->_debug_handler, array(&$this, $message)); - } else { - echo "$message\n"; - } - } - } -} diff --git a/program/lib/Net/Socket.php b/program/lib/Net/Socket.php deleted file mode 100644 index bf1d1bbcd..000000000 --- a/program/lib/Net/Socket.php +++ /dev/null @@ -1,686 +0,0 @@ -<?php -/** - * Net_Socket - * - * PHP Version 4 - * - * Copyright (c) 1997-2013 The PHP Group - * - * This source file is subject to version 2.0 of the PHP license, - * that is bundled with this package in the file LICENSE, and is - * available at through the world-wide-web at - * http://www.php.net/license/2_02.txt. - * If you did not receive a copy of the PHP license and are unable to - * obtain it through the world-wide-web, please send a note to - * license@php.net so we can mail you a copy immediately. - * - * Authors: Stig Bakken <ssb@php.net> - * Chuck Hagenbuch <chuck@horde.org> - * - * @category Net - * @package Net_Socket - * @author Stig Bakken <ssb@php.net> - * @author Chuck Hagenbuch <chuck@horde.org> - * @copyright 1997-2003 The PHP Group - * @license http://www.php.net/license/2_02.txt PHP 2.02 - * @link http://pear.php.net/packages/Net_Socket - */ - -require_once 'PEAR.php'; - -define('NET_SOCKET_READ', 1); -define('NET_SOCKET_WRITE', 2); -define('NET_SOCKET_ERROR', 4); - -/** - * Generalized Socket class. - * - * @category Net - * @package Net_Socket - * @author Stig Bakken <ssb@php.net> - * @author Chuck Hagenbuch <chuck@horde.org> - * @copyright 1997-2003 The PHP Group - * @license http://www.php.net/license/2_02.txt PHP 2.02 - * @link http://pear.php.net/packages/Net_Socket - */ -class Net_Socket extends PEAR -{ - /** - * Socket file pointer. - * @var resource $fp - */ - var $fp = null; - - /** - * Whether the socket is blocking. Defaults to true. - * @var boolean $blocking - */ - var $blocking = true; - - /** - * Whether the socket is persistent. Defaults to false. - * @var boolean $persistent - */ - var $persistent = false; - - /** - * The IP address to connect to. - * @var string $addr - */ - var $addr = ''; - - /** - * The port number to connect to. - * @var integer $port - */ - var $port = 0; - - /** - * Number of seconds to wait on socket operations before assuming - * there's no more data. Defaults to no timeout. - * @var integer|float $timeout - */ - var $timeout = null; - - /** - * Number of bytes to read at a time in readLine() and - * readAll(). Defaults to 2048. - * @var integer $lineLength - */ - var $lineLength = 2048; - - /** - * The string to use as a newline terminator. Usually "\r\n" or "\n". - * @var string $newline - */ - var $newline = "\r\n"; - - /** - * Connect to the specified port. If called when the socket is - * already connected, it disconnects and connects again. - * - * @param string $addr IP address or host name (may be with protocol prefix). - * @param integer $port TCP port number. - * @param boolean $persistent (optional) Whether the connection is - * persistent (kept open between requests - * by the web server). - * @param integer $timeout (optional) Connection socket timeout. - * @param array $options See options for stream_context_create. - * - * @access public - * - * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. - */ - function connect($addr, $port = 0, $persistent = null, - $timeout = null, $options = null) - { - if (is_resource($this->fp)) { - @fclose($this->fp); - $this->fp = null; - } - - if (!$addr) { - return $this->raiseError('$addr cannot be empty'); - } else if (strspn($addr, ':.0123456789') == strlen($addr)) { - $this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr; - } else { - $this->addr = $addr; - } - - $this->port = $port % 65536; - - if ($persistent !== null) { - $this->persistent = $persistent; - } - - $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen'; - $errno = 0; - $errstr = ''; - - $old_track_errors = @ini_set('track_errors', 1); - - if ($timeout <= 0) { - $timeout = @ini_get('default_socket_timeout'); - } - - if ($options && function_exists('stream_context_create')) { - $context = stream_context_create($options); - - // Since PHP 5 fsockopen doesn't allow context specification - if (function_exists('stream_socket_client')) { - $flags = STREAM_CLIENT_CONNECT; - - if ($this->persistent) { - $flags = STREAM_CLIENT_PERSISTENT; - } - - $addr = $this->addr . ':' . $this->port; - $fp = stream_socket_client($addr, $errno, $errstr, - $timeout, $flags, $context); - } else { - $fp = @$openfunc($this->addr, $this->port, $errno, - $errstr, $timeout, $context); - } - } else { - $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout); - } - - if (!$fp) { - if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) { - $errstr = $php_errormsg; - } - @ini_set('track_errors', $old_track_errors); - return $this->raiseError($errstr, $errno); - } - - @ini_set('track_errors', $old_track_errors); - $this->fp = $fp; - $this->setTimeout(); - return $this->setBlocking($this->blocking); - } - - /** - * Disconnects from the peer, closes the socket. - * - * @access public - * @return mixed true on success or a PEAR_Error instance otherwise - */ - function disconnect() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - @fclose($this->fp); - $this->fp = null; - return true; - } - - /** - * Set the newline character/sequence to use. - * - * @param string $newline Newline character(s) - * @return boolean True - */ - function setNewline($newline) - { - $this->newline = $newline; - return true; - } - - /** - * Find out if the socket is in blocking mode. - * - * @access public - * @return boolean The current blocking mode. - */ - function isBlocking() - { - return $this->blocking; - } - - /** - * Sets whether the socket connection should be blocking or - * not. A read call to a non-blocking socket will return immediately - * if there is no data available, whereas it will block until there - * is data for blocking sockets. - * - * @param boolean $mode True for blocking sockets, false for nonblocking. - * - * @access public - * @return mixed true on success or a PEAR_Error instance otherwise - */ - function setBlocking($mode) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $this->blocking = $mode; - stream_set_blocking($this->fp, (int)$this->blocking); - return true; - } - - /** - * Sets the timeout value on socket descriptor, - * expressed in the sum of seconds and microseconds - * - * @param integer $seconds Seconds. - * @param integer $microseconds Microseconds, optional. - * - * @access public - * @return mixed True on success or false on failure or - * a PEAR_Error instance when not connected - */ - function setTimeout($seconds = null, $microseconds = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if ($seconds === null && $microseconds === null) { - $seconds = (int) $this->timeout; - $microseconds = (int) (($this->timeout - $seconds) * 1000000); - } else { - $this->timeout = $seconds + $microseconds/1000000; - } - - if ($this->timeout > 0) { - return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds); - } - else { - return false; - } - } - - /** - * Sets the file buffering size on the stream. - * See php's stream_set_write_buffer for more information. - * - * @param integer $size Write buffer size. - * - * @access public - * @return mixed on success or an PEAR_Error object otherwise - */ - function setWriteBuffer($size) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $returned = stream_set_write_buffer($this->fp, $size); - if ($returned == 0) { - return true; - } - return $this->raiseError('Cannot set write buffer.'); - } - - /** - * Returns information about an existing socket resource. - * Currently returns four entries in the result array: - * - * <p> - * timed_out (bool) - The socket timed out waiting for data<br> - * blocked (bool) - The socket was blocked<br> - * eof (bool) - Indicates EOF event<br> - * unread_bytes (int) - Number of bytes left in the socket buffer<br> - * </p> - * - * @access public - * @return mixed Array containing information about existing socket - * resource or a PEAR_Error instance otherwise - */ - function getStatus() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return stream_get_meta_data($this->fp); - } - - /** - * Get a specified line of data - * - * @param int $size Reading ends when size - 1 bytes have been read, - * or a newline or an EOF (whichever comes first). - * If no size is specified, it will keep reading from - * the stream until it reaches the end of the line. - * - * @access public - * @return mixed $size bytes of data from the socket, or a PEAR_Error if - * not connected. If an error occurs, FALSE is returned. - */ - function gets($size = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if (is_null($size)) { - return @fgets($this->fp); - } else { - return @fgets($this->fp, $size); - } - } - - /** - * Read a specified amount of data. This is guaranteed to return, - * and has the added benefit of getting everything in one fread() - * chunk; if you know the size of the data you're getting - * beforehand, this is definitely the way to go. - * - * @param integer $size The number of bytes to read from the socket. - * - * @access public - * @return $size bytes of data from the socket, or a PEAR_Error if - * not connected. - */ - function read($size) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return @fread($this->fp, $size); - } - - /** - * Write a specified amount of data. - * - * @param string $data Data to write. - * @param integer $blocksize Amount of data to write at once. - * NULL means all at once. - * - * @access public - * @return mixed If the socket is not connected, returns an instance of - * PEAR_Error. - * If the write succeeds, returns the number of bytes written. - * If the write fails, returns false. - * If the socket times out, returns an instance of PEAR_Error. - */ - function write($data, $blocksize = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if (is_null($blocksize) && !OS_WINDOWS) { - $written = @fwrite($this->fp, $data); - - // Check for timeout or lost connection - if (!$written) { - $meta_data = $this->getStatus(); - - if (!is_array($meta_data)) { - return $meta_data; // PEAR_Error - } - - if (!empty($meta_data['timed_out'])) { - return $this->raiseError('timed out'); - } - } - - return $written; - } else { - if (is_null($blocksize)) { - $blocksize = 1024; - } - - $pos = 0; - $size = strlen($data); - while ($pos < $size) { - $written = @fwrite($this->fp, substr($data, $pos, $blocksize)); - - // Check for timeout or lost connection - if (!$written) { - $meta_data = $this->getStatus(); - - if (!is_array($meta_data)) { - return $meta_data; // PEAR_Error - } - - if (!empty($meta_data['timed_out'])) { - return $this->raiseError('timed out'); - } - - return $written; - } - - $pos += $written; - } - - return $pos; - } - } - - /** - * Write a line of data to the socket, followed by a trailing newline. - * - * @param string $data Data to write - * - * @access public - * @return mixed fwrite() result, or PEAR_Error when not connected - */ - function writeLine($data) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return fwrite($this->fp, $data . $this->newline); - } - - /** - * Tests for end-of-file on a socket descriptor. - * - * Also returns true if the socket is disconnected. - * - * @access public - * @return bool - */ - function eof() - { - return (!is_resource($this->fp) || feof($this->fp)); - } - - /** - * Reads a byte of data - * - * @access public - * @return 1 byte of data from the socket, or a PEAR_Error if - * not connected. - */ - function readByte() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return ord(@fread($this->fp, 1)); - } - - /** - * Reads a word of data - * - * @access public - * @return 1 word of data from the socket, or a PEAR_Error if - * not connected. - */ - function readWord() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 2); - return (ord($buf[0]) + (ord($buf[1]) << 8)); - } - - /** - * Reads an int of data - * - * @access public - * @return integer 1 int of data from the socket, or a PEAR_Error if - * not connected. - */ - function readInt() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 4); - return (ord($buf[0]) + (ord($buf[1]) << 8) + - (ord($buf[2]) << 16) + (ord($buf[3]) << 24)); - } - - /** - * Reads a zero-terminated string of data - * - * @access public - * @return string, or a PEAR_Error if - * not connected. - */ - function readString() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $string = ''; - while (($char = @fread($this->fp, 1)) != "\x00") { - $string .= $char; - } - return $string; - } - - /** - * Reads an IP Address and returns it in a dot formatted string - * - * @access public - * @return Dot formatted string, or a PEAR_Error if - * not connected. - */ - function readIPAddress() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 4); - return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]), - ord($buf[2]), ord($buf[3])); - } - - /** - * Read until either the end of the socket or a newline, whichever - * comes first. Strips the trailing newline from the returned data. - * - * @access public - * @return All available data up to a newline, without that - * newline, or until the end of the socket, or a PEAR_Error if - * not connected. - */ - function readLine() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $line = ''; - - $timeout = time() + $this->timeout; - - while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) { - $line .= @fgets($this->fp, $this->lineLength); - if (substr($line, -1) == "\n") { - return rtrim($line, $this->newline); - } - } - return $line; - } - - /** - * Read until the socket closes, or until there is no more data in - * the inner PHP buffer. If the inner buffer is empty, in blocking - * mode we wait for at least 1 byte of data. Therefore, in - * blocking mode, if there is no data at all to be read, this - * function will never exit (unless the socket is closed on the - * remote end). - * - * @access public - * - * @return string All data until the socket closes, or a PEAR_Error if - * not connected. - */ - function readAll() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $data = ''; - while (!feof($this->fp)) { - $data .= @fread($this->fp, $this->lineLength); - } - return $data; - } - - /** - * Runs the equivalent of the select() system call on the socket - * with a timeout specified by tv_sec and tv_usec. - * - * @param integer $state Which of read/write/error to check for. - * @param integer $tv_sec Number of seconds for timeout. - * @param integer $tv_usec Number of microseconds for timeout. - * - * @access public - * @return False if select fails, integer describing which of read/write/error - * are ready, or PEAR_Error if not connected. - */ - function select($state, $tv_sec, $tv_usec = 0) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $read = null; - $write = null; - $except = null; - if ($state & NET_SOCKET_READ) { - $read[] = $this->fp; - } - if ($state & NET_SOCKET_WRITE) { - $write[] = $this->fp; - } - if ($state & NET_SOCKET_ERROR) { - $except[] = $this->fp; - } - if (false === ($sr = stream_select($read, $write, $except, - $tv_sec, $tv_usec))) { - return false; - } - - $result = 0; - if (count($read)) { - $result |= NET_SOCKET_READ; - } - if (count($write)) { - $result |= NET_SOCKET_WRITE; - } - if (count($except)) { - $result |= NET_SOCKET_ERROR; - } - return $result; - } - - /** - * Turns encryption on/off on a connected socket. - * - * @param bool $enabled Set this parameter to true to enable encryption - * and false to disable encryption. - * @param integer $type Type of encryption. See stream_socket_enable_crypto() - * for values. - * - * @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php - * @access public - * @return false on error, true on success and 0 if there isn't enough data - * and the user should try again (non-blocking sockets only). - * A PEAR_Error object is returned if the socket is not - * connected - */ - function enableCrypto($enabled, $type) - { - if (version_compare(phpversion(), "5.1.0", ">=")) { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - return @stream_socket_enable_crypto($this->fp, $enabled, $type); - } else { - $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0'; - return $this->raiseError($msg); - } - } - -} |