phpgacl-3.3.7/0040755025754300001440000000000010476665055012072 5ustar ipsousersphpgacl-3.3.7/Cache_Lite/0040755025754300001440000000000010476665050014045 5ustar ipsousersphpgacl-3.3.7/Cache_Lite/LICENSE0100644025754300001440000005747507543270744015074 0ustar ipsousers GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS phpgacl-3.3.7/Cache_Lite/Hashed_Cache_Lite.php0100644025754300001440000001141010270765617020006 0ustar ipsousers_cacheDir.$group.'/'.substr($encoded_id,0,3); $this->_create_dir_structure($dir); $this->_file = $dir.'/'.$encoded_id; } /** * Create full directory structure, Ripped straight from the Smarty Template engine. * Version: 2.3.0 * Copyright: 2001,2002 ispi of Lincoln, Inc. * * @param string $dir Full directory. * @access private */ function _create_dir_structure($dir) { if (!@file_exists($dir)) { $dir_parts = preg_split('![\/]+!', $dir, -1, PREG_SPLIT_NO_EMPTY); $new_dir = ($dir{0} == DIR_SEP) ? DIR_SEP : ''; foreach ($dir_parts as $dir_part) { $new_dir .= $dir_part; if (!file_exists($new_dir) && !mkdir($new_dir, 0771)) { Cache_Lite::raiseError('Cache_Lite : problem creating directory \"$dir\" !', -3); return false; } $new_dir .= DIR_SEP; } } } function _remove_dir_structure($dir,$remove_dir = false) { if (in_array(substr($dir,-1),array(DIR_SEP,'/','\\'))) { $dir = substr($dir,0,-1); } if (!($dh = opendir($dir))) { $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); return false; } while ($file = readdir($dh)) { if ($file == '.' || $file == '..') { continue; } $file = $dir . DIR_SEP . $file; if (is_dir($file)) { $this->_remove_dir_structure($file,true); continue; } if (is_file($file)) { if (!@unlink($file)) { closedir($dh); $this->raiseError('Cache_Lite : Unable to remove cache !', -3); return false; } continue; } } closedir($dh); if ($remove_dir) { clearstatcache(); if (!@rmdir($dir)) { $this->raiseError('Cache_Lite : Unable to remove cache directory !', -4); return false; } } return true; } /** * Clean the cache * * if no group is specified all cache files will be destroyed * else only cache files of the specified group will be destroyed * * @param string $group name of the cache group * @return boolean true if no problem * @access public */ function clean($group = false) { if ($group) { $motif = $this->_cacheDir.$group.'/'; if ($this->_memoryCaching) { foreach ($this->_memoryCachingArray as $key => $value) { if (strpos($key, $motif, 0)) { unset($this->_memoryCachingArray[$key]); } } $this->_memoryCachingCounter = count($this->_memoryCachingArray); if ($this->_onlyMemoryCaching) { return true; } } return $this->_remove_dir_structure($motif); } if ($this->_memoryCaching) { $this->_memoryCachingArray = array(); $this->_memoryCachingCounter = 0; if ($this->_onlyMemoryCaching) { return true; } } if (!($dh = opendir($this->_cacheDir))) { $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); return false; } while ($file = readdir($dh)) { if ($file == '.' || $file == '..') { continue; } $file = $this->_cacheDir . $file; if (is_dir($file) && !$this->_remove_dir_structure($file,true)) { return false; } } return true; } } // end of script phpgacl-3.3.7/Cache_Lite/Lite.php0100644025754300001440000004331510143541011015433 0ustar ipsousers * * @package Cache_Lite * @category Caching * @version $Id: Lite.php 376 2004-11-08 00:47:05Z ipso $ * @author Fabien MARTY */ define('CACHE_LITE_ERROR_RETURN', 1); define('CACHE_LITE_ERROR_DIE', 8); class Cache_Lite { // --- Private properties --- /** * Directory where to put the cache files * (make sure to add a trailing slash) * * @var string $_cacheDir */ var $_cacheDir = '/tmp/'; /** * Enable / disable caching * * (can be very usefull for the debug of cached scripts) * * @var boolean $_caching */ var $_caching = true; /** * Cache lifetime (in seconds) * * @var int $_lifeTime */ var $_lifeTime = 3600; /** * Enable / disable fileLocking * * (can avoid cache corruption under bad circumstances) * * @var boolean $_fileLocking */ var $_fileLocking = true; /** * Timestamp of the last valid cache * * @var int $_refreshTime */ var $_refreshTime; /** * File name (with path) * * @var string $_file */ var $_file; /** * Enable / disable write control (the cache is read just after writing to detect corrupt entries) * * Enable write control will lightly slow the cache writing but not the cache reading * Write control can detect some corrupt cache files but maybe it's not a perfect control * * @var boolean $_writeControl */ var $_writeControl = true; /** * Enable / disable read control * * If enabled, a control key is embeded in cache file and this key is compared with the one * calculated after the reading. * * @var boolean $_writeControl */ var $_readControl = true; /** * Type of read control (only if read control is enabled) * * Available values are : * 'md5' for a md5 hash control (best but slowest) * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) * 'strlen' for a length only test (fastest) * * @var boolean $_readControlType */ var $_readControlType = 'crc32'; /** * Pear error mode (when raiseError is called) * * (see PEAR doc) * * @see setToDebug() * @var int $_pearErrorMode */ var $_pearErrorMode = CACHE_LITE_ERROR_RETURN; /** * Current cache id * * @var string $_id */ var $_id; /** * Current cache group * * @var string $_group */ var $_group; /** * Enable / Disable "Memory Caching" * * NB : There is no lifetime for memory caching ! * * @var boolean $_memoryCaching */ var $_memoryCaching = false; /** * Enable / Disable "Only Memory Caching" * (be carefull, memory caching is "beta quality") * * @var boolean $_onlyMemoryCaching */ var $_onlyMemoryCaching = false; /** * Memory caching array * * @var array $_memoryCachingArray */ var $_memoryCachingArray = array(); /** * Memory caching counter * * @var int $memoryCachingCounter */ var $_memoryCachingCounter = 0; /** * Memory caching limit * * @var int $memoryCachingLimit */ var $_memoryCachingLimit = 1000; /** * File Name protection * * if set to true, you can use any cache id or group name * if set to false, it can be faster but cache ids and group names * will be used directly in cache file names so be carefull with * special characters... * * @var boolean $fileNameProtection */ var $_fileNameProtection = true; /** * Enable / disable automatic serialization * * it can be used to save directly datas which aren't strings * (but it's slower) * * @var boolean $_serialize */ var $_automaticSerialization = false; // --- Public methods --- /** * Constructor * * $options is an assoc. Available options are : * $options = array( * 'cacheDir' => directory where to put the cache files (string), * 'caching' => enable / disable caching (boolean), * 'lifeTime' => cache lifetime in seconds (int), * 'fileLocking' => enable / disable fileLocking (boolean), * 'writeControl' => enable / disable write control (boolean), * 'readControl' => enable / disable read control (boolean), * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), * 'memoryCaching' => enable / disable memory caching (boolean), * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), * 'fileNameProtection' => enable / disable automatic file name protection (boolean), * 'automaticSerialization' => enable / disable automatic serialization (boolean) * ); * * @param array $options options * @access public */ function Cache_Lite($options = array(NULL)) { $availableOptions = array('automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode'); foreach($options as $key => $value) { if(in_array($key, $availableOptions)) { $property = '_'.$key; $this->$property = $value; } } $this->_refreshTime = time() - $this->_lifeTime; } /** * Test if a cache is available and (if yes) return it * * @param string $id cache id * @param string $group name of the cache group * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string data of the cache (or false if no cache available) * @access public */ function get($id, $group = 'default', $doNotTestCacheValidity = false) { $this->_id = $id; $this->_group = $group; $data = false; if ($this->_caching) { $this->_setFileName($id, $group); if ($this->_memoryCaching) { if (isset($this->_memoryCachingArray[$this->_file])) { if ($this->_automaticSerialization) { return unserialize($this->_memoryCachingArray[$this->_file]); } else { return $this->_memoryCachingArray[$this->_file]; } } else { if ($this->_onlyMemoryCaching) { return false; } } } if ($doNotTestCacheValidity) { if (file_exists($this->_file)) { $data = $this->_read(); } } else { if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { $data = $this->_read(); } } if (($data) and ($this->_memoryCaching)) { $this->_memoryCacheAdd($this->_file, $data); } if (($this->_automaticSerialization) and (is_string($data))) { $data = unserialize($data); } return $data; } return false; } /** * Save some data in a cache file * * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) * @param string $id cache id * @param string $group name of the cache group * @return boolean true if no problem * @access public */ function save($data, $id = NULL, $group = 'default') { if ($this->_caching) { if ($this->_automaticSerialization) { $data = serialize($data); } if (isset($id)) { $this->_setFileName($id, $group); } if ($this->_memoryCaching) { $this->_memoryCacheAdd($this->_file, $data); if ($this->_onlyMemoryCaching) { return true; } } if ($this->_writeControl) { if (!$this->_writeAndControl($data)) { @touch($this->_file, time() - 2*abs($this->_lifeTime)); return false; } else { return true; } } else { return $this->_write($data); } } return false; } /** * Remove a cache file * * @param string $id cache id * @param string $group name of the cache group * @return boolean true if no problem * @access public */ function remove($id, $group = 'default') { $this->_setFileName($id, $group); if ($this->_memoryCaching) { if (isset($this->_memoryCachingArray[$this->_file])) { unset($this->_memoryCachingArray[$this->_file]); $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; } if ($this->_onlyMemoryCaching) { return true; } } if (!@unlink($this->_file)) { $this->raiseError('Cache_Lite : Unable to remove cache !', -3); return false; } return true; } /** * Clean the cache * * if no group is specified all cache files will be destroyed * else only cache files of the specified group will be destroyed * * @param string $group name of the cache group * @return boolean true if no problem * @access public */ function clean($group = false) { if ($this->_fileNameProtection) { $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; } else { $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; } if ($this->_memoryCaching) { while (list($key, $value) = each($this->_memoryCachingArray)) { if (strpos($key, $motif, 0)) { unset($this->_memoryCachingArray[$key]); $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; } } if ($this->_onlyMemoryCaching) { return true; } } if (!($dh = opendir($this->_cacheDir))) { $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); return false; } while ($file = readdir($dh)) { if (($file != '.') && ($file != '..')) { $file = $this->_cacheDir . $file; if (is_file($file)) { if (strpos($file, $motif, 0)) { if (!@unlink($file)) { $this->raiseError('Cache_Lite : Unable to remove cache !', -3); return false; } } } } } return true; } /** * Set to debug mode * * When an error is found, the script will stop and the message will be displayed * (in debug mode only). * * @access public */ function setToDebug() { $this->_pearErrorMode = CACHE_LITE_ERROR_DIE; } /** * Set a new life time * * @param int $newLifeTime new life time (in seconds) * @access public */ function setLifeTime($newLifeTime) { $this->_lifeTime = $newLifeTime; $this->_refreshTime = time() - $newLifeTime; } /** * * @access public */ function saveMemoryCachingState($id, $group = 'default') { if ($this->_caching) { $array = array( 'counter' => $this->_memoryCachingCounter, 'array' => $this->_memoryCachingState ); $data = serialize($array); $this->save($data, $id, $group); } } /** * * @access public */ function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) { if ($this->_caching) { if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { $array = unserialize($data); $this->_memoryCachingCounter = $array['counter']; $this->_memoryCachingArray = $array['array']; } } } /** * Return the cache last modification time * * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! * * @return int last modification time */ function lastModified() { return filemtime($this->_file); } /** * Trigger a PEAR error * * To improve performances, the PEAR.php file is included dynamically. * The file is so included only when an error is triggered. So, in most * cases, the file isn't included and perfs are much better. * * @param string $msg error message * @param int $code error code * @access public */ function raiseError($msg, $code) { include_once('PEAR.php'); PEAR::raiseError($msg, $code, $this->_pearErrorMode); } // --- Private methods --- /** * * @access private */ function _memoryCacheAdd($id, $data) { $this->_memoryCachingArray[$this->_file] = $data; if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { list($key, $value) = each($this->_memoryCachingArray); unset($this->_memoryCachingArray[$key]); } else { $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; } } /** * Make a file name (with path) * * @param string $id cache id * @param string $group name of the group * @access private */ function _setFileName($id, $group) { if ($this->_fileNameProtection) { $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'.md5($id)); } else { $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id; } } /** * Read the cache file and return the content * * @return string content of the cache file * @access private */ function _read() { $fp = @fopen($this->_file, "rb"); if ($this->_fileLocking) @flock($fp, LOCK_SH); if ($fp) { clearstatcache(); // because the filesize can be cached by PHP itself... $length = @filesize($this->_file); $mqr = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); if ($this->_readControl) { $hashControl = @fread($fp, 32); $length = $length - 32; } $data = @fread($fp, $length); set_magic_quotes_runtime($mqr); if ($this->_fileLocking) @flock($fp, LOCK_UN); @fclose($fp); if ($this->_readControl) { $hashData = $this->_hash($data, $this->_readControlType); if ($hashData != $hashControl) { @touch($this->_file, time() - 2*abs($this->_lifeTime)); return false; } } return $data; } $this->raiseError('Cache_Lite : Unable to read cache !', -2); return false; } /** * Write the given data in the cache file * * @param string $data data to put in cache * @return boolean true if ok * @access private */ function _write($data) { $fp = @fopen($this->_file, "wb"); if ($fp) { if ($this->_fileLocking) @flock($fp, LOCK_EX); if ($this->_readControl) { @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); } $len = strlen($data); @fwrite($fp, $data, $len); if ($this->_fileLocking) @flock($fp, LOCK_UN); @fclose($fp); return true; } $this->raiseError('Cache_Lite : Unable to write cache !', -1); return false; } /** * Write the given data in the cache file and control it just after to avoir corrupted cache entries * * @param string $data data to put in cache * @return boolean true if the test is ok * @access private */ function _writeAndControl($data) { $this->_write($data); $dataRead = $this->_read($data); return ($dataRead==$data); } /** * Make a control key with the string containing datas * * @param string $data data * @param string $controlType type of control 'md5', 'crc32' or 'strlen' * @return string control key * @access private */ function _hash($data, $controlType) { switch ($controlType) { case 'md5': return md5($data); case 'crc32': return sprintf('% 32d', crc32($data)); case 'strlen': return sprintf('% 32d', strlen($data)); default: $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); } } } ?> phpgacl-3.3.7/docs/0040755025754300001440000000000010476665052013017 5ustar ipsousersphpgacl-3.3.7/docs/translations/0040755025754300001440000000000010476665050015536 5ustar ipsousersphpgacl-3.3.7/docs/translations/russian/0040755025754300001440000000000010476665050017222 5ustar ipsousersphpgacl-3.3.7/docs/translations/russian/manual_rus.sxw0100644025754300001440000016103610270765617022141 0ustar ipsousersPKB119mimetypeapplication/vnd.sun.xml.writerPKB1JyYO O -Pictures/100000000000020F000000A2B73D24A4.pngPNG  IHDR430PLTE"""___}}}EEE dZ IDATx]=R;N6쥄8\ (n[ ]`NoSwwfva/oӣ_F[Gn8 UiI8ĶP\6uPk;EwB*~RQvGMhc&RjB6kz4 |Ѐ+r"iWK%[Ѭ} ԺNcz lnm[h  8ilhB\vO@"m+!# RvmYQL0P&8 @s92p-$@EթxՃ`n^#;n=5S 5yMb U`h'A P =Q!hM#G>=Kцh]w;hR $R3W0 $ZݙF;8)%E@ł089Nd:Krb u !cXqRl!a+?)èHz%YҶmR`e4]+ qڞYVNÜthMǦf̃ C"II[bG@M>g+ר@ 1``veH_@@"! uPiXޢxx)[9N&# tx0#*d-+)A~SV\ մLSX]1ҡp "` TQ52̠\'n. ב1@B:2&@銀ؘte @ΙkĦ˶YQN 5"ah L(g1JR 26 IGQTy P줒J*ר@Tؖ +_eٖTl I 9*aW>bcҥhIW4t @%]1.mvJ*\+E9dҥ;b)*\SKyrڏie5޽Fԫ5*t{ SG, qMc<* K'Ië:{z sBy 5.#JtD%]u@@\>(3E^OD:8yGOpQ/xO©//Cr//D" onޘ(#v{5q,ml1@Jt' *8xk3F{FGG>::- ;x;NfyxgO( ᓓ€2c388c| AiEU.l_m?K|\rAs?|"6hm"m*y,y1'=>5M&>< I=Sbx0.5{ 3xv~ qD\ #M^(barNݴDMZ<.E*1; =% ;0L-6 F MIDL.E@\sga65P+x+G+#/E\Rh~Q pN ]lWĕ]J쀠1*g39"1VQF bO#ZcYFt4&d1%JbHwFM!3dpQ˥=^J9<(bf4}9%әQ5@̦"5IENDB`PKB14---Pictures/1000000000000278000001860A717EDE.pngPNG  IHDRxPLTET+B bIDATxKv(3#34|NVa=a-!Y'Y$_( UoFھ sMY < x+~>> s? `̞3GcxW5W>?~4ÃY|oq@[mG5nhjz#Gl PU  uOxJ >%o:?C9jv>`mb7tíh{fxmi xW;Bv«0x5'ۚH_0E<.-"<ɳ d mm h[{8m޼$O^]0 )1$Xmvv-#^Vѕ>OjgGn>ںtY_ۚhk >Ømâ Zݟyn˲նmeFֶx6@Gʉ|~}>x}s7*|@5KUYdy)jEKWQU\Oobvʉ\YZyە| ܎#vr7=gQUU"fkk,=vD+s=v_XtU%V£X>*>" [5CT^._~`[6=pЎRUVҪʐ}T&w2x2 /S^Ux\U1^8=wҝ M-G(fvzvgdu/oNUrJ  oNUq5"!7˥ ,Ki'MXn*{sW]Zh4gWUYZۮU%\Uq8rv:xJaŢF*2)n9yU/|^»8=TU.,1׿TުG9% 阮Us1]Cf[U֬Ul: mIZ j*vp_ nr?T0`S"uvSAWIUenu ޹y ^{*,we PU&sPU֩*'PU ֪PUʌPU6Uc T9UeU)2BU0hj^JUUi@UyU^##*VfRYWuJFZ*Ж>2#-Tʌ2{kC!y;]o#/}w[X=Hq L(m]iJhkTzUH"炕.w1ȋ$ ҚRgʧɇYô0xwTH5'/|?rn?;V#) 7-S돃w:-4w6 ^!%K2 (TƇQ2w#;;᜷DUMEQBpqfF(6hy^!%ϡT& ga N+QU䥮lad&~"u2Z/*N⎞+< <3[L'gyQ^CަVfE$Ԅgu[N,o*mӍD@T%<OIYRIq^6)j2!U> >^xIs&T]_q\>,QsЇ-A8FB=;<>À.oGx^cx;k ZVz_>gj݄g}3/XuxD&jZʶ8AZ /՞_;4PM.sUϫ2 '@qB<V=%Z m;WD(ۮw#d7H>!kݍ2@h}0TXq=/ZΡt}=vMU Ê𪪊g <qZE?4⁻Ś0p&!T!iDZҋǵkiy(lRU܀g:pRKg:Hw?Qx>xLhYx~~ax0B0-&_kyUU%WD5hzJUG[iRKmK&/VV5>~~>ʭ/@x`x$o XN xM/R{,)v?\UYO_Od|ao,.ੇ7Vep_'oZ0rg_.nƃK{vi.sf-<*֧Bq(mPUeB@iI9_\ Wמ5$tM!,+G*j@x9f,m|N-9xl?&Y*eżWIRU (qm9^g%7:9\,C>U覀Gls/Z pxY?oi3dny! _*J[g4me(8g)n&BXUXBnu:TӘ5 /fW;}MM ;ٍy'z7'i6rÒ#MFkQ1a\јq]YyGyfg= sw~4mIX4b8|fBQR Qs}no1@:7ͽWC;\vGSrkoFn4I~ޏGFg߸r:'{-i7Oͽ oBu='ǰϻ8>=6:Ɇ_ uPSJ5q7掷Y|WݬvO)O>M ȅ) )&G]ry†}0f$뛮=karM!gn{J.Ȑk"O} Y=&z4nq'gaK7bS&GLN"KǖQli9!.8o:>4>%ep㡍\`JE LI•dJMs6Q{hl Ȏ9Nnfצiܹ3dscK5z[Vޡm?ғwOsoNFYb4=r7<N{FMs/ ` wzr_0a"@Nff@zAPR $'&C W9;.O WU=CiY2 rۗ j$' W9;` 9 `0#s`pIeN$Ö}_V/@O`AZS99-Ž̿Y'@=3. 4K9TxtN?^{92%H:c2Ӛ8 JK%Gn s8$aW1I~}KN/Q w zr2t6 e vLdlnȮt&P?=6DO5m$N)Sؿtȑ]V!-5gb)9LJP# \kK+~ӵ KFv]Z~KN[B֔z_Iu%~_v&q깈_茸|&gKB$R;$GɅŽf#uE䈜9㥎fY>G9;3bKvq,SZr#GI1B|kE.ʊrRrLr@\ϕsK%|]W6-ir}hl$|9W"$YQ[zRQlQQ=g48jTd='g~WC%T=Q6\ WԞB{N},soBfUXl@r:Ɩ cHʱ!4h;N/9C3 @@K3r0 __P@ZrKʋ2rOCŒ|ߚ{/ }cm ȁr<ΐ'\ r'#w3DzɽU߽ wLr?}[F޷*]/z,#3rAur nrG \ ]M]rǛ 4#7Or-\Ks&-Y;+;TZ0r&V<ܔy7]r~=nrA=wzj][M[[q^އuGCzɽ9[> Focx2VKj|gڛܙg3ܦ݊ [Ou:f**"rՑSA6 ]\x˸ W9l- wtrf}( rA8Us Fn9΋3pA@8ö2™,Z߰? rlrrr r rYrD Hb6].t ]@*@:rUILC{@@@ngr W9EN2̔g>~apuC_aru/D(-ax~69T@r 9ȁRQI9xgʭ (f!p5ː*Ns窭`h@r%@` 9lurxzُy͑>D}?MINS^Br^'` )9}r6tGN~K>喓SIyGsXn0{:ꔖc'ș#>O^<9ײCNLсQ7l髷~2 'frf28 3UJ$#' Q=WYu&͹z9Dlz.qfk(s68MNOQ9˓#uL2Ln#r9 |09sYt47u7"'>%eJ?svfY7swH<ʐH=@r33YM,br}κssfUF[IrFl\FH'ǖs'llNOn&dsE9>,m3=g)m씴Ȗ\ sBmKbѓϐKs60u%٢PR..R4--NtcsDH܇B998c@.L_ή̈?.#lڪ\jRdq%3v ȕ` |Kw&3-)Z=*u ߁{|wg\:ɥ=kv<9'q>AN)+#܆|o!TKhҍgR&g=QFC髖P MĖ{_-t(-K z# +Pȭևn>999sxN8ߠ_y{+D( r r{sB@sTA3#Ϸ:ܣQ0g\OO."&' ȅ<9aw!Id֟{rڵlo듋ϝ9_ 00m•ɹⳔ.9ΐQKs R]V&U7Wٽ$9{k&׆ἢbrf銵qi#Bi Us\L}Nr{Fi~sʫ@."sd|9ʒNҪJ*#L{Nl$ƖcK"NӗŖ~s ▍ٴF!:>irʕ/+ /iFO[Ozk@!'z!rQar/soo6M.mҴ(-u#iSݣޭҒ xn&.hNEx!rKNŷE}(:7 r ãcD=NF NMIr[y!rg3Mti%з"wrc҅B_89m"r dHϽ'g\Cξ>s &빗"g9G OKPA'$ 勐kKC#eecKv#+e]X=:V0ڰCJV٨"GNs:T,rr17$gfyrY19X8MsB%}6\ zw 'qW>rOD9rE= ?9r'go&B-)mρ81UJ'Gάr#R΂ȁȁȁȁȁȁFkʾâG%O j%5eaEkGU.ȁ `kcόV2*&URD* W+9dcM#GruZA*kYtĖCȁ@r/BSv'8ġ'Xmc9KtLU6__Z+)BO|S=,9!v"=Uan:_ Gh9G. |n\#5O!VV뫒@M%챓EhJ\a=79"YX ܘFP0oaiǎGt2D9T@nA=q82>;e9sJĭҢҒzN%dyJ}MecK5;$sŖ*3i {ЉbKWLz' =ɹzx>8q3Y,K8: W TG%w챂 ĹCHI3 X8|szVuk ɱYXAzsx~" 9VPrr j_^υ?Vo=UO3" ㏎r+XWO\AO}(j@O4U]zYrYߕ\OO4U]z}r?h=+qv"t&ی\^O* 7TJA~\}&%"ɭ r r r r r r r'=qġ'8l@Drk&=*KK艟&rU*x9艣r r 9Ƚ9Lف8 zc1ȁȁܑ{y(JO|)g)鉛4;RrOS,7H[խ'(Fru뉯CnE",F\z+sr`4G?Jn==A9p_R,7θ;F&Gֿm9[[CO\k?-?=O9cP%CXAnUr*i\4?D(*yicmr+L9Ŗma܀r G+*\܈rxr ȭ܇R])ȭv$mɭ(s{\9PW U%" 8I@n ܘ|&AJrC!FH!9<{8 ;99hN#LU@@KA#`r@#|R#9h&rUF!FH` 99r4B*99+!^[\Cq r=?V|HqCȹtre!~/LνܸFu.|,I$rg8 sA'#Gck{qJK#/ٙKHAr!9}B_1Q4B(~Lqi)O\95Bv&'rb7.$4Bvh. :!2D)9ݞcFbV 1@(zyrjg8Ƚ<9^䞥'~>rR0e;rO?%9ۂVחb{ȉf*"r{[QOvw%JOGwF zg#ʖV?)brsVQIoVOI.ĖzhLz(27dQOއ9%=V1@N{QO¬uȵG(?h #={ BOθ_=Sz q^'W'Kns_?SmA- IǏ%W'.'#-Ygɽe$x;ӛ;A.ϖI #'cT(/ݽs>W'.Ns*'+r{@伞x\Q)-LŖJ;x$23B뉓U0ݼ9zYA~"GZچܤ$8WH̽_ r r r r r r r;{J69999 GaG0{%rO֟AgR9<YX9ɑJKARdd䐍5הUFܪk07sd4[r r 9Ƚ 9L٩@vra׳~=z6C;}3ՌcsA r rg#gW6ê~1dI HrG6HGwA}z!h0g,"%:3K7;9KGR '^%'JJL ONFJ"HH.h|+䵲M-IaM:IԈG*g"g// ML.o BA B% *BI$K=P\ M(TrXú1JKQoC.-U[rbK"ONRP)ePU#ΘͫCĖNu*&HO<碋͵C1Lz  na1Nz═x=\Bz' 7SO X/\An}r2P뉓 L_D{ ;ԡ?<9  9 ,i=q{rۑSj\8KNȕ8 7BYJΖ e=ga.8q=q7 rG%s깃2ОP@mRHNiNN!9F=SF(09;:b=qh\nh4Bj$SC6D!ՓA#^r ` r r0=@SvK#V~="9 @r0x "X &A`BG fɐe\s'9ss0 ` Ud r0@:9e3iŁB5PU: ZpP z-Nr瘩j)TiM-\&Dκ=H*(vZUuꖭ;fjZȲ۴xT0 O&p~j^L6n90 Ɂ~:V][S5)TP5)TP5)TP5)TP5)TP5)TP"P UjR UjR UjR UjR UjR Uj }cBPT|ͺO`7d&*b`>0AGGGb -p[CA5ÀzޛZh~PzdGXfpq9:V)5 0P`Aiܼ9CIÐ ^ę{\:e>r2`BLnR ,X.p%T އ -ygjs<P"'VR `d9C q:4 P,Y:{h mPANc4dzc(T] (jҩMP><٨KP7j?thP*Ae>Ajs TT1PTH I}S3=v{Pzj7jש0HzM}{ϝ>? ]w %ǕrøL&J MST*KEy?Ec(jO\IpA@CM܌PG 5Q{8BդPBդPBդP?3zMUkϤ:U*P5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)A5qJeʭBGiI-"mjZ% *(Ծ^8 %oE\6>P96ėp*F:H P9T A&:fjjBm PTuC訰qd^PYRiET:*WjCGVVZM; `j!tIq RP`^Wu^vBTp*.:UBU UBU UBU UBU UBU UBU U*P&P&P&P&P&PPw?Ɖ+*$Gbfk Nl<.7'7 qꚡݡښk`P{3T(rBMP=#Q|,38ʏI/ i# +h N6,>k<:Ͽ;6C纹~b;NR3zzmgq8:c Frt'V@@0 c1HA*F/˱2i O2>Lh< PJYب%lٞlYP P#ж$# *Vq (ڠF,=&-"*Tkd I@5&TŘm!+H X1#Cuְ.C]-N*JPYs2TWkA>TW$U" Tu}SSnj۠t*PCZX[S_?g ?K?Ө[{n6Sp>$r $l~!P#KTU>SO96Z3bBThwa u6I~(T.荁]W@KI@Qfj M *|6TThtї7M !0> (#G|} T#NE-w) 9?7FtOwXϘ<2h!cxٲlg|:FlN6SQ~kPm{(+Ma=L<ހN%!x)Ce{B) ƥ*:ءںe1:Rt _6H `swbE~NgPs=(P0L*-*][;q2Ւ^$B:^%맶Ϡh ;@vVmM{|qLQ}:eUeO礋CTM uxPLeAֹοMǬ@oZσuw!@B?z1_r W kY//炩ݥصZhO0$Ie#nv;?FAfs6>Qp.Y S;} |lgKKsH&րv76Cmu;4LE{+#V jwFyr/*@L.l67A_ @uYύ66CRuw[AMuj5;:Pu[w/Ct2䆪?R?զ~jѝJ R?՚oLGd})TITvT u?ɬ7TT͕*ԃ5RkToS5~6YH:NZ{ER~nlB5DBOP }x>R'> {_lCfwBq<%dOG 7w}!yn8]2T 9SFd 6y ¶  P>ՉJ,Prg%L!s8EW-$2WL,P;<0v[T[ҩnNqA,ldmj#MWnM]_cc#Tp~ZoR? Fk&bPqZʿdPB kd\p}@ uTPnMwCݫPfTGÓ=X!Ub-oSaXg/ڟ]EA9܀( N~DVe>1z"CSMPJC'T[2뗍IRӠꐅ2eIb&c*:ShPMֿ?MNu-[6s?=~*ROu)lȡ2|4z(PGF U:_1M P?~?:= IgP?:P?P}}BF/d=?_|] EJSZ=C~SX=@դP/tY=v(mnb ˗/ox;gԟ_[ #_ .3jPwO?[_cb!Ϸ|.lxPH(3?ρ3N+Bzmg~K*-l:[&i{+p3w_ϐ9THPğaBKXSڭX$ !Ew뙷2v/O/&/WVJӍ,[*9+oTjA xzP~?5l *I8,!P39mTѩ\BQA ?IR_0V_j'NFYDgʠvn#w#[$Ԡ6ьTz?'''gOuB\vZp a\>ōs>NjP7=B= UBUcz{cqZzکӇAnaCU#OP/w)_ z-x-f~}k5&oo/o/VE?NXO^N-pk*_1ǙAEI}At r8[z#|!6$ۀ^%BτaB['PJ Qy{UelARL/ʋ^¿3o 5ˆ]V nJʩ@3p8G _ga 9ٙdݾL(gw|5>~B@%@ Jp5 9bPi-_ġvԳ/m(C\0ɡI\9Y@8׳5To_"fU _U QRC畿i@evz$~ 3aCSHKOK@BQNj}a4r~P/kX #Rfs,W~ zY HP5 eZ,K:S W^?02A]{KgjZP\^^r*I,s>&PQN$y":W:dԙP$u*өY2FNRC5g;'}=%9XPPӥUN%OWP|eP/1($t犚|ihFt<שWWVDVOxۂN^&zB@{>Xgf,+6+ԡC zrPTRTPXVuީmmPWDtUa1v:B:vY StZFW+k\L-s4uWڀð m[XY.֢ő/+~+iH*Vw iy6Ye. !0I%B<+ꘗVhd@ bØ9,@?sɖ!K^@W4q4K7u쀠l(ARퟑW 5KՍPwCEfx*Dc T{%Tg@-TXͦuk:/v:5ѤSЩ̔%V0`Cz)ƭt* :D@(*kY=\Fweڰ"suR4oXPN *0iC]7Bu] j|6eK?zIPhHXRuU-RH*ô<2l S5`/f *C?\)IRcbehؠwO]S0 8ghQ&N>Do%u̒.tZ d^WQ rjI\=KT*ԤS:~Q+/p._af7tK^,AfE4^J] uECF &%B5Wz3į5wE*߀VTJu1DN]3 5TU~Qބ"p WuRK_~[?i*ܫE."kuK_QsPS f}֩uF9UeМXY&-x,ʙs6_sO]%{ f˾NU=.MYu&UUŨ?W#&:kzRw|u>o4Ң5o%w+>a,|M3#=B\'_AkA)X#H2Tn3 Е]*_H:|+B{5K1_1-ijz0.\3Z@?:!>& raFZe|>A S7\/WNnR~@͜oֆA8DIZ1?ݮķ8zQNޅHX66,1=>Kj0Vq;"J)Bv/T󿋩tT|w#Y ^ތxoRƒ=Өփeԟȃw6_=Cjp^Px7zjԿ$5 H}]:zfԟgT$:/5)(uF9>qh(<+ZGQ@&^+ sW+q?jEuNdpʙ W35F]!HA*o9(80o;ЇҬuqM?h?P㘽 ?U~5flPWS//+ɬFjM  *fzSGA=Gǰ:KM]q9zǯ4@pq~xVE?xNH}7#n:6svbqw,TNj ޏ!72ΌTR__0s@Va.hmì{6Q*n'].P3¯0L 3Fy.K&^;:s;AN4\cC"5PZO;ch,2Y΃4ҕLc /s4?/z{%O^_%+&xQBa_~/҂d 2*bd TZ^3fz{e^"% 9 ߧ_a!lSY~NqYN$|NNp8 4GO1Nb@,N¹F'{@RI/zr¼LaU TE={?-5{;Mw §*m8TY=uI8xIDAT/_wͧb[>~ȆRbԣ QPob:f]?n Ÿ@Ȏ G1m,3 Q8_v o2anlqqh9 =C@co G?"K_\`iN+?B63o6o7BuVy(x 37@(Ưх&A-d9)>Lh<՟NOK HQĜ@yT`i;-LARlq/JPT(Qu}L*jR"ld9ɵ%g8p/.p``PCCU5TtKXiToSV{u SMPIP%,9NuTj\T:I:S0zDzLB :Ʃ)TڨS:}Ub?tngDm~Im]Ko0&s?B?:JHSx&ɖC yOuaX^m Q@$9V}dE }oJ*~zFjMjzFWӟ:Spيf ŽB+1AuaTn5nJy6u5&ۀݩ qJ\t ]B6xɰlyL;p@2F4A܃,I-c BVػ+ԘM,G j́n eFMs綀JZ \T990F"uj+It2jK7v=7FukjK:mS[ntȠF6M6+f6K=WYJFw'^!Nz᩟&P9`ctdQe jIF u/}78_nL*q BդP&:SxY0~QvuFi8QN_oԟa"N_cԇ8>z~| 4@.?L{}Gs{>I|w1ڎE{p|Cp|'u>v ?HaO$|'X>?,DD"?@W7;p}GlU#Bߵ;k~(ndMi0uMjG 5{PYU"Z&f ]7Y'.,x]5fܦ %CЩ,:&mr GXc/V`<,,.Yű?9IE+VPɢ.v/All2l_,궦L@vtjg:uWT,eAwש,ˀ}N d?JBU UBU UBU UBU UBU UBU UBU UBUc W7WẀ+i?ʮor8^@Rm%(kGDd%ʳPh,fH2T?|+B-H-?cA~l$&WV"ʚ:Br PE*RS_d. u  P&.MQtPE%P "T@GAE*TM U*TMP1Cc_,HF "يu \WoyM@i@ܒaKii,uDn8Pi\['d)kcgTkv [BzF bo\_C#1bYt XQ$ m,q#TkH.>MPDleCl~n(qL:fz!R )@8"x\ c@dGu-s $5AqyPs5@FɿdTsӕNĵ Tx?9H6R0xl{%P8\1$w y| *-a*u+԰]iTcS8y w("[.eB#T BTn 5T5P9} TeX@Ud8YT5ib UNutkөW7.N 3*[D6P֩N:;Isj\6ϱY$~h65TRǞbHˆAeJ6 qqߵ[zj7Fj .4&S02O10C(PS?AH+P:u)g6U&XOOӪCzV~헽4jP]ߦZ?6Mӱi]qjIU#Z'6r۶Ľ^Ŀ?~?*zW+TWjյP&NmLP7~ޠҨx C G҃|xQX-"Irzr&^;[zxGȰmN1CX}Ӎ3y0׋)AMVs`_vQsXF6DwrYWdʥ7_Fq\v(< a2^OYq6\]DE]Pӆi얅LoтlKZ&&ű Ylf{Je)Yҳꕡ%3Yf橮)\[d̠2ZAoS -ȡB•9(G;@+f)߬0be6CP2ܔ7Ig$ӋkC \Fi2T63T@jS3ò1 P,҉ihe*1C>oܘR?3≖Åf?02njx6=QDV;4@u 63FٲM U2Kj6h͍[@u9Tȡ}ܢYҩN-Q(Bѩ77Ԃz.TV, =vjupLVQ/RJRxG!rG\u?̊w-Lj,:?bOmӏ$[֩Nnizl5#zWS+w2TYꩴ5,Y??~*Tۓָ#xa~m2C\S֡ߴ!4BP;Z- Uj/?o!IMIENDB`PKB1pMoo layout-cachepkP ,P :P TP 'RP @P [P tP P P P P P P &P =F2` F^ P CFq P JP aP F 9P P P P P P P +3PKB1 content.xml}ysYr)jY^%qFjE1ִncBiFH⨏5G;vxa"$"_a>ɾG 7sk]wܝȻAsy튺Ν;SwOp[VNRaVF$WYt}z8#5\5~x\ Ef1͐ +|խȇ j~v1\vaY{H2pZ}-8z2_X/.x|u޸V)y+tVtxPVxFNG+&kjvw]rS-{;enRq ]MNnuƶ*Blݲ]]6j1W/+x?6(}k?ʉ[a/wy;o{?/7+ޕfYث7V.By}'oV|A83[0w妪6VJkwknvH ۩׋%N j7{vI1c%5%]Z/ J҅;<ޥ9`ZVQ,O _WS3%e9W*n IpƨcOaj9 a.vs'yJSم%}_-&},.f2]2΅y)q?t>zمsss5!z??׍ r2\ŸAR5L??sIVkPޓ,V<}5 ?_ԅ1\=BvV]S[|eGPs3?VJ`^c!7ϜC_D=dGGcȜo&@0N9r"x/fff\>Ih~ŭFA/^}-Y%ֽ?sC۷gRϛ9&J`plcLOM11;!=#==svnqn#v{dSh=c$ [K#q%ҩx? `@2Iyo&UB)kE[.u-N--Νw zگ<[]IV9_S$2yD sSbedx]B0?\XH?/, S3 ct Usܶ21mϛZ%7@:dgLyu I۹RQ8`~|N=p&4 $$7`.Ub8J,,,[-x,ܩՋN~#v\NՕ2H̳]h IS4>TZ*M=b;ʬNqz֒f^{˜V-4z!~)K0Z=s g%t|4Ӥq^{OuB44ΞDXP K̞jnXd6h27,2{gaiܰTٟN憦S'sC)g>N> M e~h:%2?4rgCtٟR懦S)C)gNNFS懥Sg~NIvB (H5V%f"ø~R^/T'k۹u ȞBzkK%^ Q][*[co6+? Y?Xjj[KEBz}|NyP!uU,Pv?5[ƥj!o|ڨ eZiFnk+walV=Nd zr/*אw!or`zU,sYfJtB !𭾡TPUY)-їpNJe{nj};[۷__녱?kBĈC6=?!}z 51PAAv i|FuYL\{DPbQbUPbv*8j5XZ\1J, Jd,ňQbPbi|v(*(175J{5X5-zUPb~*͎t٫ :]M.M.M.M.M.t.KS4KS4KS4KS4KSt.M.M.M.M.MKR4KS4KS4KS4KS4KG.]L.M.M.M.M.t)KS4KS4KS4KS4KSts]ڥ]ڥ]ڥ]ڥ]ڥ#`O.M.M.M.M.!ijijijijijr4aaaa#a-R45LS45LS45LS45LSt$ ӴSjijijijija6}J 0M 0M 0M 0M ӑ0LӮOaaaaa:ix'ABaV,ot!(ӆ`Sl]Y̶3ٺ ϶' 3ٺȳmVdußmObug1g2?۞,g2b=Xu e =Yn it{κOW,QjXjXjXjXj o`````C1;֤XjXjXjXj0 lڙE5MNHD&' aH49!Բ&'ɺL8f;s[ c\=G7~'@\/l'z VmjEq۩W^\/j_Uwyk)Ruh/ֶKzX.L [*UJ7nE~sVswiIjA^1K1ԉ+x^^gss}ntxi>n7''V y(ynvv=o;kѿo=aRL#znMle{boO)xΓ΃~Cc+q`͞ ڼeEn?oEx~H.qKeZqñs3>W?jo }k`S]|6x˸xWx>A,ش<$sHy/~9X?8|A/C^Я{ZB<RoňO~>rS1ɰ>`L8XvڳB~jt8ų? uO2㎯|pu"(X?jCQ0zoPHik=rD!}1>/tK9r j_{8ؿ/H¶700N[aW0ŎliB` ㅁVq eda"ŵ_ +|rcĹѬ\{7'?}~ZٮyisJ1 {rU Q],;bākz_Yo/OO25=Snꓷbɘ1orsj;ʅ;cCEKfd3?s`?[Av lU(׽)TkB.{KmWrv>W/䗽̜7͞[^XX9xdY#q:+<,sc{1 ǐ=+'B|tN>gPӬK_#TSM1#:Zt7(MlZzx ?[HnM.blϤ^,qbÜIAup=;]vU&'|p:d(N\@lCNcDp}=P FeP.y/e~b ZP&?E@;@x]πWb_D"2dma-1XDN4?ns8`wvqlà@= ΀Ph $ 0Je!m&1'~Z䖦+;(ccR(x0utĺ&C&8/R#1LNPSOJoV*B,7`P4;8C)WT!e:@r /A b\ B~~ESbb%SFndCO- `$\HY)SQDvd x; oC'tM)ن% ~a"ҢtW7!*[Jq! ) T}a }ŸcZD}'A2#jT헶N ]9Cdؒh shlT ]74n822nD=]ΥA<P9 O)ȐG1^DK1DLWb e~H> L@53J)&y}[##El.n|Z)k0K\tY!PA6'SS0J)vAC 0@]t;`btPF'x\\F,a{ #&[?5 dHp<zH1~s?,eӶS8*vHrPsJc{b 2Ž}%aNEo1T2X0C 5r(D; j/ymW'YT6iNA퐈BBTVJw[#3F@L ۯħA~}(f' !0p/VLSeLx2>>:ֲOU$SFQi0 WqqM? l_,0-oԪmXKȆf-˄͑Ĩ Dm<=4jT {^{7ŷx#/؎\{M2z:xpZ5?TmJߔ&*uf^Fyt_?n㳃KxL ۻ|Zrm>U7-FJ:,\ I)ngܧ:<$({PjPqËFd,y0:lLNmtp޻{^1f[)38UzGQp+'*ނҸ(۪gaޏ!aKH0vLiuG 6əZwJ)ݺdb`M)Ap՞wdO*B;Ʃ* cUDa_WwGdF\r;٥ԃzzPPȪ}1υPtmY =~ 8mGMԇ_2 ]`ӇμM6d\vAxHS[&iX ,az,9L{0⑒w2@#lLJw3' 5{* ]6"&ZЯHM5x m"fגIO;pAtTbE&h a HqOqS}7V`֠;Z%L7ztIsslsQϖBoʯ2U"1ϒ!leϪª̧ jr G|𻽎 T>NI _9D88o{bwI}ŏX5Rpo<~txm~DlX=K;J\z;plt RE*ٝHb/ZZQ3F}v6ls$8F>Cފ"q5xlTxJ]wбVǐWxߖ!.PcFL>H2 6|TK\*BMj`iWDo3Ajަ? T>"8EhϿ{7 P X!I'SSX+?W~߹c2!K#bJq(*kheLMhc#KPd_D^rb)K¿Dja/mo3}ˑhl:܍`SC4AUA̘+PbY&ՕFsZ ]\8ᴌZ");,5 ?=пg?6{$DBW|Oq7+&ge6X!m0;]fRqkhcHUv.G5}$1_n₣Qɗ̈PĉLiRrvŊI:ζ(z$,=%J;JAK@GfMnvT-06ML:@<bXocC x 0<]IFsy:&M\l8}ʡX@SJeyT;SzdvHo(vQIW<bW4.Pu֡oq}½<-X~񡢳`:} =ɱ؃ɡ;g`rMh$i],&i](B" 42ap̞"V- P#nZV9xNSԄm+ϩfOkHZ\ :<<ЂvE*O&.QeFp]to`U-E-%], s|oB"lvSrr_#԰†̴fTTx ~'tOG5}G;L郩DPäMBxJFbZ0<]}Ά0(U$ؓuUWҤ ?XdSS91;;}BIR@SsZ;W)I'&9ǀQ|UM¼uȈڰr'K_+ \3X6V.5z&IߣOUKˆ,k0G cP%عN`[FwqQ͞ah`^Ob@&QP"vGG8*Fu(mYg|0m=ǖ].yߓ'HUezbxeoO4SZ,_Bx mi"9*V6P|BIXr*x~KKo?KOeoc69p,ܰTJ^ V\PySYT08H.nO~`j| G]®6hWղ)` )A`\tUFI.UF V?`rjUoب8-h$9)`@02USrU@L bk^y@H6R՘[rDClcKj`j)ضqE=Iܕ9, ^5b5BU]ET3 (3ˡSb)GVɔ)qcJ F=WYKe%a)u9̯+.nX> =6~Duũ|Sq8›lB+?˨qK]wY?KJh9WUO,"]$ rZsW,]VȟTgʲj0H ԰L ԰L ԰| boc[څj'pkJ?.H\Xpy Vq;5IoI8C,Y iEIڙ7o0nN ӓ\F@nSVTy#:2 Mh?ݢPR5}0뱨#I3Rf5؏Жj¸'(3t-)Z.8ѠɘXrƜvR1(z%m Sm &.^ٝ!C_U_ {G;{jK'#>-b7馏k:t-K7aD5:X]c`X#Jpr j$NW/VP^4Ϲ353(G=bOG'q.`22QW=8:KmBfOɱyǂ2nvnO%rqC [3BA T쨀VP-O&Q"F³oak'V&UZSRɡCJuw~'䯊ҭۮ~R[g;ѿ8ڗvj}嗕|@&~m{Lg̼U#ZY<PrafS=wdydΓ һILű~Pl8~aKlq?s1LBS< }.cKuЈ.u:J.+*O*[f1^RcW=:Ns)kƎ3=iY媕[zX)g >47n9$ lؚW,{k;ex|sи WoVX:ț d}VJ9}.'P,׽W/_UGdK`l}91#}-.+I"]_< 'agr/u8Jr^TJ;ﮮ^~/@m^ÒV<>SU_Rv_< o?8s%W߿v],̟8o\X6O_2!Kg+qj:S\,Üt̆U 4LQkDr ,MG? Q83P,|8rR*}Rz5:8ةePj$PA j#݀]mMJ"]? ĉwd1z̡_fϨZP5:J|eGڈr"7 CVF3~IvJ.De.޵Lb @;꜠ 5es/lr?7B[ڷd^)tRxÀgl;B>q,$rx&\sId̄@F:tt@;}*M| mks1jeߢU:)jAݝ߽I!kJM{Ƙ FumN6J4@~f7-瞩YCTZPUT5/ĜNjZ3hm rpc%֤0[qΫU] 3a7=]650' FQ #j^g un-/Ց}2@}^{? `,h䈇HMߢ*fP -LK)MBPk(=G滇G 'O0gqI<۹(װU]>eL1;Alͱ,#5`A}]hN=vmҶN1,nfrmcČ7N4K)KTB_{hM,ia@ AeL(~sn]|,lSYXw˓>>ȱۧ?L;[wURET_L.ylV|QiGm.Pe/`u-dAtJ}P~"΃(X,+s*RQ%]*Z764l><dKdum7,s8]6j*0荾6h#-0F-~La+L6\\7\tZz!XfԣO*S! L̮fc q;W-ezaRCh܊LgRu5+<*Hx40 "Ҫ>OeG/[7S`s1ՙ5v3miAI^\ŋstBD0fj"<⺸D-QЩ:%[eň4{c~/kyBJnb=newO@eӢ{BqK:trPf.BO˸\FW0VtXQk?bE {,c2ZBz2aMQ)5|anE_P%uh3-SPCI )O.#CC=ҹI!B0 (_UMGB0u p ECOM$~5(ࡉ5cѩo&xږuž);j)X'cC-1{ږ;F%w.baX(~T`+[RPN#mZ,noʝZ¬{-'F^*nuT5hVپQĜYF(UUA"^O80׏Ic79FzijM F~sANn`Cq)cf!L*C}A>n=QPnهH ]Y&=4kFt l9?F5~'3jT' ¬䠗o녍yj5H?}V?(6<еCU@(Am p(P@xbٝ2 0Y1A!b%T$W(¦†MFj򀱭^_nZu<{@*ԿMCfC@;`n c"p=N&VKQ}:6ђsiR 8HN\<,FA(-qpAE*%gnb ք'aaIք1{J`} F17Q|]HۈHѬ)zURGx`ȡDG72IYP_a'9@[Cl2 ŇDL}y쁡ATG`z.KfJHn*00ovH0B7ٴ+x\$'elRy.Aj!+Zpb"N(ᖯ,r4],Wfq]YY2&p;Wmc^/L-f&t.dgf;|}TvɼY(nl'8Vs=},򦰎/ŵz}ZMyXɾ4_.o2]*kʝ cB^^ʉGJJ%DgyinF6?S\6%. ʆ| zsh.#  @GsV⌓8Οڌ M-<3gῳgVf.^Ng,T>G9Ay},oX)1W`*Z'2ĕ%`CN0 LTF` YykcLKqX(R߯*[;Iw@}6Z`FjaԧDɮZ!jd9\5[2@q1/]"oh Rڿb~IdsYxXt£(6>'@`#P%T! C #ԽT ΤaЖ}69s /X&7~|\A40Ӱs\rqi.(-ge6\!i=z⥄B.14f<=}SUt*R( mVJn&;7|tа33ܬSf۲(3 l8F;FH"aA&"t,qtYΓ7pxiO]5.?bֹ$!3GC`)pƞ0ׯ%HײmVp9a1dx#ԡRV :@BhOդ}W*ХSt*R]NL /°,tJ/٪ yܹTiJP*U+[SbI)49X'ݞ*xZn_dVggV1@VY?ίa ![kWo{]HE/S ˶ F*Sn\rMu=Uw._W/}jJLLjk$%|@ .ԯWS6QE6{zԺ2<3֍7n{=7{icHsPߩ 'LUT+ bҧmx+P ig=РE7zNs~QPr#UEf7!^X1Z4%:/ZyF(;!47{bT,_qy{q^~@q&N6~sO2ubgaa 30-sKss}cǀ~6?PYm~B}ݧM}8:'(*!opݏh -Nx 9D ! k1|-bWo:Ra<{aA„y7\^X}ƃv{b)b"}`;,n֤~pv_*XZ[+Wn\0n7&O}%h*eS0#R>+G9H'%䄽HሞTMH4LE+h1WP_|1֢Y&TЄ. !^Q!΀}n^rƱ ،c'jeg -G1ċ x׭b?tPރTON26;McڇIzqr 5j{6"M)v|\2b` C_zYsB8 5+p5?# U&mjZ0g1V_~殮 5?0Pչ3c8#ms'%lQM92ObdS˷qڌFo<ЫobZs_䄜Rb Yʮx/ c֔Z/I%} \ dYl7׿{[r}V`H%!GȣUN4OG'̢^!$lȠ=#n^.>Sl KwPFq7zk>olIC6pH@MF@$5λ֡>72Ĥ_xzncBZЬd05;/λWV_>Opjj5*qbSjw!d`k#ЖDP_!(ZlHAlSl_Pm(oUXmRvCT;ҲFN8'T("w]^xv٫=_;ﮮ /."9}6E{_Su({ EFUlp+zkx082xq [ jmXj{&ZoaX.o&23~aDkugԘd47%4/|xJbiM@۠Yn-o:IG G~pNԃJ^W&&\0F_ay to#@BC qpi˶RW4>`wi:}5pk8hm(^H|p\9 K09Fcٲl-5;'Fn/w\?f~TTƷꅣ(P/?M" i~kw'Dv Z,_vK=H( 7bZTh3"[Q $U~+j|bl zkx]=2F<2 1vsjtP$r [I\Rem c#&Gʁ5ѿ)J!g.◷%3M$m\C\/XXGh嘓`|Q?$в[z[LA]vSaN[$!6&78)'(qP ;/:XVF@*7;#۝pS!<]ڱu wɫ^k8P!yZݫdGdɖ=M6v 2nR5JzƼǨH^hl9v$]Cq(_\( J{yB0|(Yd:|u{鯥Jitjd}"4Ĉ=]Y o ^3r$r ә,Kh ^(@ *8xXQ=b Z H#(*V4KMҊ[rP! DP*T6#3rY^s"d0ei_ ĦF mY|?L Jc>C)mi3}~LwS$1J6#OYQVE07q^{rø|TWϞD5{K?%T M>Vj^% !ӅZ 9+dx A}s^wZY$X rklKмV*TD܋б9Jǹ102۽y]"`;,qVL8U!K Bu +G|)8ݽPxPڬZS˓\ ٬f]Z7u_mHor*U2&u'^5ge[B]kMPhQU(ROOkW?{v$'q$IܫAߛ1c2s4&ۂ}EQRȥg M!B} W &5ד騎it%n#Xj S朼ZTkű}~6dDtVӾq(Exp>_5|Tm[83{ 6X|G\p&@oǔz clXq07sl%B` ʫ}z/PK):w&PKB1,//meta.xml OpenOffice.org 1.1.2 (Win32)2004-12-09T10:53:512004-12-09T11:21:29ru-RU27PT27M38SPKB1 settings.xmlY[w~?+heV&{qk^E"ФAn0'ѳs4!y󫷟&.$% rP30"T(ua)*>|f7T(~ 2hz!jo8<˭ԏAu#go+4tԎLK U"D FJ+P-}ЫPV~A%P_\&|W$iX~UAo/d_ergeӜ`*v$ޝ9X,H7M?uZnfG>2/[3M'ϋ,ekhp2m6)2Ï\I:4^ĠXK`|Ɵ8ę ̻@>XKZ>$3ML:cMV]%oy5{Neuvfv l]4hɘ˳AyD%#͵}7n|vr8 Ag{KsBvL:=iqdן9M;%<ǩȉ_ƃ ; ޶3Lnlgm[zRcf|'}\1Y;5N>\ʩ1kV440B1>1NَSv-LǺ kUY0E, JO6Jo`6LSd|<(j]^b6'mk^h!UzäDuf mvW$"> Ȩ2@]rd& Ceav']?[nu$\SS&ڪ /Zq[;EM_|-**RR%~D$cz:IZzٵ!MG:؈)!T]qY]4pJF|lN1ϟϡ^QD5tŲ55].teY&OEU~Yj5f G*+6 #T? xN>JA Zw/qh_\|u<%(B `g(,^ƈć4UUxKpmX}\:$WME.T2/6?%2~}PKߍPKB1META-INF/manifest.xmlMo@t|UЈ&m0&IM^vf3&;\BRxt˄#\ut0~ })<G},׳@!* "My ZYgPy$(?LfʦDsL=AF_/ DJy*!LŮyP[hM+4)}&KijpJU`JdqKc[CC ḻBmm~;}p0R33M˺k>9a?P&.X]+[TcoJR9q)P^޿rrhn-{D '|PKg>PKB119mimetypePKB1JyYO O -DPictures/100000000000020F000000A2B73D24A4.pngPKB14--- Pictures/1000000000000278000001860A717EDE.pngPKB1;Ñ%"%"-VPictures/1000000000000371000002E684794233.pngPKB1z//-APictures/10000000000002A8000002957DCED6CE.pngPKB1pMoo qlayout-cachePKB1.iW rcontent.xmlPKB1):w& gstyles.xmlPKB1,//Bmeta.xmlPKB1ߍ settings.xmlPKB1g>^META-INF/manifest.xmlPK phpgacl-3.3.7/docs/translations/russian/manual_rus.txt0100644025754300001440000012112310270765617022130 0ustar ipsousersphpGACL - ? ? ? phpGACL phpGACL ? (AXO) phpGACL API ACL Groups () Access Objects (ACO, ARO, AXO) ( ) Access Objects Section ( ) Mike Benoit (ipso@snappymail.ca) James Russell (james-phpgacl@ps2-pro.com) Karsten Dambekalns (k.dambekalns@fishfarm.de) (kuzma@russofile.ru) (http://php.russofile.ru) Copyright 2002,2003, Mike Benoit Copyright 2003, James Russell Copyright 2003, Karsten Dambekalns Document Version: 672 Last Updated: 5/20/03 - 18:55:08 phpGACL ? PhpGACL , (, , ) (, , ). . PHP ( phpGACL) , . GACL phpGACL . ? PhpGACL sourceforge.net http://phpGACL.sourceforge.net/ ? PhpGACL . ADOdb (http://php.weblogs.com/adodb). : PostgreSQL, MySQL, Oracle. phpGACL PHP, PHP 4.2. ACL ( ) -, - PHP, , Apache (http://httpd.apache.org/). Mike Benoit (ipso@snappymail.ca) . James Russel (james-phpgacl@ps2-pro.com) Karsten Dambekalns (k.dambekalns@fishfarm.de) . Feskov Kuzma (kuzma@russofile.ru) : http://php.russofile.ru . : , -, R2-D2 C3PO. () , : , , . : , , , - . . , , Boolean (), , - , . , , -, (O , X ): / O O O O O O O X - X O X X X O X X R2D2 X O X X C3PO X O X X , , , . : - () . ACO (Access Control Objects - ); - (), . ARO (Access Request Objects - ). , , , AROs ACOs. AXO (Access eXtension Objects ), . . , , , : : , ; , . . : . 6 4 , , , , ? , ; . , ( ), , ? / O O O O O X O X - X O X X O O O X R2D2 X O X O C3PO O O X O , , . phpGACL , . , ( , ) ( , ). phpGACL. PhpGACL . , , . , , , . ARO- AROs ( ). . - , - AROs. ACL- . . , , . ?? ? ?? ARO ? ?? ARO ?? ??- ARO ?? ARO ??R2-D2 ARO ??C3PO ARO , , , (AROs). , (ACO) - AROs . : . . . ?? [ALLOW: ALL] (: ) ? ?? ? ?? ?? [ALLOW: Lounge] (: ) ??- ?? ??R2D2 ??C3PO ARO-, . -, , . , (ALL (, , ). . . , , ( , , ). : ACO ( ) , ( , , ); ARO ( ) , (, , ); ARO- AROs. AROs; (DENY: ALL); , , AROs ACO, . , ! ! , , . , . : ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ?? [ALLOW: Lounge] (: ) ??- ?? ??R2D2 ??C3PO , . , . , . R2D2 . , : ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ?? [ALLOW: Lounge] (: ) ??- ?? [ALLOW: Guns] (: ) ??R2D2 [ALLOW: Engines] (: ) ??C3PO ARO-. , . , - (, ): ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ?? [ALLOW: Lounge] (: ) ?? [ALLOW: Cockpit] (: ) ? ??- ? ?? [ALLOW: Guns] (: ) ??R2D2 [ALLOW: Engines] (: ) ??C3PO phpGACL ? ( phpGACL ) , , - X Y? phpGACL : ARO X ACO Y? phpGACL , , . , . , , . 1. : ? DENY (); : -> -> -> ; : ACO. DENY (); , - , . ALLOW (); - ; - - ; . ALLOW (). 2. : ? DENY (); : -> -> ; : ACO. DENY (); - - - ALLOW (); - , , . DENY (); , DENY (). , , (). ( ) , . DENY ALL ( ). ARO-: ARO- AROs. , , . , phpGACL ARO ACO . , , DENY (); ARO- ACO ARO-. , ACO . , : ?, DENY (), ARO- . , ARO-, ACO . : phpGACL AROs ( , ). , ? Boolean ( ) DENY / ALLOW ( / ), - , R2D2 3PO . PhpGACL . , ACL . . , , , , , . R2D2, . -, , : : [DENY: ALL] (: ) ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ?? [ALLOW: Lounge] (: ) ? ?? [ALLOW: Cockpit] (: ) ? ? ??- ? ? ?? [ALLOW: Guns] (: ) ? ??R2D2 ? ??C3PO ?? [ALLOW: Engines, Guns] (: ., ) ?? ??R2D2 : , ( , ). , , . . . , , R2D2 ACL. . : , R2D2 , . , . , . , , . : [DENY: ALL] (: ) ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ? ?? ?? [ALLOW: Lounge] (: ) ? ?? [ALLOW: Cockpit] (: ) ? ? ??- ? ? ?? [ALLOW: Guns] (: ) ? ??R2D2 ? ??C3PO ?? [ALLOW: Engines, Guns] (: ., ) ?? ??R2D2 ?? . , . , , , . , ? : [DENY: ALL] (: ) ?? [ALLOW: ALL] (: ) ? ?? ? ?? [DENY: Engines] (: ) ? ?? ?? [ALLOW: Lounge] (: ) ? ?? [ALLOW: Cockpit] (: ) ? ? ??- ? ? ?? [ALLOW: Guns] (: ) ? ??R2D2 ? ??C3PO ?? [ALLOW: Engines, Guns] (: ., ) ?? ??R2D2 ?? ?? , . , DENY (), , ALLOW (). , ? PhpGACL , ARO , ARO ACO . . ?, ALLOW (), ( phpGACL). ALLOW, ALLOW: Engines, Guns (: , ), , , DENY: Engines (: ) . , ACL , ACL . ACL , . phpGACL , ACL , , . , : DENY: Engines ; DENY: Engines ; , . , . phpGACL (ARO, AXO, ACO) . ( , , ) . (ARO, AXO, ACO). , , , , , . . , (). , . namespace. ARO / AXO- , . , , . (, ). . : , , , , . . : acl_check('system', 'login', 'user', 'john_doe'); : acl_check(10, 21004, 15, 20304); , phpGACL ( ) -> . , , ( ). ACO -> : Floors -> 1st ( -> 1-); Floors -> 2nd ( -> 2-); Rooms -> Engines ( -> _). ARO -> : People -> John_Smith ( -> _); People -> Cathy_Jones ( -> _); Hosts -> sandbox.something.com ( -> sandbox.something.com). API: acl_check(aro_section, aro_value, aco_section, aco_value); acl_check('People', 'John_Smith', 'Floor', '2nd'); : ACO - Frob > Flerg, ARO Frob -> Flerg ( , , namespaces ); ACO Frob -> Flerg, ACO Frob -> Queegle ( , , ); AXO Frob Hrung -> Flerg ( ). : ACO Frob -> Flerg, ACO Frob -> Flerg ( -> ); ACO Frob -> Flerg Habit ( ). . add_object_section(). add_object_section( string NAME, , (, 'Levels in building' ( )). string VALUE, (, 'Floors' ()). int ORDER, , , . bool HIDDEN , . string GROUP_TYPE); (ACO, ARO, AXO). 3 AROs , , AROs : ?? [ALLOW: ALL] ? ??" > " ? ??" > " [DENY: Engines] ? ??" > " ?? [ALLOW: Lounge] ? ?? [ALLOW: Cockpit] ? ? ??" > -" ? ? ??" > " [ALLOW: Guns] ? ??" > R2D2" ? ??" > C3PO" ?? [ALLOW: Engines, Guns] ??" > " ??" > R2D2" ??" > " , , acl_check() . phpGACL . , , . . , phpGACL , , , . . PhpGACL : ; (, ); , , , . ( ) $gacl_options, phpGACL. : $gacl_options = array( 'db_table_prefix' => 'gacl_', 'db_type' => 'mysql', 'db_host' => 'host1', 'db_user' => 'user', 'db_password' => 'passwd', 'db_name' => 'gacl'); $gacl_host1 = new gacl($gacl_options); , , phpGACL , , , . , , , - . ARO ARO-. , APD ( ): -> ALLOW (): -> . , ! , () , , . , - . (AXO) , phpGACL. , phpGACL ARO ACO (2 ) . , : (ARO) (ACO). , , AXO ( ). , ACOs , . , , , . AXOs AROs . AXO ( ARO), AXOs. AXO, , AXO ACO ( , ), ACOs , , . ARO ACO: ARO , ; ACO , . ARO, ACO AXO: ARO , ; ACO , AXO , . : . ARO : ?? ? ?? ? ?? ?? ?? ?? AXO: ??Linux ? ??SpamFilter2 ? ??AutoLinusWorshipper ??Windows ??PaperclipKiller ??PopupStopper , - - ACOs. , , Linux. ADP ( ) ARO ACO AXO Linux. : (ARO) (ACO) Linux (AXO). , AXO , AXO, acl_check(), ADP ( ) AXO. , APD ( ) AXO, acl_check() AXO, . , acl_check() AXO, ACL AXO. AXO , ACL. ( ) . 1. . , - . 2. phpgacl/gacl.class.php. db_type, db_host, db_user, db_password db_name, . phpgacl/admin/gacl_admin.inc.php, . . , , , gacl.calss.php , . , acl_check(). 3. , db_name. 4. http://your_site.net/phpgacl/setup.php. . , . 5. ( : , /admin/templates_c . Go here! ), . . 6. Go here! : http://your_site.net/admin/acl_admin.php. ADOdb , phpGACL : 1. phpgacl/gacl.class.php ADODB_DIR, . 2. phpgacl/adodb - , adodb_x phpgacl/admin/acl_admin.php . 3. adodb, phpGACL. Smarty , phpGACL : 1. phpgacl/admin/gacl_admin.inc.php, : $smarty_dir $smarty_compile_dir, , Smarty. phpGACL ( , ). 2. phpgacl/smarty , smarty_x. phpgacl/admin/acl_admin.php . 3. smarty phpGACL. phpGACL ? 1. . 2. phpGACL () , . mv phpgacl/ /www/includes_directory ln -s /www/includes_directory/phpgacl/admin/ gacl 3. phpgacl. , , , ( ). phpGACL phpGACL . ADOdb. . // API include('phpgacl/gacl.class.php'); $gacl = new gacl(); $username = $db->quote($_POST['username']); $password = $db->quote(md5($_POST['password'])); $sql = 'SELECT name FROM users WHERE name='; $sql .= $username.' AND password='.$password; $row = $db->GetRow($sql); if($gacl->acl_check('system','login','user',$row['name'])){ $_SESSION['username'] = $row['name']; return true; } else return false; acl_check(), ? ARO- $row['name'] ARO- 'user'. ACO- 'login' ACO- 'system'. API ACL add_acl() . add_acl( array ACO Ids, array ARO_IDs, array ARO_GROUP_IDs, array AXO_IDs, array AXO_GROUP_IDs, bool ALLOW, bool ENABLED [, int ACL_ID]) : int ACL_ID, FALSE, . edit_acl() , . edit_acl ( array ACO IDs, array ARO_IDs, array ARO_GROUP_IDs, array AXO_IDs, array AXO_GROUP_IDs, bool ALLOW, bool ENABLED [, int ACL_ID] ) : int ACL_ID FALSE, . del_acl() , . del_acl ( int ACL ID) : TRUE , FALSE, . Groups () get_group_id() ID . get_group_id ( string GROUP NAME) (ARO, ACO, AXO) : int GROUP_ID , FALSE, . get_group_parent_id() ID . get_group_parent_id ( int GROUP_ID) : int GROUP_PARENT_ID , FALSE . add_group() . add_group ( string NAME [, int GROUP_PARENT_ID] [, string OBJECT_TYPE]) : int GROUP_ID , FALSE . get_group_objects() . get_group_aro ( int GROUP_ID, string GROUP_TYPE) : array SECTION_VALUE, VALUE , FALSE . add_group_object() ARO . add_group_aro ( int GROUP_ID, string OBJECT_SECTION_VALUE, string OBJECT_VALUE, string GROUP_TYPE) (ARO, AXO, ACO) : int TRUE , FALSE . del_group_object() ARO . del_group_aro ( int GROUP_ID, string OBJECT_SECTION_VALUE, string OBJECT_VALUE, string GROUP_TYPE) (ARO, AXO, ACO) : int TRUE , FALSE . edit_group() edit_group ( int GROUP_ID, string NAME, int GROUP_PARENT_ID, string GROUP_TYPE) (ARO, AXO, ACO) : int TRUE , FALSE . del_group() , ( ) . del_group ( int GROUP_ID, bool REPARENT_CHILDREN, string GROUP_TYPE) (ARO, AXO, ACO) : int TRUE , FALSE . Access Objects (ARO/ACO/AXO) ( ) API , ACO, ARO, AXO. get_object() . get_object ( [string SECTION_VALUE], , ( ) bool RETURN_HIDDEN, string GROUP_TYPE) (ARO, AXO, ACO) : array OBJECT_ID , FALSE . get_object_data() ID. get_object_data ( int OBJECT_ID, string GROUP_TYPE) (ARO, AXO, ACO) : array (section_value, value, order_value, name) , FALSE . get_object_id() ID . get_object_id ( string OBJECT_SECTION_VALUE, string OBJECT_VALUE, string GROUP_TYPE) (ARO, AXO, ACO) : int OBJECT_ID , FALSE . get_object_section_value() ID . get_object_section_value ( int OBJECT_ID, string GROUP_TYPE) (ARO, AXO, ACO) : int SECTION_VALUE , FALSE . add_object() . add_object ( string SECTION_VALUE, string NAME, string VALUE, int ORDER, bool HIDDEN, string GROUP_TYPE) (ARO, AXO, ACO) : array OBJECT_ID , FALSE . edit_object() . edit_object ( string SECTION_VALUE, string NAME, string VALUE, int ORDER, bool HIDDEN, string GROUP_TYPE) (ARO, AXO, ACO) : array OBJECT_ID , FALSE . del_object() . del_object ( int OBJECT_ID, string GROUP_TYPE, (ARO, AXO, ACO) bool ERASE) : int TRUE , FALSE . Access Object Sections ( ) API , (. ). get_object_section_section_id() ID . , . , ( ). . get_object_section_section_id ( string NAME, , (, ) string VALUE, (, ) string GROUP_TYPE) (ARO, AXO, ACO) : int SECTION_ID , FALSE . add_object_section() . add_object_section( string NAME, , (, ) string VALUE, (, ) int ORDER, . . bool HIDDEN, TRUE . string GROUP_TYPE) (ARO, AXO, ACO) : int SECTION_ID , FALSE . edit_object_section() . ( add_object_section). edit_object_section ( int OBJECT_SECTION_ID, ID ( get_object_section_section_id). string NAME, . string VALUE, . int ORDER, . bool HIDDEN, ( ) string GROUP_TYPE) (ARO, AXO, ACO) : TRUE , FALSE . del_object_section() . !!! del_object_section ( int SECTION_ID, ID string GROUP_TYPE, (ARO, AXO, ACO) bool ERASE) TRUE - . FALSE, , , , , . : TRUE , FALSE . phpgacl-3.3.7/docs/translations/russian/manual_rus.pdf0100644025754300001440000110140310270765617022062 0ustar ipsousers%PDF-1.4 % 1 0 obj << /Length 2 0 R /Filter /FlateDecode >> stream xXۊ6}s J,Xn}a ? !}d.v;/aYSUG ?ow1\bGRߺ_~j.\~P}m|ROs uw_B @?o%_*٠.rA^C ú͍m>¸~n-E򊗮&zcXoĸ_/l#OaLsô~9Q_ }bqwsP@f0샘 ˿}.[CAL 9[*=$۲fK?sh;2!d;:`#@k7n IU @/x̐ ڝ %2g %vJ#pVTu#\c&lUwͩ`sb r_*t11,38bx ;5P \捂~[b},A;>L&LuY#LJqUa%Aq̀&Y%]" ϶'5lK9Ҝ@4ޮ94>G8 Hӝ,6_7?2=_CO@IaRRA9&a0đ̹LZ&yRIҖqR-'MCu *q# iB]-hTwSf^d}2iJU dzb"ڞ$@v{6jt@ Q)cE/]-MX WoxrFwZ:A!ײ+LԾ [`!8M6QhQ 4C(`p1)E$3E+a MIܚe&j&"P݁IatԄ$Ǻ4Wzb"GP?iJJbhԳ9sJSq9R:K;R1ǥ&qJVUM`d"gm@Aq`H9xRO?dϢ=tbUz|Rr`J-Z=AadlM, >jqQ> stream x[ۮ }?y..lAESy͋EywI H.o[Jǿ_?_|n+po/o} x~ [p =1N~tg;/g.K!8`Sa܆kgzg_pxee['I'$>? }Mo?|p??7Wí{۟lX*L]Q$] zD )j6~)EeGOqݽCh'^|d} xH5O”,N'QF:/2ƌ,&V4xnEztBzRJkhP;X]{< =]C[l H"#=!q5&yn՚+K;{فCusW{F=(lqP`7\4ͨY}lE@ QC:^ )_ES<+ YEV'Zi'UW=?oSqB ,6^5tɰCTC$&~$4o3(W+QpSJTp\k_|D\G3p<L8HEQ)R=2Ō: D'?Vhl rߨ6i})P:^'_8B/o @ٓ`/ԕ71U a^$mĵs)&Ze\뺐&q23DžŃ I\q\ጔk1𓘁=5jб tY^'3;NC,rK1 C[`QYQ,+g¸q MRS(3mCud]m&ߥ%`Dz1n[Gm XA %+o%9;-Éj9 < ˴6 L`ޕnev[ [m|u\/FG0QُLiuQB=,boV$$!⨂4 ~EWD:L
 [ݨV)!,S`\T)oj452!X +x!kSɒbO7Aá ưqyNEE>W%*!=q~p 4U`{xj_<{j7w0@6*9<~KN}2g>*큯E*lHQڝo';HULR)~l4Vs-'XR,ŚY~"g V/)vC>HHidRbH# ўqrjk*HY`LVN6$b%B,EC'X 4'vDB/q-c]UWpTKzX7^ Yak,=t*SjT̷^ŸK{S@ɫ.fj_f&\yCԝH}rl`vB&KF*'$3=LQ2G<Q#Gϧ#Ucf'|$ -#) i]zV'I@37/ Ht`-~N2:XR4+iI yٳ㗴΄E.e3ЧbS6\ +9$7p/%6=wl+6{,t\G;,z|(@/*KTaqWAz" ms#+<|T}-3j;;-&Kh9,CTIo!EuAάJ;? Y+}rɹS0 5Ƹx4UG`2SFAs^sX_ h5R>1 {7|7*7!ҏR>u^ߨx5NGWMLD+<1}|s^6ܮs.n`+endstream endobj 4 0 obj 3602 endobj 5 0 obj << /Length 6 0 R /Filter /FlateDecode >> stream x<ۊlq?ݵ&0}˳$8`[]֪ϴVIW$s͇$꿹_~0-~ú/>G8>~p|9d(lPg`CC!`vc#s-WkZl UM1&>cBuc wbNa1'аHm,/X 4,"s ,&9t1?RlbJ]]l?]?_~?UC_bv?Pc <~&`sk~{4assGEI8؏nm¡t0n2w*DHl3pϕU C_m*nқP6Ӟ#暊\7>, :d3:t`;p`ak)D;ȕ$佸7': Zl@&9 trqGq!DFC.Y%QBYP A>|GWyI6UZ?fJF4 +37!d#= & !4U1G6[QnơhH [@!%dls3ѲhsVF8۬#qs#PmB OD} @C7#i-G A&юP>@ I_Y_%"ƪ;YIa uDdD9^*j-LA@LU,JA4 ^@1&)n tvdᎥ} + )C~]̓fo !VnR!D<,%y#` HVGؾQ T2Cnj!ؤGD8Ă~Oj"t!>͠>[U4#dܻ㾌EZr5 d&pD$g##À5A3oh&65~Sʄ ^hJ7.1-l(jm6 uG@ 5H4DhN3n瞻F$!'18/bEėS|Sk 1A[sMgSHbB#l].ZTу0Y`U6 ^d0?hL] t 8M[blKDy}mE[§8|x>$69Q{M ah('R(^yTfnGڮb{EeFՒpqwLxRetOŮVm8jT#Q1R\`\O OZfw#,ן,&d‡XBtmsF7"'w㗪VXXE>5ln X-CC*iNLG OnY4f4WIԓuLqne|, ^4Iuw 22\%V Qcv&+d {0Dj^%i&ux! 5<$k1 Ӛ&5c/&H=9 E7%K>H< eA(I>j( F!mt1ǂ~deeĕXΛ֟,7=&ǂ)s;rg"JG71吀`#D[ed:dkK^Ő,X;֢,DC~m1JL81BnHDP;? Єg"rM^? Np%7 f44~ <S'<+ vٴjDIMԮ桞PyRS78`!G^G P0QBxrx!s#46Xpwd݉m3NIRLO~ &[pvѴN,tEwRT 9vW b;НK݁8NMF@$Qeأq NAO*n'hr4,=x ]HVvZ,p lхЌ ]Ъ-`&norQ6ҍ?4{RO]z"v~A+6+dŝ`XyKBSVR\{v*)ê<@?Yz" ]{UVXd%oU10Y[r&}ER\t-Ǽϱ3,{ ᩏD ui0 ܝt0#Ak5ԾeE6F%L_T>Y䕺aPǓ`wne+ .$Z֭T#!gvO~*a=KP#_e%%Y4HnfX,'lZc=o1YopE#O$ hjU0 镔5/l{vɰrạ7Y[Ñ;dX vЗF 8]'{d4 ;8;}JS+5/|ܚXEnPPP"pnPo&6~A{.}79I@YT<*ۤZfV)&<,"WhfpmaEoE|^K1Un&uԎ39,F_K_L5 ^2U=ZKpA=~GT_w5yS\Ė&rm.5~S}߁wLKVQr֊P0[jjI̽J? ܣ k:)RoI<(;׾N-Gc la,`@19^A$mK\LggCG,BP8WeZ0e_yyh[JUäbY]OT^+Bp+9p9gu7/r!iƊE$-VއYgX-4se.,}dx{\/Hۼ>Q.~[_}"nฝ1qcgY=pW`92AG2B-" D+lT?іӑ$^=x%TJ8;Zp}8EM(#Ds+缽w[?{yW ݺevڒzՖ!n~0E}GRRc,RoϢ|W;S \,=_^Z9d86!eї* `W4x$Z޶"sg 7QVh-@Kvkmx¯&NK v;v{qC-:MEq@(vCw6aww܎I]7Qd[A]d<8mPvT˶'iKa5|jwnd hHbpdwT|{cvm/fQ33з,4R#;x_QcS]Vԣ72ZWIY Ku-%J J,NJjLu`1`A (u!"y""Rv !yH7h(ggĦFeQ8.ZR>+0AjZv>Kggl B}k`xŘ'lc)`D `h`ЙDjT+xCSH/i" GP"+en<x >@=\:oTͰ~'$daK8cDAuw3fQ=wI"1sK~>YjQx~Fw zz*^ Q{/xVO:Iz_RldF+3辴[7h#/& Kh/^mha^S%x..\O?,==6̄݁W`jsl,wǜIsŠMtcec,1.w1.F>& wUvOk.p|l-MbcnA񼘾JٔN VT؂*ܤ:4emۘ*=;؂S5=U8> T&=1my)LOLyIO-1:ܠ>LqlA)^Nc+z|lAOnSp zfw2 4S؊f:llA3nLp 9"ބ|xeASЂ>x-H/5}E"cЛe491߾ of=!"*c*YszT9j|ζ5|[wq)0+O6^ƴnE³k z@5MGu:s n[ov5 $ͤgQ-T컮f\Ηu@x]gu>%Ra9\r7TZ+~>< Ƹa>OІR:)#U@@} xǓ{endstream endobj 6 0 obj 4731 endobj 7 0 obj << /Length 8 0 R /Filter /FlateDecode >> stream x\ˮ$ 0k]Uv$q `oы33DyHQ2?b.ߛ$V_wOb-~O֥k󟫿Ƌs㟿2Jk_7cNYC&wS{94q֑JEnO iX+gTOGhJONJ>PKĭL"IHt̋]KY^Oyч3שjTvTEO'TMoõn?Au,fO.6O)_ s7]O>{__bS_̊"ٕް`ٜ7νgC4l4Ʉh[sfȊffû)"ȭޚ``Vbȍ8Jp΢m^Zh.'8}3K&1/ux7>}sM޲a ‚{C!:R`6fy2Q`Zӻg-qi+Z6(#~#ʵLrl$RQ0 htp95Uó_30R!YPh;m 4E 9 l;fxdU)tYhO۽nvs.1$a@# օ¬`"nwhaȍ1KVsdV؂I8ev(؛[EڰB ny`hxWȋ ]ܒp??Juh6bуX3UFaľvEGهݲVڵx1h,.%n+8xa֍"F(b:F+dQ\/@دqlViAҖ ٣bN) !xğfi&iYRhlr1`Ѳ6aWn$Ĺ?F4)J%BHט?zd1/%"n)P$Lڇ I!P*4k6Y/Ey;^8%Y~s/%|%]tRYJRtv`|‡Msx/ћ7(@LXELv(wqk 4£-Kޭ߅+ Kδm~"(i26O((cjsk-G%.=CN~#@cQ‘cTZ ~`(u^JkarCȔ=@)u#5/fE u@A8(0â츞9oPuqUJʳg,"jUS f5 "-X/ؾ1 ʡ `#F⣄Gn@}C*0E[R'pJȑHq̬ˆ X*Dž@G;0,m_ p0Xp4 GLcΊSV[iw(=63mщWdNò3rRtF²e;Ed ?MnH4Iav̭d^shGFHdk:dd_DBuFT)S20,f'x.,BS"C_$mmw*"7"(8c{E^ ߆.áN m)n[\boVlDYy`g^ )9 'Eݢ9, $/ <ه>mPg7d񣊖HQsD.$$J1 :9]?*eJ;ȵ JY 82,"gGcDV(Hkinʳ؉w.xaf v1&T/2+@)|y5=ٙRcarW%槩gs,y]WaiwTJ5}d0Z:$`@> d*+搪T /]qlzQ7/R%џܬqʲ%Ox:/lZ!Oyx3OŁաj1$xZ cؐ=X6Ǝ(D$Oy]m̞\vaCuβ3A؇3p٥Ďz-$gU⍥>G  ܖMJ<)Pof@Vc 7o كxFYjbQXaEN"ĿGU,y1$U`1,*9nwK!^ zCX&ֵ y_G>0( ;08Q˒'z]U?,@ X?;+WXJk@2t{شP҂%4V+*:`鍝_ 㭉K+VoB?\e?phh;y'FB&0Vi R4[j\7>8M8x=Ǩhwv78/ŌPci ۑJE{2MRn=ezZ*W/[V.9l*RY:Aerރ (ggx724s8Ju2λG:0 סG{\wɘ qMpM\\דVj1qeަLϖtinlz6]:0637]&oGL]ߪ )?I5(~Z`:b$c3kq&kJ%Ta袴.t;kոnendstream endobj 8 0 obj 4638 endobj 9 0 obj << /Length 10 0 R /Filter /FlateDecode >> stream x\ێ8iF8VH^` H;>v?гFj]gW};ӿN{· 97Nm~O/?ˏz{ww!5I.! 3KJu̕#Kesyd^S<*;1,@/4HNv9urwW̶:tq;lurl)iPת3S_C< z@Mp"3U-Pyoi IY3B8v@)aT!HhyY=a,,߃N(> ~/ I'^Ӆnʟ5* &` Pxmt5؏ ϟZS3L:8Li-lñBD^`n-`,elt}}eeNU4o|ew!UZr]&wLTrE891,lҬM-]'S$3PPџ TXidp<53gѳ(%^+97-KcPI_ ^S>zeWz)go)_ [j$ odY/hy~Cj%sZ횊kGr㖯Nś ZroUo @knX) fJcI2;Z9%m$`D_ /74ӽ$f|vtvenmЀϜєJIa61c$־j+ͬ2"`mP/f /8 "Mp:߂l-c ~v "ȾpZ{9 0/p㷓ywNo̊$(dESC RvYj l>'ֆrȈa#ٕ1#łu>Euύ^9~C]gfn¸wmq[Jɪ/aiܘ^+<C-cƉ NK q9!P{V]TA *,aJ49⚹Dvh.yW`yQƖVqv @k&mT,9wu6UMire9N޺W'$0^2Nk6q`)SxNcZ:.V;6(13D^=IЫn9|-V7kA~ZKu% -&)y~yI/X۠6V CR⯇rq&ݹ~p^Nj:|8JQU%5B9v#sF:uڴѸ-@ksi V }6u!]qJRv )b~B ;w_=úe#~5^e0^WOdp(ŶpֺsY/NGUiЩN L-xYczb%~G3F!]#MrDA&yi $&r  GfУ}@t3ccʦ8q|WUNxyn/`ehG1( B@q>uguxUAQ ^ *qA=lV ^ui hI?"x_)9n~~z׾Qy?x>&MaA~(@'P%#`6eB.^{y΂3 ypBW{% _ր1<;\^ ԰w?2_8"?*m'4Ϗ:R;6cngMK$G@|;БD!uSO ߣ2{\ Gxn"[r~F7sWC[ޮg2ѿ0g\D({ :M\1Ԉϟ by`(H,kr_`sng,Ǿ2dy MT1Qi^l.D.NS5 LJQM D/T6hMw%6 w--c5p=N yЯN5 *E4Z1UƓL$goѦ{BBC)~xkKb8:ea=M hqav>InZjB |[[ |v{h 5=T*I-ɰZz"wP>?A?;ބI> d%diwc?2:W Xص"AT;w78O.(:ELkR{UyQzk$@nԩ:#s<{YǡlAO.+`-ˤ ٝn6-g;Xu, _ pk1aPu$"ux8/q6X\^ 3Vv숦G9..q( ڸ( y:#TSs4Ռ#zÑAixav65,nEnw%ټE98#itb9n>A$ E;RX&(J2 QMZY- R_v?Լmb q0&Ϗ@?Ԉa8FUV7&.B,oɨA usɛb% p:,+ʜdWhӼP6*r#"|`p~s tRJ4 SLRnkr?4> stream x\ˊ7?ܵ|*% h^<3uGPmbZWJefD8H>ɝ^i-SN__˗ϟqK8}Op~|ϟJŵ~qy%$rs?W]G2]aBC h(_2]h;DKH,oוNP:\S#]ym!zwqs]mGv6_VחAInVs-;$ .j(2+stf’7xp5y@.uVnpWJt5_#Bbi ٧<@h3dh_ǻ+B aJS4^Ԯ c]ZW Px[2ڞq*ZrYw3Syҏi3AdCl#,EK%BJe`'/A mL|ӴZc%,GGaRrBǼr*meC/Fv"$ !T)BJs&14ˆKTvߒF^W<`3/RلRBil6s*Rot[kȬ}EH3PIhGz̲Ie\Yn\,v,,huVaLN0*5<C7dOO-QJ ڄ6x;c2ur<HDAhHV /HGPzuJ *[L;,  :i,mfPD81Rw/j_ilc٧J.RoIBPh[XaPh߮䁝VڦKO}Gȣ,I~%=RT\YcfDWvk9]IplW Z)`?sHPи%8w3Y,wUmhY Ĥf \9Ⱦz^GY7$bhC{}}1zTאN-H@¶pS*"*v(-NjQJM.6+ "(U\e`my*#ie5JEJHs0$0-bwU?SQ5xR סr#OKA*(j5q)?#H\Xz W7x^Ca7HaVw@ms#G':+V=I9~/ bD3?W\9~6CW jko`;* ;L92Cd=EO^y]Q0N i)2əBO糍YF΢%;i& }$., jqdp< bx\d[!V hgȲHG]'(fJu*<1!ӕpli//CB~+hkZ,E5:i^?`6ȒOs: ;ee=sOٲ@t9fXS~WaXYΜϬ[{N&3eߩ`Ct i%USzZ57p-!ZkYЪ Gsa0ҧtj}ThyŧꞄjVf =$HJ@ž 6G&Gn`S uǢ"m M*~ݍ`rX}p{(OLRC|`= Xuhn+#~"]ӹ in*x6_]'i|i$\X-*N^O9Q?t(( o 88&P5otZ.pԛ2g_n9LV^WQlQ|7Pq9FlEvԟ&,v>!H m_v4' ULm.;{y'#8ŶhA[:JLZZ?V̠,M`8k]]:i7>$]mj|!8Uܢq#(I,δ!3F]40m<ǚHVEG[)'md+q˲ 7օF745-9 4|tЕ;g?s~{i;U v5k2Gw|tz9޹ww~t0C}w ѝѝEq9߼7oښc PȚ,[0RhID (xx4)6k4Klo5 o5endstream endobj 12 0 obj 3985 endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode >> stream x\˪7?q 'lY9А2N]$p i}lY*Uz-ɝNG0S׿ӿ-?]|xoNz'pg_?o!Ly_3ag|6^hDˁi/373xߵl;jN|=fKr3It"cc|q2[NBtH*YȈ Xk;Wԗw~Z\AB!dhgXݫɽ-0Y߂K/fS7Ν]c6"MΒ=)58錛Bʋ~G?TPǼux|"'ѤJ&6Kpg,.fyC<Ѓ$c'{&dXd97!ݕFdD [Kǒ1I~@]ȫ!:E?Jp3jnv,Ji\뱋ylPkG^㋭2xeg_4uQi^ >K4*ƃYΓ0AGnlJۇ`K`p=x!w;k.طAy~~@ y9 bXB#~, YH>lcs nܾ_yOPDSQJzPF9JVi*U:(4bY /s y2:/@SqM{' :ž$H4yi`Ρp{ĭ 1_ʇH=(U)Ѽf6f7F$}1(ҳt@tHb<֖Ď,U*Jwi*62ޡ=,̔E^wؑ*Æw;ZCjmJ)rp"2 ~]H%IWA81$Nm&JtK7f f7^za 8`5+[յ&k *vYֹ[˜naSxZ(XV8m-d!>Nk*"SWb5W0uyfYKul\E812*Oxf&ڼ0ÆZBe>PňO8}ʔDry <ʹwH5A\aKK9:٪@gf ~S1)*ZGrT-V{Uss]|?b񓣼WbheԓE:[Pz+50A=4@mp $2v{t%NQQc#Y($QCIi_m"tn~lYGt%-6|*2W'HvB⯏t6N/4(}Cu"9fV#4JO(gZ;ܔ s( ,vU,{2kPD RܫhM RR&KVI߾`f09(-A:E ncN0p}14LG(*oIkg8c(f&8`ɶ/ľdp'3 i'' cj|Aka`RFJ"I[Ѱ"AVQt פL[JƊBw?l0Ʈ"Vo?P:!y5 |0T/70RƅDܩzo+-k&ó$sKcU9Q<#՜T)uZOZs\G0L1ajEi4'hdE UWW$/0p8 ARc$12[TxA6]],0QD>ln_)Cc:T- XWt6j? :^݁|z0ek\#@Pi}}DaBfXAcՊf%rPla񒃦`#pDtR4KCG?K zbr!tYiJL#sRusT=M9PMOQ"N$aǧAj5qTL>a{zzrZ^аpӜTޟI X_: :NQ%r*kBT;HYʎJ7ѺLE11rN Kk_bEV6:Ѧn 5BWZm!RyTrmNΥF`TBr G)K[ib߼oy #Ui-١Eh38S%5-eg8d@;L,%zGoy d:v_&ưG +.(4TBh~a ’M>vN)>223bP^ШU`lrfw6K*o&6\<%\F=/[A72ԋhg!IzitX;ް h]KR(" l܁Nqe}-^mV>EغFF Sw"rT}²{HJ*L*^VIg3KbS^4+jj[(}#--_ U&\悞iR{Rs5KI;"_#Ba žxV!q,W!f Z#XҟߌrufbU u/ucz -'TE#FQ`WJP-y(QI:jt3}0[R˳p@JLn@*f7uud[ݤ3ѫJoГWƙ5g`4UoPն@AC(6I0,ˣx~F QqpipG*kg) QQ`^w.* tU^(1.k,݈ nv+_B6uQEM3haS 4zp# k {co h'dXTŵ]pv K;RaH}N$%܇ug|g$34=52Lǁ\U*Gq a}Q*D!x qqv ߯Q8$ e.XL=mb>Txq;n}Nƽ Raj 됶q-ir> stream x]ˎ0)Jd CJ:G*J P_QdNi_^^Co_/Cnq/y/>|O!ۿ~^cyOn_؇Q.O:=Wh&Afna7]wsw\~zYYZ GUk^$w+qy6`H)ww/ uodTtReYZtNxݽ`t5%_iWDH4+&7Q1y]qE:ne^$Sʥ~h}UeZʟV5UF#\5<)(<Rj[)@/C!3tJ)!&ᔠ\񤮲ɌQ L'T Bo[([ ҬaNLW&̰ZRd %JŶ3G7ş9-m.\p?X =gb?e?98ȉ<ĩCEJuV\(l1c(??jN?=0YEpw\5$[`u u"D>wq~'EoL$fuʫaSvdTyy=X=5cG&-pjFk nTMM-R4$?A6>X$`dԾRx'xZ$a;eqX p Ct ՊEMބbD_,0AB% LU*3_I7'2iyHz,=NJ"HaN*QU5 uKC o3ECdHFf,}{y X C_ kbz9ۘӂBCyJѳ}jh0UgSei&pA^-E-KQ;IWCkn&,В}?=WRfvܺEҟrI!8 ^ Ʌ'}"gJ\)mVEچjأ|5pV/{hw B~*\m!n<.*b#(Y3F[<ё99i0a Y5N0-Q%)`wuf n7Sv&ܙx?UJfgŃvbI1\τψT4MK7Xt5t֏DBE<>ȗŨif4RU-&c-fcȭ6Sh!?y%?V{H;ܮl @h9QUE,V1>F}e4ql=G'€,I9Tvf|;oqA 3뱿f]9&r:wńҾM,ab&¤yP4}`G ?&mXc6xD2~f(')]K5I¦]lO3^c!OzM~ÕS.#֑( [9fW9iqkzXA@739%GZ|Y<}bpkVi\3#{bkW!5NdEx` ffThW0)]eA)~o6'asK2bT55.D؁&&LMMhmKlL8'9{<ԶF)Byi4(P>de#yy},X76vR$ɹJ%cT5%`TZm d2v]CQ񙖍ޢœrNhnsF3+؉T3.Qܠ"8qNsQԥc "~"D2LfA_A3[vNr:Y<ڋbּu-=񷭙q z:PwC'*Ǒ *,`B:η/4^a{T Dy(-_xz܆8"+l]a4DŽf6ePb9m@'h" vw*Bpa CŰ ޖ-ʙ5%oS<\֗~oL f Z\7L-G@uF养ASMjtZD=w:]LWَ/C3&f% 8mҫP|.lwyAh;8ϙP^*t[ 2i2=n!~Wl`6iY[$N,zḿ0Yq&:[?;MSx3Aj@_Ii%iDтU0'PqTSd,(GlHbd걳'3I"$iH@:]TsQL.XPMO+hIS)Td31Xq;0)s/;Ut@uמ3f#Z|I ~iSL$fV)cnaO;;SueUгG6wz$)r $Ld|* 1&0pL?靇oinRX8@Ar.)T lɦcئM:Ve#N!:v,]FCgF91a%-qh !bVF}txǍI8T-ImT[.dgTbANs uv>8sբrWdDv[j)ZNBInec:U{X;vJD{LtʼZ03 Nb:9Au.   vt!osypu LK`T#}yB PqҦӚv,ncڦpdZVPPF%hϛ*nJǥߨHaNEob([B: 8YָEO',i= 調z q -aCf광x4}gccz0n@ ݓ?!5NӞC  a#qm`Lryԛ: thlt=ЃLT&iSFWKh/8=>mM݆@n[Ԃ8{־rM]zMN#B1W(X ais݆@Oim+SЈQB *GWJs³N씴8k ߨr &|yQuAG+#gȓA1g!Eoe:9noV&ӉB Ys.9cѱKD >"jndt<7nZcѠ%@yj'5i7&nz s#HoznFԲXxKnbL!ćd\>RRv;x}_+Ny^j_!ɻpw/|\2āSVTᜠZ?{25x{J/a8~jn3 /e:#ZNObd7gEJ 6AD녠"ѽm8/l3z9 @L@Ӟ5eM-{rȦNV-N"Xzgt6o$Sn{L3xwuޯx-Sp^GvGgЂKn巋n$ލIz5kʘԾŤ. tt ˟O\xHt TfN&sסTgh[Isw6l5ݚmwPSk-F&:']#FKViIG+497ePIA͂OQ9ú`$ @Th o2f6~ \׺ה˻?Zl1ثѠBm"tz~iJXu#Dk߾~6i;wNj> ezg?,?6A ۠Νf|[#LQr$V f )Ztb~^> stream x\ˮ 7P(ٲAzh ?E&.>l*Ac0 -KyxHr|;ѽMoo0Cm8|O]_ܴk2دz`o2фN麺{뚮9>8 _iq_kb.}6\Օv=Cۡn;ۼ )(eJȣwM/==sRr"="&qذ`;ُqeW#L~ykrbǾ W ?e|UsSJ؁,p IC tSL#K G)p#/:j]ZGARtij':sӺՅ8)YǏE%mbYM:DcG3B V^+L[`G?C1vq܎E1]&qV0HH'xSLGSP˺ǡ)V Hݞ .)A? zBqF=K-K7#h7N{ݿ穟#/$f-%B`h&ƕ1Rܬ+TK,4Rb69F`6 Xb<]] ]`ΛóI>jcg[˲Lp^ g gG[Y8y-]E5pʹ} L gweQBPqDO,ǺPfwRVפjy#Yy%&F0Đ5,_JØ/e;$o7\R:A'w-$.Ґeqp{#;wo-ACa"Q`XA0ͧW))I0q 5b#*@mƺql8^b|oȆA{\FGڜyO4j_4^"cXǃmX3](wek-[CHz%$4&7e( 7aJ S$OªWtvĆrUQ al^(Hw4WA/ۮZE|t%vϛDžΝB]&p^L"N{iiA VL3b~DL$$NZihdT6H!oJ'?Э^[bܷiBьP!la|)7FrAg\gű{@WԋInRu FP>Fj٨ܷ~bF&IjiI@)vWa kEa)_pe .v:wCJ1Fi`(MT69~BX[|q㛳HwLHHPxќh*S|[n(:( EGKV ً7մ6~#w_gZ}@Qn[&;%@NU5D#EC'Hw*ѧd.w~yqqJPXa9FaTUf0GbPP ;s]&x?q .mH= 8mu|mL'2˦rE Ƌ0,Tl &O{'BOx#` 2j ͕ ((]YmNtwp)G䕨?Kŋ")9`j!0ͳDQ\%Cd?Ψ2gjک^tWIxxDP%N,~>!]C'#d @x,6gPc@=ot`՗QDK6V8(H7ЕcEי л6 pHVpid\nLmXf]F'CY'ONh_ju;'v^fɡUL. ڙW\Zk|P百 E 5ƤR =i&hkhIBj|q#¨H& ԑ$!' Ώ. :/vE`a S45q/!ŰӷKa,UWalnW蛥k66j]F|&!'ؚɱx?J=Ưx_T B +^dLVd̔x40ף/B]0~iV|`%~= ۢX@8")^8 Ub1P]r,\twC* ĮLpO2љ߼4?PJNK^PDHs)HIF:ʫ)B0?;o<վbNI C: RND-DfG05'bQ;)*gDBQ)6J*0q(pu>̮/ZLp[66>Ԍ8+1u^'PPM#IOP Z _}jR4Jy4z{ԓ gcSk=1g[wMa~ªI!%7*mU47e=?brU?' I5#}=XvYl0(.aB9J]*{ TwAqZmXs՞Y@AZ_7 rumrǃjscUQzqz]G4@SmNyv4+ :Z g3*7|PkiԨ"g}{;m3/k`0G2Y{Gb|>Tb͓AC0MI[\L)J]zzkwyumL06b~XR DŽgx&T栶PAtnQxQ\w!@΢mY|rGtx{^ #{^Wrhudd9\۠1Cm[)$ss9Jͣs:H8Db 4=r'#pI\2#x^vH5?6d|~G^:F̀J^8]hݾ bA9SĄ|=(gv_xhdqϮC;gvݩ7h"\./EyqAyŶo2Q(E4i dnCtW>z51>aW?Enp/to>^g])V`/p>ƨ c()?f=F xECR+RV%}qQFVD=61J/jy!F[nn[yH뢽suˣTklsC؊]Hҭ#6;\)n3R fh+ .M(ŋs$&E/Z `x\ຜ0鰌(jcmfQ\6Ĺ m~wZh$$$*/0AAolun[ωƜn0W6ذ,yP$B# ]P}LٳՊN:?buNE޿*Afm8-CcLb2w7?X%y^jrm2&#m:f;V> stream x]ێ}_`xB)Qӷ<;Y ~H:%1.ûDź:Uϟ}K?!2@] glCnqS&XG4?L;t.*;5%R,rP3+(B;R.KUc`pjG3(tY>,~W+d+OtXgVuO0Ѓ6X;,*t sR3!AeR:FGKHbaĕ0TY_<5W aWxdl"x |uǎ@xmnAs66=;­>@" c=g _"އqejt+U:o`W*` |G2:J.?Q\y}Wt1t5 S8]`y;GtNcz9XP @xLVNz͋x 13ґ,cha'tx *:qdD<{Kb!fAP6]8 ސ@=$00Ń\n>r)=.?~NN.q(7o#|0' ƒk*D572dP$=E1O&m聞d!!C.sʳROow % jFH?6Y#y[;މ /-|@2y&U( d Vn뤎erL_{OHݳ >&@{@K/uǧW:"w]&wogQ!;ɅCY \9{_P;x9 LsăcڬU ’VkPYkk†yrB=3f?]-@?;\w)oۛ#f,iWck.փ它^Jc*}q&ahhY|D0 /FnjQ^ۂ"E nڠ/ҥke\5E!SN\7ǔU#v3jImMYs7Z -D5Wѳ4)qڒIq gV9eoLK{6d1l-Pwꛐ4'':KHww2;9@6]%m/qL,EnaayqOgCѧ [vkR6(Nȗe9|V1!u9AGY |-Hl#m&Kx_Ћ#;v A[Jž#@v+ثp4\3x2bTn_vfe=VV$>8F2-soyMMwvߨ rq]21+1<9jL8ji4t[9tEc rBx_fn[A @}|x u[>BlMLyiN,^n]c yǼTqVBI:=+(]]3TC9mHg(U[蛗e …obtJ=a?8VDk %]d?I(Z"QuAh 79V)œCDz2WMp.tQendstream endobj 20 0 obj 4446 endobj 21 0 obj << /Length 22 0 R /Filter /FlateDecode >> stream x\۪}oρ9J%8I L 2vU-doNO̴mIuY/?.߹7<\?ۯފOC.xo?]||.˗?_~_._z>dy|{m|F{=O>0gKmk>0zre*oɽr>_.;wѽwzf?|!5At4B_D Ǧe,Wre_Y͕es&=~2s,ނ]p\E*>9Y._"Ngc-%/p;銾ٜ ._Q&C7} T{9ì͊(EfC]IC IɯQtB swHdn_ N@2ھ'vjapUk;"Q7jhuY<[Sr{Rs{Q&6Wq0rJ?y*t>Jk+RǑ#8ʑ14U[ǩȌ~z/[fоw6l)$q lɱAWq5dFfyk>ɼzk}*I()>t(e3i9M35r6%A,6?Cn} u?NxEpz<;c|@~iZe^"5UCj@/{zo :}0 i ltO'Pèq&F# AAC8\AD0W[*c'@#̪Z%%Q1̄ {`VQɟ2!TS =Vr`&hQ'OqpĞajHd dF@l[+Be [OҦԲgqw('*NH[W"U?: ;fZX+1[Ru N 9N|4h0*rK=[2qAD}ZY %?*jFP6P 1 6H!P cu[=Hj&Cf~u M zl3e׶+(*h񙚐q0߄Y:p֬ W*wMubě&uœƾ1 Tѐ`m1Z!l"0ܨ1^g =`v} 3jvkB,F;l \1&Gw2MQn64V٪HZ<m)w߫0 D^K˵O$ 4&%A꼐^č,&=;Y~8Q)~nզ+bѱ1!(}_˯M89Z7c_d<ch$G ekfi]&T>nrKq(lnS<&Ҫ.aUUT$a-]OM!nWG -[$m9Viw2ie8=iZAOBH&0$Տ?}r_yBE/ovj]Y}S ,%EMdEyJ0iqh*ҼÀZT`{EޭLciy=NnG|ns3ꛎЂsMۆ 2ؒScjǖ<5@{?e3)7dgRTTp4܎q0 N13eAain,IvO ޘZVWnLp:G"I$df#\z8p~iF9Y{lNܰ|`1#v&q* 3BԦkSjLNz\ 7aU5G, vu*6qYP)'nj'ƞQɭ/32i&Ȗ9b{$jPx)Y\,Cc)Vӱ2ڱ1]{!L18A{EtDӘ-;19LTl-x?J@rSڮL&X^@v[GdjHSzB!YVZ$T21DzkJ3*j1{U |M"ָ]ZOtWUySddlT 01d.&6SU!ynjN;Fڤ7Ymz;qk vO%n:O'D yLƸIf`@6=/:}MrHk/YL{}e}_9n̲}Qμ.fP gnqˈnbo]}I1s,~5h6Jj Pkme`Mleo (j Os:-^R?ΈmGDvs@g\3q a`c$>;sp> 5 jT %)W-o 3cá0EHmwh) kP^ҹ Lh0 >-LqF;ȶ{j~r>Bb?lt1yʽxx9 Jj|ء"UGjy`h>w?VQOjLa@FVwrfgQX cmeˡ°%\0 z1`:ukX^D0ٛhv_RrYbz( |1PŻ81_FSR~FroߡKڕu#ۊx++JP<Yy]Zm=>թPPEߢţ9QPt{v9.L+1#'O eZLQ˨0 ,|ђhZjC`Hq~8<9>6lK^V\ۓ” T_$Js15Eێ<{jeMR~r@;׶[btvLCuXvLsG:}5(z',nBcjs6b)`ۋ?\ ͛Cendstream endobj 22 0 obj 3918 endobj 23 0 obj << /Length 24 0 R /Filter /FlateDecode >> stream x\ۊ$}kb€^ K)vLYfAN8,/޹ݟ `7/O}^ w/1T%zm_{>߲[^po?_n}us}-_$px_K˭_zI⏈ VSQs1"cP tw/ƽܚ)>\FG<{;H{L^^p1 HF Zc"-iTnObgP}=#zz'O"c(w`2Oβ ht0_.n) %j8B-̱>klejq)ݞ_:#(~`CG<\`dS("P<>Up*KX;0=pKPIj@^HY78LPqKI<9=fOWD>Ex)6PE_O  p`[ y05 \D & N L'-.jYσly'56y07yt!)xF qTT% afSpC,{(* %T4P@CydE AĀ}SpzvjoIZ}N"b{lج`Ϭ'"8pJPX$S$"k]@lQ)GQtZs|mpVI"G Q뉽q DaBԕ d8H-"t;nгW#:h0-EoHPWR~oeU qծ@ZHzNXcv{co)UTڨ_ҘGI޷)F^[9PY yrҢ6Wn!i6C93#RHLMP*@luCq;`GDOܡuF >pCwfX@RI3ix<#gTf=[<ή.̮NfpBĭTTȃjl-Dz&ki_qѵ;T*^J9jx_Urg|/̃,#}{i$b%v>u?LPĀ/4ba4гzjx1^!?:i;h&i9٪!Ԗ(-\>k x-CKgF3+ydɟt~aKna+^~V_ 1-&U>y|kK |G)zVS/떰AmUgJ'sU@*̩ c}Qjm3Eu`L H()(Q[y85*H(:Irb lʍ!Ƌ $Az5wM=O9#/H+#C㉊lnxz^%atx~#|G%y "l=s_-s#K^>Snz}25HNh@}Re" bRz WRr}B89EN1uYUπ-@\S&CtI_ Me>:_IaF,7Z]1+/+*d_ j2!z3 } TJ2$ hɳ+C caLKV6v$U/lMK0jΞnJ<zDSd8BsTjw# nUlP30IiT g}e]Vh"ڢ"?A= Sx+(ٟ\oSoKLhh5u큀-"f\>f5463W ,jJ{rP Fu3ڤv0{s9\j x͌h9TFl 9H,J8z*"_=;F|Z aC)9%{_;U 5mP@Y5I?*]φ_VY)PG*82ޔJ_qFX#ӯ/[^L'߂)ՍܔC |$U.ѝiE?{DnK:û0s{,!ȝ7 k8YCjK0winCR.p Z|sz1x1/6endstream endobj 24 0 obj 4044 endobj 25 0 obj << /Length 26 0 R /Filter /FlateDecode >> stream x\K_aֆtW?&p.$d7njit|MBGGGW1|ɼ[P_~}b2$n_lJofy 7f+úlO}qol}n~ƛ}}?~e5S~a}(ϐtp!*mAHv2^ˇ'Bdm\}J/Og wC] .'w5a}|wwV^c)9ss*ow v)åp K =⚙ٝQ{_5UD ϵ KĿfGtM1Pw$](#\C-jPjsJ U(h)5:Wbtm2qxTn4fӺΖס9ur.g`؞,x1PHrpהdF Ȏ|GʉY3':cemۈ-.l2̿I.kHJ3r֞4p䢰UY> fۖ^IS?ӱ3z3Ej3xsϦU`z‡ICC>fAKЂ 7qiH|h_w!H] ;\o$$S2lڜ5TNā{Sp5?s:84H:Coȩ`a^g &F`C &;@3iu.* /++OSU@iRCVsG`FF݃NKju21ٳDcH뭑뫭NOH6R>NոZ< $qoh"Qt{=q"nX̻LqFWYof,ܦU, R@|.IahΣlqA.68r-UUÐ(EXy0WЕ/raA2x W"3rC:xڡ @<i/^%ek1+.CWk4vߴ@Ŋ݇͟OSuh s%Jv8DBRv&*˵`‹$ _6 ns{9:+eL $b t%Lii2SV͒Y`>,x*:vXD ƳyUSg"rR55r!Y})5X>>RR$\Qql Ojfbh(yPPInjJsAe_x9ڳ'V;u9ZS\Lf=:ׄRG#I%'V@a 8JqfL(G>r-.C7K=o]*CB)sVn:x%N&R;Sz9dbK'П@\8ϊAmu%^3ṟ|?x3"-Fu RDtw牋(bZų΁YcTZ Dk9J0ز&OEAr^Yul0XR ܷL.~7hGDDr9Jw~t^dO@H c+ǎ>'Ko]KTZ*?} 31,Ջ4BCR}gY6gʲ !'>u;{,e t@b? !&rBZS g^ǸH Lkzwck2(@!gàqUE?%+hzU`(X8TP\9'{ (`c6h41zDA9?/%;?ک93&toM,?=n]4~%oɒW2S[!l5T* j/4e VO 2 wv^Q}*@'2Q~@F<^d<K-Ca,jLΫ; ^&7O5+V?yv#kNs N̙W M_GdjNs$6c~D-r-j#D?Ru>ٍ ?]H`j@dy.br,PDtȤHuqӄc\vV ' .蟭˶^' wT_fH:>2ϵRnnEjV>Kg4mu<]̭˿/I4F{>G1h\bʷ5J2zD7)}+A+b''E]f%tr~m,ĜY5M1] 02Ӹϻ [ō-ӊEKX.q~,p9ŭpzzQEыmVR?7sB߮.AHBQdvj#'ѐhz6G8asM+nG66ݪrrXP09lMpL w6"fM~yo$Zq7lV+MQ~4vjӦojNpb"Aʵ}k;{0r0^ͅK?ը~vjlG~4` 'O#<)| U(xI#VkfUL/ښ Mi6+Z$Yc0%ޅDuO_#90G]Otr@4n.kiB 3xB҆?2$w-u6rR\Nzo9>n b*Ce U@A!ln~Bf J*:G/N93HCendstream endobj 26 0 obj 4108 endobj 27 0 obj << /Length 28 0 R /Filter /FlateDecode >> stream x\]}pC?2Jet/,ȾGR]dzKnܶ,N:U%ۿoGo7/~߿-LoF_zӓn﷟(%?~o=pu/oC-~qԟXޒ<0[OG8凥^&)Rz[ gp6DK<'2q(g0~[s2:[f \lk#g2]t꨾/$<=܇9Ʃ@L0JopnqJ!~>Q&KŸp9- eOKFWy*3ZN}Cm=5eE*<ٞ0uˮ0:E9t[d3/Am{^$w l/W.s4Yd&!ΗtVS~xQ3L'cVlmceYP{~:M!cAAJp&?PRM_DX3ffq}-.Fcui퀨eeqmg_6`U 塱Cx_<L8B+ ȋgrEĞ`0J5 Jү2uG[tyՕ"\l5ٌ Y"0d'0*iU]|“84o> a<'c\ ;ܤL `: ~rT[LSN?ܐ~V6]EGEzc8&Mo*F)Ss:-BbɩV9v;aCx }l, ,N.U~aA.D,jk&*Iq$'c-AyO5Ow aۡRD~^cj:y5gBW%N\u z|0c)vn:b:Le! j-6Al@)G?&"d@XM+F2n% ޗL>&`C$P r8M1mg|BzpAF03BY{TO fr[ sae.FB5,^ < NA%F5E]%QO{R 1h/E]۽w5S0[d[$19S*\;{ A(bO,sZ3\k^#Xq:r FQZ@(li0(mRX5f@crg_ѵ*SQn QF}h->N?G_2h4`F,$I hX$V*v HjD#%b:(3R%r=8b:Oǹ,\-,"(Nl^aaJUWUq>~mQe[DYkok<=iT;dI,_2Ƅ<[G2;T<HSȼE~2AL![~~q<` 0tSI#lh )[;U(M, zRbJ9{|E$%ȹ!~.fJ୆_$SȦ!%₸ 2.#-466*U{//X#*G-F7'V}S FyLͧx)6X2L!WQ@i uW2_7*$bT%{ִw\3l:ڒT5y3|8{M8[;C ͤ]Un;ˠuPvxœSvD{;N{n(&EoJ*uOFLKA{bMa-3P5dc"\m3ntaD}1E`wIF$Lqʶd_DNwy&ˠfgв; |m\y qhlꆫŦ̓BzS5|@P2njhWJ\g=h?1xZ`V%}yLY=ϓ:Zl(]MXоR&h̛j-^BvHƍ56ardp0hDMõz,SMC{ocfYsxΘTj4YxR1$1-Fiup񃺣CDo7?E)K-{tG-&w$ IcI@|5};l8jPV)Ђk88 zR8>\*eS#׍ltxinxfH[qh%(RO J .ma1 vl.'U (V{ Nը 2y$kI Z3TT$; YhtC9L\jʠyLecۆl~I~x⸑< P?`K4=+cblYNs4 ,z'wڼ_qu{*d&<Ug4],@X^uީ~^DTوP]jWrY6Sr*'GT2z{WMzoLiSe| !z|p(-|hva1٣x9Tg`sexO-c~}ׯ ('C1QciAu7 ޥNӼq3x6@1> stream xZ˪#G :J!n < @fߏzt wPzIGGG*?o>ӏO-c/?z?>MoB>SN￞~w߿>ϧ?>/vn|c./.`1q}ρFjFx糋YpRp.0uN; `!j|Ybd9G{%o%uLGbH K/uԳ R[e8yxN}.ch'.( ޤCf>z>F.{ݘD'9xYvhp= 7ؤ&ȯ9 1bmKd& l}҃JWќF~&71NM(P|ԫ.ߚfxG$7 C'H<ȌCJW7|;74Rz'i{) υ[t(a ~|eKqj^\i2p.T1,2O{IP'q?+\+uupoG9@r4O-^Lh\eJWwlp\ڿgݽC=0_f@ G_2~f^T:Cð:ֱ6oH;0q^[-TTI(H*Dɟ; >H;(Qûv' &NSE+J쑏cκh=[X*EZgJjwQVLy  !C5삟H&  BAٛnf*jWGE,LcSB0*A]BMᑺZHz+K#eK~Mi^XgQLYJ@6$![ZDsŘI-)4ݢ\i \=,xmIaE7XTAji|{%PՄ 3(Zy:d>olsBI膹EкRZL,qtr"Jiw9 bjh)6wѿtI]Q ؆S{B 'ՊH) 'qg6shd`I2CP%VG#)(° !SS}mE4JMmF)[[.&q ɀ:\*ҷ!&zf~wfKNWv p[STR _P—\:,W" Q=rɋc{#E?SZ ʤUoҩPCKqr *l=rkEj!,`PK xI} &Z6U$o-?> >yM|'Ĉbƨlk~I R6r}Ԥ2ғ({}2+Zew߹G~NQWM0VUyPn*p["^ $L8sKUYzessEou#HVsDrϔkGDI8U`#,ܢ@'ܜYcL)ԛCOf؂ ȚlWlN+; Ġ1h٤u^ 9SIwmȨ"lUG*ʐXBعC֜h ;9^ZYE<͏\O&pүE#hN"dh*A'@SlP':,/*Yo |u`[ Hr c S P =ot|o:hVN t( +!acn֓(.U]!RpV?%KCF̸}Pj`N;gߍꊎTƆ}ŝeɫQw*q|Q=4?^g WcpE.[Car?a48A aY'VO.xw"$F[!`yT92F:2`Zq͖}7Qkѻ4߿g 5)j>mF_Tq8A݀nI2ZR(xbqn~*1TvkabIt4hdT(^Um&]4ǨϐtˣS(Qg374>@myȹ{>J3 ۛfU{\ c1~@si:>OQe }.g򢒩# +PI?Qҡ;*jʛj5/[#b۬|H+l8'ڕ;lJFi55*hZU _/d~#Eg+ʳqtf6~Fm~ԓSi-x"G~ߪVUaG(MR;4PhBv %ܯ)uw".*OTܸ_)Wendstream endobj 30 0 obj 2477 endobj 31 0 obj << /Length 32 0 R /Filter /FlateDecode >> stream xYۊ$7 }@z-R.Xu<. r&#WYkk .K#__ n`H;N/? 0[2ܘחOqHS>l0`>!:B֟^_Vք7z!z[LF#8XRG=ye|pk:TT32-% S.ᡯa&ܳu>H|m2xռ[3KkFu +yy2w/ UfܷOM4ML#Jg,Ѻ #ovz'ax1H DemL3i,;ƃ(f۾r#";0۾A Yjjt  %F锢Ta3 79,<׽@=HEF&i +VDCL<h%[ru8뿨9"s[Vp{"-torLMoG|elHw^b s)]Nvzga쭫 ;,s~АDXsJs,ӭMo?5м&\aɥ+GhM53`^bmSYJn9-x]UWpcݞCcjn=/}h0ˊH`=m> _\r]~,mr,ǧ*_? Ç?{?, UA; } S)FcY㓊QD!,`)endstream endobj 32 0 obj 1651 endobj 34 0 obj << /Type /XObject /Subtype /Image /Width 632 /Height 390 /BitsPerComponent 4 /Length 35 0 R /Filter /FlateDecode /ColorSpace [ /Indexed /DeviceRGB 15 < 020202 98542B 4282B6 B6860A 629E9E FE0202 EEF2EE FEFEFE C0C0C0 0000FF 00FF00 00FFFF FF0000 FF00FF FFFF00 FFFFFF > ] >> stream xK8 kV㌘פpW*7d[6&8 y+cbϪdOىRvr);`3od"vg ]Pnʮ.e'ߎ7]]#3n,^bo;ٷ'+Lt|⾳`N]Λ_KrJwQ[vv4OggUvdC ͱǥBGKɥRvr);\N.e'KɥRvr);\NEvfI I&1w_V3ޒ 2SViLu֌<{h p/m+ܢ`7`-8t(Kȉ6  Ըwٜ7Vg{Zb*"Gl`y^ߪU%cJn zƜZhX%qV-s&hhXlg]@5aYt p﹛K(r);\N.e'KɥRvr);\N.e'K4BUeNv٠P5Q;זp.ʶͩrWW;f0BmvF-Tu-ϩPIɥ~\h2ۚCg;igB|7~zPv^n Ldq?{Z?0>qO =}l6Ŭx#c:lt"-;cg̎ʝ  he7ay XlVALxp=뻁ٕ-P;; ,,.og}{lRhmvvƋK}rqK{Tk}#TBM񬲫f]E^p+Sc}/ͩfdkj)ocRvrE.e'}zWr|}}ۿ{]%9hh*x7ókǿq%qfS]x!#,:jnv I66Aw,z& C{&v{ og ,Fίzͺvmv.WSag|]@2 ; ,+lEg[ѫQ*t T.4`&͏fէRDYs1M&$=GI";~a`Aza(r3vxtz܏WbfUQuG у7)tglnR&~3yN yv1ؑrvW0 i!1[c>>||%wXnL` \.,pe}#Upq?Y%Ά1kP;; vq<0J-=ag1<jzf{y;{vxֻWȮR)1r,9(s?CA{RbLӱiK(rԏrwc~n}ďr]188EВ:v7ޥ1܎^qlkz̏BSluGcSڣ2wv%8Pz[ڬS]ثc~.Mmy0nQ63rdžj?J6]l22+뻎w^q/b=GqLSY7^tqZ{fZ{>ʑ~^G9ԃQfMJG[]_QpN߅~߶qy2a>J(scg(W6g3=Gœ Nz[?@QM(vDzE?5w}yfm_'zp>h-D'=#'~M{R?\G5즿tA=eewEÏrzr=Qsû|Zǧ|jñ ~Q$w(>Ql?bC7[l@>G);ƮV0%FqVU] P+ȥQz|7SsG9/ x>!ٵQ(XWQh@Z%{8vŏ2ndoi?ʈnedoi?/wdemTmK`zCE~f%Og}>JKvR?\N.e'KɥRvr);\N.e'KɥRvr-{Nh]Hɵ^rT ;xōlPKZ{ؙٚE55S;r7vZh[EUvؤ\GKɍ.B>&+O7K#WJ6]394Kj:|)H..ג޼t3..6#as% e73Ʊx͇g{};Wj#;eEaE=p}HY"HK/'[ t5{1a!=dW,Ηh ^A3;x 7ϲïI%Lm@jXUciϜ[3]pѴ**KL+W(o3§[T=oǿ%1Wc{11~ؖro]%3 (w `vDnj]:'l݀N%0XaGѲtPc̛Q}W[إx FycǘGvc:`\|cJ^=sv m~i}W,!KL. YPR IV1J}m`{N-GʼnM\86kgbpbjEf'ʇO6D0lEfO >1vWnTc9Rvr P MOdIC^dYng 0=}β3ocw|xg3)5\jMIAN5+B"8; v$S5 m輅)h*>3$ 1HjϮTvH0pb| 0ً#4mne~4dac=oF`aXK;0~;BI)> Q_勼%g7fؑdEҧعO'z_}Gw Eb/vŝ7Bfld$vg U';o.UlQ*пKEeJBpBT1+ ZR{7UX`1>ܵ+w|~4W+X͇K{Tk}#TBM񬲫f]E^p+Sc}/ͩfdkj)ocRvrE.e' Pv(z=Ano|-Ole{㲛Y't8J;_ ,VX\iٞ;*p_~lez=WZgei3&6k[ͬC8kŴ8ά;.;]mı[Ǔ!h_c}ucMnUSX K%q {sЪlM"?JYhgookqMd=Q;ctܹC 0uJpnkCDqxsJx9m"`?d6b}ZX1 ɯmءV(DKZ;W(mم5u-K(9RwĬMڑoGUI♳vvw~Z;igs%$vkzv[uJBWR^Ep\q3&tn^)0KH3ܱŇ4mM0rGa.~j1>ȟ;|һܒ;! Dvi'(jz|>J1Yb vө`7ZxNPcŸ6 ] >> stream x]=R:H:wDLRBBҗE$,`T4U +`v05dvm?~[:tnuź٬:Ų^P\Q:6,*Ҧ4lk!B3G[(;4Ջ\gE2M@ UTom< Ag !JP6_ Rp PkY-Tm)EinrWŌ M 8+4<4=|R )K 9S8``zhӀ.zXw-QB P$P6#恒A pC,lAF녎23>,:,13-W,/hp _. @`yz BB/ILIܠzbf_ywftC|[ %/\ "L༘F&=G-]}?{L)bvu&?fx/u)qu<`ʃJa㡜)+y ƒ'M A|rh03E yPE ?py y<y4AVc/q` Tܾ挛*o뼌{EhQkUڒnta*og؊>TT?޵~l<X|Dwvx/NƆ{xP 7FD1V)&trOBz9twmuǸZu2`tVec ΊxΒ{2!,o/g:Kl('C}.蠳y1&?JR!_> 0tedIg &%to.ϳ2W`):,XV!:,h!otY䡃 <XgcjatVc^X̟:+|S+tYVv~oIg &%{Vo:rQȍ:36ڟg2aP gnϳ4*UdMgxn"Bx\ޛTk}NћΒ4Zm^wVT{03ZV/GPg|W>v Ao:M_ȇ}Ȟ0΂4;rOOO >uMu&S1z@uox<+>gፕ=kwYy},}taHW54wAG^ƯÈ.,`ڰ+]gV1|RYK<@ƒ'WyYXX/䵃!I'+tߠ\*hx <4d~ =_Pat0g>5w./s2x~q$s\]Kg/\^62Q%'_9uC=Zj x<,#^;WW|,R?95}BW.3'qeWR<#0k%uby[- o|ӶL<U3^f\g*oV 9ӊCcz67s ?szt7hxq|5'7oxо%j<r endstream endobj 36 0 obj 2698 endobj 37 0 obj << /Length 38 0 R /Filter /FlateDecode >> stream xZ]7 }p<d{<pgg iI^+Z;@=ttt,7 |}80em,^_r9C O?\pÇ28|eiʲYں~!_ FA86p `:8XMg:B[f$+C+&XƷ/OIc3ll&#aKfmxc 9+`?mHp;pѱbܨSasD5yJ0AjC!I67]R`H`y_Ɋ.૷r u¦JZFa`ȚxYB$.nFVaj#󚷼 8䧍F[2$6!)ޛ%w7DC5܀#%_sI1Y!,cdΊыMHRR 4<tb6&4|0 Bk) NY>ϊǙә4!a .<4yaE g1 =,N䩝ڶ)tHEcþaa02(; ŎAqVT'@ML: J6!So62kePF>B\L4-m{Pf(=JXEqV0!0whLlR NF^5~kmtבtHxY(%eUUs)C^fB |Oݐ$d`>] y7ܜ&"*"1T䒫/5aW=57JDEpȇrqToKs4>ZsLTXr9oV 8mCd? ;;zXSwf-]g;+YS98MyKU LFRi?:E~ *C?+?aZn:GD857Q(w QPnn`#MǙLwIJgꒅK34Jkd^-<.,z1NƅT5!RHJ8ҊŎNM88XxwIQvԛU{*9'wVrn.W ym#* Dv:5H '?1_K!FaW0rU, ZFI<d/&Y%?|xy`A;m,?.2=BfׁH?Ҝ ] uL&z/F$H׭LN0/;{Tg"qVθɝ>ښVR;z}ntcF̥N&QW\[?U[pDzk=Tk;9UM[ 6II?&#}D7)IEH:)M!̂DId` DΦQTIBE6Ȱp(8kzGBAYLX%`Oq^%&Y#ąoGԯRhICUkƄ.d1ٚWT݄%po6}BӇgҡknh5<޳vOE3ڭ0QK WG:)Y((4|l]{-#MVFA%E9,|+779M bʼ::O|'sN&N&`%ҟfF6aFڢr˴jGm fo5WDwޝz}? Kq-gcﳒޔqU~};JcDyzEn6ԲW~Ti;Ae9ןdE~2 ?9o/ %endstream endobj 38 0 obj 2001 endobj 39 0 obj << /Type /XObject /Subtype /Image /Width 680 /Height 661 /BitsPerComponent 4 /Length 40 0 R /Filter /FlateDecode /ColorSpace [ /Indexed /DeviceRGB 15 < 020202 028202 252525 AAA9AA 56B656 FE0202 C0C3C0 D5D5D5 0202EE E1E3E1 EFF1EF F5F6F5 76B676 666766 FEFEFE B7B7B7 > ] >> stream x=rֆTyLēMaSg;I/@ZUZ]LZށ@(YI ,xAUUUUUUUUUUUUUUUUUCdlx׹1DvcM]Sn /nLsz똞;o-khmG Ngdp(Bߎ2sZW+߁Mq9 +!LW(&BBƼɽ,].0 ,i$%+1NeV2SL]1 ‰35d#L,ZZm)S)*SiMMi$d*SL{-0_m;W Uv?#LSk_HoJg!洘vh{~tr'תN?;ې-\czS_[3!fI,O1,#7/v}MLiVY]qLmѩ!L(ޟ_o bj}!2%!33dAW(C´{~YC/Q38'e1IdfJo@$'4t{$MAXpO[ Ԩ<*31>CILTb$#Z1d I%dx 0=$: OPOv,LӧI,ŁIJudR`g9{LL@L;'ʘ>(Җ?+1Ӕ&jSg̘n\!C#PFEEYiznb+cj{ZAܗ`O?'e`F7Saj,-0ʞ3i}wˉ IzHJ'mu^ SncIE L)H& Ӥj,77)VFޕTX:ڧ{SPg>Ϫi Ld;;:7euԦX6YoCLw>M,x:'.)hs* ls sF 'I'tiGͦ#)n*0幇JL{L 92 S@aYR$ʆeLmP)7?n (P,4}j7Mw?#L S@qMUڂ &:v2" $mxM{:&ݟmH1OC_4vk[&| nd_<;;Лic~$L" sP5Qp -BJ5,}ZB<9_/; laJԖB;qudL'HЛា0A#|2QӯY)N'ZqY L_g/S>KV˱qՌ)Adaz B禰Dh @- Xhq׶5EXS3 =&dj:J1q3kҥۨhO1MScz*)&1llu q,Ċi0 SgX)%:.IrU3fU)gz*I4lK ǒ2[T*'Șq=OĞ,$WY7S =LLdjyHC=ΤoM-}H+̋RFOY(4v^ Sn5xbpD )W5))SH0H ?wCNCiSm/fӑCL;֫(>ZNО ōfCN fjӍ,N&1gDvf;tz~c>ۄNL؏fd˄&LУ_ͻ'o bD|O7}dQ x.0w~{Ƥz ^F.a 4ARG$!zb%zڎ^&*p0E>1d?a?T &Dohq?J.rm%at)W)mm?0diÔרpt퇚hCM0CllsnC=U׿xC QQv==Ui=%{*LT-=h?ad])cydS}O؟0!I?.pL;I4$*ӽkO ܸ LcIi? x=mkaC<!ۮ/4|<6W\<0Ӟ]vo+6]x0RDI4x6!yoMzwdH Z9?r]ۍ Hv2fK N))L,ڑO=eXxܿ'!JB@BS{HHh]LS{!އ6Sy%~,ZGT 흵x~̞ơaߞL}JOdOǣ} ixT27>?vx~hzʢ zL?Qcu<;~~~zC"|0ܴ2}?ưc$rJL!Iv6YF@ ӭi׽=pH?O,4|_pL:tx?~UUUao0g0 [Fc1inr1bKI't=p dN|(1 N{3HVawsaSy{@d?g'ת Ӂv:.' TEVd*eL?6Y GMw&S%YS;GiۉTm+11g52u1 'S9s~jOJSz4Lfj9ǞFĠy{v?L~6z:x ;IM>pt4?oOQ Sa3;iTU{dWUUu^TfV 8:1>xrmMo;'*= .;x~7tI2!ۉi7!ۏ? urڪm4ӭiQS -[v[Ѕz:Al] +g;1Y/w:_ݐ7q6Y=g[YùK/l_M7G+ưLL߿_ohQտWdPo)S?Wܟ0p7xkC,2gZL)]o$MAXo~j ޳XwL źWQ33m!1m"ÿq[kga~5gn` qCx[(R=K\T8OTE!-9#k1Kߝ6ڄ"2˘qaߞcg 8\y'L1еKTU>4˘bBLNRŔ0}n\"$YO՞>K Rr`ZO׿W0Rid=¦?ɞNkaţŭ(3wvݿ&[Onƈ4K/JwW/$ùXa80m_P/(,/M9v(^U]TUUUKQ;ͬj"B1l5BC>i??T;E#n'_ʅ J5 ӟ?|/?_~~ӯ)'IIq/Ȕ=n_p@iR=A {mL G"%L/!ba|QZt~Gtp=bDR.?+LqD (.T/S9w=jAXK`[!!S%3q]A:*Fq i=F=d ő| d{=?i=egv3= ScW½ꠥE9~ S:IӪfC/U zP!fc}sr/қ4md(fLn7Ee؍ 2+%ebKO{݃뫫+NM+ ï=>^鿄%N4}_(i1t'$MAXD[)V<+R0pxYKLGHrL)f3jojӂ1'\~OΏнN[> ЊX'^ JTOoM[=~fsa7 9#ꕏ'c7-@A:`bM $M)| 2լtECMhm4eh8ZLoخPϹɘH֛)߱ !rUYq2o3ZZ\_!%Uz2Lg\OW={*j1´ӞF ݾhFTvZ +,mkYz POڏw_R7\gXd e2zvsō6KtNĞ~rCD@tC ҫ>MYO8%K ,Wk/K>՞Փ牪L_- e9M:IͺV8of*Flf薔A4ʘS%Q1]!MSa.uLg3[(_&-E@w[1\G,*Ϊqe-.ݵԙR=k.B  TU֛5S4lT3k 6[EZKR[1] d62]7zSiKtgSG+Z+L*Z7rak/aMi >/9-Yҿ ^Tł5-ǔ5Y\Yi+03XY3Y4rq2I %m.k`^R䚙6_kHriUUUUUUUUUUUUն[ӵ"q;:!7͵ɗ6U1],݃N tszM$^'TQ4dR{ 1ƌqra$,]_%3?y1]w2t3]! G+0=Y ؽ%x.nt1E>k""tLe\_ԧ:[fZO%<1(6=-C=MƟ.FC+4H-)_]L7=Ɵ.0j8f="{}WuK-`|}>?b:+TSzq`J$sjSܠyzY1ǟ.狥?U0[Sz+;ixOaLd`m65ȴ6ϔg3䔘_tzͼDo褘C| d}1 TL=Ts(; .[ꆎ@%̾5],~hHV"8\o3z%;2]aL6*2{#ӢsH>Vҭ`a*O[[o׼(i0eFrSN ~"p&Fu`Ҟ~odX?+%>2>vLy`.vt/(r4|iSt;Ie%ZM)( ̡j{ZnjAD.<x=mTS?S`Z.GUUUz; 뾆=Ojw2!#_ԘzDX`vP~4@CT&?RYa{Z0p 7휌Oo3+R%!~õG1'bJԘ›'"|E:x^) 3p0oЇ.yC3*o&"%Ld`aQZȔC`N_?`.tB z_P#1AHpx8EXRqeE4qx*)yJ H#. $Ղz+ \b(r5V}.t?#3tR8tLS`LLp~qF8RcM%s<T ^,_Z'´r&}&uw9xJ[3[GRC9ݩ;/w3[#>2}= ~6K9ՆR~XbrHهl-ư_ az)M\:|Ka[J*^%$v_w? |Hc?lc)Ё3R3:1)9D) o k X,hHG`),-}q:Z)] zSV>V`Jl:eAa7( $ˑ1:,`2Ac4L "3E-{*9S]Ԇ0T {gI#?7} }k=m)7ݎݼ~sF3jS?R*ۇXqRԉ ӃVf=_ʓh3Be:@tdSO_eiUUp\~;|j_rvb/D)T&I?bj#*S1E;-> ݯ+jr+cG5`a :1u 7|hjM+Z$†rƣ;aK^eC'qfX݀}G-A ֶֻ> P:1 W0@\.5 /LVBpe`(`LAZU=V kvz tk{j3{dju/;< hi1BEۮv*̐T 2[a@JF, Pܡ$bt?SPWI#Gڲd/&ƴr"겍30ڽ>a3nxp|LF&ћLp465j2C[徘c-Ė黎?V2ߖ\G3\hx܅<.L%x ߁޲YQC.w_r #>K.XO՞:/l:c<مta<ɅvDc<P\G]]G]ɮ.)ŷͬ G!새o5k#\pU(Nf<ye:Zglx~߶,sHw<+N3:4dSWQdz 9hL*5S:gVmUOA‚0kN365nmOmfOLe_:,F;?ӤO:C tCIL9?dʟ1-+?ƃ+684ӑӪi PX󦽭1/Xv Rw`:EMK?.|;Ac_v#+"SI5A#_v;8K{o25|\Ŕ#?e ь/c=U{`F94.ĞF?%>p`bz?%>pKP\9>҅8U~U466FxP㈗0?rLm@+@?ӆl;(>~s;Ϸ1C֜}Y5a~~nzl< w%V|Gt?¨H+N7urҌ#}a~~a*#.S` #ȣ SGgL80W -\)B= {:i==n L 3mӧFVk`~j/.b/LƯ~O  G?Li?^we?j<Ȕj֊ HS#2cd0%12Gdڨ~O$G+fmպJ׽VB 6S5pLo^j`1Ie:Lw#@Uej`OJ2``+-Lc`*]*Ӫ13-IxG>8 uنbwJX؆;JF&$N"1֤M=L~׾OoЬN<bdΨLƄݘ6wSމN/ep?Q*wcN}eu_=Cß2S4O섫viӞބ׾s *Ub6Ym{*P .*Eޚir[\H3a M_(Tѳ! $^ -D&)9ÍFo\R!F>ro.c[7g*xa*GaTiEG.>wbprIp9S*)ЭaZ'n+Leiz$kSJ~E .QOASιhԜV>-bjj`a| e1G?irY O,SffOa7{x~dnq=MV/TLSОk=Mih2  nF=} gC !p̑iYR>PshYv_)1} Ow`*NrhO4S{ՏsR+m?UA´?pB!jMxФJShOC$<otuƴd'A[cqi[A)eS=aڠƑl- h+(UUUUU{! endstream endobj 40 0 obj 12507 endobj 41 0 obj << /Length 42 0 R /Filter /FlateDecode >> stream x[˪- _džTc8sÁ@bCxwU֣K}B0kw%iI*]pI+4hI#&#Q nvM.~t-=Ov!'͈ɮJC Χy>D2G=%anݧȝwFwit W܄<&`Bf}sa^Jbmՙdeyt3gbt sRŏ"Mz@} m`Wæ"v"+#z*iV`gȡзY h(Hs@P-n[LOFk8@w# rCXe:A$H$XW~>yWE~֋1T|g|h&s s#KPj\;WyzVrX5V2PaO6#$HQUojxϲW/@J*HNř^eGH*x?t\" 3Ԫ?XjrM},d EБѡX9p'T2#Ձ #"4ΐ*" Z%B[+~r$pPC#4Ux:q' @EφC3ָߌ&`Z}T5Ab=LP;TkH+3!d]J$G(هF ǺkǢNwPJ")Su; ރ[,@G J!MyI֩A bC/ VڍI`(|G*qu)3M6BKS|AN/-%6)N. ǂ_NG%ұ7f'V3P+Jm92wf gsHNǶVx?IVP̽yJav!\_]!m6cc+JD7 ع4D&y"g!5Q³<骛T U\ͨnWcwPAut j3"vx9"Fb!rI@uPEajh;OT C) )TލS%3[3U*oْ!1Ĩ4XQ(rm\´?1H›<=O|TWy7z!TST_GS-qjfGzv5I2TDy# T궑ϽWP6". ߍF{ nC=mU\ qǫ@~B;nuMU]iTIZedWxcIr璗-lţB 8̈#oؑ%-R S'rldblU/\F3[.u:] ׀2 X6ąk tn-江f0c< /&'l";9R! 2D[楔Δ7A<NXlG$KvC -~lG}0ݺ^䚊ZrMI,.*T1R]i2qKwۈRȒLxLAڂ$DNbOb_褌f~싑mK4@Ko&Q "3kk+X" N% Hczj[D}1n[ೲ]e%W)2P s|1WI-t3ւ2=*Z[p!.z1QƋ F^I]dݵ*)"[r̙2Qd;ECU?Ve3FGJp*dT`Yn}UBK?B`FJ r0:(F!۪um V.D^݂nkax*8C>3q "U^G$, u̳~`}Uh@T22Ӫ-¥Pi*YZ(YQ\Gx@3KqFPn`Ƚ|Kc%Y 1*О XtN(ҢD7Uч{IzE"IF]k7jFvtVd _~iͪ^d5I"ObB'/ty}:hf~HIK1ؾd*"/eHe!٪ѻ&}(D}%Y:X4[Mi |VycߟcRi`ɶ$\F1 ߳ټ{2OW其nT[q"`~!4J70Udd1bцx"5{SEuRlYْ+[ĬϦn_="K294V"I`jQMAv d`ݼz#~E?6oendstream endobj 42 0 obj 2884 endobj 43 0 obj << /Length 44 0 R /Filter /FlateDecode >> stream xZ[#7 ~?p<Z|R$B@/ЇBrbYޑw6FYslY[v'X" R?;eL_^_D !cZ SS:BK.eOZ?xJK,y ˷܊`WwKyG-~}}Aʬu ˣOw?xg6ñ"G3\6:S?mmt_KdMi Bd,DuDŇ?mNYTIGhC[ƹCdn0QD;AO{7):րV Q'W~o+Z@@]z5խCQO8<$Ljwdx|3XX}ZI)`"zl%%4& !rGb0§8#[O& $vEk#=[  F,EXDy3&36@*h:FUڃlah(dC&qN1ÞժODAk 55L߯F5gxj:Z3Uh,dhP%A(fo(@YΈ_Wet& _L\N2E+ʮ)v,' GgX}`Cz$VbÉWSL)=uB.[E,OgE+2 VɂEc΃e;)dX5< )o3SȪ'U _zR8&vbປhe0= ;fȷ|oq.0OI?P&@%/pyt/Νv.ҭћ#:jpN_,,#-r bðՕ/wM6L]jh;Y@`4&,df+u3Fقk^!UI1}c{Td@.nRV7#Ic`|د:t,2ڝݓOKH_[D5L1yP& `41:yS0{2[Z y@M͖c LO1<bW]a~~Ơ'# \ش7OqDFSv/m6t}̫>P|hmhIt7Pqä|/K[4?|skBz'5{d[a> +x:k*vVƋBzH}}}SOՠ^7Ӿ*' iL܍?!{ g*>2;am'ǜνJ7=E=M|uW .<fV9 %DP˅hi:zf}wZXʃp8-{I4U*vwزJˆ0bЯ! &g{5%=t rC{+PF.cCr w]fqh.fpFyֵ$+k^׻&ADt9ezmDD`:w4ӂf>=Rޏ#zNȄ0;" n&"SEuDWgErqЍ3~_\%endstream endobj 44 0 obj 2001 endobj 45 0 obj << /Length 46 0 R /Filter /FlateDecode >> stream xTjA }7\D!]/1`i䥿_wqwbh1A#HY GGu=_;+Sx~hEV14tEذX;a^V5mr׌ taF_Uq5@6SRx9/1_d\lި[(:VвELE3hCv-]LHnk5{AA12UjQtTk^ѽh8Q/ӭpR ㅛĩˢx*[kq\/PꌺoIp>[xuve<wC\Z('Uendstream endobj 46 0 obj 461 endobj 47 0 obj << /Type /XObject /Subtype /Image /Width 881 /Height 742 /BitsPerComponent 4 /Length 48 0 R /Filter /FlateDecode /ColorSpace [ /Indexed /DeviceRGB 15 < 020202 028202 8A8D8A 9E9A9E FEFEFE CECECE FE0202 0202EE C0C0C0 0000FF 00FF00 00FFFF FF0000 FF00FF FFFF00 FFFFFF > ] >> stream xK㸖U[^@Wg wndG!D֤s|Ie͗o / ]Vp4eU6&#*#*#y|1_=>U.Or%>KqTڝ/+w2Ụv#GBG]4cΑMG 6R׮]1 F{҈՛Qfׇ́ l6Ik+ϖ4||5p1QT$ _bpK,~s{4w:*K_23Ư}r^hϛ[*-ÿ1ʻ"H݌*JӁ{Pc |??l&'{SVGu2ǯs^~(3owN?-܇#s$(GeoVʜ.NKÜnr^p9/9Vy kW\UPO NU,Ǧd-s6G] 9?NK\wnqNm_1ȑ7R'&$}Us:ߙy1z7tÕgK"p#]ę1 NbU2*M'nQ7H:lΏ^e/׫T>Dث8IF`3kp[92UeaRNn1}b/2cEiP'l60{.Bz[`#d6mزwB_(<;+䒐 EȌ[wjQ"tW& pւ_w\Y.E1iO$bx *B<ǽ z9arm^`e̜WaC`=>yX DcjXu1rG2yx=έ _`^qsf!B}l~9 ..BSp?(fn>+g`1];jP\gC,}#8`W#8f\8l%N`58&%l(3=Oɜx*C1N%NkN9Q΃Z .UNGq5qe8MxHj MTAlWrW@J Uq&j{,/ EXl=yUؼԩZj\KޱscqW5s8WՍ87Ӈ1\z':J ^i*N,͙w6F*ƨVhjvy=?B!:'D#|>뫃8j_[a 8j꯭xOEV陂-_[a..د8Y[\_1hp0*FآÛaTE;'v`( ?8Cܚ[Gg6h\k-p hqM*#l+;}q8YgG(۵aܷБ\ pd"e-p]#?Ve'{[wN͜׬; 6#n a#0nSp\mj{p=J\zE]s2;YUYlxu>>=-Ul<q\7IB'Gka\d/ꔏOp=X;͜Tg|l,\Onea~h5~,N<6{X>V;:BQ^. q=nvEhB14|*|.av+.+ h'pWa64GQ\6ZATE;<;N 8Ep[9*avg4;"l/5O [_Ep[t였||z-z>fmIphG4Ž[$8 LL?!J7E%U/ZWB8:Q]pDpDuipnGJMYpdn Ww %|ߙ.pGA!w#ܻZaGTGTGTGT#NTW])#dN&l*#* J,RN.jzDH;fN *ǏN T% *NR2CGlk NXJ%Kڸj( j"$#*#*#*#*#*#*łM*Ӑ.pMp|ujaA~)]xW 3`bW.\סLB-b\2fްtoOF\X{ȿ~uN]N {QW.3PRڱ/3=iqxοθ`%#,'^Jg#\>28ΒaɌs (iKT~=?Oh,w*MI]ݴQ:^]Φ!@1;c"hHn%฼{4 lKcPuL86$3zę!\3yeQNGܫt׮ Jp3DE^%^Sm\118Ude8ߖ8MThqd;fatyq&HnzMp9pqq "ի,&>.Ǚ.-}*E2fOD 2{' A7~t ]R%W_%:keE?2*e?&? 'lP?<*C.NS`w0ό2~m]:plClS &\C_rK*p,u S5\P'DP2E&nD'ɼ1@qq{c"JM5}$_VV‚ь J[%Nc.82'.K[:)27__48ߋndQT]͆: Itopۿ6ծy4Ӛl=vNtu>tUYǃ:mvWXH7J}p-Ջ{]ׂ*puKW:ƽ8E- .ƹ`ni@X^* 9$dҍ`(ҀXEZ:LbҪXHm. DldR,*XNqe>J33 W6g.gJ61:%yL2 bV&j+] 6N|Zxp6.Vzu}y_:/8uDb2+ + + +R tVVn25ܙ܏L}6B5888Z `XڨYwz7PRkq nz(=mlhp[agWy0sBTGTGTGTGTGT v`Kd>5=pp;lఽhHlZ>vk[Ey3wGw4Ko|=pTS7 1)Z9fQ(-0A73P+IXm `쀇Ɍlg8 6vJ*U;W ;53'kFO.Lp3pW%;_`J7cjOjՁA$˺s[=;ꀙd"0{V;ՁA0SYN`: ZlJWwN; N~;-\V;࢛ꀟ }yp]gUv^V0m;p\l\KU2veՁW+IyWpk 9i\>18eO+lz=w{xjOk9iu-p 7G_:/8uB;n1}ppppt)pY:+} PVLZG оp888Z 8vڪvVpJj-8?X1Z?Ca;G 3'DpDpDpDpDpDU 1P` vNYSp=\I#*;aEYAshd8Mz!puv':8`pujLd7 ƔwhrRk ؈ e>V}*41t:4fu;-}t'16LhRW)Zt4*u؁ .̜ 8`p M{a6Y",q^ \pciUUDƉ(`]<LR4*J`{&#Pvzx*8;<v7z8~;k<<wp\ Q7VAIfWH+3гg:Σ!&W98p|MU_^N?L1dʋw~nu 퀯_\Kߦ}_k܍|VC\V~qu;@_;n 퀻q^'B/8pM 쀧t@g5i?Ok .SUeEPo|!)Au}Ĝ4p";9̐q=p-&z{z{z{z{..ˠ@goU>!QI+8WNUGTGTGT+};XZ[UnBI;k38`;F~6`Ha愨7j;xQ߿?X%;Sm"ӷN쀫1W2We\c-% H UG+{9>S5]k6Q[Ѷ \ v[!XNǕ8KPJJ;(laCc_<~?~%pO5Wg\w*a\{`{{Pn& o ?1 p57e<ԁ{Ῡzn&8I~6<߮&8Yd.L-% ث| r2t?yfoW]\4K+5SfDĶuv3ScOvq?!k `+n8J\Sm6pml67\S[49i `"89p ^[Lf.ˠ@g7p< 8ZSUV< 'lQQQUõh_X8ZǑZ{6,}M!Zpq6c,}`P`<:KtpcxɃյY_s&ZԱĩ&9ԜqwbOok[P9*i#FSp,]\c.ZL s/: ao U|rA7$-} nH a\₮ǁm\h) ] \S;۲:8U%wtA7UT g= [bnzH_&+pq6yCTode|OΌܸ1ˤUH-N>\`#=u87L ggobUPpF;{pz=lSG͍n|Oc!mm(Ss^\LP;tݴWj:ogΜt^SӠ<8m8;=u:p~{ap_IfevtT3[J܋=7 GrNQKm3S^f@=B&{- ( '*Rv^A4mz[d[;2 E&m\pE^\S^]+T(d\oW\oW\oW\oWpE\y\Ip=\Ip=\IBnoYPT͠}mT##*#*#u1\2}zVcw6cڪr`|M#PpJj-8n9sS10cz3'DpDpDpDpDpDU 1PlU6kk=|o MG߱9pp;3町UTw:`P`-qzD{ pU ɭDz l᧢88u;{c96 p6E3x>k[>Bb)N5QkOT=設=mn`4aR)ЁkAUi.Vөʰp㺒 v. ,!2k~EpQ U\mZ*1`htIW;)Wg.O_8fD ؛tQDl,DXvN"#m[=*6Ng50lphsT<pARJٳJ^ GsޅX5Ca&Nѫv8.8nB$3'vrn\:07`|O;wo"pknl_ڰ]gx9Yo@݊ڸ&]H.q^#q nj.T 3J1O[nf>X7B'Pp<.ptg\`{g^z YNaܮ6\}ך%S!S{0k Gp=2( 1OBT(C 8FGTGTGT1o`pm0!@mKG;@=B(#`mlhX8Tǡ:yP=y+ YGT3K%,}@$pDpDpDpDpDpDpD,Q> *No bxllb!* 7n''GI6Zh 888 }P?Bg{:w.W9pD嚷?*'V%p pUǯPv7nw~ov?] pG@8#{wrŽJTdqQQQQip} i8r8݁ J{ ARN. (#*#*#*#*#*#*#*#*#*#*#*#*#**~S|kwӉOÃ>#k|~T/Ã_L>_d㯻~"q%=yr7^۟#HE&ڹc[N:yխ pO2[Hla/7PUSNB=PQXZPTpu̫JKһdQ%:[*ﳪҰ%ڸ{~QMq1ݏ/\WW~SՇ7UUWK $Eܣ3i;L%D4Epw= _w=V/f?g:~|&nT:x@'G9i1 1]/jZ&-,w4F_u|SC{w:Ǐ\ >դg&c% rfS\7:[n|7F3pS.^n%fOG֌-qpT>gڸ[0/e}*?MOۻk,86*'4#5"8{z&{)JY8N`l,8YCqܨ{*'zSuvQgݠ<[!i*^[GT / endstream endobj 48 0 obj 9981 endobj 49 0 obj << /Length 50 0 R /Filter /FlateDecode >> stream xZ݊+7 _wBS[P$}@/ =7'Nв.ؒI/c/vll-!Svf1'"uz}/7 7; v__6_7,`kiȚ,hAACd&I Mvٞ >͌54ibC3~&Xw#Z6QDQ l&48tD@xUx.XX4=[iUOD6l0p)GRe/RYȓ$CUhvto2i9Fia!01?rlMCTYxc 8ZqwGi+gyMʼBXDܕ\ 8ȨQތ{[xS­ ?o`Z围bYŠ 7}2vXd37"M} 3Յ<ͩ =}H/u|>C>'̐'0?Q6ڡURFkzn[P3~?2U,@0(^m}PwO2N-k֊  KPU{`D߼g%ˌ^ST 4s9A0@״?)8H &:_8!x.'*w.H*LRsBs%7mCXW5p4 {%)* :+\&KDxN5*  lW1VYcU=V-bhT1JT –1?ŶH50lcH*&gJ6 "M=* ~C4šgKcr;w)XrSc,jg=aaU1a~Z}Fw-mL[kЮ H5;<fha=R4Sh (4J92&)7JM%P48XqSiv^XpAr9&T|oꌾn1s?9qh WV9N}")pU$E1Tv d$]84]vuvi=yo];q֢m}5F4^|jZrG]_endstream endobj 50 0 obj 1336 endobj 51 0 obj << /Length 52 0 R /Filter /FlateDecode >> stream xZێF }_`A꒜;d=^>h^"9%[:0jrx t=?A3찋H7o?u\Կ 4=fּ)Ȏ59>R2X}!e/bݤB=vϹC p.N Ҟ nLXj{ԡ&@$,>V%%S ^?w}]</xAI7KapZ" WJRIH 1p14Iv-LKظX$MIU z9GCq3&1wӻOs#0?[>y%,Ur~]( agTh-IRj,Ș6h1ms'\w94! 0UmC@7i$Mx'[)yp $wb4f`p0S{㮴 5w@pedC2X <2e=j EVg8;|8p(y=ř\N+5{Wq|܄q(+*82^\6 =b$,o`bm -\``߆mLGMP NɆuRϩE>eK/ ;^x, bvܭxnaZph?1?kJӰi-)%/`2r*180˫L=G}{}|Ҵvq˛_|?^nyloyR<#EMCPs&Ru=r8ǂSUl`!D7(4? ze3<ƙK Lt{_<07wt41 s_hX5D2o- CN72dq\R3`u(MBjXsU.g^2Jry}MiQe$-`!?n5+wuyq;hxKO*OƨGz|"P3hhhHCOX.=5!~<7p4wmԼk~?ϯ0Ɛ3{y eeYZ}Y5Vc@C5qV`v:)|s j&z3kMQgL5zmendstream endobj 52 0 obj 1629 endobj 53 0 obj << /Length 54 0 R /Filter /FlateDecode >> stream xZ͎6 0s$EI(gP{Wb*gzwg c ?w;Ri[__/<> ϏO@QkH~>~On8I.-$</kP$GY,,$%~ƒHdMSM!ųQ=d!IK/1&va6iZjw. ԁH4US@qQ\ CBB \n^!^yOߏ9?,w![zhd9,ɓGgm/il8aAw\WmAצh]qPwّ:cƞ aT2k\yK"zud=e48D-!%9-)>/Xas RpnX"XBe*\ߜW7WVA-$pm} .YUɄO8'U}XBDM$ҭUW` 1kc*$SɦummD[ OZU&T3^v77QD;2Z4o_$OS.Yh 6VŰ'9aS1ZQ:Yj`.QUƥL ݓ [gi 7@UZOؓ ,hpc`:y I& m/#뒝XmtluF7z>zfbBI( YT HIP6#Kѭ0B v{v0Be0/UܲxY!|멥MԲӢbMQf YocgS R)z?7U LED7g{dʱ83Yݪ`zNVG9`op2 JZU:c<ޕ{R\場2Md"^@}vY});o7\}_̔iK^Gci2YmJ@mbwgbꝕY"f-Vy=e&5 uJBDi2U* wENb,5 @58k>65;OζT;#8CշN[N-m$M C[æ;6Hz\ MuvCc]6OWaQPiH7oVm,Tcq*F/0=-VɤNq2!xUzz+>:C*endstream endobj 54 0 obj 1445 endobj 55 0 obj << /Length 56 0 R /Filter /FlateDecode >> stream x[ݪ#7wžJ%!`}d{mI9= ,>uϟ~ݹ?݋%Oo./?/__~SH/yw:wg߽@us%,VN/ cl<8#K&?urǥ,t\qN.:\m&?f]~| lAXq0*jA+] Ja A{p_JDHrWN OP>}~=(@i0US;G6!@,T3s ċ*¶]X4=x; ݷ/n:H 7zBs6Bh |./ꨡ -$2&N7_r/5ch/blC^C.EsNt*D e:9Jcsx@Jr䁃; +.5K1L}E76Ԣ2k<ʥd٨z'+eu4{bwN1֔9]EhuQ7jd;^՜ێ _S5 <Kpa8/JlX|q L/Y{2j!GgWv΋XTAC0t x(9B\zY5h~%}1Z1*m& ˲"ƑF޸H죄_\# YBA](7{JmETUUA'6cU1ȲSh;$ \\A?8G5Wuꕧ42qY/jQS̸nN>冬&;̵kF$i#KYSGIe kQy4u9b;Ulmdz&X+Y2zs&n=W_j@mεX}9Ɵ&&|\_d_%rZ-&i74j^rvl ¢X댭``+qytL{)$6ohLͥmS<A-([>6F4+S@G7, ]Q.`ư*}NaLpQgJ|ge@ @Iz7ıD{ЇpD}``w`::s۶gk9IY߷M4'F3PgW킲W}[ݾX gA):1ieCc/qmʷ^P=5k:EwġDaXoh`%X'w:.v&{K荭(u60m[7Qtj=H@4j.>mnhܠ H=%_'J%㒁FF[VF2t5BCΚii3m JZv\m&CނT/g[(ʋI'^1,4YAUz4"/]t'igbk)LBvrEΊpk2w}k{^sGU{ܐ󃹃5%[5C\R- 5e%a{u<@ nKc}^)3\LzN[Mԑ~D)fiDԏ~T~(ex-|ף f2A?{f`G3lܡb#D|_`7$>sj²J;L2 +'Pwv!hm0$қ-j4}ij -z ь. ӇxvwO_5y$OV]Lշ4.Kmѻm-cm}m;3зG܏NձT<dx w9#C驳a," C "ʀ/>LEԏYIua;endstream endobj 56 0 obj 2019 endobj 57 0 obj << /Length 58 0 R /Filter /FlateDecode >> stream xZݪ#7 ?pa M-ٲǰ23@M_dd99tg%]ŶϒIwg:8[__2xǘ#v ?wo؝Mo\,pc΢s=K"zLg 1vZwH`R5qXt<<"5Mzsg ʘ!,b֎Tێ@^3rn;pV0v鳎-w=$kO[\k/)e2bYf̥zɊbv< Q]Ÿ k7q@$K9.-; bA^ G>GdRhI 0S^d|+BJ6vvyH͘,YA9AB43){Ĝ VY !t |`v_rKIAOf1B'j]6 B,^̽ʜ/ cGc.Ǟ׸be1߾~ӭa TK4*xiWGq+eEuR5OhuPm]ohm}=NI*O~m`S@E#}VJm@m{G W7K_Wacbh4!fU?V52:UthFV!FSC۳gS,1AP5,T+`f.k ?o(! t.śj 4ZYPRBrY8PϮea{ٳx,YlTjHDAaٍ+ nH;8\~W7T > ",@A"}Z[^H'}5z>w5D^7endstream endobj 58 0 obj 1567 endobj 59 0 obj << /Length 60 0 R /Filter /FlateDecode >> stream x\ۊ#}г[%Jچ}0x_Β*"RuJG4C*~"2"n߯_s?7%;O_?~~/㷯_|z˻Lw~vsW7m헯_[/O7WbT7 %~zr}2BFtʇ|sT˗_x+1w(b]&?Q]zDg - dBox@N˭T=ǟ+Q1Ŋ*6StQpDg:: #vrGbT7>(h逺͖j.* w+FV>ݏ4 "O/<%؁>D|,'|h[)N)%*/l$A`͹Ls9s,;G_+ 8X,<Tw>,PM>j< &3yf2*JY}& 47~Q]s *" f]&u]+9/$'"NP(n "hXbQ.-}<ԏ}sQdb~$ !NH^zSd0K'w&fdzr f B,Aݐ9:Lhُ1,܆XҶeւExd(mȅ{3{jы0 Bw T(M2@q: q٭4H(^MIH-t_1ʙt1-FNbniH74h*{Ur +{'UK|Z8 ",f,iyZ\y/V)cfuvմOQѓ9:Qd6Cl; `gZGnRT3YpL}ܫnsr@oə%bLAW&̎ȍ8ĔdZSS\, [%W/NPII}\H^#QEc%3ArM.]M9{W1Hx#,n6ݐjXq +sB |'3PZyf*"ƇX'7ZVMսȴ?Y$ OvǒIx\G ؁!sKβ(6F,u&lZ\rIت`kLzQʤgj͓.Ug׉Mj0Lgߠh<Ē^'z|Y?@j'Y榔+^ꄆ5n"uI* Koeuԏ\$g<؁ ݹ94j^ əYCnRIl XsB;<V\)/0^4iQ35cb/ UciS$js0Xg}IzXJ+\) E:?XJZ5YBg9 ٺ9oBDHU^)lEfᢄZRT3MYK+co89 M fG+ԩe~*lH,IwmH-[;85E`ˮY z8˂E-CTnH5wh1k-No\~4 ZreAk$=k#iթgR893<f:6]V {c8P8hZw1;Ô̳ף?R39= 84La=NrTE$@^C osynjQ WG0 Y\_CqHS|J{rc'sjG9ҏ!X:!64|rD뙝T#|ml=;|la0D'h"xCyGyOΛ[P|ݓkz>d~{^OM/d8;kSקAb;NԙoXН4$ծ<:Œ2a ^iz{G渐t@8.^}mphew.\&ARC)5aG{Y,'۫CE@\ f>k7Vy_y-/mnouf>Զ{:},sVاjnljj%[+ۛŝ7[}þZ?ږ,g6d{^؍^?R; wnp{7XMw|dyڮ#{q="zA񧞾_Wz:MvvI[|w؇tSPdm$%KbSQ%/c֯jcBii"}ɜFTОyd1/4Z-V6x_ M?IM>2~%W:_?}&N?/!jAX@GЎ`bbFo$nJsL]*95-gXqu>Uzg-T}]hODO}nE_]oW~I]Mvܟv+mendstream endobj 60 0 obj 2732 endobj 61 0 obj << /Length 62 0 R /Filter /FlateDecode >> stream x\Y$~P n:2a1T?`U!u~즧f`TR(UM}BO/y?svǿ>?_ï11O\Δ k1c<ݿW1F^S |aӋq}l[a&8-}҇ڥ<7| )hS:geK(,^_F*UiLWǂ+?ܗɊsBM4}qTQ^x=,;$Ls# (3{;GzYXNBZΌ3g b$7zFRn=;XgehlP-b /W ܁\`xQ8"SG `k ]i İR!/}ojُ@1j>\u~6`v&D 9VzO<@ ֆB X$cWP]RC\nke,؟%x$=#aKi 7IL $0c16G^PɃ,Yv>RSWl=zNIĩ5܌"ѩV]c7w'5i/RSuJU°S{=PhO9Hz}Mur /=\*ڷŎܤ,\1Xi&KAV,_M1O4ke c]F$Mt(2C Oy95B+Y^$wu 'OG%e6Fmʋjm Drofَ{RB 0qbo 45$ع*uFfkJLP6|ӴM$Y$PZtkD?4uˠīmTI^R`8⵪2V9y$5hٌ /(#-WLu)4/–Msm:F"MAW֊KoN¸5 Yt]H +9B(]sS7 v9MYov5k 3?3.θq +ך8g%lkA%üL҂ݖĦ1^nQC' fvV*4=VޔQ) mcߐ=`ZBh0n|endstream endobj 62 0 obj 2239 endobj 64 0 obj << /Length 65 0 R /Filter /FlateDecode /Length1 66080 >> stream xy`0<}1ds B`A!HHQA0/DBֳzKԂZB-jFѢ=$3 ox7gfg}^/[0yf[̄r&R)u݌~ c !G&1sP3ZGoY/aTD/P5KEdFP_8+^C:$Onq\dQuJw##:)ՕYP':] w (t-UI2ND<:f_nGo^6cl1_W==J K]h<:MCG2<Ƽ⁒=.@:z ђdQh.Z!"ڏ8G<?Jc=@, NWfth#ڃ?55e ',<_7[Q8( \>~Z|-Yz~׃H@U,: /J$`V_,6&V-B( sGqSl =羂~>BUb |.c O,YF6י ;}kV\X*=R)=S FV@;ZΖ! ~Yt } ׉x2 t܅Wn|V!o%JbuJErl%{ț Yle1e96ֲSNv%{)]##L8xF7t]=/݈xc)+X k8 í{ l܆ORXOU>`"3I!=$S40Nx55d kc*y8E]=(91s(a~GfG340'UҌ/!_~~LcVC,_R$Vĩ:5J\r5D_oDWŽoU7h G^Cjf#{zE9XN 5 ¼ |p&Kx.ӈV+GnD!sߎg,{L"2? ϸ-#;/.Ydz4|7S.ݰMh) |/6o6ar*bY^'m}'5R:%q?ލ~GnFx1#G&f"ڄe_d_$,\iXZyG &$ xd&_2 ?@Ơh1s6)f0 b;*HѬih4`i)Og+.m5p$;BXݮZv<OgKd2[*!K"C ( 7pI%>_PuU y5݉~ ~[QXa5Y2գ 33O=ff<3?8sB3jN CB+BB/;}ojzNn-p0y8w { 睾v|8&5ԺX[D:h؄ #76MCB0 N6}qQlNE=(4ǔVgz=*g%iu¦rhagBJ0 ozt9k0k;vg9Clkk$R\[q ~\6_?)'OU~štsУ vi'ƽRv緗 xa9!j[0λɊ֞r+/Nnd4UzcrKN[O\YL(t DH;gbq]iipV)%=εH:NQDklȂʈ2}h vO"SQLGLu^$C`mL"uy:=)гSI8bE82xzg0ylPz࿉[Ɵ>3Bϝ#_YY3dz*KC*-a䣀'}ԧԀ =\r٦!z\=r=#aa_ (D`O~6aj3zIZ&j!(rl"#$ґ^3 %y0&2t.̐_$IMLpۻfpӱr9oJ91=:Z,m֐`Jugَl\ v{>',Q֕'1pTӁ>ƶ0?akI s,^zST6nnuol<\gӂ̝[䢚5wSǂ;#>QDUik@~.& "ӈ(%W$dUNYD0 A{I)E*v]~Q-jE%=ޥWK_*]#;8yv`o{7NPDF-}-}}}vh43M C71' ldRҥC(_W^]ʟ6 Su0 Sw"?Lv#](CA)nl;2i)TF4c63-#lV{C}fΓ~F^3uꅛ =+nwܕ=y 矵|碕wu&6iYkrY;/܉ǟ>#2.KjQ&ju[o|ޏ=8 ~$ ۢq”Ȕ,=27ԴԵ$rKo-}?bmd;#rjBvq6wWGk8os?`{$lrY=`.d@Y1U*VC 9uۆ^ w*Jyev%x)}2(S4d_3L۳Y `I$p$6#J`q+ʤQC=󬓷8bv[w͇|3;o73a'?ӬsbyeL} YU[!|ȰH9!@o v::lM+FM*No2+ƭY \k>kmsk8q7doi1JRwTj[IhkGjZ"ExKe^v\JƽBۼysۂ;!x,X x< &hT>VgY!?֚Ϗ rX].njYqq% 6f[L6/uuffSz|wߒM6bn#JsdV1te&;6ώ5nF=Y,v.mUoMr_豾{`ʡ)dJۨ^yi^K-£ףh[T@I=p?q& WRjN5s0+kK=P;!)2\tB'|jf`a *UJᐅ %4 zB"IZ)y4S>%FnFO<{f*<->3 *EgzGN҄ 9-(hꚼ0>mT:,0iߖ'1>w7Q7Tw^ox\znK1*-sWWuԴ4 s/j|<[][/b1"#j(O؟^OPCH;h ,fa 6)śLf>hЫi_SijUPZ,G+rfuN%cc=IDJE dA%* ngóѳ˳ߣxu d5dx- Wkjk.1G4cMJ:sζ`-FD ]Jژŋ?dznTy xl_F sJlYidGT-uZSSxi73 gQ־zGnj.&\yQeйR> m&Ͽ`y1hhL0`R͛mƔVx=B\D)&5V(M_e24PArPVT2i`&+.9Q:K&(}J e+6 D@P0:e݅OnQA=W.?w.ZvwӒx;~:睳mғE%UK9U\r}n޷S'@Tryݕy.Ϥ{KZ p5&嬩u8btyTU !LGtD6GJ'x@5)FӃE4b6Yࠒ7gqK8hlؖB6-DSՅsKljFcmm4|2TTA!n@F z.L;$vkd5PʦlYgd$#(e}F /`8<4Johj* v LSӑ +//OduMM_?=P!p~~=?s[)ݯx1{V(/jAS'9[O/Ya=۾¹աmdxR陫w u&5ScDSׅ&u?$1JNQ#=.Pkc\MkV%dY!PkižLÎ4D-UW![lيv>CgviT4@P\e5* PGk<0oSuXI़ܺ46 JeL3x{W<kx>649->0g͎Լg#7~b\ֿY&WHsb>Yag^/ )zCh೷Evv+i#'SU+JVyZ6կT̿QidKEb1)'Rr:U[ `"b P\^tZ`xM66M٬TiNt:*SR.WCF3M&@farMkXޕ5٩&'zZµav9h8!݀j^eV =kJU5 БohK(jmenʵt33L7g\vƜ+k03'[>BP8j,B=zI.Z!b .KJgzr~^;'3 @g6$gFG(UPUU^]8%AP yDD*:UD~ɨTx]h 梒hDAT܁7T`w'7aﲯVJ&Pqv}C`s PMhY≮2724eQeygfm0|u&BQwPZ}vzfx|GGeXPd<퀣f@3^W$YҤ8۷(,- Ʋq+B*$fDoWoә7h|e>e tI5Pfӳ-O*IFYPRnUDkaS`5Pᐄ5!I |^ۂ5Du 8:nyTV|^H|z`p_Jc)FEJjmn6VIU8ES[ՃɎV8VΝqCAtMˆϔpeNdrAr~lqMLlea0Ț'L}Y꒥]\(Y-?rӹ&_+8 On?}yMxOW0~:;RP\0{湫Ə\(|=ANe5Dp:sfҜ\IV&86+n XEb6f=tAWJ隢(FHXCB#Z! `X(UN`U*he% 3I7ZÎI:ÉAVj# ;]L$|9asr:6PUPsp$j(nǬ#)q! ɁS^˼yA&;f:Nq\ D0102dL]&DU]&e$] Aһ[S.z!TY/ٳ pF:+#t`dOY|֙fG65 =tb&~O'>ۇȥvP֠@>@qG!K0ˣpX?"}pȳjuyD*/6v^Cmm}C,8dPqr-`lMTEY]SRk Btj#1+?3-mh<`,|?ł" ao4$b!bS|rh${G斪eUU?mGQ0.o4cu0'*pE0u(3X,CX+:!UZ#62]\n*S3M~_gɩ谑:r?ڎOؚ*LlcTYg.?*`B"30 fJ4#7%\9sFU(̘QJx#Izo̍+^‹+騹yy5l;. j 1~!0=fo 3(a*}JjDĴgtrD8B4~ݑVϸ)A ; @l0pḟR϶L;nåooo<54;WW9G-#lMLZԪ=JDm*ͅYd`a5或1F␀j@[#*w녲6ɨhK D6k-Psn-vFZPE}Bk` [Zk*3M>Pz&?TZnZ R;ilP胴Χ*@: Ui 9/g5_\reyϯSŪ~U܆؄l(~+P|OoNHG*s 胸TBdLG׃VkS)9̵840*~w @$B,rE?zlz6qhϊGp|4ZoB#ϵOԥ`'U3IH(|Xi$I9V^u>ҿeKOp#L3QD ~r~uNjJ"!s>#V()(Zuw+y4Q }Mu;jz(P{ɹ-e!TD7(I*~}`_kmV=gMS.k2mキSgO.s_\pDZ*Dw^vV9(}RS&M̰#$.Vz6llV}($Y[ZM{"&(/ 8&M7EԳ* ny¢$L ON\mTԛrHqd%K7MԚK'ILz%) 9OZX[{MP'^+NԊV02^${TR}zա_-}6|E:LLu}Cfٝ4CtI+Q"JĖpͩ恺ueNV"%"qku5hC8WUN ydyteNhVrP[uKnQy+.iY\1uԸ[/nM`tϮ+: mΎg^jH1]:bUG6/ *`@%T΄|R4A农S|Q`dل|J$4IQOtR5F_ 8 z%é 4Iy$]J.3W{+BrbfeO1 lM75+~]<ZˮX/zfo;yxNA`o)(f'="ԋOަ[G)lGҟIO8=tձ=XS51Z{FU?ac!UG3e }v6VNu)|LX6 Oުh^o'#"0yTdtpr7c%ry PA%F¼$#KȜǎ50Xݸ;6B3I4A;TqbɦRT* }=3Eg>Rjٱd{>N>"^]O-{vJjڑ >ϩF3sku`YIX+][uS\Pi66>Bne@FQU kL; FCjL&l2 &br$~,pꝸIv|Rΐltc:/}ZkmF7LRD#HUUZx+Ob$ `S@ H# Xb*G3UGMwYeQy?}d|ҍ5S|g]b3 - _?"d;NYre_K%ڧ~¨iߎ;`)},6oYň qIYHJ:{>-;/[^xGg;J)3=o5HS{yQ613t"Eȋ\~!&%] 7lF5ƏRF5bC^kxM{dj$/IA4|.S @S'p=LI!ʝR"j 2(JgUDҡ<(lyHyle|$@= l{7;XVd`ɰsTDTǀA&oV/3-r΅_.kqֻO.w^<$g֙WyGy9Mg?bj55kו3|c p{i;Y7QQO3޳=y\nUݷ 444y$!pKNZ]٪T59W7GI\vD.0u:S uTސ۩(+j⏺)N\oxx{'OyR_X~//>ݵ/dݖ3.grnPa?FfTKf+u`|qZ wyN3`$y=B|ۑX|n"O]\9}#_5$vB :*%AO4ab"W9gt<].5k:Ns'ia9GޞwNq]?Vh$:Gcmd5_9^$/uX rM0r3XmbBxETx.<-;/јO!R1ib|)iu FSe\>\N,OXF*XoZA;]VE0aC3Dr~ZcVD7dPJ=h8 ֽ#q{~ \ؕg\tcڵ:P8KѴKʛbXGlUؽ/bN';fϧ xV2QVnV笈fquVl8F{,ITpsC]}݇?wq\nL8?rqngn?Gr Gm%6zÂ[̏&H $ᶻ+a=AB@ԎҡMvn ,"!NF!b9fyvrgiLdO_.gF:`Z<͎#A&u̟]+,DAu7uHv ۷t.wvG56wf#iG /FNfG@8p,tF-!SGxI18f^C(F"2$#FeVQ8-oW*n(h4 ưB.K;UQ SA9654jmfe!*7?yTx>q;*?O%C/ɏR%TAVeUnv} ͚dWd2:ںXKSLJݔ}}>3IΪK{E#ێY(CiPncZ|wKy-Q 'FnC (FyOZC R_2ΞrOsyS5,x1lj[\FXQ$ͣr.܁(TڽEoNz˵HVh(PZo˃( Xil T~+ڑd ęx9fϿ-X7~Km;"hwlDA$<^$I.%jSLJ>RySSn[^3MC4trM+{;o5ەzM5GDAZOgЭ+^8Eus%Sb*T<`4vfC!Hw#kr([|Rq7snᘷ=c4#xp*`,^s %pf4f b}Sx77+e8744x;Y˳ ]_O| 8S,>Q{WdM"[^eI̎`4Oz:hisq:uq âb*9:oRkrIi *,%62wl.O?ɵC󞠼=4[ZW9* k ^i Tv[-?l Aeߟzq!JwzW?E']9?o8q#r }@Y&? jGGMSoVnV3ݪ*Hus%(o%74@f rd ؈ٜ _UWLjUzdƂhi9"$2JŬn2`}.;$EyOdUUϛ&+7Bݺ 44V̓%^aP~pގ0 iPCmxjZ&J/)2=9YV8OG9-;cUmŀ0m^`zIRR(b|x +y_|-x0x8рtät7qjk[^(@ŕ=9R.57c!,뿭,FeؿOE⸑&#}yvЅGZ^y_x={; >{K3WA i]!WВz\~7Wd)[  DWe* @_B:^ۼޗGAL%h #;3".μ!E/ 14#ƥYDXJ)y^=QCҴz ڮm𰒙V$JDIaF4`SvNqw)N/#Lq5Вq ywr0ױG,,JbflbF{^-Ӥm]]0{i쵹sd:QnɨP64zF+7j0Vm/leJ*_{o_Uq/Ϝs}=77rq9!$D IUnZVRY*nUZZj[ЇJjn~ߙsn|Ϝ9s||U(_l/wY%I؂)0S 8jW&tع]cwRT$n8gdjQK1@OuW¥@ɓ|5pgjd3eE#ѓ6*$R2ִӆ?Z 4@^^Pߪ ڌ*klp4M0ͪk$I9b%ޘmxs 鯄~ TcB Q0R:'w^~ m(kߡrf0 gYYu4ՖG,̋io͏%yՑDm2ea* [*,LڔCgiդew[wX"qkF;/%rPă66@]K@(Qa+.nLCrqsOkHI;{btӹtڬ!R@MO$DX4TD#_y"LH LR #Daq$DhA^ !{ ԿEeb TFr1 mM.vq.!"y.?Q0oȹUƋMa]V]CwE2frR }0NfdEHgC2g932dO{>Qiɡ(uZTCtL\\a 4JC.C+hpJ+ee&<7}esZkWC&ak3*F7C*|=n yAX!oG:UI4-9j~+ an<ߚ;_/QXbO_07;k]jʬ":ڇƏ@Ć7VE\#墴!]:rx{챞w{ؼ_MyݿwR֒+Ol_Qb敇~oozMΆa(ch$;8w)vkzmBh6^# 4P+\H^;])e~%|Lת8 & zbj/hQ(mV2ZǭՕKdt!,@N:OJ_bZ.Sbmqdzy+ =y>?Qc՛_N?T\*M|yUwj70_|Ϳ9pHas-78~]my6mg){Ċ_1+nW~7y왗C- i]^V^zPHñvx+hڮzP{PpZ ~LҊApx`Ï &aY=Ie.|1bBB#P#m! dh f*@xd?88z^6*`&/(gl3.ܒ ՗ }N|; tSբĥvF6u.P7i.3o6h[qEM2^VXb2Im5͉E#`&J= DThjL kHh30q aLXv : "K5$v]᫿"/dO{b5^q[h瓣a}a5.` ]>=ɏ;緻-.%^;d`$U}gJb¤|L#Y,z ħ/UM.>?}/cG?f*>ĤGc0 ]am1SWR)V'tX*+q<.`.7Ʒ_A] क़َ_ 2W02 Y[xVf,Y[>,r20]i)3u=\&)74sCdn-9-#[ԺU/^8haI%f]$Qs:"01 ]dD[OIM7G\i/en:TIē=Ћv9r'ZWhk+TK}HA+T}Hnd8#+4.=oR82Cfl4?Kd/YfDžxCZ,h'g2-;. ۟Q#2#/xNgsF nPUE_l6bOwNv>U?+|̞ok?%_0{q R58|ZpAxȧ10ǃN:hj3F#۸HV'"Cl;l2+ŜޖjYj:REmE7f$Q'-j#GTs,; huuy˞l_}d2&SWǚW]xlVW?S]z^%NƎ҄^2ۙZK'?Q{ўaz ,:Yqk5+ps~0Hg 0ƻ?:^x#MB\edYŬ6uX;{!D#OY|11ũ ۼjQ˄B_ ,n:E;gd7fpaxjX-f4vh،Fg2Aka2)Si5! V z]4ދ"8JL[Q[ן"g%͉HmM*CqҚ"[.k(ΎaݚŔp:\w`>,Ad?f=xlBKZw8Q^n]kcjBVbX#LUy5q8sƞߓ/8?mOD0kn 2JPV5iOxciXmϖ_pzA+F+r&Q-z'>j'DT"O"Bڭ۽m>wً:?F`]xh ]+GpI0:`\;؜bVB`<˧+uu4 _fϨUD*F+%ݦ)*7bH DL"f51eQ bP]Fn;͙)hSZ{}]a'쮢7f 8EJM9FP$."8<ɝ)l?g CzM'}}n%7߸m̍’Go?m_rlsxƭ˖rwd.G ʌe4}?A'6ԇ|twӒݡldgjw~_,]>_"xqIzDxU"]蜙\滝{7ߒGѣ>FG Wf8ͧC$ʋb1H-bd,HK:u)Y2(,#] 󹜾a,lk:N=?rł֘iCNeV-Xٹy)sVHlKml,Sq_T,EZ)l%ʒs/DN PJ$|xv,:U:pӢ7X)59pUb(Ȧm:M-^cTb(,o?EOlx?B<'Eϧx)GgPNqal!Z&Paa1囨">5j)&ce!2n|X~~8H8cfMlb|Q:cL#jB]rU*šޥޢޮ]%cҚ1"DFf𖚇$W᜺+SNEn*hd=WN :ȿo̻q.Q&Y`Ztm_ C/+]@9k:]D+:~^ЙcߓKDZ}R,+8aY,(['STWP36P&kkjDZERR-b./ <d..`d<˄C~ԮXAAdR]h4jU?Go `zf{z~9*x`/ff613Yt1,sMC33hw>YC%iƩ:Ӏ:g)XŨZ^ f' O<wIa 1g+vtC,k&>SM#FWyKsi\n"ģL#2#K=sB>F-\LJrOMTkB>OY8P'i_!XDeKL>W%HFP8Jh$15lMG 1r\d]qu'\k9r;3sKVF`Og mQ*Iq͝˒QȐ8yRHlUMlHbfr Cu+)ldM#{mL:АxjC54l+S7TW]5hvUWu_.=Ӟ^}_ 7~nCW2וxǝ~5;~5;1;sPdۈ)2]Vϑ`m5m9 I%rx+@>׹m@E111F,& 75qs7I:uw(fnα:f;s &V&aP=m @R" T9" aq+0fM`\Ǚ ySe׼2]nDɶ?9xȍ nGfLjH'(kոt/){# ÈU,h`z 82(&-Ug9l6m1+RH#tJ6N9a/ 9{LK`U}c$[2m: yV]T,VAS^C~[>h|:%]gaיXYmFDUP3Ȇ7fﮪ*iymտ D軕4xe+Vڰs!2e @^'hFy[M!373f:n$7v0 fŪ.z7G9}zSP f!;+j :ݼ\;)1$Pl6:!tLO:JA&+RCUk,6Tq?tHB&f7RA݂N)eF_sිȕN$鳈w*īJ2Jq-ogך\!V_C.L*t.˂}t%atp{E_&B:vfxf1[1?l‘hL47͙}ᬙskW8k`cc~ X"eK'^ MGL& KW2?=198]2\c1R1&}Bw>mp\丝*;R@znHǒĕC髙KDb]! nAE!jn-H5 (铼Q>,r1 @AQ1K( `E:aRxJI@yl48& T`0svjn;JpJ(W3oj랟ud{.#2E tmT2{0ߣWʻ[i\ߎou:!_̧1j]nɌ}\KdM_5'5(5{ txκ)Rq <;|nԻWJ?%: %AG9.75gj,t BMO[^ 2@4M֯קFm=!V? '&{S V$JZԌd`~|MԘHQ%!4/_w(?1l2yiKل/7!c͸\FÐCH&$LtOt3QyNrWYzKwOGJRY#%E@P._9M& әwmv:G Yp-޼t-dNަM~/~ m!'J QA,*VwMC~݁7 (3t*!d Be *GB(ښ2BB҄͂`eKЧl:a(p$GRu5)P!;7C')ֳ o .s2#2?r9DI~|~plN56MheUv::ۡn/Z.0̌ʴx6@h^k vտ_~ji'។>~#'Ğܥ$./Eҗ1Œ1g>.ɔf"p&j>SIg2B}[HE{k9!dn0hR>|Q%g 4]"/%Ɂhܚ|EFqV(aωP%d/%obNdޕB7rERO~}QH'0 #Z mfXsh.!5'PΡF^ɩ7ˀPde$=Am֚Zs-zO IcwL1R_7bWzMx .c-&^P㧤Z])/%~pJ(׀$y&s,~-ŞA1kbV;JegRVG~dxp[IXB'w#t4vueqL*zL~atv*حj*Cq8( jsLW;/qf{+{}^y۠lWʋUv|8eҤSB7f"72sf&qw q!&(IUe\S֘*++N*~7 ”+*yX_)dP"?Ⱥj5Fmw؅@y}x\ǶƘSPBGДcS^NqMJ`vH0)}#j!ѿLuQ߱HS%mu|^drm4!\88ON; ZY@cTifHN9+xׯySPSCHK[-TyCx7,MwrLY/[ꝴb:pkNYѪF 6LQvSIކƆ̅ $=>s VX)^QǠ@=rJPl7% TvϜ=I󣝤Fi+'1>xh8;^_50ZpPȰ TlPw.0(*_RRyܭtLQ5`eKV2ӈ%$ĉ2mڳ^S*LLyy&WdJNRN$ZXkL1QdZ}"SZ ϴ^rn\zv+"'1W)?>VQ?iz$h<d6߱xK.%sX#Yt%uα'Քrn7 uT+ MC8} f5HfsG6]:KPӃg9k^d$J-JzyO"Zs1֣5ȄO Zm2DiywOs6Z[{?0LIN<2;:UmtiT.٧-DXEƪewC?l?P!cطڟg}k lNӖ$dyJVe@vmlumka*|lmckudWx-{ sT6hxdޏ/;ޱ~ґ=$'׻%K_$ʸ!7;Q a#[# 1 D";UddXJXbI~{fQBBXZ|)j$ĻwQ 3Ҝu~:?:?:/Y9O >pT@ӇecEg o]z 0 .Ls1tK(.w yɴKs3Ү,ߧfA |F(N3$C1gJ3^/a V֌L$Nˤx !@Cx ݑ -rXaOPb '#$ib)!jF5gF$)r9@~\Ý$?mHd$Qx$)Y)J:rdܨ-Tl=k{\{ÚÃk$F]~Rgyr{ g*:QrfEAgGO!𩁠o.pEoNfDZGD8aV? 8ֹcJ/ ^HO5S= G37boBvY`.ђ63lϼ2y2{䦡E6_d&={셏w"gXdx*{MC.4m_g}S53:{t[S8?8+uEI!19vV+ a(-j("iS^MV-YW(Q)ݍ֨P DŽĖă yp)8%萫5i'U"'iNgY%?3FZnW [V!njXon^хV[ \+RQx7,dS/ĀuJL^_KwbrĶݒo?9u_p "޻zTf,tV"eeӃV;6^wpSNaW޺u?.QEYCقJV4/.y:')uܙ[XC+[t*B%g[Ry ȋe}.Y(d qa᎒_~Be#D>QBUccI8K کdF`Fűp,?pe9ؔZ jd< :եxԩ%hj~+\E%BIs {L SRWy/D<iZ'\@c!3PMcM'ԸCN\&#]UWgtN>ݑH $.$kjU QGD"8ޗjtqB'\b&D?.h|K;QV8rxEͰPb-'$*-]}CmdYo{l'C1SW՝j:^2`"&vXڱUZ݈g/WoWoTݨT?SBG_S'SCAAV;-V~AN}4!hxI12{bhcrijK¨˨u\F-EbVQBHQ{CkS^04.qOGwTI;A<|Xe=_=(M4( ԎdNIt| %΃ru42$*49sQsTV$wrkGTu%镫rj< ]*ǨIX -ҊEک>dNGn[f0شnsr&˷x^vfh60+G+v|ˏ Jbato%Tr{JIeZ"E&3 2΃W-9 (fM}jnhqj.)*Y=CΪu0pJV5djYxͥW9fϬS;[TWo 2C!-o |!sPr0m$!A1K(϶Fj$\,z|bJUd;sXb0r!\Pɀzp,W#OwѠ"MUjDz24ֻ\eÓz2A z:[}<*_RJrc+L[(N͋K f+{y}dU<"B@[;,C2'1g2^?>-rg('9zCԗDcciV[qqD,\5.F@.*t˝ fC+R!!nc[)#H6\kh}pPjD5bϩ&+(7`cg c9=`(XVa7rπч}tS!N5_2֛HĘF9בcBFRt!EݤTgbdk)4la4t]`&R VPޘV9 m,@'gK8/hrQ=dWWU-i&(TWBw~rva쨑rX"ĜZe_{}/* F/jC#C;nciz$>xlhh;#ײWjnY$T?'w*Ck$[ՠ߄rt 3A@_Ck 0@S/f2G񫸆i#_:Upgyi\τ'AYnGy8_'Do@8Et k7t9z-]GqڎffAwB)Ovtz|c>\WA9CSE|vC dI;܌Q2F%7++;קJjI@g G %F8pSyei-Vo:>v\S`'͞k[|e/-!.tQpd]dц;5ꇦkq/Ȼ{}3YYkf: gOI"-ɖ}_E|ź-9sKo^:ez$)r>Ojda10R=NX*aV f%8$2d_`bv V /~\Ћ^`1F^&^?u~":J^%zl@KtsG H/`)U̢B} MˑNIȠVh6 V!!_%XkQ*:Tf)zUl@E淡$XFj]gSXNZb)VQx2դ,%:Km md`h#$z\`h#El%ȖKmdL. 6r~K%F)!Ւo֑xAa& |ͼV6b ivg7QCӈe4wQ8@P8B?A| p!<7-z:1] rͨp*x1p-q <uuT<41|@h|0Hx]a$M/̀P|_ _1qJ:x¹j(C}j. m%-q<&j5wBz&|yDs_RJʃ\:=p*-q|.>O.SinfCM z'ү{!K $ C=wMJbRWL|]+yc7l372gA>uќHFە\:׫w3JR }$CӯeDf9MJ5,#oiWH@P}im׮ ׬i{澟|ڑ5kM=O˻cJjLuJ6[I>K:wwZhМheeɍ2R.Gn \JHZ 9s-vV]J;Z߰B1KWcK{`0?# 5 zL+Fd4h*1e1-;i DeShNH\ό{jod>lݾmE;߲ݵu|w"~Z[_?H":7^~:xt_X޳}e}׆~N7t24֤=%E|=]]c\<& fHW-=m+׶\w{Ww;+{Hصa]d[4 |qꞶ5/ֳ3c:xoIM=!OzUW?c>WM_Ӷi|3_m5<%>kl_EXC|ɽcE[' xawGߊ5J~-)h׺vImVmiu[}c ޵{C-IO;TV_o׶4 ׾b:Z]+6m_GhM__TjӦMEk+Zѵ6omgjmߺKWձ}S% 9M-3g׵h75Ψo3>avÜFiYK?6/ttuC;o&}K?zvB6'WtmdúPG$$6cE:H޶TF[ӶZNq!Q;+;z;7z֎ ku;M R>mO }54 }g*F uc[熶POuҏ_$ 4m|owU;'|9cjlʕ_@?Dк]Ύ}bϤH4 Nk{ /׶mTݛIōjmoh說cg=Ri5]:W¨ѾOAKw[F(mEhkJ"<0\FM" ϫ te,*,NgAdqII: ~eY%_YQ^]^לg}`$W)xtB.1IH`NZ{$L(t{>w=ʹ6ӾLC_m}fWi_m}fWi_mr3mܪgnuݳiisI{k_V"%. ~7|\1Cpk>qqg KC(}#eYe h0`n%VO~uYm]}u 5p2-R ׂ#أhg#sb@L-;0${?WpEJ?shnCAR)\*!̇pnuj#1mOo*=L @Ju>(}Pǐ. ~ET=@VoK1iEŰ p{qiX2v>d-D2R[-pk)\+O7΃`/bgҰm@Qs4Np3C8m3z5 up1>? 0 4 *l4f)[^7W: eo ;L5TuU9@@ %[>ϖbpfp!xUo(` Q 27#+mujj \7́~Xgt$m \඀{^p*T+L-S61M zwbeb]jaPM 8"'SsWp t8:5p Ceġ2qx>NS)h c!iԘ\HlU<i 8>A7t/D;svҦg~Ě.'O̳Ь}Y/܏js F- 0ghߺ#g[nYk)iImrαHI|5o}`طh<~LS00SIE k2U sIfE h:ēt?+$$i:t'si&4ǧY=>jfǤQ@A&<1!K3͘l:Qy>L]쯿=ܰ,nپm8m]Q=cc˖XC¶Ap}iWNJr{fx~teCW g 3m Lobn̽n~92k#wM w=Ayta:}-Uh%b8h5Ёyڹ)7gΫ=Gd?Etȭº:r Fehtyu&9nqm OEΆi+_򿷷K{IH{6a݇ tt~ 6&y)f{{ z7 [F36@θw.޳HH"Av0mz1܄l) BXQ endstream endobj 65 0 obj 36939 endobj 66 0 obj << /Type /FontDescriptor /FontName /GAAAAA+CourierNewPSMT /Flags 5 /FontBBox [ -21 -679 637 1021 ] /ItalicAngle 0 /Ascent 832 /Descent 300 /CapHeight 1020 /StemV 80 /FontFile2 64 0 R >> endobj 67 0 obj << /Length 789 /Filter /FlateDecode >> stream x]nAEH,EK~ y0x@c߇%غtuQnQ=wnmo۱{ۡ_MӮN|趻]y}Nfo/|?b:SqxΖC6=ݿs돝N./m{s~^[7)bw|;?]'ۡu\GM,揜+Ԅioq]F1WVX@|>°mWX}^+JRRYU}OQԝjA@eUTPܪ*8,+> endobj 69 0 obj << /Length 70 0 R /Filter /FlateDecode /Length1 34720 >> stream xy|T8~ιk2w22@ H%,FED2F+m]jKC@+*uVFh_3}ιwo8999g9LN"A7&-z!loҥǮ>8~]VӚ#$ɐ޴eۺb[3BMڊ 9柂>&5AwWZ0M] BHK[c-Z@zCkʦDkr壎bHm] Bd}>{2@>"MNuzlvJ8/ Z8jtlq'~/("qL7gr$Z@!nFgs$zG}WȃfK$s-Z _}>T^ uGȍh;:ڎ(MC QF{wP9:mB'"sch? 2"?j龎gB[ѝ=| QzPqf} Am9hz"1=>^|)7zQ&9 5<&aaUf^U1.^Dh|-lNfȜD>4ͅ_C\zt5E4`i46sKtGM0 %B\8r h')Wp/2ӑr36b/'I!ׁ$q|עf8y(FIX"Qt7za W7_ M}.УSlǓ"|n—f|'~4l$'&n34?KN;5u?͔dAߊ~+ۏ^C{XFlx!0Q??' AII|#l%#';py\Uqu\jw| DMGxHxTxN8)tWIHz  Fҽ̟ h,P> @ۀGcΏT|`f5ހ7W,9IyH$fr73rVqz.uq۸۸ g4%|3y||7CCCah[k~IEzn>Rp =rWr5FRȯɯW<J;e S<䣀=4õx @&NxTϣ)XۯDM#RcǸ[{X߇ ؃OOS(}ی/COg끏G@.,%3.82 H~`Dzt#*ŗу+F "х_$.}*p>'sw'P7z7w_#?'Ÿ ve9s%&#/GQnr%|WTY2m q s2wڍӗv ;]|0&ʌ%rl`/+|fAZ3g~ = $h :U~ #|'3ke~ ajʴ)cX|ƲSϩRY1|bYiɄƎU-ȏ䅕PnN0y=naVd4%(hLMdV6hdΜ4I@FbDFCJYgI) rv8\qf|&*T5vRQRΌ(x03R<71 p8 oL%Ԭ-MjfBw{I1h'Ҿ{bOM$3L*̬I"3 R\AMbmj53p1)<1&"SfaR⌔ 4ՠ=c_FkbU+R\a3Syй}Ɗ#KܮoBvPR.Z14L:ڒY fk(0^"Ð ] ]d4lPRHӮ @Z-g"kH8U%f8Ѯ쒱c6{,V 0Gɡ2]uZES JOۧ9D~vNB O) gT,**,49Necl'HЇnuŀp8ZTϢjZAk(^KZr([ZFKz%C"}u?v4U'%E+W(54.=+O*Ӡc .@48V Lj2M0'2^ۯ+YVf9j\gF{ 7ӦrVvq0aPKWe8 XMpGKW) vfLC] ͠,-yVŀr1@5+հ+џYQȮ9ܮ,g\HͺpՄ+aS4}O\'w.Yb?8gΥ+z &3ɇpX.4&@K$V??P+YK7clFD͓yx5/ʘKW%7̌g5C>&ɝqc2c$Q8F(у:ycyU`K&¶0/ЗqA z.e3] ]n8{\]59yp\` eqY@~ D^O.!~o(ɜxX "@ ,>/Sn2bXVp}Lpje}f3}&>3E | 9 8uT` @E|:}`ݧmy*** @\U}j^Q<(WTشC,ɇwXovJ&O%%W^a4us{pF#}moYz΂[VEW?vܔs;qO;[ ^M[iǎDN{i/Ǘ .XqCw~^@t1$ A|>{p7R, "A]>Nf1C@qEQxHeda X ^Hx@Y r w]a[rӏM?Goe2hf8_O%"86Oև7syȎqF&+N X]gfsb^l2^譑k*a~\ow/ rսֻ`[*,5\`jBb2x48L NѢ(t^ܓ Y ae#Pp(/(H'^hHx-dΑvLYQ,d#ÿveU&鎭 ^?]?J T%6ף %auVqJK)p/Kv{;ݻ}G/7nIiտ]s__ՂI:Ȝ%yYA3g&= gH'@S%b TU]+V϶%p)ߔ^rP8ԋΦP18(}VhqU1MK)j}2}Vv0>V:`(r.n[U5LΤ^{Thddhfe㮳9\fGkżvbv.:0h;C'Asg&k#Y`δ^m嬾ԩTVIGd hp&Q$g$O.~4nd;8 a+ƛb+TKycͧ7xS|~<  *3JTX DJ:\,MyADqdǓAo#g} Ȃ'\ywƭ/"lT(!ZJVQGq?moQ, LStD!9ioM|D!mg\p9)3MtnV==qHv {y@>ݱy VSRP-8U@UUULmT@(;.l.r{_>?y=GܔcӯgAB<~. F+4dl|ެ/EEEI@eܢzs}ssQ]kF5jBQ|zȨ}^;nKWnx3l &O/PUcs,bӋ͟leeӻzth2:XlcX{,[NX8R֒b,t`%Dg)- ' uV3uPSh( r 9DD (rJ>O9oSO)H䂸0rT>*@sQgǀ 4/nڬPWxܦ=޼gDE"ZJEMt24LՉ2\0yIrj &gAU۪cO۱X1t!AE$I*DLym. :l*%@QLRS].rB\NULS;Lz\Zm9)#|d=9ܶhwzt+;-f~as6{7_WO;g33*(W<l8z Q0L,(kU0Rg[ `U/+Q}ăJn!GJФFt +'.EvvRVfY ʤž3&gLNX)MejX1VЄ"9HY.fxaNqS0F`6C<FW^9\9,Q-Gp3 :IJd.1kE%w=ƹwl~澎]Oߴ<`5?!z?}k2'>1l q˅qOqsJd9YO.]Mn u,], V'I19 N(]1N)bdeh^3LIuj)y7yi)V2ΰK'ex FJ"O٤񤑴{SCFH 'S nE)yڣvb^O1+#z^?dۜ52^̦[o¡ Os7$} t=MO0ܦkL!%ozL ;mJ+O>F]!T`@% dfAEVZV@ee?NF@ډ޼$MuJK]CY86qL*F kjOKm~%6N0銍9S} q_jkZ6cAM~Eo.wkphIdy ZQ.^$ϑkeZI)$6ErJ\%9sڕS8snNʳ*Ahj[=םx{y,Q>&\JyR~lY8w0(Z?b!nh0x,įȫ`bʧ@܆.(%a|İp50Ae;,.1 `[[5`8of7k̘8}ٰKGϜ.S7 go괋9j% $@.Ӱ ͈i[<4pl"lLڵ{l'M2Ox0{Am+:EӼz`Lo߫UH¥'(,uS~#WiIJd֢%Bel|iJ~X2! 7X,GA)anXhRHȸj@ )yU!4Ǭ˝50ƂE~!_wQ SpQڨKj Ź&S4Ŀ\ 2saAPҔu=x3=&-^wx,VQ,B<whXqP vzĔۼxT6.{X(\[P;:w9.%<{…Go|sl;of=>zV=o\yռ v/C|G`?;( 1$r&K` L>-jғQѪy-LWI Z GĬf{tfL;)E|wtיrrN58v7M#5:K'`QPг"]`_mo߳ &q~l@%ߩ 8˭b|}VN/E6% RS\zwҤq3#ޖuՋL/Qpn< 6O.صw7@S V씝΀'yw=g xDɉ8x 2jJjrC7f9eZ:#Y%H2e>|`l ZFitTʨ@N=]VuInFQ_.}Y5dU /6*7WTzv7wdN]&f]x`?J?[ |,2M^~ J\V?ЄofFӃ@;fZgGRL#M}}E!9XMa"$AJ؇eF`}QFThFg*:tH>r5 (. QDs,Y,X2<|frl 31^WPT&`/H0qE+IPUg8H-q32تnk9U 6ԫav8 į@*9I@ⷀS+@iiXVp[Yv%# $RťyN6mCOtX-q LfxAP2-.qL$A2R:5{~0NWydc/%#o5AAumʸ6 y̛'Oõ)bn9f1 o+3;BEy]}3 @]]-+3frvw&X8^xzp?on/f/=CE ݿCdfN17/079! $<u$f4yyvgƲ^}΀㌱8dW٤̮< N+L=zQߏ?0G=oB*ߴ W ^xPO~eB#apD8* E%#Ofʐ2nhZY8H6@7Еơ zTJf YՇE+4y q:8ރ#$1@,7Uza0h[Y7B j vpkx` 2L_^2bsB)- F\Qz2C|`-`gyzNg=a^9KFdbLd7&r^ =PH1#@Tջ N>sPaX6a39 F,ġ78 ;slVLd z=0M&$+8I3'V{7}qyP)nPz.7zz$g~Peo4)*Xڥ>|9ըR|L`3ƍ 竰x@pR^ V0K-părݞs$=@ag>FSNrUFL9Ć% 3p냃$v2;JD$?O^x>>8ωqąi9`J##5dpn$`ɆQSFHDw)Q;2cKqbQ IKV̰SQ,Yd؅K{P$,eIʼnBd(6UҋR3,Y,0 IȤLb>Rщb{.}]㗼Ȃ?Ӎ'oHZEpI|̒$9" r,bHǬW`*IHn1kF1k-LnҮLxHXٻC+;İC}?G5Y}mE?@xhp9`\\!͹Cms1(d `Ď';`a;dzFoHгN|ϝnm4>\>sM/ M zt6G}^x-dwnK|ՊjEM+b4 ֘sdG ڡojcxހzE11FF%򷁞ٝE@-l(a~ăȚN YuNz-;c z[XܢѣQ0'`͵!MTEMV)̗uD.NvkiA-Ḻ0n?&'WT*:=Q۱oRN LuǦxS~@. 3@_ʃU70&fwxMRi!.)||>rws|n~~^rקO^`iS=4Lm}^iYMԶg>aDO2c˓5zkJU٠$N!-DF®b~;ʦ'NqeSva'q\~LYMG CS_SArp@TOeY=`:FW%s=7ql׆{ Zf Se <.VZV.mLIKϿmo~x=ssO#1́AQD*{H󨧶 PLzdHgFxOCʡ`zG=0j`G&;` >&A3>7w/+a®̳z/P,^$G #,g^-śY'R& UfJ~{~O>zb2I\6+<#Sq6s}vw`H3I'MlT찌 Cg')nʕ6{-Z *lTB\Y:B${ثqڟȮï\F-;/seomoQzp`}͜xO6m N8r/5;M:N6>2:\jyhPP@I.srA0 m(![xƯjWʹkt@ݮo\os4n;Ȳ\'ͣ}BYqd7Fhl)xPKŠnδԲѴrbǵ1> 9(ȹIE⑧ؙR VT~X j \+wtPmM^؛  T/WTW2 p*AF f|o[ εn65]k?6ch2[xq1 1>f<{4 aLl\)Da?TztQ*1G1 ziOPOA7ǷhR]9 zbE8eWg`:}H|&Eɋ. ݺ↓<͆}좗6[{Mo>޹#uե9H.񪫯V޵?.?@- 3<1IF'k8˵Z,evdJ{dx ɯ1{M`v*H)yTUө~+_ \`r3圕d-Zn8bz'֟7=`zpUqe |!rU7syk6{YH~Ť Oi3UX6Ne)~led%3*)b8>]g (ƟztCCdqEq\ߌR**FFcŵF[[wwn_E4mMSv֓o :%o_fgH34d]CfA~fa!S'6~ޮ^m7K~Kϭ'c{ә$A'qq50_${jk'R@bUeNLzhPM9N75=C<_ske"dGPD3i0 ܉eVtoD;ae A:4{@{XiZG,lD7@? ˑZGX?i𼍅8]+}TeIx# s6tϥu&åLLM7[le.r % ϻ^{_5H>tRa U M@`o+!"Ic ,űM,i0.n wir mC,(i`=En``#Z{KMhTfO_-hu\a`y'i0s4:-, YEj C@F\ $aF4q_I,u`3iL0On\`Rs;EX^K ~zJ# 4ok0|5h[@#_|[5h䟢@#BFi0(n fp;t-73cj2]K~;NV v~g`m; 38@RC 18Yr1c(5bt-hvm5֡j51xjC tizm`' P ڏh&O?xhf Z%-{N'ͅ:h<A%,whbhZ-:!t-ZPd=ͶrDM+u+A:;V@gPKAfgtBX=׿j9\ca83Qвh{\+KW 3b3Q Mti.:(h!k(o `u O%W孬%m7IoKuZIWV6C6k\;ѥ f=41Il ͽ43]q7J&ft1,gsQVBA2΢5_lne+λ oe=WجlFj uF[n5.o(dd.K6n6F9,)nhz+i7кm/ul. #v~^dXiw0M9j؎`maL)=[VY-ڨJսG{:[a6kmVw0U;}3OdejZhY+o*6jMhodNvigkYڞfMlߵk<1[4l= K afFV=7JFrwgѝFV6ʛ[ʝy0v}Eu~e}kX k$ Yv]: かn;u t~Ҥ:5.ji+H]ګ6aNSG_˰vɶUdd&-O0Lthc=b8{;V&7st^U.-~w5RŽcҩj4P+@,ՉV3P)>n#leMe>HgAGK5,c5bʛAw]аqS72·y?bÔJ,װmP{h!٦>)-U1,G۬NmOg$d"ݭuhtwod!mo0[SRq!`K4~&|ew!XrD/û4 5L2^Qf7QlL\JA0jNM)Aΰ5Z$5+cͥJQ;lf'4W4^4Beǥd-t-1F3Tc):Vo+FF͑vhV>l0NoZ:ݪITjcXCnE1^T$5 M]j52n :;J-lhu?S?_f5k7Yq_a}X+^5a,}3JjVll?TO`e|-  b,CZoWQ)j ;]׌ٲ`oH.֞\hL*+KʼMm]hhoHt5mR[)3]R1LYMs:݄c!*LkiQ7oT';[ku4'Z'w$:VLE˭\ٶk45e[)K;kJۺ;k#+ّ\4oR%DUS[7NIlZ$[:[ڸ`m;MFf%͛Ӷ̀ڱ5ƦD{GscsBY޴h\2mSWέcҙ )k7QT4BD3u$&ؔH42 HlRl]3lLvvptA 迻IiֺޔT6w514) 4R;y][Pm-Ɏк#j"ڠIKv)niomA7bK֒ ʩtdGk&Vm#t7v@*6'ַMs)iS7oI J CiM657BD{{и1 nRbZ-X['N KDxbMRbLnnWֵGXTWXzGd1dkb}Muq4h%A[oJnlOԠZbWs'Vohkmckj,.޺uVa57uvsu.|lM@n5`YsgL[:w|e,弹3j/Q^\S3fRlXhb& +b-Cy6e[[7mH ʖG6Adr85% m-Κ έ@$tG 8 ǨO{AI}%97:ۗcrY1F!t/B)8gߛ-G#28kL3 9(D>&j k3\g8g pEWC3^pH=KEV*P a5{ <yb^f1!Ge6,m-"o^-(πX 4ѡ~J,t @`77a!DM&BHA͛MhW CCXA"Gza~Zotzhy_+|=_g.<_"/4##h#Sg1 ٽPf< A\ ! gH^ڐ:9^E~ 7c ==Qv'$ihzhJhmk7D(`)@>_*_+Ӭd+`i+`i+`i+VEtnwFzpbs?Iqϕ \{b'{rqOē=8wV"=/pO'ܓ{\'޹Q{}sXaah: _a8TRʾ\[TUMCGA@=V!p "΃fbVC " iS|MX"×waA9&v5/r?O$[?6٧f'7(qyN=·\W(.dQP2$³7Y{cBY3s?}ԭMYL3jrV)P8Z)RZ.^zzTi=53:=YN2KPqX=Nj* B (ȪUY>\XrPkHT똏f똏BIN)usIBhH]ɛY({:ڰ>T]$93왲W){Ъ+'gNO$f흽l7tvVFǚ] x6UNǚB#u3VϽh~m릻yA2R-;m4Z{Yey/iE2d"Qykg:Y]jW(I3;M-MU/ZbN tIlXӟ9fJqCi^뵊_d܋:.[(X\ꡳ؉c36]o6tuk.&Yt }(\S] endstream endobj 70 0 obj 18700 endobj 71 0 obj << /Type /FontDescriptor /FontName /DAAAAA+ArialMT /Flags 4 /FontBBox [ -664 -324 2028 1006 ] /ItalicAngle 0 /Ascent 905 /Descent 211 /CapHeight 1005 /StemV 80 /FontFile2 69 0 R >> endobj 72 0 obj << /Length 411 /Filter /FlateDecode >> stream x]j0E"XCE48$54QEI)\iv띯ZK/\k(DT82VU>7qty珵HK>_C)'5\6ͷŷJQR}yϢȇ˨۰I5, {+R.M^HIhumGQ:<٦,,Y@ H7׃0"lcjR `A5`vXzl6lAiC? @?p8sf@?fF 8K h XnJ?)Nچۆ~ ,cǡQkYiY~~x%v4„pnvʈ> endobj 74 0 obj << /Length 75 0 R /Filter /FlateDecode /Length1 87772 >> stream xy|չ?|Ι.K}FH%Y$/rxN,6Y퀉 ۄ@!fPR m)hqBMi#Km [-mKsFv}?j43Μ,9ϙM׮EF4$je7F!l]y/BB]v&K96lYm*~52#u7-φ|dU_\  6^ix^g4\_vūW^v)go5֠H YbھZO 3 0,gZ7Tt/PXVV%t&74M,O2m9s_yE,]սKz.]ѻr5k]+ptp㶛|˭عkhܻw}/u cF^J4KgUWp)w'V$@!A?F}MGn4 G&XF3HDPra.A-t U iE}ȉ `;*}=7E( $佥QBJC-)B!6ڎ B,4V=DhgJW)( huQ&v;,FkN]PhT3-HDQ4-@+ l5\f҇ПIʾZzTxumJ 5+(~5] e݈nFCNA(WPWݎ> ~LԑfBevX&'k]Ǭf3<ɼ"eK,,U{T_TjfkVi~vlj{ET/./Niii _B)T 5 hAXJ|=ߕq! ֌g9ex9,+*x߂oŷ//+˽n'ay ? /I6=3"& PDb$M -d al C'1cc$&Ŭd1?f^e>e dl}}}LP֫WXUKWU?~W}VtjVinԼ)i%V?>9VQI SKd1W:!#7s9seLوP52%!g;/&J)i!jE`oU zQ#نGO[[KGI$ IA]l^;롽]yń_x?Hp;!~$8G}H_Cx1Bo \%&_eGwY cbgv&Et5p$2ɿ,= [jL-DC^Do҅v,zhpʐ{эA|+P@ZnA_8Id x' _?]EQT#w zAe ]꣪C؅+*%t[`Pk$s?\l$ò u|ɻt堣N|]^@-,ZڃV(].CJ\:NU7YJy㠏= H<=,߃MS=\ A -z]6Er:.*=T `=Z_YFg vͻv™MF^ըU,C0Jzh0 ϞJ(XyAAEm?gXUN? g331'6TRl / #xE]p8<+}J \ gøWlnۼ~w&AnYO%!H }kVx mTj:Ϥ5f֕k;/j Saܲ:jg )Ey̰eXB>_rD-q!Trdz8i:NS2DANhەݍih`vE]弈Vy#9&zdpȹ{@G19u}0vז]{.uwm.\|ܱ.f%h9a ˻hUR[jPa[K%2ʭ~/9wg2{Z5#-RŶawvyۭ>W)Mpc)z!*Fˇ;jh-, ) \xt NvH=;%F mf ]E˻]*Zu`;PuA>+BipaUTJ_=R,h)qer-.STJY*Xנ~0R#GQZp Ǒ_/E|iiwixj4w65%h`Aѳ }gloTfՕ h{""3tBIIiISVVNfuf1FgdL,XJk9K Bux1~0s1x< 3ފbIi<X gB,9&f>XQAs5kg`cgPx3o7}pf scgƸk2-[dU5'j:%WBFlnU ] N(o=hh8&ՙջlXEkH.낢z@`|s{o{aK`]gz7K{^Ǡ{WOaчgf;m h-f1CO&ȌxPڑ}vl[F`)9B҄lr뒴Njkix×&RM{HXG2F k4,0Yhu;}{{Avx! :J(|QZvGNx l l*Ц,%qn-גk[[M[;i{{PbY,/hC 簫ĺ:WHǢ/oye~nYy峘 gݛ>nz7r^JkabF~EEyXM]6'yp8d裂VyЮ`Ү1 ^H;.W0( ;R=^YzJCHOeJ Aq&2MD)$^"8++)QB҇ҴsDהK$`?Ȧ,t.čY-„"JK`)W6`! ΄e&==tY$ ք-uuVh]-g*g<5ˣ[ptx%>┽әnn?V}1'Ȏ̶ :d4_#3)\ord@`W=bYc, +Y4HiWQzkx)5P{"q>-hjOjN+kMSZE)]1Iٚ1ɦNLI41Dmi:'u{{`7ӟ@ PJ i:с{2\׃P?G߭7ǐfrP~)t-c9}9/tJN6 ]pBG ~ bmjׄ)K@N8&^fS >s;*νnѭd7mZ}}o0~}yƮH|sT=w;VpBblQʒY=E%&x# |}4vH0& ~e(Th|HD\csU2RQWSF1RΜ#XsׁVSԨ,vS@`OtގKw=^WuEw}/tբ5.Dʤ#La-+7W;̩{INCfUw7m/>1ص`ݱ_AG륟9WvV l3m1-<5{ v3Wϼ写s`3gigLs{;a~m6IaXf2ʼa,-JTɐ@*͟nt)QګvN;] :,hj@nl[$P;fgh:7~z'=>!ɄzH-CzVi~AG|LMcߟY[942K2[K(SK6*$(Q@J.mQBp&QCZ?/=n$=@wMn4Uiw| [r@&>N贈X@*! nHhG~ 5 M՗!%B@A8RGcA'GEe]SÜ,#W۞DfOYZ<@]Yݖ^:Y8߱!Qwm픩ՕTJCA]Y_Iza9lETܠᲟ'.EQ,p عPDf}!R7u B-j$.]fh[WӈIv\0ﶫ,[hyuP:O-H^|)vMES_=Mdoa Ѫb=``Qؙ $Ǔk5$h 1[E*Bw2Ta3mo{yǟ䙽~gydr/OMg&` `JQE'm=L;: ec|)T߆Vg[57),rُJѨYwJbz?~{YCH9va_R7Oyf<yPu{ӃiU,t0G:0`ʗ*UeeL]˨V|^ҡN zieQ$lv:>>I'y9|:)x+GJwhd)d:tz\_₺=;5W8*Coゥ= V8v$w ;?Xܙɻ 1BcUtu+ u.i8m%9z~ʱ0 nK=%+ڻbX@9JoT+^jNyP#^tH;puxf'ޥa[qTu|SI31#2FfTqkE'~KuĬ no±L4q^dzhI!/SخS)N_n֑B.6݌{z0Ho鶾QsjbQ2w}w^?} М-yb%mV:ʹ+e7GAa-'1 Z!jdizTD^X*~"H$E5`u4p(!k65U< S{ULE|""bM˧LY5G?O[*]=K|+½|o3?ig<~TS{Ӿߋg0&e==`ð"gKk# 2\2 |V6N? VONI{@wr/i<`ƽFbLs}hSHG zϭGQ~HͩEuF-UP1ETvM9O Eh汱~>N/whhh tP TI#/xA>i+8yW n܏'|<BE~eڥoڻbzW-K V|Ώ}zKY2v4JQi_YwC])=7ײȃZF]k42gcI5 F$,T&6FlgРCԅzXM:ս>5Sn\FvFV6\<=n5d2kK 3MRCƜĥ$^ɀήg"`YʐS,$"+NXdlvNɂԥ6i&E44zzƳ9΂E6)@\Bn₹= XxROb ?Ѫi5\hyh/u|,dfE1ѥzeU,wqN;Uj!$sVwt}j4 /|xwE{:_1;.Mڿ\N6%h4Zͪȭ%ݑ^ ^ &ps7Oa6Ƥh*d1N*VHEBHM(V#434YdۍNGTNnlg8$k{8e@dT`;.a .Хpb&E7*|p^U2oSJ𜊤,SfZ؃Ac(J<,qDhGdBWF" v{+%!D3x, 2=2dMe_=ưAr3c (KOA.c >PUy%)ce5ESo¥Sݟ]m]$ҸXpoLyʃR6g~ϜϤs/ޫ pW ^[0TQbW c(zf2rd2^&ͪX_`b  &O&dX1BnIg?FD|shLyyKvS(6e?B18&fP'Z[?2C ŋ}#] _Iڗ,w OFL¡֍gv6_Pބ7_o u޶q ^ qqPH!SG9RDVNZ} c@er[}z5k͖73u\C<%|u۴F6u811+5&ft'+*x2Xz-bqj4zAKhfYáڷˠ]i&AlhE"AC4AG?]ot.OܿОxሒ.GNwkKJo1Sӆ@{T5gvC64g:2+274>W+L dT&rhr FGmnf q^P49tҏ~ř૞b2$gHJ "堮>;3ƝS5SuGWD]WA)PP#GFޏ>Lo pV Dž>P=_kj7EvZ:Xo_ӧcԿe 3_fQ{rWki8:=)qوd-ր [ފ`HaPe&xF +`1b^ Y- (3sŞ2Wۨ`RQ(Vcۯ\[u7ݤWM>1$vRq@ wW%]=Ct3SNuF"]L1ߓg^ϼdvHC19DeSuN˝DsK8RPRz F(sUDѿ29@H"]D"h* h`D?RzSѼxKѼʀP2)=+{O@+$(Ӎgr<G[ՒsCTFp^sM)0 \oN(R9(THCngMiG {=+Τnw峭m{0OvԲ! k.|e  /YEQ_:MyEM*JOԳd~x ?E|!AZG4`ujZAٚ`"yciq8̄*fMcDJA`D6l&iL8La((&Ɣ@ӖSeH6i ]CG Hhmb5(SÖY3V_n_vWYŠ_*ϷxC媞ɵ8qޭkVt\>pO7O_Uc ڧvΘ抴Yl_ u$sG>IġYb610wagN1s! F^]vcB̯l}HRsoVK|Nrm+WL@hIszL iޟ>S]6Njkl+~_8]ҵwhSe,1gZP<ʞ t*]1UmHOZ&$4UYHBjhK|Kse|(K BfqYHJass~W=Ǜ_PAssw}@']yJs 4+?㬆fSqbW;ݪ]Mt:Uzt 'X#hYkuyV{'noq_>[}/Wwgt":idUkmn`Cɬz`jEǓd" '*kwuZmfb|bJT6G+{I4OG=֬Э1ml"6D"]d3'w.෷S.>=Xkg珟NLi2bk-?n5Iؙۤ&mA.MWSM\$΁zňuR/Y$pT=u_4e^}&X "ӋOnΕcbXk1 Tշ9Ӻ'a)>d^ꍣ sbєm۟]6+6;%Л\ 1kڋިdXPuC*pQwP죬/;F$8@դZcʑVElTkvE)+{_0fBÑ@NHC&a;LC ٦29 2өL[ %.8Ԕf?:Jrx0,xg"~lsSג7ǡ%WD{fjV|'>G> ̰`? >`G?(! z淲KK|c~ÃyL(LK'^J)rcc?n9XipWk2R @gP08xzKtD+p\,F\R;|gW p:e{p2ndw;®W_|U8f>}sLx3 _7˽+|jԙzI@_|:ޫs\^hZIz̉,gti|#oN߯c^E:fG~WݏLwK@&+ & @}UUqFk*6bNh?݊󩒖=nc:lmfDABRJ2>=e4 HFcy}이mm}%{_#=^Գ#'̗5R ^Õ$lB>++agFfJ=o"oޒ4`~IYq. ΈґζFp{yl?pk\Gz6j=O1*iVM%tvEy#d5ӫUjz3x "ĶVަ! 6~3~ow!Nq."a60xC2T*kkux$ՕTb(n ><=OO){V^o 4$k*Y`se KXqYJ^RK*~4ni|Y ѕG -9+iJOK1Tä*mCe1*?%lJ^fTD"R.ZGs81y\cbxgT9$C9$d6)t* jぁ4nQ3Ĕ`iKnD^(ͤ@1жr[ヹ%#ZoV҂?sS,i]^rDqk-̆IF{tԦG.GTfsz;mT+ B"I*I%(Y#JnOj8&$7y 9|9&-NgGTH$^#?GTgQ=W?#\GBnqR#<qvT4.(T *m",=, 5eLύ4˾B 7=[G*&-\׶>!οWc@:yJQM="xNp$s_/t`P(Q5FeXi:&Yt;Y1ke,F磁rˋSvmfh6b::^@ב (LdF-``3YC49QǨ?͝覜_ԜKtm`nFhib>`лPOC_W>& korgl1fXU{Iٹg?!o5,J{~3%'ՄbV^S@ƱQa˼99h~a'MJ rA8/`'`7Ѩ}z"'g)ÅWɆ*.ì_>' 9Mi?4,tLzQ$Vn++#uy.u}U9B\&i v>A"bo] 嗏4_?wFY$_x̛yS7NTGNPAAޢWVGh(>J< P8y%k>JZ kc)dF$#=RiiLfJrfzyJ_ Et=V JXc(\`upmmYI~W LɺDqN&B[)*[xb,7 CWXD+ER+EzV?B/K)TRT_LKF) @tlљl`[ FJQGYecnl+r Ej֜CgPL72}Afg43+b԰7< [UwںDĢÅ(GTW<ٶjy* sn"uuk>U}a~mH7vYWw]TkdvFZ!`8| % `͹rKW.+2É`"(AMHY,}VbvX r֑֒i۬#٠obs4%Q 8z(GGԮ*O5ئ, vZJM&:5/&4VcU1FmF$s2,~΢IS" N4MA: I K/G%=IJq? A>JE.˃K{p-?(wLL_H* kN.^;۷llټ]w藣޵9楫+5_ֿ\;¹Oo&_H#>#Hrh\GD#;iMr@t/ 9뽖!,yg/L-z o #东vK.-]!N`-RWʞ ah_gPҦ=B]:b*լb7}vnQ' DΘunZI &bec{ N3C}s~3sf]٠ߌKckZ[d)1> 71g%D0ф%UZKh#9JJ*Zb SȪNUQD-E+LJmؘ р7l 8u+A@XƑS3VLd:09cVos&11D|< /k6 >d,x=_f}܉ߎ<^]z/{d!~ۇw6ǸVu~"2y]3MH2+*qsXn~#^_߃~Lδe:.3g0j^qCn jtZwm9ۑ]ߞݓv`v8sh71߻Oe?Fs5#s\m۵4}zsO2eʘ#T"@U xpy*Ƃa w T7LˤޕI .":L&V\ ;] f\Yńޢ%ZhS_~n>H.^Q3JjA }Y4g76+sHQW1M㼂rew''cmW+3K#Ni"Px0,I̲s#ڳ$)/0Wcg*ٳC }VeQ!B 4 rjDDe;jULh0:@YB=yβX>?oCW&mLָ-q"*ǾcI;` 2N)㏩nU~>sn:1oqtrīCu k:K^lȩfep]3hTvH8mG0$r?aX;ðuLme5zkD8USH< x<qbu:UϫPVkBm'9Wm˟KP5;D*NӲTcjH2{#Eʦ1d A4tB΋ٶYm7[Z|LpӲLf4l/~уt1C;?V܁_Ӳ! [vP+_=1ʦxWqN{M?8}x:L$\.~# VTARJU*cRqVF٦4eM+)>JM+tٕ^LLңiHJM6Mqڔm+U}ޕ,@:oQw4jO u!+ "nE!M*/]m곿RI?q*_+n )_ZP*B⦶pw6_8B z%ns߱V5ˊK>:}Q^GtgNjY-VtZuDNtJ}G`.օ%5LmPӖdT)d2ok,Q^ )?N|%ܔTe\B:7A-G=GѩQ8T:;{fVʗ?ӚqˣWn޿j[~5d wnN;~ViFvZi~pN-jӊLL'Hv ,}`R>*! pML~ 3Qn!]ew)+47]0YOP>q G(ڗrupP8o-QlJ^|Ǟݱzh[3e=#w^>O 5t-Ybɂ_/_9sUO\7]ŷԻ7oϙE4}f$vK%ٖD,bcR 4I$pc(hc%n  NM m<)B>) VsHý|͜Fs~|ߟTzBg8Hy?]n.4? \s\_+p'Ob)Yh6VQ14JF 72QȘvAՍLlW*-[H,rܐ £A-3H D3^8@OxzWx&O9+g/7ySx먛6ychfWɘM "*OくD߉́[]F I\nZCO :Rr9tOc+rs< ȼ߂aᘁ6Tju${Dl[ i#PVڐl1*Y`Ob[XUL&Adn}֪kML$jOpLZZ]?meyW#MW\}0+\uqW~C`sn VU#U>s 5CHlUT"1LSDK[J%@J:~`sʁ= v^I8)X-)גk:8Սuu61aJ gtI<BD GsܘTHm_x$&FϠF 籵L'gC&>_"0I\ߊZ4;YAC3uaݧ5=1Si D+⓷GcQȃ&Qv3@QO6=tYp]eya#<+[~ w ML3vyZmQ2LLE)FeG\G@ 6$טѭU"k X+!sķ)ncj[0\6o 6=?(|\|d)#RVj#X-n뎆YMχ`2exRM Rss =mۭReXN!= ;ax :8NCf +ZhmvONM ; E g:%7-9HYKw;YHR0Πz{RqejmvA)8F.]L%]c9KoJaA[}` ; >[VVۿigg_j[jgjvms9>ciJ&ppm{hd]br5x"QYeİ.5a;.`ԸF00Ǚ4Z] l6 ߵ1Q1+-XsA+m%t"JM:bƟ%lj4{"{'PT7_=eLj^>q+w؏ۙ5‘Ͽ}%BԗBt= 5Dx9v^oBt0ljoÁ[5asS"Ux 0 :  RM!(!gd $aNI UQr8a7xgX/Kf/zi˰FϙSyuWΪyV5=|=k;)W@aΖb6޵λwm|0sO}k`w~Qc|&-Iشx.,vil03ѿǗu~},y۱ET>9kL88!. ?^wpXmOS<'=3(Aʙj{phΤ! A$CDLdDLeUŋ.&ê'}]8%dDq;NO XJ5`/p\Rz4W{;y&O9y([&cQE敍C"A/Z) j\n |g7?ig㘆>Kh4t樆00 ymßE_HGc"#~4#Q$\P}l(w޺ju2n-u3QQLrԜnNH$I6kVH#/"*bOqV#I#sd۬ ;s wA4+ 0@/HwAl'Bv9ɦ]Z GwN$% Rp0>]}vv3 \*KIDLK7s͛`08C#}9VjdJn`r'aGCDys8ڢl'6kD %^jSׯ222$Ls$ pD( NpT`ģ8} Qj@R£t^h?l{zކ SlW^4 /[u9qJ=ֱư'A;5α%.y#{c}\d:N_qk]@r՞4qBo FT1]jyȁg-5)P><>$Xp_nɆ*O%~ȇiK6FG<#ֺm#9JGk *.r蕰G;lNxk=(3tw?uͦ; M} @Lو]H+7 z Нoo,HبgYS&An_P6GXU wſ8Nd Anq(cw&\z{ 9X.FB4@ڐ:1| :-¾RI]!jbSxJFI\G2^ #Fhגz] Ri҉dDP*u,* 8#*I_qCLSQϨww:q n襲>FnM?y0)%m]DW)E8`ݺRJdI 4_](ʀV˪ˢ҂q0DF=F|ňLjA/\(?T@rJAڱsAM??`٨yJ楡F!>!!*= T^Gê}?6]Oo`n0OhsհEc۬lfQ`G,~dpc4i7 j65x`k8 =_홓S-fb'-h:*zt.pmQn*/5&%`a4=TqU5oRlvqҫഛ֯=,/iֺb1N}r?~yzwHD| ׆ŕv _8lAZHX8Zl=ᎣbZ#:ۈ_|$2Ukʷd-qSUd$>px#Hyf+VK0]hLB@NFSTI.C@Qqm$݆?jJc5d<ښJ<|i/]V&hc֊%7K%Sݛ1 Ps 'G$k $e`BJV✶\BZϲb}V&9l8PG&ɞx~&un}J[kyj 7pkP/6HNFn䩳ZkLxVoJ68%G,p=J9p~C.V岻b.aw=4 w \9*Chkh }1--!S~!ð)IIPVe=F@S}9N/5z84c~gP hb*oW}<'L }6o3̱MW9s_O<;#_qqbjt^k>2pX/|AknZt4"L gGeärŹ]yXԻ&1n|V%ڣh|}ՐVK[}KGU*OCzFMےo]v8`]d%-7E_ XGϏrK"Gk4,kMpueJaCjE ?l]%o{ 2;TVu<-%K ;^(hC$$%PdP;EZaRi90o8 BTJ"wT7PnlU\/GZqɩ` [ ..\'wuźvFNTJatiSoH1VѦiӒio̚ѣhM&U aqxZN Ug:'_Wuhg[sdW GAh3x+$47W/q'g_QC8j-Ň;|ON'' ZzBQyI$9K9A:Q5'\}ӧRXdm4=iŏ@>'ބ/MmC}}U]¬wIz\DC:ts II8Hr(';Mm ~|D"Q,R>REMzsŞ@q8Zd(YT,-R;p8\G")OD IL D ?ǒm@l7Ga3霎zTwj $İG? &z3ML 'q@AJS@Y\nZHnNa=5(>ӵaO0Mg03y;:~feU똶6Miz.8hKQ'Xzm%A7a~U P9HdJye}ų-3iM Oyv@!"%K{x B݅)~1 *"bLMgjb>S $`LTHqEh_dz@]Ő?Z?{O몕|ÇqWKHoA3"^W (ڻuQÃ-vt%wk)GAjD{%%CCM\vB s,IfKͰWE<~%p >wo|^Xj&қ9csSPu>9ZUnϧjY>FAŊ]kMH ѩ1}u7 LG2\wx8W>exRfʻ;b! `Nh,˶A<6RBlBV(N.]N t^̶h.>g1P' ᘁ4ƱTs <, d gvH4ۓ# h.ëw<:0At?sIv>!`gqII`\US^!⦆VC8:\#Alv;,r0n)A˨9K_ռvK*+ǚKUK9;fe V`ꗁ.0h(yW}ъP.Q+rg*]ϙ=i51kʅPO -osk6D uC8EA)4W\>odζ(YlM %MỵUZZ[(U:QA2Kh\2FgOt4UM~tpn{z?y^d?0Zf ;ͩ@R6I>)kKCАZ)IRɾ^I4mbC &d) "T|S%djvsIՓ(YK HTYס>eY%Z_*5ek_w\>672rQ`5/}nΟ3u/.-w/3A=|Vzh5Xؔ Ug$ ƼJDIW2\ޘ'$K Qv,|IM:%h$H Y3 d/"x] f3W{]/ԭidOO`$d'z zR+fۓ})"K#Ǵt%e\ܪ[-|yh}lj0'"5:ZC7_?4e%U7ns pPrA5+<iK(z Z 섾wuYf8*RF꨺"offun@yP(1g*FS HAiDY:)Q#x| 5 M`ǫl^-JG=F,<=j5cdf ΀8u"wՐE>ѽSCx2^VQwzPQHZ-NYkSsc3jtJl*K0>`rAؒ4k ,ZZ}D}Z Gc a:B!=k鮲dQ+ȝ=J38q'}R`v꘬ח\D/}IeSuhCk-Zk PB%U5"Ӳ sj82~&IDzPsi޿)K/KQ*;oi#N"Y\g̓Y;A'l:|x|W= ͽk2^(hD5N}WG YL. iO8ݸ'ܖ<#(ն0h&%q PKs/FJtzass.sv"12הȤ"͹&8شj]K".jk]|ՅIMlBqjn,bJb*ʪ*n0}.ۆwM5]ܴC(bR81h B&ij4WR}MJC[0ghSA]ÞW}c0WZD/({K{gtwc:7<$D >^E+XrmebD$\OU:HňH|W/MFo$❶^xo| M٘vx{-<iTZP%ȇdC$"qLjjd9 .l.c=UNr8 qm/MIYIFn HF$d\3(B4t*D|:;O:i'v x-sqecs9C9bL$J`hFQ cAèapv9pTLMz$b{v?y6 s+rc ׂdUzZ-E\.kv-pFsH8`iJܖɴ=Oϛ<v0P66j%G7Ks,QB'w)HlAF߇3jq&4pڜFllа4jNkh U%G3A1"{wr,"ScZ 0gP DZJǫ3x iPj)7d=×)׵Z5NW mk.\B>xEꄉKɷZƙ &·#R hPl";iP<pPaa/a.Ongf1 eIbFHUW7z# Ԇ)TeT*0kGocpVe7l븹nݮǙ+W:/?˽狶re/pЍ/pϲF^Z6 [m3vitHqޥPوlwpΣFѱkt\ p4?.\Z ܬA 3:e{.=71+dFh.Њҩs4:r %E ksrst΅J ?W,e葅p!6sBmD0-J.qpz ;>??g&|d6 `<^X[./C]? MiĮns :^s|K48n 9zh)( Aapc?Fe«uOoDj)2M'RԐ2Rw?NJ`2>NA f+C+|<:{pJ+כ>u1{w%gц٢ ښcI˜Hw zZfdt}>QLXt=u]>p^jg}pQl.^oý@gv,(6,L=O+9Y>ЪY$jI5>2&濬`˱[iKVØ) Yp5NY#&˶)Նȧa%Z-]\7OhKs_xdtP1oe:梺*A =++fs-+|?sw~Ui78-̂Yq𼡋XrI3 -6>df~q}~Lx?~KZ|}KaBZ,j<`=ɴ!wMqc!A -̜yec#5BmG=0hCD(V!$3AxBd Amj6Rmջ9XLgΣ#>jn#(|z|O¿Nft9B!WXwib ZsjgQc.R47:LObWY͋? _\v$^Iťnו?> gqTv , :޿h%T.r\;Гf#VU}*;$dB~A[[eB/$:I$ paz|$E;R|b]NΝ[33) deeoaF;j9;@֑4 SP6c~˪gO(AssZnBal~>~)j1C%`mmv}LϤ0 ЋB9d8F;t1%zT$>S (Eլ̬ubbs )p wy8oZWO4hbRT@AZ`MOŽ;Q{{Z@W)P˔=Dd /Y# 9] .Ou+'3֋K,߹f響"R5wG8^o,u~gA3w76e?֑\GuħEI5D#d1aۂB:IZgup%8iQ:'G&]>q KY#8EПPEЙ4߇c3RLd?-6ҵ.J}F6V~Qz6=0}}}ٺjT]]ygWj?ٟ|ӞP9l PsDrT\2a14jqxmundL!/ h#0`@[Zu#d-;|ba1@?T&w UQ!:A& h~q%)ub)f40RJ@/e ObӲ)I^$b~;|'8D|{׮{.]R8 hF}LwN<:JG"+eZd@Qj_y@&YV)hk JM&@43 c:#3s#ˣ3"G/ GL `۠ |VF,kPc1SUMf IG" +IM֠'0礇ܫgx Xl+4Tf4AMYCkk$_$7OZV mw:QHZ"lM# U= "U&aLxu+S7Tnj[>Utqw~𱡛HTr{c% 7|ᅿk_6Cwrh?;liJ5Jz }Eøvv8Fzc}cXi/4Y w:M!teb ,B\@bsl`?e$BWoppDn`=1q\DF)ӈAzqhPrqP3 0 9MvLc9l4FOYvv;i!/n{⑭ vq\V=S}X&/jؒImO|fꊨ`8M飲6`)9 8=#jp2ފ80%&nͷJ̠7GYJˎ쳣&G_w'sd B\J M8D_-jEV/W`xΥN^ݕ?E;ܰ"}z8-(|vCIW5ܔJjLgvZFcv, >~J|SӔEFtm&^;tm:SiĞTqjhpσ8Nd#E(q#=X ӻЃ} gR?gڧſ)Rܺ% CjF?b쏰sQr! feiessvs";7(|iKs7eݚ8=K6ׯ\Oח3D1&ՠėRĔh:nt|9NWFQh ^&4l6 1f's908v #3rq\_8Yj%%8=Z`P! D2#9:uЩE-F~ha׻/U[ c[qo sV)MN#7U =[yj|CpT~cS=cEFmt멗+Ao3qx|㕧y7~.?+?Vy'YM|p٥Է_֟Ca<?"஋4HTbY;^qD(%GccQ&jƻM`- 1txJ0 )ZyހVY€DCNd+@PYm&rP+ЫA o_:|IJ&*hBLPhƪ Ojm |_ sJC\h]Wv׶ʎ+sY/~>xʜ8wpݱ5@G1]n5ڴst& 0Pxp:5(6̏""3d +.Їa&1o`:h!VK:~9-ݼ16Fjxhh?֍Quh~fLFQ8V8\ H/[Y);<;7t /ڋ'Rjdjt&8FbDDFŢաeVZ>Un +Hsu$\3Q P*ehjU,aNƦ JAny,y;*m8+=櫝R{Þ`sxp5`ݗu/}v^?a*Ck;Wm&4-H">) RJ;Ȟo RReRTGc"th06H$to|<)\xvOze?T=A(7jy1*W:1FDvcGR5\ݫYkge,XA,0v0J:ģ"[\|$ :Ǒt_פRC&h~r_pd`pv˙43 "jQCY 'Nr(J:sj=`첽Ǿ~ }ǩ bͩ>7L\jsdĕ^dԖ?G4\:\H-im_hZ}֒.M>eH3c TcEyR1QI&ƻϠ6g`/-sX)}/l0,L/HzHIX"/Ei$_\ tWnо-}l^God6:nr2[E Aa+rp~k X2cQ@r,y,$ o)9lYVwHkxUqPn%$}n|ʫn|"#>Ik>JeQIFR " +@Ȁ`rMVhXԌ>:q䐠Jf 9,Y B\բT]1RX5bssbԉX)F7q>ZM t" JVR].dRRܨM&pAjfHpZF64r(hrJC6'H !{)'/].981@\T>Cf+\kVDX՝= iXP5nL\5(ƙ8syI|TxNT"&!Q:@\/*Iݝdk!1B=a_c9aN),V._ 7f&~ ,ם#Iݯ/X-J,Q?XNj0 zffm̷"Uы% 5--[|?b?: oXƆzd "CDG] n- h+c{"!tHH_ C9<HlO![D 2?S;t ?$bxNĕw8&? 4j^Vw-8 axu*rfee` բQW$ }9սPMKnlHX (v<1,x'Tl^Y6-Г+kYϭY| Zp%#{uŚs'n[|]Ý]s.k.roҫ6tVg+jd7.W /2fʄz蘬Uyh:9' ۅ-bيÄO^ԡDE96hV :W!_03<Ռ1@]‰Ȝ yT yޗ@U\V{-Iݯ՛Znm%ɖF» nKje#X$1K K$؁a7,a[`&$$0g'HLVߪZdΜҭuV{ṶO/oiO͏?/|H}uC@uP{W-U*>Z]Z+XZ duϦ]k(.hh4M&r#NgֱG&qބ`BaVa`bjƽ㵑$&@Tb^AY1!လDŽ1|vhYFqHoߠ+d|51,vEPCh|b.mSx;9cc^+X.{aZ=#liiAŲW5YLid:TRL)U&N#!hEEhEBY+z'HjM+"7:"*fv1#0kzW&H]vGSȮӮӭ+drS{y&iw ,QD&1/ n\RGkM?j-ZymTZ(?"\=Ens&g饇Ui*Z.yWm"{zҕ_ꅉ]`.- s駺jŏ|=1NkWdwgJmNg߾k n>`X J VSӏ79WC ZC7vAM>QӖOb]dxC]+xh@*(DNrY42kKѩ-i"t/§ɦq⃋X|f"1Prh&q9"nOq; +s9*Zc :?_0bg0 Cno^|z1+,Ƌ[}ڎ5Lcɥ|/H%͜}򝠝]GnKr)ƴba"Ez)ozU^r_,2?Iې,^ؓ횵Ci`FaP~Oc~Ӕǔ],y[9ER4tv`إ+Шi9ɴy Gq9av8Z) ;R1s~6[H7#GxqSmQ@0 &_@lC~/A 0_K}qG"9%65F__cyi?ՕrB[\(""tz8jwWhQ$Dȷ| R*@*,OPeqCRֺ2Wxum.]шeexqI~fh19P܉ 1f>!fhxNv8Arw&\`"OF5r b#F&)SS!(BaKKZ!ȋQ"% EGÆl޴ϗFq\0]޵;*ĽND73I΋@;C(G قv GG0*'Xt荐:bF,%6noFUg' ό >RߕTμ%S#.:?6KIj@GD~'Sl* ֹqKOx#ft: _MJ܉\{YVZK{](Яe^2fS4Z p9B>YY:ueV=٤rFBh)1Ԋ׫Ĭ*w!5ï0hfrb+ Y{FϜiFHYS.;NbX+xX}8|2 V[NJ5rH[Uq H2FȍzΡ.!=*7ҡʣڢ. 5NdZ*׏F"ט}" !"b ӆB~ʥj2k#(7Iw)y%tVBK'6) Sgg,Mj.!ojF-߸=MT>U M>OyMÁ֖?&/' ̜^{k%3d}/xSgj;>˾ syE~l8cmJv${b$lڢh+1xnA6}XTvԮK׺֙X|+Qgdu0 yMl1^SNZBT( IݦD4o`Ts@sLCRw(9ۆDx /sju\W0<֚YpUyyy%5/}20n:UV23elZg#UUv $ȳ跢nܝwEaV\zL,JT67tT`Cb<o\tkȄM:riNg!OTϢvSӉ&V)***k\Q`ܵnFYL@9-wd_-8qؒԺU$ywU8hH)9\ $R0ܹ^q{,ZQgj %^q{8KQQ1w00>W\Ao|{(gh5t6d&SNg&QdefWu\unU,uѢ[NܺHdoz%ҘoӍ~\|t%w<LzPr9^V*o,"'}%%C^Ujk ooa݌sp؋y/}?/?j#rt?sF/Sq5L6\*iEPR GPly<(7=lAUQ" 0|e5*//ϼrMܷ}d) `&#F2`W7mѫve3]]ۜA_'[; DN|.P3Yr U[ {9-8DK^l@ɦ&(9O,{,Ej"1-mw9Fl`s LEfduc67yaVaoѮ5Ma\A+鎖ԻԳ<7/ EIGgL1s(fFˑ}4sSW:Në5^mγDp|dl7kNzi^WrmPC`r^[jQi8V֫sՕnZCCCCsfow(.+zWloՏhϿaĞ>gls%OG'qͤkK>Eʽbct V(dzmrג`[TX&v; ZVFV-.\zmNqF 2kpb$QaZf tC%wv/.x^S\(q:9o/p!GЬTN#P_9Q?9=4jXԂN8 ^\5: 98ZqroL,ZROq+́`fˆj4=zl\SXM V+M+Ùs\oů:ڵ'afZb {a$sÄ좴ZS!So| v$ӷFn׬H뗠F-LILlA H'1.J16vBHާ?KOHUYS{mykr}+(6[~1έ ̔xouˊ[-~xoR3{RVf[P)[b #:<ȣ0.RaWXoݥSqǸ̮5sqS^4гV@"y5`bd{dKy Kl0ȱ[R5Z!"SU؈@tU 2uH(Ũc\c|>eI>r35X)fbLQ/Rd3A))%v*'aGwG\FT7曪GE0^6wˁJ56-~3}teϿ}1OׇO~t}_o8Bsw=C ª~l s?d)Gi/X4G#Q:%oG4EI^z3׈Mިw cn.oiVCxq#2ht:~;_''ҍD51BJMcgo bN):DR+0[2"VFaKl\伩V,PJH<ЊU@'H.gE3[ a&Ļ(t1v̐}V$16(8mߓ*Ġ {A6 RA|dulzͻ؈u޵ۦVf]kC*IP ZqE s_6?tmʞ+{FgQ}#^j#uA3NrotoLPV|IJ6\NU>TÅTCG_Pk+lVPTXPmJ/*y/C P> ֪:%~ B ܅=! }bQտ^jGuX6םS t#fQʘ]<9G@9 Mْ^wsg%箦tZ܁ރ L:'Q%tKY2*qjh%8࠶l ,؂HEHV.Y:LEL~z`h4wm&Bof'n2(>[mǟW-y"x[ڂsۿGqG%?tu=(|߻􇾿y [*;r?N4RYV9YaE5 k~XNgnǍjs2-[sK^ZϮxk:V]׌yj|Ã^}^ovn^_o˙nw/={;ƭ[O"е۫SI &ݗcfk泙{22xh4MXn.vW_ݶGoghs0Ȉh-B3B TeO,ӓe^+IƕȊOʸ yd\~ߗq8d(ZFע}ס^qE)C7ȣc2AjS& s+tt@h4&782a7d\ Kd\ ʸPWP+( Vl&+IJ(WSzs_JqSơd>,F8O2mȗqh#GXơ+dڨ'Fm2mT2m 86L8QhR2ŵ.e먻Thx&Lܖ-W zC2VE]!+!N/!Cc)nȣmn4L> S5hOb oFrNԔ,C}R\BIOnV,)΅@*m5&෍}+RSWB~5CKN/3 S]%$4`C=]I IeS\L%!_R!;wIT3=ba\Fd:hHಖPw-#u"9LzV0@i(%hegGeIZhȩvQʤᯇ2aVvQg l9HKq/j4[,O7ņ).2i]z>~),hP."T $Ji/CFE;1'Srީ:s/[jrəK|)aPS\nwS K) ӺKyvдHpϒ+TZRH"nd-5c\7%;R4m)-q2n(WJAEeL rt -݈\a*kvR>Z$%6#z94-Oи[J"!2[V^ƀ쓼HvSs)ǩLOJoE!)Rvq>`92"K( R<&$##31渓DN&^j%/Xs+= L2Y #z+tw>9fɂ!#3oe/Uo\;f fK\ymAA98-E>s#s,P/\R>Fܜl<Ⓘ?KΖROꑹrJ"K\$Is}u˒$E{"bJIc'9}T<,%NljKϛP4e$M%U<9 5LLŇrr'gНH#KzxK:}00"lIt%%`G3N ےPpWW"ER J4F URN AN.B ԟJtц2r)Mf'J4,v7f d*`C$)*hGr`iN m%˵2--I>0|.(-t2h8Mbv$RCpJ@|0/8rDHt 4mW_<Z+-l'")fB/GƮd<<4vgd8ʒ6^ߝNK|%ܚ?HK\J@CRdװTE8>N`顁ؽ#F2htΝm2!#:Ѿ̶`|["-)C1Ezdxw&'(W^hKۯ ˗.lzUмxek֫W5z ;3} b̗鲴VPea0ٕAEIJ$hmŅ  B X-8PZI:(<4RBڙdoBbRҲ32~`H)Y , z )f"vHӉa H)#Z@dILC~`O\*qCO18(m,PeNHLg$L8:&w2П#@ZKBFMe ]Ҟ #]f+kMYS0dI8hS/p3ubAژT,."D%KgH5aP έ usVϯiXeeM uuBݜzSz_)*CX,'2, "qaqLA~K'.9˿n({2 ${ʇ+V|Xa]rʕ+W>\rʕ+W>\rʕ+W>rۏ x[I\^Dy_>᳞NEMX1r 2R"{1~ Rt#e.7GnH2hi*qpP 1n/sF 89^>_@F̕75/i4D0RuY0F.t`|_uePVzS${=}N䙪Psat7P$4 0tDډ <|f0@4{=&hM@n0< Jx2@83\ $>d_ՌO. 7\3`#2xÓ <R35`bf?GD9/Cx:CXllj=`MMMMO*ޤ/L>hڃO*M`ӓ7O*\^t=ط򗗿\iyrl9J?UQ#qA5z|=Ivu|'|u<a|u>_h/z7H16Z,ʵy"$o25oUs'҉](1Hc2EYSwEj<,3-k .Q 83g88N)8C\+PRSAq#CLF:`3϶1m1sj[`.6^9676u\Ɵ넧1IV%Nw84;s;mcPaUP5ƔVTdT2fٜqz kCm֏=W9VEζū֟`3֖:ן71[Ww|SKL0a j$ 6<$ l9 lim9H@>_zJ! HXqRV)$ !lt&VL H9RG|u ^TNp' aR9 Ao D_Z{vYF‘%߼ؿz-cH2];m9ZW?%&Zboxj¶;ݗIl7Il!멶xH^m$6SS4 p[#Zй:ɞ`Eơh8@0liÝc:1=hh&^;W8d/܎&Ft:!0<33lnUmcٰ~quLI ˿Eˍ72ݍ4nTwef'99Qxap~-4a,qmgOԞdg0{ 3p%=8Z(7Da Q46L 7s.;W endstream endobj 75 0 obj 51719 endobj 76 0 obj << /Type /FontDescriptor /FontName /CAAAAA+TimesNewRomanPSMT /Flags 6 /FontBBox [ -568 -306 2028 1007 ] /ItalicAngle 0 /Ascent 891 /Descent 216 /CapHeight 1006 /StemV 80 /FontFile2 74 0 R >> endobj 77 0 obj << /Length 812 /Filter /FlateDecode >> stream x]nAEH,{,!$ Fb8Ha@^aiG7tU[]d^|sT}ַzɯn<2˻W{؜ƣ-zút<ۂVw%ҏ_mkusOo|ݥǣ٬ݐesDmr+m>6m7ku=l}xꨊZ^6 TDdKmj?,G > endobj 79 0 obj << /Length 80 0 R /Filter /FlateDecode /Length1 37116 >> stream xܽy`E8^3{&dr$ i J8H $T\oWQ uakuwߺ=\wk5>w%3OtBP߾_iSUSU7w څh$W5#~62ȟyhw_oƿ=z'B?_߽mJ,B9Quv6AP.9o|seQ ? ׻{׶ Gv@h.ovY!@iQ[A( 2Fw NT/@>B 3# @@@'A?AF|I_372g#5`;P!%Yhz;q9߀-x?I]Oz~`aߑ-Hߓ$M5xqx>Q :}O5 J_W 5@R}4t72^K-v;'N7g pC%{9he;{s@Wk?n} ojl>up,/Ǘ6܉w! Q6OnD累oi&êb8f!o%5?iMڜצ,ll~, td\RW5xj|ߋ|ow'2PF*AQ,jZKuP}N.^Q4P7o4Jѳ6*W7w7 Sgaavv{Բ òc>QlB HatHppZ q:ʌR:܌ӈgn:˰f4:~jp7-).>lb`1q'_Ciʼ{K l CMԟ4.;AO**6@T|mdGL7 :>vS3R}zo3 #ȋ?FOC^ޠv ?@\GfP3oQ :)it jczA}}h?LqR'lx:]Իx &Z*9_O=A7`蓈?eyjq=%3Bhmיdnccv6e^6f'eŷ噿wPh|*F EQ.r޽PR2~52##iq?,ئl79^ntZ v/FX F7BZkz 0IZUYQ>,,).*LscH8 }v9vb6 z.GըUJ\24Q^]`j~8z f'@M*X=C̋ j55lMa&*Tbro 4ã" ·`l0^ 9T;VP#*5j#ZEUB vjZ҃a:T>pZל7g F9q !fX6cX."A{#ygnКqM{maC[]Bo|Iٺx>h>rmn6Y*4sLxMtt %;@)YV:6z8mp*rCMguĄo;fxwp,6r$@ tL!:O ky d tLACk@55cxj5z a6/=0%mR,}Hc8<BB|*?o@CC c GgD@k 3klGkG7SɝwKȝ]w&_=.FaEx_g1uV c˿ݑ__b9_7Zm}E)$h8c9$r]UIff ?#rXk>4<%&9\8_yQih0V .7?shPHfךN7BeJ|]!c}.`y>{^`䙳H1QO6# c} * 5Z+ <W>6 }%`οĿ_bW_~M En& WD{E2,+P` fϫ jPNaqXVN+`6|~f#(2Fҏh˥[F~ʏ>)…flɜ=HG vg/G  \E2'u=DxO z%-/ߏ rQSe(Op -`C(N\L:JrUU*[ZZP ڄ7aZf6Y-2\+A}w"8A=guZͩU\-YNnsJoKKk=jvB!1;5e6 薻ccSVe>(L$Xu5Qr)@y8)Vj\VG7Q{|#'ǂ9(cgoTv]o ESbbAHRQZ~3A<4ySjjL%5V-j)#xYW ^2{,hoh>&9T3׌b ]9jT?k.-,f>ḊS4R!,`I1QlJ%#_&'d)).-rˁCطgcwr*;l;/yk7܍VNG=F>ߤ6LC;gֆ&<ʴPéC6bSPF"%U憦(+=ssU+˸%}̃#ƣ!xN).}oP( %D GCNA:΄v:ՁAFj"5SfG^Ы]X:/d hkǹܗ- ՚Qxr'bX)CWzUBJ$UjI(qkF-8W7/E@"JSIW}5v۰n^SEۊ};*;SZZ8oԘC+.7;p3!^ލ 3BcQfrljsNE9wt׀nhVP l0&BͪvjjB񫺊wxz'?"1dFdLlB!>]Qtv9ϟ2H?'hJ%Ȣa ,#@aMyXb!(N-v--)&J`jl.zSξgȷdk5sc%7z}MN[n:k p]W.~!v=KG;2=B0PV֫U[[=swpdFe%nmOG(3vM{R7HtqS9\7B .aV}%#ڱc>΁B;WS5 wl7qAPKdLX*t68gƱ-|dZ=ϟ}4 >Km`.{izNO%u5;=>Ï9ß%LH0ѢǀA?%LSPJZ6>m;8ǾLd4}P-Yd&"Ƥ|׌^9%;t8"*i$=$H!NO"GgVbl(,"@Lb.H%b@;>wk=sZxG'هO>=emzCd<Xx P JlӔé3M~c"aANʙ`+{=FNիLP.YrgrliTw^oOĘʍǑTZ"*,m@8Q1 579<Ҹ7/ߔ!U\eS;ˢ3?3 GcyQMI֪s4js8Z)&br,7@PLbhw$?y25,S_O&)3z焅/GĥPSSu;ɡ'}1?^xQ1GA+vOiƄ`iCiVYy(#V2jeƬZ_􇔕G=ʪifNѧj-d%P# ,= ybU69R_%!% RGnS +#cc V ngz:HaQ LM-5'Tt7stS1XY2+W ի:O"(kBEj,BZX/<(wx"!Wfz{Lt1q! VޠlU^f& !!UALZ"\{TNyK#cr+5Zjy-LjY$,&$׵iiG'BZ6G^2tnt q~ǙTVCʀ}Njb,ӏҬ q ٠DN-xi=7|Ýpay˟߳he}m;H&ƺ~Uv.oU~8=fH ' B,&CvFTpkSȾBÅuDaaFFj P -# *Ccԭ44E/+֌c 8 ZDsXS:qQ*^/32**,>$`xV&-@Gz0(Ά/ѥF,bBY _J/x)]nee.鱺T3w[ vEk4}ZΚJp_ٯ*ބ10,ìF40 4E!AvX"<$PXh^ ]z!'S3g ʎ)ڝOӑ"FcԺ5jFV;Lv#z&h^p,QZ~2eg,\iz'ѣIbjAJ4E .)sDOIi:jB(wH|;VNNʍ^[[[]F ~bj,Vxo75Y aHdd"j&'l-dgY0YA^Fn- y ЍpmYz#f|_=|mKc˅'cG\z²\A ;.Y_ v_;wz{[z(SP~z.S|(g *S暚?7`ZV*Ij ~B)#O*8QWpIWo*g:b<21DiTPĊBy= o/Q(v#ya|&GAcV:08Dt߈_gO9% a8$ UH"(sڰx6pJl3e.\~?ҽG)Ĕ:b#iĩIIڵE (HP5$=bh pbsO)&E@U51!HCLmVԳ@LlS!6A~My%pz-[0}ap-xV&'STYZk,h,+i'[S -*s\o}PJD/; GYMcO^0ἀܯJ(zge,I?mhW~72nERW ̽!Yr}(a F93|`/L >jXFb1LA,R)e4+-ME0Kqs47)nƻ}(ѐU&&`ȟR*=1, X,,JV1M@r=H 1743edq'W"zCuac⎤[vY9RT)v{Q*ŋT3 $rlX=d%C_0#e>,XRp㟪۾#}p.l+3W=zqhMŠfSKJCEsV{ mկLι켐]ɍ3+4w%3cT(Ax7 e>`-OՕ NUkr!>,`- X{*R-)C\0dYl|+؄ȣM,<Sɍ'q][1, ]$*hL5}Xn x"zgc n㿚Jr+*cʊMFm"P:_{:RVfq&.9+U -g{SGP&y !Y?;!35G!7Ɠ-XAX,30>-skۑwb 3 ڭAvl7v #{8sldIi0@4Zݡ` r3{pys805`i9@(r Kz<Ѳ Ō% K1&(o:f-edgGTVrĘegk(~/:uv պq;vkmnۚӧرܲpz_"HҪ_ mz?5N-P!R@ +YEσ`S _ _ݏr˽ɒqu!ѵV`-QsyJ㷧4:4#.Z驅X#@V)iL1 35ZK$QY"s^;"Af 5Mfs811pG:\ 9y7pԀ(Sf~+$2sB3}hc<W!r$nR$pťfTUE*FkCNJ&́l#CGԘK>G 2 F >fg0p4{(?dX?X?\h3(J|HL!+-qb:]uI˾-BHỈ Rh4m|Mςys67n"s%aywN[R4Jνֹ1+ZXhۆmC0U$}%/J2 `k,h]З;~`a]m V0rs/S1\{# EhzEXlV*GW{Uyj F2ӐL6nճt͛[c:qܹ㚈y=DD(.[GA%VեNJ$FK=UOV{7%Y'q&J8HDiw_;z[wmݿsvL"nGtͼ=qtۓX}ɂOخL_T؍UҫnU>#]ze?|H="vOļ< b(/7kd]W A-WZae:\Gy=>|s|3zˈ +WR ┩8ͫLZRqNNlTEwUUA *>KAUOx uc*qeeASvE1kçtZxvj4Ui9o·}GX:.ɒF $ jF, ݩݫkتB+ σ8Mz$Z΍81 ˨<=ZSu h=<h ~]*1 h.T+E޳?N~{6Us 'Oxx5S>Еk+Sw]~7_ԁ3[j׼vum=>06c{\?@2<PuuxD"8K(#(=h!?Z-Rn&3 F&Pߖ%6D;k2i,dU(QR '#B샫Jbuys\ /O$>tTю;^6βLMMϣV RVXR̼,R{JTzarqˉ/K KK"O(dYWPt0t0L+#6G2$b^J)jx#ڡ6ź-J\J,a[_?hwYCV.;.gMrDF@vhXqY#2jF!^iMZF2fqy kdʼn3N0őϨ,n{yѕ3lNr3=4/_`]<N_|>ߩ1AI$#ڳG(R5Ruµv SqrU̴jJKEK%UKw,iY]Vޓj{u?1|H@S8\t՝rХejŨvbzw鍵wK7W\^eC(Gf%6>c~9Ӵ Ez"e$7X)+TUTx+Mh6jSo3>3{vee*|ue?#QPYεb.:'{+ \A͕B U)8]^ UX~4>fS:gDܮ"&x<$flndwΧs9:ke=+΍~NboL$J>i;9`HKv~򾖬Y':4U|\HKrsr&lac6sj8yel2E(7L!+tԸ(_#&v( 4P^6uExuC-ZPPi *e*g߹"HVz)ZM ]C. U=_r)׭.OUͧz鏹}מsAii!)b%=3SO:߃w[Z q)g-TX5 nV__7i aZMeT,EP2aZk WXEg7)df5TH!bQSCV4LVlmfŲxo+NPY*F1U /HwOtx6LBK hPy˷S;q䪫:Z^\>4ܴlvq?^|Ft2{) vGڅ vN)Nu \5'YlC8ZͱA+y1S4AY]V[m*Un F  *rX47rCfIMO*zɚ( >K!;Ղx Qk4Wb'3$tH|.F yby&w܍K!dGʽ@"V &ϔh.~w0䪿dM=zv.ݸ3͑&Yz 9#{ڴ>'7N2S985!'L( mÆWl3hv)Lq ci}P+}R02Y12AU{UTgUj $;i\ET262YC!(z|o*O>_ =7\`z{_^t?t?oW!>Jw[־s>Lf9KhNߧx98P,'ɪZr5֭z0a+NN^Q\OZzD6>Aaz /췇FPArq4#YDMzk#}hfp86SZ֖q.ךu pnNXʾe6-_Sm6Ԍ+Pxw]m|sͳtK%<]ן #x?;=<o[$mCCS#yFMM+8ޗi l >C"3ӫMz77'5QϹ sʰz|@1D$# &M䓅ρOΝ'mML(v.-OY(.y{?ё>NeS {)/-[miW LWBnE"d>6 b~VRSxKs+s#ވ `#y;L"7*J)T)/DsA3沉TQQ(I#lΡSd4Q lebZVL<U1)NL|dVS ^[ 5^O%a5&O*jOp+h1bϚ ʶj|tXL#{TWH.Kټi5& >޺>vE_TԹ.sܣ{G;_HFLx$3\Ա9y1_aw MteUx:rVy"dV̇[ y]& u}{O9#Gm_W70+푝݌^]ULU((۫5"sEL "2kr-h[$= c! Ds 촗45iD]i6Xk4Fsf7#p8pN Zm eddnqdZ59BJX-bfc$ɉcDra1?1_xA~Ue%yU0yKN%8@+>#.ɍ7:v+ik~=+W&xW̨5>>-PNW6uhL$ z-%\]:\?*&}SEa^Gw ,7*/w >Mf L9|0W}ѴN(lJvFp |@BӑQK2`UJD '.}a&?>1^Ʊ>}䍇-\v l۲q'xSpZSV/v2_`ީ֜/vgCwdvG2ҿDKI_'P)Z3Me/͜bF+30נ3s!cNӫxig%e(f+eToѺ_Oν9qrCߌ4Qo0ll?}`i'\xoWwd񋒨 LAd@KbQhqOD+]I$s1G;x4%4Q f߻KY0/J ?HIi$X9 ViإF%X֑:)Zq%XVq7 (G WI0H0,{$EːMQW@F J4(* Vr󴉿BYb.Zz) ֡zu=% aV,$|+S 8aX򿊰; 厘+ }K$l`$@_ g u%*`Mr [%~OJ `o(@WDXEpH0$}MV fP2> $AgDX̠hSHyV B"l%gh"+{E,EI_ zvk$ɯaX׉[LI3"?`R"$$!1L7p>J03+DO",LEj Cy'"] $h} q8P7^TG3 0AyXnxZeKtulr8 QnH3S8;FG!*酲6h8n[o/z-:01JKkEmR ZھF /c|M;IhHY?HCiP :(Ѕ<3(CeyKu:D&Z?.IbE %$<"ARXΣy"&{1- &4^UD,okm"x$`OV6Nam4d9GjMEhSFYz-_#j.re_af_*Y\"K]td-u|zqu\Iek4I('42mwk1%oKQ0&6NtzMDY[b0 풴ċz|.b:kֿR7Q^K5E ~q{9 `\] ufΉd57JZ2TIqA^CjDc8oSnf=IZ"U{AoBd|/.wd#q=?x{Yvqo1%[]r$%8Fӱu?_oٵ䷶ ]{: x, % tl }ƯXۻ Dln7B6]10ns5~mhtoώ~ 7=B:߱V$Ěm `b|Czn^ t;. TtP{ ĺt?Хs>|u׽}%x^8`_ wMW|Ndcc g˺ɺ8c,ٖ)Q 6j I!Mf̤NmH; i,Șt2[?%M)d&t?ޞBc3yoۓoG)&ǢZ:}+9`c#'D|PJi0ÑddAHIic|"1Ԩ%!Gh<1m*N4ӆD<ڒV)6L ?O$FRAcє(D CiS  px4ŰaMkr]Cuh=NZTCĈt!s8z"ٳc_[g_~Czh_[pwP = >c-ؙ<^CgM^Š`-tD%T`,q`gJd4sIa`%XFoIcp^1 , % hfsQ2oya8H#4R"TT[,ADBL0FXt .K`E!.aC'a;'m\rKX92s2z/LRX2= . 퇡 7-XhiE.t&f }HF!N >B |acB^-ooћքCliJ>OoCCY١6x<^/>'vܽsDg#^ qT$ ">_WOW$=H$ la9gřnJERqQT\T*.*JERqQT\T*.*-*N,E/~ސ'02 U޿ |;=Wڲr7q7۰|,w џIӮ Y+}6fV+}]6m2Ѥ,'KџFa'F[" g)wc=~R]]{7qj&ٟkaʽ_u8շlUf9c iHӐނdsA9a1VsZ8hdP9!$Ả0sQ`PP{PH )lRԉ w BcmHBC2*xW@w`aR9!Chݠo@c%P+9.`uOI=Ezͫ2%5^7 ;t5g:{}Nٟ?]׃{w=ϳ9^`do:r&^p2Wr *{B=} 9_˞.'CգvނC7U7H ˨x-bk؏&gu\|[ tZ>LCL_̓毚con420יk,UJ= Kb),XuHJMy`TV+<$;K;XN;0pN$5#4Hv0c 73ł3qiLq̔i88"LI%17J*yu8 z`,>ت>k?D[Ccݭ4;c!rl5OV_%L5JZZl&R(3CB/Pl-vv{xue{ZLU kcW7H01 0cW )TJKP`fcagk> ZPlf J9ә6N@7ь2%|9D@RIOy}{ީd:H<5S"8LSJ.CCJO endstream endobj 80 0 obj 21167 endobj 81 0 obj << /Type /FontDescriptor /FontName /EAAAAA+TimesNewRomanPS-ItalicMT /Flags 70 /FontBBox [ -497 -306 1120 1024 ] /ItalicAngle -30 /Ascent 891 /Descent 216 /CapHeight 1023 /StemV 80 /FontFile2 79 0 R >> endobj 82 0 obj << /Length 433 /Filter /FlateDecode >> stream x]ˎ0^N"Ee!14Rq"}gFU7bIRvߍ&Fs|ܭƙt>DM5⯹C%SqϽlȘpül`{h]ż*\:އᏻ:?4´Vd6UXGp6O'Qd܇>ЇOO} '0 7. <Νw_{fendstream endobj 83 0 obj << /Type /Font /Subtype /TrueType /BaseFont /EAAAAA+TimesNewRomanPS-ItalicMT /FirstChar 0 /LastChar 46 /Widths [ 777 610 443 443 389 250 666 500 500 277 389 277 722 500 277 610 500 500 610 277 722 500 443 930 500 500 677 666 500 467 436 722 464 477 443 333 674 636 505 677 705 664 638 496 688 443 434 ] /FontDescriptor 81 0 R /ToUnicode 82 0 R >> endobj 84 0 obj << /Length 85 0 R /Filter /FlateDecode /Length1 57980 >> stream xԼy|չ?|Ιh5fF%۲-2q8$;DY ҄%@ &t!lC qmRI[Z%6@[b}HI{tfy_k mC RV]b$K=B}զ#iqt#^z%ҿ慗lYk}!v]fճݥkԭ [W/+ǡ%V C7/]q 8>\5O;?Bp}#psHFأ5# }_ax?45Ohuzb.Ca!xBܛ܉+/ w ݏ~Cu%̥>WJ.F=g<(+yD2|\uO8<AKУ8|>|\K22;\?GBozuur-q*4ZO=1؄83*|Hcܭ[@+yy1݀hif;_»(sUGgS{quzj^y] s=o08 WjJCx-_%| ?'^r+y@cLI2>6Lb*T8\h8LgIu@k=h:g5w:k5bX \Ow.U|?~OS+II4 zrXWt 5Ux;`~3!RIf9D|YE#'s:y#0 Ō1O22qv;]Ͼ|. q[oqr?&iW5Ԍku~okѾ%ݗ!澊gd&?$3?%_Ï% wFCd{?0VbW_.=񣿀<Iifo`_[%BK'{.( 洛?B^$DgН^ݭFO!&mEЇw˦fvl4 ‹_"=h\܇AGXp l _ j +}1Y=kqBfrzTɽJcwrԂJ)rp fWߠn4B2F :̇_SgJ9"+\P^N82/;d2|NnK؅, po2j6q{pOr?~AW tF} >g\aA2۳aJ~ 萟ћ9U:N/Z  H~Y( l r=*go9{m.Hi*p3[> P{PBd^ABh6,(?b* } " T4{+sL!^r9t6=Z4756粙ښtUeE*Y^˒Fp(xl2:cQE=qqJW3"0}9cag*p:S)>RY#c?#x٢hSMj{6C;/D:#cx85ֽiݮNFCرPY7i֘Gy{ڰ ҙbgטOw0]+V/ Dccr Ǭ)ԡ̘cLL"4Gv:Σ)jqc̊An ]c`G̮.EݵkgdEgrp%rn[a{G c}T[#vё#czqnð4]c-'~qZ2 FN-}Jwʊy[qbXK ƚԖz:mszf1#b,*w2 35bMڵNA [ +rјcxD8#>F@g(hdcDNNiRc$%m)cUVl'>Lꇹ]1ؔF2۶h؏'N azRzd̑_Tט.~mݎuMc?^S<޻X]l ҵk4K7>Vj9:)HQQ.?}2 XWku@ts!_~i|Z}m57?La6Nz,۵pֱn@vu]ûVOo[)Fxq!+]#]3+:>-[!&Vf?.=/8k%OL:g>.CuѩGYu@H͌ajxu *^h?ю3:Ł89st9|BzP"oe?ٲ`C?EMuP`ĢS)C{RH~9rgOYᵼMH$A;cX4/fI~@M#FkqkƧ<ћ,j+_sT(55#5l݊PI]1jb713q&_u(}(!d&z9oDTje*mԚ5Sk8yI@ Co?آDްONC\-Ox2˂rT ԿP%}NVV rVHjq$5uVv^;cwK:t}h4pAt3h.g Z7hmF% wfq1ŏyvV7dzν箬qeͬs^Y&Q0֪zVjn$O;bl\6W5pQ~LxXE+ݛr݄W"ZӸ5q k5Tf>WhY[H#$X[-KY'QVeP!8_E޵6Z,fNc}PJלCi946ADJ(ba ޖ=V8:qb?R*"UJ[>~Z.p%RkYA!Hɲ:7v"Ӊ#c FMYNk)T}(M*)GSH2x2E?J@<(UaPT9 a7Gp- j6Ccg"[(G3QP t$‪4ZsX"[kBge+BgKqc{ _]J6_ﻼ9ZU0t3.d5w K$F_!$v_*)iAdR)IhӮ3n6n1?d5IC]$wߘM>a O˵,^:%#^~9]9sVL-#!DZSeN fr)Ɯ鑍[s l^澏( ɛ;cU/~}|ULO-F xr''S8jԻx]:BjJH FM^F(?G+IBϐ01FrN" ( A# V{4Ob[@C\N;HzuE1ViX1(Zqp7ɒ?Rͅu|o/M?Wߍ,xvr _y+fˉ,sWxGzy]oy#9_1n j1ho_o7;K"A1Q,ƐFQU!e4Ujf8EDrYR9Dʼn;ZӾ{igw[L翲i?i=f2ڭԭěU6=A1DB$GB1c$dL@yyD&+s8>hK +&B~~.J/EɖCdpMO ]mnfyFD)Y0PDǕ9\m,vyhyп,0Puし2G5QBͅ MwFٔhLZAO}9-t%ʤ%~d6{v;),fҺi$&8AjO*}v9!r?29Qè8,x<~+"q(<&:8B1p8?/89׵F UYf/J2{N]U*ec'9[t-S( u5T9 J]-&Z`x.K %)_vΪQSV{5m񚚮u/mXzeuq7ZƼWp`o\A@#>>[8ղdi7C)%MO0C*eUJ(Cc xP`&h :\A MM὎G:Ge.b,c8hc8&:\^H?{Fmt.kJa (k}Vz[֊x LH&rjK@Y*{LEtbj}R×0UBsb*&GUGG2H6*@j&X S+F_Wy#] Ge -ʲξ>_fќ;A@(j cٸ?'1oHŴ 67tq|Il灂@9!3<[Pl{ E9ZBq)-4 %,I(켝ؑwmҒ4b*Z^!B bww }?v Z`@ bHbQe:VXSPFs\]I%[.QJLsָ'4>>&I.~*$e}YfĩX[&JfҴF }BL&O ӣ}CgpkL  u%UwzያѺb`qpP8]',.`᦮Qcl2Xs; $k RyH& sm{^&ٹeF_*`dW$e.e~r$# 3}TZ<ťF67*-[r_ޝcLhj|`動oFߨ|3n>s? &FO&i7pI~:9OiakTH P`| o|\C: )k:M۝a`i3$ NYӋK+Z"Iʀ0cm<GL ~–ѯ`fF#ZVg,qFkss:S8^!/ا+`:'8`cbEmk HjR$©YGDtD/hf{2n=PL30vV~(଩<꟤$j<5oBGkbCU:U\TnSL(ª3`Y *уwNNkRMG[mL ^CyhSfO="$e9'dzYM'v53OU IRB aǑ .&$5{.h_ƃa xX[ CUJCXB$"ώXvŇc,aU>hʲ}j,Adˌ~V{4RAIz7UҿZTgrEh۱? 팫)]Ռ߮-Ǧp ~ rmYdyܼ 4mW/Y1݁,r TAfЃ9sf1P MYu5n:gVmmƙ*EiyUͤߌf-:o =soى(vnwjuGl%qQqNZuww:᳨xM8SY{z{=9^w:u9Ojn̩5[{{}5RTJD-W۽p1^/@@{:.1J ]?2Y[S\wg}]2 ّNٝzYJ^b1h͚uV-=œ{{K6:̚uD ii.Y6uJ.ݷ2 .HvjI}Mzɾ%dɒax@AHaO֦C9Ԡd}7QhӨ6AZo%ޭhGZoR,O/< O;{_4xp)v?:FJ,e(; dgݙlR;˧U#"KOi9baLnUN%k;YXT=REpmCWVʜߗ%' /"pQٗysWV+M]z?i]f>LvoM=7z[܏+Dƻq9:yGj!T"T,9oǤ{e5GE*"R$PInd Yt:)H epij04XsM!ܦc?, 1զUO30%/Bm:OJ=@2m?W3E'l#9]J:}9gK79ی[gs]{/֜,RmkjܺQ _kk-SW0(|#HAȯge/Rc(@fQš x7xnh.r'*,d[m2ۘ&jFHFAƌ@`Bh۲zI0r nd#)߾L穃@GSU?%o@>y5aҖ'A͌<8DѸ2.1LfnBi*Ψv@z)xVs<@Hu)~>-'AGa+麯nw\pA|.Ͻڐ^8ko O)CCpPS9׸aEUÖȇV)G+FW !bCVR>`}X1 91aܢԤ t' *"?f$X>*{{/aǠ8Pr=7D>vYyxP{џ{*p΅ZEU]#";cOjD^?yWw"bSZ&٧U5zXnd/͂v HQvga#;nTȦ]ňxG& _ '6E2 j. pUu*IwĥCE7Zqs^{C- k?Rf؛2H@O(M]tٽC7 !fpHMo{<Ȩ@j$*hXq3u5 9i [1Dc)ؙs代)Ȧfs~B@efKJMܓ{m/ȺsHn~^sU/~YTl0}6:8d}~n6B-Յ]gd:ݠv`e4(J>)$Yt҄Dm7OmA u,TJ,g֜PGYyґci(꠨٢9,ei(KGY8ʎg*H9A~`މE TM}J iJ{8ڱ9t93gعE騝U[ŵsWiX難meV'd>po^uNݻzꡮv"i JG-S/Y)  m2365jF-ؒolĖ nQG??g?kONsem-X8)֊!Y۸|W%-oKe%I/PKAzE4O'avjq⦏2TSxb`A'.ٛZM-4-h!QH䕂x2p@5Ckp CRқb6i6R#3G͌RL LiL)Ьrgk8'$*Tң:jVa17a7 YO=}KP8Lci4C`lA =rө\SPr銆S*=*^7T,yO OBV6+iQL> mwx}M`TOW}iCnsqO`) !7*^x7^brPHHY#!gLÌ"5I$ Bzg n)U\N%;7?#V63Q$Ss&l`TW*5jIuHP5%|쥼FM"ZR z*i=r,5 YtG8F*^"\232if^~e$^n VXVC BAN&JY7 pt6V]wQU{ ǧ^tɮs_u;EV*v-p3rYcL^@~ve˾Ђ),3zC{[JxUcWs?\' X,QS-!0X 'V*/cxEpJ 'OܛK>j}xӽ&DaյÛk*ܝQ41?_T64|HeN˼YrԪBT(jN 6SpKd5~dssՖy<xD9ȅZ].A+S%-<>;?OxFKGcLݥ-^Ҙcn[诪fUR V4?L zK6 PS [h"`rsvun?ZŃl4c ڰaak7PVV%-&Qq0 %$_-(eA͞<(̷xt#.)R|ɐcӥ>mͭT +[_*۰zieꆊu?{]z=w`3 G:~7|o w^Ն=k=p$e~CxސUJ綞MkM^MYvrN^^ҿza6V5^c!Zf&ߦ^:K1Vo7ԈNŇ<,?i[ ރ4Q*UY'LMbZ"݂ü;z^?Qy) cZ l&A%v2PR-*EUJD}R7r'x&0FBўQ͍m93-גh$i56j$xp'ޗ _-|wㆧW,rv \}77d*\Ve|ߞf\:.< ƌx05VU+;\N99U1qHsN_I|%SXZwU߽>siמ ic1641 1d@q*ŘSc[[[Q2 lMN>;cp̓ ĸ\P<"ᰓ/XL"q~1$$yM 24hCاfEۛ&+UQTKIn7my9#O]^5LT8./+ ?)we{i|mgloԘҙ^n{~hmi#fl%5+֩y%铉=t]@y]aiw] o\\AUJUl Abr27kTZ۔=|OxUS^_0_V]2zEoGCѧ~,w}ՌִTqMXXjֈGik,ڐ|{8. %ۛBfCb/ yʆPC`E,q_;ϗkp Nj鶈 פ#a0ftV7sC?$vZ )) 8y5N5_}w'oHej&ɩ'դwա!˂d#gI5f?5֟1zjQfqێrg 8\7cL zpzS_(a,V}pgXA,A7n=.PPPUSnx8~(N՞MhqBIR_b2yB!vYD z[ $Ez=]Z-GeN:@bVթ]ޥ^J,] 8U3y;*nl~(.nK8+,V_SX՗nXXWk05w+BMn\6nYv]yACϹ71f|7ʧnXW`hAs%Xx2'#IQ-YV*"8 ۻQT銐_85n=JP:Һjŭj @־LPWW%f*3[Iӈ+)J2%ә}Qs[Igff>ʰL{f3M&zHT(.n~e04wG(\7J=*X59G;r(foOMRg&ќ 螢pVJM'4CCE%~f˿&'#jbÜn\oȑYᗲb,79f[EMcMd6@rMVҶLSϴb Y}ҸbK Zi`;w!FsVOq:hQ9ϹֹyVv톧//U.ndjLn1:8U\lxa4nJ*V4ŏVDBQRYTT W:V)UUycn.EܥRM)¦q,5^vMG1=g4--S\[^W aA+l}zݟ1?`*M,mn[j2ϼq?o+70+u?bENBQhl1LX*LB/#NhspsF33tgL# `xڭIHi>߱U5Ҿ=)$t$97)(`ʂg VYe( ׅ.G0ˡݦC!@m6Q;Fb7{zC ]lCX(KjǪWW0쏶nYqKl#ǧ6f-ëԒV-2t;iP"S"UuF@)#8c&"^ʸW#EYƊb{oީ|RWf$p0E̵9=g)S]=ʄlx>LBE$ r]f%?ue,OZAq e01!mh70X$K<4s9&uɩϔL)p }ȟʏBl-)T]sdѹMUVN.d63Onjn"t~ww+l "_D Muw{=4/дk9՘r{Yd0@d9)çR9%)f0OJ$SD"*Rh<\I*+QYB<Ɇ2E!  ZbcK;$TKUe^VֆkbU(X6T_iU=58I߶WJl*$8s?9P)Z3BpxStiԮám=Sj7LC1mB&wSuyHǎʭk*d{9|A:&-w"#=z4Ff;cD_'zict*OcPә9{ʮ+pVV 3D)M{QI`Ĉ u?& g4uq<|ygǞvnyғN*޶ć8[1>1x &du5joHq]GbV LLj(NROmzzFg6Cҿqj58JV?/k\ 3>z,eƧ?ƨqa`ey!Fq\k"dx+Be="nS1 XLj/z]V5S'ަ!B.E\~:,vo6;[8%ܣ45Vgb[1nգ1 1%;{' 2 M6Xu??'4V"4&}mmK؟g_Vx^+|pv|Q^ t9݈tNaנFP=p>Y!'GhܨtiL>T.^Eʄ՝fwxaQBT݂KAq"r4L`e1derWGaRFʢdhhY!S$|O}wf}r}brJlЏ튾eu?ϱ+ηbOm l<T6X//{C_[g?uݛVSQ'eX\C| @O?+%PogU ;O.JJ Ҫ}ENޣc8|9X). [ָLJ;huF`Hqy'5}ɣI&I$NR24R̃qcWu&B _>>f ^><;7}mujUoRwZZ ] $adhZ[`N ޗLL[ Z^NJ3Y<wXt`/>+*# K7kv{">7Y^5ӕؼOs|UGf/r;ݛFǎ tZ ":ʉ.샊yKb7"nJr`cdy'=j= "8_7iS0dw{3Y[>$% u@u.ogWV^#ٚd2,ug!YT-pMjWn1;kHKQyB๶inRV6/g > g6T խ~`nEPA! !y،T3ViLwd~ ?; #f [ @Ss:tL//Vp MJyXsmk~`,\ 4GA.2 !HKSLyš 87Nb~[oa&u"ynv-{iw7[*cR86 3h5s.Hy9˨X<^2 /z\9P)V}*(Ǭa1\$;B͓StuĢ>CeX:h,S>ßgJT v?9}KOlhUj[˩;~^eb.B JFT֑UJ`2J*XĪUhg^ǯ+xI_ؤe*-W-kQfktV4Ɨ -"Dzd$=Vl"V'X00(~ 1]KL/ oWׂLŸ?2|]U] 7A0J^?)VfG .`GGK_Y᝞<4s'9Gry|]vXR/\յbO}ce4 } |.;jg> 7ɠ/|*nhn)ڜ0X3w4?jԝM}:Fgf N|$_ߜ7Tw!rjLB:2M.e}rS 0=;ζșq 9]3Č;2^*C}ڧokP@XF@*rj^Qq~Q+ie`,Rѕ`z]vыQ 1.\2øExiƯ񦊲x)1SֈJ;qNI(ǯ# P"I-TecXbYU-IYcU1)#{JHō5/M_4>5L?_1 VS40Dyzk|LݵҩX7v&-wj}BGkfwj}BEVصP^R>X~i[ַlfئѥTǪܧ{}Hwo:`m"8*uNǔ3S|]-OZԫ jjApݡե,rT1ҩAm>eY٬Mm3p`r4*5nU3u`WRSP8Y]V~(ZM=B-дP5l A֎Lz6Xd=?4E9ds0i4%;3rKλ +n0YMUUN*}&O*B _b7L~;f֬_':JV [{;]I++fؑ#ik|h3&`zx@XϺz,Jb/mZ>}s5_P7߷ͯ>h=ݞ NU+o~o2hAZ`Cxș4t/αgzytDžVV 3mA\s@%=s-p<@[`0p00@؃fqFd d/< ((yw\TG)j@B Ȩw\ȣ) / ,ק0*}^#7/Qiϧp`B ^G/F k\眡oܠ {hR>yIYs٣أ=|ЫKqk,jjAlg{˳[vy˪ՏպP9U[|EieAz媩C|[J*o9Է,\QɷjHzٓ|KWXSrU`W> ]-Jr8[(̅L̈[y*m}}gWYh#WW2ϔ:'_ubk#52dB9.O)vr;rֿN pzGYQz*x3{~WI86܅]ix5y^3kOB"L:'t쐏1'0^Vr/]vGo9#9(N^m-XyeUsam[we9ȳ]3bb} [c>m=y^huۧ={lDz'Rng{Wl\]ol~P/Г|V`jbZD~f^Թ4-BM=M*\Yeqa/_hu{E7-X^`y%WyAOoxpe+\ʺnNFME rj夜_rGGO,W,r3vqq{ԿU >f0~qU|Sl'1:-'뙃ll}qfX>X|q63ap }"KdPG6MtMR+/BkoTY*ScDUK: jZ=e+֍f^h&6 5P nY~r92x QzeUӢmO,W.`ZUOT JHMr23eՑɊ1R׬'|4f%p39I.y>}A6dImͥ/E!'ĪE[Z6o- N;fkδO9kQw.zw 떬[}7E5^ܰ4nZe:]7gX`͎B3;M.S@m6m?x| ]'qb^UMBP:^ƛo~$p_(xc zRߑ8c%QRf!H+f1r܌-W~ӧ;&' G䏌f5j| doSٕJ?pH=7:_E3:sYV" ՋC}nKlw]l& z7lU`PL!;zHدo`7 8~Qcf`/ыzV3 ;3DR!ׁ $gZ-ZGMVF,b%ܑ4م= nTF "LE YY=+u}k\_l>S g;^[qvfOt.v?i|oHj O~F5߈+ Ln1?5J԰ +w1dHcR4&)GcӨ@^ݦ ^rVzD^ʗ+08]15 Nmj3{>[YSW*U0s(j"aF5 ++=G\4YD2@} ^tqu /cR\؅.KMc'{zvn f[PHH~C=Iaq%Z(i|^;7lHgA?o'6#$]~ XNB'HG΢E}_tr24$mB̭5kQ~ss:L'{U-ٶ݈јb#U+LJ;Սƒ_4}ҡo0*GTd+m0^f hG-y7-׻ӠYmLȝEYN%ePNN7),oHA:*]qE9AY~7|O!ƫ\LhB3ʹm1Q3HLc˫h {y%Xt=RkуQ^Qzd࡮آz_ޤgQ31י''J$hfB֙~et2Z7s(//VW>YEFjn['U s>w!{Q"e"33MfST/[x,Td\2zaCʜ偯4ـ pslDsmd.(,=y0.~Θ^t޵u5RD\uLNTN0BXPo^g9qK`mEq|emŧW=Ő](e&}INa2&g@f[oL6&_p2x~gOx{JҨ dWT^402=XԛdQGF폒C? \GpE?JHmvz&xjtm\TzrSqC%CO*H59:=UE4$ȝ(i qL^.|4[$o/hm@#Z4<`eH!5˙wIBbȿBӛ!Bԅh)~ ڱX+ %4hVIB.j\gu F "R!Bgwݓ[ '/'Hs/>Pc &wJ/y8pWV-sdsO=U^=ַ<ԗ[o],iZv5}|qCt (%h1ZWL0.ݡ3|om:bG?.ʵjBEO[s[QS᧖ކ|c DŽZ0F ;|.tjEsu/֒Vʉbz̜gbJVgՃ3°"%)>Q7#MR:VX/ϩ!Ge\SP38qX(j s |Ӱss,UaO83Q0B8 vY<7Bt|ǭO7 ey |ٻm8<f"0?,ԅHr[&"#'BBO@G~OY6hۋ&!i{i#9s;|?eRfiy3ZH) 4!g9R' TǑG4/ &ѵ>64BumiEԤƜ%SfQD( X={\|~)RH^O):$gWH{H3uKה?٩2K,0FyV1 +J\6K\j93VKy|.5{Ughx†m&ɷlS=~vx-G^^gr #t PF^F I]7.Y Ř6a22;j`r;>6p"Ʊ\Go0 O;>Q<l#-jR K4rQ;t)L.< ` g~zJt yD5"qɠg\d~bƑnҤU\X!1eab@jdOihkg΀lWSȇSiI ?laHfN#WqYт?cOv#7WϧuM ߶=o3U4!GJGZ<RO7ffq" 4Z发 jzgHϢ2Lbدb_Gy5Ga@+Q& i%и'Fy`V@6 FV!b!jV܀ rM!b d@|f)M+_AbrH+(n&TNJ!q(nAQųȫx\b ~!]i!Nj NB(Zճha%)jԗ k\(ZļPuw7ʰUgd؀ʳfh3 T!Pv +P=Da%2@A{'U௲Eoj"YؿOa ˰Ha-on | C<2 u408, _;2 uNAyNͤu:d |0+ _1o*xBWI rȰ660F 2@%(la/)LKRYdx^dST'@OQ3Zt +PHJ+@ ⟕aKi90@x52 o0Oa/Kt AŸb %IMPD3)==2L~$2Lߠ0K2L=֡4P'z:Cf4d,-Ď/1Ef)ZxӇgq\)RT %(,CeԷBlR4/ &viO@-8h]^splP+B7I0 Y΅Bυm>i%M#~ hJ]A"jAgchb^@~^3حv7/CIJ{[Ehti%4 gbũ]m%$Lu2Ei +k)/U4ODWAjTy\4faIHP 4/m<>><#上%˨r2 qh n9(`xS'FqO "e8I([Gy:)]=O#vQ撸v2cI_7<@W|&ef8"ImS:GIYO)/ 2ziJVz" 7eJgH2Lk?A=:Zv8{r(Hag*iNZs- 9ĬK+cRHz=0Kz\K:E/i00ͻfĘؼz mJY 9퓵1&;$9PKJ D R7"xֵRLIuf,FUESy4>Jni8D8 nC4e~]At]jRs._Hq&E7He2rfPvFŴO>3< Ŵ֘r; )ZSmqKNmdl)RiRJ}%!p+Dk>k&eТ7_3]HG\sa 33.<2[3VvNV%ݚFΤIrs^`~-;CTvkb4H/Ti*iv95R:wVZ{-gqfKnqIYWί奒-k5 p,~NM:Sf/LKI[eo;/^)-P+.LW.p.Fsm'^xڄWmԦ\ IN 0F)k/"sAřz|ۼtIE9-R럘7^(N۹~ssʹIs:osp08g6?]{\KJJՠs:y9ӏ4Jn-zvyZN*d8s1 о\ܖ~4Bgd\nOmgPbxFbxv)߶O)-a ڞI#BEQ-K) (D]Vʘ%De U4T%1$\[_ogEl{nd0 zB@@ %D,;/ u Kc%"$2a@0I +!\iuuIbOXӻ'ēx׺%o뚉wyY>HʊK@r;|\^ӷmk4uzZXuwۅͫoMx/ʵB[,%uBkww}@+ 4-&bz 6 @35V]l]Ooh!$ǶŅԌ<{ɔd :qICW wA㻒xHvv)aW,)tœ]ł"%Orxk2>4 4[;@Gk וv x2=GI:q$Fm ]R.` wsM; l$~@m= !~]*ޟLƉTL RE;vIt' tdh;܌H k%$~%.:Ý)@Nm`<5O{%6FB25%bbKv&Ò|b]Acj@F3G2:75 ҮTm`4h1T_A'%=:0=I OJZC HÀ]R r/9@Ѻz}ɳkg<%y-Ik"]vY IHv#;SDt R"/pWh" ҺnEӊ%VMªK[6 46673:Zvf {c)4WeQas`' $%vYۖ Êva lMŀ s!u.(Bj n`]D@D/D dʥsDA)el`cX0Rbd<5?tp%H()#3<5!(aLH;{AE.̹\$:uur ?Aۅ"❠uyD5rLIu2<9 *}%{$$59tnB+2GJ!TvI x_AB"'{&;{A!:pa H2T.o6@$R>'cLuţ$mk|&"H'!WFpA* T.DKJ+WgIii4 vUyPUYQ]Qm}FH"2yҏR7t.i\F0ѥ'%$ɾ ؇/<D'.O] #7x&a$ås)9̕r{<\K[gt6]2ZwvB56. !/ҹye*Xaedre'u >(C-MC};f,_`ṡ`mzXWe7+rrʭ,7y+AIZٖtNC`elAMYQf%{$NVR%cLRJk2wH.wlpj+Akh1z3`J1T-d-`Az >[ yDg JKQ`^snÀ `1&E0[s16 }C%`3 Kv E@G[ hJ05xڃ0{====WT&^KbbUE;ݫeKM˚V]fUM?5f\u@u@7@ṗ`Xt O&Xkc'?lf{eO? bj#bfvg<a@[zۦgMzAԏ'QՄ#ME5ګ::RKԢJQԃ`a^PDL}>["m]B 0o/<<@ } ü#K1y/#/>=eF^fyR6P6m6Mm 0-E<]o ؃n1o=U\yb c`NaQ:0'̣`#cEe{ǙG~qKKr3f-\1@@@$SGN0܇Ӌ%D}4!0 j1 E!>}B` B<3aY{ E=!7gj^3?<.43&9i[xoUDgScy#jOk ߽yOx{G{ /\Wn㿸/Ϻn*7z&FIl| D``````l5Ci_Ԯx)jFg25:!QW##KkqVK&?'/\#ݼtsK^^p0Hx&אIGVx:i 'n8gQpHWP7ߥߠB-53w<\/IQ-w \:BpJ<)9O<8LwtU8 H|BCOd$tsts8ӵNo Ơٸ)twܻ$gכPyYDRočrF%caZm:gĹE8 ҅c\.pRN @ $"O:@p\Fp$$eS5ZJTf:HL5֣n#/S淋x}D4q!8/mG8tk@΋ @}@Qsn7Ab~(?a[C4!Xp`*`?Nh i@DG7à }|2 -$ 2 Ļ J5V<9ZQK_,^7𢎼 ^A⊣G(e+ x/X~U}zzڛOvƤ4:F(4ic%'nyD b3q 0 4jaW2+6VVϮ]Z9nzaڎWNt[3k=XzӀG+u 9<9Ѻ , qcԼnM}e˩3fV7-E+ yr>rge8l_9X+lpcm\zN Gp׸㞥퀶Zf;f 44 磊kk%V| AiH%%ػ"}CJ0t@"qMه4~?'(p_F_{](8s+OS3o/cwoh{x`޵'gtVA8ܿFY=čGwzKG=KË7\zgaq݆bqq'}C礵o67]$Hd HZ-Cy=D^HZC$rM^ٶ5~;uPZ8M,Ϲ᧑>>j4Wp=yEoN~s|i 3= (ػK> endobj 87 0 obj << /Length 626 /Filter /FlateDecode >> stream x]nPH]?$D H,>/b,,xTU7A;sof2~o̾sjںO{_%ޚv:M5|H}Tc7uH}{r:1fpyXחI_~7y9~sjSL'i|̔p{'%R[uӵ;V?oi:Y,wtE T:cGżXIO^"%JD( X"^ Xo$Zb 煰Ď0 [ ^$,BXrlIDD)QX/yOUNJf~<4eg#7UC<0x<̳`>41H!lS.<5c:=xa>oTy7%lÏ6 ;^4P*xN<" 8^qy>cՐy@OX#ԪE5>SR5> endobj 89 0 obj << /Length 90 0 R /Filter /FlateDecode /Length1 1384 >> stream xkAƟMbbM ȪUKH6PA )l&llē7=B@z&xE^i|v3&E}xxg^8Ba11!#eqhCmbNB{2>x47Mʤx̮$fJC1665k;N2~^k4c—<@` ߛ,Jxc=z2zFΊowvTǬuAW'2ΞKXyPH_ٹ|:ׯx_ߘX U-=68wڿzǽ{T@V?? P~/ hJQ r RU$Y~]rIc$Go @G`eIVXU]!a.9B}3Eh)$ӧ(>ŪdSܒLdX%qt2Zpϣʜh7a_"&(N63iHl\;j(mǽ{fY3I][nZjzFѴ蚉Brz.ZmxCaH+ר\m.Zo|o#o] endstream endobj 90 0 obj 582 endobj 91 0 obj << /Type /FontDescriptor /FontName /FAAAAA+OpenSymbol /Flags 4 /FontBBox [ -179 -313 1083 918 ] /ItalicAngle 0 /Ascent 917 /Descent 313 /CapHeight 917 /StemV 80 /FontFile2 89 0 R >> endobj 92 0 obj << /Length 229 /Filter /FlateDecode >> stream x] EC!i/0Qy1Ӗa6sıl #Ϥ:㬔`cg"2E{*5Svw uv9E(8k0دo~pV#1q9$r[܂r립)(܀UEQCu:՜3oR뇢., )7f컗R<\e-a|soendstream endobj 93 0 obj << /Type /Font /Subtype /TrueType /BaseFont /FAAAAA+OpenSymbol /FirstChar 0 /LastChar 1 /Widths [ 500 356 ] /FontDescriptor 91 0 R /ToUnicode 92 0 R >> endobj 94 0 obj << /F1 88 0 R /F2 78 0 R /F3 73 0 R /F4 83 0 R /F5 93 0 R /F6 68 0 R >> endobj 95 0 obj << /Im47 47 0 R /Im39 39 0 R /Im34 34 0 R /Im33 33 0 R >> endobj 96 0 obj << /Font 94 0 R /XObject 95 0 R /ProcSet [ /PDF /ImageC /ImageI ] >> endobj 97 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 1 0 R >> endobj 98 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 3 0 R >> endobj 99 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 5 0 R >> endobj 100 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 7 0 R >> endobj 101 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 9 0 R >> endobj 102 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 11 0 R >> endobj 103 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 13 0 R >> endobj 104 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 15 0 R >> endobj 105 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 17 0 R >> endobj 106 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 19 0 R >> endobj 107 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 21 0 R >> endobj 108 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 23 0 R >> endobj 109 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 25 0 R >> endobj 110 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 27 0 R >> endobj 111 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 29 0 R >> endobj 112 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 31 0 R >> endobj 113 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 37 0 R >> endobj 114 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 41 0 R >> endobj 115 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 43 0 R >> endobj 116 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 45 0 R >> endobj 117 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 49 0 R >> endobj 118 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 51 0 R >> endobj 119 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 53 0 R >> endobj 120 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 55 0 R >> endobj 121 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 57 0 R >> endobj 122 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 59 0 R >> endobj 123 0 obj << /Type /Page /Parent 63 0 R /Resources 96 0 R /MediaBox [ 0 0 612 792 ] /Contents 61 0 R >> endobj 63 0 obj << /Type /Pages /Resources 96 0 R /MediaBox [ 0 0 595 842 ] /Kids [ 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R 102 0 R 103 0 R 104 0 R 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R ] /Count 27 >> endobj 124 0 obj << /Type /Catalog /Pages 63 0 R >> endobj 125 0 obj << /Creator /Producer /CreationDate (D:20041209112150+03'00') >> endobj xref 0 126 0000000000 65535 f 0000000017 00000 n 0000001709 00000 n 0000001736 00000 n 0000005423 00000 n 0000005450 00000 n 0000010266 00000 n 0000010293 00000 n 0000015016 00000 n 0000015043 00000 n 0000018719 00000 n 0000018747 00000 n 0000022819 00000 n 0000022847 00000 n 0000027457 00000 n 0000027485 00000 n 0000032966 00000 n 0000032994 00000 n 0000036989 00000 n 0000037017 00000 n 0000041550 00000 n 0000041578 00000 n 0000045583 00000 n 0000045611 00000 n 0000049742 00000 n 0000049770 00000 n 0000053965 00000 n 0000053993 00000 n 0000057749 00000 n 0000057777 00000 n 0000060341 00000 n 0000060369 00000 n 0000062107 00000 n 0000068218 00000 n 0000062135 00000 n 0000068192 00000 n 0000071262 00000 n 0000071288 00000 n 0000073376 00000 n 0000073404 00000 n 0000086257 00000 n 0000086284 00000 n 0000089255 00000 n 0000089283 00000 n 0000091371 00000 n 0000091399 00000 n 0000091947 00000 n 0000091974 00000 n 0000102301 00000 n 0000102327 00000 n 0000103750 00000 n 0000103778 00000 n 0000105494 00000 n 0000105522 00000 n 0000107054 00000 n 0000107082 00000 n 0000109188 00000 n 0000109216 00000 n 0000110870 00000 n 0000110898 00000 n 0000113717 00000 n 0000113745 00000 n 0000116071 00000 n 0000292133 00000 n 0000116099 00000 n 0000153146 00000 n 0000153173 00000 n 0000153417 00000 n 0000154289 00000 n 0000155109 00000 n 0000173917 00000 n 0000173944 00000 n 0000174183 00000 n 0000174677 00000 n 0000175063 00000 n 0000226890 00000 n 0000226917 00000 n 0000227166 00000 n 0000228061 00000 n 0000228914 00000 n 0000250189 00000 n 0000250216 00000 n 0000250475 00000 n 0000250991 00000 n 0000251418 00000 n 0000285124 00000 n 0000285151 00000 n 0000285405 00000 n 0000286114 00000 n 0000286767 00000 n 0000287456 00000 n 0000287481 00000 n 0000287721 00000 n 0000288033 00000 n 0000288235 00000 n 0000288352 00000 n 0000288447 00000 n 0000288550 00000 n 0000288681 00000 n 0000288812 00000 n 0000288943 00000 n 0000289075 00000 n 0000289207 00000 n 0000289340 00000 n 0000289473 00000 n 0000289606 00000 n 0000289739 00000 n 0000289872 00000 n 0000290005 00000 n 0000290138 00000 n 0000290271 00000 n 0000290404 00000 n 0000290537 00000 n 0000290670 00000 n 0000290803 00000 n 0000290936 00000 n 0000291069 00000 n 0000291202 00000 n 0000291335 00000 n 0000291468 00000 n 0000291601 00000 n 0000291734 00000 n 0000291867 00000 n 0000292000 00000 n 0000292791 00000 n 0000292853 00000 n trailer << /Size 126 /Root 124 0 R /Info 125 0 R >> startxref 293061 %%EOF phpgacl-3.3.7/docs/translations/russian/manual_rus.html0100644025754300001440000021705510270765617022267 0ustar ipsousers phpGACL - русская документация

phpGACL - русская документация

Generic Access Control List

Mike Benoit (
ipso@snappymail.ca)
James Russell (james-phpgacl@ps2-pro.com)
Karsten Dambekalns (k.dambekalns@fishfarm.de)
Русский перевод Феськов Кузьма (kuzma@russofile.ru) (http://php.russofile.ru)
Copyright © 2002,2003, Mike Benoit
Copyright © 2003, James Russell
Copyright © 2003, Karsten Dambekalns
Document Version: 672
Last Updated: 5/20/03 - 18:55:08

phpGACL

Что это?

PhpGACL – это набор функций, который позволяет определить права доступа к произвольным объектам (например, страницам, базам данных и так далее) другим объектам (например, пользователями, удаленными хостами и так далее).

Это предполагает подробный контроль доступа с простым управлением и высокой скоростью.

Скрипт написан на PHP (отсюда его название – phpGACL) – это популярный скриптовый язык, который обычно используется для динамического создания страниц сети. GACL часть phpGACL – Родовой список контроля доступа.

Где взять?

PhpGACL хостится на sourceforge.net здесь http://phpGACL.sourceforge.net/

Что нужно для запуска?

PhpGACL требует реляционную базу данных для хранения информации о правах. Доступ к базе данных осуществляется через абстрактный класс доступа к базам данных ADOdb (http://php.weblogs.com/adodb). Данный класс позволяет работать с такими базами данных как: PostgreSQL, MySQL, Oracle.

Поскольку phpGACL написан на языке PHP, то он требует установки PHP версии не ниже 4.2. ACL (Администрирование списка контроля доступа) дополнена веб-интерфейсом, потому необходимо наличие веб-сервера с поддержкой PHP, например, Apache (http://httpd.apache.org/).

Авторы

Mike Benoit (ipso@snappymail.ca) – автор и менеджер проекта.

James Russel (james-phpgacl@ps2-pro.com) и

Karsten Dambekalns (k.dambekalns@fishfarm.de) – авторы документации.

Feskov Kuzma (kuzma@russofile.ru) – русский перевод

Последняя версия перевода всегда здесь: http://php.russofile.ru

Введение

Понимание контроля действия

Хан – капитан “Сокола тысячелетия” и Чуви – его второй офицер. Они взяли на борт несколько пассажиров: Люка, Оби-Вана, R2-D2 и C3PO. Хан должен определить ограничения доступа для различных мест (комнат) корабля, например: кабина, машинный зал, двигатели и внешнее оружие.

Хан сказал: “Я и Чуви могут иметь доступ всюду, но, после особенно грязного ремонта гипердвигателя, я запрещаю Чуви когда-либо подходить к машинному залу. Пассажиры ограничены пассажирским залом”.

С этого момента, давайте будем считать, что доступ является Boolean (Булевым), то есть, результат запроса на доступ персонажа к какому-либо помещению является “Разрешить” или “Не разрешить”, и нет никакого среднего результата.

Если мы нанесем это утверждение на матрицу доступа, показывающую, кто имеет доступ куда-либо, то это бы выглядело примерно так (O – доступ разрешен, X – доступ запрещен):
Кто / Куда Кабина Пассажирский зал Оружие Машинный зал
Хан O O O O
Чуви O O O X
Оби-Ван X O X X
Люк X O X X
R2-D2 X O X X
C3PO X O X X

Колонки показывают список комнат, к которым Хан может ограничить доступ, а ряды показывают персон, которые могли бы запросить доступ к тем или иным комнатам. Более широкое объяснение:

“комнаты” - это вещи (объекты) для управления доступа к ним. Мы называем эти объекты ACO (Access Control Objects - Объекты контроля доступа);

“люди” - “вещи (объекты), запрашивающие доступ”. Мы называем их ARO (Access Request Objects - Объекты запроса доступа). Люди запрашивают доступ к комнатам, или, в нашей терминологии, AROs запрашивают доступ к ACOs.

Так же есть третий тип объекта – AXO (Access eXtension Objects – Объекты расширения доступа), но о нем мы поговорим позже. Эти объекты имеют много атрибутов и упоминаются вместе как Объекты доступа.

Управление доступом, используя матрицу доступа, подобной выше, имеет преимущества и недостатки:

Плюсы:

  • очень подробный – есть возможность управления доступом на уровне конкретного человека, если есть такая необходимость;
  • легко увидеть, кто имеет доступ и к чему. Ответ находится в пересечении человека и комнаты.

Минусы:

  • трудно управлять на крупной матрице доступа. 6 пассажиров и 4 места – это просто, но что, если у вас тысячи пассажиров и сотни мест, и вы должны ограничить доступ большими группами и сразу, но сохранить при этом подробный контроль для отдельного индивидуума? Это означало бы большое и трудоемкое регулирование матрицы, а так же поставило бы очень трудную задачу проверки матрицы на правильность;
  • трудно суммировать и визуализировать. Предыдущий пример довольно прост, его можно записать в несколько предложений (смотрите высказывание Хана), но что, если матрица напоминает такую?
Кто / Куда Кабина Пассажирский зал Оружие Машинный зал
Хан O O O O
Чуви O X O X
Оби-Ван X O X X
Люк 0 O 0 X
R2-D2 X O X 0
C3PO 0 O X 0

Эта матрица не на столько очевидна для суммирования, и не понятна для читателя, почему те или иные решения были приняты.

Управление контролем доступа при помощи phpGACL

Ясно, что для контроля над большими системами и сложными ситуациями этот подход “матрицы доступа” является неподходящим. Мы нуждаемся в лучшей системе, которая поддерживает плюсы (подробный контроль и понятную визуализацию того, кто имеет имеет доступ и к чему) и удаляет неудобства (трудность суммирования, трудность управления большими группами людей сразу). Одно решение – phpGACL.

PhpGACL не описывает доступ от “восходящего” подобно матрице доступа выше. Вместо этого, описывает доступ сверху вниз, подобно текстовому описанию политики доступа Хана. Это – очень гибкая система, которая позволяет вам управлять доступом в больших группах, так же позволяет аккуратно суммировать политику доступа и легко видеть, кто имеет право на доступ и к чему.

ARO-дерево определяет иерархию групп AROs (объектов запроса доступа). Это подобно представлению дерева папок и файлов. “Папки” - группы, “файлы” - AROs.

Давайте попробуем составить ACL-дерево для людей на судне Хана. Сначала мы определим некоторые категории для людей. Ясно, что Хан и Чуви управляют судном, а остальные объекты – это только пассажиры.

Пассажиры Сокола тысячилетия    Группа
├─Команда                       Группа
│ ├─Хан                         ARO
│ └─Чуви                        ARO
└─Пассажиры                     Группа
  ├─Оби-Ван                     ARO
  ├─Люк                         ARO
  ├─R2-D2                       ARO
  └─C3PO                        ARO

Это дерево не определяет никакой политики, оно просто показывает, каким образом мы группируем людей, которые могли бы запросить доступ (AROs).

Мы задаем ограничения доступа, создавая инструкции для определенной комнаты (ACO) какой-то группе или AROs в дереве. Хан говорит: “По умолчанию никому нельзя назначить доступ ни к одной из комнат на Соколе тысячелетия. Но команда должна иметь доступ к каждой комнате. Пассажиры должны иметь доступ только к пассажирскому залу”.

Пассажиры Сокола тысячилетия
├─Команда                       [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ └─Чуви
└─Пассажиры                     [ALLOW: Lounge] (разрешить: пассажирский зал)
  ├─Оби-Ван
  ├─Люк
  ├─R2D2
  └─C3PO

Чтобы интерпретировать наше ARO-дерево, мы начинаем с вершины и двигаемся вниз.

Во-первых, политика по умолчанию состоит в том, чтобы запретить доступ ко всему. Ограничения были изменены только для команды, так что они теперь имеют доступ ко всему (ALL – синоним для всех комнат (кабина, машинный зал, пассажирский зал и оружие). Пассажиры имеют только доступ к пассажирскому залу.

Такой способ описывать политику доступа более ясен чем матрица доступа. Вы можете легко видеть кто имеет доступ к чему, а так же более легко определить, почему они имеют этот доступ (кажется очевидным, что Хан и Чуви имеют доступ ко всему, так как они сгруппированы в “Команду”).

Суммируем:

  • ACO (объекты контроля доступа) – вещи, доступом к которым мы хотим управлять (например страницы сети, базы данных, комнаты и так далее);
  • ARO (объекты запроса доступа) – вещи, которые запрашивают доступ (например, люди, отдаленные компьютеры и так далее);
  • ARO-деревья – определяют иерархию Групп и AROs. Группы могут содержать другие группы и AROs;
  • политика по умолчанию для дерева – запретить все (DENY: ALL);
  • чтобы определить политику доступа, двигайтесь вниз по дереву, явно назначая разрешения Группам и AROs для каждого ACO, если в этом возникает потребность.

Подробный контроль доступа

Ой, мы кажется забыли про Чуви! Отнеся его к команде Хан косвенно дал ему доступ в машинный зал! Но, если помните, он не хотел этого делать. Он не хочет этого после того, как Чуви недавно ремонтировал гипердвигатель. Хан добавляет правило запрещения для Чуви:

Пассажиры Сокола тысячилетия
├─Команда                    [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ └─Чуви                     [DENY: Engines] (запретить: машинный зал)
└─Пассажиры                  [ALLOW: Lounge] (разрешить: пассажирский зал)
  ├─Оби-Ван
  ├─Люк
  ├─R2D2
  └─C3PO

Это пример того, как вы можете управлять политикой доступа в подробной манере. Нет необходимости перемещать Чуви в другую группу, мы просто запрещаем ему на более низком уровне.

Другой пример подробного контроля может возникнуть, когда на корабль напала Империя. Хан должен позволить человеку Люку использовать оружие и позволить роботу R2D2 восстановить гипердвигатель в машинном зале. Он может сделать это расширив их полномочия, которые определены им общим статусом группы “Пассажир”:

Пассажиры Сокола тысячилетия
├─Команда                    [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ └─Чуви                     [DENY: Engines] (запретить: машинный зал)
└─Пассажиры                  [ALLOW: Lounge] (разрешить: пассажирский зал)
  ├─Оби-Ван
  ├─Люк                      [ALLOW: Guns] (разрешить: оружие)
  ├─R2D2                     [ALLOW: Engines] (разрешить: машинный зал)
  └─C3PO

Многоуровневые группы

Группы могут быть расширены на любой уровень в ARO-дереве. Например, мы могли бы добавить группу “Джедаи” к “Пассажирам”. Большинство пассажиров было бы категоризировано при “Пассажирах”, но Люк и Оби-Ван будут выделены в “Джедаи” и поэтому могут иметь некоторые привелегии (например, доступ в кабину):

Пассажиры Сокола тысячилетия
├─Команда                    [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ └─Чуви                     [DENY: Engines] (запретить: машинный зал)
└─Пассажиры                  [ALLOW: Lounge] (разрешить: пассажирский зал)
  ├─Джедаи                   [ALLOW: Cockpit] (разрешить: кабину)
  │ ├─Оби-Ван
  │ └─Люк                    [ALLOW: Guns] (разрешить: оружие) 
  ├─R2D2                     [ALLOW: Engines] (разрешить: машинное отделение)
  └─C3PO

Как phpGACL определяет разрешения?

Когда компьютер судна (с запущенным phpGACL конечно же) проверяет доступ, единственный вопрос, который он может задать себе - “имеет ли человек X доступ к месту Y?” На языке phpGACL это будет выглядеть так: “Может ли ARO X иметь доступ к ACO Y?”

phpGACL определяет, имеет ли определенный человек доступ к определенному месту следуя от вершины дерева к указанному человеку, отмечая по пути явный контроль доступа для того места. Когда мы достигаем нужного человека, получаем конкретный уровень доступа который и является конечным результатом для возвращения. Таким образом мы осуществляем контроль доступа для групп, людей, но имеем возможность расширять эти уровни если нужно.

Пример 1. Мы спрашиваем: Имеет ли Люк доступ к пассажирскому залу?”

  • устанавливаем правило по умолчанию DENY (запретить);
  • двигаемся по дереву к Люку: Пассажиры Сокола тысячелетия -> Пассажиры -> Джедаи -> Люк;
  • начинаем с вершины дерева и двигаемся к Люку: “узел” “Пассажиры Сокола тысячелетия” не говорит ни ничего не об одном из ACO. Оставляем DENY (запрет);
  • идем дальше, “Пассажиры” - явно говорит, что “Пассажиры” имеют доступ к “пассажирскому залу”. Меняем внутренний результат на “ALLOW” (разрешить);
  • двигаемся в узел “Джедаи” - он не упоминает “пассажирского зала” вообще – оставляем предыдущий результат;
  • двигаемся в узел “Люк” - снова нет ничего о “пассажирском зале” - оставляем предыдущий результат;
  • идти дальше некуда – останавливаемся. Возвращаемый результат будет “ALLOW” (разрешить).

Пример 2. Мы спрашиваем: “Имеет ли Чуви доступ в машинный зал?”

  • устанавливаем правило по умолчанию DENY (запретить);
  • двигаемся по дереву к Чуви: Пассажиры Сокола тысячелетия -> Команда -> Чуви;
  • начинаем с вершины дерева и двигаемся к Чуви: “узел” “Пассажиры Сокола тысячелетия” не говорит ни ничего не об одном из ACO. Оставляем DENY (запрет);
  • идем дальше в узел “Команда” - явно говорит нам - “Команда” имеет доступ к “машинному залу” - меняем внутренний результат на “ALLOW” (разрешить);
  • двигаемся в узел “Чуви” - есть явное запрещение, которое говорит нам, что доступ в “машинный зал” закрыт. Меняем внутренний результат на “DENY” (запретить);
  • идти дальше некуда, возвращаем результат “DENY” (запретить).

Вы можете видеть, что если группа явно не определяет никаких прав, то она наследует их от родительской группы (предыдущей).Если узел корня (“Пассажиры Сокола тысячелетия”) не определяет никаких разрешений, то он наследует разрешения по умолчанию. В данном случае “DENY ALL” (запретить все).

Это подразумевает несколько интересных моментов об ARO-дереве:

  • ARO-дерево всегда показывает полный список AROs. Поэтому не имеет смысл спрашивать, имеет ли доступ к кабине Джабба, поскольку он не был определен в системе. Однако, phpGACL не проверяет существует ли ARO или ACO перед выполнением проверки. Так, если бы такой вопрос действительно имел бы место, ответ тыл бы “DENY” (запретить);
  • ARO-дерево может не показывать некоторые определения ACO если они специально не определены в ARO-дереве. Например, Хан определил ACO “ванная комната”. Любой вопрос, подобный этому: “имеет ли Люк доступ в ванную комнату?”, даст отрицательный ответ “DENY” (запрещено), поскольку нигде в ARO-дереве “ванная комната” не упоминается. Имейте ввиду, при исследовании ARO-дерева, некоторые ACO могут быть не видимыми.

Обратите внимание: при выяснении через phpGACL вопросов о доступе невозможно использовать Группы как AROs (даже при том, что это может казаться правильным). Например, невозможно ответить на вопрос “Какие пассажиры имеют доступ к оружию?” Полный ответ не Boolean (не Булевый) – DENY / ALLOW (отрицаю / позволяю), а более сложный “Люк и Оби-Ван могут, а R2D2 и С3PO нет”. PhpGACL не предназначен для такого вида ответов.

Добавление групп

Хан видит, что ACL начинает выглядеть очень сложно. Есть очень много исключений. Возможно, следует сделать подругому, например, создать новую группу “Инженеры”, которая будет содержать людей, которым позволен доступ в машинное отделение и оружию. Эта группа должна содержать Хана и R2D2, так как они могут восстановить двигатель и оружие. Это позволяет Хану удалить некоторые из “грязных” правил-исключений, это дает определенную выгоду в том, что дает ясное описание:

По умолчанию:                [DENY: ALL] (запретить: все)
Пассажиры Сокола тысячилетия
├─Команда                    [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ └─Чуви                     [DENY: Engines] (запретить: машинный зал)
├─Пассажиры                  [ALLOW: Lounge] (разрешить: пассажирский зал)
│ ├─Джедаи                   [ALLOW: Cockpit] (разрешить: кабину)
│ │ ├─Оби-Ван
│ │ └─Люк                    [ALLOW: Guns] (разрешить: оружие) 
│ ├─R2D2
│ └─C3PO
└─Инженеры                   [ALLOW: Engines, Guns] (разрешить: маш.зал, оружие)
  ├─Хан
  └─R2D2

Вы можете прочитать это следующим образом: по умолчанию никто никуда не имеет право заходить, команда имеет доступ всюду (кроме Чуви, он не имеет доступа в машинное отделение). Пассажиры имеют доступ только к пассажирскому залу, кроме Джедаев, они имеют доступ к кабине. Люк также имеет доступ к оружию. Инженеры имеют доступ к оружию и машинному отделению.

Наиболее важно, мы можем видеть, что Хан и R2D2 находятся теперь в двух местах ACL. Им нет необходимости быть уникальными. Это делает политики более ясными для читателя: “Ах, Хан и R2D2 имеют доступ к оружию и машинному залу, потому что они инженеры”.

Добавление людей

Хан идет в Город облаков, чтобы повидать Ландо и сделать некоторый ремонт. Ландо – предыдущий хозяин Сокола тысячелетия, Хан предполагает отнести его к группе “Команда”. Ландо так же предлагает услуги своего главного инженера Хонтука, для помощи в восстановлении судна, пока оно находится в доке.

По умолчанию:                 [DENY: ALL] (запретить: все)
Пассажиры Сокола тысячелетия
├─Команда                     [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ ├─Чуви                      [DENY: Engines] (запретить: машинный зал)
│ └─Ландо
├─Пассажиры                   [ALLOW: Lounge] (разрешить: пассажирский зал)
│ ├─Джедаи                    [ALLOW: Cockpit] (разрешить: кабина)
│ │ ├─Оби-Ван
│ │ └─Люк                     [ALLOW: Guns] (разрешить: оружие) 
│ ├─R2D2
│ └─C3PO
└─Инженеры                    [ALLOW: Engines, Guns] (разрешить: маш.зал, оружие)
  ├─Хан
  ├─R2D2
  └─Хантук

Это дерево показывает как легко можно предоставлять доступ новым людям. Если бы мы использовали первоначальную матричную схему, мы должны были бы установить разрешения для каждого места на корабле и для Ханука и для Ландо. Вместо этого, мы просто добавляем их к нужным группам, в результате чего, их доступ неявно и легко определен.

Устранение конфликтов

Что случится, если мы добавим Чуви к списку инженеров?

По умолчанию:                 [DENY: ALL] (запретить: все)
Пассажиры Сокола тысячелетия
├─Команда                     [ALLOW: ALL] (разрешить: все)
│ ├─Хан
│ ├─Чуви                      [DENY: Engines] (запретить: машинный зал)
│ └─Ландо
├─Пассажиры                   [ALLOW: Lounge] (разрешить: пассажирский зал)
│ ├─Джедаи                    [ALLOW: Cockpit] (разрешить: кабина)
│ │ ├─Оби-Ван
│ │ └─Люк                     [ALLOW: Guns] (разрешить: оружие) 
│ ├─R2D2
│ └─C3PO
└─Инженеры                    [ALLOW: Engines, Guns] (разрешить: маш.зал, оружие)
  ├─Хан
  ├─R2D2
  ├─Ханук
  └─Чуви

Это делает доступ Чуви к машинному залу неоднозначным, потому что теперь есть две дорожки от корня дерева до Чуви. Если компьютер последует по одной дорожке, то результат будет “DENY” (запрещаю), а если он последует за другой дорожкой, то результат будет “ALLOW” (разрешаю). Так все же, ему запрещен или разрешен доступ?

PhpGACL предупредит вас, если вы сгруппируете ARO таким образом, что доступ ARO к произвольному ACO будет неоднозначным. Но решать конфликт придется вам.

Если при такой неоднозначности спросить компьютер “Имеет ли Чуви доступ к машинному залу?”, то ответ будет “ALLOW” (разрешаю), потому что система вернет вам последний измененный параметр (это политика phpGACL). В этом случае результат “ALLOW”, потому что разрешение “ALLOW: Engines, Guns” (разрешить: машинный зал, оружие), назначенное на группу “Инженеры”, является более свежим, по сравнению с “DENY: Engines” (запретить: машинный зал) группы “Чуви”.

В случае, если ACL содержит неоднозначности, такую ACL называют непоследовательной. Непоследовательная ACL может быть очень опасна, вы можете невольно обеспечить доступ несоответствующим людям. Когда phpGACL предупреждает вас о том, что ACL непоследовательна, разрешите конфликт как можно более оперативно, чтобы сразу восстановить последовательность.

Чтобы решить конфликт, вы могли сделать следующее:

  • удалить дерективу “DENY: Engines” у группы “Чуви” в группе “Команда”;
  • добавить дерективу “DENY: Engines” у группы “Чуви” в группе “Инженеры”;
  • удалить Чуви из группы “Инженеры”, так как Хан всеравно не считает его достойным инженером.

Ха выбрал последний вариант, он удалил Чуви из группы “Инженеры”.

Обозначение объектов доступа

phpGACL уникально идентифицирует каждый объект доступа (ARO, AXO, ACO) комбинацией их двух ключевых слов и их типа объекта доступа.

Кортеж “(Тип объекта доступа, Раздел, Значение)” уникально идентифицирует любой объект доступа.

Первый элемент кортежа – тип объекта доступа (ARO, AXO, ACO).

Второй элемент кортежа, названный Разделом, является, заданной пользователем, строкой, которая называет общую категорию объекта доступа. Объекты с несколькими доступами могут иметь тоже самое название Раздела. Название раздела должно быть короткое, но описательное (наглядное). Это используется в пользовательском интерфейсе в списках выбора, поэтому логично не делать их слишком длинными.

Разделы сохраняются в ячейке namespace. Разделы не имеют никакого отношения к группам или ARO / AXO-деревьям – они просто механизм, для того чтобы помочь поддерживать большее количество объектов доступа.

Третий элемент кортежа – определенное пользователем, название для объекта доступа, и называется Значение. Значение не может содержать пробелы (однако, Раздел – может).

Названия Разделов и Значение регистрозависимы.

Примечание: обычно спрашивают, почему для идентификации объектов доступа используются строки а не целые числа, которые, якобы, кажутся быстрее. Ответ – для ясности. На много более легко понять:

acl_check('system', 'login', 'user', 'john_doe'); чем:

acl_check(10, 21004, 15, 20304);

Так как из контекста часто очевидно к какому типу объекта доступа мы обращаемся, интерфейс для phpGACL (и эта документация) опускает тип объекта доступа и использует формат “Раздел -> Значение” для показа объекта доступа. Однако, программный интерфейс приложения требует “Раздел” объекта доступа и “Значение”, которые должны быть определены в отдельных аргументах функции (тип объекта доступа обычно неявен в описании аргумента).

Пример ACO “Раздел -> Значение”:

  • “Floors -> 1st” (“Этажи -> 1-й”);
  • “Floors -> 2nd” (“Этажи -> 2-й”);
  • “Rooms -> Engines” (“Комнаты -> машинный_зал”).

Пример ARO “Раздел -> Значение”:

  • “People -> John_Smith” (“Люди -> Джон_Смит”);
  • “People -> Cathy_Jones” (“Люди -> Кэти_Джонс”);
  • “Hosts -> sandbox.something.com” (“Хосты -> sandbox.something.com”).

Пример использования API:

  • acl_check(aro_section, aro_value, aco_section, aco_value);
  • acl_check('People', 'John_Smith', 'Floor', '2nd');

Примеры правильного именования:

  • “ACO - Frob > Flerg”, “ARO – Frob -> Flerg” (Раздел и Значение являются одинаковыми, но это хорошо, поскольку namespaces разные для разных типов объектов доступа);
  • “ACO – Frob -> Flerg”, “ACO – Frob -> Queegle” (тип объекта доступа и название Раздела одинаковые, это хорошо, поскольку у них разные Значения);
  • “AXO – Frob Hrung -> Flerg” (Разделы могут содержать пробелы).

Пример неправильного именования:

  • “ACO – Frob -> Flerg”, “ACO – Frob -> Flerg” (тип объекта доступа – Раздел -> Значение должны быть уникальными);
  • “ACO – Frob -> Flerg Habit” (Значение не может содержать пробелов).

Добавление разделов

Прежде чем добавить новый объект доступа вы должны создать Раздел. Для добавления нового раздела используйте функцию add_object_section().
add_object_section(
    string NAME,        Короткое описание того, для чего этот Раздел 
                        используется (пример, 'Levels in building'
                        (“Этажи в здании”)).
    string VALUE,       Название Раздела (пример, 'Floors' (“Этажи”)).
    int ORDER,          Ценность раздела, влияет на то, каким по счету в списке
                        в интерфейсе пользователя появится этот Раздел.
    bool HIDDEN         Показывает, должен ли этот раздел быть виден в
                        пользовательском интерфейсе.
    string GROUP_TYPE); Тип объекта доступа (ACO, ARO, AXO).

Хан создал 3 раздела для AROs “Люди”, “Чужие” и “Андроиды” Давайте посмотрим, как будет выглядеть AROs с их полными названиями:

Пассажиры Сокола тысячелетия
├─Команда                    [ALLOW: ALL]
│ ├─"Люди > Хан"
│ ├─"Чужие > Чуви"           [DENY: Engines]
│ └─"Люди > Ландо"
├─Пассажиры                  [ALLOW: Lounge]
│ ├─Джедаи                   [ALLOW: Cockpit]
│ │ ├─"Люди > Оби-Ван"
│ │ └─"Люди > Люк"           [ALLOW: Guns] 
│ ├─"Андроиды > R2D2"
│ └─"Андроиды > C3PO"
└─Инженеры                   [ALLOW: Engines, Guns]
  ├─"Люди > Хан"
  ├─"Андроиды > R2D2"
  └─"Чужие > Хантук"

Разделы – только способ категоризировать объекты доступа, сделать пользовательский интерфейс более пригодным к использованию, а так же сделать функцию acl_check() более читаемой. Они не затрагивают путь phpGACL для определения прав объекта доступа. Также, разделы не могут быть вложенными, то есть вы не можете создать “Мужчин” внутри “Людей”. Вы должны будете создать раздел “Люди мужского пола” или подобный.

Множество целей

Возможно, вы будете использовать phpGACL сразу для нескольких целей, например, ограничить пользовательский доступ к страницам, а так же удаленный доступ к страницам вашего сервера. Эти две задачи не связаны.

PhpGACL предлагает реализовать это тремя различными путями:

  • вы можете использовать дополнительные базы данных для хранения этих данных;
  • вы можете использовать туже самую базу данных но с другими именами таблиц (всеже, эта возможность пока не поддерживается);
  • вы можете хранить объекты доступа в тех же самых таблицах, и очень тщательно управлять ими, так, чтобы они не вступали в противоречия.

Чтобы выбрать первый или второй способ (когда он будет доступен) используйте массив $gacl_options, когда создаете новый класс phpGACL. Это позволит вам определить базу и префикс для таблиц:
$gacl_options = array(
    'db_table_prefix' => 'gacl_',
    'db_type' => 'mysql',
    'db_host' => 'host1',
    'db_user' => 'user',
    'db_password' => 'passwd',
    'db_name' => 'gacl');

$gacl_host1 = new gacl($gacl_options);

Будьте очень осторожны, если вы выберите третий вариант, поскольку phpGACL ничего не знает об отношениях между вашими задачами, а, следовательно, не сможет контролировать различные бессмысленные директивы политики доступа.

Например, Хан может решить запретить доступ кораблям, которые вступают в контакт с его компьютером, к каким-либо комнатам на корабле. Для этого он может создать “Корабль Люка” как внешний корабль ARO в этом же самом ARO-дереве. В результате чего, станет возможным создать APD (правило автоматической обработки): “Корабли -> Корабль Люка” и назначить ему права “ALLOW (разрешить): “Комнаты -> пассажирский зал”. Согласитесь, это полностью бессмысленно! Чтобы помочь в разрешении таких противоречий, давайте правильное (информативное) название Разделам, чтобы они четко давали понять, чем на самом деле является объект доступа и для каких задач используются. Это должно быть очевидно для любого администратора, что бессмысленно назначать разрешения кораблям для доступа в какую-либо комнату.

Объекты расширения доступа (AXO)

Объекты расширения доступа могут добавлять третье измерение к разрешениям, которые могут формироваться в phpGACL. Мы с вами видели, как phpGACL позволяет комбинировать объекты ARO и ACO (2 измерения) для создания политики доступа. Это очень хорошо для простых разрешений, например:

Люк (ARO) просит разрешение на доступ к Оружию (ACO).

Если это все, чего вы хотите, прекрасно – AXO является полностью дополнительным (то есть может отсутствовать).

Но, поскольку все ACOs являются равными, это мешает справляться с ними если их очень много. Если это так, то мы можем изменить способ, которым мы смотрим на объекты доступа, для более легкого управления ими.

AXOs идентичны AROs во многих отношениях. Есть дерево AXO (отдельное от дерева ARO), со своими собственными Группами и AXOs. Когда вы будете иметь дело с AXO, думайте, что AXO берет на себя старую роль ACO (то есть является “вещами, к которым требуется доступ”), и изменяют представление ACOs с “вещи, к которым требуется доступ” на “действие, которое требуется совершить”.

Посмотрим только на ARO и ACO:

  • ARO – вещи, просящие доступ;
  • ACO – вещи, к которым нужен доступ.

Посмотрим на ARO, ACO и AXO:

  • ARO – вещи, которые просят доступ;
  • ACO – действия, которые требуется совершить
  • AXO – вещи, к которым нужен доступ.

Пример:

Менеджер сайта пытается управлять доступом к проектам на вебсайте. Дерево ARO состоит из всех пользователей:

Вебсайт
├─Администраторы
│ ├─Элис
│ └─Кэрол
└─Пользователи
  ├─Боб
  └─Алан

Проекты организованы по принципу используемой операционной системы в Группах AXO:

Проекты
├─Linux
│ ├─SpamFilter2
│ └─AutoLinusWorshipper
└─Windows
  ├─PaperclipKiller
  └─PopupStopper

Действия, которы могут выполняться над каждым объектом - “Просмотр” и “Редактирование” - это ACOs.

И так, мы хотим, чтобы Боб имел возможность просмотра всех проектов Linux. Это возможно при помощи добавления ADP (автоматической обработки) связки ARO Боба с ACO “Просмотр” и AXO Linux. Теперь мы можем задать вопрос:

“Боб” (ARO) просит действия “Просмотр” (ACO) для проекта “Linux” (AXO).

Обращаю ваше внимание на то, что AXO является дополнительным, если вы не определите AXO, вызывая acl_check(), то ADP (автоматическая обработка) осуществится без учета AXO. Однако, если APD (автоматическая обработка) существует только с AXO, а вы вызовите acl_check() без указания AXO, вы потерпите неудачу.

И так, если вы при вызове acl_check() определяете AXO, то эта функция будет искать в ACL только содержание AXO. Если никакой AXO не определен, только будет рассмотрены только ACL. Это (в теории) дает нам небольшое увеличение скорости работы.

Инсталляция

Базовая настройка

  1. Распакуйте архив дистрибутива в корень или нужную вам папку сайта. Возможно, вы захотите переименовать ее во что-то более подходящее.
  2. Редактируйте файл phpgacl/gacl.class.php. Установите db_type, db_host, db_user, db_password и db_name, которые вы предполагаете использовать.

    Теперь необходимо отредактировать phpgacl/admin/gacl_admin.inc.php, внеся в него туже самую информацию. Этот файл нужен для административной части скрипта.

    Причина, почему необходимо дважды ввести одинаковые данные, заключается в том, что класс gacl.calss.php намного меньше по объему, чем весь программный интерфейс. И нет необходимости тянуть за собой все скрипты, если вам всего лишь необходимо вызвать функцию acl_check().

  3. Создайте базу данных с именем, которое вы задали в db_name.
  4. Запустите http://your_site.net/phpgacl/setup.php. Этот скрипт создаст необходимые таблицы. Не бойтесь сложной информации, скрипт покажет вам только сообщения об успешной работе.
  5. Следуя замечению на картинке (Перевод замечания на картинке: Пожалуйста, создайте директорию /admin/templates_c и установите на нее права доступа. Нажмите “Go here!” для начала работы), создайте необходимую директорию. Права доступа должны позволять запись в эту директорию.
  6. Нажмите на “Go here!” и вы перенесетесь сюда: http://your_site.net/admin/acl_admin.php.

Дополнительные настройки

Если вы уже используете ADOdb в своих скриптах, то вы можете указать phpGACL использовать уже имеющуюся установку:

  1. Отредактируйте phpgacl/gacl.class.php переменную ADODB_DIR, указав в ней путь к установленной библиотеке.
  2. Переименуйте директорию phpgacl/adodb в какую-нибудь другую, например adodb_x и перезапустите phpgacl/admin/acl_admin.php для просмотра изменений.
  3. Удалите директорию adodb, установленную вместе с phpGACL.

Если вы уже используете Smarty в своих скриптах, то вы можете указать phpGACL использовать уже имеющуюся установку:

  1. Отредактируйте скрипт phpgacl/admin/gacl_admin.inc.php, установив нем следующие переменные: $smarty_dir и $smarty_compile_dir, чтобы они указывали на каталог, где у вас уже установлен Smarty. Переместите директорию с шаблонами phpGACL в другую директорию (например в ту, которую вы на данный момент уже используете).
  2. Переименуйте папку phpgacl/smarty в другую, например smarty_x. И перезапустите phpgacl/admin/acl_admin.php для просмотра изменений.
  3. Удалите папку smarty инсталляции phpGACL.

Как можно переместить phpGACL файлы из моего дерева вебсайта с сохранением связей с деревом для администрирования?

  1. Перейдите в корень вашего вебсайта.
  2. Переместите директорию phpGACL в необходимую вам директорию и создайте симлинк (ссылку) на административную директорию там, где вы хотите его запускать.
    mv phpgacl/ /www/includes_directory
    ln -s /www/includes_directory/phpgacl/admin/ gacl
    
  3. Теперь попробуйте запустить phpgacl. Если он не стал работать, то, возможно, ваш сайт не поддерживает симлинки( ссылки).

Использование phpGACL в вашей программе

Базовое использование

Этот пример показывает основной способ использования phpGACL в вашей программе. Он так же показывает использование абстрактного класса для доступа к базам данных ADOdb. Пример иллюстрирует простой способ проверки логина и пароля.
// Подключаем базовый API
include('phpgacl/gacl.class.php');
$gacl = new gacl();
$username = $db->quote($_POST['username']);
$password = $db->quote(md5($_POST['password']));
$sql = 'SELECT name FROM users WHERE name=';
$sql .= $username.' AND password='.$password;
$row = $db->GetRow($sql);
if($gacl->acl_check('system','login','user',$row['name'])){
  $_SESSION['username'] = $row['name'];
  return true;
}
else
  return false;

Вы видете только один вызов acl_check(), что же он означает?

  • Проверяет ARO-объект $row['name'] в ARO-разделе 'user'.
  • А также ACO-объект 'login' в ACO-разделе 'system'.

Профессиональное использование

Использование административного интерфейса

Отсутствует в оригинальной документации

Описание API

ACL

add_acl()

Добавляет права доступа в лист контроля.

add_acl(
    array ACO Ids,
    array ARO_IDs,
    array ARO_GROUP_IDs,
    array AXO_IDs,
    array AXO_GROUP_IDs,
    bool ALLOW,
    bool ENABLED
    [, int ACL_ID])

Возвращает:

int ACL_ID, если все прошло хорошо и FALSE, в случае неудачи.


edit_acl()

Редактирует права доступа, которые уже установлены в листе контроля.

edit_acl (
    array ACO IDs,
    array ARO_IDs,
    array ARO_GROUP_IDs,
    array AXO_IDs,
    array AXO_GROUP_IDs,
    bool ALLOW,
    bool ENABLED
    [, int ACL_ID] )

Возвращает:

int ACL_ID если все прошло хорошо и FALSE, в случае неудачи.

del_acl()

Удаляет права доступа, которые уже заданы в листе контроля.

del_acl (
    int ACL ID)

Возвращает:

TRUE если все прошло успешно, FALSE, в случае неудачи.

Groups (Группы)

get_group_id()

Возвращает ID группы.

get_group_id (
    string GROUP NAME) 	Тип объекта доступа (ARO, ACO, AXO)

Возвращает:

int GROUP_ID в случае успеха, FALSE, если неудача.

get_group_parent_id()

Возвращает ID непосредственного родителя группы.

get_group_parent_id (
    int GROUP_ID)

Возвращает:

int GROUP_PARENT_ID если удача, FALSE – если неудача.

add_group()

Добавляет группу в дерево групп.

add_group (
    string NAME
    [, int GROUP_PARENT_ID]
    [, string OBJECT_TYPE])

Возвращает:

int GROUP_ID если удача, FALSE если неудача.

get_group_objects()

Возвращает вписок всех объектов отнесенных к группе.

get_group_aro (
    int GROUP_ID,
    string GROUP_TYPE)

Возвращает:

array SECTION_VALUE, VALUE если удача, FALSE если неудача.

add_group_object()

Соотносит ARO с группой.

add_group_aro (
    int GROUP_ID,
    string OBJECT_SECTION_VALUE,
    string OBJECT_VALUE,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int TRUE если удача, FALSE если неудача.

del_group_object()

Удаляет ARO из группы.

del_group_aro (
    int GROUP_ID,
    string OBJECT_SECTION_VALUE,
    string OBJECT_VALUE,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int TRUE если удача, FALSE если неудача.

edit_group()

Редактирует группы

edit_group (
    int GROUP_ID,
    string NAME,
    int GROUP_PARENT_ID,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int TRUE если удача, FALSE если неудача.

del_group()

Удаляет группу, перераспределяет детей (если есть) или удаляет их.

del_group (
    int GROUP_ID,
    bool REPARENT_CHILDREN,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int TRUE если удача, FALSE если неудача.

Access Objects (ARO/ACO/AXO)

Этот раздел API управляет объектами доступа, такими как ACO, ARO, AXO.

get_object()

Возвращает массив информации обо всех объектах указанного типа.

get_object (
    [string SECTION_VALUE], Необязательный, название Раздела (как фильтр)
    bool RETURN_HIDDEN,	    Возвращать ли объекты с типом “скрытый”
    string GROUP_TYPE) 	    Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

array OBJECT_ID если удача, FALSE если неудача.

get_object_data()

Возвращает массив информации об объекте доступа с конкретным ID.

get_object_data (
    int OBJECT_ID,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

array (section_value, value, order_value, name) если удача, FALSE есди неудача.

get_object_id()

Возвращает ID объекта.

get_object_id (
    string OBJECT_SECTION_VALUE,
    string OBJECT_VALUE,
    string GROUP_TYPE) Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int OBJECT_ID есди удача, FALSE если неудача.

get_object_section_value() Возвращает ID раздела к которому приписан объект.

get_object_section_value (
    int OBJECT_ID,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int SECTION_VALUE если удача, FALSE если неудача.

add_object()

Добавляет объект.

add_object (
    string SECTION_VALUE,
    string NAME,
    string VALUE,
    int ORDER,
    bool HIDDEN,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

array OBJECT_ID если удача, FALSE если неудача.

edit_object()

Редактирование объекта.

edit_object (
    string SECTION_VALUE,
    string NAME,
    string VALUE,
    int ORDER,
    bool HIDDEN,
    string GROUP_TYPE) 	Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

array OBJECT_ID если удача, FALSE если неудача.

del_object()

Удаляет объект.

del_object (
    int OBJECT_ID,
    string GROUP_TYPE, 	Тип объекта доступа (ARO, AXO, ACO)
    bool ERASE)

Возвращает:

int TRUE если удача, FALSE если неудача.

Access Object Sections (Разделы объектов доступа)

Этот раздел API позволяет управлять Разделами, которые являются частью уникального названия объекта доступа (см. “Разделы” для дополнительной информации).

get_object_section_section_id()

Возвращает ID существующего Раздела. Вы должны указать название Раздела или его значение, или оба параметра.

Если вы определите только название, то возможны неоднозначные результаты (так как название не обязано быть уникальным). В этом случае вы получите сообщение об ошибке.

get_object_section_section_id (
    string NAME,        Короткое описание того, для чего предназначин Раздел 
                        (например, Этажи в здании”)
    string VALUE,       Название Раздела (например, “Этаж”)
    string GROUP_TYPE)  Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int SECTION_ID если удача, FALSE если неудача.

add_object_section()

Добавляет новый Раздел объектов.

add_object_section(
    string NAME,        Короткое описание того, для чего предназначин Раздел 
                        (например, Этажи в здании”)
    string VALUE,       Название Раздела (например, “Этаж”)
    int ORDER,          Ценность Раздела. Учитывается в пользовательском интерфейсе при выводе списка выбора.
    bool HIDDEN,        TRUE – Раздел будет скрыт из списка в пользовательском интерфейсе.
    string GROUP_TYPE)  Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

int SECTION_ID если удачно, FALSE если неудачно.

edit_object_section()

Изменяет атрибуты Раздела. Функция не может изменить тип объекта доступа (для дополнительной информации смотрите add_object_section).

edit_object_section (
    int OBJECT_SECTION_ID,  ID раздела (вы можете получить его при помощи
                            функции get_object_section_section_id).
    string NAME,            Новое название Раздела.
    string VALUE,           Новое значение Раздела.
    int ORDER,              Новая ценность раздела.
    bool HIDDEN,            Новый статус раздела (скрыт или нет)
    string GROUP_TYPE)      Тип объекта доступа (ARO, AXO, ACO)

Возвращает:

TRUE если удача, FALSE если неудача.

del_object_section()

Удаляет Раздел. Все объекты Раздела также будут удалены!!!

del_object_section (
    int SECTION_ID,	ID Раздела
    string GROUP_TYPE,	Тип объекта доступа (ARO, AXO, ACO)
    bool ERASE)	Если TRUE - все объекты Раздела будут удалены.
                Если FALSE, то вы получите сообщение об ошибке в случае,
                если Раздел содержит объекты, Раздел удален не будет,
                если Раздел не содержит объектов, он будет удален.

Возвращает:

TRUE если удача, FALSE есди неудача.


phpgacl-3.3.7/docs/phpgacl-db-schema.png0100644025754300001440000027344710041540457016767 0ustar ipsousersPNG  IHDR/sRGBgAMA a cHRMz&u0`:pQ<IDATx^{fWuʿg E肺@$CIL1HgXHD !m'8VLp%j;J%&(r.1GMM&Bꚩ LMM |ʋ}n{ꭷ<>k^]ϙ>S$$%I@$I@$I@]]7z駏9r`zK^/x.I@$I@$I@vR^x!軿II '!:u E袋?]{tI$ H$ H$1W]uK_C} _xgAѣGg{_{$I@$I@$I@>[*CaN筷O$I@$I@$I@)}{37#k~G~S%w$I@$I@$I@H?+;+akw~wK$I@$I@$I`$ |_|ކyqy͛og~gK$I@$I@$I` /ӧOgqC?? {]$ Hەo &Ѷj' Hp _WgH!a^tI$ *wӟ408w|Aږ vIƯOOpCL.Aو +_c# DaCJJ$I oxñcViݯ}kG]$ HK܅?? r❻;HB?'GI DA _ųt0H0"$Cv ͮoTRmQcKo^$I`t _nof7ۿw K$If `)\}K?W|c "O4+;8 H~M|M j,jc-qa'N@B`}% W"Hq*m: H$0O8)w0w3JZYf(HAHV?yAWbGaAu>DD^L?Gw/ǥ"%a ;i"mH%I@p@qC8%?8% H$P`yi~a~_`#bt^L 1~B('@L@ȏi)jaD@P g_Yԝb ZW aP# )Ї/"~+(jcMqxe MqRўx[֬o[fc}OSJ$ ,|+oz{¡Ì.I@$$1 u~e?ao}į''Dc--e .p]wY'?|}j  󶷽 Z]}FD`ϔ ӇMVH3ep\L%(RoJx@"k_jy%xLI$I`~ `&C.8W.I@$$}`ǃp`?a3aįO~~‘hOέj>!D$Jk%ԂYdHa7Bl3֖D~g~uA}2EI,P((,fBQ:H-ʦoIWI@$%pUWa,xᰋ &_uI$ '709KKsc?"K_O =yD\LĮ{ȁ>e]_Ud} fe`j+gˉ"$g/$Z YGD\" l)c=<#&˼L\LbDY'4E%I@|!a .3;K$I> kHkb~__9GG ,aC} %}rAfe`P8͸$RS o1_g)(\LP2D2H O).˨0_fmrrc:V*ByRfIW曨o}m_%$I@8+{ w1$I@Ri‘?c?S?S84̊˾b|#_g{p@\x?!RFH ,QP +K炟+NYOA0A!q _Qa(DdX|ei)^hLLC<"5V?/'+sS(W+gU*$ H.O|5IGs]$ H@ -hX / H$ HI[` 629]$ HUJM{Wߢ~~o,$I@$ %pWsS^/| .I@$Z%ٻ?}c)$ H$ T(Yuȑ % H$ H$ H$Pk9ì_K$ H$ H$ H@8DJ|\.I@$I@$I@$Z%~wN^&]$ H$ H$ HK-oy˩Sy_WuI$ H$ H$ T,w]=9?]$ H$ H$ HKCЧ?s?}k.I@$I@$I@$%OO3?3ؑ_tI$ H$ H$ T++x8K$I@$I@$IZ ;v gz }@*$ H$ H$ HߑNA$I@$I@$H@GJ$ H$ H$ S$I@$I@$I`5­Q$ H$ H$ $I@$I@$I@XpyT*$ H$ H$ HB8I@$I@$I@V#!j * H$ H$ HNm@$I@$I@$H@GJ$ H$ H$ S$I@$I@$I`5­Q$ H$ H$ $I@$I@$I@XpyT*$ H$ H$ HB8I@$I@$I@V#!j * H$ H$ HNm@$I@$I@$H@GJ$ H$ H$ S$I@$I@$I`5­Q$ H$ H$ $I@$I@$I@XpyT*$ H$ H$ HB8I@$I@$I@V#w#$I@r?_riwI`Q^m;j蒀$ @֐pO>% H$}N:U`߿;%%UJm-ۄwjuV%I@(@֐pH$I@r'%%UJmN*$ HEB"*$ Hj6G \!ZV% U$I`t d !2W$j^!ܮ7n'*- H%B*$ H d5Ng% *K@L% H@WֳCW%ILYCBW&XŒ$I@6 Tv[B~$ ,!nǢ<n%I`=­a$ d5n%ORSB1$I`BM?UN@V x,sa ~^#!![TI%I`%j^!J9pcJSiI%5$p~$ ,!-X-$ GYCBJJ$ռBpUi9 #J'+*0…p+ H. (,m\.cqURKK@7N/$0p#u%%mG̛?8oQYF7=ߖ iɳP@zpx@ <-O"^|B}OoyhG s b!0aR,>V^-pA f, LpCSeOeD^/nt$63;K"Yv} б "LbҔoQ4Q0kN:J4Eҁvlfs,K+@8k4i{ jFvpd}" D??0ł6`#0F|tG8LIYЪ Z"gg3"\_/t6/t;nBS{;_xt0o16!ξt b L{͑sjjJOi%1*4oڦ$&`ُe#p&oOpӽVGkt06pMњ(nfAD 8jmQ AxDlD8]\nD8hF.ftH*vIZir*#xw(LYfAUзE4f@^Tc|.u , m$Bi9nRENDB}U$P µiTfW-AbXt)™%6ȥtZN۹!j@u*՗Z7k5Zշ *P` *xnu,qv6ynu^Fp;&I`c 0ΖbA/%JR` Vd(dAYDl MmyvMTŧnnJ{p35aBa"PֳLpUmB}U$ JY!tnΖ\W.+"E4f*웨O'!\UZn{ps p!\,qv6S!\UngDU|: rsF7k%%U`/p/x .:/ 5Gq^aE2"!\f"KUIpS(U)XFu< GR z 5Gq^K/(% •$ JY!"9ny#ܷ+.ӧOWV9~!":c{V}~oeP`Y=-"/9;ČtI\s5}7eҗSTMtc%a_CFx65$;vСs]'3fH> {ڰ2 ޙøy #wU,++^uOu饗~ԫ.~{[|ev ̉pv6:M7MK 5W|3Y{'NxeP^Fu p!\(` TT@{%u rH oꨫz)Ȓ@w ̉p Q3=vȡCo!ųw=z{SQȄpYi F.>ҞK@|[{zʲꫯmBg?[霱`:82 <.rBjj3˂nl) !µ)&t44TUp,L[o}gqKXZ88pcg# p;Gxp7f<-B76nA b=bFE QvOJ'+؈!p# 3c=v7^y睇^D{Q/b,U[|a䅛=RF ;arBa$ :Xۙ\[npO>Pwm;q[n?Ze{[ +RX㎛|qM#@"D(B8!܂@t1 ;RR?>'Tel1OZ)'pTFm]^"QK[NFHs;WҶ3OV,G# @ $[Op#I!vЗ׸S6Fqz2Iq`4BM hmlĐ[2ER[ۙPxjkxb3 cQm)22 3p%ׄpSzզ '#|~;sq"ĄKSK9p* 0骼ѫN0n=m '[Dqgph1> ƙ?)^?r.q}y~#>{M`-OR0,/p8S!^8|Z$7jxjCUq™?|h 4T7ݕa.Vj3E^ . oCNFx nt^7|#5@N,:zbj'ՆpK=hqlѝi@8i\JWp4bl~w 25'2ć{2]Gz< FTݓ*F8t9`|x4" 9{ڑn@?h;L[`$p#xD&y&N4nNm3MI6v1bl8_X:BtE:,BFxZk3 z衙3!.н(h;U>aD<(@62UCo`J$70g!Ur:!\v3f'f[o}gȮBneZC"n+a[)^8i͟> Q{}بH۹pn|vԓ+x6M'? Q 'AWYF(ܳıc:tΑ+QӧOۇG.~[I FW#zRfu\AֈR)pY\!p^b /Ha p'O8.Ε͊>-ܲ\*ɓ'"$,6W*f#"\aE4f*ߪH(UpBtcYapdM^^#0f۩S%<“ڐHhxxw`t1R2ތ$=0,HY3%d!n=S!\e0Q,!;iH!n"-7gBID['#|-FTY/ͨ$t! @y\?{b's2 qgmHֈpƙ9!"*rLpsټpB^pY}U!@=PO[5"ǦyE8;x/ 4αNk8׍p;3iOd F|$%eUpC@q^)e'?S:#|MG¡,7I#&:һjm_^G.bHt܏{`#,m:B/p-E^'#` l[  *Ğ׭yW;p.spi腳\815n 8bB2`XB8!܈opBܜ ('#͜3|j/hoNVx ֘qCxxci!,vNho_ Fͨh,1X _?RK ŏ>y^۵[*ީ^׻޵P̟_>z <\17{׫ǎ;t9Gb=}} `k R$2i {Z\ ▸b,LΖD-/\ndO8=:]Q~N]йg cǎ:t#{W"ӧO۷o;[W\bF`|F6Lc/R<'O Y,ŝhq^[^^ px~OR%K&%Q p&kLv^`?s"ՀpFxN9|f q _Qt^:QK\BM^wK\ѷ3ߒpB_[dOgo7\`׋pp悈pՉpx l(g1 L? vUuZ n[⊁oѷ$j!neE/p2…p%=C8ҮǏ[sÜO[ yH#)Ъ*i"eUd%ЎUǂh;X,pcerUzoh\f{ un䅓n,;WԩUR3I-qXDlI3{S(`D.zR~ۮtߎ޾I>7z'RVpG?Ve[4N R1Gm_.|%x?ܬm++.䐈[!r!D='=c|ݕW~tד[>˵lB8C8M1kH,#e=>\k=7vR=P7C8NOo)د,>X|& !Gq$nx p56e4pp2D?m 1H nǹm㜻5kᰙdPn,iEs._A%aDJ۝wy$[Ww#xkp>_o+~U.eeK\$j!\1L^ns*p\^wn]vbNFALqGS.8gٱl3rpH4qxOrlvRl,spӎÉpLPt1x/ۅ)хc1_i?O"կw,. -[le %Q :.bX~ uL ,gk\VfEW꺞ֿZK\\## .^[pv6Žɞd)=n#: >ބpE4V{u)6 ̆>@8An-Iq]2Zc!\Z.`KcN0Y ~ Q!wacՌZO# vG|p נoQI=n1pYhWd+l-Z7jL7NwqYS*X% H\G}'NFv 72F8zX`29Ю`"DV v̽y56: C9W# pvC+v[!r!D-pu8ViM7;Q 3x A_ӸGF Rйg cǎ:t#{Wc*v h{L-ƣ92( 0 /}do`42Etnx"%hy\/p7?ͦ y8e_-qEؒpc!\gP iJJUO:; #>5w0M!|_,84|5o~mD8:s;½򕯼gUw˿˚;#Ua 8)TcDDzzQJ"˾G[le %Q FQPm:Fܿ.mkJM^;pE8ᓲFv5Sn^/O|ki6"-ύŀpQͯl0(߮㑶6wBL#;aFwq~|ɩxGJ+O5FY0!\ܺ-.#\W~пq][DJߋL867pE-˾[le %Q׀p\DP6^`BeVq2½+nHE[.YC>g&FGKyrY#BK._|0p{SO=5۷lXB8pu"n3*tp!f˭gV_)Ra.Gwu^8i\p#*zNB+h"h٤vz`}]w W] _rM׿VUMxWmUB8G ᠑df5—DGկk^|Řzuע5kgl;5!<}m^6\p+MZ'G ᠑dWe #;ChB^fn\pmoJwH.^cMZ'G p20ϖ.IAhjl5n\p:7ܞpByNe_` j{Ld%Y!<}m/i n:=Q p(|!n1k%#\WC\<g:d) k+E~CwH.ID-ͣpBŬm!\m@yp}--!<}m/yUߘpByNW^` WC\<By seez jx-Z'G pYۻ{o}{5\s{/'^' BD/7 [0 ԑm⋊ȋY"(aP|G 0]^ qBn\pٗ!R%D-ͣp]F5Fx`Y-OH.2.F2Ssy"mhTAe3ʈsǝ_bWꅃP̌X )|- aϟ' b~?xϸӲKq",Mϒ?YlDY ;(6A,|ep S#"ܓO>Yo)6}W.f6>Ƶ(h=] [nziqa>^ǁ((9.~\rEEH’Y6_HT9ˠ}Ƀ:u >|n3>xd h ^;q%\Rɧ*tx_CW"\ %>񚙱_揃=Ы kD8&(:1!F߽+;؋Y)دs1&v۾yUŤ=Ѡ!q-D/ ^B8XPxi*@c@cpLyB*f*L >˧tL<ӫƟL9pfCP{sa]&}U6H)!w$ du5: 3â/!".F@Y A8I˥Γdh)N)7i l(@.⩣m;,f6,؆pԱ6Tdp"MAŒ2gC6xkAf: |eL$d.,?2mGbfLhZӭבZpGMoHP0>6aec},p E|hL|+ Nɀm.D6hjKr:Rv*Yn${#N8al#; <>OD_nh8I` s#@`b `Mi:I(eެ &hY3L?N.;{:f%Y`\yvmvB8C[l :KLKçXd'F8^9+kn42DKCEδ:u8]L-EiE@%cV¶+Qɟ|Lۮ]^ G>AXW޲E,8q/wiF/zԸϼLi#6?JA$f5,/sXAw]|ن,wjYOŏ. 0ůL $CM 64+3dax_󱲆H@_X^-^MB-1_`R±fL3pN̔`fc 2/8m׸=w6-5`SRFu1cBb ՙ[ K# G­^+@8?f@8*[ tcDJ*X9fl8Ǿ0giV6}/ᨆ4گ/| 0m{|J[}F5L5DHnصC{NbQ)4y3"2w (0+Hq-u)h=C)@8 ?,o pr {5RAoN 4^8Zq~4ݤ@i6Rkm±F^vf;UuaoƄAx/gy֢ʉAx˟jCf~+:Q~!\Gxxcy}(@ iBpS %fǮ`±^aZ XF,wv~NݠdbL_ի^7[=IzކD`6R#\]/)}CO-h=}gcc8elM|U>`døgMXW5tKe.KF:W\CA : bXmu!OOAap)yRt` I|yJ8s.B8ױB  ~/W*XW]CkpY,CpY M!@/؍*=V,kU :+ie@oC{O`;}cv~@#DʙUPlzo'gw 8> SΆ,0…pY>/% g1bLd^z6d:X|UW=:pu&t';]  .C2-HYScUvk3apse.PqxTpipM4mGĎ&B 8H1 #\7XYgή}漠9,.dH_.:cCD1n\!ʴ 6Oms Խ@"Zqqpls{챃8!ܪ _` V*pA뙳k9/CMp#aB+>uRB64!ܜw7YXF;G= ;(0…pj@IٵϜWp '6}!zfX7y' hD86u9!zK^` ')sA뙳k9F @N7]ZyBIt,ٕu $S p(o=}fѻ$/#\뎄(h=krL \p [iҽK?Lwq_;5nٸ$i-I-nZ1q+9݆p_U$#%^`x{FhHl75rp.jf_}/㪙wwL{K'%Ckb<뮻{!KK/y^5]y/|៯˶oE]ϽuE7;S B>U$]rwC!-lTs!(ؠDꑀ.p3 lc*u?..磏6JJT>~1/~9^ oj;us\[s *ۑ#wm~nw#= .?OL F4i,CJO 'pm_[?{ad Z#2-ptbFWe^8$fC,B8zdeBO_ 3BAND نp7XQ/#9,p WGM F%Rpilh2*ԘH㎔y,J1A.3P69B'~%#\ :1yxc+E8?(pqU%s_N^l#j6ph8qzC%TB8q2'[oTԶҘsqژ9ᘈg3!/pZ 򣌂^c%"N~54E =ֆpk%^8rͺ-5@4"̘醨LY^IqB/! ՗mZxP,f-k!V"gtROO=yk+U</gp#8T2BٶfgBif/Ax)z}>`0tu\$um&RNJ2;oXk0퉿rۙxD l78CLZ[g\I3N^)0³ĬY>{hp3;g}eV ߎepXB8!<_dmFxg3! fWi@vp3X>+^'{ί:CH|~6s;""T 'I k+E&B=#/tEH^^5aQ C~alÅpx3p!4"nj" Odn"T#t]NAʉp< MEpD6Y!\VDepj¡W͝HFyRB.'#ܯU5"Wʶ!i L tPQc'{Z}|vx(fIݽp k-\_\f_;{I{O6Jӡa'5vn6# x\&g|ӂm,6pe0<n Sk"!]5j2> " zڏZǧ$iaLXNr _e"ʍ!MJ$㮕=wvgK~njL02Aj慳)}vHPlVmŸ dz1_L vٳG׎"g(>{^y|y<{SECwNFS`"0p`I9m,pD6Y!\VDep`3$8Yޟb#y.WA^sO9FR q6v:w q cT=Bbq* FxugkIE0HS*$~-5 @T_)UF?YMGO3IAպ 'X?OeoF4"=ĞDZdtBAQgS nƅpe0<n 兣_TЇB H4r@K y7Z}ljo 0nGiY׆p2WdׅpiDi3$U=hoF֘~b*m$l!G ǺR~;.Ԟ_G ev07>m?>G">$3尭ebb႔%DjфO,@KK*h=; oJ#pPGn:/;`ug~=qq'=X$bE‡8_?6Ŷ.#{ۈp0>"B8!\k kW)c-,T_uL (@:K۩W5iO V?~:#6޴^zi߈pk7 ~3a?GV10mTŮ ٽ|lx8H6g ܚߟ Ӵ?eᘝ?ޗ cv#ŭ؁Lx"Bf1pA/6~WGCXL.#\#ن"6z!uM4 o;ڻ +y#G}uUW kb)n,y4%SJf "=6~ab~-@8FoL,? pp&twl1>XA/6^׷ǵI-^G84WXv.!ǬvAw9nYҘ{UEDw#"zpشD/0Bs眂/ =G>~Cw?%ӧc-+h=}gc׋p< ݼGn6"`uWZ8'aV^7HdeET@WiC8h](aҚG8STdKD1}.6BU$!\#u0k7c ~. #ZD>wwaױGu[8p_WM.l4{tv n|/]@3}ض?g>,>ʆzaOU{'<FI e6¦lMMA؎W9XG{w0k7¯^-˷x;3ust 8 AE0n=Vj C7"=i/ӟfq_ۇWv hGʋ/ѤX 71!NR?0QS&B  7*#!Z%.@lgT+4lzEp~P*(E `(~!\ HBQ[V ap3X;;7.Gv.X•-7Z 7pꢶsl4-[IV\8!ܸf -8rRA2+z>B9 }y U_7pFk,p /S!MkHƾ2 WݺS!DUgp=![3mHV7pB8!tJSjuB$/P {(B8!\>g"+;o?!N7]?Ҕpk.HB un"M}ӗnM'M׏4e![w'HB un"us&ҸMf8Rv&ة9،; tv&(?e2]> ה#Z_|pukfpI^!U6 y:zEoDXR{ަ;vF݁;AzUB8!;dM}K.8pښ^\±vevm3[.8gzdscI7LIcm0]Wd:FϮ@/B82ho= >4}5I:ἢf\+nL;u85e]Ѓ,¡K>gߗmWZt~ÖlP`b dc^8X}FzlODqy![07Q)&O*!X!\&fDJpS`^!)QpA#|zBq-RUce %Pz ώ ;:xqXaZK{Dκ$i-^"\2H)VL@x&^;Q5"B" DyF'rOa(LB݊J2NC ĈXh۞,V\*jm_qǓpGcPAՎ]Qc酃6M۶|`qᠲ+=3D•QSU !1Z8pz n[2|-()9ˑc!ߚ ׆p4zp2(j lE8D ߆piY$΃Û@83DG D!KkN;իK{9ⓍD`$W%p'R2} -&Kۙev#fz`WS)+Yض#%:ilɻOOtј`:XYxy/nDtd.@60pm^FWl"y¤JK}\ T8K8 0pm*LkD~ng_z4I%j-/܊p]QQbnSFY±6IV'Ȗ.Neh-\% 7O4 VsXa3s8oS)WkD8L;:"\OYHm;@r態.XuwC= pЀ[6g73%5,D@spZDcTOD!\ n, ^8J|5\+}/DYc=ݑ $8'lqG/'omn& O+vᐠ_ r&Sfa8{IrZlOTX c:upߩhlZ$-NW.S1,FٲvFB8h<ׁ@ ߏ$b/\5=o PȜmۙĻk&LDu5$iLM1զBI^d+_vKWpK oBٴGpe7pusBrٵ=K!\/Binqpnݩn" &C pY{L^nDJ7LV7pY-+n>I^d+_vKpI^7螑N^n}D!Y!"l-!ܾ}oݔW_mUmɲ a{ 64nl8*pƺ3-%>DR˪WvMݥZS>^U\ݳ'g ,_BqԖn\ɬ+5*_W)LY'̟ʍ]CgCnR`wjjTIDz"%޵Htt pþ:NmpcJsÐPpe҆p8dvp!҆FnUI(豄p#b킽Kpk||qpB8!\C(/ܸ N7<I?]Y•AHm` {! lFDHwljyk ?!\Wxucp[.­*<$5$pj'cJ@74gL+4W'µSNTVp~|ŅBb*bpkl]YlV pB8!\/e NpcI{tp˼Ǵ2,x}p!X˜ռu"6fIۙT qI_q|&qtK33D9>裁 &Y*5pcIr]d !ܺhѣ@8t݂+T(⓲3jD8; Xo 8\qk  :/O2' nB=2+pvr{c|uTɻH kHẈQa2htv#첚W'r׈pE­gwV]G>+!!Ph4 ./ZC> F$lRBo V|8V|;>kH:JR9[u ||Y+ekBo VȂqrv)֐u% Y!pNV #Noénno;R\o %&,pV8#, [_8~BOHs8W2|Bo VȂ'qrv,֐u%`FrUw!ռBn B$DmmOxӱpÚ#:aOlPD ;>_9"W:>vLOBo V9R!!"F$4 *$pWWpniD A#4E"w!;s%{`ny# >!7zuхp{dqqrmvB֐uu\p$Uw !ռBbKxWHb⟹GfQMg' .ut!nB=ǎ8d"kHDԝ)$pmtMSk<!@g5ى4<̯ p-isb07K#J=r .^בpE­58pڱYCb']OOؔlN J;ypdØ+X6DJ[0בߚ̈́Lf4BLuq55 N)]gqrmvH֐$/~Q5?n̳WWpBF,Ǿ~LnۢXy^6m(#i/BrI\gۮߘ~#ٯVk:L^o V l86L;V$kH#)k<0!@g5 H>~;xGJO |oij̶3ԇ(]aȅ~f&o"}o }Xڑr[Sхp[ztgڥ.YCB3T_E+G'xĮ^a|USO=E6j^!\w s ږH7D$_qZB6Sژ?\7$>NY0>`: .b,}t{!W~W7IAq$p>ٙUp/zыް}W׾??PWzTF DJ@]r%;^;[}Te/{Y%a4p9׿~DuwSGB*Z9a|%?2/3ypF8x 1r#cTL%5SfUfw+6=MRH^__|;!.&kHv/XVoԎ#܂ 6ά­xZ!Yp5haN JMmHX8=}Kq5\?'?.L7qDDt0eY>},^bqxfƼ22zG{+>xscZ#dc5R K``•>}eUp帬p 6GH,[p ?ϯ @+ 8e#q%_09ϸ ï"30d T$K8MP&I]h%2peOnb lB2•-KJKU|pjY 6GHcߚG8|656H #7^}S>;cADC8hB,p K}O GhB'6N!\*O8·f-bkGl&- :~v{ɲKJKU© njCzGA8[f™.sY_}q$Odv_)'.fB LN6C87Anu4h%2pßFS8pe Y7F;ۯw;?r~'8bKQEjJ@^85pS#?bk3[f H>F85sq]tejC>R wl՟!nb-́@+y-C (+rI$1?Vx\N^eeW z`+()+6w,5iל1"oGurp)q T(ycmxZD8fA \h]E?7 p30$|4o(}ĶTVks9T}B pAM ۼpn(m֧5Nl$1~Y.7J]O[ZƆ!gҋE |IWܷp6BTu<) lB2iC8 6ÙjOF0?R!Go %V\aas00Ɛϊz-jحjFI@DGc Ac``Ibf$Z/f͔^fCc!ԆG8kٴ#GڍDX7 xp@8T>/8(q|E5Jel;$<5A|~5#L(O|foЪ]4nS7.ƹoK-`ႉZ 7"〬)Ss- ;զ;M" ˠciS9*34!>ی=]`y@.m~Sb))}0p2"\@G6, m٠H\if^6;Z}&+*p|r2(Dd`a1ةuyZS7ͦF C xlDL5ӆp N¥[/ZFb/\v:haapS#?[0T*LR3I}vZД[<X~\])s>#^`[K ( {'Z#xaمRK؞6= *_ֆ7S8pe6pq\6-H9%Fʔ9ux pfA$=n: (lvNM¥[/M T{#h$P !ԆDQ%0Jel^ ̅˅pн̞Knd* $@n9_S7۸$VdK!\ yy5PC'R]$1`g8B&Frs.U&p> ҭ||''vK3HMmH~U{wFZ# wvQ *b~Ym803oM &RYn"tJC8xxBGCpd\v-Zj-\VPЮnRY"Hg\\.y1Zow!`g.z PNz޼yUp6~-n8J.l Ul:^l-gj-Y3i^ٶo mfs#-xĉ7-e5]Ɖ#q& 8 [1ImSԥel l|΅ǦVrCb:ə`|G.m~xC0hp,#XFԫ=Hc-渮p]R[6!S !\b ȪJYgC'˾B A['k'egQa`FCpl VH~@,s?yjg7㤐¼Kx̶ħ(OVB]fYw!n>^Mc>*l#]/~ɒr_WLLKn,n"<DCز0<@{,q|fPTF8tQ¡E1Cyͩ=W */nrNWЊ䅫\NZtG?֣S?C?4+!qرC=GЯ|[Z^B8!\l.ӧr},h6ռmx|G. [i=Jw֋kjH, ]gvx@0a#0<  ;k,)[nG,\pGJBQ4zfQ72{nu}qɚ/ ^1!Ax炛ٚ ac ;p\f7ہ ?zhoN3_BΙj(/|UŲCxpi&+` #{ XgrGkq+5>^Q>R8|0>0e\gΜkC B2e`m7[fr])'RDʁLDJ@7ވp,\(A!\G~C  9@$lڑBὗn p a֎p\OK>&R NkB9l`i#Yq#NqtG8$etMv^B2׎p dp)  P'b- 79yY ܘ//t])ZhaH:B!K™5V&P8 l>ttWn%Ã".pcVKSΓW)tevnz˓aVV#Y"H1bmcps0<kJ .#? /ܩs~&m WWJ0CjΓ@z#[얀F w^ w,.S@tøɮy>n ᘲqeiVd V`D{Ync'!D,NV7؅pLk+@x+PTHʹwܴg?Ҫ5p,1 ),+fPy4tԌphsދg3<2#]}tQBx5GF7e _X^8ش/ [0jum`uZ~uN ᬏG9-:!a<`G#3bkcypn !3C}0jb"Z ؕ`M6H*C8cw`3(h QFkpeՅ>pE<z`(C8nsmY/+p`ΤGˏ]TcK""7َ _)~L8g ±1C=iBEHC^. _XH(P/mJ:֏1"fäDgaα;ڳ=E̜ATΐZvɽ/It4<{4tHz&rynw!@#zpvt[sx@z!(Fc?ֲ3wtp %*1Mw+e/5" Ҁ-|j \wb{xBXB^E+3eHH=i4;yXX*p7OZʟǡ F]#Z^`v'(L\ܔKl!85L^9km"s"A^ײ[]5"\Ol[QHI/v1qJHHW'Pq-2mE/v$:8ܲ~k_[7n+NE/zѥ^Z}o~ r!<.?яBNvHkjhS(DJ=N_:kህlOE8M~ !\7Dsd l&8ogbk426pmzhs"8jo G#? k<.H3g;'ƃ6seIerCC>.{ZVBhb Q?G}tH"]wu';u#詧:s zZ6pgꋳ*'y?),@8V#%ڬ tPѾڏ1 m;b\Q'R!aw4I{2 v& Z}e-[G蔻׆$™? }Yg~O~݆>r&p鯶}Y$苽nBnś{1CŧBצl Q#nu]pq.G.@Ea.j6o 8w>ǣnDv`Y^u3JWFUB'm.‡z8.G.YB3zdx@ŮBw%p[y\wGw:ZS=u"p}¢y?7ᘐ6_QB7VG!\rĭ b\G\[#N(!tnp5OpySxH97M{yT.#軔 ..FF\Uu!\∋]p+r #\_ mW[a浬rIpZB:!ws??nvOp?Byܱø5m zḇ :ˢqYǠA^u5 KTG:rlD#6&kW?i b悫"ksqm.8E8[G]G`ۖ`@#yn+"jXBIc9VbT}^7^ľTO6|mә7gK-|1yɹpu~UJ'Czg0kD8tEUS\A㮮U.Y&-Ō,h 0M;Xx3fei.'G:]c7F&ON`IDeY G܊vĵ[cL+oS.Ɵ͝NLQUanн'Oޟ` 1.2&P*Xe!+Y9*a(^T}¾iKЗWNïR |pU3kG8OGڹCt[>I'Ћ㧶ᘑSƒdi$tm{buxƪTh<؏ 6&F^ ŚvD[|kʵ \Gagĥ]pUq5 RR,7#XPA@=sNi<Η, 3eFhEN0X.E(,4> AM6Sܯe.lOU!ܟsn}vd('{Wp yX(cS zСs]p;L ^f!3f.H:fw0 3\!wC|0(RpD>&UFax~ܧA3a`xJ#BhWCr@MԴ:z1^8I8B8d]plrK( *?#.vpQCBK4Nyr0nn"P)mq"," +L™[GRwemH]cikPW]tEm?/Do>{'~' yY'+xv/X~a4bgo#L!^{0<,µ7vG8D.$D̴NW0oo_ ׿>OQq.# @h52[p{5A7x#/<ᒜ(8M*FM1 cFkۑ7U+1v"6T rYɓ`} }ϻp (M&1ݳŧ.2BSt+w5V/%@c 8M 4^86h,@|@t;6 & )v޴ c0GgoV4 ɬ˙. M?Q*!'y*{E>*.' L% zZ6p G/N-G}B1RW*+?Upн^sVucg< [YA/iü G)lۘP JpB:Y!ܲ=B܅pu<+E0'{&~1XMfI>;1@8+0N6m׸=Ѧɦ5`3F(mO :.ce{(Lœ^9r۰ә]!piTP8!"u 5j@ 3vbc_!p#hPC8 E8c9C>5@8w R}@K.XW狺I#,ɡ~ зFy{h'PKi~Xq^*̙Ʈ==mŰ`3Uaլ"0foƄMV0;!nJfC8$A]F!i[|] g3~^eX`0Mz`i/YvX!h} >pfJj:!\R!l\yFB:_!z~1t'*;rBC0-'mCG{#::Byo) Yc{c2pceYM? 4&:V`$so eA!\Vj"eVD=([S>Fa=lTo8=J hSb|j؍*=VEUQg_qߴz,"SS.IH'~w#N| \٥Uk;c  8pm^=_~R>x:@opB.gL!\cZ Ҿ~[d)}%ߗűq0NU mp=;%7"'4OYpCi!M?Y80>U܂ hH90MEDʬDʬenje6r|w!}M̾V O-‘)=>{;[ä/ P7 pYW (Bl&Hm8Y!\R!܈TIB8P'1ytY KO ];)} PWypB-{.DbH ^:nEREؑMT/cKp6yir%\pLGy &R j.^ym4k .P ;q/+?%@xGfaehE6<7FK}4]'=]Z pSkK_^7.11 7Pc@yVq'k];qҸf6Tm9!̙3H7MWCCZ'O"5daᑎ||qkp>\>]I!l\yF1蜊9 ,UpFk+$i"ey3F8bcg=ܳ?xěX BߌLk }NMOJFA8*1*.F=| @+O*mFiyoF8;9xFu(vb|?bYQ_'us~kF؊ @]̼`'X}qM H.ٗ}B:hD84>∑x&_x$&mXH! 7"!1 mK77q T'R̆tNg_µY$K֣+ͣg.kpKy6юޞߠ9=QRe^8,T<I2z].J) G OHq h af_ef3d"*\f@+g(6iP~ٻVH"L2\M(}uk|OZp5;zyzl[0mnH!gc_  HꤧW'+rze)#npiTeicRmqgA5Ѕ蕼#nC?pmo&zp00_.mn1dEo}T~BcC;l``F2[ynhoN&sg3B6B)kD868F^`cJ<[loHLAW3#Ѿ6 ORC]pa[]W_}ѣGV^W|-tX~;5K/뮫H] 3 |BF R <&Sr׻u?w1~fsVMZkb`aLS~)`bd31gQڌ : ^8۪1ӕzhulՈpхZp7wr%FfB.=a.h;hCw9ت;bK ؕ6+]|r4sOaW׈pD5N 2(H4\ m wI੧ZX??B8_-.ghD<#QQ;H1{j%7^8[FEmS0nBs϶T D䅫AAr6c\>Hnͳ_E2 pS믿>:t#{W'*r 7fc=1$L sgmB۹pS\MAżs̋i;_GSƇYN_kԃ5,X\6 ۜPo]+gaԓmsݭe)d>ԯcDNcNr"-8ʸtН'R䣣>7Zxiv0_yHu~Q@0LXZ &[f­!07,nGQYP厞Zn ̓p Ymt! pMs!);? Tc͇'"&/L>?.l Yc#sb`?YNFCGdey+k´sn>)N0@8@ Elπj=CBhv!njU#OXD#o}3}!\TOp+CfF8ґM8.\Bj^%"j eQ_ `7$B]p&Co 7Cw5DjQaBhv!.uLPP$!\=TmIp+CD߅kGk<~w+>#@8 Yl/kl:'D7 ac Bhv!N7ƎU NL>dNktgS%99`"7s@%=rWG˩XB+[f­! pil!\E7@=T[Ip+CG8F셃N60mF6/ e_TFeIHǟѶ.5兛bFB.[f­! pil!\@G# ms$vdc#^` SW^lfm'p{`-/~ G %mv Ў)޿rT'k_yV$f` JvH9i%/D­ΖWUԄpB4p :nwe;Y~?^>k0mHH#\/Gpwg73ռS6}h[,sm7j+ jH](KLsۘ Q3~L7\dJ[pX(!n"-3ϜQ4|lkx,Nj޳`PB@5)/pm.4~2zVSی ̄pe([̢;c!9=!n"- .@ǩX8l:"%$FH8ǒ:;'3Q4^CsdZ&Rvg)Be~(F4p;3!p FӗKN;,MpftH?ʯ#}!\pU 33G6`t~lǔm߇s3xdǹpI~_[9bZ^L_ Sk+;~ӛ^Sy!XLq\l B);mEB$O'S#Spm aG٦|gŢKGԎY3&+T/e {9r䮉X;vСChX@hǛTCoOB0Lmn&+IoX;xNLjAnV<yE!W7\Z 7 +!\.GpilJ9S<_!\*36a'[;1@ 0 ]wd>*nxg&.C!ܜxҼpwxB8!D[4pjiMuąpõ)̌p\6>_xM}W]C!L7\BU\ta>B8!D[R`J@W猶x(KHm /u5[iWE ޙ P764/!ܤN7 1v6nV<V0'8@8lv!g!?" ̄pe(\ϕn.I#Mp*PmI˜9ګorVǎ l3kp= H96JvBQ8]"B8!D[)'fu6II[U q+! Gn:m?Zʋ + Ʃv0@8MAtKH7[_[aFBnoI]pu=4B8!l~if{?=1- vm` KFWWyqxK!]I ^\Wfp˶-!l}m }r•mXB8!l^g< L|9] c6jˆlRBٔyFK!&gNr"%rCԄS%g.@8Nvm)p;3M.C+׭U@pilM,d)$ÐhmI~Ο, ]Wy|Qt]9-ϓ9q8A" ҈+ޙ P%ޭBnrZ,N7GLGCÆ#!Dg+f2#+[3^Mjw wfB2uץUpB4.F8lIjé^q_@裳ύ~͘BʻpiSWD73 nJ[pX(!n"-kD8LrGMMS) O_Xg=cy1(ޙ P]VRW &BFMI_ef=G8ݑS |Ue W[\p;3!p VĻQMN &B8om-}yE73n 兛D.n S'Hc 7񶙼p=RAc o<]G6sWܴ]Yyg9v8#'1cZ?]I1 B9KAu"ޙ Pf=_!"7A!n"-xL^Bq5$%ΎޞU+F}nĐvwl[!!XmH)gk+H)zLG7  fHК"B5>wa#VmQv㹇D  ]LsF!XK7[_[aFBޣ9)킼pBٴapj|(鉔 W݈p{;cSy!ev)a4rxgeskM&q &ثH ̙30}py; Vh<9+kx-|_>?N' ]jT!8}TG$`\w/\`d7h,btW73 nR;gB9]N7^ųx[ qGs6t)[pɏO#28b!\;J^ Go[z-|j!(-L)gk+H)GyfND7f'ͦkH/lW߅>Uy!X_FZ `50-aj9?\Oƕn\y*5I@\e%+pDʎ*eEռeeX@W 9Tpe- H c6P$ K+(JGëf5n/B#Y3AN0* XS_-QլnhCi/B#Y-٢@pRwb[#[ 9H9bRRH kH ;A5jQ &5irLjo' 9VpIk<{=$9}W<[mg2GTz$ [ϳRI%IGؑ[Tf'p5X!\mO$(Og/{i;d3$e gťNS**$ (!܈TR$03҇ p;>ijUK}c.[#W'[jA[wK2sX9=rSrj% [S%@e,mS4™UX}vXu Yͫp݅= Y!_A$^6 dSPlFB3 L`#S$-h$ % HuJ kH|p*$ XY+ ᦐiֆp#VMII$0n\+5I@[ ᖑ{\pE$z P%I`~ d5pS<!R1M!܈TR$m d yT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHT;I@X@Y+ᦐi F$mK kHtEo~w9$I@:Hk_Z%߿fj'N\px:s " H$6$"\-($ H$  B!қ!n1zX$]n( H됀nHHoBu!R !( H!! qpTKI@CB9<$I@"! qp]H$EB.RRI@搀nHHoB9$ݐn7j) HknHHoB5(!Q!!:J) H ! qpTGI@GBy\$I@K@7$pC7C\!\^($ H$ &'$I`z ᆀnf+^(I@ vI$PpC@B7Dz3կTBI@XpkyRk-ѣGvL}قV7$pC7CbۄwjǴ+ H;-!!+N>}ߣK1 ۷ԩS]N7$pC7CbۄwjǴ+ H;-!!+ph3XBJ_n'"_rpxz$ ZYCBוF@BUXY6 W,sD qp3YYH6$5$p`(]% ۆQ-J yp}E% #[PZ%!!J# W !ܺJ;W7\wy9.פ)8qIs!q!\ OAe$UH kH DQJ@ 5B. SO=u!lx^ (XYp6B%. HI kH}K@WFPy@V FyYG\.8Acp/JDV$!! P.!܊:W7ӎUp]:!($I kHt S(!܊:W7Up]z!Xґ$H kHLpt}݇EG+$ [P9Ǖ@V x#n-.8!\O7t$I`- Ç [eϸEM+$ [P9Ǖ@V Fx#n-.8!\^O7$I`#<xqr-dǏw % V&T%ռBe;Vup#/JJV!!1>p 6eP~rjݸ3tQA<MD2bcmQu d!*Ԅ 9W7G܊\pB.}_¡VvYh& )!1>Υj>s*1:{'L!d01Fį26zQz?S41MI]M+z$ռBqwĭ' G?K. vU/{ˎ=:Qj#T_(JѨACEI7-^;Ot]e~?؏IN#O2'! NIO_(Xs3"nbOn*UH yp?G:V vwxoI -O$Gl"!ʻB=3~6Ųgbm,%3]&nF z  d5ng \pB8!ܴ(׉p]} ^oH6:pv)g-S1q'w}.H7P_Ags,tE;F74l.aW4ڽ<]*6@']: :AMupeJ`=`6CvA|}OYJq&! c"DJd-Y뒎8K : G­R%0+wrZJ~ߎIS]. &4-7nvZl#VfdtF@ ""Ӓg`LGO`|Ddĸ0Hc6s>Ho߾.FH[ ڧmKCmU9!\]cK#c8WDs ;R"S+~ii-"*9 }VqGH)/ܖDu.!_.^<'ok&wp׽ޛ~ #?n Ʋk]븄pRUI`5^&'b_!l\'D!lxq"_ǹecD&Ddyui=P?UsB#p~܌pUF'nfZQ3;}|e0Ԙ)""<(~`_!! 0`D8|@ ^Z0nAYep?=WU) &Nm F. v& IW^XFSt z"` qN:~%D~Ujˑ( Y }"}znBeX- mW64gQW$$ [;(W0Fw&5n,pBM% Ljzq1   v6=yz:R7mƯoh0~Pfk$\ѳ9Lxc'zP $ ¡#]+7>AΖ8Dz9m|zl#eFk j|kp3ƛHzwNM k1O(KA`?OQ"  iݛ.ۑmK@klDWV ^gVJa~oV2e%!5nB8(zΏěI[fmki&"%r"\zfV3 Twk}͆k" `OݾϮm #:TX!E ^3~9.!<),g#:[i?{{~BDJ@7.P͟Z_#li2c=턍97ჃA-a$e]nWl0.R|efYK1QyĻ+ې79w}υoK/%78O#Ham&l>f+"ˊhhiPɘ>~B8X⧱VO:B,2(@]]8s^x`{C+:xqXg|.+A>b]_/AnlGQ SɸIXGۖcWC8f͹l~>4q_ Sñ8DQ%"3QmZJmp_QB+W H#׌6Xlr!BDcIAEI@WUQPf:[\7C?fS+ʜ/ӑ#w}#ر#lڼp߷wj/Ýo;s{ݞ5$;vСs0Vk,A@,7f |b.E%2_OtcNtn]V\Z!Pj޶hsǡ5٬]]d/ H7"Vp#ܴP›nβT/| ׁpph:uiUBLv8^.;^wQȁempkħ,[|;ySኇZbݣ0^!Kb^8z0C7:=vD8 rBfِB -[H@[n  0p52p:3Y$0HY` 1:.]!@ede!Htp]00B8!Ddp!ps!\q>|_-?D |O* n; s',wd)7bO}_f ,X~ YY}Rۺ0BNW QpbcI[D線( MBKـpW_}5MtD)u]|,Lc}b_|^OGW'VY*!\G!|PF\ᘔو'/<&8m8a"e6_U q?|wpOg1XB9ϞťL*Dpe_a`xn&J|dqh})psRy D8 im$agWȋ2}p)? G6F]YWp[Kp@ >ܤP[v4peUH@W!*/ҺO>gcyô+q[nO?6RP#%|F0QBΖ~^HMd!,Ek1DA`1X#/K,ƉL yq䞯)J_B!\ 6'&ep~+xsUanV_!\H|pxB5"z}`=c+`7_#!"ё rA0dg(!\ϓ$‘8?)>Q nz<hHO$)2075n٘,p_+7{E_~;ns2Ȗd"m&}!fTDW!*nI8`two|cDJKASW (1JhD `VGq C*O<9E8naUpi|'".+BB^Taaʭ_bG$4HA#X:~8aX Yv& 9Z8C6L ~tp@804OW/~8F[ow_WwI|ck Sׂ(8}euz~.J.~%[!\/q)UHe#\|8 TŰcmڮF VosJ `_,5ϯޱ}Q8 $!"EYn75')җI6|Ե'U VK#Wt[Vm: z 0ݑ#A!}AeL>׀Y)ZmK0~Sh{|( Ue5Tf{QIM].@A#B{..h C +jh ֈp\[ǿVRU!\儖-^z-A p^ g #7G 0q}͉#2Op?hv7>;b1V6mv^6>86lA>a<D혁.^8Lgl<9G0 u|MenC(+f?~m; 8s2X0K-l^|(lp֠pBe52ТC T=l<$N؂ <2mD8 ۀ#9hu KO\pVFg]VRU0!\*Hۛ-bUzǪq;fG¼ hp'F Tt!\Go1-y~ïJc^~F;<O Ý3?.8hWeW2TɦǑ;eoL0p>z#|DϱMgB`9 ΡmQH M7&8$qqG8|aʹh|A6VW9N/Nv:e["|bC8N &Rz{&RzUY!\Aϒ&@O;Z̅en&gx \.?2pq cc:b9GLDK/uꪫ@N7 ՟:3Dȉ}M0 \d1'?|F/\\0/bى%l>@֫X0?ۡ#Sт m:ecv&"$̦B^kcEK0F1nl\!\g멀+6f7E<`moYÇ;CF*? 3 fiDzPqTXe#g),5K͝ xl7.hLv2ʮƳq0"'ć 0M}0H7#>!NL+ǝx;N`D8YLUHe43}Q̩?|k_p;aC*.=9Htxc4#p!]O#%LH.$uH)^)Eev&F4ߦsͭ=x~jlI5 \rYJ.i`Z 7l12]?[#ԺzcV!\/P$03 zW^~9ha:teM.y[zwNn1eυkÆMBOv IZ;8cF#xDaXH6 p`~v& %w>{$-3sX8L<,bfXc';d&Q,61جZ@2u`Ff} afYVjaC;ʏꪷ޷ߪvwy,gb$f;7::Zh1:ʺPs}di 0FQ˙`){@iY.~Fnfdp`o5]RnYeO= NK/ 䂐$CWiM#aͲc>D7p0k070cQ8x5lٸJ[jĺX5=`0gkșښݰ˒++1+4TȊbeY1qsrv_Ţ7%Zߚqu~y)j ek)'0TXJaYQ8Ġ7w9} &V;/Њ[/$/n>#K%,P4ǎZxk >O^'7KKIPd3 ᖻ4G崓M­]uK N#µpIpʂ3)qfaѧD~ {?I@O}SXD)7鋶P 2 pGưS.0L*J#aC"J;~g&s-`b]vƥIFqwIp{n}[Nw 9ncc+0, po|p{NOwe݅puj}kB8!ܑ1씺 pI%%*N-& [i!\U9elypCWpBS0ypB$~' M|p_!( T[W8ٳG|s\fr[yH,% ZC8,rv6ؒiÈH3AuP~bw"XM?+z.wm_6v/ַԊI>g7pΰ|LpYK_:wBnUU p c./>cvo ~ۑ0 P yDž4l_8n?UH4Nhc#Ƣmwl< nY+ż*wU.$/ͫ3!wBwee~zKRŲj.˒W+ K'@J lwLQ֢p]B[`' r20nx GSvvlS Rֺ@k׮= >bҥKx{뢽o^|Ed4DW/F8<=C^,JYy\S8I,Fl7 j)Nˆ5۞ _1y;CA9~ xA\@}9E!\ㄖtO'kuO2a#Qܴ2b:|41=Db!͏ BgG&of =spG=:W8It=9/y0#}ipKp eE8R=$aYn}xςXw|e,Ip+Vd&o ̂]l1趽RR@A[p]|-XTpd \uSzXY׋6%B_[?0u|0't.\:q 8ǜcY~{2[M#f_ 8gy=b;9j3PABgHOhႺ,}L⪞# pDaE8P ޹\ Kv !D:߾}!2$ރʢP;*9֯eF%z1 eձ HY 6qymc@q ʽG?eħ( kВ C8iF!L`oO1hh#U1Wa /'}Y0ҕ+ ocNs|.s/µp g 8 1.~qT*Zfa1b9ʳqR(~MF?\Ԛ]efq0E#k0tXѡ1%.DF<=AA,@!5h}Ti5X%7| "Q| ٓ A8hA&9ڢCA"2 1oO-P@dp ./~LJ?$хND95uB6@$I;.C$g7 7T't;[I 8enpWď5[㮊tp' THMĤUp$LЅpJc,bႀdpKb'鹱qxOp 1x L/zey?E ҂͆}_08ha DY( kВ p=Sp&Ohi1:ьۜ(.l)g  X.p1ɯµRl\\8Dž1F ͯ#-j7 ;l_v$[^l~,pm1r&'" FQKAy'8 vp8@-v…joLz` c?6XoqwdA.t - p2^W_±k^Tp7!qa_u{JVє墪AW} Wf =s#q \38:!xްbHq$xޘH[S59  &^}KsH?l(雹G }m@ce~48nő,4gؔ9"RG b'm*ZqZtO[E[N.nN?*ƖHL&Dd T|lBNEmV* ,VF8̈́8ɻs9N!\0}|gь_;} %,Ҷ2qA9I 93Z鯸@KV !ZJJ jVv6A8a//< ]LHw\n T4RWr& -wT?RlL) (\3pIp?(%{-)ξO{/m!zZ* և% <`n55@ pqswFk1.XDnO!\V(pHwhU \"[} lvd7u- )k+ 4X'xBxXg=ܛx4c^;o8LWGgl>^p3ӣm>|q{ Fo48ђW< 8IFl:gO`DMcJ+@*H!\SplA4xgyc^2؋[(g =z<=?L@~ Bse磅ƚ5p/_;s~K_RrYb#T`+|pIZIqu6 P}n얋o'~ N`^hH(N^ |4hL²~T.^c D >9RԽX Y-ee!ΓR3FbH r"a/p6B|,ț>MZ4$_cNB#eY њwe`@軕k.rw1bY\sDz&"Z u`vl_8Ŋp-1 +b55ɖ{aP,.ϩv^ś/0&zfVFbn=N~[ obsӠ:Vn|OZ[uh`!\VB8!\ Pq@Epc)`X4=R( )~B ш )jp @8|puev m9eL~!p_~qc2,1Vx_X媼 @BHx Bxaq-L/*]I Lø/:b>4 V bt$}C#K$?.> 4X' (6xA(2 3 U#gh+dbc)9f҆MqglQ%8@T-ɭO. p-wp]n,>5ycXG` %q,4gcÐHw_[a=,HIbe!\V@sqlOOΞ=[lu.\zڣHcXr pY߻wBۿ۬lyFS8y@pB^EእKf'`A f I2Jb7 bZy Ըp6T9y~rYΩ#V`d)R?L +n-j藅B$ pI\ъIIS=7!r fp!fˌ;-6‘`-6 ƴ2p>ٜ7[vBd˙,\U<;XG&QE% ,RھsҹpB$~'q l嗫Ǚ3^O%|+8l3FܙA|?| 9q-[iV$يIYHƨL(Z#A<.HN[|Yk &TOhB~F!ҀԾ}!.BD p]ACo6jJ6ጸ:_Nr@:!\,V@&QE1a|wfk~/lP9Nr8omrhO'KBD pb6s861 m"C -tNi |_0k.WޣDruQfex7 l;Wi8W[N!\RBa1‘vldcK!х(,V3jYZr&~4f Y*እSmIT,p \k#"wUpŢ) v$HG@8C2"Mr$&l7Dc= :;!.BD p1+0U+_+*-gRWOY[\!\V_wAos8e ,1/p[umm!.BD p=#*m0nCUtB0*npNpB$~'tDZ'5#Z044Q䚐" RGoKl+մyQ.߯ĻQ@&\N!\RB@YzB8`c{!j )Ҍ?/| H(*W  LB0*eZ7!.BD p9!\h3p (Dl*sw#QXRL)n[`ELtpY~%ލBu0RpB$~'tbCOm_p?ǧfoaFpKH 8%n[[aWAR` ,G ǰ 6[~8N1tx.•yW/m.u#x]#h\8\o uǛKS" Ktn[!̖]v@Wun+?p$5}VD83NQ[?>D8xzq;o270q8gH#<,ir \W'YꜨ@j]1.H6{$4#!%?%YF~jb nXP6'&rfZZI)O.*ckxAJfF8dXb]7?80^$6\wk lcgf1 鱔*Rj e_]ZYp5T.[Z,pK%~ U1U$*߽)!I^m@¬VDB* SQWT@WVj!nN'۶R)PZMԹ_-w-dyB־B: U5|B8!:JE{+1;a g\{ݘ3xAWY ;m,2:8eZ): 'r5|1YW!\dcB8!\VEzW ^6X:mup|x-Nlo吅{mZ0Rz5 z_W/S@)5[N7?C5{{ͼdOAD\Gn kJ#C Ế|Gt^lӕ` % f6n>7=~~ μd6ppxDVɌ>ɼ1s4ȱEX P +}9㻷\q!EoՌ  vp|[ d8&ǎn@J\*^-\!^To5 hL9ph9JI a.15r yYsyєlrJ8bdE6n jJ5)0N, :\8-`x._@!; 0Xi|bu!>{{#KRV»Bl mkHEP|Tsظ9`#1ӗ> t247x- %5C\(̨rK-mD1 F|;1??׽}/ Wv>}8=}y19 xϿ/# Mefn>Xr-(} \ Ћ,Q9ǓOpC W"!@_ġ`JBx^6pŴԥ;Bc^wZWpHK/?>Ŀ`x^>я wp`i22 ^pJaz{ $xŒ'. ~R$@~303im(\k8!rW A "GH'&HA;?l;ˑr&\A+0=qHlA<;%"J3?˺gn4BQU,qB} B!\INn9ÀCҚ'%Ć"Y0xq1Dy$>, WO9!ʝ~bC;B@YI8s&,dB{H*p\єBe f80`ƀYHHpG8?A1NC $CYe 'ٲGA8Ě2ckTnrog MۤF*k$gVS@*#p! GqـL83a SA6G,WeQ!ܢ]C? BzWrBbn-pFV0%l$T2vL !K912(! X GTPWypY;!ܚj vREeɥQ@W/,f@E#1!@'A_7_`24ڴ*N"BVYv@{p;.ea՚pYwDb!\Ɏ"!Ҁ|!cQfq0E0!Pf`3ʱR@}D8F,j‘?@dA( P# ZeفɖW**17;nֿ˿  dGb,pGN1`Hhr1²E8!jGB#d&!J ZeفɖW** T x"ysR*ME>xm+fephh%`igON0l43HɉdQܶ pg1A,0Fp`7lp e<M/~+}b~ $[^!>.jN~{(z'+ Wd#Q\g2+m\C8FFṆ87NpC&|`WOrc.y, ,r56BVYv@* pu`.WbHG`NP+c؊1]J&F$"1Jh-Hi$_kd HkO@GT#[ & RĚ,:8gG-,S)lyp]MZK!փ;HGxy#!֯ B=0e.f a$Hk`E5 qg 5W)ɖW5+  Z(QWpwXB f'ѝE ZeفɖW** Ս@g|,@dGECh>yN\zU(lyp;ʪBB8Gay45HIKsJO߼Bg\WK1Iv$ZD~vy( 0TG $[^!\GWSR@70+cJ< 4o `\mpu4߼gKÂ/r@!\g_ 䊙HL-.U}(lypЪEBgKRsy6[7$Ik0[sЉi!\>X) Lv$p4$})+xhe $[^!V-vAY6c#3ᘘa>x-}5bH#-U|e#p],B,{l/dC9FWZ8 f@WqP@dGBQ4DTSW*H* #R5"uBSz?Bg\QZ) wʓU|e#p],B78c k.s2qd!RJ j5(H( WTdbL!\C,;P * ** p\H\;&p}.gMtP!\3(\-S+BLu%WWS@WΠULۜ6qbpE Lv$ۢU8LBVYv@UnWYUU@7۾G e!0}>Rl(͛7Hn;xmz|X ibT7)([;l/ĉ>JWO!\ŇY:R@Œ) pM߿T_SBZpJ&;KE@S88 \X / `lRl(FrMHWf3[$~ <-gBVAՔ,S) b j5(Hԏ#!X V,𘘱F X6qQ#:VV@W*H* {{s O>3<[};ށ6>WX!\]HG2HJØFjK$<ʲ-nWYUU@7^~?WV7{z׻p`z!\ɎD+;46.Xjr ƐE<ʲ-nWYUU@v9@g|,@dGE >r:S@W*HB\eU!W!n.R v'\N( 0TG $[^!\GWSR@'$9s˵ N#!=[!Z1V tP!NV3J&;Bړμ<ʲ-nWYUU@'#}5bHW[CopfH!\GKNWGGWqP@dGBQړμ<ʲ-p;ʪBB8!\_!\3(\-뫭ءBLu%WWS@'뫣#8( Wd#(\_Ig ZeفɖWQ\eU!W!N.WbV[!\ŇY:R@Œ) j+P2ّP3op@d˫(n&½׮]VΝ;x;s_|qNŋYʟ'0qxpGNBpH!\3(\-Ŷݼy駟ʕ+.]xm>BJ=Z6Z1׾/L8-#[ o7.16Ik,.t,;_xWn !\ŇY:R@Œ) pƷW-20 `g䊍Pp7d B7U(lyǞda U@w:¡ӂ)kN @8d - :4Hks}95fA]|]}'z~Ņp󵲔Ɏ"G|$g ~{2jAF ~CBFecQ8/fvrPĊ۠ 01)Iq3ZF!\C,;P vpU\p#aV%F8d Bc焯/±_dG  ipsT $; ~122 Lp (qؠg)  2Ĩ1_pp(b>ƣAv+=Z9U(lyp;ʪBBn",64YF{G#"_rJq&kq' /⡉u~ SxM*C8dS$.?7 xP.!\-,RAp ^BU0SpFnYV~z;h '{ ›n~"Ֆab2qhdh.11O&HD<1a3iC1cnl %郓>:12(Q) ek* [SmՋBGf T=kB&()" wwRipsT $;R[l\5@.>_w oh HxF)[htb#qf&#~~1bXmpL#"=lHV(&3e벴к%}@ʂVYv@{v0n&q{Dt'ND8y5QR胭pҢyk~+R޿;9o<7`X|4420`񱸋énlG?H3h񩍍D8ҦcM`mzS_UD0ϙ?J'-nO[un&gh,ڍ`ė{ sMfA ݡ1'ľ?~?Ļw>Os?mAdG>•=--x51Yks [Y~[|,kdB85CT ycB&b~hIC]ӝLESHRp/>ǔƒYOv$ZA8U|B,1pozxX+N/VOE*nHB\kUdB\;[x;f-`d__}S9ͅ(eɎDW1v#S\f[ W%ލɖWkW@'XԠMNƭ;rB-Rf)Hp>2BUw@Z" 쁜^V J@#!;QpYFd+͵VE+ a\"89!@)Hv$p}'y+z\x7 $[^!n*2_!\SY*uwb9!@)Hv$p+M*nHB\kUdBFZsn g{ ěa?N&[pƿLa 69nWY;6FcLodgu*[R ّ ֭.qU(lyp֪|p-  `k8qx L'e;F+q :~pr eF-=1?qpsPy[CA?'EI+U ّ­4+M*>HB}\h"K!\ R-^,'p̿A (1pW|9ZFC"T jriip H7Ad'v?l #s6p3Z`hc)p@1IAvFu~* /e $;K!zxlď4\@)윏vyG RZ 4ĆpqB5|!F.%MSwv'; |.F!~=|VǢpqȋ:/sml1Bz6ێ+8XqFl U _T@_(M@B6[xH,pU'~L#o-~C8{(ceל6[<µȓ5HB5/jD!\ k~HlΆOqMaf~m|ߠJ i:F#"\!1: tP!\;$:C8QomfosIIp6eGEplکr#!T@@­y9TV# A8[?F8ĵ,RGGقX--i]"!\#M1Hv$p;hdڭjlypIC<-p}ȍka4VD8.':,^=jɎK?pdM-nˡQ@¡ b͂i8Cq#\H"Or=m(\#O؍ɎK4i!nU$Kd+S)3fQ8[Ħ1f=/\}}賟}k_C`ƻwNu!ؙSXdGBWyU X!\ ob}υp{SR{ׁaEbg'bcߡ>㭥qя[DnR>yD !\mH Zk: $[^!:B4n&qk%n޼[㶽P|~os7p)I};.ZǓY`)Bg l0$mb&Iozǔ&*HoC:PW(HB\eU!W!\a6ηY! fm7n>/'}dl!f"hU|0;H:]e5J-:/"v]?G?=,<B\٤À#ul.]8rI ݾ};rOFQAZUI6!\ˏ|F!\͟/V3ߟl"`0 y>HR P@p枠W@`#ڈGHGrz,Ƣm@J BS *l@u#1Kj`L vdnHᳩW@u'hZMWRCp>#Żpw2 A8@Ћ7VD8X36dq c5 jqr|3w>v7gKLj<B~?y~#;ހ[o~xqk_ ߣr,cqBSnµp[$^O唁9"-gr`s)0H2>l~1I.|2߀y4Cv 6f:ّ\ձH rX߇ɖw/BgB x ܠfю qnR !\_Ml9 G\΄p %l~k 7"ϥMp<*t:F􅭷yfl,%t۴}áD?P-*k lpX\kď&&ݻHl >/V@g.ە f` 2 l!R9 %aH OaR č93M#(.PJ?J'-Q8FZc))m8'ˑfBB)Yn&[{#ňVΎxko88,Q؉9cq6p4vc@,2y o~ݓ !\n{ ?J'-Gf l6 %; nO7uDoTR9/5}%9 4XYvc{si-8przrJv$pjT@wO!>φ>*pd 'YnJy7spsTZAy(YEILR2 f 3 ic2.ɖAwӭ pYFd˻\Kd@kBoTDQ8E6) K۲K? !\_Ig v٬RI-r7pl7;Q * Չmd-KdGBW{ҙBUw@]83܂?͑ Htp}ubp*Hv$p}'y+SctL-Bg۸`ΚKHI;Rk!Nʃ/#e $;Bړμ•=ջɖw!GKz%Tc~0ϖB~snNW'Fas !\_Ig Sd˻%7 `/j9rqEw[JpB:1B*)HjO:VW*W $[%p'Գr~QBt>: ?-)!ܠΝ;2ǻZ>#@#! rWwuOkNvp%XJ`T6)8^. $`~ Y>zV $;B3op[=*w[-ouCČ6]ql$o,vFHx-mW-+ k7) R ّuE}+k93)ly#\!/ο*eD xp3Hv$!7|3}ၼm_!Uv@mvvTµvE*Hμk/BH R@l U _T@uWR@ 4@#ݻwϟ?5(ȥHBhM!\kWDH)ЬɎŋ1~'|}$}) kic*lyp/m* k+) T ّ?gП8UtH l $[^! WAE+"hVdG'1-x;{,:U;2vtp6 rlQ-nQeMpm^y%@ $;OBF"$Ei*R@ H) +0Z ptQ84)nKR@ H) ~#@aC"R@ H) R@ HuxW^:V(!/|EֹN*E H) R@ H)~M0pv IY) R@ H) g?9cY C ̋;ġ phpGACL




Generic Access Control Lists with PHP

Mike Benoit <ipso@snappymail.ca>
James Russell <
james-phpgacl@ps2-pro.com>
Karsten Dambekalns <
k.dambekalns@fishfarm.de>

Copyright © 2002-2006 Mike Benoit
Copyright © 2003, James Russell
Copyright © 2003, Karsten Dambekalns

Document Version: 60

Last Updated: September 3, 2006 - 06:55 PM

Table of Contents

Table of Contents 2

About 4

What is it? 4

Where can I get it? 4

What do I need to run it? 4

Who is responsible for it? 4

Introduction 5

Understanding Access Control 5

Who/Where 5

Who/Where 6

Defining access control with phpGACL 6

Fine-grain access control 8

Multi-level Groups 8

How does phpGACL determine permissions? 9

Adding groups 10

Adding people 11

Resolving conflicts 11

Naming Access Objects 12

Adding Sections 14

Multiple Purposes 15

Access eXtension Objects 16

Installation 18

Basic setup 18

Advanced setup 19

Reusing an already existing ADOdb installation 19

Reusing an already existing Smarty installation 20

How do I move the phpGACL files out of my website tree while leaving a link in the tree for administration? 20

Using phpGACL in your application 21

Basic usage 21

Advanced usage 21

Using the ACL admin utility 22

ACL's 22

Creating 22

Sections 23

Extended Return Value 24

Notes 24

Glossary 25

ACO 25

ARO 25

AXO 25

References 26

phpGACL API 26

phpGACL Examples and Tutorials 26

Access Control Resources 26

FAQ 27

Can phpGACL handle large sets of data? 27



About

What is it?

phpGACL is an set of functions that allows you to apply access control to arbitrary objects (web pages, databases, etc) by other arbitrary objects (users, remote hosts, etc).

It offers fine-grained access control with simple management, and is very fast.

It is written in PHP (hence phpGACL), a popular scripting language that is commonly used to dynamically create web pages. The GACL part of phpGACL stands for Generic Access Control List.

Where can I get it?

phpGACL is hosted by sourceforge.net at http://phpGACL.sourceforge.net/

What do I need to run it?

phpGACL requires a relational database to store the access control information. It accesses this database via an abstract wrapper called ADOdb. This is compatible with databases such as PostgreSQL, MySQL and Oracle.

phpGACL is written in the PHP scripting language. It requires PHP 4.2 and above.

Access Control List administration is performed by a web interface, and therefore it is necessary to have a web server with PHP support, such as Apache.

Who is responsible for it?

Mike Benoit <ipso@snappymail.ca> is the author and project manager.

James Russell <james-phpgacl@ps2-pro.com> and Karsten Dambekalns <k.dambekalns@fishfarm.de> did the documentation.

Introduction

Understanding Access Control

The best way to explain access control is to use examples with real things rather than trying to relate to concepts.

Han is captain of the Millennium Falcon and Chewie is his second officer. They've taken on board some passengers: Luke, Obi-wan, R2D2 and C3PO. Han needs to define access restrictions for various rooms of the ship: The Cockpit, Lounge, Engines and the external Guns.

Han says: "Me and Chewie should have access to everywhere, but after a particularly messy hyperdrive repair, I forbid Chewie from going near the Engine Room ever again. Passengers are confined to the Passenger's Lounge."

Let's assume from now on that access is Boolean. That is, the result of looking up a person's access to a room is either ALLOW or DENY. There is no middle ground.

If we mapped this statement into an access matrix showing who has access to where, it would look something like this (O means ALLOW, X means DENY):

Who/Where

Cockpit

Lounge

Guns

Engines

Han

O

O

O

O

Chewie

O

O

O

X

Obi-wan

X

O

X

X

Luke

X

O

X

X

R2-D2

X

O

X

X

C3PO

X

O

X

X

The columns list the rooms that Han wants to restrict access to, and the rows list the people that might request access to those rooms. More generally, the "rooms" are "things to control access on". We call these Access Control Objects (ACOs). The "people" are "things requesting access". We call these Access Request Objects (AROs). The people request access to the rooms, or in our terminology, AROs request access to the ACOs.

There is a third type of Object, the Access eXtention Object (AXO) that we'll discuss later. These objects share many attributes and are collectively referred to as Access Objects.

Managing access using an access matrix like the one above has advantages and disadvantages.

Advantages:

  • It's very fine-grained. It's possible to control access for an individual person if necessary.

  • It's easy to see who has access to what. The answer is stored in the intersection of the person and the room.

Disadvantages:

  • It's difficult to manage on a large scale. 6 passengers and 4 places is fairly simple, but what if there were thousands of passengers and hundreds of places, and you need to restrict access to large groups of them at once, but still retain enough fine-grained control to manage access for an individual? That would mean a lot of fiddly and lengthy adjustment to the matrix, and it's a difficult task to verify that the final matrix is correct.

  • It's hard to summarize or visualize. The above example is fairly simple to summarize in a few sentences (as Han did above), but what if the matrix looked like this?

    Who/Where

    Cockpit

    Lounge

    Guns

    Engines

    Han

    O

    O

    O

    O

    Chewie

    O

    X

    O

    X

    Obi-wan

    X

    O

    X

    X

    Luke

    O

    O

    O

    X

    R2-D2

    X

    O

    X

    O

    C3PO

    O

    O

    X

    O

    This matrix is not so obvious to summarize, and it's not clear to the reader why those access decisions might have been made in the first place.

Defining access control with phpGACL

It seems that for large or complex situations, this 'access matrix' approach is clearly unsuitable. We need a better system that maintains the advantages (fine-grain control and a clear idea of who has access to what) but removes the disadvantages (difficult to summarize, and difficult to manage large groups of people at once). One solution is phpGACL.

phpGACL doesn't describe access from the 'bottom-up' like the Access Matrix above. Instead, it describes it 'top-down', like the textual description of Han's access policy. This is a very flexible system that allows you to manage access in large groups, it neatly summarizes the access policy, and it's easy to see who has access to what.

An ARO tree defines a hierarchy of Groups and AROs (things that request access). This is very similar to a tree view of folders and files. The 'folders' are the Groups and the 'files' are AROs.

Let's make an ACL tree for the people on Han's ship. First we define some categories for the people. It's clear that Han and Chewie run the ship, and the rest of them are just passengers:

Millennium Falcon Passengers Group
├─Crew Group
│ ├─Han ARO
│ └─Chewie ARO
└─Passengers Group
├─Obi-wan ARO
├─Luke ARO
├─R2D2 ARO
└─C3PO ARO

This tree by itself doesn't specify any access policy; it just shows how we're grouping the people who might request access (AROs).

We apply access restrictions by assigning instructions about a particular room (ACO) to Groups or AROs in the tree. Han says: "By default, no-one should be allowed access to any room on the Millennium Falcon. But the Crew should have access to every room. The Passengers should only have access to the Lounge."

Millennium Falcon Passengers
├─Crew
[ALLOW: ALL]
│ ├─Han
│ └─Chewie
└─Passengers
[ALLOW: Lounge]
├─Obi-wan
├─Luke
├─R2D2
└─C3PO

To interpret this ARO tree, we start from the top and work our way down.

Firstly, the default policy is always to deny access. Permissions have been overridden for the "Crew", so they have access to everywhere ("ALL" is a synonym for all rooms: "Cockpit, Lounge, Guns, Engines"). The "Passengers" have access only to the Lounge.

This way of describing the access policy is much clearer than the access matrix. You can easily see who has access to what, and it's easier to determine why they've got access (it seems obvious that Han and Chewie would have access to everything, since they're grouped under "Crew").

To summarize:

  • Access Control Objects (ACOs) are the things we want to control access to (e.g. web pages, databases, rooms, etc).

  • Access Request Objects (AROs) are the things that request access (e.g. people, remote computers, etc)

  • ARO trees define a hierarchy of Groups and AROs. Groups can contain other Groups and AROs.

  • The default 'catch-all' policy for the ARO tree is always "DENY ALL".

  • To assign access policy, work your way down the tree, explicitly assigning permissions to Groups and AROs for each ACO as the need arises.

Fine-grain access control

Oops! What about Chewie? By grouping him in "Crew", Han has indirectly given him access to the Engines! He doesn't want that after what Chewie recently did to the hyperdrive, so he adds a rule to disallow this:

Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ └─Chewie
[DENY: Engines]
└─Passengers [ALLOW: Lounge]
├─Obi-wan
├─Luke
├─R2D2
└─C3PO

This is an example of the way you can control access policy in a fine-grained manner. It is not necessary to move Chewie to another Group; we simply over-ride the access policy at a lower level.

Another example of fine-grain control happens when the Empire attacks; Han needs to let Luke man the guns, and let R2D2 repair the hyperdrive in the Engine room. He can do this by over-riding the general permissions granted by their status as a "Passenger":

Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ └─Chewie [DENY: Engines]
└─Passengers [ALLOW: Lounge]
├─Obi-wan
├─Luke
[ALLOW: Guns]
├─R2D2
[ALLOW: Engines]
└─C3PO

Multi-level Groups

Groups can be extended to any level in the ARO tree. For example, you could add a Group "Jedi" to "Passengers". Most passengers would be categorized under "Passengers", but Luke and Obi-wan would be under "Jedi" and therefore might be extended extra privileges (like access to the Cockpit):

Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ └─Chewie [DENY: Engines]
└─Passengers [ALLOW: Lounge]
├─
Jedi [ALLOW: Cockpit]
│ ├─
Obi-wan
│ └─
Luke [ALLOW: Guns]
├─R2D2 [ALLOW: Engines]
└─C3PO

How does phpGACL determine permissions?

When the ship's computer (running phpGACL of course) checks access, the only question it can ask itself is "Does person X have access to room Y?" In phpGACL terms, this is rephrased as "Does ARO 'X' have access to ACO 'Y'?"

phpGACL determines whether a specific person has access to a specific room by working from the top of the ARO tree towards the specified person, noting explicit access controls for that place along the way. When it reaches that person, it uses the last explicit access control it encountered as the result to return. In this way, you can define access controls for groups of people, but over-ride them further down the tree if you need to.

Example 1: We ask: "Does Luke have access to the Lounge?".

  • Set the default result, "DENY".

  • Work out a path to Luke:

Millennium Falcon Passengers → Passengers → Jedi → Luke

  • Start at the top of the tree and move towards Luke: The "Millennium Falcon Passengers" node doesn't say anything about any room, so do nothing here.

  • Move on to "Passengers", which explicitly says that "Passengers" have Lounge access, so change the internal result to "ALLOW".

  • Move to the "Jedi" node, which doesn't mention the Lounge at all.

  • Finally move to Luke's node, and again there's nothing there about the Lounge.

  • There's nowhere left to go, so the result returned is the current value of the internal result: "ALLOW"

Example 2: We ask: "Does Chewie have access to the Engines?"

  • Set the default result, "DENY".

  • Work out a path to Chewie:

Millennium Falcon Passengers → Crew → Chewie

  • Start at the top of the tree and move towards Chewie. The "Millennium Falcon Passengers" node doesn't say anything about anywhere, so do nothing here.

  • Move on to "Crew", which explicitly says that "Crew" have Engine access, so change the internal result to "ALLOW".

  • Move to Chewie's node, and there's an explicit rule saying that he doesn't have access to the Engines, so change the internal result to "DENY".

  • There's nowhere left to go, so the result returned is the current value of the internal result: "DENY"

As you can see from the examples, if a Group doesn't explicitly specify a permission for a room, then that Group inherits the access restrictions of its parent for that room. If the root node ("Millennium Falcon Passengers") doesn't specify a permission, it inherits it from the default setting ("DENY ALL" in the above examples).

This implies a couple of interesting points about the ARO tree:

  • The ARO tree always shows the full list of the AROs. It would not make sense to ask "Does Jabba have access to the Cockpit?" because Jabba has not been defined in this system. However, phpGACL does not check to see if AROs or ACOs exist before performing the check, so if this question was actually asked then the result would be the default "DENY".

  • The ARO tree may not display some defined ACOs, and relies on the default setting to define access policy. For example, say Han defined a "Bathroom" ACO. Any question like "Does Luke have access to the Bathroom?" would have the answer "DENY", because the default is "DENY" and nowhere in the ARO tree does it ever explicitly mention the Bathroom. Keep in mind when examining the ARO tree that some ACOs may not be visible.

Note: When asking phpGACL questions about access to an ACO, it is not possible to use Groups as AROs (even though it might 'seem' right). For example, it is impossible to answer the question "Do Passengers have access to Guns?" The complete answer is not a Boolean "ALLOW" or "DENY", but the more complex "Luke and Obi-wan can but R2D2 and C3PO cannot." phpGACL is not designed to return that kind of answer.

Adding groups

Han feels this ACL is starting to look a little complicated. There are so many exceptions! Perhaps he should make another group, "Engineers", containing the people who are allowed access to the Engines and Guns. That group should contain Han and R2D2 since they're both capable of repairing the engines and guns. This means Han can remove some of those messy exceptions-to-the-rules, and that has the benefit of making the description clearer:

Default: DENY ALL
Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ └─Chewie [DENY: Engines]
├─Passengers [ALLOW: Lounge]
│ ├─Jedi [ALLOW: Cockpit]
│ │ ├─Obi-wan
│ │ └─Luke [ALLOW: Guns]
│ ├─R2D2
│ └─C3PO
└─Engineers [ALLOW: Engines, Guns]
├─Han
└─R2D2

We can read this as "By default, no-one has access to anywhere. Crew have access to everywhere (except Chewie, who has no access to the Engines). Passengers only have access to the Lounge, except Jedi who also have access to the Cockpit. Luke has access to the Guns too. Engineers are allowed access to the Engines and Guns."

Most importantly, we can see that Han and R2D2 are now in two places in the ACL. It is not necessary for them to be uniquely categorized at all. This defines the policy more clearly to the reader: "Ahh, Han and R2D2 have access to the Engines and Guns because they're engineers."

Adding people

Han goes to Cloud City to pick up Lando and get some repairs. Lando's the Millennium Falcon's previous owner, so Han reckons he qualifies as Crew. Lando also offers the services of his top engineer, Hontook, for help with repairing the ship while they're in dock.

Default: DENY ALL
Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ ├─Chewie [DENY: Engines]
│ └─Lando
├─Passengers [ALLOW: Lounge]
│ ├─Jedi [ALLOW: Cockpit]
│ │ ├─Obi-wan
│ │ └─Luke [ALLOW: Guns]
│ ├─R2D2
│ └─C3PO
└─Engineers [ALLOW: Engines, Guns]
├─Han
├─R2D2
└─Hontook

This shows how easy it is to grant new people access. If we used the original matrix scheme, we'd have to set permissions for each room for both Lando and Hontook. Instead, we simply add them to their appropriate groups and their access is implicitly and easily defined.

Resolving conflicts

What happens if we add Chewie to the list of Engineers?

Default: DENY ALL
Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─Han
│ ├─Chewie [DENY: Engines]
│ └─Lando
├─Passengers [ALLOW: Lounge]
│ ├─Jedi [ALLOW: Cockpit]
│ │ ├─Obi-wan
│ │ └─Luke [ALLOW: Guns]
│ ├─R2D2
│ └─C3PO
└─Engineers [ALLOW: Engines, Guns]
├─Han
├─R2D2
├─Hontook
└─Chewie

This makes Chewie's access to the Engines ambiguous, because now there are two paths from the root of the tree to Chewie. If the ship's computer follows one path (along the "Crew" branch), the result is "DENY access to Engines." If it follows the other path (along the "Engineers" branch) then the result is "ALLOW access to Engines". So, is he allowed or denied?

phpGACL will warn you if you add or edit an multiply-grouped ARO in such a way that the ARO's access to an arbitrary ACO would be ambiguous. But it is up to you to resolve the conflict.

If we now asked phpGACL the question "Does Chewie have access to Engines?" the result returned is the result given by the last ACL entry to be modified (this is phpGACL's policy). In this case the result is ALLOW, because the "ALLOW: Engines, Guns" directive assigned to the Engineers Group is more recent than the "DENY: Engines" directive assigned to Chewie's Group.

When ambiguous access entries exist in the ACL, the ACL is said to be inconsistent. Inconsistent ACLs can be very dangerous, and you may unwittingly provide access to inappropriate people if you allow your ACL to remain in this state. When phpGACL warns you that the ACL is inconsistent, it is best to resolve the conflicts as soon as possible to regain consistency.

To resolve the conflict in this case, we could either:

  • Remove the "DENY: Engines" directive from Chewie's entry under the Crew Group.

  • Add a "DENY: Engines" directive to Chewie's entry under the Engineers Group.

  • Remove Chewie from the Engineers Group, since Han doesn't think him a worthy Engineer anyway.

Han chooses option 3, and removes Chewie from the Engineers list.

Naming Access Objects

phpGACL uniquely identifies each Access Object (AROs, AXOs and ACOs) with a two-keyword combination and it's Access Object type.

The tuple "(Access Object type, Section, Value)" uniquely identifies any Access Object.

The first element of the tuple is the type of Access Object (ARO, AXO or ACO).

The second element of the tuple, called the Section, is a user-defined string which names the general category of the Access Object. Multiple Access Objects can share the same Section name. The Section name should be short but descriptive. It's used in the user interface in selection boxes, so try not to make it too long.

Sections are stored in a flat namespace; they are not nestable like Groups. Sections have nothing to do with Groups or the ARO/AXO trees - they are purely a mechanism for helping to maintain large numbers of Access Objects.

The third element of the tuple is a user-defined name for the Access Object, and is called the Value. A Value cannot contain spaces (however, a Section can).

Both Section and Values are case sensitive.

Aside: It is commonly asked why strings are used to identify Access Objects, rather than integers which ostensibly seem faster. The answer is for legibility. It is much easier to understand:
acl_check('system', 'login', 'users', 'john_doe');
than:
acl_check(10, 21004, 15, 20304);

Since it is often obvious from the context which type of Access Object we are referring to, the interface for phpGACL (and this documentation) drops the Access Object type and uses the format "Section > Value" when displaying the name of an Access Object. However, the API requires an Access Object's "Section" and "Value" to be specified in separate function arguments (the Access Object type is usually implicit in the argument description).

Example ACO "Section > Values":

  • "Floors > 1st"

  • "Floors > 2nd"

  • "Rooms > Engines"

Example ARO "Section > Values":

  • "People > John_Smith”

  • People > Cathy_Jones”

  • Hosts > sandbox.something.com”

Example API usage:

  • acl_check ( aco_section, aco_value, aro_section, aro_value);

  • acl_check ( 'Floors', '2nd', 'People', 'John_Smith' );

Valid Naming Restrictions Examples:

  • "ACO -Frob > Flerg", "ARO - Frob > Flerg" (The Section and Value are the same in both, but this is fine as namespaces are separate across Access Object types)

  • "ACO -Frob > Flerg", "ACO - Frob > Queegle" (The Access Object type and Section are the same, but this is fine as the Values are different)

  • "AXO - Frob Hrung > Flerg" (Sections can contain spaces)

Invalid Naming Restrictions Examples:

  • "ACO - Frob > Flerg", "ACO - Frob > Flerg" ("Access Object type - Section > Value" must be unique)

  • "ACO - Frob > Flerg Habit" (Values cannot contain spaces)

Adding Sections

Before you can add a new Access Object, its Section must be defined. To add a new section, use the add_object_section() function.

add_object_section (

string NAME, A short description of what this Section is for. (e.g. "Levels in building").

string VALUE, The name of the Section (e.g. "Floor").

int ORDER, An arbitrary value which affects the order this Section appears in the UI.

bool HIDDEN, Whether this should appear in the UI or not (TRUE means that is will be hidden).

string GROUP_TYPE) The Access Object type ("aco", "aro" or "axo")

Han creates 3 Sections for the AROs. "Humans", "Aliens" and "Androids". Let's list the AROs with their full names

Millennium Falcon Passengers
├─Crew [ALLOW: ALL]
│ ├─"Humans > Han"
│ ├─"Aliens > Chewie" [DENY: Engines]
│ └─"Humans > Lando"
├─Passengers [ALLOW: Lounge]
│ ├─Jedi [ALLOW: Cockpit]
│ │ ├─"Humans > Obi-wan"
│ │ └─"Humans > Luke" [ALLOW: Guns]
│ ├─"Androids > R2D2"
│ └─"Androids > C3PO"
└─Engineers [ALLOW: Engines, Guns]
├─"Humans > Han"
├─"Androids > R2D2"
└─"Aliens > Hontook"

Sections are just a way of categorizing Access Objects, to make the user interface more usable, and the code for acl_check() more readable. They do not affect the way phpGACL determines access to an object. They cannot be nested (so it would not be able to create a "Males" sub-Section under "Humans" for example; you'd have to create a Section called "Humans-Male" or similar)

Multiple Purposes

You may need to use phpGACL for multiple independent purposes. For example, you may need to restrict user access to web pages, and also remote host access to your server. The two tasks are not related.

phpGACL can handle this in three different ways.

  • It can use an alternative database to store the access tables.

  • It can use the same database but with differently named access tables. (this feature is not implemented yet).

  • You can store the Access Objects for both purposes in the same tables, and carefully manage your list so that they don't conflict.

To implement Option 1 (and Option 2 when it becomes available), use the $gacl_options array when creating a new phpGACL class. This allows you to specify the database and table name prefixes to use:

$gacl_options = array(
'db_table_prefix' => 'gacl_',
'db_type' => 'mysql',
'db_host' => 'host1',
'db_user' => 'user',
'db_password' => 'passwd',
'db_name' => 'gacl');


$gacl_host1 = new gacl($gacl_options);

To implement Option 3, you must be careful, since phpGACL doesn't know the relationship between your different tasks, and it will be possible to make meaningless Access Policy Directives.

For example, say Han wanted to restrict access to other ships contacting his ship's computer, in addition to restricting access to the different rooms. To do this, he might add "Luke's X-Wing Fighter" as a remote ship ARO (in addition to other ships and an ACO for the ship's computer). Because all AROs are in the same ARO tree, it would be possible to create an APD like "Ships > Luke's X-Wing Fighter" [ALLOW: "Rooms > Lounge"], which would be totally meaningless! To help reduce mistakes like this, good Section naming can make it clearer what Access Objects are for which tasks. It should be obvious to any administrator that it's meaningless to assign a Ship permission to use a Room.

Access eXtension Objects

Access eXtension Objects (AXOs) can add a 3rd dimension to the permissions that can be configured in phpGACL. We've seen how phpGACL allows you to combine an ARO and an ACO (2 dimensions) to create an Access Policy Directive. This is great for simple permission requests like:

Luke (ARO) requests access to "Guns" (ACO)

If that's all you need, that's fine - AXOs are totally optional.

But because all ACOs are considered equal, it makes it difficult to manage if there are many ACOs. If this is the case, we can change the way we look at Access Objects to manage it more easily.

AXOs are identical to AROs in many respects. There is an AXO tree (separate from the ARO tree), with it's own Groups and AXOs. When dealing with AXOs, consider an AXO to take the old role of the ACO (i.e. "things to control access on"), and change the view of ACOs from "things to control access on" to "actions that are requested".

ARO and ACO-only View:

  • AROs: Things requesting access

  • ACOs: Things to control access on

ARO, ACO and AXO View:

  • AROs: Things requesting access

  • ACOs: Actions that are requested

  • AXOs: Things to control access on

Example:

A website manager is trying to manage access to projects on the website. The ARO tree consists of all the users:

Website
├─Administrators
│ ├─Alice
│ └─Carol
└─Users
├─Bob
└─Alan

The projects are organized by Operating System into categories in the AXO tree:

Projects
├─Linux
│ ├─SpamFilter2
│ └─AutoLinusWorshipper
└─Windows
├─PaperclipKiller
└─PopupStopper

The actions that can be taken with each project are "View" and "Edit". These are the ACOs.

Now we want Bob to have "View" access to all the Linux projects, so it's possible to add an ACL that links Bob's ARO to the View ACO and the Linux AXO, and thus we can ask the question:

Bob (ARO) requests access to "View" (ACO) the project(s) called "Linux" (AXO)

Keep in mind AXO's are optional, if you don't specify an AXO when calling acl_check() and a matching ACL exists with no AXO, it will be allowed. However if only ACLs exist with AXO's, and you call acl_check() without an AXO, it will fail.

So basically as soon as you specify an AXO when calling acl_check(), acl_check() will only search ACLs containing AXO's. If no AXO is specified, only ACLs without AXOs are searched. This in theory (I haven't benchmarked) gives us a slight performance increase as well.

Installation

Basic setup

  1. Untar the distribution .tar.gz file into the root or a subdirectory of your web site. You might want to rename it to something more suitable.



  2. Edit phpgacl/gacl.ini.php using your favourite editor and set the db_type, db_host, db_user, db_password, and db_name you will be using.

  3. Create the database you specified in db_name on the server.



  4. Surf to http://yoursite.net/phpgacl/setup.php. The required tables will be installed based on your choice of database. Don't be afraid of the truckload of output, if all goes well you will see only success messages.





  1. Now follow the last advice shown on that screen and create the phpgacl/admin/smarty/templates_c directory. It must be writable by the user the webserver runs as. If you don't do this, you will not be able to use the CAL admin!

  2. Click the link at the bottom of the successful setup page or surf to:
    http://yoursite.net/phpgacl/admin/acl_admin.php

Advanced setup

Reusing an already existing ADOdb installation

If you already have ADOdb installed you can get phpGACL to use this copy of ADOdb.

  1. Edit phpgacl/gacl.class.php so that ADODB_DIR reflects the location of the ADOdb library in your path.

  2. Rename the phpgacl/adodb folder to something else like adodb_x and reload the phpgacl/admin/acl_admin.php page to ensure it still works.

  3. Erase the adodb directory installed with phpGACL.

Reusing an already existing Smarty installation

If you already have ADOdb installed you can get phpGACL to use this copy of ADOdb.

  1. Edit phpgacl/admin/gacl_admin.inc.php so that the variables $smarty_dir and $smarty_compile_dir reflect the location of the Smarty library in your path and the template_c directory you already use.

    Move the templates directory that came with phpGACL to another directory (e.g. one level up). Adjust the $smarty_template_dir so it points to the new location. If you like you can move those templates to your existing templates folder, of course.

  2. Rename the phpgacl/smarty folder to something else like smarty_x and reload the phpgacl/admin/acl_admin.php page to ensure it still works.

  3. Erase the smarty directory installed with phpGACL.

How do I move the phpGACL files out of my website tree while leaving a link in the tree for administration?

  1. Go to your website root.

  2. Move the phpGACL directory to your includes directory and create a symlink to the admin directory where you want the admin tool to go. For example:

    mv phpgacl/ /www/includes_directory
    ln -s /www/includes_directory/phpgacl/admin/ gacl

  3. Now surfing to http://yoursite.net/gacl/acl_admin.php will take you to the admin page. If it doesn't work, make sure your Webserver allows symbolic links in the website tree.

Using phpGACL in your application

Basic usage

This example shows a basic example of using phpGACL in your code. It uses the ADOdb abstraction layer as well, and shows a simple way to validate a login attempt against a database.

// include basic ACL api
include('phpgacl/gacl.class.php');
$gacl = new gacl();

$username = $db->quote($_POST['username']);
$password = $db->quote(md5($_POST['password']));
$sql = 'SELECT name FROM users WHERE name=';
$sql .= $username.' AND password='.$password;
$row = $db->GetRow($sql);

if($gacl->acl_check('system','login','user',$row['name'])){
$_SESSION['username'] = $row['name'];
return true;
}
else
return false;

As you can see there is only one call to acl_check() in this code. What does it do? Well, it

  • checks the ARO object $row['name'] from the ARO section 'user'

  • against the ACO object 'login' from the ACO section 'system'.

Advanced usage



Using the ACL admin utility

If you want to get a grip on the included ACL admin utitlity, it will help you a lot if you run the example.php file. It contains some ACO, ARO and AXO objects, as well as some ACL defined using those objects. After running it, you should see some sample data in the admin interface.





Play around with it, and if you get stuck, come back and read on...

(yet to be written)

ACL's

Creating

You must have a minimum of an ACO and an ARO defined to create an ACL.

Select an ACO Section then select from the available items show in the Access Control Objects list. Click the [ > > ] button to add the Section-ACO to the Selected list. You may add any number of Section-ACO pairs to this list.




Next select an ARO Section. At this point you may select from either the Access Request Objects list or from the ARO Groups list.




Select on of the ACL Sections (usually “user” for this case), provide a brief description in the Note area and then click Submit. Click on the “ACL Admin” tab and you will see your new ACL in the list.

Sections


A default install provides you with two ACL sections – 'system' and 'user'. You would typically put user created ACL's (for example, those you enter via the admin interface) in the 'user' section and put ACL's generated by code in the 'system' section. However, you can use the ACL sections to provide any other logical grouping that suits your purposes.

Extended Return Value

Typically a call to the acl_check method will return a boolean value. However, you may specify a different value or evan a string to be returned.

For example, you may negotiate for a user to login at a cost of $0.20 per time by default and another for $0.18 per time under a different scheme. You could create a separate ACL for the default login and for the special use but varying the 'return value'. If the call to acl_check is successful, you will know the cost of the login via the return value.

Notes

It's a good idea to add a note when creating an ACL to help remember it's purpose, for example “Basic permissions for a user in the Administrator group”.




Glossary

ACO

Access Control Object – An action that are requested to be performed.

ARO

Access Request Object – An entity (for example, a user) that is requesting an action to be performed.

AXO

Access eXtension Object – An object to perform an action on for an entity.

References

phpGACL API

The API documentation is included in the tarball under the /docs/phpdoc/ directory.

phpGACL Examples and Tutorials

See example.php included in the tarball.

Access Control Resources

. . .



FAQ

Can phpGACL handle large sets of data?

Not a problem at all. We've tested up to 100,000 AXO's and 100,000 ARO's on moderate hardware even. The performance issues come down to how well you can cache the ACL's, and how fast your database server is.

phpgacl-3.3.7/docs/phpdoc/0040755025754300001440000000000010476665052014274 5ustar ipsousersphpgacl-3.3.7/docs/phpdoc/media/0040755025754300001440000000000010476665052015353 5ustar ipsousersphpgacl-3.3.7/docs/phpdoc/media/images/0040755025754300001440000000000010476665052016620 5ustar ipsousersphpgacl-3.3.7/docs/phpdoc/media/images/Constructor.png0100644025754300001440000000154010130374430021630 0ustar ipsousersPNG  IHDR(-SsBITOePLTELL<9?::UrmnfP 9ffa14++D-H>Y**.O|>7-./ǕGi\3?9UPtsa14ݽrmHڼo>YfO|7\\TdfIvApI[.sĔWDS8l>GBB<tRNSDffffI pHYs  ~tEXtCreation Time04/02/03ْZB tEXtSoftwareMacromedia Fireworks MX*$IDATxcbd;SS3+kg&E3LMW[!&ࡧodn dsȂ -퍽ؼL,\C9b]=}#kZT54s*2bjD`Fs%7 Q  0>MpLiqzY=aV\RXI*iS(k[ޡ3DND(30^4-n#IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/T.png0100644025754300001440000000022710130374430017507 0ustar ipsousersPNG  IHDR !tIME #;w! pHYs B4PLTE{RHtRNS0JIDATxc_ E:o)յIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Page_logo.png0100644025754300001440000000253010130374430021177 0ustar ipsousersPNG  IHDR DsBITOPLTE=== vvv333Zw ݳڬإ؟ؒՄno3Z2U.N(G{&Ey#Cy!Au;j5b޵ݯپرߥަްНުݛޖࢷ͕۟إŝُ͒וΒ͋ҚՍ͕˘ËNJІ~{{ytuuqltpkknhsbjbgdca~h{Uz]zZwXtTqUnNnSnLlHh=i;b9aB`7^>[.[<;|;9w85o2,d^tRNS""33DDUUUfffffffffffw\ pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxc`B|ť PY\.Ƅ7P 5&p0д>U@DfffZZZA<U4I3D3O8H.=2<1<=33^K?++2*12-:3%1005':>%5~||_OsC{{{XSC0ZO8p7.X"QԖ\-Yxns f6e=c"[=ٞWw "wN.8ҜVk1fz|:a^!"/d,GqBG6ܬ s/J&ık$6Bd^rdT6R;3pmIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Class.png0100644025754300001440000000107110130374430020347 0ustar ipsousersPNG  IHDR(-SsBITOPLTE1f3-ш&N3(k3X3DA}-qLi(R;z33!HNK~3f@/5(c_'YH3X$F3!/S~3+`-+x3U'R6k4 ?E5d5g3]-}'[D1E)JUi/.Ţ pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxMi@Ѣ(*R)J,D &DZ_dX9fȩ\A޾9gy~[]c@ z؜>=/T!P~YWs]܄Tpq1~|xlH%+ :yIB@ POQ'ko ?x,pؔIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Index.png0100644025754300001440000000110710130374430020351 0ustar ipsousersPNG  IHDR } sBITOPLTEIuOi>a晙/֨rfff#Fd5nۆȾ}Jl:zۑ*[/V뛰إ}9rۇxnPwWuѻߞ._6nz1ӎ<ֻM` pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATx=@@ᕤu+-r)Ƶ(;>ح态}ӌc v68H/A ![^^VQ"!gUh.STĩHK[ ُʲ3-hb˭uf0̵njn\חGꯩ0/JvIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/folder.png0100644025754300001440000000075310130374430020563 0ustar ipsousersPNG  IHDR(-SsBITOPLTES96 Ω+x!Orݾڼ8?WȓHݿ0kmI̮U̯6CǨ2q|S6pRvzu"&$'=H\a 3p;f92r X{7eLjCZU~&G:IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Page.png0100644025754300001440000000111710130374430020157 0ustar ipsousersPNG  IHDR(-SsBITOPLTEI}udOi/#F5nօݲۙ>a:z̬rЪ*[/VPwن9rz픫nJlWux._6n1<ǡը}O%4 pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATx=@@lIc!Q"m_L1]OeY\cr/ ' (xbYjH@jY_ÂA5mJs\ h7i s&"jB?#g'(F8>q u CSX=)<3@UH&%u-\IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/AbstractClass_logo.png0100644025754300001440000000231710130374430023057 0ustar ipsousersPNG  IHDR DsBITOPLTEȑ@{!!uqqllcc[[TTOOKKBB::33$$TqqKeeA:3*!ZZQQJJ :DD;;11))""tfZRKffssiillff``ddTT^^WWJJZZUUCCOOTTHHQQ<~Od2Z_}){es.󅽊jqexy_5s$ "TE.rZK:|8 )6M90A6A \J@pAFA;FhvgxÌjzv Eǁ8X8XZfyh^BޮE4ֲ&pkCHX_]+çٔ$IU/Z ^ yӲ'QpO7 Dz,>9yƱaat:D"ḯaz$*h IB&N@*-IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/previous_button.png0100644025754300001440000000120310130374430022546 0ustar ipsousersPNG  IHDR%leIsBITOPLTEQQQ癙3f̵z{{{Xsss?n鍍fffŒ߭Jwlփ\\\fffc֭Kx{۠, pHYsodtEXtCreation Time04/12/03s tEXtSoftwareMacromedia Fireworks MX*$@IDATxuV @tS,,099hx%mi?'@`]9 J$ՊdSŌ G8$Qъ#jiol;$tS"Ќ@3x TMSYd oMX(+4i4-=ZI9lFD,iWE.YYʚTc$h-en&3;@:s;pEqVN]"5 ^]Bα2i9դq 1*dڎA\ Ђּ@`4Cn'6RsRCm><7IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/file.png0100644025754300001440000000071510130374430020225 0ustar ipsousersPNG  IHDRha,tEXtCreation TimeTue 29 Jan 2002 20:17:29 +0100uPtIME pHYsodgAMA a%IDATxc?р񨨯Gpf744+:pH^_]`BP% ~~a0H?¥]ے޶1H!TYJ*fg/뺰yyZ~p!G{ غfHujWXOnzr(UhcnO+e)xhN érGΟ9r!(f#?z a' 1`D RdaPa`1[THT322z2IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Method.png0100644025754300001440000000122410130374430020522 0ustar ipsousersPNG  IHDR(-SsBITO#PLTEC 3f"+|=2EtrDfGXVͧƨ^KJJCbtZZZ(`h"cOXd'Z@Ze)X5[l$Z`LT`RRR(TKQ[%T\yX'Q$RIMU!QJJJ%K#MTmEIR$I!JBBB!B"B!@:::9:z8p7s3}3332k-r-d))))U"""utRNS"3DUUfffwwwwwwwwwwww pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxmSaq(:D;4-<53R < :DYN] Ls,< dH}w13gyw} 1&-_r5kǷnKh4}##vt7Y@L;s=^T>ɎEdH0{yy$Ippptt|JVAVMg[,DEEǧeh\6p40""d\\rr얦˙GT0 9Gv6111M,Vw;&R}CO+y2JSnr X*:W&0t;R1LcPk+A,Z|*ATuQ4}y) *TW#7%7ڠQ*ՙIVV[TTo>'aR{'AVާ@\aC r+Է@( wC>kk;pH` aUu5uQ03'V B|%R]w}]COoO)b̟;Mf;P8bZckd1`m;z젿H ;s/i"ZL&`?A/` |A0mX `kzȿ`J֡a+}&o p?a&;Cd/C}왬M:zy .}q).涀吱#pyq9l g2Yr1@_ZIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/PrivateVariable.png0100644025754300001440000000140310130374430022361 0ustar ipsousersPNG  IHDR6sBITOhPLTE333[2YO@~{heгEfff.ᥐfuοBQQQzBZkrѻB+݇l1@wŤ2\fueԍЉVOㄲc5t̯ws^KxܼktttyEEEߟ/eXXXrCooײRkH]?tƢJIᗍYridTm(mf̅zzz};Xmm1DvQ~===jY;z`[QΨNUsdyk6cַG~L޲Y[RH`]WW/呌WΫJҺ pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxcq62MwkL6(?8..WFWf+JTQQex̤=ty@|[$tGC6 @R02[a`cS4 IMHs`cQ.΋PPp/`c`c- aOSPHIacЍ(U`Tp WaP76 0Otoe-Q"30$Dͭnĭq?0rCa,?zIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/PrivateClass_logo.png0100644025754300001440000000345310130374430022730 0ustar ipsousersPNG  IHDR DsBITOPLTE8rJJJ唾|sqmfXHJJJ:::|||8t333)))"""怨yrlfaZTMCz3h*d'Z(WBBB:::2k֐ztk–cZ_RfWJI𵵵UuHB`WN@]ErkPMEQ9jDJ;9팢dr:@H]憜l<42h1^Uバ3;䃏d/-`Kۅ1+1W-10fM|||)0.,sss%/|-|*{bqDtPu8t#}1y-s)tfff*pZfu8m*j(l q3h$l9eTalm*d*bJ`r>btZZZ(`h"cOXd'Z@Ze)X5[l$Z`LT`RRR(TKQ[%T\yX'Q$RIMU!QJJJ%K#MTmEIR$I!JBBB!B"B!@:::9:z8p7s3}3332k-r-d))))U"""utRNS"3DUUfffwwwwwwwwwwww pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxmSaq(:D;4-<53R < :DYN] Ls,< dH}w13gyw} 1&-_r5kǷnKh4}##vt7Y@L;s=^T>ɎEdH0{yy$Ippptt|JVAVMg[,DEEǧeh\6p40""d\\rr얦˙GT0 9Gv6111M,Vw;&R}CO+y2JSnr X*:W&0t;R1LcPk+A,Z|*ATuQ4}y) *TW#7%7ڠQ*ՙIVV[TTo>'aR{'AVާ@\aC r+Է@( wC>kk;pH` aUu5uQ03'V B|%R]w}]COoO)b̟;Mf;P8bZckd1`m;z젿H ;s/i"ZL&`?A/` |A0mX `kzȿ`J֡a+}&o p?a&;Cd/C}왬M:zy .}q).涀吱#pyq9l g2Yr1@_ZIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/AbstractPrivateClass_logo.png0100644025754300001440000000311610130374430024410 0ustar ipsousersPNG  IHDR DsBITOPLTEx::: BBBZZ333))) |||ZZTTJJBB9900**''!!ysK:::fZRK;½yy}}ffss~~{{iill|||``ffssTTddssccyttXX^^sssJJZZOOUUCCTTHHfffZZ<R TC9q"21h1ȴYӹh4B?8.D cu'-Vq\0 NF$ #EbBO} 43l2_OV sn|zPXDPpFo^?kf{1ys zs A_T0Sy;kLwzc넭DdZŰA$Jb`@_ )};@ Gx(3ZbD! $0 2pT1Q?g!^&dE 0x^S`rfDAR,f*h ("H1 _QBhTq-@_K-IQͰ1)NJd SB4a,ݣ'kZh&GREe?-+SFy{vȂJ>@@0033HHH** *3vVV333ķkk{{{ooaasssffPPfff[[PPUU33BBhZZaZZZZZ;;@@''88[SSQQQ11PLL::00)),,CCC""(( S00333n""'''zrf(gtRNS""3DDUffwww pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATx-Z@@rWIc&6bjR&1+ DߣZ}{c_>W50P&sK2#yP]JAl "We BX *669B! @>s*"L >ķbb* .IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/AbstractClass.png0100644025754300001440000000115310130374430022034 0ustar ipsousersPNG  IHDR(-SsBITOPLTEˢ}}{{ffYYPPKKBB<<00 faaPP33BBBB''881100))''!! {rfV?>tRNS""3DUfffwww pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxMkB0᭭#I30_gWBn x2`q]I1K{)AX(8OQCk: װ c~a NB'QT  , 5-YU3FL ˜~? ? IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/package.png0100644025754300001440000000123310130374430020675 0ustar ipsousersPNG  IHDR(-SsBITOPLTEE/ՐpJ`{ıKޡ!gGUgşMfÄw&n"Z>Zɾݞj 9t!9;f6jmό+U:{(2uPfxɨZl/Q cD?y˶Ōs@¿~ߗnʈ` I2Qߤ)mqM!ȶ2ޭ1yS^1f pHYs  ~ tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATx]aOPcZ#EÊi]vfn"ʔ }/Rmճ߻?lM(XrZq9tô0>t@m;mf_Fpp6"\4ZBHSJ&If35\Vcm4Vp~}_ ÷(b<|w7"Ӵ/<ٞK$X‚rSđ#o? ;{IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/class_folder.png0100644025754300001440000000117010130374430021742 0ustar ipsousersPNG  IHDR(-SsBITOPLTE^3f&#Gї_3pkFX̮/CKUf+Op3z23?waH:s;޵<3z3v ,XɮY\ַ7#:3OC8rtR_f'Nl֝|F3Hܹ5EX1zqBɭ/3?[t2ϴ3TUּi+(U^WtiXD=R+$H=Ka{E*nӡN:; IC^+ۃ~nX&2ҙ?9IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/next_button.png0100644025754300001440000000122010130374430021647 0ustar ipsousersPNG  IHDR%leIsBITOPLTEQQQ虙3fyW{{{sss?n΍fffܥ탃ֳ\\\oJwfffd{٭޻,3W_Mk7=7Y]u,1~u CY(W;ExBBC A|*q;J^[6DXLp>d+i"m} -^cW4/*eIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Global.png0100644025754300001440000000130710130374430020504 0ustar ipsousersPNG  IHDR(-SsBITO,PLTEVSJtˮ@www|Te宄k0yRխcEzc.ᥪͲD\ucڊlh^ٌxJ>|ZßJŅJuCxlYRΥRa^Yuu>o`ˁ~zfy;ܺUkWOSs pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxcpEN(DpBt-\]=}8Afڞ"22zL@m^ Տr wc qCT9b=edMBCT}D@@A? D^(PBGI bad40HaSI~7s99UdMx[c.i7N D>CHeIFNӥU\ԄmE|mؕ#X@|o9ŹAk/6IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/AbstractMethod.png0100644025754300001440000000126710130374430022215 0ustar ipsousersPNG  IHDR(-SsBITO>PLTEC %5hh>>#,eetr%h(o \Aѷ?3NLYYJJO8§$_(33 ͙sC+2J3DfG:l:??or:333HHɀ*1^KIwI՘&*X NwH(1nn$!(w$j䄬IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Tplus.png0100644025754300001440000000033510130374430020413 0ustar ipsousersPNG  IHDRYQtIME '*I pHYs B40PLTE{y8tRNS0J3IDATxcE! &6Q% P0!J LFlLFEEE & IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Lminus.png0100644025754300001440000000033210130374430020550 0ustar ipsousersPNG  IHDRYQtIME %# pHYs B40PLTE{y8tRNS0J0IDATxcE! &6Q% P0!J0Qd.04i 7gIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/function_folder.png0100644025754300001440000000113410130374430022462 0ustar ipsousersPNG  IHDR esBITOPLTE*l &v їf9̮/R#J/^8(^4 C=X>=0 c̯Xš%;rJALH׻:޵<,A72)|my O2G?}¡9=T)_=3RB֝N!ȭ04C=CT9C[3@Lc84k9:*X0D2ϴ3*}JA=:BP0,kaL pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATx=i@%"IMQ3p{|y3( ,'`5 7:|@B)?S9iԴS+ܥ&*P,}R差,;/} Xr2`C]BQ[OQ|PaC@pğd:=TuIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/PrivateMethod.png0100644025754300001440000000162510130374430022062 0ustar ipsousersPNG  IHDR(-SsBITOPLTE yLGR )Jb33i^K?++2\?>*1U2-U:3OŌ%1P005'qa3̅4H%5:>WVYm{{{XSC6ʜ *n)U 3U*U Kk  :%;}J}*`e/f o.kߒ^IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Tminus.png0100644025754300001440000000031610130374430020562 0ustar ipsousersPNG  IHDRYQtIME &b l pHYs B40PLTE{`*4tRNS@f%IDATxc`l  3HwMN,]IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/up_button.png0100644025754300001440000000123310130374430021321 0ustar ipsousersPNG  IHDR%leIsBITOPLTEQQQ㙙3f{Vӽ{{{sss?n΍fffũñcKw\\\޴fff櫾䌥pWKxݎ pHYsod tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/12/03sUIDATx} o quڭ(nXQLln4%}r#s^S;hmn$M&KX Zq> :X! E:0JkXjYU[a!` RXVOɬ[ձ3T-M^5^_;^Cfq:>B6\l:;Yʊ5̬3%ԟ[ .ֈZ| K$Nc Sۖ8ksh΀oOŖ9׸(frՋ֟B &ph‰į& ^1D@4CW@P<Αv2Fղ.IENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Variable.png0100644025754300001440000000125710130374430021035 0ustar ipsousersPNG  IHDR(-SsBITO)PLTEl1/(mUгEz@wyŤeDu32fܼk،5tͮƢJOЉLemmvHB޲YנyovQCctҿV֓zB6c.ݕLyIa"Ew?f@]նKeOZrom/6a;ΨNk1𵫏ڻQ_ޚkpHײR݇ԍrWꓴPWΫJr/Rd pHYs B4 tEXtSoftwareMacromedia Fireworks MX*$tEXtCreation Time04/02/03ْZBIDATxc`aS1pec9)3mq#9Gw'=T@YQU,*JG "lΝ up +9(jh(q,Baa.r ,lr&v@0_O, ' 9 xu\fvIC- %z.A l8>p-VKM~AKIENDB`phpgacl-3.3.7/docs/phpdoc/media/images/Class_logo.png0100644025754300001440000000307710130374430021377 0ustar ipsousersPNG  IHDR ?sBITOOPLTE5Sr唾ہ|qmfXH*[Cs5\ '(F#@: 2 . '8tNJ耭yrlfbZTMCy1n'[$YztkcZRfJWIBWx=Zr`ME3SkM]v*y1u0r-t*s)t-n'q1n$yW([o0} k?$ s1 ;]nBG|[?P]G@=)YOpl*|n+ȷrFo뛺`9BP) ڸ?:ԙ@ W\T^9a´i3,X|ͦ(O,o

[phpGACL] element index

All elements
a c d e f g i p r s _
_
Variable $_cache_dir
gacl::$_cache_dir in gacl.class.php
Variable $_cache_expire_time
gacl::$_cache_expire_time in gacl.class.php
Variable $_caching
gacl::$_caching in gacl.class.php
Variable $_db
gacl::$_db in gacl.class.php
Variable $_db_host
gacl::$_db_host in gacl.class.php
Variable $_db_name
gacl::$_db_name in gacl.class.php
Variable $_db_password
gacl::$_db_password in gacl.class.php
Variable $_db_table_prefix
gacl::$_db_table_prefix in gacl.class.php
Variable $_db_type
gacl::$_db_type in gacl.class.php
Variable $_db_user
gacl::$_db_user in gacl.class.php
Variable $_debug
gacl::$_debug in gacl.class.php
Variable $_force_cache_expire
gacl::$_force_cache_expire in gacl.class.php
Variable $_group_switch
gacl::$_group_switch in gacl.class.php
Method _rebuild_tree
gacl_api::_rebuild_tree() in gacl_api.class.php
_rebuild_tree ()
a
Method acl_check
gacl::acl_check() in gacl.class.php
Wraps the actual acl_query() function.
Method acl_check_array
gacl::acl_check_array() in gacl.class.php
Handles ACL lookups over arrays of AROs
Method acl_get_groups
gacl::acl_get_groups() in gacl.class.php
Grabs all groups mapped to an ARO. You can also specify a root_group for subtree'ing.
Method acl_query
gacl::acl_query() in gacl.class.php
The Main function that does the actual ACL lookup.
Method acl_return_value
gacl::acl_return_value() in gacl.class.php
Wraps the actual acl_query() function.
Method add_acl
gacl_api::add_acl() in gacl_api.class.php
add_acl()
Method add_group
gacl_api::add_group() in gacl_api.class.php
add_group()
Method add_group_object
gacl_api::add_group_object() in gacl_api.class.php
add_group_object()
Method add_object
gacl_api::add_object() in gacl_api.class.php
add_object()
Method add_object_section
gacl_api::add_object_section() in gacl_api.class.php
add_object_section()
Constant ADODB_DIR
ADODB_DIR in gacl.class.php
Method append_acl
gacl_api::append_acl() in gacl_api.class.php
append_acl()
c
Variable $config_file
gacl::$config_file in gacl.class.php
Method clear_database
gacl_api::clear_database() in gacl_api.class.php
clear_database()
Method consolidated_edit_acl
gacl_api::consolidated_edit_acl() in gacl_api.class.php
consolidated_edit_acl()
Method count_all
gacl_api::count_all() in gacl_api.class.php
count_all()
d
Method debug_db
gacl::debug_db() in gacl.class.php
Prints database debug text if debug is enabled.
Method debug_text
gacl::debug_text() in gacl.class.php
Prints debug text if debug is enabled.
Method del_acl
gacl_api::del_acl() in gacl_api.class.php
del_acl()
Method del_group
gacl_api::del_group() in gacl_api.class.php
del_group()
Method del_group_object
gacl_api::del_group_object() in gacl_api.class.php
del_group_object()
Method del_object
gacl_api::del_object() in gacl_api.class.php
del_object()
Method del_object_section
gacl_api::del_object_section() in gacl_api.class.php
del_object_section()
e
Method edit_acl
gacl_api::edit_acl() in gacl_api.class.php
edit_acl()
Method edit_group
gacl_api::edit_group() in gacl_api.class.php
edit_group()
Method edit_object
gacl_api::edit_object() in gacl_api.class.php
edit_object()
Method edit_object_section
gacl_api::edit_object_section() in gacl_api.class.php
edit_object_section()
f
Method format_groups
gacl_api::format_groups() in gacl_api.class.php
format_groups()
g
Class gacl
gacl in gacl.class.php
phpGACL main class
Method gacl
gacl::gacl() in gacl.class.php
Constructor
Class gacl_api
gacl_api in gacl_api.class.php
gacl_api Extended API Class
Page gacl_api.class.php
gacl_api.class.php in gacl_api.class.php
Page gacl.class.php
gacl.class.php in gacl.class.php
Method get_acl
gacl_api::get_acl() in gacl_api.class.php
get_acl()
Method get_cache
gacl::get_cache() in gacl.class.php
Uses PEAR's Cache_Lite package to grab cached arrays, objects, variables etc...
Method get_group_children
gacl_api::get_group_children() in gacl_api.class.php
get_group_children()
Method get_group_data
gacl_api::get_group_data() in gacl_api.class.php
get_group_data()
Method get_group_id
gacl_api::get_group_id() in gacl_api.class.php
get_group_id()
Method get_group_objects
gacl_api::get_group_objects() in gacl_api.class.php
get_group_objects()
Method get_group_parent_id
gacl_api::get_group_parent_id() in gacl_api.class.php
get_group_parent_id()
Method get_object
gacl_api::get_object() in gacl_api.class.php
get_object()
Method get_objects
gacl_api::get_objects() in gacl_api.class.php
get_objects ()
Method get_object_data
gacl_api::get_object_data() in gacl_api.class.php
get_object_data()
Method get_object_groups
gacl_api::get_object_groups() in gacl_api.class.php
get_object_groups()
Method get_object_id
gacl_api::get_object_id() in gacl_api.class.php
get_object_id()
Method get_object_section_section_id
get_object_section_section_id()
Method get_object_section_value
get_object_section_value()
Method get_root_group_id
gacl_api::get_root_group_id() in gacl_api.class.php
get_root_group_id ()
Method get_schema_version
gacl_api::get_schema_version() in gacl_api.class.php
get_schema_version()
Method get_section_data
gacl_api::get_section_data() in gacl_api.class.php
get_section_data()
Method get_ungrouped_objects
gacl_api::get_ungrouped_objects() in gacl_api.class.php
get_ungrouped_objects()
Method get_version
gacl_api::get_version() in gacl_api.class.php
get_version()
i
Method is_conflicting_acl
gacl_api::is_conflicting_acl() in gacl_api.class.php
is_conflicting_acl()
p
Method put_cache
gacl::put_cache() in gacl.class.php
Uses PEAR's Cache_Lite package to write cached arrays, objects, variables etc...
r
Method rebuild_tree
gacl_api::rebuild_tree() in gacl_api.class.php
rebuild_tree ()
s
Method search_acl
gacl_api::search_acl() in gacl_api.class.php
search_acl()
Method shift_acl
gacl_api::shift_acl() in gacl_api.class.php
shift_acl()
Method showarray
gacl_api::showarray() in gacl_api.class.php
showarray()
Method sort_groups
gacl_api::sort_groups() in gacl_api.class.php
sort_groups()
a c d e f g i p r s _
phpgacl-3.3.7/docs/phpdoc/errors.html0100644025754300001440000000165410476664473016507 0ustar ipsousers phpDocumentor Parser Errors and Warnings Post-parsing

gacl.class.php

Warnings:


Warning on line 37 - Page-level DocBlock precedes "define ADODB_DIR", use another DocBlock to document the source element

Documentation generated on Sun, 03 Sep 2006 16:23:52 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/elementindex.html0100644025754300001440000010143710344402167017634 0ustar ipsousers

Full index

Package indexes


a c d e f g i p r s _
a
Method acl_check
gacl::acl_check() in gacl.class.php
Wraps the actual acl_query() function.
Method acl_check_array
gacl::acl_check_array() in gacl.class.php
Handles ACL lookups over arrays of AROs
Method acl_get_groups
gacl::acl_get_groups() in gacl.class.php
Grabs all groups mapped to an ARO. You can also specify a root_group for subtree'ing.
Method acl_query
gacl::acl_query() in gacl.class.php
The Main function that does the actual ACL lookup.
Method acl_return_value
gacl::acl_return_value() in gacl.class.php
Wraps the actual acl_query() function.
Method add_acl
gacl_api::add_acl() in gacl_api.class.php
add_acl()
Method add_group
gacl_api::add_group() in gacl_api.class.php
add_group()
Method add_group_object
gacl_api::add_group_object() in gacl_api.class.php
add_group_object()
Method add_object
gacl_api::add_object() in gacl_api.class.php
add_object()
Method add_object_section
gacl_api::add_object_section() in gacl_api.class.php
add_object_section()
Constant ADODB_DIR
ADODB_DIR in gacl.class.php
Method append_acl
gacl_api::append_acl() in gacl_api.class.php
append_acl()
c
Variable $config_file
gacl::$config_file in gacl.class.php
Method clear_database
gacl_api::clear_database() in gacl_api.class.php
clear_database()
Method consolidated_edit_acl
gacl_api::consolidated_edit_acl() in gacl_api.class.php
consolidated_edit_acl()
Method count_all
gacl_api::count_all() in gacl_api.class.php
count_all()
d
Method debug_db
gacl::debug_db() in gacl.class.php
Prints database debug text if debug is enabled.
Method debug_text
gacl::debug_text() in gacl.class.php
Prints debug text if debug is enabled.
Method del_acl
gacl_api::del_acl() in gacl_api.class.php
del_acl()
Method del_group
gacl_api::del_group() in gacl_api.class.php
del_group()
Method del_group_object
gacl_api::del_group_object() in gacl_api.class.php
del_group_object()
Method del_object
gacl_api::del_object() in gacl_api.class.php
del_object()
Method del_object_section
gacl_api::del_object_section() in gacl_api.class.php
del_object_section()
e
Method edit_acl
gacl_api::edit_acl() in gacl_api.class.php
edit_acl()
Method edit_group
gacl_api::edit_group() in gacl_api.class.php
edit_group()
Method edit_object
gacl_api::edit_object() in gacl_api.class.php
edit_object()
Method edit_object_section
gacl_api::edit_object_section() in gacl_api.class.php
edit_object_section()
f
Method format_groups
gacl_api::format_groups() in gacl_api.class.php
format_groups()
g
Class gacl
gacl in gacl.class.php
phpGACL main class
Method gacl
gacl::gacl() in gacl.class.php
Constructor
Class gacl_api
gacl_api in gacl_api.class.php
gacl_api Extended API Class
Page gacl_api.class.php
gacl_api.class.php in gacl_api.class.php
Page gacl.class.php
gacl.class.php in gacl.class.php
Method get_acl
gacl_api::get_acl() in gacl_api.class.php
get_acl()
Method get_cache
gacl::get_cache() in gacl.class.php
Uses PEAR's Cache_Lite package to grab cached arrays, objects, variables etc...
Method get_group_children
gacl_api::get_group_children() in gacl_api.class.php
get_group_children()
Method get_group_data
gacl_api::get_group_data() in gacl_api.class.php
get_group_data()
Method get_group_id
gacl_api::get_group_id() in gacl_api.class.php
get_group_id()
Method get_group_objects
gacl_api::get_group_objects() in gacl_api.class.php
get_group_objects()
Method get_group_parent_id
gacl_api::get_group_parent_id() in gacl_api.class.php
get_group_parent_id()
Method get_object
gacl_api::get_object() in gacl_api.class.php
get_object()
Method get_objects
gacl_api::get_objects() in gacl_api.class.php
get_objects ()
Method get_object_data
gacl_api::get_object_data() in gacl_api.class.php
get_object_data()
Method get_object_groups
gacl_api::get_object_groups() in gacl_api.class.php
get_object_groups()
Method get_object_id
gacl_api::get_object_id() in gacl_api.class.php
get_object_id()
Method get_object_section_section_id
get_object_section_section_id()
Method get_object_section_value
get_object_section_value()
Method get_root_group_id
gacl_api::get_root_group_id() in gacl_api.class.php
get_root_group_id ()
Method get_schema_version
gacl_api::get_schema_version() in gacl_api.class.php
get_schema_version()
Method get_section_data
gacl_api::get_section_data() in gacl_api.class.php
get_section_data()
Method get_ungrouped_objects
gacl_api::get_ungrouped_objects() in gacl_api.class.php
get_ungrouped_objects()
Method get_version
gacl_api::get_version() in gacl_api.class.php
get_version()
i
Method is_conflicting_acl
gacl_api::is_conflicting_acl() in gacl_api.class.php
is_conflicting_acl()
p
Method put_cache
gacl::put_cache() in gacl.class.php
Uses PEAR's Cache_Lite package to write cached arrays, objects, variables etc...
r
Method rebuild_tree
gacl_api::rebuild_tree() in gacl_api.class.php
rebuild_tree ()
s
Method search_acl
gacl_api::search_acl() in gacl_api.class.php
search_acl()
Method shift_acl
gacl_api::shift_acl() in gacl_api.class.php
shift_acl()
Method showarray
gacl_api::showarray() in gacl_api.class.php
showarray()
Method sort_groups
gacl_api::sort_groups() in gacl_api.class.php
sort_groups()
_
Variable $_cache_dir
gacl::$_cache_dir in gacl.class.php
Variable $_cache_expire_time
gacl::$_cache_expire_time in gacl.class.php
Variable $_caching
gacl::$_caching in gacl.class.php
Variable $_db
gacl::$_db in gacl.class.php
Variable $_db_host
gacl::$_db_host in gacl.class.php
Variable $_db_name
gacl::$_db_name in gacl.class.php
Variable $_db_password
gacl::$_db_password in gacl.class.php
Variable $_db_table_prefix
gacl::$_db_table_prefix in gacl.class.php
Variable $_db_type
gacl::$_db_type in gacl.class.php
Variable $_db_user
gacl::$_db_user in gacl.class.php
Variable $_debug
gacl::$_debug in gacl.class.php
Variable $_force_cache_expire
gacl::$_force_cache_expire in gacl.class.php
Variable $_group_switch
gacl::$_group_switch in gacl.class.php
Method _rebuild_tree
gacl_api::_rebuild_tree() in gacl_api.class.php
_rebuild_tree ()
a c d e f g i p r s _
phpgacl-3.3.7/docs/phpdoc/packages.html0100644025754300001440000000161310130374430016716 0ustar ipsousers phpgacl-3.3.7/docs/phpdoc/phpGACL/0040755025754300001440000000000010476665052015512 5ustar ipsousersphpgacl-3.3.7/docs/phpdoc/phpGACL/gacl_api.html0100644025754300001440000035334110476664473020153 0ustar ipsousers Docs For Class gacl_api

 Class gacl_api

Description

gacl_api Extended API Class

Class gacl_api should be used for applications that must interface directly with phpGACL's data structures, objects, and rules.

Located in /gacl_api.class.php (line 50)

gacl
   |
   --gacl_api
Method Summary
 bool add_acl (array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [int $allow = 1], [int $enabled = 1], [string $return_value = NULL], [string $note = NULL], [string $section_value = NULL], [int $acl_id = FALSE])
 int add_group (string $value, string $name, int $parent_id, [string $group_type = 'ARO'])
 bool add_group_object (int $group_id, string $object_section_value, string $object_value, [string $group_type = 'ARO'])
 int add_object (string $section_value, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
 int add_object_section (string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
 bool append_acl (int $acl_id, [array $aro_array = NULL], [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $aco_array = NULL])
 bool clear_database ()
 bool consolidated_edit_acl (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, string $return_value)
 int count_all ([array $arg = NULL])
 bool del_acl (int $acl_id)
 bool del_group (int $group_id, [bool $reparent_children = TRUE], [string $group_type = 'ARO'])
 bool del_group_object (int $group_id, string $object_section_value, string $object_value, [string $group_type = 'ARO'])
 bool del_object (int $object_id, [string $object_type = NULL], [bool $erase = FALSE])
 bool del_object_section (int $object_section_id, [string $object_type = NULL], [bool $erase = FALSE])
 bool edit_acl (int $acl_id, array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [int $allow = 1], [int $enabled = 1], [string $return_value = NULL], [string $note = NULL], [string $section_value = NULL])
 bool edit_group (int $group_id, [string $value = NULL], [string $name = NULL], [int $parent_id = NULL], [string $group_type = 'ARO'])
 bool edit_object (int $object_id, string $section_value, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
 bool edit_object_section (int $object_section_id, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
 array format_groups (array $sorted_groups, [array $type = 'TEXT'], int $root_id, int $level, [array $formatted_groups = NULL])
 bool get_acl (int $acl_id)
 array get_group_children (int $group_id, [int $group_type = 'ARO'], [string $recurse = 'NO_RECURSE'])
 array get_group_data (int $group_id, [string $group_type = 'ARO'])
 int get_group_id ([string $value = NULL], [string $name = NULL], [string $group_type = 'ARO'])
 array get_group_objects (int $group_id, [string $group_type = 'ARO'], [string $option = 'NO_RECURSE'])
 int get_group_parent_id (int $id, [string $group_type = 'ARO'])
 ADORecordSet get_object ([string $section_value = null], [int $return_hidden = 1], [string $object_type = NULL])
 array get_objects ([string $section_value = NULL], [int $return_hidden = 1], [string $object_type = NULL])
 array get_object_data (int $object_id, [string $object_type = NULL])
 array get_object_groups (int $object_id, [string $object_type = 'ARO'], [string $option = 'NO_RECURSE'])
 int get_object_id (string $section_value, string $value, [string $object_type = NULL])
 int get_object_section_section_id ([string $name = NULL], [string $value = NULL], [string $object_type = NULL])
 string get_object_section_value (int $object_id, [string $object_type = NULL])
 int get_root_group_id ([string $group_type = 'ARO'])
 string get_schema_version ()
 array get_section_data (string $section_value, [string $object_type = NULL])
 array get_ungrouped_objects ([int $return_hidden = 1], [string $object_type = NULL])
 string get_version ()
 bool is_conflicting_acl (array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $ignore_acl_ids = NULL])
 bool rebuild_tree ([string $group_type = 'ARO'], [int $group_id = NULL], [int $left = 1])
 array search_acl ([string $aco_section_value = NULL], [string $aco_value = NULL], [string $aro_section_value = NULL], [string $aro_value = NULL], [string $aro_group_name = NULL], [string $axo_section_value = NULL], [string $axo_value = NULL], [string $axo_group_name = NULL], [string $return_value = NULL])
 bool shift_acl (int $acl_id, [array $aro_array = NULL], [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $aco_array = NULL])
 void showarray (array $array)
 array sort_groups ([string $group_type = 'ARO'])
 int _rebuild_tree (string $table, int $group_id, [int $left = 1])
Methods
add_acl (line 921)

add_acl()

Add's an ACL. ACO_IDS, ARO_IDS, GROUP_IDS must all be arrays.

  • return: Return ACL ID of new ACL if successful, FALSE otherewise.
bool add_acl (array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [int $allow = 1], [int $enabled = 1], [string $return_value = NULL], [string $note = NULL], [string $section_value = NULL], [int $acl_id = FALSE])
  • array $aco_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_group_ids: Array of Group IDs
  • array $axo_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $axo_group_ids: Array of Group IDs
  • int $allow: Allow flag
  • int $enabled: Enabled flag
  • string $return_value: Return Value
  • string $note: Note
  • string $section_value: ACL Section Value
  • int $acl_id: ACL ID # Specific Request
add_group (line 1684)

add_group()

Inserts a group, defaults to be on the "root" branch.

Since v3.3.x you can only create one group with Parent_ID=0 So, its a good idea to create a "Virtual Root" group with Parent_ID=0 Then assign other groups to that.

  • return: New Group ID # if successful, FALSE if otherwise.
int add_group (string $value, string $name, int $parent_id, [string $group_type = 'ARO'])
  • string $value: Group Value
  • string $name: Group Name
  • int $parent_id: Parent Group ID #
  • string $group_type: Group Type, either 'ARO' or 'AXO'
add_group_object (line 1878)

add_group_object()

Assigns an Object to a group

  • return: Returns TRUE if successful, FALSE otherwise.
bool add_group_object (int $group_id, string $object_section_value, string $object_value, [string $group_type = 'ARO'])
  • int $group_id: Group ID #
  • string $object_section_value: Object Section Value
  • string $object_value: Object Value
  • string $group_type: Group Type, either 'ARO' or 'AXO'
add_object (line 2951)

add_object()

Inserts a new object

  • return: Returns the ID # of the new object if successful, FALSE otherwise
int add_object (string $section_value, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
  • string $section_value: Object Section Value
  • string $name: Object Name
  • string $value: Object Value
  • int $order: Display Order
  • int $hidden: Hidden Flag, either 1 to hide, or 0 to show.
  • string $object_type: Object Type, either 'ACO', 'ARO', or 'AXO'
add_object_section (line 3429)

add_object_section()

Inserts an object Section

  • return: Object Section ID of new section
int add_object_section (string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
  • string $name: Object Name
  • string $value: Object Value
  • int $order: Display Order
  • int $hidden: Hidden flag, hides section if 1, shows section if 0
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL'
append_acl (line 413)

append_acl()

Appends objects on to a specific ACL.

  • return: TRUE if successful, FALSE otherwise.
bool append_acl (int $acl_id, [array $aro_array = NULL], [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $aco_array = NULL])
  • int $acl_id: ACL ID #
  • array $aro_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_group_ids: Array of Group IDs
  • array $axo_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $axo_group_ids: Array of Group IDs
  • array $aco_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
clear_database (line 3782)

clear_database()

Deletes all data from the phpGACL tables. USE WITH CAUTION.

  • return: Returns TRUE if successful, FALSE otherwise
bool clear_database ()
consolidated_edit_acl (line 156)

consolidated_edit_acl()

Add's an ACL but checks to see if it can consolidate it with another one first.

This ONLY works with ACO's and ARO's. Groups, and AXO are excluded. As well this function is designed for handling ACLs with return values, and consolidating on the return_value, in hopes of keeping the ACL count to a minimum.

A return value of false must _always_ be handled outside this function. As this function will remove AROs from ACLs and return false, in most cases you will need to a create a completely new ACL on a false return.

  • return: Special boolean return value. See note.
bool consolidated_edit_acl (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, string $return_value)
  • string $aco_section_value: ACO Section Value
  • string $aco_value: ACO Value
  • string $aro_section_value: ARO Section Value
  • string $aro_value: ARO Value
  • string $return_value: Return Value of ACL
count_all (line 84)

count_all()

Recursively counts elements in an array and sub-arrays.

This is different from count($arg, COUNT_RECURSIVE) in PHP >= 4.2.0, which includes sub-arrays in the count.

  • return: The returned count is a count of all scalar elements found.
int count_all ([array $arg = NULL])
  • array $arg: Array to count
del_acl (line 1189)

del_acl()

Deletes a given ACL

  • return: Returns TRUE if successful, FALSE otherwise.
bool del_acl (int $acl_id)
  • int $acl_id: ACL ID # to delete
del_group (line 2248)

del_group()

deletes a given group

  • return: Returns TRUE if successful, FALSE otherwise.
bool del_group (int $group_id, [bool $reparent_children = TRUE], [string $group_type = 'ARO'])
  • int $group_id: Group ID #
  • bool $reparent_children: If TRUE, child groups of this group will be reparented to the current group's parent.
  • string $group_type: Group Type, either 'ARO' or 'AXO'
del_group_object (line 1965)

del_group_object()

Removes an Object from a group.

  • return: Returns TRUE if successful, FALSE otherwise
bool del_group_object (int $group_id, string $object_section_value, string $object_value, [string $group_type = 'ARO'])
  • int $group_id: Group ID #
  • string $object_section_value: Object Section Value
  • string $object_value: Object Value
  • string $group_type: Group Type, either 'ARO' or 'AXO'
del_object (line 3144)

del_object()

Deletes a given Object and, if instructed to do so, erase all referencing objects

ERASE feature by: Martino Piccinato

  • return: Returns TRUE if successful, FALSE otherwise.
bool del_object (int $object_id, [string $object_type = NULL], [bool $erase = FALSE])
  • int $object_id: Object ID #
  • string $object_type: Object Type, either 'ACO', 'ARO', or 'AXO'
  • bool $erase: Erases all referencing objects if TRUE, leaves them alone otherwise.
del_object_section (line 3635)

del_object_section()

Deletes a given Object Section and, if explicitly asked, all the section objects

ERASE feature by: Martino Piccinato

  • return: Returns TRUE if successful, FALSE otherwise
bool del_object_section (int $object_section_id, [string $object_type = NULL], [bool $erase = FALSE])
  • int $object_section_id: Object Section ID # to delete
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL'
  • bool $erase: Erases all section objects assigned to the section
edit_acl (line 1145)

edit_acl()

Edit's an ACL, ACO_IDS, ARO_IDS, GROUP_IDS must all be arrays.

  • return: Return TRUE if successful, FALSE otherewise.
bool edit_acl (int $acl_id, array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [int $allow = 1], [int $enabled = 1], [string $return_value = NULL], [string $note = NULL], [string $section_value = NULL])
  • int $acl_id: ACL ID # to edit
  • array $aco_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_group_ids: Array of Group IDs
  • array $axo_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $axo_group_ids: Array of Group IDs
  • int $allow: Allow flag
  • int $enabled: Enabled flag
  • string $return_value: Return Value
  • string $note: Note
  • string $section_value: ACL Section Value
edit_group (line 2024)

edit_group()

Edits a group

  • return: Returns TRUE if successful, FALSE otherwise
bool edit_group (int $group_id, [string $value = NULL], [string $name = NULL], [int $parent_id = NULL], [string $group_type = 'ARO'])
  • int $group_id: Group ID #
  • string $value: Group Value
  • string $name: Group Name
  • int $parent_id: Parent ID #
  • string $group_type: Group Type, either 'ARO' or 'AXO'
edit_object (line 3051)

edit_object()

Edits a given Object

  • return: Returns TRUE if successful, FALSE otherwise
bool edit_object (int $object_id, string $section_value, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
  • int $object_id: Object ID #
  • string $section_value: Object Section Value
  • string $name: Object Name
  • string $value: Object Value
  • int $order: Display Order
  • int $hidden: Hidden Flag, either 1 to hide, or 0 to show
  • string $object_type: Object Type, either 'ACO', 'ARO', or 'AXO'
edit_object_section (line 3498)

edit_object_section()

Edits a given Object Section

  • return: Returns TRUE if successful, FALSE otherwise
bool edit_object_section (int $object_section_id, string $name, string $value, int $order, int $hidden, [string $object_type = NULL])
  • int $object_section_id: Object Section ID #
  • string $name: Object Section Name
  • string $value: Object Section Value
  • int $order: Display Order
  • int $hidden: Hidden Flag, hide object section if 1, show if 0
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL'
format_groups (line 1300)

format_groups()

Takes the array returned by sort_groups() and formats for human consumption. Recursively calls itself to produce the desired output.

  • return: Array of formatted text, ordered by group id, formatted according to $type
array format_groups (array $sorted_groups, [array $type = 'TEXT'], int $root_id, int $level, [array $formatted_groups = NULL])
  • array $sorted_groups: Output from gacl_api->sorted_groups($group_type)
  • array $type: Output type desired, either 'TEXT', 'HTML', or 'ARRAY'
  • int $root_id: Root of tree to produce
  • int $level: Current level of depth
  • array $formatted_groups: Pass the current formatted groups object for appending via recursion.
get_acl (line 690)

get_acl()

Grabs ACL data.

  • return:

    FALSE if not found, or Associative Array with the following items:

    • 'aco' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
    • 'aro' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
    • 'axo' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
    • 'aro_groups' => Array of Group IDs
    • 'axo_groups' => Array of Group IDs
    • 'acl_id' => int ACL ID #
    • 'allow' => int Allow flag
    • 'enabled' => int Enabled flag
    • 'return_value' => string Return Value
    • 'note' => string Note

bool get_acl (int $acl_id)
  • int $acl_id: ACL ID #
get_group_children (line 1457)

get_group_children()

Gets a groups child IDs

  • return: Array of Child ID's of the referenced group
array get_group_children (int $group_id, [int $group_type = 'ARO'], [string $recurse = 'NO_RECURSE'])
  • int $group_id: Group ID #
  • int $group_type: Group Type, either 'ARO' or 'AXO'
  • string $recurse: Either 'RECURSE' or 'NO_RECURSE', to recurse while fetching group children.
get_group_data (line 1507)

get_group_data()

Gets the group data given the GROUP_ID.

  • return: Returns numerically indexed array with the following columns:
    • array[0] = (int) Group ID #
    • array[1] = (int) Parent Group ID #
    • array[2] = (string) Group Value
    • array[3] = (string) Group Name
    • array[4] = (int) lft MPTT Value
    • array[5] = (int) rgt MPTT Value
array get_group_data (int $group_id, [string $group_type = 'ARO'])
  • int $group_id: Group ID #
  • string $group_type: Group Type, either 'ARO' or 'AXO'
get_group_id (line 1394)

get_group_id()

Gets the group_id given the name or value.

Will only return one group id, so if there are duplicate names, it will return false.

  • return: Returns Group ID if found and Group ID is unique in database, otherwise, returns FALSE
int get_group_id ([string $value = NULL], [string $name = NULL], [string $group_type = 'ARO'])
  • string $value: Group Value
  • string $name: Group Name
  • string $group_type: Group Type, either 'ARO' or 'AXO'
get_group_objects (line 1812)

get_group_objects()

Gets all objects assigned to a group.

If $option == 'RECURSE' it will get all objects in child groups as well. defaults to omit child groups.

  • return: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
array get_group_objects (int $group_id, [string $group_type = 'ARO'], [string $option = 'NO_RECURSE'])
  • int $group_id: Group ID #
  • string $group_type: Group Type, either 'ARO' or 'AXO'
  • string $option: Option, either 'RECURSE' or 'NO_RECURSE'
get_group_parent_id (line 1549)

get_group_parent_id()

Grabs the parent_id of a given group

  • return: Parent ID of the Group
int get_group_parent_id (int $id, [string $group_type = 'ARO'])
  • int $id: Group ID #
  • string $group_type: Group Type, either 'ARO' or 'AXO'
get_object (line 2496)

get_object()

Grabs all Objects's in the database, or specific to a section_value

  • return: Returns recordset directly, with object ID only selected:
ADORecordSet get_object ([string $section_value = null], [int $return_hidden = 1], [string $object_type = NULL])
  • string $section_value: Filter to this section value
  • int $return_hidden: Returns hidden objects if 1, leaves them out otherwise.
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL'
get_objects (line 2623)

get_objects ()

Grabs all Objects in the database, or specific to a section_value

  • return: Returns objects in format suitable for add_acl and is_conflicting_acl
    • i.e. Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
array get_objects ([string $section_value = NULL], [int $return_hidden = 1], [string $object_type = NULL])
  • string $section_value: Filter for section value
  • int $return_hidden: Returns hidden objects if 1, does not if 0
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO'
get_object_data (line 2687)

get_object_data()

Gets all data pertaining to a specific Object.

  • return: Returns 2-Dimensional array of rows with columns = ( section_value, value, order_value, name, hidden )
array get_object_data (int $object_id, [string $object_type = NULL])
  • int $object_id: Object ID #
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO'
get_object_groups (line 2890)

get_object_groups()

Gets all groups an object is a member of.

If $option == 'RECURSE' it will get all ancestor groups. defaults to only get direct parents.

  • return: Array of Group ID #'s, or FALSE if Failed
array get_object_groups (int $object_id, [string $object_type = 'ARO'], [string $option = 'NO_RECURSE'])
  • int $object_id: Object ID #
  • string $object_type: Object Type, either 'ARO' or 'AXO'
  • string $option: Option, either 'RECURSE', or 'NO_RECURSE'
get_object_id (line 2747)

get_object_id()

Gets the object_id given the section_value AND value of the object.

  • return: Object ID #
int get_object_id (string $section_value, string $value, [string $object_type = NULL])
  • string $section_value: Object Section Value
  • string $value: Object Value
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO'
get_object_section_section_id (line 3349)

get_object_section_section_id()

Gets the object_section_id given the name AND/OR value of the section.

Will only return one section id, so if there are duplicate names it will return false.

  • return: Object Section ID if the object section is found AND is unique, or FALSE otherwise.
int get_object_section_section_id ([string $name = NULL], [string $value = NULL], [string $object_type = NULL])
  • string $name: Object Name
  • string $value: Object Value
  • string $object_type: Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL'
get_object_section_value (line 2818)

get_object_section_value()

Gets the object_section_value given object id

  • return: Object Section Value
string get_object_section_value (int $object_id, [string $object_type = NULL])
  • int $object_id: Object ID #
  • string $object_type: Object Type, either 'ACO', 'ARO', or 'AXO'
get_root_group_id (line 1603)

get_root_group_id ()

Grabs the id of the root group for the specified tree

  • return: Root Group ID #
int get_root_group_id ([string $group_type = 'ARO'])
  • string $group_type: Group Type, either 'ARO' or 'AXO'
get_schema_version (line 122)

get_schema_version()

Grabs phpGACL schema version from the database.

  • return: Schema Version
string get_schema_version ()
get_section_data (line 3731)

get_section_data()

Gets the section data given the Section Value

  • return: Returns numerically indexed array with the following columns:
    • array[0] = (int) Section ID #
    • array[1] = (string) Section Value
    • array[2] = (int) Section Order
    • array[3] = (string) Section Name
    • array[4] = (int) Section Hidden?
array get_section_data (string $section_value, [string $object_type = NULL])
  • string $section_value: Section Value
  • string $object_type: Object Type, either 'ACO', 'ARO', or 'AXO'
get_ungrouped_objects (line 2563)

get_ungrouped_objects()

Grabs ID's of all Objects (ARO's and AXO's only) in the database not assigned to a Group.

This function is useful for applications that synchronize user databases with an outside source. If syncrhonization doesn't automatically place users in an appropriate group, this function can quickly identify them so that they can be assigned to the correct group.

  • return: Returns an array of object ID's
array get_ungrouped_objects ([int $return_hidden = 1], [string $object_type = NULL])
  • int $return_hidden: Returns hidden objects if 1, does not if 0.
  • string $object_type: Object Type, either 'ARO' or 'AXO' (groupable types)
get_version (line 108)

get_version()

Grabs phpGACL version from the database.

  • return: Version of phpGACL
string get_version ()
is_conflicting_acl (line 789)

is_conflicting_acl()

Checks for conflicts when adding a specific ACL.

  • return: Returns true if conflict is found.
bool is_conflicting_acl (array $aco_array, array $aro_array, [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $ignore_acl_ids = NULL])
  • array $aco_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_group_ids: Array of Group IDs
  • array $axo_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $axo_group_ids: Array of Group IDs
  • array $ignore_acl_ids: Array of ACL IDs to ignore from the result set.
rebuild_tree (line 2148)

rebuild_tree ()

rebuilds the group tree for the given type

  • return: Returns TRUE if successful, FALSE otherwise
bool rebuild_tree ([string $group_type = 'ARO'], [int $group_id = NULL], [int $left = 1])
  • string $group_type: Group Type, either 'ARO' or 'AXO'
  • int $group_id: Group ID #
  • int $left: Left value of Group
search_acl (line 323)

search_acl()

Searches for ACL's with specified objects mapped to them.

NULL values are included in the search, if you want to ignore for instance aro_groups use FALSE instead of NULL.

  • return: containing ACL IDs if search is successful
array search_acl ([string $aco_section_value = NULL], [string $aco_value = NULL], [string $aro_section_value = NULL], [string $aro_value = NULL], [string $aro_group_name = NULL], [string $axo_section_value = NULL], [string $axo_value = NULL], [string $axo_group_name = NULL], [string $return_value = NULL])
  • string $aco_section_value: ACO Section Value
  • string $aco_value: ACO Value
  • string $aro_section_value: ARO Section Value
  • string $aro_value: ARO Value
  • string $aro_group_name: ARO Group Name
  • string $axo_section_value: AXO Section Value
  • string $axo_value: AXO Value
  • string $axo_group_name: AXO Group Name
  • string $return_value: Return Value
shift_acl (line 533)

shift_acl()

Opposite of append_acl(). Removes objects from a specific ACL. (named after PHP's array_shift())

  • return: TRUE if successful, FALSE otherwise.
bool shift_acl (int $acl_id, [array $aro_array = NULL], [array $aro_group_ids = NULL], [array $axo_array = NULL], [array $axo_group_ids = NULL], [array $aco_array = NULL])
  • int $acl_id: ACL ID #
  • array $aro_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $aro_group_ids: Array of Group IDs
  • array $axo_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
  • array $axo_group_ids: Array of Group IDs
  • array $aco_array: Associative array, item={Section Value}, key={Array of Object Values} i.e. ["<Section Value>" => ["<Value 1>", "<Value 2>", "<Value 3>"], ...]
showarray (line 66)

showarray()

Dump all contents of an array in HTML (kinda)

void showarray (array $array)
  • array $array
sort_groups (line 1250)

sort_groups()

Grabs all the groups from the database doing preliminary grouping by parent

  • return: Returns 2-Dimensional array: $array[<parent_id>][<group_id>] = <group_name>
array sort_groups ([string $group_type = 'ARO'])
  • string $group_type: Group Type, either 'ARO' or 'AXO'
_rebuild_tree (line 2196)

_rebuild_tree ()

Utility recursive function called by rebuild_tree()

  • return: Returns right value of this node + 1
int _rebuild_tree (string $table, int $group_id, [int $left = 1])
  • string $table: Table name of group type
  • int $group_id: Group ID #
  • int $left: Left value of Group

Inherited Methods

Inherited From gacl

 gacl::gacl()
 gacl::acl_check()
 gacl::acl_check_array()
 gacl::acl_get_groups()
 gacl::acl_query()
 gacl::acl_return_value()
 gacl::debug_db()
 gacl::debug_text()
 gacl::get_cache()
 gacl::put_cache()

Documentation generated on Sun, 03 Sep 2006 16:23:44 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/phpGACL/gacl.html0100644025754300001440000011664110476664473017322 0ustar ipsousers Docs For Class gacl

 Class gacl

Description

phpGACL main class

Class gacl should be used in applications where only querying the phpGACL database is required.

Located in /gacl.class.php (line 50)


	
			
Direct descendents
Class Description
 class gacl_api gacl_api Extended API Class
Variable Summary
 mixed $config_file
 string $_cache_dir
 boolean $_caching
 object An $_db
 string $_db_host
 string $_db_name
 string $_db_password
 string $_db_type
 string $_db_user
 boolean $_debug
Method Summary
 gacl gacl ([array $options = NULL])
 boolean acl_check (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [integer $root_aro_group = NULL], [integer $root_axo_group = NULL])
 mixed acl_check_array (string $aco_section_value, string $aco_value, array $aro_array)
 void acl_get_groups (string $section_value, string $value, [integer $root_group = NULL], [string $group_type = 'ARO'])
 array acl_query (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [string $root_aro_group = NULL], [string $root_axo_group = NULL], [boolean $debug = NULL])
 string acl_return_value (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [integer $root_aro_group = NULL], [integer $root_axo_group = NULL])
 string debug_db ([string $function_name = ''])
 boolean debug_text (string $text)
 mixed get_cache (string $cache_id)
 void put_cache (mixed $data, string $cache_id)
Variables
mixed $config_file = './gacl.ini.php' (line 54)
string $_cache_dir = '/tmp/phpgacl_cache' (line 98)
  • var: The directory for cache file to eb written (ensure write permission are set)
int $_cache_expire_time = 600 (line 101)
  • var: The time for the cache to expire in seconds - 600 == Ten Minutes
boolean $_caching = FALSE (line 92)
  • var: Caches queries if true
object An $_db = '' (line 84)
  • var: ADODB database connector object
string $_db_host = 'localhost' (line 72)
  • var: The database server
string $_db_name = 'gacl' (line 81)
  • var: The database name
string $_db_password = '' (line 78)
  • var: The database user password
string $_db_table_prefix = '' (line 66)
  • var: Prefix for all the phpgacl tables in the database
string $_db_type = 'mysql' (line 69)
  • var: The database type, based on available ADODB connectors - mysql, postgres7, sybase, oci8po See here for more: http://php.weblogs.com/adodb_manual#driverguide
string $_db_user = 'root' (line 75)
  • var: The database user name
boolean $_debug = FALSE (line 60)
  • var: Enables Debug output if true
boolean $_force_cache_expire = TRUE (line 95)
  • var: Force cache to expire
string $_group_switch = '_group_' (line 104)
  • var: A switch to put acl_check into '_group_' mode
Methods
Constructor gacl (line 110)

Constructor

gacl gacl ([array $options = NULL])
  • array $options: An arry of options to oeverride the class defaults
acl_check (line 219)

Wraps the actual acl_query() function.

It is simply here to return TRUE/FALSE accordingly.

  • return: TRUE if the check succeeds, false if not.
boolean acl_check (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [integer $root_aro_group = NULL], [integer $root_axo_group = NULL])
  • string $aco_section_value: The ACO section value
  • string $aco_value: The ACO value
  • string $aro_section_value: The ARO section value
  • string $aro_value: The ARO section
  • string $axo_section_value: The AXO section value (optional)
  • string $axo_value: The AXO section value (optional)
  • integer $root_aro_group: The group id of the ARO ??Mike?? (optional)
  • integer $root_axo_group: The group id of the AXO ??Mike?? (optional)
acl_check_array (line 252)

Handles ACL lookups over arrays of AROs

  • return: The same data format as inputted.
mixed acl_check_array (string $aco_section_value, string $aco_value, array $aro_array)
  • string $aco_section_value: The ACO section value
  • string $aco_value: The ACO value
  • array $aro_array: An named array of arrays, each element in the format aro_section_value=>array(aro_value1,aro_value1,...)
acl_get_groups (line 482)

Grabs all groups mapped to an ARO. You can also specify a root_group for subtree'ing.

void acl_get_groups (string $section_value, string $value, [integer $root_group = NULL], [string $group_type = 'ARO'])
  • string $section_value: The section value or the ARO or ACO
  • string $value: The value of the ARO or ACO
  • integer $root_group: The group id of the group to start at (optional)
  • string $group_type: The type of group, either ARO or AXO (optional)
acl_query (line 295)

The Main function that does the actual ACL lookup.

  • return: Returns as much information as possible about the ACL so other functions can trim it down and omit unwanted data.
array acl_query (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [string $root_aro_group = NULL], [string $root_axo_group = NULL], [boolean $debug = NULL])
  • string $aco_section_value: The ACO section value
  • string $aco_value: The ACO value
  • string $aro_section_value: The ARO section value
  • string $aro_value: The ARO section
  • string $axo_section_value: The AXO section value (optional)
  • string $axo_value: The AXO section value (optional)
  • string $root_aro_group: The value of the ARO group (optional)
  • string $root_axo_group: The value of the AXO group (optional)
  • boolean $debug: Debug the operation if true (optional)
acl_return_value (line 239)

Wraps the actual acl_query() function.

Quick access to the return value of an ACL.

  • return: The return value of the ACL
string acl_return_value (string $aco_section_value, string $aco_value, string $aro_section_value, string $aro_value, [string $axo_section_value = NULL], [string $axo_value = NULL], [integer $root_aro_group = NULL], [integer $root_axo_group = NULL])
  • string $aco_section_value: The ACO section value
  • string $aco_value: The ACO value
  • string $aro_section_value: The ARO section value
  • string $aro_value: The ARO section
  • string $axo_section_value: The AXO section value (optional)
  • string $axo_value: The AXO section value (optional)
  • integer $root_aro_group: The group id of the ARO (optional)
  • integer $root_axo_group: The group id of the AXO (optional)
debug_db (line 197)

Prints database debug text if debug is enabled.

  • return: Returns an error message
string debug_db ([string $function_name = ''])
  • string $function_name: The name of the function calling this method
debug_text (line 183)

Prints debug text if debug is enabled.

  • return: Always returns true
boolean debug_text (string $text)
  • string $text: THe text to output
get_cache (line 568)

Uses PEAR's Cache_Lite package to grab cached arrays, objects, variables etc...

using unserialize() so it can handle more then just text string.

  • return: The cached object, otherwise FALSE if the object identifier was not found
mixed get_cache (string $cache_id)
  • string $cache_id: The id of the cached object
put_cache (line 587)

Uses PEAR's Cache_Lite package to write cached arrays, objects, variables etc...

using serialize() so it can handle more then just text string.

void put_cache (mixed $data, string $cache_id)
  • mixed $data: A variable to cache
  • string $cache_id: The id of the cached variable

Documentation generated on Sun, 03 Sep 2006 16:23:42 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/phpGACL/_gacl_class_php.html0100644025754300001440000000752310476664473021513 0ustar ipsousers Docs for page gacl.class.php

File/gacl.class.php

Description

phpGACL - Generic Access Control List Copyright (C) 2002,2003 Mike Benoit

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

For questions, help, comments, discussion, etc., please join the phpGACL mailing list. http://sourceforge.net/mail/?group_id=57103

You may contact the author of phpGACL by e-mail at: ipso@snappymail.ca

The latest version of phpGACL can be obtained from: http://phpgacl.sourceforge.net/

Classes
Class Description
 class gacl phpGACL main class
Constants
ADODB_DIR = dirname(__FILE__).'/adodb' (line 38)

Documentation generated on Sun, 03 Sep 2006 16:23:42 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/phpGACL/_gacl_api_class_php.html0100644025754300001440000000613110476664473022336 0ustar ipsousers Docs for page gacl_api.class.php

File/gacl_api.class.php

Description

phpGACL - Generic Access Control List Copyright (C) 2002,2003 Mike Benoit

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

For questions, help, comments, discussion, etc., please join the phpGACL mailing list. http://sourceforge.net/mail/?group_id=57103

You may contact the author of phpGACL by e-mail at: ipso@snappymail.ca

The latest version of phpGACL can be obtained from: http://phpgacl.sourceforge.net/

Classes
Class Description
 class gacl_api gacl_api Extended API Class

Documentation generated on Sun, 03 Sep 2006 16:23:44 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/li_phpGACL.html0100644025754300001440000000341210130374430017041 0ustar ipsousers
phpGACL

phpDocumentor v 1.3.0RC3

phpgacl-3.3.7/docs/phpdoc/blank.html0100644025754300001440000000066510476664473016263 0ustar ipsousers phpGACL 3.3.7 Developer's Manual

phpGACL 3.3.7 Developer's Manual

Welcome to phpGACL!

This documentation was generated by phpDocumentor v1.3.0RC3
phpgacl-3.3.7/docs/phpdoc/index.html0100644025754300001440000000170210476664473016274 0ustar ipsousers phpGACL 3.3.7 Developer's Manual <H2>Frame Alert</H2> <P>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.</P> phpgacl-3.3.7/docs/phpdoc/classtrees_phpGACL.html0100644025754300001440000000150010476664473020627 0ustar ipsousers

Root class gacl

Documentation generated on Sun, 03 Sep 2006 16:23:42 -0700 by phpDocumentor 1.3.0RC3

phpgacl-3.3.7/docs/manual.pdf0100644025754300001440000267627610476664473015024 0ustar ipsousers%PDF-1.4 %äüöß 2 0 obj <> stream xWK@ W`{^Bjq\%!{ٿt S]1`w #<}ݍZב u9}2h7/9}c.6wt ]vU"Q*,E!\EWYdg,Ԗ`ijV*!*"2%*r.{ꋷ; &DP_ EaL:[;4_FAMGrB؅"Yt.ٮJ s%dkT6&ؤk=Yf9w56i 0!9K8(f_}7U(Df\ tJQ5tLN1ԹXsu#Lo':ζ[RKo%8 om) fFfνR Z]%=-Ko8N9hgլRQR+k )'7t"X^+ԿDo%RsqtFsISO{jUlZ>EM擳|QTG[`?r! |LU,)BNO''yQpRaM Ԛи;@6|Qw )zSqsv0* TJ-c6XACf;v*Va5z- L64&?*TQEl}JT8}؈G4{ M#+بQ湐F=\-5[3<i-;Xk4UVjoCAx%7 ()q=\7ƞHY2v1Znv?dkK?bs~ endstream endobj 3 0 obj 878 endobj 4 0 obj <> stream x휉ST'&Fcb^ྯ1B"T\#"T F\ 1q 0CLeTpr3sMճkjνgݧ;K_%Pkӌ<.w9knw䜮rf%=or%{ U; oRmn?KzO *,݂tsjm6'mu7kZi^CΗq;VIݠ}F%j.ͷ+\Woh|{w֨Jx<@555 ySB,94hF|bZڧ=vW+"MA$|ʕAKM615ljjoFNVfiСC/--bu:]~ȑ#y(,,tt-sUVT9?t+?ksE\hmO\)>LeWA#kEF [';vv;JHH߿2X9|^wɒ%R~ʕ3ޢٳg ^W_}c[fn֠JM: Bq]GH{z Z|_y&nE8quK:q2¤_y)?}1n{"4Ʉ o?x@z53' ˳5XPqnMKUj/d#hF%#傟RKJÇߵkÇ#:꼼*Af>}(CUaeee(T /XhBO+**pnw}7&ԣMc6h]$je bf"wN 믽Q" c 1!&&fw.VA/%%%<MNNoذт0H\ ś!_`е{ ]od1v<#3b0*q)PTpWD# ydڣC~t…ꫯңJ }qƵmٲE#X(?ydl}v7/Pْr HF+d=b3cLz} lq>orqgAC3Fkod o_ی=모rV{ȑ#EV!:kbC&߯\1MLb5;T(#uE 'WDt2HPSP+{Xt5|c?4E&N}36e oq8ws|[AuƇ Xd:%`1^2.5GL(ٳL@JN6Mn2Gzc@E*{Bt!Vn3a_ ",sC޽)lHi[6\܎[.ٺZ[xvۉFYuǩ:hhWT7*:Yj3K sy[\#2"Ts%-4fmz>uVkzѦ2$`ÇWWgvN@')sC O \; }>X׍uv>$Wdo Z{ĺM:,Ēޱl#iQ#M#㲽U-[L$"CCeuR0M^TT&~9BI|!gGg7JDY<JB?s6l1 0F;>_OX[p6X4F'HC0!+`q!`S1 M ޕץ㡜-RbK<,*0%#ԵH ߛ6^36(CA>ct1Vb-W->`2"LJ "7~ٳGQ7N>}L FJuY(1]C1b(x;.W:[+>ߔSK1Qz뫷(gbU?'v?w4x뵊*s E8`5!MR\\Ќ>D{ L2ZFF! #x*BL@xo:?aH4

GdӫТE.O(HU>D Hp3ҽ{GQ3gD `͈8%nZt>(?gH䌙'k37MVOP?_hXVIV f&mmr -b}8pF?B(((PT5k$֎$ " 6**JË8)d% {BW9Id:pnn4i諤D[ϤщDLbbn I~|Q.(8uBLZXIHSL>̛75@ ˬudSj!<馒1V:WPɠ(ؠ)$r|PErFn.6{nS A 5ն8'OD< @Ԙ[D*][+Tr K@CNAU9o(aaEcjDPF㟯]xPiN;tLU@^ t%ᖧW>zhsPSNFbbbd5XZà}|AzHF*rx4K˪ ۶m ÿ8Tfk3HGΈ''J-mZkwՆ8lg2P-4a`wV'0T =εeѐpaz%jͪ꺼 7Yq 5b'+/gϞeeeuGHnl q.(F~!IGlf R9x5WLq2}J(66V8)QJj&rJ2Ќ*<1ŋD!)0111ȅ 9g:U:7i 7%5Q.LH*[v|@[OqD;xlU?<-rcƌaP W۷ObzRRNjΟ? S1#gφՅgYaIAZYRDRB;&A2~2"'dҎz S㶗s/Bԑ,h 35gkI PA0bglz=~X"oڣz 2-HʨeБLP;DX,EpsF[xi3 ⾕86xjе?j,GYbyC 4 O! ƒh(lB#eO]{4 j~Fp5w[X8E,w{\[DS/K $ :tWWf $w pE^{6;rC۸Q mD}Y );HHEeOZCm L}֭=[#H-b?1 @  T v5:jY{Mϣ퀓fՇzU'(U 'c3[C_9>ٴIHaa!q!C!1yqA-pi gΜ裏JvHbR9}]rYeZ@1 "1c_Σ.}[F'* pK]i\ENr&iɓ*SQ.w+ucǎ%4vZ´ 33e׮]/M4~SŽxZĄl=Xj!qAֈ&m0`&1P߁ȸ1uy=ݻtqNpR~`9&Ga. +q}ZrpV}w.^H;/< T-u髢BTǒn۶ 91TZ/s$;;;?x0OYUUE_$ #uYz Nx lftr\ 3dģFZ|9u`0| Vxlc}rF~! p **ȿeDY_7#hi֦~u *z"9v>-//UV1:P+cgo111˖-QVpLa.!)? l뮞v$$Sk<9]˾"%Emmm- B o Dɟ~I|;KMM߹IGh>0`< J<ަMP`0 Lgc^cHx~51UEZ.MÇϘ 2 7n a{߾iimR:]mz:zN-ިM*NI^k/4NRXφ $1C",\Xp"y  XwG-Y b.`2^, )NFDY23W@/'%@pb &@VB='7QMəj7nS}v47^ ]c4U8?rô.HA;!OP+U`@$Őq*LהN՟UTZAqroewOa:>8^q d_\/s\yA;K6^/uI_#˗/Y2=l3(ċеi\%),ވw^Ə^vpL^F8aҾY MIP' _IE1ѹG/  CpS>P h0 AZE8(a hZW/oBln֭[#?3HsMwGyQGh=Iz hmatDxOh }ASL+_t)?yjfZFއO2d+4Nd_` Ĉ;YIbB&@Pd %HXy7yf!b|SEe8CtQ#5BRBB:YQ C;\ypA4˅xhuk\rظW^D^p#\ݯ_?z_31  єy=))x~rCI-LrSExrL;weI3$?<3X%C1-}" "ĜwFF@xf<lM6)F$}>)Pim&h#@y(4!hRF;tKvJ}t:_Vj9r.>y:_8]z{z>$vu8!.0ez%תqA 9T/!^ոOYI b.1Zvdd8?{JqRDžx2*F%> stream x]ɎFW^& @x!@|[7ZDq6\j.XkYoQv:⟉^zҚSU۞\q.?U)Oqj3PS 6O:H5WƤ(uލ+̼Fg5rEuj#abki)V)/O7f&@3*d:j|^:ze0K^qNK{qu]=@SjJ'h?OǑ2!@#6+hYmvPdďx,(fc\ſ{"tPjIRr > ta@ ]9=Ϋ\RaI;;ĚP^(ۢNvR$-pCuAнjJomKZ}g?I.)rl)2xtPB@M/bDޒSs&kK% &Iq!dc;\*3ܟ)ǐCP^P.d;X+!*:esED LHS1X8)#:p&Ϭ1ҕ} I.yK|i!EC=!\C=_psBG,3I<()Ϥ<ԚYK TDsovVY`]n%A5̦fh/  \#[!i#P %uh0Fok%U].'a\tJ[3!f[ 4SN(T+Bb=`<գkuhV'K4!DqF KH!\XuE&(,&pRZxC2eLu{H_)AetbA-O8̏IRsP75 CGjԔҊ9x:%"JBӝtٽ?]$@< %OtɂcƳd׀Uo&@BGFl 43bSlG I.",)~ -!%t$r Ѣ)ĩ4N Ǧ(]DvaK 5i;jQO3,yͨ o>^7g WfYjJRR* GGBeB @ <H'\1c=`A,TZ?7v@\Lo貍(82CVqFN!&eݰ밋ޓ7Jrre<Į '"^x:J,Vږ'0CVqE7}GoF*'BGhD\Ly* 5%{kj A@xz® 6rccư΂DԪ~%|s{P\o2ݯ.%}@rB.8~):p:N絻w_{9İRrTE~b:8J΀ӺIVm i1~r]2}@ @ ]q{^Gk2oR Ik[#nK7Dq.Xv#Ddj RCf魴5< &Q&*7Dtbt㉥>BI@@Pv' dɘ]$@< %G1nYsJk15c>L9}hIoHHH()-^l[iy;k%\) )JR#{ B#0ŴSGR?@ @I9iqtӴ17BnQ/_ɛ zwT-v)CB e]d=\v/wl}!\vmg-a*׵W:_>-/8^ezwHdnj*by>! ~k7( )͗dz@ fIb3їi5״lH|J-].a} ۆAro):N4،oě T<)BpJ^->PIiZR,=_Ibyزom+]=)G> stream x]n0+x%>$ 衩} ?C߯dqhM*)y ̮%/U ?GZS;3+?;jZ_UR7`8=b\ Mt]BMoAu5u Hk2C4LX y Cؽu=}} D/?`Y `#"3 ZCS8ͷ@0LLH;TR"ya2OɾY7a{,` X=!DYZ;+] HK /]|t$&8JԔi2P)gH͜W$/u7,` XAVt*:q1iuN[b 3<Ԝ%Pۡ[Q!RlXv]-Vk6o}^FD)a9_b),Ȋ-V+6k׳l Z|SePo=nG ",` XR2hfusC>P [!͖? -tD.FO:XYe$J؜2etwlk!Tr!fbb.쇣ǀ@E"XMtmF>`]w })]Dz X!2ZaI"tP_ɶQrι$ :{%(&F DžU)^&K(B`N5CM;iHϮ熭9 :=ת ێSBDn8>:VBXb> stream x[ˮ6߯:8"e@Ygr|@&` CʒHJݝAbNϷ?:?np}˸>m| r;dw]';'w}=IG[F|7Lo?T[}\Ώm'vX?eq Rq/Ey2N;18ҟºe՛NЂdu4utGzq!tN&įzMUӫk.ˢ/Z,chq%Y &J61aT4Ȧbn&|?CN8seRE)rXA΅s]ogO^SԀܺES42nYtiM}e;5qݘƓMy,kc6+ k:\St'V='12_*f ~Re*$0eS)~ne W[~x bc{XDxfcu!JShn ϙ&׳ 0j } @ ))/b",p2^sHx1ȒKaRxgTf~&Yo՜,>@Q/mwPm0i+ǘJ HK:%%-LXNK%Ui9ۿf;4toݏ`>Q>/kL/r؊ط\šP~()ľ/e]OŭIed! F am !i=!SOxhX6͔ AӤ1q5&xتE~ kq~Yu'+k7`b\Ӿ7JY6'֖LFv7OM;,* 9e|š9u$^I0s+q=[X E k%30mM _ lЅ\:"˓Pv 4:j){@o!0ck -IBfNW6SP|\2d.6H.(nRӥYaZH噳W $Qҟ,f~wM6Ŗl b  #myE5h+7<q#Ҥk C|-FןXt<#F%0;m<8]MJMX`CU`|o\} [K ePsnaCچ >c=ѹ}% @Gf R+ 曂=ZninFJǺ;{|b1X3j'[Ӏ֨̿Q IsC5c i(018W)={@5p ٱP|H6 rH F4> stream x\ͮߧ,z#"uG@2lƜΝDsc(NR.tww~?anu7g=ܺo2ܻ4>t}sovVBui3M;}ZD _ +z/ zw*#󨚏ЋyTC P8G1˰S>a-F2':秌̣NGX5&8ecv2HUqhjMZ]83?xQjWs>_5+c͘Tf]BM%v]\u5l- U9w&M̼ψ +eҲRc2zeg4a4|΄zSWS_C!$&.vc}XMǪ]- M8,%$iR)3`ac)uJQ sI2Q&vI RǭiA@WeJ-ȟ]UJd=}4)kEngEX2&sOI$NZ51ŗE;=9H,u)# & oܷ_ Lz T8a8}ɱYeOkLH66B#o.ΊCMZR`-#F*&%;XMorc}S bݴ1' ܛn21-|6$9c-b7a<;on6߱ΦzqsoTSȉbSwfQ"*k,fGR:EpioNGk3[tzG:.;`Ax12;bzN[KVEjc4ħ" -:YPo$޹Lq(&2;bzN[afZ0k&<J+ ? rzZ WdƉn`&\"=' K!fV6?&ZO.$=СvXKS *әHD:ʉIg@"S"<"GmYٷ.tay|KaT؞嫈38z TtFY88.V?"t CΗ7'd $fkxvw1^:n6hG9 ᴻWwr< qw.,/v.FV䓜 G1EjU͸2Z^ڀw+B:-t&CEE@Qy?d%T HqQv` l$3(Dvy[ucY[|씒L۝C9<(puַ 5i{^K&'5pO u,ED  1{ڸ:L128ԱD=i sJV*]x`9nǠWSpp)A]r.DMG>n''C>,ԘO;,Ka3}3YvjQHkzv9="Cf:邺hyewcJ;R$r3O`z1{ݽu@|N!+]҄z;֒iJ+d?tPh9bG tz+[%_RԒ&ovREyQzj@nsX{1L)QQ pֶKxbM,=n@}ؾhͿ3cm*' o0HG̃\Gt}&>fo1]{x}Il}tLX9hSg %CEJ5Dg?w?w?3O6ƗΟk|]Ǽ@o꯷?L endstream endobj 17 0 obj 3333 endobj 19 0 obj <> stream x\ٮ8}_q[)R熒Dgp. RՉ/z(3w~ۇ}yg݇N6ثj/뺧O.{: nge8 `#BV]9IV?-Ro)GA;ݑyT1qT}¡t.iVpcjmeKNN=u*qXe(PS?:ٳ|Sw~18Ǭs4 WNOZ?A5_;aud6iT#o5H\>hmTRj9v #K=(U٫F#ŝ+aZ2uLmWv$d%+)l.nV'FF1,Ė&"9h8cɁ#3vz[d\Yx d؀'m̊:YygsM9%JSPuA*J$ 6S/A-D]C -,HO@dQ%C k!O.v eYʸL=ccXoꜜ.26Jdk)亍){=O9.Ӊ9Kբ ;T9Agvt?e4C6&Zj6fHfbZ]Nf 6p.n.]<9Nntv;ַfj{V3JGƣlo:ɜàCdVo'[0cҝ _23qu%*)l4z!#5:]2a\pLr 7'"2~~`١:/K2Tu^hSE sƨ-Md!_R0"_MNc.D|fH \Kc8qHy]atDvG,+CQRCudғ_Cz=]Ӟ€I5Y@+ 7' $J|NbXh\{nx QBpDP|݇EeDfDE'A+xbFNI<:$g#dd$$a/| aF`6v蕉{e€i)ϑHܐ#GlI4WȂDH@usi'>"4u$HIbؿ rFcĥVɵ-MC )pX=0{col*'у#wY>8Up-Mt׮_CPz6ע5OȋH0\;TŵB"_6=U `##l$cXq~0&hSTbb $, MF$e,,IBf^sj&L#.39D7蟧x2\RƑb $Xw=9(|mŶQnX/{VBh"!G:F41!5{T\01_!pQǔ"-u֘|;R.ϒrf":c0XF{ǃXReh(LS}`m5FizW-CN m}kU1$HSDNDwwjlJm8n5r"]ܫ-o7eY HW^B~BWlRE*`s/۲klq!9;:i̼8n Bb:IщY·-S>ݟ#Mߜc]DD9hIΠ(xQd++DFgfԭ] Fg=%vm0 t edFDI,3>3nЍV;OBPa0FbޜȦ[sf6'lr>h ~r始/2-]ScԽ}d;s@o΂:!?,\9MdNMvmZ7b0Ur A7V &{=M 4.%ieeh7S)ŽUj{8"UeC:S-ÜkT{=6-B DVL>OVH4בv8t\tt先P@3IcwL.\~3} j!BRPz2kͪHVDC8HChF-k|DFZ5ⶰX{S7YcG|۶W[T]`J.)&RgޢIyڷKKD%@;!-ﴈȚλ4xiG.6۽H496ߡRj2B{mۏ{H[w"#(;i@M}6JլA 7)L , hLyǁ:~%FB1xBӬ9(渒rBd ÕBmIТR+tSU|:KB"E䴫#YsݗC dd'8z耹`.ġ{JOoSOvJ%ܲFRδ%޿.iQK"ԃw.z.c8~Jl⚧?^U(*0D窌Krk*6f9~Mb렎UBVE$1%0$]xNj(i3+7hǰU$5ϚC_9>^? x E6~obzPJ BEv>^kMzbЦК@Vå!%˘/|Q4_V/wH!^!{31+ {Se$tor1'np@J.TZ )*@m.$K6L y]M];Aq~C o *V:;.}g($k'R87=-eIޗN1κ".f¢qpΐD"ZZED٥l=MPؓojݻB%0I-',w(beV囟T>áS9d'!M3S7o-:G/7 cg-4IT-C͹:2M6+_qlIPX21kE &ڻdh'?RfX0<!{BᲠa|_)^thþD>af'b{NR$}l}eW>F5XzOjMmv.˅f"nLL>>{|~? oM/1ϥטK OKO ntpk;O.Oa.E endstream endobj 20 0 obj 3643 endobj 22 0 obj <> stream x\ˎW@%ZU%n ^%RУ3шd2̌LM׼Kp_/_鹺%4o._s/['GZƾ5zMz\U3O3s7^2:-\f\ǘMf~OJL>كOP0_`m٣ŵAE~&}E!MOYk} ]Uuhx׳ (0F،OX!'rd̄JZrA@[TxoX}>6l J+`a>ۼ"AhXSkJ(՝lYf!eWbg{betE|G lxo k3 DjtvHM"5-7jpTYTQHm^cNL+ꃍ+8z;8Tס`#"-_v(c<0%f->ܛLeMꍥ.UC4oܪf|\f ^97?҃P EE{ӃFejeZӲI;Jirj9kzPYU?("GN+m{KgO$}vammu;[>Io>7Tƶ`i*sJtTXGަc23< H1$?Y}8J%])\\rdrtps dӨw#B48|s bW x2s';e#L:;d}1= 5sg[8=Ɲc:ɅW` RbC6.M!98s0Hz؂FfOh:4\ᡧya*LШRm.J>GGG~@Qr=hwD\9.~e&{ 0} (u2!XCE ,j)dj9R XYM&1B) ހbtYSԤxDfY.w(B;iUם1p:@VmRg=^ݺC:$Ml,`quGq``~]'`eJ2J,~`Ag2Ĵr>T(a;srPh%kwAP*sNhk#Vcv@%`%"5%$tE1]\m,ҵdG.zJηi6TpwUם>&bk_Ē[qf[$ +up6YI0}5ܛMTtKi 2b.FՕܵi!+6}gcՔ5j|Ye涩Eo"7 gQ=n+]ɂ~c4OcRmv|έr]݈ͺ rlx6 ]>,&Q]'K/Ex0~JU 1CTe0ϊrH4*riuߛDmc&EtOyoHɂ1ٹ) 5P!#3B A$ٰ M2cݜW-mpp5%)佑qiF!oQ)UhC2a_+($jkkC_ +LV:\^f/^M^l,I]aЗa[XKK3Xppq߸i5A aⰟ$r˲yVBx=P|7Nfj GU@ |Chs,I@=ꁯ}.?+k'<ƒ[*DP/n;=h{vXgE:1KZ,mбFA0آ}&O dN2]*y ܈:Z HcvznR/Xݳz*n|+KPAK٥Kc~#/(]mk;{R>JR쵰 g94woʯ|?V!Egه ?4H :ƑhYobMlF9iJ'd+Ve@g9SM&%t6B=G-n/ endstream endobj 23 0 obj 3001 endobj 25 0 obj <> stream x\n, ߯u{DIU. f Gҫx\m8LdDGGI9?N&MO_rN߾߾qr ?/?~_OᏧ_]߾Ɲܿ_j{9,[<+mKn"QBk5QkibM2ڨ; ZXХƶx[ 6@ L~FO# w*v"qڳ=8r9gAwߌ(&,H ɄKpk"H?ǙgO9 QfͪHΉ : DNP&[G|Bz~/.YoD;9ѵy琝 !5[I \V|c o v`v#SkR\ !*Ft)֓WjdZiTQXXo$㖪Adf;댧x 4 +BF9-Uq@a" Fջsko ,drGߪ# R~5ѤϠQZl :Tv>P|7P(`kȼ@xnگ;scV-(Zml(}> ~ Yz;dkd؛@A pNOiX}XTk8oMdQ ~A,nvᓫ|A|}7Hsevpu> IΝRD &ֱ`Yj0ɥĻ> ֔JTlt%^ zBqIhlТX"z C.(eHjMQp,h!IB8N(KW@ǣ53ȱ`I>ގbM {&. DW@@ʤdRF!cYA \h}Tweb lj- u^݄کkZo -U>Dsbl_AJBk.1&(2CR$t$zR0dT<CPsɑa!f9Yik/p`Iz*ÁcT^?`YA1^ؐV"yWrajɅd9gV]Au"ǭ vZosj$ܕ,mS%ͧba7վ;hqs*$3*z0*gkb Öw' eI~%\B*H^͋|/D]G!J0(PO7w(һfa"nD N?OEw %4_-DoO(*q6Łql95W-2y(tXPpCQ0 3}JB^QM쒃;aZPˏ Kz[C| \hB[JV&.*;W%h"<=1֩|4Iu }#h>J+wGcfAZYҹ{ܠNxaN QǨK=/` oF:34y'P^._An_u$s0Pzp1z?OyL8f>BRT 1`!_M1wIi@UҒJn>o'ZY(ǀc:O]y{i.4I=P'a]k}uOk "NFIb$|H'BSFY+]}4#`D[3о糨B6}>c@]GpNޭ#dWc=kɋ{FY qgy3!yA4] hgA~"~XBƷN{Uد|5V3;©L^rde ˤ|F) endstream endobj 26 0 obj 2941 endobj 28 0 obj <> stream x[ˊ,߯{T& 0 g߷TW9khgVW-q"ℤREyxwəOqu߮߿Mûx_/" 7KE^l_s=a[ikR0k7a407GK6̅JOj_ {SXMכd#Ih4fy l^(ysViz,%eL'YZ#121R1[B^,yOigQEIjP.K]]aBQ*F9ENe*,IhX@è8)KFRkr:{Uot:{Dz)~"I*>ZXxilG{Ӹ>oujZoVeq<(Nw.\|V\0J7Ok7OV:udur"Լw+Q&cG<=Ax{j7( d8FXпO(vS䡮FeTEiY  Jv†% s{XfXlwAW>TIe@aNfs5"ZȒUOvxګ>Hk'b_ DUx.mK2 pJifPV{UN|Cތ<`9`.yIi=9ƞrU=F퇐?*%{w: r~ѥidB>kJ|.$<Wzeʣ\rnsKr덺uS~64ڸ@J]jb`yϫ̠վWЕ(tM|,ƚ~K!ce5L~o&춪d5taM4S؝`oP&fS]«WDZ38M{2AÙa 0w@Ig,h4{6o=uD |Rotn"d5!eI'> 2B 2d |Xv$h͑eSu0JCi5-ԊW酏rX\ X, ABbh޵mP'0'8# P]fXYdE ,63mn3]#Q!U0/尵R(ǜޠYu+zʪy{`u#,1RJc-uƄQmR/2&  `!8cF,(!H0P ٧;@gXЄ BےWW=+/#E׌P,.U_z+hE0 z5GcZ y;ᢱ軔p5`H2,bH[I1&<",ĘbH[q'H-BJ 0}]k<6ӌAL79o L< ۞ ޣsI, /'avkYe]cHm3ヵYh"CZW\Е% % kpHdum%V'dX70ʦ.0Ӈ m`$ZWJڢϦ6i3B%3َ dSQ$;\"!RU(Ÿ}-o?DLHMiU vcs=A1@PYXs*yPt;ĸ4S5r^ Zn?N͚ji^f/x#EfǞ#Vs再 عb"aD\".q^(5!Fd4Mmʐ37[S,DO~jԲ3 gO=a"֝:O/ɲs1!<">C~mvԞ{uׇT\-6 i/|!|/&l(Mjߜ83n<e:J[W&Ky]YϺVMW.kf/s Iyڣ OoȱxFPbF~qǞx~2Su \o]Z\<>nYdc. endstream endobj 29 0 obj 3042 endobj 31 0 obj <> stream x՜ˮEȹ|\8d w)h}: EڵHs]L G]~s\~o{B/r^|Wߚ!^W-?n+w?>pa:yl 3T^{= m+30nYjK= =x8k2[ط_|Drߎa|htϧ\=q{`lXlG[Y~wy:5~n7G[–sgzxݳͮr``./ 쎗EO뜥ŕ!9e>fr>=?gw}+#|tS2 ޡzbƫ4.LpX2tHLgsҧj{SkKˀ~0'\1 trc $chkY7~7|oA=?dس^!6aV+ dwefAM-do9]F9yp|y6"_n}rvVr) qW 3Lz֕@E8DN:C. D׼4z)y<@ i}`Ք1s;R9a Z_qݡ.fhQeэV0m6&SZ>ޟq}xA,3 Ђk# 3U^?Ϟ-Ocu.gؽ>n;D .9z8x b:;]X.ĬZ4Eә4]#f9ٻ Ҙw7D-' |v%]NvoX@ ˕01[X $sU#m̔Ep$ߕDj0x '+se([PIn TjBKT䫬wG=JȠI8`G8E |P<7L% T܂,a!RfNHZ?OAyRa_mfW]kTZ뱫v T *C9/S \v4*әFBbs{H@׊@$M>Gl(8S6%$e6#"Cy<˯6@$ZrQ\Sz8>Lqۺz fcEJf{Jf'P!m{Z;r>E +ZꪄVoaIX7,5R %z~b;Pj7:*.͔j:x^1dkqL8 sث-RIcI$ؗAsY*uv>DP>* U[9z;/R6 o+JyBUsg0nH!:QıG@L H3._J=.:fϛH,# 'ɷ{,|L?=3//?+9/CxxK8R=>Q>"d?+-tnCrRijKd _,FO{<j$aȂv9j<9wm7G@͆H5 ۡI!OI^R`oXOQ|*Lř[鳬]\O]Zn:A0S>Q& Z z'hJB \FK :NZ%I> @}Vo>?9$3V=&e:3:徔k1ƽ r; !D_|MNS^")eӞv#.MXS=s#L'9K_3dZq#s8p?s9oD$T/'"h0Zq=nAڑr||Y1l;cŀ]=h /wYs7`WNLl"T{ pɈMVj>Rka'AڷvITpOiI`wԾ\z^mND!}wvޤb endstream endobj 32 0 obj 3808 endobj 34 0 obj <> stream x[ˎ W:@HlE@<G*e\ӓ̪UDQ:˿N*՞;Oqu_/~x'gݫ;u'No?bOo}_078ʟ: 4llmSv04gmYHʿ! Lyʈ40vwt } u8%NОYSk {'I3.ls#tTi'Y&`PlAm OtEڡ+w2ܵ4lq.߱6_e6|]=bZj908a{a< @O,iY5%]\vVJYwk#41 ]ne[dM1IxLe@}vʸ&j ?J-.v֡U族|%)qbpWQT"P 9ZdU/Y`4Y+s(W/g]'1/+4jx&6QL䉥8)ijKΨJNfPyZ(-uDQ&y5(T^Oʋy~pmdko%SJG_'B[f3IkVqIj-V`35mZ`R" AD9Fp-r=AuƁEQ4@0T쌿%OZEWw5*w^iI-eH>3_ƂKF"{?{`'D<\E#N,B^9iс!{_N F@uEa}õ;HϖM#ȢWMiӗĺ{f"O!Ac wUkM( y G7AMmNS#"Lb"镄;ݹ *g^YMdMϾ ۙ#T^e82yVښVeĠcgnϛq$ Zחxb<@x >+x#IK©C=lr A B&=} lWOy#IҟF,7Ue!柒?jӅf}?%TvМЃJڦ]Ͷ&;&q)hUK<X%Lfm L**ԑj-- ɓ*bl|C$EVnk6ۛVʌy[g:G$Ȉ5W2v2v`0Kܹ$١fZ kd$f),cѽX|]+ҋLyPPjqylc+&Kۦc;^f=H4;ٕaP5^āPd2F* rMBE+ Ҕ&{܆+AoWwDvKʔE(i-Q5ԉ^ ;q4y vɃF5DM$˶w dD1 N%ɄhdiF*57j[͡y^}ح7m?DoY9[F;@!bBO[Ff(?Fon^) rv۔()Rj%˲4j --(s{KbgdrjБn 4|hNw MٯXOQڕ4hd)6<I,]QVtw4ÂZs( endstream endobj 35 0 obj 2982 endobj 37 0 obj <> stream x\ˎW:@%Rv2@> / ~H/UJ{AmENkF\~}E`ͥ7.q]\~wyqƼ^~xԗ_~T/|s?Q9%Ȃ􌎞q CLG:T3uOJ"dO~TKY./11(pl$Pi5X lϪO#*_kzްtwuE ^L4UҐqBgq͇FT¥ws2ݮaZdgٯGwn%>o)i!9]LٱI?_o(зvDاgύ {f&D!.t#eL S|.4Y;κ;9"'Ԗ:XS,eL*L@q' Iv+SFxE$tLxO.<_[DORG(uO674*w{NezJUĆC:/:@Yi\(c.֑OڌϪ)`7Nn/ Kdج(!=z zg1q #]AdVQ [f'\mks"*BB63Y&)DNMl QnSLMa6BԛegPsI$]Pj ݱj bTY2ZQ[2jz󂦼v-=6*m0 9ٶ`")Ǭi(m1=LuTëYU/!GM #؀5C#F{$Ѣ)Q訊pKDї3%'NMOd71]Lnժ|=+уeJعi24-i :tU<6)%dYڎN 5|74ID%a$CB9Q듃B6?Q+¥Ξ9OĸT&iw_ЙD~ Uw>8e9UvG O+`avds2>VM 1۩ ^-lo/_l8 C'N.٤r-%kM2;sH1 +yx}my{-oaJu0;2gSj/@!|-gmP!GPw崶*rK'&Sy╠d Sfpt #5 -#=ܙ!`O` c5HĠA#cڜ^XiG9S S aUT]MSvΞߎet nsPt'3 YxvnFd%Gm>O- RJJX߳CA^^`y7[nYڲTl#PY0=-6ىs YYm.kHJAabABl ʙ.@{tvjHp7,n|:%X˦A{Jۑʅy kokx'|k]€/df ;*sCs)NNJ$HFY&B,/2h1aUۉ Ub!`Pp#?:(Ol*2A7al4^Qi[XZ+VLGJslhU'U l4kZ2CI|`rgd:;o Sv(1su-d"x; ɍnd;NM莿ìKnkJܞ[5LƀT l1 #"PՂ!zjz+f¸|k*odOP&KSݕ rZk]#/{&+Jm0a c\eiAx` w4/:0J:b[TXd@+LAUni2߅+`Yga4`g"##t$/ѽ ~&"z=v`UjIb;/$> stream x]ˎW:@&|I#:k'$@7EJbRw Ha7~}k13>7_`x=?8ƥol{yO/ ֜Yӳ{Ls~c3>w>O&ئ]4 }剬7MoӈЛ>ӛi/q8}5iy1]gRX;nN{OY3bg'zZГqĤh%J 'Q{EgNKu(pp^;.,9h|3Gq5!>1m& H5'q#gN[܌m>w4xK 3[|GrfTg<.I`k6wLgu fX>.;"Lm'd4˞Ue-^X2' d0=A6}7z|'mF҃xy#?yDeELt+gq)-UDQ Yя,`sڒLg,G B;hԍCHt >i ]z)G@T=IC`(.gEn;j1hQfqiy Ziy+E^O+ Tu(dT@1=3Z{[??1~y y%u"<ә_>El:6`E#|IL IF3)$Zg֖b?H#x$y~#Q Fd Դ;^`JeΦ,fҐŘvH{yhkcTlJ8gpʙ.$C<{C DG GkɱI.TW+I*ek-)LFBR/i:5` f*^G~ҽ4k25E8!7Pt#z,DW@(oPW} vo'Nx\RÀJjqw/?H>S&+ + f6;X~|#_)t>l*e4ز2=2E YaLCkUU,)f8-31Y=)JV'mH:(;_x`]-wC!OHOɑ}EH˝T~ 0Jrb3(`Maˇ%G7~Rc+лb7IIYH4hl{qd{=ob4Ra@BtT%""[1H~bYr3u7PȣCM>e7<Ѡe2[%m~ ~ p |0 N5H%\Q/v2+⩔ݑ9:8-BCt5luD Rۓ]*;X{(ܖ5( > wuOe4Bi3sgQZ`H. TD< Q i0Jj㊫T!{J1 8,q8vVȍC*jDvMHkV-;(DŽCߒ= =E[? <%cCI+ !R ^vjTh>TґC930Z/iCZ~I׶͕RȃJRjY#xby\qr%*1b{Mrcv۠ҥx뀸? Wքhp[%XH:? /!}k#j:yDyxmR[>lX]X?x4G4b$/k (B6W$gK@aR2ک>RPRk鴒h,ŻQ"Ad!Y} X5YX/V6HL`*' 9?UؼVk*ϖľ)/^$u .ݡk]CT@_)"{u݌jMnxT$(•q2NSll/-Z{C  NkԕI]E ŎD0VScj:"2Y;Wdΰ7u»Ā t~0Meo{7ʊłxY`҉a@(,0H@do?? Y_GÝ(8 )1괡JKORsØ 7ଵ.m6]E=N4TTDqUG|Z"rNn푨 njIVQ댽)tCwA94RWwDW"u%Uq0VЩhhyˢ>ٲBk\D ?@ֻqtZ 5NfbU͕ |2*bDURtIVeJTzu*7\AF{2$FVDJO{[ˋ(P"JyT@ܶ U`=K"V\֧uӐ;lb6 T5k4ku4I_2Ŀ}urbJ^|tuƸvo=;xHo>=\z^I")rƶ޶ 2>93R05zjݸyz똈M؏u6cjCjĹ.o#qzo kB&} f6ᖯy&4~3]=b%!eE;黻RHLs6cE'qLPYxќ/[*2qC.G';P)K7Ez +Ys|dwFT+¨jDaCfknI\0n;:| ?sfS*ý7YV/̹߮}fE5Xx[5wh{V{(P/{-W)x{# V@s c)9]}c% V\gvEB ,=uU՞ƚVe|_)\, e,j P|l<cklKք*QlΧSǤ⻾i34dc4yZ}O11iӳmh<}k4vpNH$ hhAjFA7TS4SL"LlgBJ$]R endstream endobj 41 0 obj 4072 endobj 43 0 obj <> stream x]n8WyvEJ@˾7``~``L_;bR:]e4uJZ܂XjNSs׏C< =voioNtczN8O9c/ϯ_~>mX]t~KC:ajH?w- /gtihz5B۔=:)X~1^7/vb΄[sjt7Fy(AfH[kn| n\d98 - z3Of;\;ܨVc. p)4}lN:G'M^Ҙ 0Gg,ζnCpkRk mgm mxiB}xPE WI۔뿗jcfE Vz, &}c/{YcoHGhf%rP "X}+҆jôq3ٮ M̟w̝(KacE)ģ{)B;~V1"XX< g|R$6\whc&N6QIL,Q T;VI#uwqkt\/k0jkpT0+;qI4[G/  h;zEG:#UƉx  |E93QOmg^©խFȸB=nSBz0t$0ghOJ4,q1K㶤Ee{Li4k?JsЫcIq_;mYGpI`i*Ld EK7EhJ!%U%alAW&t l6j P72._Z)2c )TpB:D+(v50ohؿmmSk5M7$1q*7RA A*jBܫ9;R"nאZЊ:}$\BbJWqvb-:KUm8`$I*)lҧ++'S)1TpFRXj@Sfq  njpf|JU=ߨS@àRZF9*)w8b- ͣpsz }BwƸ%W,$=H\$$1ڣ(iּkn0Ո3 {6| n9AݮL#2; AD(S͚ s~ `uXCoNm9opm5?vĶ؀f /8Ɖ5Up+2s xI\dhxێ h;t{2WH hPFyH|jS+#4qFR[!;mjJ Ƒ]{YT3Xd ;aw@x*(2A2]sb]>LΟJi>YyFRJyGNNJ$pnɀcuKHu/VvU\n|Xp 䈅't5Z p,f䓄UCԢttNC^\|bkn'XrELH }ZZ5D%8C5uq~ʤx . _Yy]EQ= LGUm> &d,&:"8xd dOѰmŷ4&eg35)˩H}CW4ʘ՝3;6}ʺvm7w݋_nwH M7ezAkn^PpYwsqBM%&/!=:Kxz A*g(o+/2%DY7ڦ~b` R) ב? "xAWMg6HOAf8eܦ2u+.ٻ|O9< dۖi4 ^;&JwJ? k֓*6F|L|N\A (i`Ӑkئf&& Rr!vXM\*p6e=銎Q2k),2֊aZk0<9"XNA(Q˹'.͔BJq*H~3aS^حgP,jQxi,,Xү9w}#x`ڲHrr?սVysV>UؘkKqrUy('X->h8+|AMi\P G~^X5-Wo G6ޟJ[H4~W 4( V -aJDrz㮐P/]z/yU7%H.`Jm n63kZPuK=IVo$BfL[RGL~n 6J]Bz:];թŚ%e˕>Q/*sJ7!I( =*A5-T L`&"zY9/Ie'?{b+) 82he{<&La[hpiq>S2x, mqG@Ea?M)ǣ, g`u ooR: ?,/7?IvMФ> stream x]Y}_Qρؒ,0y0?}OUKU鹄 |ѷ~i87-Ӄ;Μ~:;>NONv48}1_Ni:yʒ^+߾ 듗~yo':fsYuw=?>i _X҃)xƒ\]w?n> !i^rHDZTh0V95=QǴT`KM q/`JM_/1">-rhB-ҒgE[{^̏2QE(.GJH)B<%6Eʃ[ 6€y,|2;_d2Jb3:|([Jjl 9})QAnYA]^A8ʸ}joQv.$$#ltN1 HAPбK!t;0&%|,C+hcsV1۩~񊱳8?>! )oSoxZ~9pFi`?3ϠG WanY/'p_&UcՇ iKnlY/aPb{5hץ[Mf/%;e2Ȉ8Kw9^Yٟ}{?L?HBE+ +.}}4t)bPvc>s)ӓan~ʍF;i[\'|p7b9\th|凵8.ü3fa~de ߛe?Qʔ!I~=n4@_puuR=1C,sEe?GMR}b%ٙ=F 3[;uW"AGLUթE{QLy\4[:}";BWF%(ÅBW(}򝀳ZC ^*pBHSip85dq_}OIdXfa)3ߟ"{74.8xppz@![as(G[bx\EOɌop"L3l,AdW~dۥ%Sݩ AhRPHfz%))CR]iZu+w ŷi 'چg\RD̥iv߸5"x -l yreZ/+$d J4|7O]m߄|9c%5jRª[g%:D3+3Y8ܰeRVJyNN3zՆ ZX"Fԃsj*TM)] \n @v4g=A%AiM F` ]r--4Upؤ[ .Q-2; CX=\ Y(D=K6&qG$>*# ,;> lc%dAsX)Ap4P 4Kz-diH{ k2fRgB TLrt.xtiC38/ӈK,EQĪ'aE袬gmZx( 4kusؼX@9W,E,p 2`I%6vJSQ7??<:7c|.̄ߣ@?8@ڠ./xU>F,<+VU?%xM &<巈<)T3" XaM&Pm4PpΪg#cю)淤I q'Ώj0ޤF34eR%Tyq \bTI{XNY$ NGw\$}TedCV4QU*Ɛ=HTշՌr@ dAfL”N :Ԡ]dsְ]%k@ɒ+0{V[>2Dl!W{.Ah7ל&29Wv ̦'|+O>FIVޢطRޞ>pVQIG8Bd!eԝ}8ۃw )mTvԑ:(sjr&R|E1 E/-%+l'));2PlcO/FBQ*9rۑVy{5v5F_K6:(]tt@^A\fg~Ȭ;V>Tҝ{K#<⤲J%v6 aVk*l*j qPOUsEx0~';*r\[c^rogٗBnA{1RhߐGn۞w\i^p6\  !C"C:*/okHʌɘ͍0>%P g'?Nxvn^{Zt endstream endobj 47 0 obj 3640 endobj 49 0 obj <> stream x\ɪ,7ݿu5$0 5 z/ڛVVIJe`p'N R:*pÿsu_NߢЇB~o_oߖf/#؏Pxy2 L}C7A[AAuM[ժ Lɘ=v7e,W[_MZQQxbm:i76dLmu@g⚮aMHl<},OL!HCT`-3 n"K T X6 JH\EVYq|E@DhOhꁂۄwWB.hg˾1jCDM3iXtJmS\+I]L(`Dwqs\ĉ 26gT CDL8ʾӡ Hd+`M@RP={ɔ>4F$1W0wtJ!BMMxT$iЕ$y8fK^3u{ bi TT*{ӎXC8AEj]ҝ)=Qp/',=F _)*֪ͨ>m,.{у zs"8Ӧ<G#IMc3J-!ALT'$Âṑ-noTDS,D.;K0Zqel뛦O@:LL .DNT[ՠH!Y'Z =x.L,h:CmEܷ'U"%YT%*:28E"Z[ro݁ .fZҶ(6ohK.;ƸU G& 8M^ڷ9-W{}V6ta_N߮ռ4t_Zc*:!K'.LĎAUhڠfe~kaJfCJ|XX]we#  CE^ lRfx @y|f2ҒP3kqtPT8N*`,*r0i`h_Z(W4 )$c dI8%& ,9"ְfW`(8*T0P)CY*1؍l(( l*%Buli{Z˽y3B?g; ͜'Z_װ/SAy5-((ȷr)v*F0`oŖ$wCpk&=i=0jAt 9,9׮/9wv {(h[ Cl nVȡ9N6UW9($2ς/׉og3К.L5j]y6b|b4Ϭ#_b M -G6_笙#C+d\ d7WVC/`E;z+{=6AqtGrN~L nk>U]N5@9wLkFW[vO91%==ʟ gJ {G2å-j?'1yXtc-lplSz\=XӢD~{]Y=~GHV_R6q{cƯW0_W7Ounc_qS~r1 G7cS}m}%6M^|mS6 | C{ntCR)OFe{K}4 ?#sNl<=Q endstream endobj 50 0 obj 3558 endobj 52 0 obj <> stream xZێ6 }߯sJdIg z@߯dfzi HDԊ˿7ozp/|7˟/׏` 6|?A_/&~ Z1,>fyRz`@7iy+֝}YmLu˧|&j~RAIR6O%|ƴG٢H沧)Yn~7^Ir~+.zN,'Tד0➟~\^usm& _0 /l3!akXob%f ʹU/>wC7xvy7{^9ԥ3ݾ:g1tyl6"Z^tLЙ#KǁG*<`uzmOkJb=ղwF,.^]lO}nb9drhƐD@aUΩ{P=n#8JºZ#_]\$AmŠyPZA="TR|C1c,_`)XzmfEoXDo}T4$BiO෮k[ϝc&cPz|.?+SD|ԇmCaTJ⛜{dw+U9/J5 dhkْ'|lX`<05 F>yy<,春1X {RAm ~Qހ3lU)Q dbr#`B5832ӃQ+ȷ# ]΋d't&Z֏mIQƧE7 d}QCށ(5Z+X8E%WȺҼn=^t'l;b=p( w4c3 4ߗ6w]E/:̫pzIK dXwL,S:ۘE'~W5 }s(ѫaFD9́()\^Y,MRRMh87]s{ΓB(;IZCc>I`v*9XaPv5`:wl'1e6x0a#xTK2a+5>/6ӁRa=ǘԞ^&`I 4$,KQXUnh |K*Й 0ʁISxwB YqKRs(88?Z*`.#|%')&犤 9R+fCj#`DSBLތh6-:4ц%Fff) 0Da# 鿏axlBɚQ׶7cy#2"5(@wͫЍw6"-+ov0.b_c*uf x~|gHt~nnPrp: ¾U`=YqV 4sbXl۲iqob``&^!\60#%$l& RTf~-3$r$l');k)vGH A5)2MJB?(hkqlwC;w|H,qjǞP\s\C\t4fG1azh2k-3Fyp]@YТ*Q'0~\3dj\P%j`@ݥ>bx+-~S(FB3`{uY"[r~jn)\AveP;8uď 64lD3n~nF%86ǔ$yضWM1!JNH0>ܝQ b~): lh1\ Cf+*jonTLdw(^Vc77,]m iA m&jpihY8 PA C{F]D0j7)8K6J]_"(¡M1A\.#9U3;8  Zi endstream endobj 53 0 obj 2129 endobj 55 0 obj <> stream xY6ߧu.~, BamS(%w-Yg 49swO~p*:~~ǵ8% & V=PNѨ_~QO~0ڮJF&8ҵ=[̍oPs شU- Q'VŪB$e$0Fx^`EHp{B@Wkm$7*1fSЦLQb%%YAN׶T6 V,$r!EX bE'1NR(9;C,Ž@,._Ѐo| ,8j>v&?MCxѭ)VhPFB^M+]EZxUk[E)hܶէu՘D.7TG⯐/Ilu"lL.<끯~̱D_~+CCg35X>|jT"cC n!mg;}xAY؄-qƃs)a{2\e endstream endobj 56 0 obj 1298 endobj 58 0 obj < ] >> stream x\[ڣ Ŭ@w/r$wnN8* !Miaaaa>q~Arm濽m8-ԊllwfIl+k^ WJ-^^vݷ9cۊujMLimymOBSF6M~7r84Evk24bDiT8ɝ$erY(R }}NDr)',qF9%:|(ְlxQlÞK5Ӓ˨C0\NǦmNstm-j{M31W:P3+к`̕agLRDzЄB>ѯ6plRG GπEF;As910Ml/7L0LAɕg0s$OcTˏȑz\KSrGTRO!ΖT.`)@@BT@SZ0dYsS)rrBSr+;s$$%%'Num#ƕ(9&,Iq " rF,=T+Ugj1mz̡vgM.!..:ɪ*JɍFӐsܼ|>*nT2*Fff Nlb_#rLP`gnNΔ'':~S0x\z5{j(nt?`&fV7]Px!P9F` gSJFkF4dMk"ϘR x b jM6eTFE2]#M:=r↜K٤S?4#g'y*DPKnd\tMO$ I.֖23:Z؞z:U[nռP ;l>9zBQTj7ĚA&f5qhgրUxby `ڛ^ۂU0yPJ*ZE"!7kEґØJ&R½&G[FJAj>j"XRW NzTm YXAKUTES+? 8$nSZ*"Ǧ @İ,ӄATyq5joU+bItB*sV gErUu9Fs14ShO :09 _@zkUSr2'UDP U ef uc%W׀ؘsm2o>#)h< NI\4DN)„Q,2qĀh* E_]}EJ^z~uK>yAq-Ԣξ޿$.C(Vl Wf#%) ,!$ Qx,/sGT'-͗e_A\ Ø=jֱ0\Ǎ ]t r ]0\e׺88r j<.>i>aq05dbCcV:b}b:5"* "#>]+9*偷oGN%VPwQJ4& ^JN9<9}AVA[ %fICHNWܐE\j[<{6;w;$';.ոRj܆hMȍj܂d-ȥ"BnT6Aхt endstream endobj 59 0 obj 2225 endobj 57 0 obj < ] >> stream x]=R:H:wDLRBBҗE$,`T4U +`v05dvm?~[:tnuź٬:Ų^P\Q:6,*Ҧ4lk!B3G[(;4Ջ\gE2M@ UTom< Ag !JP6_ Rp PkY-Tm)EinrWŌ M 8+4<4=|R )K 9S8``zhӀ.zXw-QB P$P6#恒A pC,lAF녎23>,:,13-W,/hp _. @`yz BB/ILIܠzbf_ywftC|[ %/\ "L༘F&=G-]}?{L)bvu&?fx/u)qu<`ʃJa㡜)+y ƒ'M A|rh03E yPE ?py y<y4AVc/q` Tܾ挛*o뼌{EhQkUڒnta*og؊>TT?޵~l<X|Dwvx/NƆ{xP 7FD1V)&trOBz9twmuǸZu2`tVec ΊxΒ{2!,o/g:Kl('C}.蠳y1&?JR!_> 0tedIg &%to.ϳ2W`):,XV!:,h!otY䡃 <XgcjatVc^X̟:+|S+tYVv~oIg &%{Vo:rQȍ:36ڟg2aP gnϳ4*UdMgxn"Bx\ޛTk}NћΒ4Zm^wVT{03ZV/GPg|W>v Ao:M_ȇ}Ȟ0΂4;rOOO >uMu&S1z@uox<+>gፕ=kwYy},}taHW54wAG^ƯÈ.,`ڰ+]gV1|RYK<@ƒ'WyYXX/䵃!I'+tߠ\*hx <4d~ =_Pat0g>5w./s2x~q$s\]Kg/\^62Q%'_9uC=Zj x<,#^;WW|,R?95}BW.3'qeWR<#0k%uby[- o|ӶL<U3^f\g*oV 9ӊCcz67s ?szt7hxq|5'7oxо%j<r endstream endobj 60 0 obj 2698 endobj 62 0 obj <> stream xYݪ6 ?O^Zǰ&zp?(lM_rƱl ydI>t}nt}tnMoc:w/Ah姏Vy؆y5vH2Sf32o?&ll}&1eL_bf`R2Y rk{4/NCKY6=gxmeDE^P6S,$8t IʑI6nB&q>e2pʇ4%=~2Y^fV()X[wIGKL*~tfvWt;\t*!Dz0NYI_#2AiA$Fʄ% `wqK6f=E/V+Q͡xjD6ʔnFE9+غY)".> O-5:rlp^8 taU0ޮZ g άR ~UjW ϥR%=PZ(1p8A 'X5#^YQJ T4.=X#-䂥ˤf2e,8( Df |u`gF)0gK)0B=R,&/Ruł{p HB[x{;r]gA*6 >E @(AGWxE~籏g endstream endobj 63 0 obj 1476 endobj 64 0 obj < ] >> stream x=rֆTyLēMaSg;I/@ZUZ]LZށ@(YI ,xAUUUUUUUUUUUUUUUUUCdlx׹1DvcM]Sn /nLsz똞;o-khmG Ngdp(Bߎ2sZW+߁Mq9 +!LW(&BBƼɽ,].0 ,i$%+1NeV2SL]1 ‰35d#L,ZZm)S)*SiMMi$d*SL{-0_m;W Uv?#LSk_HoJg!洘vh{~tr'תN?;ې-\czS_[3!fI,O1,#7/v}MLiVY]qLmѩ!L(ޟ_o bj}!2%!33dAW(C´{~YC/Q38'e1IdfJo@$'4t{$MAXpO[ Ԩ<*31>CILTb$#Z1d I%dx 0=$: OPOv,LӧI,ŁIJudR`g9{LL@L;'ʘ>(Җ?+1Ӕ&jSg̘n\!C#PFEEYiznb+cj{ZAܗ`O?'e`F7Saj,-0ʞ3i}wˉ IzHJ'mu^ SncIE L)H& Ӥj,77)VFޕTX:ڧ{SPg>Ϫi Ld;;:7euԦX6YoCLw>M,x:'.)hs* ls sF 'I'tiGͦ#)n*0幇JL{L 92 S@aYR$ʆeLmP)7?n (P,4}j7Mw?#L S@qMUڂ &:v2" $mxM{:&ݟmH1OC_4vk[&| nd_<;;Лic~$L" sP5Qp -BJ5,}ZB<9_/; laJԖB;qudL'HЛា0A#|2QӯY)N'ZqY L_g/S>KV˱qՌ)Adaz B禰Dh @- Xhq׶5EXS3 =&dj:J1q3kҥۨhO1MScz*)&1llu q,Ċi0 SgX)%:.IrU3fU)gz*I4lK ǒ2[T*'Șq=OĞ,$WY7S =LLdjyHC=ΤoM-}H+̋RFOY(4v^ Sn5xbpD )W5))SH0H ?wCNCiSm/fӑCL;֫(>ZNО ōfCN fjӍ,N&1gDvf;tz~c>ۄNL؏fd˄&LУ_ͻ'o bD|O7}dQ x.0w~{Ƥz ^F.a 4ARG$!zb%zڎ^&*p0E>1d?a?T &Dohq?J.rm%at)W)mm?0diÔרpt퇚hCM0CllsnC=U׿xC QQv==Ui=%{*LT-=h?ad])cydS}O؟0!I?.pL;I4$*ӽkO ܸ LcIi? x=mkaC<!ۮ/4|<6W\<0Ӟ]vo+6]x0RDI4x6!yoMzwdH Z9?r]ۍ Hv2fK N))L,ڑO=eXxܿ'!JB@BS{HHh]LS{!އ6Sy%~,ZGT 흵x~̞ơaߞL}JOdOǣ} ixT27>?vx~hzʢ zL?Qcu<;~~~zC"|0ܴ2}?ưc$rJL!Iv6YF@ ӭi׽=pH?O,4|_pL:tx?~UUUao0g0 [Fc1inr1bKI't=p dN|(1 N{3HVawsaSy{@d?g'ת Ӂv:.' TEVd*eL?6Y GMw&S%YS;GiۉTm+11g52u1 'S9s~jOJSz4Lfj9ǞFĠy{v?L~6z:x ;IM>pt4?oOQ Sa3;iTU{dWUUu^TfV 8:1>xrmMo;'*= .;x~7tI2!ۉi7!ۏ? urڪm4ӭiQS -[v[Ѕz:Al] +g;1Y/w:_ݐ7q6Y=g[YùK/l_M7G+ưLL߿_ohQտWdPo)S?Wܟ0p7xkC,2gZL)]o$MAXo~j ޳XwL źWQ33m!1m"ÿq[kga~5gn` qCx[(R=K\T8OTE!-9#k1Kߝ6ڄ"2˘qaߞcg 8\y'L1еKTU>4˘bBLNRŔ0}n\"$YO՞>K Rr`ZO׿W0Rid=¦?ɞNkaţŭ(3wvݿ&[Onƈ4K/JwW/$ùXa80m_P/(,/M9v(^U]TUUUKQ;ͬj"B1l5BC>i??T;E#n'_ʅ J5 ӟ?|/?_~~ӯ)'IIq/Ȕ=n_p@iR=A {mL G"%L/!ba|QZt~Gtp=bDR.?+LqD (.T/S9w=jAXK`[!!S%3q]A:*Fq i=F=d ő| d{=?i=egv3= ScW½ꠥE9~ S:IӪfC/U zP!fc}sr/қ4md(fLn7Ee؍ 2+%ebKO{݃뫫+NM+ ï=>^鿄%N4}_(i1t'$MAXD[)V<+R0pxYKLGHrL)f3jojӂ1'\~OΏнN[> ЊX'^ JTOoM[=~fsa7 9#ꕏ'c7-@A:`bM $M)| 2լtECMhm4eh8ZLoخPϹɘH֛)߱ !rUYq2o3ZZ\_!%Uz2Lg\OW={*j1´ӞF ݾhFTvZ +,mkYz POڏw_R7\gXd e2zvsō6KtNĞ~rCD@tC ҫ>MYO8%K ,Wk/K>՞Փ牪L_- e9M:IͺV8of*Flf薔A4ʘS%Q1]!MSa.uLg3[(_&-E@w[1\G,*Ϊqe-.ݵԙR=k.B  TU֛5S4lT3k 6[EZKR[1] d62]7zSiKtgSG+Z+L*Z7rak/aMi >/9-Yҿ ^Tł5-ǔ5Y\Yi+03XY3Y4rq2I %m.k`^R䚙6_kHriUUUUUUUUUUUUն[ӵ"q;:!7͵ɗ6U1],݃N tszM$^'TQ4dR{ 1ƌqra$,]_%3?y1]w2t3]! G+0=Y ؽ%x.nt1E>k""tLe\_ԧ:[fZO%<1(6=-C=MƟ.FC+4H-)_]L7=Ɵ.0j8f="{}WuK-`|}>?b:+TSzq`J$sjSܠyzY1ǟ.狥?U0[Sz+;ixOaLd`m65ȴ6ϔg3䔘_tzͼDo褘C| d}1 TL=Ts(; .[ꆎ@%̾5],~hHV"8\o3z%;2]aL6*2{#ӢsH>Vҭ`a*O[[o׼(i0eFrSN ~"p&Fu`Ҟ~odX?+%>2>vLy`.vt/(r4|iSt;Ie%ZM)( ̡j{ZnjAD.<x=mTS?S`Z.GUUUz; 뾆=Ojw2!#_ԘzDX`vP~4@CT&?RYa{Z0p 7휌Oo3+R%!~õG1'bJԘ›'"|E:x^) 3p0oЇ.yC3*o&"%Ld`aQZȔC`N_?`.tB z_P#1AHpx8EXRqeE4qx*)yJ H#. $Ղz+ \b(r5V}.t?#3tR8tLS`LLp~qF8RcM%s<T ^,_Z'´r&}&uw9xJ[3[GRC9ݩ;/w3[#>2}= ~6K9ՆR~XbrHهl-ư_ az)M\:|Ka[J*^%$v_w? |Hc?lc)Ё3R3:1)9D) o k X,hHG`),-}q:Z)] zSV>V`Jl:eAa7( $ˑ1:,`2Ac4L "3E-{*9S]Ԇ0T {gI#?7} }k=m)7ݎݼ~sF3jS?R*ۇXqRԉ ӃVf=_ʓh3Be:@tdSO_eiUUp\~;|j_rvb/D)T&I?bj#*S1E;-> ݯ+jr+cG5`a :1u 7|hjM+Z$†rƣ;aK^eC'qfX݀}G-A ֶֻ> P:1 W0@\.5 /LVBpe`(`LAZU=V kvz tk{j3{dju/;< hi1BEۮv*̐T 2[a@JF, Pܡ$bt?SPWI#Gڲd/&ƴr"겍30ڽ>a3nxp|LF&ћLp465j2C[徘c-Ė黎?V2ߖ\G3\hx܅<.L%x ߁޲YQC.w_r #>K.XO՞:/l:c<مta<ɅvDc<P\G]]G]ɮ.)ŷͬ G!새o5k#\pU(Nf<ye:Zglx~߶,sHw<+N3:4dSWQdz 9hL*5S:gVmUOA‚0kN365nmOmfOLe_:,F;?ӤO:C tCIL9?dʟ1-+?ƃ+684ӑӪi PX󦽭1/Xv Rw`:EMK?.|;Ac_v#+"SI5A#_v;8K{o25|\Ŕ#?e ь/c=U{`F94.ĞF?%>p`bz?%>pKP\9>҅8U~U466FxP㈗0?rLm@+@?ӆl;(>~s;Ϸ1C֜}Y5a~~nzl< w%V|Gt?¨H+N7urҌ#}a~~a*#.S` #ȣ SGgL80W -\)B= {:i==n L 3mӧFVk`~j/.b/LƯ~O  G?Li?^we?j<Ȕj֊ HS#2cd0%12Gdڨ~O$G+fmպJ׽VB 6S5pLo^j`1Ie:Lw#@Uej`OJ2``+-Lc`*]*Ӫ13-IxG>8 uنbwJX؆;JF&$N"1֤M=L~׾OoЬN<bdΨLƄݘ6wSމN/ep?Q*wcN}eu_=Cß2S4O섫viӞބ׾s *Ub6Ym{*P .*Eޚir[\H3a M_(Tѳ! $^ -D&)9ÍFo\R!F>ro.c[7g*xa*GaTiEG.>wbprIp9S*)ЭaZ'n+Leiz$kSJ~E .QOASιhԜV>-bjj`a| e1G?irY O,SffOa7{x~dnq=MV/TLSОk=Mih2  nF=} gC !p̑iYR>PshYv_)1} Ow`*NrhO4S{ՏsR+m?UA´?pB!jMxФJShOC$<otuƴd'A[cqi[A)eS=aڠƑl- h+(UUUUU{! endstream endobj 65 0 obj 12507 endobj 67 0 obj <> stream x[ˎ W:-r4"@fIlWn:ޝK$" ߢO|<>bioOw헉nܾ~Ÿo_>?jiMORex#ekڠ/3;Z!A$* 7끣Ȩzʺ-ݡ C50Ocq^lTA͊!d-բ{ *U̎fm6x7x7JgU ȗ4x3IDI15 ̠U(̋F))Ort=E8dj0;AfdsY8QQz,_@/j۽0T&BGLP̬^e&t)JQI0]{LaۡGFW W".kf~VpZpD3AdϵDugX#7+}FpO=[ M@ ȡ7}Ju-lOL]{E?5EKpNQy7Iq#Rxu3e6j{B}ѩAnA\ q wCߑ ŗ rHzs r] ^q [3~t hb.rʪVLNjeKZ-u])0Ho(5r 5KXE['@K> stream x[Ɏ7W9,@`$dr\![\jXFl>Pb-E"_I/^o/?,q-ȓYZ>~]~E'c~^>|ONm#ymdX߈dqeۈ/D`=s)ɖY-iUFTd2gSq0!SeQg48D)Qff6R`*-Su6` eo_|3wXIHJC0lQdѣ,h,&1Q3.[2c<ͩH{ )6є+4E"kZjukYXB3_ʨk+yZxXꩮ)H?UՖYoҫ7y6= t 9'όɬ,0&KS*o{ "͕\w JgF2O6º̀\ōzZ2Z[1eqi;;7( C$g" Kly3yQ.ڃ4V*b1U jBIaL؂=T7!Cè;c@"}[!ʸ݉s $7+beeޚUIٱ恚hW-іҥ@9wFC<sc-*%1nS$RalKrYq*-VXmnz#0Ns],9c<9,2؎ r\)1 $;&gxlO+n(g #/*Na 0ֆ=٦E oGXG[H"n\u÷1Lay=cU &V7%C 2a{s1f :j@kL5"4g,S>gj斪X*?d-;ET.鑓3m{>cdC1&pXNƘTyy6ƴFY0VPFe#PϙTro,D*쪇cSc1?Zm5 kgcj<< Sk?Ϫj (;*X$2OleBA@_ݦ\!L씸H&xIxuFs3n@$՜wPyݳR"N<E{hqHL(VS^qTԃST# rU6HaKDY'nv ;wL p\}E:L wlGs_J1VDxka,[٭V3E%>A*A3f^m7YȶA Ӄ0tAF38 ݪ8yCc\O+VK{{ l\lX[ZrI8TF2i= Dn&%S3}EՒz$4|`_>+G5|5 0#jphpmMA.mmO-0Sϼ:,Ga"urwSSk)&0b5]l|2E&&&}IQ[YwX<43*8/uo]Qbx` S8~'p^dO/T.xIt5})s1; \#R_ہ6z1TͅyD "MCw$VCQ Yvkluk&wkGgcCwK/LOh[VrfuZGykRw4VT#);m\gEWB)ݯdUGIoy^U7y> stream xXK8y*=, B`vn ِ\Sp%0եz~2_ 蟋n*X~|Qe:_/qp8u[ެQݿO}V/'f7+%:Ƹ܀loTf/)VJ(BHdwEX,k HsћlǎcŐQ\f]/BP8 q,{5Z!6: r n=ocXČԘB8[7/^{&2=0pBzr)hzNDD`?\/}|Nf.;FsYH3{ᦃ) @#"3E@\G0fOrvfKtP!@QqP#^TtSb"}^f{y暱O$8c#`·&>5W5BA*UQ,SkX8,$'H3ژ 4| T ug@ zD({|)T}p5`exJAgAz3Ô'dn{+-WA{kH*5 8-GV:y`@j$6r}Tug@9PnXi3T4<'*OvTd@ &NYPOr.gڬx^]{PՂzڧBEͦ[Bȟein"YmH@0n5>p^i ۉu6Zs_lI\J&k w}XKvVA9>V۲S}U);RBm^-WAk`Iǐ+O5soN)-q=<*5_n)6L55[0_淄v..6Q,iDm<솛璚vwd"ڥV1p˼j5g:AdH]tA$oݢ+$NyR"LQg 0b_i60hlB( ܘHKr4ن7//lh(?j.Ec6&[!~7z'q`aX: endstream endobj 74 0 obj 1104 endobj 75 0 obj <> stream xY%}ۖ?X~<䓐bdY/Hukwwn7&sir*9묳ʧz#_W7|3;ߑje&qF /|饗J>Gy衇P-L1ݻ?Gŭsų"*/żj+P__, ħjamϞ^pZo-j]ҷm_{%߹mlkU޽{Q-P\ wϵ?N>`H?/ lAL6HZdJfj?pƪmseV+=UY9ڵ;z2"-[va3wSJŦM. CrVJ*֭[Clz뭷j۰qTP"=\|SV+W+=z3WgV 0yd8r?$Nx,Z9Kc9|gH;8 "W)_~LEb}1d}p]ɦ, b"RFnSW*S{ K.yJ o{T~YedsMUP߽chTzIOUd+Hg"_%߸0G$wC#$"+XWN<8∳:+E]$r]ŊJoa&Nwm"J_ZZml";w6޷kh7nYq=B]PyovW4b`}7#(d앱Ӆ\nҭ[Pݹs (gEb;NMϕp5Oή[N]VVWW6[e~6AO?v_a5-IVי#"١jd3Drs+C9!V J2s9KZ|'WŞq6Au"Oe~\G>ߴiӾ}TLl%죢Z簥j{2jX3[xGP- J͝ 2J}epRUzXFeqBI|YcwH!cO<֩(|G7C^~PEbLTš̪ ȢZ>\E&[y 1:coTwd-'eRvVhOV.kTf9xS2X7<#N8A9S.5NnΑ|鸺teQr XJ,8ʹ[oUWۮʹ"u˓v[I+պ<.~Tk)tgnZGܧ\ɌLȏHJ!'xbyqIiZ}W}P+aQJS^yz9\E MG4e*U(ݤ >~ݻ_{ƪu巺8.dhLA4~^\Q֭E,HM)IVV*Mi7]iGOπX"wo߾ݼm~['0ף_s5g1tꡋ9Q"_Yd"'R./2KD)aY0U @d_~})5c<2,1u]p%uV#WhyW}L,~A2~Ysϭ|+߿_*^:VSId>(2|wjesN/yW=Qn01gؿ>ǎ8 .+ݷpPzT]߭jGZvQ-t\^:>d'~ qJpz$۱[ Zypf.ɩonx嗗~W_#R\C~~tgS%qVBoԢP+ѳ~ ,}QSTY2U_UL_RSLu;z5(zݦV%ߝ)o-/ʨ[ZY?,o^s52m,'jf{ʆgƍi Z)AARL3a򛟨ʺ]Yt|ɦoEP뺷Y~Y@|qˊjw㲨߫ 7eeyկȊ⏼=]۫pyt\jZHgxn~ӯz 7P:]Yԝjʋîn3M}ZV)bPUDUݗ>l/-/Ш._uYJwMN_~hϞ?4|x(uIQU%EyTmUkH9=TfNT П`,S;!s+[Um}΃~D1t5|g%k?`?;J`T#8BmZVr{.j2[~漢TȊL%ﱟ~L<^mnUkKOxo{2=.aeCZWPyp9-M J2%/])1!٤Sj]`E`+!+'WN~˭W Rk-j Jt\窭m5vO0vB}ϴrIzu|pzheI=1a*G/7Cj._ "hGU+2oU-ݪ>Un^y+ee\T`,ì9ϲ|wQkm|wmJG}֫[D`'ȅ/kw~E8Z,y=ߴ[u[_=ӏ?jeE}H2"+zܶ~)A6ɨ 5'~> ~Es]SCqiǾֆWj̺OTW^ "1u`'pjjӋz@-QqG>zx__j޺qˊV|%X;˺m@ 5]?*~K~ J*gbdcʆi&AG|鷺.H.AW.W;_8*/](!_6ujՏ +Ŷ(VI uӲ&RBPy s$WYToU7gK3_c_RISj:7aefqz^XO>dԪOC6,Ccov<|Zr_EImUбv-{'ve[>C4Z, oٲң>ZF`a/۷O=#e6oRWBd>?CXqw|\=R]DKlڬ!Tg_[nUۊ+0\I%?j["5rDjKDzH_8c<ӟ,;z^CVG/8 ~;D n; DSʓg+ɊuDK*OY2ΛhDR"_E?3e='GQeVt%%*6sPe-+G.~~ȏT<7GI2ZtqRSHpgpU!ꅈh_qڣO[-I1y!C-Ucɿ~ӠS$X !v"l8p 'P~(㭚IdE}T~R_Ͱ;nS*nq@SΝ;lٲk.+oUW^y~euׂ=AuV.\Mzi&)\{/UůHxTQZ$7Mԕ#/"TaÆJJcFƓo}[rx|_=s%j'ڞJj4;Slϡ*Uv ؏>c~(F%Ȋle+?[8EwKإ.\}H[n]y4V NY~TץURJsxUO>dVNA"rDC]0+S}[I9DU{>cRArbzEag-P԰_Mle>W+ W>۷q#TO}JÔȨS[n:B˽>I"O=~W'?QNPy?#dd̗Xߥ%/HrdP r%,//yTɬ^!^($E|zLcKQL֥:y)ulŲU?2+# r83 }H %Ѯ&zzf˃ַ[¥!&/ 7x4T, P3;E5Db(B&Y ?p0P{ꩧd#IiRK}}WqeQ0eIHB!3KB!+C>$Jjm%S̷*)TK/ɰ/#4T2ګK2eSC-9r֭M=P òբJJ=>BJJ%7TA&qRGYa_d* -MS}V꒓Fu%2OoFTFWŀRU9]&߅t\J _I 7pQGru[ R!JgشiQ]'WScRK}ko ;jm_JۊCŪ>(LZe -+;[@9ɿ2»&[x2ŨvjOc\Ԑ}kJ') CGsS8V '˕ojS(S}$N_V]hȜT)FQZ4jͣBG)SB,T ;EF"@檢э7JRD=X&էe]*VرciiUT*m++il"ZTHx 1Q+>U?k+uI.U5G֊p5qY] q~. JxʰXU=\)3L.gFڈ*WmE򌄞[,5}TsOVSW&e}2dE|qN/_dXy,PHR%8KJVSQucH5u5Uj9,2U [NeT%$Q^DO*=sD@фT˶`/UC'IU)\6iʊ *붪Fw]WrCMɥv͒.Ki;V";kuNjZUW}p͠NT`=[r%V %doT{2kDk !m;Q-nԪy2q!WQm2JN>c9FD)AX=tZsNQȐV0[/2Jq*n^~F}F,H3]O8uG.RjxVRlnRwT5(T.%ĕ?8NuJ )A=R$DLܶ4l(3tE%l.%mVwy\ZT?{?J+2Q5ϕ< !UߏjmzFUvx>ݽʪʪ.Ϫ.&l%]MKՆ<_xQer+ރVRyxKib.\Jץ`o*V=dXl>U՗eټ2REUWԳlx zYijFMmEGmw/&kI)嫮e]_חpRm!3{k*3omUHK&Vn=5 h~TZfT cAV(GBRϯ}Pګ3h{ؐ7| 2dUfh$ڞbNObaT Ӡ|Gi?#l\Z`).9[ A߀aZDhۊmZW~h0̞6r9}=gЭjPOD0(ĥ2hî@B=h%jahOjSv^P-@^fa2'ڨ /qP;(w+|]4CA0 "TgϞ;vHg5 6K{FڑpeZ>@̮@ 뮻8㌫Z}YRUm۶}K_:ӥH82- @ ݰaUW]o߾=ޝ{Qկ~sϕ#ጷ˴|DS[k.ur-7n.>'W}iGoi1o~}7o}se]wWO?M3d1w/hgUb2Zĩ;ۿUNUדpB>!?M6lpg1h»lmCc")Ί;Z0+jr1Tӗ f 6߫ݲeyeҩlZ4KI;!Lek2ʫږ-w{ߤ@j74Uy+!orSG?{ZkmIxb/m+xC#0tꗙhv jCFWkONZ;T )'CPm  !EHEMё8vԭjCT[Z0:V0rm;LZjC'M lvmȕމj-Atvg U{yGC&тVm'z Lҕjj:Z&OGήrB^h;z."Tݶ6Qmxkٲ/M;jI,Jb-S[5u#l'Z7;6Tmm"Zk+պm3͞rBZ5jt Iۨv jA눕iXLՎ#ጷ˴|ZhBT $SW]o@;~_׏~#T T T T T ծT T T T 0R: T 0p =ݿC uZ!jKVz濕r*$AjlsP-iYm`6H8jZbsP-Vj ].W^vsP-@RP-@RP-@RP-@RګAIPź?υ|ʂjS6{3|?υsAc\>BԱތy.s!\Pmfo<Oa.6ul7õe[9콘R s]/ ~ϻÌg׌bAc?/=o c%֘?զmfXp CBN,~~!=쎦T:ٛa]Pj K݁jQmfouA -]PgtwZT4ٛa]P퐃hw ~݁jQmfo6<[$a{CFan8բڦ s$Z~k M0q'֐1ߵ#~?0cj6{3|?υsAc\>BԱތy.s!\Pmfo<Oa.U ZDvajA|2B3B3B MG%>[w]B <?E/61o tV]gTpNF~F0A=P|#?|߆kY$gN'a~)AR|#}CYnŀ ?#t_JZ kIA%>P|Ϳ0ऀgNKIKV~(PmZ6PMߺ¿p &엒65B%>PnE^r)SpR@3BI'[fts^lz3#u̗G~ثGfu灛}i)J6B3BI'jj]g]YJ|{>Ǽ:oܾerx.2рC ~.0d$ ;Tm^*zlӺ;&wv֏mE.ygNKIWw Wl[m/Q훳9+C{dI &엒?TkRWU'̛:+)-ogNKIjuxOPy[y[[!9Iԁ}_,2z!~F0d^mt=VSO%>Wz?/%Na~)ӢK%>>eǼW~~$[3s>O &T!tu䀿gmVOSCI'vH%><xlF~F0A=P|0dgTHYzX>BզdggT?LګAIrL#M4?^tL#z/[7}VQmJ2A3B3skMEV Tz|OC2G~FP@LJ' !An@rMPm?a}F~FPmC\P{pO,0gg$D;XBJ;5қjC1߲6zGÀFTT[; m UPժfRڛuG C;Bt0d>#?#sjow,RK҃j;ܛNX2,pP,0ggDWkx3Pw5xT[JVZ/of#yভw_Zl2yyhʥg f؉Ov0d>#?#mTzWgSZڕjoܾerx.rz$=> 8Y`H.զ<Ҫ7޸@s햅ju*t3.?40d>#?#IUv>xS[ώWfQ-4>#?#j͙K^+Y_&<]PWU;Tm%պe<LJ' !:Q>%U/ԺZ.&lR_?oϻ~+~Tj-kNW fYN^0d>#?#mTZ7{R/_utg䯾<ٟaP => 8Y`fRW~V"yZN@N3B3jMxVgm?7 9&RÀF!jAO9C~F~Fr}OTȴ+F4B5ޥ#TiA >!!F pP-@R!'#?#?#4zjA|2B3B32O:6'#?#?#ދ_>dD~g _[*g~ >у~ `|!jdD>}PmFPmjT'#zj3j;Gv, C|2> 6#SM50Zjw~k7 5>zj3R0;ZUZ?!U[W~Tkqζ5> 6#ն?PRnE^r)SzP?z6Zf3+D>}PmFjUz7L9]-%V+@wiݗ)[L^rjP>(*>уhT@aVPMUkjWqeںˁ6~,@gHgQDCZKRtLxB*cϵ[m(%OW^*>уhTYmpMTC糹TaT ЃhT +uB^VYPʶTkVf|73gfdm3t^uSV]ZM~毟wb)W'f6k!Dʋ5>zj37bH=/_utg䯾!!F TiiZ$ B|`jSB|`զDO@VQmJ*F& MT 0CPmjZjM =>`tZo6!uZDͨ6zg՚]s l[YMҀmQ-ٯj)̵"em:7ՆѥQW +tZeWm{}AP!d\ 4]Ȼf͛jfHjF YfA3$jrfKy\}APEF!65z|P- AjfM?> Cdզ*M .T ZDZpjj1{_D#y?d]P-T  E!ZhP-DjQ-@ -2A`DE0sP-@4=6]T 0@P-@4U[1ukQ-@jP-@4CSs/jYm{rT jŬhT  Q64T T Tˬ  eqy2T MEeA0sP-@4jA T  E!ZhW9AIAIiZ$$$$$$$$$$$$$$$$$%B$$$$$%j#ٱtHT[,i+[` &ϖWHB:R-BYTȦqNh/Z1y2Ymf7-> %lسj& HJoT T T pP-@R"T?^ڗB<(ɇNl鶿vJ $ƒ]&::8ղO"Z`$ @"ttpbK<x.mQIL>ttpbT ZDvK" ]TLuIsGmQ !Dٛ@xG3eoF>Ղ "LL;8T;[=ÇG3eoF>cª-̔ND͋ƺGCѮ0ѦCfbHQ$*6;&M62eoF>cΪ-4Be=8uި5D`z2{ղSbnMDٛjw=Cl^ZzDVػsm:d&&ތ} `(}7=0ѦCfbHQVKkJ-(OϐuesOM62eoF>cΪ5<[u'nts!sm:d&&ތ}+\BJn'1id]tmx)*)lj ŽtZ 2ª]q_ڤ׬8TZvRk## ~ $enMdSg[KY/s;8ܭs<D&*t"2kk3QǂsGg) en?͂~nTCնa$XvZpA:ŪbmIh8H6"۾I6+{%i -|nM46UECf%jE2-Wla|Q%9dzH26tHǪJ\2 ȚnOz?*NU[mP[f:Rj]/]tĵax m脹6DXfQZ kkf oC`!]7FSu?6tLL;8T[Kc-EO)֟OcifX ʕژ6tF7#usG1BCgrz 1eoF>Ղ!ijے !#e&&ތ}35D8({3Rw0qK!4T; gjp431Qf`P$b:j}jyg_^x_|'?y/ګ]L<uVWڔJkT˳?k=SttN )a8xEYmj#}dQuVB&)T+T d;n~VeVoT d;IU뙽\+R߳P-䂓}0]Y_T-o/*;]Y7xc- jTmCBVƫZ9W YTl}25dUT 0 PIZzհ߁l?mL_f7߭WVD'Ls.+c>ө6m*uٱ\)Dǚߕu1IZOJdNF-ՅR\Tg}*6ՎLP+^ˮz50a;P{Tb=y Mx`sPWm:Ub;+ o @W:~֏w1ծԙ&CV- 90zºcñ0=|Tb3+5[:Vd 3?lʄU S}mZcgn{d;sۃF'ؙj`tm+aD+ n[ # cgn{d;sۃF'ؙD0:8 5!gކ4Ȍ 0Ó}bmNXG i/ ׃LSIBa҅.NqB-pQ{xn}p6Z4=a* BQmڦgp A߼6 iZxqk_Z`-=nҵa&'K7"\e6MSBdS=Uն/m8]q܊vd?\EgqW!:\ZqDf\(<wlg6xҵMT['B3CVW۪F6|҅oy6AKj!\ϦxFkbhm[ ( OǛ-ogJi9Z5O-eX[r[z:m_ڠTb(Қ\o9hWΝ1z6u2Yc[t'F\v|T@ {aC'tHunB74O^O }iCSfI]_Q?7EyR*o8mNdOz iOP6vGH Cn;Ukm.nڡBBrJ`]~[ȆJQmҦFhzd-M۲LOQqU4 f Jjkͷ́"0gmɮĸ!"#Q!6wmjۗ6@ծSuKkz"FQH!646*,3:qm-6TvvԖy!"Rː6%-m_0UШ9BVjmɵ6/dvy%䷶S5nmoڝ٨ ykۙ`/ jlًuTؒڪ+o D?JupAT;+jg>vQ-N3=j`tpAIPZ ڗ:35D8b2>)Ac5d(ųUxiq}Z=Dc~m鐙({3Rw0q6$5ҚIܬ֚2F7#usG1[Ֆ/)̢mߋ5T31Qf`?Pm-Ej,)T[Z5ݺިwvttmLL;89L ҟmtO뻅"szm=zjTftmAk6 Rg.f6a]vA.Zx6w GeiEպvސZ Kj j=uXj=}?B35D8HƊK:f-֫juһXvZpA^%+ev"H f&&ތ>7xcDmssˈZmu "H f&&ތ>v]]XvmRi-.=+eEm/ƭRmD"LL;}Q߫Sl57 jG0 )֟O{D"LL;}9ԥZgGZxG3eoFFA*KƔ6n .jy2hfbHQt_T;%gjp431Qf`?P- !Dٛj`tm+aD+ n[ # cgn{d;sۃF'ؙD0:8 2łܭ;U[ԖTmCBV;Pg<8&ZK+`wF8fZskNw+i{B-@W\6|w1O6Rk]?=Xt01;P{Tk=a+9ǻ㘼j=%óվkfsPi*z2ށC&h]w1yծx-j @QwQ=UMx`sPWm:Uf;+ o @W:~֏w1ծՙ&CV- 90zºcñ0=|Tf3+5[:Vd 3?lʄU S}mZcgn{d;sۃF'ؙj`tm+aD+ n[ # cgn{d;sۃF'ؙD0:8 5YQivksyza'Ęj]~v=4۝Dvg縊fxEyWd̍hXcOzxì^i\騶}iRm3ՆWגPmVvQ-N3=j`tpAIP*AIAIAIAIAIIڢ(@vD]j2ժ@ i+[` &ϖWHB:R-BYTȦqNh/Z1y2Ymf7-> %lسj& HJoT T T pP-@R"T{-L6Z jC0zP-xvAETj )@Rڢ(@{Qmq(6Q-ӛjZ4ȢJu`&78ZuMx3D Tj )CPjp`6T yjfgC,ud5,`Zϥ][u%ZhD?؂j Tj )@RP-TT C jaoC{@8 ) ) ) ) )~@}l#=> ) ))֒X**+C`Pjabn֕oKWei UN0pP-L S7P?js_L,_fQm &Cu"1}Fk!!)**vj(a+gQk l0P-Lc\:K4> iymb'8ZUD$ dHZkiYTj 2?4{6D0=P-L9֟ӯ fၪ^ڵ֕HT #P>(މ^v=a&tMP/ mjajo6j]Z>⅛,3Y6F5E5) ]kz]w݆ .bjEw{n2@7Mmv`޽[7|W_ve_[rVu By~߸7n/~7U~O2ߝ;wرcw@7m۶e;s=S+Ko&1._Uc]wUW]ue]p-u։jWVVJ>#.M$x/.7\r^xߞtI]tѾ}^z7"3~K_p |#ZwW^yTp7mڴ~s9| Qg-ٿ?g?+U矿Ŷzi)я~O{G*z-U믿O?m$xcL/ u@/b(U*z}Sw/,oܹs͗_~E]4E"PѨT*b}Px ISO='Z|.ENhTd*JiCo~+bDNhr'` endstream endobj 76 0 obj 23239 endobj 78 0 obj <> stream xZ˪6߯:Uɲlm 0Csvsҩs޾W.\ꪭGO?~v}kEXNj>Z>/?WKgsO]-2 \6]4\]K3b]6_\]YeOum6oٛ3+Uwp :72uf紧ě݈7bg#;ߧkkl;ycU`p/^;C Uwma ̼ x1 D;kM vz ]yuwGR/g#\)q 褸زPQQ(>#g%BKFG/`MwXe! & 5nq `Ku?0PQzun7,͜`l2ig`<¯ KφGBfzt'gMPE*xooUUlqDw?Ht4dpghB$"$=g?jC|!ddz\36 [LgPM/ , *EPT +`=ӑй>=^>nvG>x AtR:iFˡ9)TSs<=<Ȍompc/JN$#I64[*Oc(&zѥ66=H;2?AfW~ǡLFtlM*ӊWԻ5zu KQ'(v>Ò^zIiLk^08D*}6<=-MN=Kk%m"zpQRz׆%[D{(/ WN.+be̚gH<(k5 n5,MGh&:>kyj1xb-CRBg+pʱ@ ( ?ieM7,ߌ󦋵PSKY +6¶")C%ώJg,JⅆO=)y"HVYѾG tx[ ~$Hߡȫ<$CX;7JėK<5M|3K¦MWt=+bJ),qSLHbn9Y_\ \fLRl:xߛ yі$&dݴA%^Hbޖt,TRp:RQ-svRU ]/;p¤K|{N=k#O) ࢅw?Q ,h1Xʃ` ܑV-&;*]#<}qP,H[!LB:C`M2Zfq #KKnlO82ZVQnߞm?ӎb+PGS5g$ȕ0#)t,N!=ҹB$Ÿ̷T(`n`Z}6A0&򭥶$8gh&Ц?iHK´~oyߤ?֚JAy5N7ӣ?%^vyi5M_^?T endstream endobj 79 0 obj 1613 endobj 82 0 obj <> stream xŕC))"V8~Ll5v;Y0k< a%#R GKDvrS]]]]uTOsOݾwzf3oHL|!ATT.94$\C H(]r>yiHQ (SQ|Ґp 1 Pt!b@D%LEj\FIT1h(r 1 Pt5\ɼ\x *e*JWmΫٰ9-Z7l}jU۲L&/*e*JWm΍^蘠>;p߄k8hPt-x6:n!y H(]9-8wckcg<-{[@9J@j?k(OΛuzu'33rw΀q %y1fwᗚJ@ӓp鶽s Iۯ6WjtŝϾ!9xwl`/ƀ KeZ e@Kt^l`Qn)jc3>W5tm&&NJX :$n=ܲ2}'s=J8kH*gT5D9V|JYm5w s>nm=?QPC5W/Xɋ:#X brӅ3%a)B8p`I^$ sp 1h+yQgL+AFs\;VfZKJUРnk@aGpV!! `_>Oʽ~k-J1ds,aM/>"af&p 1 T vkO~6 ; oĹq 30>k^c]ԞE9Vn3ATT<<ƽ^iRuƀ4J@ҝ?s v܂Q (SQs=ltl8ki2KJ2O/b@D%LE60Ʉki2K'/  *e*JO^!ATT3|sa4$\C *2ҕ !b P )<\ hJLt!hH@TdJҭ3`3c!Ƣ-z!W#S 㳆@T#S O Q7Wu fںuT [hD8!,u1 ;-@FP r1jP-a܂Ϟ܂xJpv >{-#;`}].N_㺅 Ig{=B%mYC *A[s(|nGh /z 1[#if5eGhK4b P ܂B[pewk[8#~oA-4>ng}w:g 1[--ϕ[iq mGlࣗik-4poA3y"lJp[LnFuˆ ګeL*@`0jBmZ&c-|'!a[iwؘUxFYC *A-VDN0x $B*1u ֧n6Gp8܂ĎDj5] a<Xt!hH@Td +9FC5 S(]yȹ0!BC΅ѐp 1JWr.k4@%@{GD5$< 92tgΗwнy맟Bh'@'#Ȑ iTywo\}n[E}[&F“`Iϑj-@Z"nY)5>E7q Hx89\HKM>#k믵;bzJ OsDW#ntcʘ.-s%#g,ڵn|Ma+ OsDW[X؍cU#tq n\"3_Z-#I0b-Hp5r m:Vujm!Rz0*G7|7#kV7޾9>EK^-#I0bHp5 -=^\{dqBr×,o}3b$< F  F\H +O D'#8[(]0fwo\W +: +¹'>~h7޾X⋿$1ps$u 7n U΋w.nzӅַ_]^.tKN^:pEϕ/$1ps$Bswֳ?!wJ ?g,]xB?ȩ_p È$1ps$q %;WpoA-A{ %;W-$\dbwY -ȃ[zP2p >UJJw-[=rfy&) nju|N{ LQUsnA!2ҕ n Bʃ[z`ȅLt-[=0BP- 1v8 %;W- >!IJJw-[=(rfMp FIap ܾ]*jhns*]ap iIh c -ȃ[z[puǬ-ȃ[zcl#na>-ȃ[zx~k0'dT nѐ^-LJw2-2rx [MTɨt'nA!2ҕ n Bʃ[z`ȅLiG!Jw\p @k;!JwDp @yq迷Ξ_[ n0a7bo؁n Of b$< F xhnނҝaFOO3a0Z:~tތ'B8plTnނҝab;TL)1Wn}.-BcgN-ȃ[z[gv`Wy yp @f[< n[pw ε-[=L-8&&\-ȃ[z+01A м8nsaVm;0%p t&aFO8nkȝ U [(߃][rq PWN܂0-p S!2ҕ LO"{”`ȅLt-{ Wip S"!7[0=*i[{ ܱB,GU [=5"ҝaC^CXy 4hyN C- nAnANds`m <9x sO^; 9/6[ O'=Gqna6'vGwTnA;!(Hx89\Y$dBp poA-ނ{0 y @y[-cfiE3B1#NzW#C.-7Hx89\Y1ɧJ ϮmV쿴+myyC!poA- :r 6Ňh,痟brB0[{ -t-,ݻ_}sރ/<ϛZ|_z+{|na p Apx҂i 7 t[ n0nz[z,IP3k-bn![`zč^nsm -[z-hC57-[ n[?P}n|B ͵͏0 WcӒ -[= Sp Iʷ}W& n2 nAnaO-Ln𓟑~--p #[(s nAy0|nazĻc*:{jKnA܂0[=uoa-ԓ)?cvW,  nAzc}$ë>XHanA܂0M{ 1♵كo}8vUSoi;#Hcٺz;$G<-- naJpo!FAZCjo}[ n!UzC='vQm޽yp @[9hgxSA 8uf= iʚcC*[ nL垯y*a] s nAnANdˆZfkϤz_|O>t[z-,,,mc9t^Tn07?Vѧy6naap 3;[غZ*p @s }7ΝO -ۦ-];[nA!#-sގplֿzʢr 綖ԖnTٽ7.8TőS#I0bHp5rBpuv0Apl?[ۢ(bϲHt g6-,-[=L-87w6 P/gR+jK7 p @9M{ B>sb7u|fb-nap @9msz~߽úA-MBI[܂0Äc~oCn&` q-[=L-vO/gn`c7[XY꾊bWX-&LuqϢJB-V Z(-p mA9upҁXU[[z-@Tn፷/vWOҵ)!,Gax#abr,6v|$z ap OB e~q/_ݷ\xqŵ ˥_޳p -[=Dr iXap n es[z-\l摢bϲH.l-#I0bHp5 p ;871}C_UK5\\Y-#I0bHp5ނz-S.ֶcX[N'#Ո[O["B0>J? -#I0bHp5ddaڍeG`Å0"1#NzW#ne*B0p} ڕ 寸Q$1ps$q (S~'O\[;gP- 'F“`Iϑj-L[>\t*-Hx89\p To_2Ek/$1ps$n@3 ba[;~\yX= O'=Gr(]17p;|uc[spŵG*T,:|yw?- !F“`Iϑj-0JjO\C“IsU@.Psa4$\C *2ҕ !b P )? endstream endobj 83 0 obj 7485 endobj 81 0 obj <> stream xyP_+(Uoo)RQH U ʨql/dGI PAds:ݝgvgvg9{<=@L 8@h2 "k! h "0(N\C A&D`xԧ$|K! 2 uƻc8Z uh싢8Jn[bc_*0D_L hʡ_4n+GSU0D_L hɡ;ԻM I'dAF*xJ#߅Cd-jN5D"*OHFphۓ݈Y+, DKQ~-xWFQk; [c ?fBtD_ qM9ԍVmYOl3<`aҕGzצǎ=t1GSC)V8V9~"ЇQU^ZUV F3!r*ƺk )>efҹo hu#`cu93+dũʯ^9s[4u#E?r\C<"`cu33+dEƺוUTr` 1BPDCÀkCHxlxQW9~]τ1YT_?,'hvlPqЪXn5w1>F݂m_y Z@(hp } {ᾩr7Z}HUD#055hBYKC@d4pv5N̅m3gKFB¶FCYKC@d4*GZ8} ~-]f7,]|Jsh4Qm:M\{P֡͵_eڡ>h Cqh>4@&@S Ё4@&@SЁ4@&@S:9^*xqh )>:RNޗk-}]hLHrv)x@d4ZpE_T|$th~Qᬥ2 (2\C@d@ Qd4 >@(hp } PD0 "a54@&mh/AQ^8J< LÖ 3\5q_XXxfs{ݡ-1II"وC7I"+"SNޚ{0ۘ-/Mw6< Lz;qp+=,lyj JV8":pt{{5PjcC D{g#= Sy"WD[Ÿf*UE,5F< UڏBq{g#ew'/F"WD?az8븈XQסb+5F< NzوCCy\{'":-Mo۷ĸoL{"ڴp$)y4mIodwu9eیzوCC4 m*rEqn٦e 5-}\D.UN5 ZIojBv/폼M;qphk*&hȚ 6z;t)(d6< IE"ZެjJ"OރF:(Eٝ΋Nm̖$0=lġÁdɊj9> OBLzF:(mΝ?3k޳bKgt&,sۚ#4 OBLzF::ZSSSq(+k2jڡ?9PġOB-㝍8t8JxBnݭs|Y]pU?L<7ޱl7?ly[ГA9t'!E*3."SA?}aѝs?ݸO=v>qn]>]fuOġ޴1IvlġáC&p6,^\P֡87|dK:t z!Lo}ٛο'?3O|3vڝ+w^-_q\h P !{_ctb&)L̿j{_nr26-C#CA84;ߺdˁ_* +n|h-[ޗ[YS -C#CA84uxtsGϜ|ΕSJ[Fph#p!ǡ~; zҖ8 zҖъ-[~c-4ūv.2E4[#52eDr$@ulġÁҖI2O6Hm|>:U&:'LBU^ wmiáy%Z>C -ǡzsr3ۍ"CG+O#ОC_8x_:t4H@Ɵڲ;~r_97Cq^C{  Ĉ^}sog3ϟpᝧKK6ۮS4_^n>ׅҖQxe9=X]p(P  !^nQUvyUpUAi$|=ts_[u(0aזm.G~$|oRD􂺥ÆCiN@h|Y7\c!?9j+:rE-6uдȴP:4+w84o_ ?|f}j;ԍ, Luh#&š=`֘3_ 9:4G.8T 842סuh/!Lo͇z'wzM8T' *HޠʽzhW84}OE@}mJ[&8tJ` zޱKK6?bQ_vχޅCuǡ¯Ep(Bph ȏx_~+w:E84 *׭;WݕrA8_/=S.t:TVJzhH>C -ӡrӡ¯Izw%8P2 SQswhK?qh/Җ9J\'T *J[^|Ghá#ni{N84pEJ[Oݕ m xr}`һw6@iDX$CAC -:42CAC -3.//#Z"OBLzF:(m"#͡ m xr}кw6@iDXg+ܬe z` N(mP ͡C! C -c+^inס-X3 Juh|x㝍8t8P2iZD/FYm8k8P28428C -ܡ'f+n o zҖIry =67Sz! -ߡ\.Ϝ,z덳gO;ֻ};|p.]ضC͹V{S.8Tx}"wCA84Cm|͡ŗC0 =2 7^FCC#ܡ?u¡`h #ЫWPסY{tj>wy@ LBc&Pc̜@M.,\pM۶/ި^ҖIFC 1[f, O>zhEUW$m:tJ{wZġ`=;VgN׮]{.Z-Te=Oil祇{bPCY )LrwsP2i?B# ߱x=CC0~KQfX9}r}ZҖIrɻvP^0[qY**)}D[G/4CC0~ 4{nN#ѡ9lŽ}(>$LBjktr-5sc[JܡC QAi$|=Tphv1ġ-.U9=^.R/7,UCK:^P8C -֡1Zq(t N(m i^9LFʤC'ҖIZNn_ܬtᡋ{ #0];qpephdp(J[Fp(8ephdp(J[Fp(8ephdp(J[Fp(8ephdp(J[Fp(8eNQdNJ[Fp(8ephdp(J[Fp(8ephdp(J[Fp(8ephdp(J[Fp(8ephdp(J[EN8eE]*w|w~gs-rOBLzF:(mYswWmo\w8ԃ6< >0];qpexUXWv_-Ǿ.|xƀ'!&xg#LV?\q7_9CicEJ[&+"SNޚ{0ۘ-/q7m xr}`һw6@iˌlc<~OBLzF:(m\8":pt;OBLzF:(m\mٻ"9zƀ'!&xg#LWƸXCicEJ[fl˶}Ӧq۾Ƨ?isoVD=CicEJ[&WD/e~[EnRM\.❍8t8P2E4sdͦ6p$wlġÁҖ)ddd*=Kp7m xr}`һw6@i˔Q_P m xr}`һw6@ih0~#6B\.❍8t8P2Eqn;^sQu s/ OBLzF:(m"ZެjJ{icEJ[f\Dlq7m xr}`һw6@idEtB5\wƀ'!&xg#LVDΟy5Ygo{`֥3_^:Evm͑{οwƀ'!&xg#f"\%Ut> stream x_W/xc{a/1pC…jߔ VvcPbDBe#?" լP(lqqwa]vT,XkߤicTsΜ<|?l̜Ϝ33g\!B!B!B|;vXjղyB!NV\}?׋/˽Ν? ./!B|ӟΟ?_:vZ_z//^'BE}Xx޶m<{%1 bE3??&B!$ ٳǏ?rʕ+z-'N+W?o,!B,}XX!V۷o_nHvŊP:.Fwjjj B!XWa ٞeZOFcǎ0J!;pѣGŰ;wܶm?$+}^wށuRBZ mjbKq{{i&] {ԩ_cדa!Q(X]hS}W( H={{{{u°Oɾ[,!n}:k3:h'''ĉ>qlA{fMMM9nRdꩡ?ѷEObhcJBJ1000{I`~ $qJAç"w'kP uN0lwI[],r…g귏obOBZ4k׮m1q^t)͒%K1,Qޝ;w.XoâEPiH57oFCy\~I꟔ b5yѫŷ&M %K!%D my'nѣG͛WaӦMЙ#f8 7paX(SիQi8IϚ5khh%@ag|(1z{ p]=5,vUc! 5$.CBZ~oz:Pl\gvÆ ʱcl&[gϞ 6Aa/^n]d6 {[nE8|XwI70az6T&}r6dnǝd(#A*n=$9%KHK fM7$ vzO>c >XFdI$,1{C<'UHeJH pBfg`K4ի%+V LJ !mOOp rԸESqru}IYTŽOd[dx=glD۔6+(YBynBQ@c1! u:}{A$ 6`m|eLqԀŋUOA$jDt?61vuAVCoBt<+Ő,tq׉s@5Fw`$a_s'֖XqԌ1j{Ҵyd$xR&Q_H?]棐Yf:t(<~dzCOܹS3C^oq2QzvQyGt]e&>\w !e+\%XmÄG!yU'ȞOT)sN! +VP:Y &0 E " BB [W3V۪GchYq*>CKY&pyNMM!bb=bCVT .E|6 ~s9'yǷLum8y#ۗ^n=**Op}Uc0jqU/!Ć~Q4d|"FЫO*ߩޜ¥M>ݹs'( N OEh[ \ykyS r(N 9Wΰ3>/[[]0izw>y,adbq|59b%kk"pę i㶎bD&OruΆn !6KmYzG2޽{UOv{-"w*;2j(֛]Ydn \9;䢪>P\ͯ(zZgp|] TSݽ'Ʈ]Mu/>c\OF2.e3>l޳w{1?qv7ez>-bDy_:b36裻 A.jzrR P/Ȏ\HՁa@SCI\fL!\9hooן=l zNKV]~.d*sǂbNI6-G`Ƿn}$˳;I=fDYKB<xώ_tYAWl_5B=^ՑԕjHpeI$<Pg׈Yxӊ:ZU.7E]y[c V1C D2죕1~Y* /!Ds{v yn408n.ek Y$8{YY=|eFC!_# +W*YYtǷL豺oYEWBH~fFw5ܷ2vH;{cۏEpalWNkglȝWq|J`gS>rAڅk=Y|g| >ےe8t 1`ÍNCǔ,!G`UmSs۔'s1chuAVzӑ%XYpjׇոCz$3z荶/=3'{9-Paw`^} LEzj2>3>,!>ũ0͛O^5 P.#ۅ×w}hI.Ν;W}w܆ $p q) PzH EP[dʸvKV^K³Xİ贊^U!7$ヺtMQQ7gGSՌrUqW~iү_TpXꂬL%]`YVlO%e =u1yEG>Ϟ=YcJBRU)39&P?6KBHNfFf1^V[m*);w!E<P5,Or%Ё]fzhѢE2?BA"ew>~6^ 1Їca+.UA|jI\Z>~=K=.mˈ1°2%)*(YB By&[ tWƻda rTffKއ o BqАt՝Tk׮vFܿsOr[]8@ٳ3mKrV0>(:|U;&''!,vܹh÷KX]N 7I2 L@vddZwl.y5IYb惃-,ykYW ĉGdNEV@|D&zO߹^oCgX.kaOQGi`\ GcJ&0S"Z#򜬨Nӵ}GqV C`߾}/i3-{'鴪lwIyVKk^^{ ={vU>(w~guwwwamNmjIey C@ZMzgҺ_p!ŋCUw߬Y]0Xgј# EC? "z(1@sm>KZ‡uk1 9JueeɞA# cs#")rq6`HV"e#< .<*з馛- $cc])\"`ڱ9!beP|`byW_y_|1p'|پJ2"0W[n}'|A1mij[_3U(i"6 ّh7l_@pX>h̙ DdC8 C!6D s!ٳ2+#z_36G21}yL_N"4-!C!~sa%bߍͼF#D`^L [*WTX~9gַEHWe3oGrMp)% B9ѥ<##){գXo$Oǿ4F6[l!k; v8'Jl8͎t'&l"S,D0:mJCF0J&@ /s=.ㆄT)>Fԣm/H %@Z[n#E iAS]E2.?bst$q^#Cȏָ-N$+;aIVnEG 6yti=ttt0 dDn6#W!St?#A0"ux=D1 .H"Ŀ$w=40%6gl+}IK\4=$2"W("&%>H_ Wm%E[n_-8ِn8R2D)hΘI:ʪTuVQamiK@BC XVWaIgb-p gx\ ȆɝȏD`zZ!b ð*"6l%7Mɝ")wLasD`7XCaa7)5 O}JPiV9WTņ1y\#alW\px4~؍N/zs'NIaq$`%H3ÞZ)YWs&-)<*p<+cr[lh 3ZE>0j+v|f~$ #ʛ%W<0;YGɁju\byW@<$4V gi ȳ\r%Og_@ۗAvޱE]cҮ `k{6d&Cϱ~dlqs哓+Y۞,]eKweV,iij諾9rmf 5>D;$q4>;dfߧAsg)\.[95Զ#ZzSh}dIfGT/1aE$Ԍ草P 4FnP_ D68m%}dwMѦT> Q͒ VƜR,(z+X0*yh.-)%KJ)lh kX,i&rҼa3RnޒRe%Yw):U:ݼ%-e$$#%;66644`+V(r<#&&-?[d漄F_yie/7D~n_ty̜[!Kv֭;v옜@qroț_x5k R+?[d漄؊F_>(NeGFF۷o޽{yUV!R&-?[d漄F'֍OknϞ=E~ݻ}Ywww/(gA2y%"71&u9X4B?$ꫯ+/b c+DN&I`[>>]pY<1E6!<O&'͏loIF897cB9Le .!ğ tmnw<У$ry(Pi~b%2݆ugɓ E#Ӏ'h 3և;yoNζ>2>J|Gn s_wװ-}v3=ϴh+Wx% $ M!Yvuǘgl&3-'Y !ğ\_qi(|J < LO&ټsUZg#@ߓ%T4dm[NCƕ;#M {sG}ۘm}&5愒%$ )%E6`>[Rl2/3h$K61y7dsszf$YcZ,!i d[<>Hq9Mnk $먄+r)%5dm?ώx|racmDVW[XG!dqI#ْJ4PMgףY̜J4PG9/!,!ipHB!٢${B!x_|SBHPBHNPBHNPBHNPBHNKF!',!,!,!,!,!J'BʀQ,!JBɉ%V'`1B!F+RBC~mdWB!$6Rr&B""Ǟ,!=!I\01B!Fӫ61%BZJBɉ%K!JB JB JBɉĒ%BH,(YB!$'%2\eV+ eٖ-apRRapRRapRRXgFlrJUK.\Zf)Q֘6&/'?\__?ۗ_#oɶi׸+'1T}4r?Sm^@{lQ'X9WIfد=Yw8ꇒX9%JخWpvlNI8$07|]d5VG'`϶mQ3W*V9%[bq"KLVևC9>*/6^d?kݻ̗%k?e.>2ƽVvCwqE6ɺ wdY X /Rd}-fr.>GH6_91{Mn~(<~D F5kpYGS&`Î/.K!=YʔqzK6ɺ {ҿ,Y[orsޕW'$r,=caȥe$ΰ۰/_.\dkr?YȻd]G&TÎ/.K!mz12Bnb>'Ȍ#ێX _o_ch^"[+U^I6\G;$6l$ߏX¥jGMnRͻrX9\T(IsapRR,R_X9.\ \vDB%K!䄿dkG-++Qmy)q!$ovUJU1X9bU AHY?j(Y[n/߲rbUJU!6(Yz>'kMA%뀒%$C(YB^&ٶjk)Y,!B:0WIfد5P5II!-%X^ð|uQ=ْHM&-1d1ʩцsxIx}ޙ$M %X^moHfUNlY<$V\+YۿEZJց۰}lֶBO;orؓu7~2֝1WB:0u56ƶ%2_Gk d.نT,icy!Pa/]wKJVvQES%32V+Hb(YBn^|%{2n!m!)rҀ?w!;dg5P@݆&|G0/T$Fᐑ -fw dAyT TJց۰W"l Ul_WcJXwqQ;gJu`,/6旬mp߸y%k N]uK֝:!%Xފ]g|r ܩM5-lfl˴!K* ^RN(Yٖ7P(ٖuP JA | %[)X9bU AHY?j(Vq!$ovQU〕CHT(+d ! JB VV!yS,R凕〕CHT(dɽѰrĪEPc+d$^eĪ4BlPŭ%뀒%$C(YB^O>9p?ɽGV&u@!cydm/vB*$R(YB2u`,Hao=ْR-2+ yiq !- %X^moHɖ4 IYJ d wlv:qc B"ik?B!H~zU߆W6FBHkCB!9d !CB!9AB!9AB!9XB%K!%K!%K!%K!.+o^ endstream endobj 85 0 obj 11352 endobj 87 0 obj <> stream xYˮ) W΢ nuWI|@fi((ܻjcc]~mW#!/϶.oq3J8x|M< WeW_ fYa[&iӑ%Lt $WGKdmlT6*ݪD4VΡz.[޶#=|fO[OМ"g9.KJeC%)-WToe\ -S=fJQPVG~fW]Q[ە2oJSK:%+:xB\hio*+K L^z~L:fx#LQD~N0\3hjX`U#߀RtY˚ UiZ.4fT0} Usx=Vcw3FϠ |M}59\B&}.جR,rNFn{4此C;`[IMضkl46C\e?׊9& ):?hHE겛ڸ4 ,}tU#2!M~R9 S!|G of#5aet+re—y<6ϏGhxqrA::E-W'v@e: 5 Z?qw\04'?`Gf bD5ai)4-ʘGGIZNZ[^RKb> stream xkUW?ߔƋ@śIXm%X0U1MRiŋa.C$F=.˳>~xu^{?+z~lߝ(~ϢH(z`loo/---//ommh"rwEQ5"")(*ZD$EQEE(hԂ!Vw"N-ZVb>11yNغv>G{EQwHJ/8tW/)|mԳ]ۭM(x FI%._gtr)*s _%+WH7=M)?tӪEe("Qޫ]AlfJu:FOjmia٥PD$eZ7.џEюT~cBv(*Cjbz}G(M‚=ZHF5 ̈́IK>{d>VhoI٨F9q/M#;D9b@ͦjLa'5)K'H(IQEQ"")(*ZD$EQEEˋH. &"5h[3u9[/"bFdVN7mڸ7m`^Y{.}vjHaaDyDd"2`G MoW5#Rv&L{il#"3-tOMD@kDdTID@~Dd "" ?"2v_9YjNt C "30f "23{k "30f "23{k "3辷ݿӦ%P ?BD@~DdD$#"2FH{o#`AUl Ŧ{o #"3hy[<0@c15D6d#stB!"3ID%P 9U|-25k""30f "23{k "30f "23{k"KDYdfo4DdfL#ADf`fo4DdfmX7(&jD8̉FdRjG'faWFH3"u+wDC^,>GRkװ]t7ciwWMcC^2"kj/H-t}B,%eS$)E7~-r"_z6\#UDVy~14=rxՍD$0DDF'+̭]SNrD[?~;)ƫ<{( tZdu獄k>5k"rƊY",iaʄ1$'$Z9zxj#_}v;y-" ("ÙٔZY"v‘]dUJpID |G&VaBy>`L/?)EDži" pp֏\|AgxKzyw6mhZ3,31Zų|X="g^@t*"?bۘsGDBD^U;:Wna D&"wkGNno_~+G^*x\+Wnw. 6zXԃ=wQ{'>|ӧ>}~/ߟEDʈ,#1YDm`ID깍%"kfDdSn#D$GdT8Nm."y#rGD6BD0h GuF^Dm`^߮sPr!"ȃn."y:Ԍm`CD깍#"H="yHy#}l "eֵY)fwD$@Dʪ,*3o޺ADD̋͛ͯ>%""RE'SUE3D$@DʼS"͓D$@D܈w,GNϖqHcNJ?*"^=JDD̍ȵ+N&zgH&c#"EDD,# &2/"^~gU<(=A! 27"ϭ{?ZQA, 27"\zh:` )"//־y1y{D$@Dyg}_32,s淋oN\;rrw.~G^9RQ㲎_9rȽwHAH{2FH"=2n#D$  <"23{6BD0޺#"30n#D$  <"23{6BD0޺#"30n#:"23{k "30f "23{k(#` endstream endobj 90 0 obj 3688 endobj 92 0 obj <> stream xXn@ +會:m +z-m'QC([kZћo˝y"5wdoC0ǯ!}4IPp>b鹄yGOIVi`=5 1t9n5xxnd51 yyMH'xH(#{rI0qiaV"z6ٽXaQOTFx$t%A֏SK ]<5 bJK+mO$#i^8GBY*́QQ-pGpSdӯ 劗-v]"tFǤg 5㔏':@Eތr#noRgcB+9jTP0JH!)-fRF-:n(粟@$h qz%CC 2ePTFA>1+guz`6V2f;YCHcK#QOVIMA6 S:l3sMS9:2V .Mɉ.e aIYE[ʪ'&` omMigAZu}\+>z%5w*{9ѯk{e_︗wPуS3ڶңR6 m>^ HQ;R>_MӿG$OǻY Ń c endstream endobj 93 0 obj 751 endobj 95 0 obj <> stream xW@ [_)0H.@&K")\Dqr9{cGhJd~ݚϟ󡺉Lu |9E9g_9b!-*z$65H=4Ƒ ሄ\χwRhr&]J9%LTc0da18QŐ̞ ?YriFٵMr05Ҡ\?R @%%F;<1qAjhJ2L-]#kҰy]ݶ#zCxzBQq+}bc̢ , ,QJ_ʅvˣ6%֩3 m-l1'u tGr24p( -: i3q*&LXbrhPx[~W a#0m۶au [l n$+,]"(HmKRL- ΀Kux;\s.~d)G; 4!1sb_pnvX݂V-o1؃xhvRAޅRԭS$;GlND4 کw/{ҔF8{| 6-E6c~gvQZ>!Tix/6ɋw &w endstream endobj 96 0 obj 710 endobj 98 0 obj <> stream xXˎ0 +tރk=HY&-CKNDȬ(hhhgtonThHnJw_/P@.2Dw>ί1gwy(Ͼ,Q'95ը;kx=3 $k>GCUC##:ЊMYwS:ŀ'),c ƻ{$k3IhGmGr#ţ`2Ab#BGY ,1y$Yw8|%*9?V[)}TFiW!Rƹ%+%wsT^jx<ߩԄ5d4(+q2` lCgrmmD \%ds\2YLI8A);PE##`z _K'+4M'`$`3l_2`aPe+-S&ܥt-Do\4iYFҔ(^\6G^ތ#τ邶lmc0 /yh!+-N"Kv^mkڟSZaTXԮȳ9m^@u^N{ږRsd+&mM,A;wL1U H Y,zz6Ć$h*l@EBK6xxwTP67~Z?(yu䵫h:Twh0@{>RW[XRo_>,hn32˭[v[֖IS7xBE4!O|]\ߺObNC{ endstream endobj 99 0 obj 792 endobj 111 0 obj <> stream xSKOQ X,: bL1q4hH3. +vJ"qaA7.и!Fְh wcL(xf219|{ 5=HQX-DUWd ILSQ7g@>< ~r{Y=?)pw3jlt@AԤ沭4jØ#k%px͘y7_-5P8tm3HȆƉ~CAg\k.qbz⎀O$-%yu(M&/SvD-NNNs{9^ގ5nQ9`E3PG`}=t0Ǻ+9X~`g}J *C \,yM7QCd5c3 -YHr>rf*c(~o_TÕ Oh dl9ʐIEypMXFaWQe(g&$!M1MA!w Zۏ~짖 {Fmj? J endstream endobj 112 0 obj 694 endobj 113 0 obj <> endobj 114 0 obj <> stream x]Mj0 >ĝ. !P d6+;yBO.W p +'E:UͬےpF7-68<0/dqpFm GyY( u,]ڎ oAޣ`q i?hrizwډa4xIukkm}ѧ+ߕy`V"N[Sb c*t] endstream endobj 115 0 obj <> endobj 116 0 obj <> stream xX{pT?}&wkn6&!$d ($A@EVfI(8@tb 6UNՊ+uбT+C}Tf{wIwwu|&菀P<8(G5|{RaynG&snutʝ뛖v6ݾyfokC^AW$^rXNfu<ج,cl> ĨNmJKeƊCvZFkB<  齩SZ.ϑve^Z)UMQ cTATZ /^ͲJ exҎݧ.c~DUE2L<7L߈0ߤtCl@QyW "/ x B|Gjy{b q;6PÉk˳gP[ir]KǮg&pics,WH+Mhjv'/:fw(.-v:x!Ge*m;o?urp3{߽\o$^? `f*h5`ڗ`@XW ^tq7fmnV4jAzm054xIg1 _֢_"XFpYwکbN˔0]MFъP0g|rjk%3kK(W(尻\vAU0}C+몟8Hn?PMK>۶qw7s՞XMQ[5ѽdܟ=|2PNd*Ys.:ʎh@k@ƪˬ2Ҧ\eP4QhqiG#_KE&$;y__'dkR#Cv r0ǭ6AFau>M좞Vs]: K崘Ҁ\iGf\\̬?~ў ^W^Ʌ1CCw=9:ٽbF>aE|')1dTV͠ר Kw ̈́9SFų\,\M R \ޤl &"G }6ixr8?0t1L@% */rAIІ+"A=0Ei"ToI3 7{<)ply6Zyҳe3>d oø۾GƴoFxC}@9U Z8xQ?yq3V75N0O/xҩ߽cˏeJ2b9;n()G'R'лʛ^qo M7v!:24X-WF-xwh]ٕ;)!dv1 ,DL 3?ykٲ+Ƚ--#{$!"k"++OE:FV#Ktj:P#T5U*%HA5ɑ*sӰIt$5Ə~4:m )MN UUkɽ yy;Οq Qpzh\p1uPE\r'a { c/AZ].'a51vf6y%#Wý_&AA2F>AoF[sHq Y4[ÈWd^Uxٮ“(<d(lĒB%@![.Ҥ3Ef@Mb$ʴ4f"cRdLeV)&EVC)"k-Pd!E{X|M"ٕ=bRl_#6G;#ݱX-a?>p2×xuK^QPo_s#TMzqa8k$ʀO]ʲsDĐtFDCL&BPb[~iėlKDgw(+x}y?1;"hgo=tɣXƏ8D "x 37xWԎ=DhD1cr7~vFű~Eab^OGo?ǙVX:WKٶzY.FիN{/A1ly2]GT=%tzR/D/!"+.x6}Sl\[<3]$X== ~4q7w\+~Ғs. ԆY endstream endobj 117 0 obj 2838 endobj 118 0 obj <> endobj 119 0 obj <> stream x]n0 }uA`nNUDdLiQ˱I2{[3Leo6{QMxHise qw F0񤪘!~4֭ eu  ,V`כ+$%+J5Q2R}8"oy}yGAG8rAg yHȏmk'gGnH} N⽂ϋ q2ys.5.2' N]^ endstream endobj 120 0 obj <> endobj 121 0 obj <> stream xݼ \UU0^rn9"x@7(`K" (h蘚cj`X9Ӥcf beVfjMVfXdZb>k=_X_B(,mE6yIJkVF7˜l]u/D(U|tw(B2$_ C*  %oF&{jN p1۪+]0܀{7Ba5u[Q܏ZWƮ׻ށ}>MsDI ^>F?2$f j6q I#SF:v 2'Oə:-wz^; ]?w)ĭE\ߧ׉O-ehEnn@ayV̝ÇCo f :NwJ`.>Ewẚt98 W'sq5Yr36mIT mt512 GF<: \);жx:xu0S<P-A;c}^ *v~R$PA^'/c&=IJ\%pR2prnF CNkĢ F$J3\; cfG, }õ8i‘9 {&<9,_o,p?܈_' +-Bq !d@DOrXr4m<7R{_)_oxgܢyoI}1&b_l'<'^ݠdB64t6fܦSIm $j՜j3PL{zn&.edT$4I~W><ǟlX<6l{J.KN~zopĚ5w;–N>ut8E@7Ah2T/bTOy;@)4rF%'!/-{}Sggux;;;/~zqɧ - )YPWl6fC=Pk$fLTYL6@ttd"#8̕5?Pևz_^~\>:{# Yq @FxLtz r1!F%~`I ﯎`e 0̈Rp{ENzC>cc-dS<֜J$pYa 90>Z¦x,O7#7/U|Eg/@¡.y_|3';/ۯYݎc&~಩'%),ĘHiIIaVxWR\fi;vxu-/o䓳/|t(Gσ$E(58^[o jXì@&|k?l]a:}XCDIf RD cF+fK**kP=]F`(ZDE(EE:I]fn^eK_)Zu5M֮\xs^>`\ܝӦFz\pddWJJuwضO އȯۅ:ّƑ8,fhx!#>v=t_ź/1&%Kf@b 3Fĥ?qw 3>GwJMU)u:qw:/('oLUQf~ zpp]xbSnׯ_|]C O=Č ͣ*RVw =x\;߷c^y i;K*?MA=܌<ҏdv<U9I(fС@d;<ڵ䃼u!ƋŧF}骛(URSʂЄ[BxBnvv;{!Ѧ~)q2SX15߭x9K_n{|EGtbBKUsṽ:zuc7]PyXz~12ݎ,/DUhd5Yl@_&+논q1_ 8$5,_uG^+_Jc770rdc1z bQ$oS(shDM&&D=;L\<8.k8"06/on>VFсf-/Q74HWȱ L>biOސYƫ,2,x4(9&' B %h }Z@%-0-(-8-$mHZhuYǫ:Y][o h i mFLo(T4s"D?c=f.0*ȡ3uՀrOFOTtJqONI}\2%|w%՟[Cŋǧe2g:1 (! rH "b}2[JS!3Wӟnv@8$?J"7G4ڍv);(To^$C?~F^ i0:ht}ZɪG)fܧ'Ghlr /s睏"nŗ4JS9h5щv$DOx6_f=/"MtAI)Og5$fETvp8syi{W@7#AÓx]} C^UL_]):}l{{կz\XػE89r뷨Z/%kcMTeq+|M&%FdHvsy}R3w?4~q4oy),b 4*@ެ{99nk8-Ëъ[MY$H#9q|_ΛG>o|5iՃ"6MMt'p䴽sdYg=jM&zi-J)YSAkH|րfs|ر;h,SPҞ$ƙn%,!ߏOp&pTR4fyxM7=زy+[dG7>V\>8} 'n=$P\5X!s(w_=/hڈ{[O+UotP[Q QϖZ|W<  X4QBffkh*QZK-Ċ1Rf,!|2[#ΑfkUrKܬrS2['{;i0ئ݌|D䣕H!QʶKZ^I!#MJ` 7?yG؉gLC>&ݼRɏ:zVlWcJpG#͓ <‰+p3h!iݬ4{?Q?.ruvrjAS0G> EWم[v`Qj;&Cݥ.P+OCU{X@{/tm}6oԴqfr8&U$p# ZFRK>Lt^^Ig\Z'gZ:6'8Gp@@zpئmE% Tۥ ' Hg>TqgԎY'Oy&'ep܊kpn>cx}@LQtD{]]0}un" M"kxP%'{:w%~<\'I4tG|;BG#>Q"ww wwIwi҅ЅzT6|UaVl13SL۸iMM~s7\]=c1yNPē8£qnx-ެT h(‚tamFx܄Z+!7No6+?mbGŴ}-/__Dv~o#'yd7 _G;;:nUxkXoO>~р04boI׫M k*?3< kJ"a!w;}OճJ)9p;&Jn Dmյ/-}m۾i*ks,l!aѯ?WQCOlz_Uu {fO `)G tP,-M%鋯Ol/x-kM#\plED$/@>HigQҜdC2W:#O&O;!܈7R$A{DHd5Ϥyn7-S?)Y 1ѱcRXnym\4nqA *m篦;vM)`3t.w6dP\Y hobhu{=Zcha 3 P. Ⴤ!]>p.J."5Tn*Jɚ,M6[?0 2R2K2{4G肸0@ta.FMqBn54kU¯tku$L!Ñzy9~H\Fors7Wڻz; s;!1(PGw{z&s<"gDFq)#ͣI 6n{6|I/{+QkP o\wOR?Q攑^`袹GSy|?k]¯Qɟɋ*;|ueh69UB+@K_ G'u>Sׅ7vt)T'@}1ra EAwtL{AXsPvXV3@4G5[BP I,đ#8 gkgo =4J8qAb64$1&d&)kq'MOl1*70'x7ӏlܸmg}_O{*)Q?M'm։ ŝ4g>:tkSКk'ýޛJ=UBCޢ++h6b[@d-7 1 !@4Xp@& a4vyrƯdUWcH_r?N=gʊ82r(=纺E-{+]Qwuow4<\+.ao,20mĈr2Ya GS1Λ 2Opؐh i#,$@A Lݝmz8MO!Oh-YZ`͍D4f)(F>I,3T2a؋xA諗 tȪEPo}JPC[y+pL7 Ya]¿|PWmwN;a]eHƒ~0N3V;R?P1>ѻ:I̘sij>Spއr: %f/sz WJM(cEh#$D{Pp9Neb 36l5.ڂF6-_` tZ}Q+A۠f~JՃHuwˁڣ;H=5h y,:R'Ɋ:q~x-sI[J꘣OnX0+hB >E䗀-q !X8.!t48Y:zAk>1PKEhTl2G"4 dFjOϽRpgdDn0pQ~[;ᣑx@#MguGX uP[/"B7ev&u jĝf{ZZM צc3ULO?\Br(J>/N#.-DΘA#uշhMhD6Kk"E+AXO@9Hb=$fc7(|CnTu_;&yш.uX!XXaVzխ^Rwn'X/ЬٱFW[rS/~46=l|#kv;pK>H ၼ/h k5>[íF^^ ټ!Y->DJ0$_Kfu]WRLkVؤs7;XL6wJ%D=p`կjZPyΉKO.q*yͳG8O'f˯prw.\zY4y(! ?81ojaﯾ;Îm٧^ K/r | tݝĊ藞҅o~-ry@ &  i 9~+oOuNd ji huP<b>n3D&]Nw%rzi||ivU!UP߉Vn_JV{x>2}"vMWޡR=;L塴|M:5nE|h"A&9-YG[$ajZC"'<,Y+_#{mv;x}/FF #Wiۣ7o{T~s߽{滇׷KwM_׸jՍm<ּ屢k9s`7vy鳓O%{=֭W8|?.PyGc 8g0hhԿCaJ9t_d`4gPD&`/: hBQO+n|@_NFe0v zi7-xJmP@xx:niµF7@+].M0 1F=1?oVx &\;$5__:#"1_HitfךCڃot#uhhoxpW2Wz Oϓ>'|.??c5amn| }[ـ܀ʀo|bV9]=*=#r"a}i>`wX%M]7m$MmK(=5UmanBhlxpM^gQR&!&BmоmOPZS4DVmoԶ9Jͤea%mI ɶ%ZWqe-$Ζ^Qa+l:W 3omVX\Ug^]U]P]Y\svU\%%&C:`يmNWeqr[u魈we5cW\ ?⁗vLOQyfj+ʫ2W [Z[\UrJk].:v+V_m+jոj`B򪥰N NG֗lUXq S2^Q^Ȧ#"0ֳ9K*]U Wm& KW׺"3Lj]5Ά,ʗ4Ի&ʫJ*e Le_p6xJNŨiXRQ^W;`Xf|u倪J-KSl et:ʲʟNb(m]lVWkkXURO{WTTTW9)uc@a.FK >EA)T.5:<ՕYK\**[ J@3jmյnoqBq?,n+TV;K˩WԃA;z}xMq-`PQ\˖rʗV1DV4֔ITKKH֕s*L+=u~h^UE|9.עl,mQVRxLzR_Y]묳EYc]A7BeH'W%.' @IXQ]އzM Y }PoLYq sqʩ1طD(4d+u3QA* ).Y^H{!j Z  (UКe+gN/ȲfʴE}DmvNєE6QW4זmKϛkk˚3 Ж_`˙>#7' r&ɛlˀyyEܜ9E(MUAdR`ӳ &Mܜ윢< 3fL^`1`F~ay9yJ, Mʟ1 gXT̬b)@r ,-k\8%=7זSTXT>ܙ?hf^fzQN~-# HIRpR&LeOOUؿ:arV^VAznpF֤>dM*b#\¬;gB,Ŗ$#?ȥp PSkK/)(dT08IKeD~0V JeuOj䊓dULsgj< WcMi//78CU0u#'\OXG] H5u*+똽C8V_]q,F,iu}h6*O`-)+k؊|kՐu+t[u@*_h41LʫJk+UJz|im)«kơI֠FTR(E C%h8|'AFPGu[\XAU0>Z>6H=؝ ]0g\0plP *k9pdә\@I1D6xS8(#clʴUzΫEˡZLnUPJ['հJó'xZϞzbܨt+`'+6r vn*60mW2yzd]0u0;g3 ]}U/SᜂmMFߢudVXC)P˴_ɮtXM9 F0xʘzLfrQ$Tv2ULYh:V3/~ ?ԧ֫Q7h^6 gctZr_yſ S YLU.cPq?/ͧ|i죡Ya9ig׫OQ|u@S(a(V1ޚZ?OVX:[SP[+]+l0 سAnWOf?ցL&t>`9b>`)0 Jaυ 4,4 {:w:Θ=3ា'#*"fCtEWU[уt+Sԧ;2Nv^*G29 0eww&|πqf0Z'U`*zDNb4R f:x7 B(T(HW_yt':iO2bd]DžlfUg0g߇`z3? ,ڃ%)WŰnʂW>=8$3ԁh;03Pd6qVW 40=%τ=وÕZi`&d9e)JTV]+و_[WAq+,%,f]6HukXbJ֮WJ_:Jw2_]X4S"OʁRk6ݚR,Sx5/zv~A[m~^a,҇`Ԋ\ױO/&6Wo:d!ݎ3 ;\( :OFs%yOXzcLd!nܔɍ$52(\z3v5J6\sW?y|O[&$rŏ|Jztx#dee+$߿./B2|&:L>[ rU&e?J>LU/ş|?c9}O'{:rF&&Ny[&oSMvLޔ5 .drb-29&Wer#G χ.Ÿ?_9l^YH^qeJ:[҅er /2yI8ɟ~3'dG/L̤C&<- w4&N Z!@:'d0/qXSdLb0f-Q#Q2IFF& #dJI LI"$R&>>BLm#56M#VX*P IH%$$H&2K6 !d??/#fo (7e#`4;oC|y{oVx/Aޠ%[yLt@N&1I&"e"đn,ANܸF'/!W endstream endobj 122 0 obj 14372 endobj 123 0 obj <> endobj 124 0 obj <> stream x]͎@~ 7{WBH,,(lX éH9q/6kŷim׌:tiI޴dW__Ck۔/|6i)[|4%9ׇ0JMyVysZoxK%|<^;}nñN㱻lY|ۭ5{,9qNusjQj֢|oЁ:BGR@9/;WU^7ƵφCoSw+#g5N&#kEEeM~}_&>#G_ M~! ={'| <G11?h#fs1/`_ṞZ{ Ό'z?bFO91+ǻ䏚c\`{`5^Ư<#a.:j 5䟭Ϛ8+FlKoT. endstream endobj 125 0 obj <> endobj 126 0 obj <> stream x{{|9sfk MHI-lH$l (M쮻Bj!R mSKREJiR*֪Thk?l|m?֒}Ι}ι<"~d@A.o'{VX`.|_wȯ"fwgwia;FP.w馧> `!G a~ӻzb[VBDWvCo`ϖ06EcCx3~8bp0.g9 *FI&j'gdfeOp΃(z G;mE ?keXy~[yFaPph7F97::iX} aX ;ڍAk jTe ʎH> c[@~78D>ir旓6ׂ܏B2tAEL %a>@!Rl"ځ/HEgUO8(_;Z*Q2j(j8jG\ih3BQ9z.rlhSU`P k03X"F. d 5pm9%em,i ,5FYjZ='N4OvaETILۖLZ*F]v;bOf”EhA522 6mfp G%Rg7S̲$d"b$C oN5QChRSr4 U` KaA . [U>I]޽7o48|Ҫkm}_tc]B ֹ[网'V#:@/C ].,!!==M2 2O&P*2d%!51J, I +S.\Z|\˵]EYġv(Rcͱa#h Rt[JUq*ܗIg𬛅G Vlá%܆a|3D.@e"#ͻ:AoesX-p.3sI)g^9{u\xY$M0 dzs^.~)pr ઼Ρs}ތpyw?2 =VTp߳ շ$$7stڦn!=}oF_2ux֟ƶ<͟K_îyĞ $!pUAOH(p<+P}В=i=d#C'fpbEOV&i Dp+85Ȃq)EFqh֒~ a΢9 ;;w͞0<4J7na;ۚmֶ 젽X/=FE5N'i&Or/ƹƂ Foo^YHs^5ܳg?z6o~AtGl TiUXcDnVUjDƂT]a.jG&M&Vm6'NX~h3 WEkdBUn͜S_Nt2_ͷTv_VC1"|oSf3.vVfF0-MHNINRR-fSQNt:t!?7?O/s%x*M$;禥hUH )&S(Gvbfa1_wҮ6ʞ2G MA+' :ZhY 'EH>rʸT͠ [W&o;j+.]4k6Kɜ[Vm=a3Z\Y7Mc}=zӖ.6K8߰aҕ#LeqH6q @A0TѹV : |#:I Z&yvš׃䮞ff͟S=4EԖG$;'}?^p-G䩏fph^6 >r]S '#OѠ FC NMqδ FLţd'{vq-YlAUip]$!czň JulI]$W\n`gfNIIi’, q 8e~=a>p귧o?sM o% 2v{ bܣ-#B@LГAn5Oar`Y4c0af49,e] ؼv]{Ѭ_}{я^}/x>,u8|? Ji[N3ue܅|i5x_1#SzI%hR)۔=4 `M_wcD6aɆGWti]?y3gb_~fZziw>ZBmptP;{\8#vIq`0=˳,ժEy Nd=ŤSUv>@H%ugڨ*4W]RmpRZwϙg5HtT:=Yo5T|.C\K۷mm}d${zxES-BP"pG*@HU4$@xA1|u>wBLb[ >}-$/<1gZ PȤsZ٬=q+3hQV~REve]%\ uged3GTSKX$ZStrdabn7pa9Pzyg.Wxpdځ2M!|;D e|PrpGTLw.H$ĻX_lV3?/`X}2oyX]P^ڞ?eJḾFf֎s6?}OKvk󭥥 q+<>w̴6âʬoyuD|Pn . sj $KOw ׹+moKEi,mlG5RRSXRSSʴ^jM֧鴈$RUTJ@>7c4Y[PXC 3Q!5}CPfPk0&Wҋ9YvB Ŭ!ҞԞ^T=ڹghA>W¿\pɅƇ~L*\J~[ M/-te@ʘcqb%JOJ54PWwHӎ~GgP]K\\e*2ħ^+hHA#=/@j#ChNӗ4V!R":]8:i&w}`m%\V)xt#\_>Ľg7_JkǖswHK_]3!2:Bz$m&jZ¬ѨiM5!*4,W#c W@rNոʇPס!,ra7m!Y ZaJMzn )C;O['I@ן0~8s*'֌\Е 2_RpR, +<&HDmʘq2@+cFO*c55( MQ2jm8rkaR=9; rQOğ;q#!_ Z7g^V{"Yo'H@g1CK=^@'ER'+}mʙ8؀`w NOk KQJjx2}/*&1Ҏo4xsujiC%v ^8Ay5'-ĸ a-P`F<^Eq1be  eq ZN? ̂=//(+@Nѝ~:`F1y@#iR ] o2>ǹ1 ba7v4OF | 1?^vjM(?27en{C=̞q[J(v^GgBYx㚓 '<:6%cD2DFٙq>Ki8ٓjb#@x>fSyC.qP8-KDhrXC,Gab^՘Ix<0(jW43oFclJ19#¼(hZ3~;X&ǝzOâ0b:(1%+2T }aFEY/`sq|[jtKYLȥEiLQ_41&ys0T}\7bnv.1Y=d ?gJ4ZEJO|^6&z+oOcvJigoq+l5G[JD#W2ˮĉb^dϨ£y}EZy2Zf ڛh,Q(It'zrb20M۩XMrW;Zח]R;&iU1ZfV=̚P tnWk"vnYJXd)g;t?Ef )zp8Kq!ٜV|gP+Q^R Xi9@;&CE ǩN檆Qs fn_dN˙(f8e3 kd,g21þ,K@Q<6X|51.(&$TJ*sVXDZ+_䯅o&fr ê53ʙelj6` /j7y%T4xMI&[ZW4UˠAU_XZV)[d] T*g,!q)d +t6n:ź [3/Z+-,T9ucB YR8oaq;6+Yl~q$C=ق̟jxW8/{r%Iw{Q焜;3 3n|UrM>wnD]ib'c=F]\GBNwv &,Yrb*.΁R^CWìT8t)T^9rW6a+w0/jLQlehj_Ju9sb/ѕ; ]g#D>)ә)|}DzJp#trx|JIɃ“9L!)Yx\"_j _J&ȣ0y|Q"K :O"#k,#Ys)dD"!#OyP"FaX"{$2$$vvKF\@[Aٹ㸰S";>^q|Sɧ\}^3BámwKdD3l%[$'H,$hXap DHtG6m4 F3 HD:#p~_xwړgq# 6'ߚ/ܾoz|N6=i[I %f4Im%ⶐiHD,˄FJ"[[ +%rVRDG TJB"r2|9q--\қ]W"r,b$EBYp,AHdD}DnIHDfb]D U"@kYF#MI"f!)%b`#D'-`JDBԼkzG23F:#xv Y&lwy endstream endobj 127 0 obj 9502 endobj 128 0 obj <> endobj 129 0 obj <> stream x]Mn0Ft@#!ETM_0YDй>O Sj_iw/E DHk*alcneA,swvgc]tA8#ޗȊc9aV)gȏs|$g )\+qΒf%+t쟠dOGGdWT +_Q1KWԇ)G6 ޥ+ ڻnth8 S|oA endstream endobj 130 0 obj <> endobj 131 0 obj <> stream x| |T՝9;g2<$LB$dB `x $@$$1(`&If f&$)PD>R* UU+ƵVk Zu--[[Q[|arϹw}}\g~ƣ]~d@;ANo"^F'nUj7N[{B`޶7x!ǟ~" DR# S:Knxn"W4}D| TPbO5{B9#X?Вz$r?ax U#Rk:h2[Iɶ{jڔ̬ё7ՙ_0E3]%3Kg.3r<  )%\֡MX)h:ހ+"Z)iXXCW[xy}|qp}e]a$:)t;a93aN>uWP#P{ ݇uzH8CЯ%p‡h3,iM '菜#weUw{ofV8q[m |3>Q.t$ עkLIR&!?@J8-D[)@EB a>2azp N*' "rsNoX-lcFѤWѢOS^Hj>CXsJsTU>sw*EST[*`%Addu;ŽHn6lBi64բ6՞cgi֤r/^5zδvfؑdϰ 3-C̠,%nW^My5CX4<Y-hV)81P>c`J׵"+?'k6}"k3pNIoHoμ wo Knׂ2Q;Il>SEdYTٹ,tz"qMM7u>ҝH/K.Jtc9 Qzp%:w`ҘI>F[L6˹C J1//, o'd9 l𸯵νv RV}>NJˬ2{ov֬УoqpX ×W^_G)jvrwAZY˫QVJbd_Ɓ)53/4kY=-ҥ_ ~ɂK@#PIMy(mU[grn]6MhA"}sgԈps֯-@ ^fHp@gڑ>rHJ XeBXXP@4 KvX??-Yz;?ڽ 6L/*.#4<7?]mIBd (‰7E2 7{K(3ڵXeMηN1pDM0fXHTY)9|Mr✇z{C?[758zBzOt vnXߴiؐH|$6FQA&P[GPXT`o qC/`%Wpo!_{ۅIBj Nl5B83-Đ <{$;.-gra".%T.KҺt.}\+ U:/疓rrr]yoT jƣ|PfYլi|Z:pG~{9=;dz{SsUV E%[e˖-+얶 7l~O?>[tEz_t $+]ҷ-ҷ.cv\[.ң̝U[37z-[s-SXr NB/`8X92\A&u@77 ^7b\[w߿kܿHgx#}! >HΦg~ǽVeyBx˹imǁgpPߑކY0>_nT:i D+*z>YB KKw.*7 -p;xuVzzczUgS:#(6 xjdЄB,><0?M:/ImMؽA8/ I2?OuwQ@cuET֗,:$89Vdfhfʅ29/fC\~^ IooK׷|֧5Ϋ" G85!sXqةSsikAr:'4!Cr,v-X:0ZI /->-ݍfYpBnΔE\H\d..ג %}dv>B~}ArLImz>ّσPKyp[:* 7s}W Ǹ;=+q[P ZiNuª^hg΀ :#5N ^9)9e\|_yr#P9!9|Tn֮/v.٨mӷ\LQ{ۄ}= vC۴~?yD@8i+ӉeC.Hɨ؝fB>*]; ]_0*+0oIXwIoW5y9^.N6]eo}ۯ7y!'viUhJNɠңTU5' ~7=o5ۀ2S It0y> Q*܋JX/g9VϘ]S"vtqє}/ڋvEGQQq)GGS׏Ƴ2hx=1Kq2(D;}Y"Io9/aK?`ۜV{lwn<5siMټ+>Hh;Š*OShMnK_:VJX:́ztPhj B}Xh*Jv\)E¬œBqcjAzle X-r^, R2&yGVM9]};n8qs7p.+8wy ʯ]lr@3BB #nv_]vYΩZJQrH !.+ݨ(;aad' r̹s*O#he-σ^M:,9J1Zi ?4Ku k+f[?}ՆU ?]Zo[a⪥Kݵn"Bm l؀߆Oĸ>kӓ4WUN}pbK*!;ƷMןldO?n;toO6˯).ϝĉCJ`A ;v.ҰIq'e\ܩ֤ssooOа`uBlף<iZf )`%])vI<^u\8w4y V@zTjҧDJޟ6@޾滲 {3&b!4*4!Q9(-\&΢W@*rJTEׯۼy(vE`c-[v}:vrG{\z@X\uKoC.k6c>{8a7VFY,~O!U=$OrP'ZQ,qo p&.Y/Go+@@WudU RiZ{R1 5bbu*_XP^.ƞxKK;~z?6sP}$=efx; E ȗ2ƣB/cf@:W4Wgn ]w,v#EMd7?jEA˥$(PfhtTwxtBzS[N\?n0lP/֍ 6;+ ;` %iΌFX*g"D(.tY/\yxHپ=B>jx qP_<y6XGtF4DD& ]k ]ryP\ǺgMyMTo,S['zgZrTd*Jp W.Ѩ_qeہS J9L +J8~*A}uRZ_l2oAq9d09Da{w–6D#ÀrT(s)$˗Z)Rg|.^ywUm_??ӧNw܋:U:RF*Pя܋(I ~AtZoi-Cנ4UԨ\` vD={C$-8-V/s*űCJ{![oJ'ai^S|OcXqiң3 wYbI*Mn&e-^|LblN(e24>KiབFBυ*f[٠nsρNIU' ]J3H $)+Uz>ݑ7s`HK-ŴVWkʷ\v{*P~A~^ \"-F[aRPT&4M7I,~?`fYmwq%G/N ٹfKi?eLTJ5ьbȞ4.[&d9.7Y &.j 6V8r?d`5IbB# A [N@+jRpjժ|QPO3X._f fMN$34 45%:d!COHJ.J'Ux&`+)Ɨ$ɉv]&ĊS5X!Ȭ͕J"| rS K‹gIKCob5~uJq)&݋ v@E5MsPrH6C1MbjUߍz]^W}fGG꠺_cC6' f{˻{s%齽{q<Ε D~q[2$l#nr;248B2"0kHg9YEiM39VB[DIog&fW<q菋 g}6Yc4*~pð44d@D@inqf>e&nKf.຅_!z 8iE4>nՙ:.Ah-~Fz_{f 0N *S,v9`uZ$.5 Ik:Xj;V LޠZV:ZB@)-H~!Qq?$P/ZEۦp;x3&f!G( RǺ">\7 OzC8?W~e:wp8@=gqj"*OQ@t̨] !AdFf#KN^kAz(}2XLFN"Hc3. m1L4pI_ dt䣇?><-q0A7>BE1uA$}">84xʻ`eh>Ý A!Xm[q#_!8\Hu3\r6i˴?RgЕM`i1cxiQ/L{?,EzaBf<Ȃ`P<stՎMr7*3 +x<&V)c1e,@?U(ݭը$"5Ȅ~26jRÃa.GcT2H2&h%ẹyzACW(cQQ~2֠L!Z Ho4Z ҒYbK(ţ~ogXj-+;:F+&6cxtMPL\;|_&F3gn6kU@cWG>7I Mf`l?} y.WwwwqKb5da]MF$ΨkG;X0GW$E~z5xXzň?ᖸ7 O+Nw~-¼O @C iKBX5|/ŽqJO[Qnw{BFI}]~ւ-]q?a"1jQJ@+tDtT&~Ng\GZ:@8E+c~P PSl :!;|+~vc"1ղ3;:ݔp|Qz`g<ȶH5P8ɳT/1X l!i8;QU'Ț:þ`[#yʺ:QCH FQ+=(6lu>YhގP$:zSv~/^:QQR$\v痉G}11ws)ĂK7WhV?z,l GIoDo$Nm{=I1o\ xc(@7f>+SHΝ[re^1LuTQ^FƈuX!ܴ&D;dUKA;c3俘]3p,6JDJ$H4G8JJ*)k2dX2VpFi^cCmh:_k|^"vh1 ԋ(ڡ#MCP^ 4 F-CD`O8#/DE0[BF {ïlo2 p &+|NWω ') AD3 /CLK@K 2 (f| Q ¨jjk}z6"74?5A? f'o*[+3L!Gتc+4\-̨ -`8~~Z'`Wne:a =iV8R,p4KFa2 ҳ>ctŸ:X]Iȧ FLor1 ;(qhLJLLÀ9Fqg6qިv;oT&ds9JNM.&âqڡNτ#颯hO@[EQAE\'$'S$㨛ɣaHxCecg07Q~$6ŽVO3ގ)aYC QT(7Y4&/GQG&M˘ǁDƷWV"1{%d_S YQfEaETtO/E KT.EaV_#H'Ns;g$SɧOBx|SRR>rE:wAбtMFx G$g>?>ڲB|iHDHD6Ⱥoلu-%kf$ kd܈%I/x,IOo p4XHDV~Gj%\"$ d Yz,Fm*,N#&RQJ LB,|=W*'TRVOus s9@ܜ4R6;M([Nfϲ ,)&LXFJ\inPRE\iɌ"0#8͹ZhY(טf24> oרt5&ri}#_L3$j?8ǹLLM#y6irKC-8#"̋INMI'-,ؒUJ2m$#=YȀZ>}UHO&2MS4faL4ȧHrbp~2d-$_!Ij)WV-lZ-V#1f,Lt0Qhd@ԔC-JGT1^4F)> endobj 134 0 obj <> stream x]ˮ0y /OG/BpX>@H TȄo_=3<ʔ8s5)]ڨ6yY{k̹}pszm7㇢^~Ny}zLӯxìbQ]:ssd|ל/sZSx6fb]U>6E٤shUjl} vAbj{lo^ [];GK̎,d~g?pYWdiw L LOM{>;Nr.{GK8{f,[ ;a{пF Ԥ-ulC'LgKgK 5⏷X[kC>kIyjYcj)QtC;8!K~ endstream endobj 135 0 obj <> endobj 136 0 obj <> stream xst$5۞c۶&m6&g&mgb۶;W}kuU}ήsv굚XQ^(aoB5vuV1W , Lp䪖.6UGtr:\rbF.;U\rFNf373; 3 x@ @\…?*5e jZZ!cfb@gKs;ſ E݀6@;^_`n` 025ndHXX:8ػD]@;3%́3 ;Ł/el`taI.ng*jo4pp4Y:M> k;{w;Yڙ# 0uu`TttJW__9:&RFv3#g pqrzcfZvpR ZC?9S{;kED=̜lz.v3 Rc /Ii;3:_k@o; oj'LLcaGKM(jcR:dkcl-mQr c~!ՇP II#vI}h3z_`}er[AV &z\}`M/8!F˺7& r|yK\&srM(L&)iPfC:r2d;Jn>\bhoANO,z81팻Cqxr\ǼZ";LO$ٟe-jl)?ƕf"(юq'L|$W3l8&+˟<\J;g(-vQ.͌pocR e.˨~\rm9lB),zNd6Z[1OWJcyUwl2S Nެn9FY*TfuaX*- \sLY8mI3;:8 C=$YF\K3Q]g3 Ec4~/ծ8YbgѮ~3ջb= -]60ޫNO4>UZ-Z9_OoFїZq.i𵑻l&[ԈQvߍgviJNQUtꤟn 9HKf3[WZDi K&?Z}Ӗ<28"]J8. flw+'QPwGhm)es о LMέDx23F8 Fk~{ gvg䗒>UğLXz 'm4e<F GBx^G|Br%|@Eq kEi,ݸ)eQYgJܹ}zW#Q6r~@A鍝 1u4>T*: kcVr,L}Ͷ |]umx qO^/ =ZvTE zG/nƈ-+|M?&i +I(~;G8V7Kmj tUdyѧDz:z-^% *O TC(SKl3[RqѕMXy=W)S4κ߀TpZE=:BP]}gtb17ЄWtjRu_qfnC.xi1ZO.Nfׂ 6 g?eT;+`Ͼbc`#z 0:pLM4:u s,"%S~GԚC/L&j{=@XhŞ=+Ep!6kQz4hKlXY_+yr#v{ě/:<6=,#Mse<'2n{@A[XiSJDӍ*ѲOC灄$}FH%G.lɆɠ}LOwqaQ46Ũċ(Apog̒Cl/fe3?[ihwmƲz.Y—8+}^hme:jLjN:H x| ŬS)n 0%%Nم,o3F:m{]QV=1O%agD\G T4Zx)E8s RڽQڿz-"z1uIi u ZLEC G:׈|C|[O 6. =*wSF>I@8Ǩ@?~0lӻy9ڠ۪G`q*;asQ%^~~Nk](pxBy hhltYGz F°gCiGs v<)'|8ŎR[仠OM" CUis&bO]D9j?\#޸Lx)CKHs<r @z625բ7Azq{..±@DX $igDDnvȈP ohd=趁V\5~^\ṅktb%7Xv'o?-^Iak=+d/1[C c)r<+M='.RP.**%e;Oez@sCR>q%''`m`@Mw> n 0||#E O΃c ˄kĖ9ХPU[PzGy[YkGE,XZIn& %-s!'+QM)ڣ ywޮA F6nl%(]ݪ}s.:ˏ.z([d;c*_c]j?d q'I֛_?9ptsゎ$~͆(Dvʕ>TDd1GBmyj\A}64UKnAn%yxYi{ YQ>DB zKQK\H9`qt,]@EM&.~x19lZZ6iCC)\D'5[Vg,gwk D`0[v[p.+NLsYꓟC,='=*|p6 a5s49rtgE3i}('(l!]~ev ^eğ9$U۹[t&ӅJ]!`7;KG=|MTUaظq:XR <1/MfOhB?_8l,`'7WeBZdwa 24# KܪJ/01EqR xY}ni-mb.Y.ѫXn+C8-YG<^GUXlԻAw8cMdVc2)~kGaO-/w=ImCYkm8u=eAq z +DvĮ}D.2j]է&b%aџlW4-T x-וpd# ٕeQ&ۜcnDsa;c;7 gk?2 \0J'>/&?}8 7S?C g% (tk2?#e2hT+[tX4^^%f9ffEi <Ɛ^&kڛzuZ /EQfn Ghc/̙;𻌀{. z?g`ehOA$@a͎LA)+sl\#ScnVBנyrѤ8<7ܼ赐 G>;xO sY^nP;18 j5oMy(scք֛F^&-ȑP*b'n>cfA:4+YW|}*Dw]&͗cq`?DKCgI9 94kRL Yk483m꿋=o\`i[|DB\'S‚Q :7B$1Wn"ͪQ=٧i;ݝ/"SD^X@G91UzXA*t%y4R,a識gB[_,iXr!n뒅8&z]~}Q~%oM&x6j;"ptV0 CA3.d%zf}VAIt| $. 帡eT^Lݸ[LMxe^+v9!᳌(=Y\!`//e)@݇u<۾ XE *j˚3`=4/c!fYTrT[`\˭T)훈@-]yA6+ $_GmHM^Sz硦Nߧy:ް~bWߠVIU"ՄN rb巗/r>T\x$ݭFc c ; \;Ed>F1pe:j# ߜ)KqO.CLAd5buEaUH_)]g[7$Tf VAPG %M;]1/oONjLkZ"yTG+BI>n<H2 c]-O! ^bGo̤B&cCQ^# GR. /Yݞuwu`+0( Cɬ?xK^֍ eDyk-=; S5Z.!N5-(CC|y 2VÖBNUը]z,Ph&^@wezGeOԴ#ϡiabKh,c9(ƨjjOݓ(ؒHڣz@v[.uR土çIqn=(ؖ;I*Ur3El]9TܸwPJn*-OcR+_ ;5"0Iݣwt Ǧ 1YVRbS_>fBwha~o.|Ob1IʖoQRD(-(okڦuj͍Ba/j텯wNG bs}Ak*Asbwa>y(E=7Sԅ} 4'yڈa0 iۢ_u=kHmGl(D}@Ǭ-n s3k2Kn HW,ʸTӈFR(3z gsߡAL x$fts\6">ls_|ti,571\pWQ ͬo.w[hEؠ>&0Y:|{[' ^=]uio"k)>ě/TB`4Ev4+\`E'ĮHG}u: (MD'S%[V\k7gTDdPԔAihZm9]iJNGM鏁Y!G<c{ש@(y%l>p3O ҉?IgF6Jq"#.J̈FRg2pElIVbJ5nPOIt@Ӽ(dS"+`,:&9E%驪)ha4I\}o7v:,yL#pM h\'lyZ+0քT%U,!")"ꡫe pt?ᒙ #"-Aln.ê1jXh' 0QcOϼ~[4F-~M=.&8[ ǽȅ,]oE@[3cB-& |{;꒝NX8A/bcck*珙( e :S89ޤ҆Pg}A 꺀b@$ S\@4( K Ty\NfȲ82!9FĈ s {tqQ I"aKGaW< ar("% ~RJjug*69Yx ,AmT^*[!SNAlYC,+KzZ9d]oz5Dq+1A7Frxq6L]oAV3D\nt;ΏӰaJ:=]VB=+b/|(''u`)P\\4EObM^ U -u)q ݌NP*joTL;%=A 3T0fzCuW6W]h AH8.ilb~V^ZQ^>RE`d>աe;,2-ղ3!"#nzi>p_OD1A.CQ+N5 3v;3Z[_$0 o6ؕbGN %2+/N~K$ 'riYFQ';%' Nҵu;y@ 'F]L@Q] NCj8l% U=/c63WDП-MN W L5<0Q߼aԯy<ϣx !4c"p@? `U &\ jugE)Ml.fZ|Ma q)0:ɦΔ936BH&-]'ؤb[ҧ8:NhD[ =Vjџ $s{T8 ЎTDFDܾiO:@RK.wπOYΖǘH`RU}pP;zKh3sHM1&tW./JLLR鍣Cd hsb-=wzB,sadLEHx}13ȉ'Q2iCf}rۆ0 ½ J/l `kݭ qp ym^CU؞DaȚO>2J\_ΞH>6l*C= 1䎴˸}d|~h?kU,^|-lXE`cN0aġp;v ve#~0,gE!V(ս#xi6rU,f &61|.Cdt{xםA I 7M+̙d'ȭiQ'd\{?]h]*;OOj?mp Ln cMً$ɯϹeg"n0ܕ밢 u,HWΉh꼒As?W`y.Kr/]6m:7s3!?*t)T`w'=F4 ,ɒ.r[B/C3(?)9P\ 3sYЃ@?aPÿ cٺ/#M*M>/k3x~cS/'MFE\9%fBƿzA<:YF j`dH=;PKӱ$%~]'R+#R\-*J!;<ĈH$wH2+;ñ=|es޵Q'ڦCJUJ) , }J36y&BÞkSinzʩ#=|!.lB$dRk,RB2RuZ@YаIk_a2lv~ r _G>_TؑQ.MZYnoH;3MU]ApŎ,.d ZF1 Ygג(e7t2p0yzIIYN%4},&>*Gzԋg>OI9M1gA6>":|7LXu.3um/U^$hBy ŷ9JNtc4UBXl )$nvQ"jZHt˛箳Z6 /:aK#5+NQA[k}\y2} ݢēEmB޺49.KBtx)%F\odG2WT[ G--!u֩Hi =^ ?'4p2ePjEl Pf._g*^2S/AjnLj:&Dzl>'4S^!FfIN}Bq|^Mrwv̅2lH3q農Hh +=DpO`h c U"}$+Q(ŏs"2#oጝ̡8 xdb“1#-!!_=DeKĤ9u3iOZX@|Nܽ$nAjS$-j"DÆEyT?#!qw2wF[/$$5RtxOv|l69]D[؃z@aO<5QVzLjK~?#F[fVC-cfq6ۣr8a mى(h9+Jd1۞Q[֗H$k) ~FgQh?7j8<zP!%}bzFDr͋ !HBsѧMJ:w\IZIq#3(WȎ*uߓ & 52@EDx&j.s4$+[Z M(PP-hs?>l̀ \->I1Piصy蛤LcSi~3mIܝ>_$5Y1}!{/< eOGcR6L8Я N(Ξ10}T).$r+88Lx)k3:0 Y^nx /@^Fd|Yp0AgO(p=ِKn|a{9#wͽn,lix^X0@l/̎)ꏠW3 7}L3BmP]d4="l_d66~ jE}t:v :V P9}hB㆐j'/115c|?Wh0$>n"y=Ǣ+a0|&-3"l3k>̕ `-)~ب46jT,].ζt&)Jn6O\O 4 (p_v0Voyr¬q5,֮]9IJi<=vN$I/ n/M)Lp7Gݱ(*UѨq hI8zC8mhX1`I@'P,Sgj>N봠 C{+-%:1 JJ,;=IC/vf3|8174NQT ` ~!Ƹ:dЁy *6R,c+iJ2N׋zχա: p< z]MILcEt=jtu?Cg&9S:ibtc =ᜐ} wڮQfHfREZxtp,Vdb_+-_.w QdEp6EcgPea˵G,K F{3yx}ȬAY~Ao<~fk?V^]*QpZ obAAvh!gL 5*JlE ۓ>moMh[;83j[)W~-G3XAV.iXJuPf|D95$5s+MkPELj.W%-  ԓeL "Zڐ=hˡRR[II~pAb %E(2ޥ,XO?hl7K^X9"~kJa) uhn3`?r^=yL 4 }+"3O妦{ `J?]4~H*-;_Usڵ]X 3*VWvQa⫙FT8KĞzN(F8jFq7$/m 1XG.wPO3{P*96S0fh{dIFl ][PM4@Н'c;*vɘսjL$Զl Ng3㦿p`Hz_̲L3[/͉bkTz' tRT0p48f/Q]^lA_+ ^P1;S6z,&5'Frկ&}wVB<<]Dh\Qi &ނ0-21nYL2~hW|Q;`to,I$ EKQww5l 9 (6kLDŽo-s[**cB[v@n7 hdܵ7Y6Hȷkƒxtê Ō7?K :/d 8%[Er?Ѵ@H#Rk )Uk}Th.` Vv].]D촪g\g6v5rb$++iB.mqVρ)f'a6&p$̓a X4j"Η@Bc@>^J|A 4SyE,Qp>̱!L îɐ_sBdd! U|#-\Ω !G#!Bn[TΞ]%_R,KN'Aخr'!h@. _@0&C 16+1{SFֱ)c&/[ӊg<p5߫1@=:IL?v7'?=5iܒO?< }8o6yca,?{RSJg2vl)\N&zyߌg@}VAޔ`fW"U̕sW5e F+] QcIpX?wỲv5OM}N0ȘpcQgE`вm8?q!]_L/tQqH9 !rÏ8jVxOC-g+i{`zF)l:bjZ0;Np" d|4 i|&d0* w_ $:tQY] +Aq\ xcUCS]B "[L?-dqܞ& iKQs0y'S)3R"1S dzi`ie#JXhC|1ۻ#  ުuB-0{=i&_aR- #z@uݠb]r9e]tۖZtd N[JofF=Q?GßtW}@QUv[Qt -=4Ur/m T0먣=W՘Ky )ȟɴ eu/i'wӅ\mlr~Os~E^;pCʵPuyu[U$5nLQH7FGQ`nFA3݊V| iC;пp1VΝwA /D.j ve.x,]ٟBN΃ԩoD긐W6g6Ift @)k&(^Qc(24zd @ ūV5_,Y k@+ f| ]N䖛/haNF/i2aeɚ((i4rK])zVW@#^f}p8UD.dW>%%=RO`09aᢧUx.50GϵZU'24aaj '|RNF>+#"qDr.%b#_/yfC'Sp>yPch}JS>5[epTZMſ xoTGF>tjkɉj k;3XoMb`=YCTjٺ}|A5L(ec>z)_얔_4E%ꂩp>UԮXh|z9d.}+~M0C}DM.=y#E3ۚO4gSdtT1,PH]n#4vK#D#,KZMj,uB]vqdek&)qdtpa{cu_TC"+ZGQ5_TID {PU\%;.xn.#7vt+R3Av$_ń$P4-ɏnQBґkȲ Mr2PQ.+x a]{ `gMfx:eAѴ󔌦>R@t_ֹlkZ`Un_<^ۨ1B"M=째Ŗ`fHui͍*+,vUmZl m=YAP3s!+I2jP|K$(qYXѫ &Wppo+d˫#fs%Y#mgRC+=hzJX;3ڝf?wS8e؈ͳ>ǮHY1+, nA,f+To̫%}T[f)yz2+ˮN:Scӻ uo^@5h1+>Pz =|Q$UR ^9KdSk[D_9!3Yf|'E@Gn;ݦz/QC6ggVCi= 1K[m|6,Gnwf7$b+dzrBnڵbd}[cK=l/f}SCw[c ɒ)/v&=~]|\ܫ| 8]PP/t{-U`{3ʍzZ3AW|^pi26[s?'b>9C\L]p rmlh`k]>˵ٮT̍0B]f۬ച{fDoT|W6$,O f+~Vb"{Q赼Ԃfq|}8,k4ЊnOhYo0g}p'%pӚ&'B:-gTJtlO;m9^孔uA4Co]5xL0犐zW21(b0/ئq۫t N2T.$jXs=};Hb/@"'XP"r@VުďX+Rfmy\^~;;}_Mz+BK]f(s.oCߚDuۦgMpȳ@' cZ _o2t~eS7٢+HO-Ag%ֲ4̿b=h]p>FmC6> !S W)U8 8B8.t"U (2i̪VbXش @zpu`-~3eedGCkW;m hBJ>u: t5{ݷ5s=A>~q%}nTd֑vóm-Yhn2 >)K3Y_o0=͒{\iR 6MYK X90c)#^&U B:V7.)!1Sc'͎Ai BBo#mN%6&46~|D !eiyNC)|hGuHWƇPߩ"n&;Pqs?X{#VYy9Ւ9.!A//giKB0nN0AtUSX%5\] yx*X)Gi&;!o/gtq@cntWd=ښai~99I Fp˖";p^\,N%kKC +2YZ(Zzm贠-d4֊|c>gb}QߵSaI~"G-D_|ui+{)yc ]zW{Hד$oer $aQ96< #SRX9<:S濑e1 wnP`X명EP#5߀ Y ,\c{ Z@CYm(X} w.Y$:/\YeVny.GRR{r k35I;\9㔄n]"uRbHO u>^0MßM87-˖\"+]xe&#GoW  ;R FLB*£ .؜+Z!5"Db{D+Rk{~i6򍓎q_m]0|P^}e+PN nW]o {~]iD0Ōa21ݳ'-Vm*ʹzǼ9kEJB7:J g=I\ROǎ}`1a:%#R*X$l6jpJ{W79 MX#_h6kiGu48>C ~'L,s+' 8•$xbERjH#zd۝>؆%g82lBb%yFQ W[hɆh`gTVޖ2.=x49๗lFf-ntJj0hd+ U+ctxItԧ[ >RNSVot6JB{ejS74C4vSKKsy Pss]o1~#m(:Ds~Dt!@\ɂ7Q` mN/T]^%wJc ?& z7M j_ P^ Wڋ2~1DɲY5mwEjz26~½>h#'>92RÅ>U9{YONӕO1:y&uqjE>jy]dLtz5H-jN 1  IƆ4%*[Ȉ 8WYgf+R}2 lj~N{+1 Ēe6% Mg29\<ΛM3x3L~=AZ@VI`Bj/❓027CQUQzn0iU ^Sf.H`;RI,|bdPLeHk#^" r(,gJZ_f^7Y-a< T¸# m7ߛU8t%艂Spdt;[#`rҗDiιlkd6,4`oǶO%.٭1S>-#b8 5; [rz*'+pDU}b~]N;aI?ڠӿ29M;5Y pq:{b@}0RFUp meXuxJ>qJeH*P)  FmQ"GɃbNrc^Zek_(!!Ձ!syEϧj@*u-P=r FN6&. ?1b`~KIjfTu|MYVkJXݒU$-ˋC!v}fͪy@9˼kغ|<F b_"I,rT|Jw3Ӛ39Kݘ3HuPRϠ4͹)r#]ᰙS}'܀Y j۞FI[bAj;h41gD/.a{N-Qd;df;|nO Hf4}*rfP2ִ`].Swox0 >hM|{~W=0$a i6ލ:R cT\/(ƸsiHC %'%Yn@y1,vjFI~Po}"ρ N{=r_OГʵIm調$L"i$>rNfi g7qZ{HIOrGC'@$BOI-j!a'E&>d8"`jW%9#շ5В~ABluL\0RՙQtV/r-iqsMY-t&ZG1{@I^#Ȥ0Qp/kRcfph9\y7dKN2KÍ7nKY';ۖ?dAl hEUiד0 HHY : !&ҳg&@N0SYO>>%S}QelaWz8oؖ]kp?Nulg҂Uw$7jj>ۮL5Izw*jpd|ΚjNÎ4mڇ}#[@Dv5oRuMM0Ua^˫]eSsSLbjZ/=%xJM2(M'j06֜;!X NNVGCC !Mބoڇ2\lqbiI9L 0;AZ7쏴Q8xw[;v#=(㌓xCI:yp$eʝ6©ƮbaBk,UZ2+Q1iq@oL"H*_G껹"Ugâ 3s:nAUw^g$mX;5`7/i5uD |ǷpSS+BK"YS =763)..%2yC/X = a=R|$a}e?AݙzcdSyK|!ts8撝]@MT'Rqz597̅Vvυ9^@ABnQ)Mk1mU=5o ͺEG GP,n| kwG\N|`"bgj^R_Խh]3wXFwXIжH-&vu^o_ (8 O8B% 1B'&)!T\J=r@2L>oUFa=nf,xҔ*.RC8|o/^|3]_DBD Fwơ!ʿ ehIZ.~D5h bJnf\@#| fIvD~ް_N{5ꚴ/Ebe?Vl-="Bשކ7BmK0*8͘ªk7T9Ic AKbv?^符+?PyFgkS wOfj6|_@ [*o0 EU/Nѕ'(q@WMK\-?֕Wuh`D^ Li ~fp HL#ۻ㽜9#t &dxZg`I!T&=^U>'I\]B0',v,^ HyRwNQCAEEx7{`6nIJO2~o)޳Q5L1xūhܝR|Ц'.>3VoCNc!%qqɀDOAc??-:M@ȵ!K2C0#qV"/zab \&Xs=[Ν]tSV1õ U Vz8i˜Scnr" tpBCݝŗ' 6J;1:2ƭxeDF=|>5_~ yMq?[d噜GUcl^dM$>Jb%=3gz'HVxJP>,yDâ || ^\ KMߨܤ 9 R,£pO N a.̯7FƖmX ?kn&r{4cU1]_0 0p[@EEI!M6   Si󏉬O`J@ 93#<M;껣怭A݊,P=?0_Ue Rk᪾ȘQ :JGqu{%6p9=ДPJ]V.AMʕ*w-l372 ?Vj RwYg`Ze먥B;U;}'g8 CNfX.cE+qw'7gRں5ħTB y+vOgqep'?OD_ݺ\2"̝r(lp2}v S{kzUPmt:'y*ܷ1%[!^ /|M oЫqx+$RA@§DJc{- ~?R X\GN/vSύڛ S'\5FkF٥&nyTP^>Z"F9񣓗 +gل-fHg#jm6]o!@/I GEUBV=]P|4SA~?׼`[QꡤZ'Z,3HḀk@3o =V7P&ܫr&1WD3鑧{B;X=0kOe+Ì@&ϪVL뎯/<7O 6+вdeF1oomzU;]F!s8eyLs1f.k!}Kqaۓ5ȱ!;&ys)IuiV:?2:奘Y hD <}}?{3sS-tKIɓf5O1(SC "o޹od׎h`7ۆp.r =QU8|6UEKxM1Mܔɨ脆BXPϸАG\;ڄ7 Z,Ha*WY=Fdb5YJ@HJ:->@~)ӹkx# k1 K-4\\wq@nU%}ÍE^ ~ PDE_kqB蒨Gi4$bU6Lqk m SdO60V1j 4>XLԹ]0lP6f_%]&P߽vn&jPbR?!e@b4zRWg3ɚyg$U2}jǀ3 HL5<{x9L|ڒZK<)Y)4`L9†oyok%d@1~gD ͝=7KO<&Me/ J :I[й}P2Pɮ )ΖR_qLqj})8#' 8PyrUXȷPT/o3/5w rڴ[EM|) &R%7',hW=SB~E;NBjk}wJ+CmA (fkѠ?·2Tx$ mY&v 0ڐq!Ae ZW[ՉIq(p?nSps9^[#vGއ=ȋk+Bن3Fܒ6 /\j)О0!">ipy "RF_7ߠMdю[3*hJW $C?c`A:g hnz*.5C  6ȓE*ȺklI}7mR B\wYTIoI>ܭߏnTv0ʱE̒+A94:]8dhn/9҇BQmL#Çe-Z&+0B%6do m\7]IMGxɎߕ(i2&K$lK> B=XeSw47L85jphM5:[edB3W7K?7ʐr/{<^&@E$?ɻ1b>/ MbLm \QykF+ɱW7yԌaZoawU5SA ʼn1\ z[4j?-7 -bCT߿Aj$,Q ؊e;XQ,C#.0/{>Ƌ,팗 Ra^6naؑA>A(#xyA꯶>ығ_6sS >M FoeRnvzi"srn~'H#K]vQ+>KoY-LEb~vt(6P%n,N?[ngZ%vVDoHnf;G<:qòUNmF%%CIi(0#YqpwIaNu)v< R-J[Z߯-ӳd"`ߓSP,8Rjz +m&Eǝvy`_@pJT n]nnFE]!*W G֓w ޾T?ij$vJ[jao/nR?2w(RAz0tlA:OJ sZa0;ڊ*iuOfs:(` 5Z'e$;bf$/m=yH`^NDvpDN7;v(W֬*飱RY]Q[%k:8)% RA&(EqYLp) qtLMWH<ٔ"u>^2Ⱦ)]l*׳ǘt%ʡԔ@A7Xc8¡lAPRLC1V?p_OI*/C#1ؓRb'\BA^B7Y`8l_TjTA[N?m΃!YqTI4Fm>v 'c1@j!4Gx6_9%Cw$e:nO=(o$ߩși08f+۠C Z4OH "-z ]lدbVg'}.AXC A>gs1YLGb|f4 7Ҝ P^:S(0~տr8BڄyvM|YGS|[vm$=(:ul8`FHAcRQ` F (oqnՓ'f28 /s1X sPȞmK&cW5äq@Yp).PXp yׄEX1Qҙ؛hu"]-߃6S mSFBv]fHjvI3@ %D/p9 ,NKG`x`_ fBj!`wl.5@pD ~n S?my4s !kG[Eҧ1* ԉ7R&HԗK?${7HG@Mx%L:Cw7"dP qjbH:6Њ!kf4!&%[ 5H #6GNvut~5x+,"83x-5Ћ<Г]`Z,Æv1Ai;Žד?eXz_xzGo]΂Ú*E[ːuz}cزl;@l)Ng*KfML~}cknrvoģ9Xy#[1h9 ۱q*)B|Ǭ$0&lawy rVt_E^j (P22Yp;CE`*ȓ9 a]ŒSp4K0Fİ%WcQpvE{%q@' fؗS,X6-QW.^ E)\0zK~| ?wI| 2!>!WOadu5;^ΰO~PFj x3"Mz7n bpӅOTGɝsג~AFMMz1 {y!d H;1s"_PXK{o09j6[7y7at8,aσĂ'Nws ,aJ|gBA@* P&l 1 x~E҈AWE뛎e?.;j=;Rw`Dٚh>Yt^)Zu'->F,ZiJ6Ƃ~u?jmAiqiDq D?Hf;ɋ 9T1m-@%.SE Bbg @*WEI Cwgr*Ap4$8 烱˥v:>\Ija]B:q^6cb`E-+!b<%5 ' ycdED8~4̙.uqs)5ry"q`X*06 l!JXt + _s>KSO]ٙHXyĽS@ǃ6]|qSB< BoӐ`h<mg.~?&=C3fd^dmJ=?W/-{vn2H$e-3]+{ֶv:lfٞ1:7ڂ[ -ɷ;!­xJ|#%eW>|H`𪗡TT>|4c>wҳL XF޼:>$',g09#iSwy7z};ir%Z+*em87OVh/ cwr\T|:ѶQ2O],.0$Y5}-z.;oY5PVP?9C* ee:&7F8ctW( a'+|T3* \\i(w$9nb]bb*a_kؚ23+egĈW elA,fzπQ%t9sDTe4\gy\{%?Ŷt@ e*JrM7iN[N {wƼG!?JL" M#H"k_qo>Ϋ@)ưf (ۙ0d.)pq|%{>SbF׀aPlwvNH5uDuLͯ@x[J]B8}DDbưakR=)F 3Ƒ&OC; j? OBY`M|t;Cp[W9.QAR߁uscβI*{Ho-t>"nItNt=0o}ʂCZC'l̟m ~k|meQ^2" yiN$\ X DP;D wZ,nkom73/OM=f{)дAs=<-)%xhtj7$#C ?^R3 [qhEky+OøT??pdI@Tr[Ѱ_qiO[;yUQ@شz0ȩsy9QSy{ٗMzU+SchYǻMwWH)ۨ;UVwO><" ;.5b::%|]?/)!mw*Od=W%,ڻ>S~ "?¹?hjD6ZjڞЋ֓ϡa+yipU~m +1g6=25"ir6R=rpYX인&ʍ-3k4]JLe)8ݭƘ@)."o-挴6ؤ~;<'5v) | .04nU;皖A¿˹yPׁsaW/x#xOi-#_sE+n+eRs L?S`ȀX @0" d֥R)NRVc&outb (놝ƔRZy|L`93vo^ @U)2@>kQ - eQWPeWxGjGE[$9B9mN(\Wm6ɈfwCªা B.''7NagG?z?rEJ薔-/0 lg5YI0VU{886ME72IL-tSx'r܀ѫP9#L4٩"Mt~)]1+`EKyÚ?WԳNSߖ ~^kݏM!Y3'B2yn}%FrȮxW{=~ڿ<')hqPpHu|!{Ui㇠Js綕 B:.NcJQYUe1N}v=ZwT~bwjTJA3T224':<ܼOdPͮyUy:1ҝ.)ljmKh.i~q4[|ӿ:' b- +RF)+fIfo2~h8ݡ`F}&p9s#'͘"$i cPbK+̅ R>{j:9T*Ֆz\A?!y]CT"Sc@OdT\)5+ڦ`H[hVuV#ill?ϊ Ϟg:}=ԩ:[07`Ywuo)ɸ鬧z9쑒øqKu6H22|ywu6RE>g12J=`IW=43^wRˌpEa\ƆJ82! ?cpxǹ7CN0B:Ib{4(%ʼnи jw/mzY-e8^m )z3 %MA+G+m*kF/ue_"L{EM EHOa}OxXl<( 'Oy%} mƁqɃ_Nޫ?\vPR't@"VRVD,"4)`o$vW"gQu9:dsqĹ|2Ҁ\rL5D ubr,*kuY ]qEşewKVmߴt}\o?]Ӡ68tDL,YOnB<^7 "lɢy(W;|!TH`FђZ,ak'^m;;9 SmrG&Xc8l⻌$2dׁq~ -94‚)OW duv`jol*%.~%PVNI4{ԭK:Xk& ^P3icUUtp L-<Pr@BmDќZ0:A.gy2QJ`G$:CU*>)@V2t7Q6r9Q;~g&#ݣ/g4B~Z>vXdz@ޅ;;1sP,{ }Dv=Φ_쐛Y=s@y`@UCoU?m ̬sP~k+g J'~dC;籑@.E׆ ~qk.-nkhC.ŐٴxԱ@~l~Hwu?yxwߵS51ȭFf@ctQ;Pq9AP'oK荛%z0aO?Ϣ3^WG:q : =WtK'l{ 8VNlwwF1ɉ5^,cT}3n ɧO,uGx=Mx ]չL#1 %kˍz|K2RJ_8Z)KtQǝ%7jBzX7h;d{^~Uu h Hܒ8XzHBk@q⍄h O7 \Pf'g}Er53Ic/`ZR?oZ |g* koi/v-) _ h՘T ]xAP4cy^r] i87 x6;!G F30p*{X xd{fEs)#!sʙJBˀqtVmަ[;+QOp~2ɹb^,pݲʱL;V૫ ՛M Q\xƇ x*"8;߲=/tJF BsbQ8Yx:0*̀_ba:kI]`/ּW߯!Jn7]!W~)[wd ]HE3y&I{ *IВ-'a#cEP|FKgO# S{/H:V3iN[p$ ?ۺo$PFYY\}sTU8K=Hfb *{Ysq?7P`dl7Cr}}b5TF Ckww,w=8LT:,&H ̞$\wFtH0`;@)dRX}~GyaAV`C|"ɻ"SLztj{bԶ-QwGv%=ip_Q|,jP'Rœ-1"ϡvq#6YA۳D h_hvꅓ[M<(]ݫS:]B#Fi>zv j6l:B s#vCKQ#kIi}GX.ztt8]^ecV5~kQL͈#uFh\Ì8<|b+#/kahL]F-$%x7+! ̮A3`?4+?[ve.\ɵTj#'͝nȅ4R!}Z+Wh |G8LZӽթ~CSB=?OIM ~k2HSж7Z"·ÛWhHstq ]kWxq" 7Sd2Ӻf.jQ+cN7 9Rd$4J^%^eCѐ!O?Q;҈ԭ}nd$B@6/P TF FU_" $s?k)0͡Ì q.pcEE*oc XQ$kDn8$J5N8˧>C6T(+(BXz;> YU+iFڮiT' Z HϔsK.OdYX5 Zm7¼`&vt >l9H́QdO`m4`)`i#߉v9[bb2o ~V5eEf{K$}1-xCOW' 1Ahin zh`ۏ9jMd+J8U 7ʡRQ*˜넌#:Y#q|!pp,10W dP~،Tx4Lk;VpL i>Ub6^ EQZt\cf{1FDC閫eb̠Ԓ_Jnj5Gɞ&7,/g~>Lr.dЏCuf ":W]q bł2F ! Q5ZjcҰ-ثM0A_1:Zɭ&8Z0wS{0it sf0e<]],y a \3KtѰd(1#t]s HoQ x5zrPH|%ɎAr]+Vtߞ{ [ʣ5Gdy[ :CdBhA4A~Hr~DWWG_21hEt "q5?~Niz8)hrsɂnj;&+- H]!m:0F}gm?рzk]~oWAȭ1ENjR.Uo EUw*0kp6G vL{ ȃ o;?s49.`)_oe+ΰT$/وM`Œ04(z!]c /).&gi/UcBWN˅YsA 䫇`,2ŗ9S/^DOD u݈^1ԁ@RҲ>5JH)_sn%q^n%,j6!?R `# 7^q1KKrJJq,dMMfxǖʌƠ7 qvqGKi'ʶ0 WMj iy6T5M5i~eў" g ~ʮh֤V(NVم&B6/kńoJ1iCLe}@BXf ͜uѫ3LTHGwՀk]4gȃT$U?#5qv#R:@D[*  z rtÐQsC>EB%%T^Mڢlea0]jCL^Jo>/љrMDIKKZs%KL,؍=t.i u_v W"` sqs-I|H-$jſ:$*X|VuőI ~ra4|wٸx2zf{>- #1ROZ!a9sjUգmwqxm)Oyc2GYߵ=됂ޘ#U||!\UŧaGoޥpui1t\ʠ2sMbu(MN#hrm4@9 W@(7<݉MQRחRxͼso/S,MhvSa*3GCXI@VVVÔrL1P\"]nd{Š,I6uЁh`=PiP"b΢ODkhH7O<\gX+&bn79D`%5q H%,͡#eNy< b\UhF._vS@ 8aKIm&$a~ҽ(!.ήMYw$c%85]6J_gui 逥,75-E5SܡȲF>9!pn}cīmHYv=xz'JҨ] _.r5鱺ÂĎx{:֙֯-hP}'fHjc/U Y o?opˬ [}w&hDKzMfTB'Ak4QH(yA=x;kGF:[2өNk>s д䍽1$bX)}K=@ݛQSk7)OGIZHbb. ښZcQvN~+K|H%85Z\j3F}}JX|`ɱm\hrA9ߡD&8+vl '&^O3"[KPXJ P Wns8C aOW֎&Q@@nLz>GxsrxKMvt?_n&rC>f??Z K0 J8b.k]"N2wtCLQN<]Aة-b6qWSGǴ8?Z pj@=9NB5ZG(z<\7֬̎T 6:~S`#L#֔/ʐB8~3!_p,OԈMh7=IRROqBVTL``*.(H]7@6ųwW u<[7~E0gf!7x>Y;W]~cb6,ĮS}Pev#`qkz$㬿iqA腚FP~"fTx??IU-#'۹)|vv^햐Ң. i/Gt-3Yzp -NbGkgNߜ[5a]Psٵ6Mf+ ax@d @HDܒ?͕%M֔X[>DUL !S?;G@<|6zwh6"[yHM ʽ0=yYL@L̖: C)dp{DbR -^dNb+d>!nqd-=͊>L7}2Zө!$GLh#D qg ;lT|B-N )t{nCQmmJ,k 1Wd s]WH:V/hݛuOފc\ nq!tXdq1-!kRm$R-~pl]%rQ8ȡAN͌qϪw @8V"/tpʹ/M~ޚE^%Kd8uN+"0^.N ';+۟{].I>2cXB Q Y=B&8z_/U r5~s @ÁfjeQEu77jiRy b|_?KЉ)M` Pi#rԣEm;q?9x2ɒ˓3k[=dt}HD'8='Zr \5^=ЄS0gE uDuWS>XzmKmpk q@9jc[BL)_|c<3Ŀ#amwsPL$4: !c|ɩaq4-KHvc /w4z6 +,-{>>@ۯ^r~8Ov}K|z.=D/ˀ,M=?.<; kP%}uϳMIWrAi+SFψF}NZ{}Ro|7+DPsΚh 6ɎU.`Ѱ)߯I˝KE^n3$lwli^@׀ -iM J!{Qh=-Ga SYڸyzgq]:V;h8U.o&;2<_+X:?zq*Buh{/g2yRbcC~ CY|HR;WQ"Z"cO2Arc">pPX* ݢa{H8Ý4 aSNu;_TQ@k#.?aVv?aRj`*8t$p˝ONk/M)">eoU9l֑Jm[dF!lRor 2޽1Iݚ,wij$ R :H|%pP ѿrv)n]IĬQ~E,|1's؋di o*Եn`DIB+?g -ngJ?ޞS7^=q#i3Z]WkI_m^%;՘cBEi<1joTp׵ !L1$)[% ./u 5MEohhK Aմ E~!nqAs2 NZE+YprWR*u_fvnfk{8R KfF Ϊ>I?G6.UJ`$fzqP: !U0>S[Xjcn+< |}z zQ*m2" Bk nY^7!Tؿ.0,(X34 mHv4rv/%"?Xw5>jOׇ]q:Rs}5gH_yQZS .ʤZ2ζx鶳RFGl3Y`']'egSq؆s!˶{Éo: BTUuF4kNukV+Q0vLi}`S4V@œWs~PE t~QZ51.WU䃆צK y\}H ѐ_Sr]VJO<>cP[W Q,gYjwMPBپ GSg3l&*4t- %J]qMljzi)|#n0E #VVKsk|FիPS\Ĝ!+('?D9B${v i3}۽<@V@\q8߼CU.`{BeO_&|>z?#B⤁:{A^ 铺 @AєCƱAN,ho԰f9/fOnn!!:g? 521fb bfy:I:?Tg𻆺}+ %yj\9KC~06ߋg:688T0o*1sMg *l<*݅x<2i%^W/hEK/R|/|qd7m v~Zj~%/Kٲ] ȃyzpRjAp(Ze?Cs}+%zG1t5Yr)W[QnS[/Qokk0XJEgCR ]c0(.b٨xj&"H#< Y>Чg&v!Gu{H\^~y =tBD#_0MBⵯ|UGg}`AL0~w$AUG2sF}F",GC,)=7 ҍo$F}~Ktn PܧG\u>y]#,ͬ$QU]paTiޝ Ьo@bK_݄B)dYS~hbmVkk`~VF88{?21*P!<Ϲmw7Qw}X7 8/堸ץKɘI"i:ƉIVjJ '&ZydW4t۵iI!0/͂wx{jKVdTgiVf];XZ&Ģ @HPj*%8d58caMR C Psv8x[zǔPQ$DÑ8$j#ؒ,w]$OF$33ٺDqM^ry!H\^[VS7̓aKz~"](Phpq\'.a Rt?A'KdcC$@C"Wvw6o@ysʱrOV.v T ,i[$daPybh޾"hGY!['%gY|`Ϸ<,}u1*ZZ=XoAuxY==yl㷍ߵJDr }[3BND p. ͮ=YƊ Ϡp䔿dvterZ䫘am|?lJ1~>}M3 /(=R wU䭚횦2h m7 1ï9<5<{>VθK='k=d!춧}7:/Uv,';.Cf$&4RӋBfT#LY kT{Eaqthx`;Ggv`׃Kw8<7@1̄h׍$szyyUz m\~:lǭ~ӱ(%_hGt#JeC%|*_H+exsXoWy& YI-62=>E.oQYH3W',MUVT}О]+lwtNkVRrwzćk(9s!gа8#%0Bi0_l)Z3cM0Np.`7O>#Au51;Rutd=nc,uz$ACOF 'JvH}$}h#n͖myW10/Pa'bJ[D_m,K*`UwܣGOvmr^@@L$n'R4q֖m:Ur? =4&p56HG#'~bAF.KSl"JC.kMա>P3< BƩ|kO+rc .C<:?"ڔe;^x$QW0a(=c^ZĽkLʠ4A3~ #?2dڈJ%ܦBX&QKTKDzmG$'8.@ΧGK4*sYPR2GA # 3,SUε[>19]@qxU1b[bP i2ECּN}_Ic{_ݦګu}5+"W#e4o ޟC':PY0 ?Ճf7u!II|S* MpQӃ)B2 9^T G䒫,^ES ԳeepI`wב{J,j6D:7g&n<][(h/㫘F׭"sxba vOvf0u9♉ehg=^("&l∧N"v`{lQd~=Ld-XBѤmzKz40ay_l~C ĩա\MLg*;^pE,0Nq98gfN'w.YyNآS>G7!I#w8 1[5 6{<+.9n%v#.iqW!SSVt !yD4VPs!Þ2OKW Ekp߮A6˷ƩǐO[7$r wڀڋت ]7heY(`=FݕnV22=RO3V= ?\N.)+j(hykykTeS0)ixTmYe= ؤNcqA}jEGLtWču.Q:v KC7zAkve"\ūV @WXX-EZ֨q&"6mm? iJ%r{z0$*h:9KXFB0k~E*!QyM:E.>k=?>WA>ib)ASX-+/hkLv6$C׎vځxzv0+5,HXfEoċOӏihd!N VG!X L#+Ƿ*%D$6=#gD`EsP`ĤCP)s1q킿F6ax}#&|Q,2N6](_cpp6x Q LZ8%[\emF4ORXi]rqfmBNIe+{[.Jm~Zox.b\qQr=t޾;/o"ᨂĜFs'O&~x1I[ZKqCPgdEs7;`s*_>j7Rn 3d&`%~ Ij uS5PGMddIC1&9rH(]V\[t]jSci*sQv ~/dr=QR' cyl^eձ@\oS-m>ʔ=¥okH4~W+eZ#?!ҷQPJj Ǧ9;L2$cdl9? ~ebSe<-*UlHT`4ymdžryw5hVw:Z3 |Yep}=A" :"Asof4ph* p\kl3PR. )έRe29q֯͝Oe_g :ڈs)JO-pKrG֓ ?a6 a.p _;nfp ,"yBD:\,،=䞽}Nh|1$&mc/ <,M3njqnЄIe;dл·YQ"[Օqr^$PƖ2"e[#+;NmFJn(c 7vEOBMOPi]++/4V,c2@>AFŻ"]X0˦U 8o(q݉LZ/6ĜUHyVpiɷQJV7>GA۔X{]߹ Gƫ,~rNs/jO=?h^<3O€.Tj34|<\bόtW1 `*`j-^h̅c3'ѡ)F= wJG ψk&90srTБd0a!dTr 2POed񷱅=EEeI 9P$O{!T#+s•: yz bcO B)z9}{M+JxA~tMkt2e=6 C%!4updE5 F%_RwH8ĵ>rM06KjgDe>g_aW5I _ m/6-5si(GwB.*$yK٤t944~}FƑaA+wcXگJr*S,>-rUUɬO sHj#e*lWki*ůVjORRx㔐ɕ# e<:]/:¦Y?kOAY*|{?: C ?! (1.Opy & ;LEd.ܥpuz#;fzߘd2@i{fh<)xW(hOd4Dy@e{n`s+ KCʹAz8U3]Âi w]Jx7t^Wl\{Le=]p=ڪj98g'bs.15p%bw_Gɯ*9z$TU@5 $TT|9"4ESR(x3aM#A &[`}bQODĹ픈Ԃn7 =?L ;Hx;Zێ; S-A7^_@S>%aZ G ]ss_qO`@Oc-GZ YkW B˺kBUh$5nBuY[!Xk939Sc=G;0ǜEۙKA*s<g*@ϏGa ӅnNhU Zf|1@Aid?BwY"Ou+#}:[eLnB c#͉8U4VhKdk퀀ԓ}+s >i$K2'q3R$왇`9ϣ dGkgsǁܟӵl{<I>o&`;Ǫ[(߳Aahf FerUŮB@VYJdiF5ڛ|ѬPf遫N`ɀ޸#ˋG7+7BTeP`=ÿPw\pSqļ6XL_fXm_ns.'tI S]ʩM%BSєH}}]wkPrYC9BUtKGtI޷@ $zV&t]-/Qzz$jdsk9ZYbe '-?IP%w|']8Vz2|23co$f٘.<=6 /W/J^l5SS M x^Z.n49_jNJ&i+b[YJFc ;Dl oMa4㾧K%a%>Gs+sJ-o&d齠_\5Il( ~IHE^MW+Bt&h}в~9=f^ty\(hpovxBrOKT03:(#ޡMj/n#+f[mk[T ^|6#T_%.rDt5sMPiK{S]玕=ʇ)hNUbReà,gG??rcL6C]L}WI= 4:.ü8jP/rY R eAeyTH],yjEa~VY9FQ[^ s׍л%:,*k=`җHp>5#d?PֶýωōIqwuV&01%Z͐ooxG B=gWx< Z!/)KsKkj@n|UvFD?U96u,lf'6y{Y{XSmDnZaZ+yUc(qD]eCuVziیB:^sW77i"Mo7,Ҝ}IG3QŽ8 Y[EŨ/ЉkM(gkCe\~2f?T0sCMTC^ ]'[gqjvEcJ:*qBglQU1C6c/cqo|c5%)}Ms4m+R60o ]r|fP3}d_8~dv;Shᅡ Ed:9{(3ϲ+5z½I80bTJZ}7oαY X-?FGIf긯d蒔A 2#e2+ViDH`k1+G+ |D~ap]hPڜ5MJS`<- 铷RZh'&m3Wi 6 Oh3z|dvz&h~}60IU22E̬s|V9y&b?VMyBa9}TK8Yv0Ǐe`S |.5ͰESX&)<RiLS1AsHnḱ1/gQ1glNćVGTș:{Hjjye tQ!MִVT;5ڌBX*wxTUnpЫhPjs[kH %D4 1BQ{aVs p+nݩ,b\vİ0ݔy#-a<~*HoGn.;5sy6|Kmu!/q4S )>d-9~ʴ6RbyS5zB{G yqU{ˁG,ߛhq Z0il;$3S\ݖ鵮9'FuIeQ^(V!(fc1|"q ỀX:=U%<7F3egu9O\vV%=M*g&]g {lJ5$YrKt-ֺդ8&u ;hbNy3>á\B}$̽-$=gdgPn"쭚-rA´ M4PGS}EYUd4nF$OIZ5lLJCf{Afj=bTYM2z)O,,e'e}/WB+P~~cD2%x"S~KQs>٢Jㅢ6qXׄ]8WK*"KF+G;7n2Ӏ5gdBH\Vqlb4hgoV3 *գ4y̺oE@5n!PC<,P 9,c%ל H*m=q~܎/2YhP, 1+|nXy"lLV a5 4v]6Y$/Nh|lD JpZuhXtΪÒ̢q0vm #h h,jZPYh˨RC]YdE~Mey!~-=|-0KX-;k3щ'JAz6HnĸE\[Ǚ$k 4sL|ĊJ:fܜRzb&[¢rWcy ]#\DVpE/6@َ%{c>\!f:QG%b -F 7|e&L+8KvWJPe\5|-}NG`3V]OA({%gT]&!HLҔӕ^@|z|A(8#6 WPv[ZXx`N`hgi's@ȓ(%,$!$2(wSWjx[Eyߗ(F_LѫvCP;t XH~3 hKjWT)2nǫ~)2bH) wxK4ߚ+]I|`ZL y]NUzBYK1_e;pfEvZJ2~{lȚ3875 N " =م.mb}JV]nɴL/X[̗tw7r~hWʩF`I<^? Ct+mļZV&!M;^ pw[#3,kTϛDdNGKKvnMJ?cS\ {ݔ$RTI]b Rqs IS== /-dsIi(ζ2mjQ"]W > 'ȹB|Yw#s^Ox :uOK(9yQ3h2r7.6'xU,1*JFw^||B`|OS5ɨTboD{YWșnidݐ*a-S븥0[SdvʱdVqQ.)z˗'zT)x&1=ӹ}cFԣ/ײ>K4d޹q@ ,i?.E s !#Bd Gmn4,E-ed!ÝR%2Sh-|Cܚljn9\َS&t5Rx2',yzO b R<3X ~;nD͘BW#C\3hQK5wRp0\|n\4)(?;Afl:ml QďZrR0"{e>iŝFxq;Wvt\n@=@-X7CVIm!鹪&_*U N}RwvVV!Fl xCˆ)Y^J.&hZKj @wcL .eIs!4d9j Bh©=mRDPFlp6Rh]q|r{(g"/Ka!%8[ܱZQ!uW^ӈXUfwm?mbx1gj)xj)HZ0|*fKy0;{(kbZ ok,Y:Q+?6E~mL_GQEy:;x7S_>p<*-iK|0CYReLK j}9 v)'s'];RlAGO /fxq6N x',:Tt<3?ݾy\(*Ǻ+JPꁶ;(Rd˶ZE~0u7ԘS)QNNgX+ϓ>~KI$H/Y;5{놁'Ct6g 0Qa0]dnHduHïӣ7y| ăq37Ldmn3)ZEh8d C>MNq"|$nN'|x!hZOmcy}AÍ%8A0Iys@zUNo8EQ,pc 嗦0@*2I}p0VfΗ|YSԆcpӒ(y7Ym.$n3"z4 )ޭ*҃ABH Y$$b:-8@Qsz3"' XRbGs >G" hϱ ,7$N "_BybL/5}}&м,tьMFU tgYNeqd*6喸ѐ/Ƣ7)q0[If0?iIGSrBlޚxp:>_ ζze:+fZQ,.CѨ $^F`J&Zbaui/f [g MK{h: EVoZX5ENhFEc`s2ύ-vB;$_ҏJ W5qgSP<ձyA~(B.jBH4l8 N,D QGf V u A9A2av=E|1(VBٕCO' e7 |zZT*9xiU0>"+*qxI#?0L*5Ui72RqkÍYkhlw;qoX8Yy"C]p$[LvDf4R7HAzgh#t)$Fe%Ud85WU"-{"2qlw|_^OI9[z* TW3+RMá*돿7bBm2;es%8 afAxkLE}=^ģ@yA0EjYG(nXM뎒M"?ul{w%dNW/Y '%2咤 VzHz5φ^S h7<6njsOBJ2CzV .lnrl11#9捧$0f߄.毌I;DnG0q=F H/ݰWAi5Wjyϻ -Lk5#!rMMǣEVSH.z(vf$v&g}}mD-^|CđL o"]V1ȳۡjp2gܯp]#i"zx>OJdYzI1)$44(Į@(ι=aAFT{{Hھx܃8i\R:7[SƹmjGѺ^ho Q B`]ǑD'@\ԧ@ w,&]̨Fc1O^Q#ceS.O?k@sHP rV͹l U:yO|qLwBlb}FAle*un1=*=n) 4N~a` no 'O:/8ծ6^+T۬W+pJTȽrQOriw"G!g;/ztw*CxbHwEG{5jK6hzߞmdaCQ į|\Q!%7y[\gj~KMpp:a4Yisb4a0hPdWFc`7o`{p"vt 66U[u^K'In`Zl8V:2!LOM /t$=3 e &cX56P\"!jhJ=i?j:AJqĢ{8E _J{1jz0c4{vf:LWlXge.szPՔ #S[]p<ើfUF'E (1v]@eI}ɖ-s鲄09)(=p<{~lp-:~w0{{*tLEִz.8>FIifТZ@%sZqYS ?{E%q)jw T{cC@#S~Y;C⵮B}|lԼ\(vJ}4\]sw;`PIҾoQvwAÌ˅pƋ_D.2Ƣl®Fk\쳠\3`p ޙS WEPıO]atU [`dD6K W]Ϸr*kp^j A3OW^R*7%˪8L.HAZ-ruIcuqĵEp+nS}yK󉼯7 c~r\(4PirN}䫃o{wca|ڠ3& 4e؛f}?"vLjPg˲ 碢Pdx"n7O[ٍ`֙X4H˯p6jfN/+ p/B,`Z9g޷QqNqK-u`]022>:FPהѝ:X5_*ݻ(^b]7`Z s(dRU*ΖE3p5bs(ߗ%wIκ rR.gc.N}e漜X vvk4QV# i9|$n9|:z4o L۲Y,ƛ qn H{D%0${ mz^GzXB 9Qra^M・|.Agw Qܝ!;v*:ZE R.!u93w68M^%5 R7=Hb`ruWl__ ;mi~[j)i ґ&-N\dʲZ8X|nb|X՞WAwڻ04&[`*]v|aه򐁧B zus k=-c*5s/3ΈI4ޗ/YO,C-*@Ѵ2<#kM5el/N6\}4^ g K(^TЙB}: 1-g0u6JJ񈐺MG 0Q;"g<gAYޫz;Mq%16K ;`d ljGS)!T[ '4+Qv1(k}O)B91`RNuܞԏ,:7jO2BNq9^[ဋv9bαMD{h0> NCr 6g*}YQ+θFkx8]n}&Z ԽRێEB. QP%B4pG 'ѽW1v?hS*y=2W_[#O$ uʜٷdJTrE Gb fSjX1hJZ6En$fSjVS0vIin[H=1E&|0\SMxfM/+[ы!>B[)8[߳%ޜjX ? hA~2i]GDTԗڟُ9Nnյ|:PkA L45ۋf:a+v,ecM)J2 dЍiJ+ɱGK0O2I^7j6:5RF\݉_yV;ǽ0<3Nu&D&7B\ȕS)ȫxKd&>#PmIeQ4 !e(0U C9޶pH ʮZ/%ܜ^ZwqKɋϻ^ԀT֜3*~;"'#%Ss!q2]s:Y~@d:& 򦢷aէG~/"v&.خ_#1]=\(V«5ɿ>]L˾!PPCmEzyM!`y :w5FFː|=$mYwuݵzܣ4ndv09Y0k5 @@@?Ȯ"OfX 'ǷI?8s^Ɠ[XA*֞:>ȨWoob2 /  ٦ȏlfwIiSܒ͝bj5z~[;TwOlA?L+t|aS9Hz/|A%a[`7,8CA԰kyY.5 ݯ@0jI#JQ$fNX8mRu4Q=WI{J#*j8PzU?fMUJ ߡ],ZRɦ%dwIr7Mlym5nF ͽ50-D4DmL3uIv8ԙ;J A3tr$Yɞ dO+Hפ' Y\$H@daɝ9k'C.h27Nu%Tmc?Z3BN^\?*K5og5iGd'OT[%)Cz4 gKlyl'6߸fưbm\N?J{b|I9L}rJTqYr >2?!<ƀ6J\@9wP ) RSwpt8~)= )+AIb Fug )߽.+D+4B+l3X# EDL/swdͿS[҆حU_7|SlQWewv^]yMx71)'MIvArJY)TtZ$#L|aR 򌜂"pentSV{0CQXq]3i.37%ߊ* W ǶsBߢrihhi[ W,6: /7JgA)/mDYd+0yeI~D^<}7b5#q"ƒv]28t54sE[6U.$r0ؔ ~z' KKF4Wdz2\iv*O7/KvrZ ?T6'<(vX{+cdZ!w^x=-a yܞeLRt 9fmW.*] L~angz2k j,&ZJx fI ʾR;7BCf! z#Fh֘:!-4KWxĕCmgX1@:8$#TPʎ ɶҋ]Ż$Fo6 qqMGZÿBKe0P<)Nlꕃ%f5__(J&v$bY%\K%EL orwct7+)lֱ޿ݩ+&R@z[\^RdBʓ*)Jb$俹@Z%"J׸B{#FOHy 5}Lo9S6h:֥9=Oq&y\U7!Ȭœ3-xbkiH{ibrfͥϤ>oj#rA7 ٝVd%k+W/eMQUb3Y3lpðNj%Ja;MkJMbļ @̵ 3+GF.uN_S%^ꟼ&rN)ԓQQOMw^)R+e%8JDs߱:q~7 O^pzKY\]~/֢Tϧv. H>R꘵qi(x~º\*\?o]_lwՖg ̒2K qȁk쮓\#Rvǒ&6gS_x:LZҝ&6U< osVuaڋ8< 7 ,O@/yKnb{1gZp϶Yh7^K']2DLClĊ/N17WoIP-y0=8!_*$mj‰ pT``pP й^5[Iڲ@,:BpqB^lAEZ `lm4Nz"?Č>_F30YE {Z- .E(m+z`sf7J٣*۠:+]1.24q`x&[Z. ѳUAEFoGPA/&F@ƽ3YE*X;=#~vg Ýa8H{t@7|}g`{_٥:G֛ށq-?>4xϟZAyɾSHhh8&A|)uC^GaiyΆP5rVg0UlȣK]OcKHYlH kNv sKU'V?O-_1g<^0L]Q}(]l>'7@NZ;+>r%`0\(0g_lQks +#W O_I.C(j;s<U;féՙEc'^;B?9 `BH:}<~r9a\wHŸT] dftlS)@aMLU|RA!̫y1f :i*.^ liL-t㩉u-ZVz ^ᯅ#g+j{N~{}X1D7$^Eɪ+>YtџGVP{F\l^enA \ W"mX6+XѩG3rO_(.MB @!|a)sG~^R Չ;,$Lc,<XjѡiƿB!>XH]o H,>M ]n5pd@n#Is/_#MIgj,qOQI( [hdzcIr(&>> 䦒j惀P@ SaP8b<o{SZ΀{tv+#EUOE,[W-:òbCr*3Xn ŮQ] a%kֹàm˰D(KQ@+1f<{cBef#V!yh3/td2}@Ӥ@Z^y} jǤ)3y],Sphw3t8rQyt|<蹿BpF_kIE#:1LqsKoV&XX5/fWɯ$#<7'SX{u i5l1Kᠱ0HE-h\I7[V3L3qdtE!]ryo}@S2L tKT#4 ȡvldžA)9q`m_q$+U)S ԮfV9T)D}k8*6A7М{ؚvuT2y09z1U˚ƀ$//~:)BR1"#[/X;7h#SfdHx*vc6OP$-#8{CN^6< Kgr `tY,;թ'e%iN&?`pE(v7n;t%p^kb< _,)^<Q.EH<5bVq*G`=KodžƎ(&.rwIQP(:52@zgّlMpY& 7nh:Hp;Ҫ1#mgA,W%VvsX!F[j@*o%[6!Z/gn:@Zwr&-"Y\/lt ML{5bo\fIA=ݔ1qu⼿9+!TLI^uRn离xZ\M|UlED2o|@L%ˍ7g|5fslSW7y10 hr!sϮuZ%iKT6is{l9{oN<<ſRǹПT Rfc߆X!*]86ŬEG"z,śƔmF,U>UrGnS@5ipLXRhR6k_dsq* P>3PZƧ2Q#1kn%{S˒(S&9e~N,~)[OAyGjϮdU\Yt`q:4z9~4|d֦8I"@Yp]YC5DX 0GeN)V䊁 5&Z({"= 6NAK4rqw~Vk-vT\v<۪"k C3uХۏ7:˜%S"R j(:5 *5v͡i\`ыW&)R'?aTpTFۈ?U%:}-mVPh "j@3H؝gDKW)A"6t4 0FHW. -`fmD]iQ^3LsžO{K\YAt-~?%,sէ4賣k" ?aZ>qT{A]ʨ (mCΕP? o8}  k|e;1j^7ᙐ}}[XOB0D0^O 㥊7j' N:]Kys=ɤt\^{*?PKHI[V=༃jV4f4FnU E JrLZ[hiJ(GU`$U.yn@N;;[h3&53+ӠDUG.~T`Mf(9'Nxe›\~-Ez4$xIװ͈ O5B>E?=vwoJ*0͏4Zg^1ߛރv,Ot #7#ՌK@JN}qy=ėħ0'wNL&զġ(c;K0X\(! 5 .tQ<|bѳ\E8 `諘9-MȎsJZ>Uv`sVᵙ Io { ?q0.5έpY5>ţ#C^8JN*O pΊϴFc F=ղrq^35`DHPC0.#::'ߍve$?αd7}Ħ} ]3hғnWɿ=k(y >}bGDSa%S75H F̨"m Ö pm=&BL4"+*c囹t!yF9 c93-vJS"!x*Ms3lY'T.%rWMi<%{ gě:sdF!盦CaXLJM֫Oc?UxHSI*[KXWFȉ߹tL8Oƿj"r5/I9ܖd{"UћܥNO:V`G}6\cdy^` zO!0sm^~_{żwQ(NެnцCn@fha?x ǜJ2Pو{DŽx2[*Bqgs.2[8:Х\?>P?@I㖞MmoaERmdaUusoirյkfٴzi/:қ mjX"Dp+$5+jﶕe0%8<Ȟ ^7ג;BqH u=GoH!,8Ti$?W .7JΠ ޏѫ^{I3[:VV@-@R=GI}|J:DMYMZ FTº6 s+uL\! 8&a䟿T`Ϛvy.fĩgtG›kR[SOA8}Oc)FCRAp[,TR,Zl4;d`fb y%*?1*_ $:a)]5QYH\Xؕ7yA6[Ŵ' ɤ"jKwua6Ņ;tPlAq9miކbše%37?6*.Pcհ_2ɼ5,7VWEQpZa>if;dL>?dX]dbN޵![gBDqRI%ѶN.f|]qa=?8F{Vj +) GF Zm[] ~vHNRdbNJI F}>:B׽ƨr{k"ķ!ȥFzU ƐJ^'rw B p>]c!|e+ʞ2s =Дax(TqG"r:^m-b>>DۑlSHggM0ZbF:i ? oz]Zڒ]|3Ɓ?## y5 KEV=ȁu8AwxQ"GlSC= U{{mGvBRu)MCӠrYI5A:WwgQrĈZilv-8C592Tq~]6]Z16R/>!g);Pt<̚/Lm+D>͡],?̬ir1hj6C\[KuA5\2vԉv~' H=䈜čF'N/@3&n .zg^YMj5jcO`MF֦jƳcQDOx>1Bd+qg4WEČ-}6 4V*L`$[Ul<+*|:^+s/0_T쮱V) N;iʉ''+" 8sOY?s6=ݒxpvX[2 ٛeq: &ʩ C_3$fIǑs!&"d̜z0||O0Aw :}^1&N& R45HRNiJ7(/DצscL"l_]RȿO鉋 3b$ <j% OF2M5H+&.zSq$STudUM H}tŽsͨaltr gc,$Pd.HN) ؒ@/\9*sO(Yqf\vfioeyi<o++WJ[@fG3*H16k-;sw cއ6u 5+/{&5LW vham>\=]"Yn4]>1Yc$)/q^z Xh#Z21Ti>cЁ˱ ,p *?#bQD/]N+q /K5fQ{yfCC9Y[k 0AmM_DJiJً<w< ;e6N.r2 9e\?RtMcNjd:ֺO\O@=\t%d滊:P gç |j6| :z71ۨq fƻ%u@?a9Mn;_D=T^pna{|OϧvTq+=b8Qomvu8,SկV DQq1.Q +~8TmBT}9ω&lz{C3S ܵA8t/^vc_߸W l<')1!B @^Wx 얎KnHB;l2įGLu"Ma.zCTf}%? w8O<-wZh钒\涨rpyC҆3,bȝ6!c%eOlE62{cofwDŽ3dlCH[s' lwAçە~5h|/IqdO <rkLYqa13IX4A&SnazW.ѳ 03o"kT=ȅBErVk`&oC):sug-ϼю7BF2Q n/4'%1nʧTSPZv-\R݇L'ѕr_s(.,&O(LBFgIAn>WeKGl1ۤS|E#urHnSj"SRg]vEQgGPP&Gʒ}M$/i11n{j7J ZUsAӨd˰]&XpHrnc8Q͞<͑ufs o* Xd gDЭؐ@±3#oWx+zSr"Oľ=8Fi1qⰨ2bUs]З4/OmN6綅iH*wL Qaޔ mzwTejX(,_3U(S<^e5ݭ/^0qyk@zy䳓p]zNƔ2jܾN'm|WnYf&<' R59Q^8 Bi8Xb3>ɌF{yt_\D {Wvn4*ʅM.)- P!\nj$PFe)iS ӥ؃ SkdL+`z@905|42GBbH-!^0s=SHeh!J83##~BƻTOH'A~ (p8qQhؽ]+=p MiNߣ0e?= ђ㰯$EN4rGa,-'W Lbr? է׵g_3HQfg$nWsٳ_z,Vi5.L$X'{"jt3P2 P+rpE*o-tC(C.~SX"}\3i$1Bl\ٮۀw a .dì:.r'|c4f!\%>NjH`~ٓa9Ó>8Iɜ #:R!Dz3hqGӿAbAkxN sV11&kT|Gׅoyjxʴ4E9S@ u"rHա_y§jvF:\m9F#B$AAa?ϰIJzTL!x32i*ڶ}0;ݑFt f2q4趏,Ka}j(8՚EҤy©zP…t{x &`"7 'Z5;ZfztSa(BKIDc+1+p*uflQ/Ifm_lu!./aBrB]s_37Bb܀уN-ʲqSIb؇;a~7#HMK$M,|{qP^褘̸xTU +DU#ɒOS3l])Z0i= 䟮_`[!z ;Pތ >v>-pB|@0QJUJeXZR/I,ms$67NtB:L pfsKǛ/N"@z9 , S# ;sq AfXeYX -&:ڃR_@, .\Lrl9)8_$ #`5N&9CZ16Iq̉xrH6h)J BNM'ˍVOBNCLLoJJ|Suab] P>K4Tþ BR(tU,D2AJ0xhLwdNDz''Zq;, jmJ?hZYm[rdB Gٸ2Ç=Mr|:gcןѼ&%6*>jRv3U͵c_[(^>m3WNcTnt5QkAWZݻ0+pOiprx<}\XÂu{Sg]'cMrJ@0yBݢh#]߾Pg4Q U;uh Ss^BZEP. eBe|urHΔ>a4ݫ`[baPjh7 pIy3(jp¢LrT;PNڶRF7nDxI>&gB$;J{!H+q`yFDcUILiOCayrqMgt5q$]p! '^k,kxQymؓ5F'rK +K5C즫HRYQĒsz^Wf٫y ?3<)4Mm7!7kc /aS4-I Us rd^]HO9jL/Ͻ; .`D"wόg]9TZ?;]VtFqx)J9ow*V!0i-^v f4r*Fl51fKI.2Nt@n$@Á;a V4XDT0Ơs5LRdp4LHZ!7pL]ZcbVv!oV@^399t$l@CGȒ!]_>BfIJ~o@ ע# y LD|"z9W '%-@-' z,_*@1"tʓxb}-Ӯ}@l"d ]A 8z\r( Y|\##ث5+`5?_lP=(.{- viPዷnHR =DsO< B<WĢڗ¤o U V^#UUA-Qkh7ݷypB}gɧ _/Q#)7<הa@Ң@u` !;~P'9+rli+gr [je?HԤ썚 "@J}L,.NPՏ+0ī@(_mmFR<%x%\"0/㽠aX@eStqPё[nj芸#;Mq)P:nܼd޺U݋@XrR$EA9n%Àg[1q -2Ṭ4ӍC8U]%&r -8sᬟfQLTYa[mUԌFgzH0;T[jt'U}oϩOşg X. "hpWB<:&`6xa)[ⷵq&zw\xlS`t?$ɺxa7@*K1}an]o %p1 IqD#ӻ)\ٕ3&[hôP|7꘨[Y.5ėEYQuTsh"զy? =/? gzk(ӧQ>wQqKxZ@0TTNC5;o;3K4eViGH&`Z5e8xcH@!<+%:˱33Gt!dej2< pVN|\% w/xnv1L;.) {t *g.pN]9)YD7{$LF\ni˓6 Г.~(f@?$BgOLC(Iڎ{'0QNM(Z2p66;3" NLwS@FF\} q$ڲ) I~-GJDi2z8suYg~*⌋pN8J`:Iz$-pD~jDU2qTl\Խ:c۷W^W>l.j-ŵM8΢ \- " XU( B+[_zżOf[0erf$F>@evk+v#:kV{!(vRҺ# 'k>5Si`%۽Z2Uft Dk Fl[րY8m{]͜,(&5.FN#l<)efk[7W.{b@%Q=n'v`X}IdF-`1C@o -~f25xy CAUp 뒧AřBKb|N"V/ol&vKtFD%r;Ď@i`7 tǛd !; sxH|w;(˼VOT:]Ex+Lp`0ETiJcگ]ߤ@XUpb>cm _rۼ6>}wP3ؚ>K{ G\,A_sVsA Ҵ9FLIi0PrNtB#Oietg-a#xgRr/$uXNݢhc/S) PP}T<| dwJW"i[n~9=DYq`}~*lEDTAdjX?Ruِ9Xb++4Z<#4)Tu_#%-8Mʲ۝M֘(32,aXŠAԯZ6+Jx=TSFca5\a {RM8 & 6ܬ F DS*faK&Uj>z3mFkO^?.΢Ho;^aYJO>g$"`l0YI^j#:rvb(H(Ȱ?~K90:~64@1 2Ƅ3(D69ze_m_p#{E0(eRN47'{ )⡴rY@ii5˼HϘiD.LY=Q/%4 .z{g8p!j$P>WW,$S([i]jDmN$yZXPdf}p FsXyw$\m7`ot$q7\VwfRruYNe_GƆOIcbt5U3${p,#쪆V S wYY^ͅvDR'LCYd,|`m8F&O2ut((P]) KlZJZ6[LRAT !^'[ˊFdDH2W-[5ZT #b՞ڔݽnUN\b0}Wӟ>H n.xIV6jQ gʔ0$Mg׈\AP2xη8p %Te|}nb(v@dVYe %͊m47x<֯[਴YLcԄMl`Lf|E.߁(xtP~Q,47Z]CT:Z=u.4Q}}Tgۑ"G[fu%2)4@|:yzO}#|У+;h5U4yp[BW5t3oX^c )a,a.0&H y 7Nn.K 7Z8  KNE} ᄷ%hJjNgl!Ҽ;6{{C,pY.TdX=M @T>w[}( g)W@ֹ؂J&*9y GcD:1|JA;)B€s7Ô>=++g[Ao֚,8]lx 1y=Xϩee?@  آgJbJ$w Ab`^WzZ[9M1:9AM)5@- rCN{vChU {R$ AFvUuD3Vn1dOnC_ٜ!`3j6/A8_7I>|cma&۳#ΈߺsL M ̩:K2>`ψCtjtlK\?Trrp~}}l4&kޑo؎R,juݒh 1VLRrśn3Ҭ{Gb8tv e[M'X=^FN^)֊\2 6\Q#&<T}C aZ8j^݊j#t$VD7k&L+GQ׳1ŋ!APKm b kCR8.\X2o>? KDJrdpPv{Zˑzkh^E_G:Tq{[tMnpY}TPv*E25oXjGzqbEGQ=1iYez̰gU61h' mPLTCp*M_k _,P0J]iWe#O=*8ݞyÎ)Bt8]2]WWFTcXSK(/&u3F L]㋨'* ljEJmW=[D'{/ckbn7Jl BLy8 y8qvw5-H4JQy\g_αψ- ORfAz寒v`6[4ͺCSś<֙A:ꜰD*jrT@u b'iK;'E@%mmgжIbv>zq*s=Ifj;aZHmp> Mt&t~kh*S>k˸KNڎ^bYҪ{|aI,zwԑAz"4+U1㾫Cw@/~f@q+y/s@o.bPž"rO,ݭCl&9تvn\Ƈ\8G^ u"=7kX־lbsMlU 14t_Ébn}PCK[=&ѴI gޮ/Z*$c BB߹! .: UL(4Ta=?\v("Bd}FSr<θAvecEyv'h/SC,^q?7Üs;m;1l =u03k:ȭ\Ymo0Kg\Me@qP񽱐y  M z0X:( 1M:V`q{%3B嬖q ݐߒ&C&$/$ۻTf-#pfQBSI^˭=\\/}} i L9[ƅ@Y)I$f[/IM\[ K=čBKTx܁_)7D; 񅢊]%ɴ+Er,( ^7,wq5p=:]F|z'҄l [- '웻+j7FPO}[b=N2M[]ɺ0-%vvWv6jMsǥ2 Bee?Kiu֊J]$jWR=︀`;Ж)l]N΄kH;Kàcp-S~a2B'b4uP`NX# \OUSsT=T"eG8k{;}(aj~yE[ݼqA )E 4 Ў=S%vzBZ8>˪1rKG.xh9p ]۠[WfDFJZ ء)6Fr$=5 lQMY}06_KʼnbeZh#sp"BҰ礳˃#Xg5ի#g$! ]|.Vz?ߝ|u6"9UO`'GJyq"ԝ1t)raD-b[AˬU{{A oܨ`Y&-{͊Pj 5LлuڼJ A %c E*+ "V&#F u I终 _G#Zc8(Jǭ]))6G#/]b7.zt8K0^;ZinF0;(5Gr-Hp\jG'Ou 5||w}CL `lK$_NL@= /I9*za;ʲpIԩЉFzzp`? o܂;b񋻞H$tB(( 3JJ1Fk("eQE;ʍVL$k4 t-h x$svIL]\O|VeG7L}7.ۢltcJOձ{vQ-(2=g -rs8B Dzp5.oA=ꆼTlO3hAtr%Z;~$Eg6ZwCقp`0CxJԷM0eZZ!wkqٔ:jyAʕƞ܈US2W(}35#'!vAN$1 WOTeĆ#pT`=ҿU%la`4$oVRt@}7koVuMÑBhE,c E8.vM.PTku];PpJf 3%}=9u;z0Pk%寧l Ʉ%XMS>Z$󑲴yGR]y&Y1*ScܟXV"Baa )BxҾ$KwuI0l+jTHz`{gU<} hmzuW!cQ|ջ/1 !nA'aZ48hbVnUg> `v՛X۶^]ɬ<CynaсW\&O7 7= U"4f(&/Rڗ ]Z;faQ?)6<| eN-y\ ѐafE6"v!!PW;cdFȢb{rnP?UK}=wn$\5l]5t%H/A )«Gax _OF훠cTe) Xecq"򥪿3l9ucq̛a# Dݽo>ERVn_r?kr+9}. +h{o7YߒI*$KB PVc$PxVyIpθHgPSä6}O'7nLriۄjumtGwȗg idpXj#ƭ { YR.uJ$\ڧI#۽18~CaB K_Oܱ]gЄ"T7XU$+zf:͛xϡV--짪_y(x{?Bl Ⱥt譶2Ġا튲P @`tg 8@ J ~1]lxl= nDd4\_k'_:=H;SQ \qLwq6G{hH@쳨f'xhۣkS<=I4EeOSaNaŘJ$pZխxrN6 ̽ .Wp?> lũ-k-U+i&Q`;^o k';ᡬ@ ?Es\ KU#@IP joT-~&/A!c<$i'vp4HfKako7,IJTÈ"vcb"y?F[  :70.UJk5k~ô\Y6nX k"LTrRt",C\#Vv, dV4XO V@x^U]\)DHhA\18IUz03 C֨B>.@Щ]A f>@nE u +}|R0-'Gc=bn+l'L+Iz{UH4\MH +QpopS," 1%qPG<)n4}FU# [VjPi)#;n2ib[Ԣs~-.+'~mU0Rq5%<:H Hӡl&vn{kHLS#(◷<h(mHƒ![ ΫNtcʑzg[\?hou(U\3<\ e x昬Nb)0!TGPQ~JyCJ:9$ЅcM oE?"xtQ$wh)w`%}uZ`,wA .0!j@m㛼6w"k#F$7k>y;8g{x)`kȐ{MɯꃆpI7z!-WKЀ|貍 6|,_P^+x]]ɸQh/_7!J!c}LGZ3xF<&*SVJE~wSDԖk0Qq] I.'l<} !J¡\v6@غ~6CH-{z4W^5FF5%TlQ3h97ڝr5ȗwpEE˖o[(0$Zyb|#I.ӕo !M+9͇{D W,oٝ/o"8GO%MaN KQ^ߜBPr>Z]E5rxϹ[E.3;?pivǫ?(9zbGҲ.&Hw2d62 'h Icmuy8iصYc I{d\xlRr E VXjY9| FbR!¡\?l0_涝q+1d /8$gs}MU:ئ?{/8v61=Tԡao&~cNW(n͑q5 u$f%8J=ez0qVĘR㵸NN[aCDUWLVgL33%6<څ'5^Xs|Fs@r eM$zBc 2{Rl2}BTJ"CA/DƎ+{Gl(l qwЕ8\'NW;z)\h;H刳aܰ6z;A/ Nld~ 鹿g&p@-Kzŝgˠ!OI‹ Q`>HnA4DgIPۦy nP={Nj?`lÆA ǣK_VՙHf2z>rD( K96*LRBy:Ga5$O|-X+0Al1TJh8̙8MlB2p.L-]VpAfnLlW8[D9HKL-Cd`Mqk߷D(m 0Ny8I.lH|૸ϘoNMAV"o5g(`CbWt%FJ3'-^ -Gy".-%Rs/6"v}jsk;LJ.[,*)]T]|ȓ i8|+Ss`s`ccU5R3[XRG\y?@[ LH@躷lsS;L]D8(;R@e٬ޠ3E8qGuXOBS:ԜfIc 1މҢZoC0R3pTM>q퉓Z.z)pH"f0&H^' 2o@P~[BPvIh=@"'X)uMln5V1Ci{Z$s|?<6hטSjXLC۳_`t^{Q*(Ba? ނZG;vSIj:5X\#&k_$ ".k$x *֩/ngjn6߷Ԉg `M[TfxZE5BCGaeAvSLrM/p:sXO#at2Gpde,jvCiۇwjԦUd%}5"C|Dk QAc o+A~hFullNK)@i~y6䘡»h]AC"2h]%pp8YpOL]Z8W->D?$no&5ꯏn#\o"؈k6Ʌ?1ޱ5Bq)?#55ٳ]0yQLxo sx>.6'0 N%- pݍmeՏMDYԍ*#oG3*P Fʩ$gtОTaCXlM\D3VjQ*Pdgv3+xk_h ʓr蓹rS+v&ŐP|X}YZϤ][$0,ƽKX'S8 &`l#'?deAEEԘl{h,yV',cffJW$m^*׋xRL{ҴFQ(S^y$  ?NrKn*Ϧo\ZC U >9Zk1/՗j)lľˑ˱ %,)71GL{B{\[3$uB|XDzу[NNTi`N3  ε}O6@ rZ[D &CRXwQr T%] ֬O"QuѴ_l9LB0,aϷ` g$ : u,OgAdlc#|RaL<@ܩ_\{AYx.v^ߒߵFQ">Bd@oSq 2V˯MGeGi_RY|yTW0~y_7Y,7%@UGOttTJ.nYa].De2++TP"sl qZ'j5 \hlEIᴬRQK*1+B;=~"3 lo.É?jzp\wͿX!D{ұ%54ATz޵cU4Om*<͊.%$[7 Bj:i"*F7{Y-P)D%ʍ7rHD,~L=*@:1˰)Rތ'LWclꮺs Ky-2x /?},[^IPܴnO+ ּ!$}ۢ7҉2nWy|nStp% r)ɵ(7MX{4h3 ߀* km( K#R3 *"A. 5YEvfXwU VA#!4/H%I!4GPF 謱^P $1U(GrǏgn }CH)Մ^:z\ lL?G=ډf6n$5E-6`\)X&.{*B;_5V9.vnΖ,<7a࢚f598bsJgu%{A~|ے 3~Lj4i'Ec ޗG'/)/ k۪ |)$I0/_2pzޮ6ɾj}3^C ѸVf?|t`bD{0LQ%`E~,If-` 'q]||UH%9*罣N[6͹ܷldlzD5Yb^h1rE\^k|ev&٣]4Nq7 oX|8=dN@!X8NH6F6?ENLsH(Uwjb>xVEm49d&_ۆy endstream endobj 137 0 obj 99403 endobj 138 0 obj <> endobj 139 0 obj <> endobj 140 0 obj <> stream xsx%o=Ƕ'۶mVVǶq:mulN:gg^Nս־Wj:EAJ/l`pwgf`(Xٙы8ؚ , Lvf6pjVUGtvr?:]rbƮw䍝,fnfvf :8$\@+ߧjuMZZF&Ā.V!u:8]ya[[+S h- aek*[fG1SS11-]]yRP . @Wƿfvtf1+g_<߻acVfH܀bYo `cbgae@SK.H`c{3_oGG b:}cfYLVpk-oleb!ϿF퀀5i7=3'+ O_dl 2_ ǵvje gSo,/h)_JrZswQ*5m2]N#loa_\$N;4ӗ1\->XM9>[#WJ҄@.<8hXF_J ^j6P_3ᛆ=qyZwȬ]Ҁ[1TӈdFyW&=oZEK l̶x{ruDүolb.)qdCV%;27<9^ʑ^&aՌ+嵱J! )tϧ{,C{bd[G ΟsdҰrs mNJhpK^3T,3"Yd[0~Pw9D~˨ RN na#?5'j4)Z|E5YfŖNu\Wveb}C"L]ŲPA.X93'X.R͞Z*;ˤ }.2͗.loME#̚ -jZ\{'8N?mYlCtF `ˤu103W ɁX"3!~|>yDwu߲8v3EEJ H%li3PKxSkmǨt'M |>]evBjU QxHK$+ _hN|bXJז274CG7}* >$6&9J I}/!J+0pޟfN Qy)d#/<c ?`=+K]G>e1Ee= jO{bvr7u:$CG2] ;9?b 2dKJ wP\Ǔ_.S~E7d Ai>G8#, 'p0uTVۻj̲ov!U,4PefAM&֑o.xl )JÁKPkEҼsXqt&n&r ֝&I(FQcP$2 6c.,%[zz3{ Zͥ;kNzO:,/quQyv%b}3Jl="6;BWg=?;x,zBbg\S>|C/%{?+9μI$/7,=6tb<N2ż5TZxU'^퇉R{`L?Q9NZBGv;r2qEmx],^~X}3>r@^BY(遐`g3|lÃ6r6rښL`ܗgI:)%,qQɞFK&n9?6l\BZ tt2WpV EmWzru(mk l؜q6]ydYY=\_-Ro)+G0l~牄jdAϥ*o}N8}-i*yŠ昐{ +;ABMc#zϵtEtxktͫgF.Ց=f5UR^z4 ?XLD0S>*흸bzK(Kzu=qp.NP$[|.ɏcԷ1aTneGQ|$/6R5A [@$6ж 5/ fsUu8̀d{!9B3䛒U38b3' Hq&ִrdm3fb. C T*x?(4&a:4*]CeqB'OgxQe8"B!i3>/LYr[FJ@ͦIwx6M _b,B~Pjɬ̐x~g5,RBҹ={zj>ӈ&_ʧϓ I(%`-5'Fu`` K={ ofm[7٭Hh 0ŵ@xqz3s_C" v,H e'j4\vxI!& Y S4LfCN|L8ot{M[9\1"o8*ݲR W%nvR)dî_oc75^mi uQ՞S߰Xܴ~^6Rg&mC9EЇ]J؀Vէn))[.L5\d+z K m?6mjhe\eMgM\./ ez7P S+ iA$?Kݣ*3hDFE=%8nt/u,i Ԏ?rף{k.wF/. I}fsqZh-1 X}F Ş@ e䨇(WZtqԛ&`G冿ʉVD5eXCx(z7$(7~]BDq^+YQv'19&;A.f䷇f3D8T$ y 7[zk,lt,7?D* C@FmE;/=qvbQ%X=QDS`6 \k",?IfPB%xaJ=<(5R;.X <;PAfk2B%e-pm|"C;^O=  es6BͪqUlP9\4󍽀LecScp cs̟u.84u{e}G߻FZNI_k)pk|NGn|JcH'[P/=q;@L҈ͭg\EETziaohVH^IÖ>aX 7hy$f!~aGTg zQQ{_Zmk`BMR{oh5_p/7 }Nv(may! ҝFD}%^z,,64+Nl#@p{D% ;e~O1Ӝd.}#3!2.,$OC]fe:9 g55g(& ;+gPĔUNc7xDsxEib8ߚu;y]֌M1(`x|Ów^BhB m,_!*TB%0ZxPDyqVDKR*IRGr֐a^rh)ZQ[ƻ4J IxLV6>ne[`cd#^#xha1B8@ _rt`X @Ԑ|jƖ)7*nL28}Ɣ_^>#@ p=,d~=Z͍`Kж~3 2W =Q0&[OBGvS3Ak##Q<,?7aϔǏt[/qܷOv/70r;?;@"sk kfm 2ɐChg\@#I0u7>'wU}ߛF;!Q,մ|BlL`ž{p`K6pY1X"'=3P-Tq@BVN7HM;+G؅l$aX,e9|rEHJk0$ ѿ|NYc|,Y SQ80>3(,VP!\HUƾia|ٗmj\͜d ߛN͡q{Fʟ1O92RD"/y" RgTE\LB !'.w7?c%:ubYC0OE{3:bD:SB3"=,{'lM0W3˿}`EwfތfHư8CKW+VpV^u((nWhr?>W<1hiLn^v<{P\Uݫ"ŧ S ygi٩-=ysR&mK}Oy~+J /ywr\6F^k>w}3hJr_g੤.DĊջ!=IɮRh`A@'[)9&Y}kdyM ̤ҟN۶Qa poXzD3p+]aݍ`AW^K!/xZث>z{=(|mm6nY7zk C=s՟GF+fEd2׳ӗ.^M:BkUE*f˼rW_;HHL+OXS=b)vl"`I1`M9<}۝q)k. y=4SdלqI]8txWBwaeL#R@H5SB8 `h syvE`gSiR6%]i Gym6e(C7Iϻ_/5kY=NNXs'D!Exu kͳcQgZxw Fpjmp]-bۚYeeۤ~<埐Ƥ] .f4+' xkSóh`s띡ܵy"Ol_1a!`|岝V^z![_AVq0[kJȿg _b4FآUP+*URVo%w.Sgh?<ChaJtoMo4D e_LJr%֦eR]q_zGt:7WMYhlНC/mGx+r.mob&Ӗw׸[:pn -m_mpU[oSUs°TvxZ"9a,]ݯ !$a(L=[w9x])# $|Oa.pG_O"@<;QtfYQ7.D5D4vAwPSj3fIw̫3Sr1~Ԛϛ 5v(\T#qqBChU\%^ߨ.I+;7|w(Ӓ4}Mlx5-y |L=#6)F6h41728b|"m=y`սZvє3CYwwgS*l\;d?Rb+*B00157pI fqB撝;&_qQw.65Z+a![–BB8]DC):?p/jN~yv5Zx\ra6ɧ2`$Z);瞧`L~`܃749Кޣ$<~4+ 1\DOeP >; Pw'tPy)$ʚێoϨtk_n{0M3Ymc<ud*ꁪ})JԄ:̬|Hz;?&ޙئ8 )+FH KIo+͎yf93VX6s?#Q'| lqӏà{9- 3gx{ܛI%PeC6Z#e/sؑ4C}Xl=Hȧ5m8Z^Z%U5NJ< sVfk4^BOËϋ} R Po!u4ɟ݀gTj:U< LLoㆦؽ\dUvUmׂظvv:BL)`wtB4{ U؉#s5q!'s7 #ղX4C@WYgv \̐cD\po WTFי o<*=1B[Hi.ɮ+O:BQFR|3D^?sCDI?_ yrHLp7c8І=EOK.ҏCfBt4qwG.G*=ӱQn]ԽSfw>z1ش~xUO-ᅐQxW\+;GEiOcinM6=$G_{ң7  '7FmVyɳga_OO8_x:UH>Yy(QdzƏ[R 2_…Uq(~D%-r,dS7F6J:VjU"6߈yxm|}+~#U9WրSضxJǫlP %τBc](m 9F-N]]iFѮ#,]1|kpWywx: o AM+1/,M ]1.hsTڃ_W$_P_CfH~ew<<wXta^e%-o` b(o"G=*}7}:#(94MR0(xq G>!! e';O_GMP[Idsg{AH<=@~, )&Kr*~KQNܐd\b5 B#zvq:)]Ɛ3'0̡)hbIv!F5N+KWvdiͦ%ԣZFL %#%Ć{,*x-uEBøDy 20bgnwpem>->8UuⱮm&rngxPQ|O#/P2,W;ƵSצT xn(=1GQ-1$aȯCb ~߳)I#*Qcu x4ݎxHK,oyhzYsLQxie—V,O4«';p6Ui/+1UUV3QVqM^81 ´5\Ma _%DrVDytB7ȝ!z?p~˾E'}K"o)|F%Z'=h"OM}[=܀DQǕc9T>lHPD̝A x'd w +\A&9U{ᖜz3 KC 4/Q q -RaK586 /h.{}uLEY ^`4K*,FbƮ?<,'a?}}Hx$JcgyP``0)ܪl_VvkWtInzP+S+ ,'Qod`p'(1/ :ũ4 >!|Y :[L@:O4+(%* B3uןYAUJ77=G0h^=C 1sj4H[d3gz4w8};!Z] baJv9IvZs2"~$Vufٸp {qbY\}=3o4TUt=rʬv?%r$X$38dq)bxHhYrcf^uBYB'j5Ʉ3›2FHdwפ`N@RDz^I,\Ed]%p_i 3/ gY)JzWjl,'qxȫ OO |u BWPpc&bכ*N\UwC݌[ys؅6F}9,QOaie{|0X̣Ә:W/,s2aL74-o 8֞Bf|?$ÿ {z_*70d4B|E-e-ߩԮǎMq-,fមhߠH4XX3^Ĕ;̚'|Z8`lTLPϹల#5G(r7ȟlz =VQ{E@E l`!`W@ëԌX]Rzl3cz^( YŀK^ f}Vx2˥_Z?\a~.?D+p.R ETB%IPBl3{P#5x *1I4TWk<JD5k6#<+j馃7fzxzD2yu]௎++jH%w`fF"?u^@a\ʈ~/kOt1cW /3!@>²xG@^8w(M 29+w7$Amѱ0kL1#^t*GXA+k͕a}eebW9LWcM^ը{4E7?렲EŤ"G}v =TĊ4fQ|iӂA`*Rst,<":*.*Mlᣥ;>8O>jL9" E>qrαP"FcfJWS2)[ <ٷHqo|W?D)Gvb,%M&K$}~b B(U4hE*U3K&igR)o omIn!b\FxxE!ǔ =_x#4X}mnB&WLn3SKC޿TiORm? 96lf8[Vҵ:[櫾zJ/n06J͟NWWJ6)aEgR[2K , wSH,D 6dCfr\j%U ^ 3##~ 'qAV4:g-}wVxyl8Yr.N끗qޠF l<`2##Zϱ0Ѯ3dx3Y%O3>7n`] c eirBy)d}\G+C 3/R0Ly5Kc RGeIW ۡxH'b5c1)z:N1W)y;G)64g{:Q\g?"W\m8~>i5~ eAL%ea[NGn45ݓ9 x⢊|}P`i >TMLX6fTBY߀ ,I/D @;ߛԐ| )#*F Q,]Im(R.ozK[kuη(O@zؒVk]Ai ѪaҠQ MyJ )ZyU)lj5q̇s[x) *}V,3Itsi }"13';C^Zy|h3g*ɱ`-ob^yϼTr 8 ,RGѵu'Ͷ2\]kRa] odi< QO͹acFȴ10Z-rt%Ly0Dt8ˣRU?j1o, ڹ i_^N<3ܯ^H@ QJ֍gͽEe@x}%/AyBKq]a0Ms<2-sn%KK^2_nKa3}d`ΛbvyfH9S xQs\w>< ӓ5O sv]?ʲtOz[ adZ#T)V-aBsOTMv*u3CB3Pd<̧ yV@e[L $)m/MAbjnHe^ib'?/SQɦptOB%3PڋPb ~ 2s\Տ;3SYAHLj[9;LM3 .bETlT% >ݏ]#0BA-B8a[ 2pu9f?deuy2`}R>$=qPp[EܘaG;)Ŗ^$no*'r)({n# av9#q$ ת}Nj$ZE5!"bv[[2yD16,0k /ubApemmx1\e@% BhoKα?z&Dhɖ9΢AU&u1]ܱHa7Fϵz΋՘dZx0cY :#/&pĈ\qY|OP,C r?e\) 缋F@&4 U%*W-Q{XCb}Y-<)XWqm(W-.YK_Cp' B |X"! ^ 7;T3 "X.?g3Dlϣ6Io)fgs~v顕J(C+Xa.!=z%>[HzzH^4Dl{?e0-sK` El1by8#R=cG{s`Sڃ}-r|rmAf$S,,) Xvu$6pHSۢ߁ZyV|֊r_5j/g)&2\h_Xnu/-r Û[buKj 4>P}$ (pv/vr0'ʬ]G51r~ 0Ly U383e'.`uuuY!§uFC!w,AԸIV.9oxI%9T4 M 4s}3*ȉ 9b$cOG`r:ɳܛa5NfsyiTl\ ^_řLDDk?ͦz5OG02s9'ecG:=aЗvgVUww>^u]6cB VB89=&*fra8s(pb^s?HN@Jy`(QRN.b~ɍ<rHW'j5Ǿz#:`"U[krRyV (B>^u#HKҮ?FKӪR-|M(@.M6T\G?yiH]uV|Lf d=Q,?D'Yv׼W6'z >G<\a)*q"tC|Kܨn.LХksBq{=*;b-Lc_%ZwQI HcA9+P9Ċ6\y x2pOy_^f|.W'1Frr9,K4o DF$V ?a%|a<؍GCD|aIt"Z^es/8 T'<8O1|sf @u/ z/RK ҋe4ޫ)%;1+vWwtK^^%\Jt5g}R%ArY!uYY+渦Wg< bӸ&gq6LDj}6թ'=YͳOB-' &L78A{LwTKCPyi;N [™;E c8˷r]0j{g$X\vʎ|tID‹o&fdJ1)5X˳N̚_Cuz)N$A#֝A;}>A t>6?X 0=[ I`+m4V~mv5C5_otrx(p(WH ?VYU@."qC!iew䭚'QJ:z[l!LQOPF]}}`C.D V[M_Iq _H;Do^w7D=Qj|JJ9{37os6NJDxEj0nLǔ)yE&c~PyW |wjtNG+ JMjj/I]V{ pܸaIx ݱxDk_j8i*B(\(7FG^~ ,Ō6ql)Lu'y.C]f^Ie녔gD}ݛCz9m(!Pd;yzD|QEtfՄ5( mwL' U=-wAT17`Y)]'@-ZDZ4u8(=V|Yip>{8zSHsC/om.mţxcȵyFmZ2]0O&50ʧK`,ăyRe`9NkC: 7ܙAz^C-h$fd #Ӝ |nEţϵA{i˶׭l Qw}<2-Ik;!lW#qQ>Sa#QeoD\ af6!>Ȓ6c|[R"&(H[zqIɞ$!hk+JT &kmfE29l?[8vcZ_Z)sNd<#U,&"d:ĭzed <m(Ӝ-_{ 7,r]lsG) ?z=XbTCzX N ӷ&o)$uշӆKtڷפR"Ws(%ɥ& c&ʅ9_BNS2бsK¬3|NTŽLo7,<^i0tt ] (3>L?x&%X`X&8_y93km/h\^X+0I'mV87Ft7un8-T- 1fN6 B9E@14|%**CVbYz$4 [ 217.EkJ)ěPD„E7U'0'=NZDc`mEdWycIHi$]`S[s6a2(3}.ԅ^Y|!z-]OY0c ^Z(#[XP-,5Zy]jh9Erw lLs<o@${VCJԊ?ǷI$r4_J팱JŲ _0][Ϗ'v5ZL\Ch1^;d+=6Q|ۚxe-UY|ۣ[i3Ћ0C[A%[[.HFV_ZoW T9ܖCPSR4ĿM]kPt ^S%8~(MrARhvm* Tk]/ @/֞ME-Bjshᑈ*$=қyt}5dA,îr%Mpʃ@"fo@ZUC!JȲhv|2ޔE7UT ZHxܡDqeL͊5wzlN+V> rZ([t#[b[?qw l (=Yh,Ds,=6vʘm.{KG54--G)r,0~֐^"$rEs9k݊(i P*q}hgvG30DK g|ؑbaBDj,Z:‡N? ,D04U׈tU1*'48J70769X~EE5dT[ǥ9WaCdGbGQ (8l^ ){a3v<O ")}8vtN_Oq6l -7{q&t%<>,r5|gяWޣ!#0]M|q{炆sHv~ `=o*TvF#<0ݨzw+E-l>]2+ *#2/j.pT L>ln#jpW&'*&yW]~j1+Gh18[-e̴uB58jKJ$nhzSe%un# Nu=ռS#ܴU޼l>[ټ,W\֍8[o[8Lp(uICvk*o-vCb:[NӱG_"H겮\Xr&,&N!E$,Mq5VYj,5&i{žGɬiXEG,FW͙NQ,IE7ߍEJ/b{gн& \^ޤ+ /Xa:8B=u3Ё:86zq"~'Ja#T#+U-2> vn"K7Ϙ?Ƹ4c \ÈJ`5ieY8!g@Qc ԽbO_a_TfަbE9BH?0.뷂lJt+C-9k9,+>T[ɸlti_Ppc|35w, WW&n;IQ%¿ySQ.+O(kXPeZ8wVmBlCn@ A}BC x[j*F>kzTPͫod POD_aN GuzBvٸcZ?WPQcyW,a7+@#ЙY<T,\V'\mĬs'Ч$ +YxU#LoY i!ӽ\zsDR82dd#^L%ڗmcV!c rטda-L;u!'}'PU~,k/Ꮯ=y\C*r0B3w$ Q3oVs:лī t2J;ndrjU$B TUMa&f،hK7^ mK`N "@)#{JzK9} ɸdy,no>\?꜎(Y{2å)9X S8oq-8yU7ygG\JhGKxHB?tf5`3yceО,|RG a2| D r#Xxk7ELQ+wY8\GG-ͥSq6t#Ev# 씩>3z+~,P#>N1k{ G͋\ a.8[|V^3xǙѼ_Bh1t&IA.t]PR-au&4ޚLk޼h(iΠ8 2C #  p !QIC6_zJ!'uJ 6;ըܲ6ՇT,74R͝%Wߕ+5+d wE϶Vh&'e(<>JhAWV"yZ+S|]z}XF+]e2#]-¨}੊5v⏔jMɁz!xhhQXP,V/M/G`橗TέwR&Yq8J-M[6" 84Z{鞾Ӆk1bBGtܴ+ʛP vzZ%_]50}^RQؑs?`GeМxU(E,ec4vwVZLMjy Ntٻ`fX 1ns:̔,sQ4.sKY*v;k2߰R<#^b̴T\\+{2|[HA2ocnsN իY>ɻOe "сBENuiI[%oly+O4jFGU߸DHpZqog:Ctړ~a|fjhw(0/3Ș$K?r݄ivv#4].A07J@ʧ baJJH4 u1Zn|a */!0;t.#!SN^i7Lg 9YwoI[#K){}ӭ^69 yz0|Gj1j __yM[/?BX-!evs eܜE[׆?*''7vD>Vy(h|WXVnȬL{?v.!U73qMF_`UASArs,!&[pu%րe8o0HY^gDthzƦ:Cy뇶&j¦_tC KQ P)l5Uŭ54$!l UI 5R)!o.0L4>~eGYY2E$PRXw9JjEGIc ^St T~ oA{LwHœ? +uChMFri>'4@zxqdFa&/tǿ#`1jY-t O=I'deS9},;x}.nCV%޾{)F8ξ-A#ɨh%]t4|x6Ga*ZzC%-Q9Θ͌= :p_ɉZ3OIL1-W| ]uz1);QzCAa7hD]eT(I"#Bpg-%l<5HwHIJ?+t]BIQٷ(-J @tjx0u-,O0ZΕHt@^ciگLk*ugZH46~(~b!6@s!]LG[W30O8x NSpvnzЁnFN6A\݊"O㥞"{ܴ# L)ݭ5Gtʯ@6!Tlc% -DWk g"nvoX xe#QVTei<< P(<]Xݻ i+645echR X h*~=㆕0$2ߤ$+Y[GXTEzb^s;tU?:Atb l|/WBM$"y&ar@U&0!O_"/Hmfav tW93|# UL4vͬB\{SY>C>~g 9Ix6cy17b}+ZkXdԍMEHZHeW ILjoҴNXG14wvx-# K4rͬϸM.cbl@%tg$j pt,ex}"oWˣ h?܍boˆ 4y/y5F,*a/ S)'5h{eَj[,v}t0ʇh?:4_`6WYv N/NP \S%fr#q̑|[V-s 9O$2{=:bG>9}{FQ\|{ "\g#|fCgF~Qq>6ŧJ )uM'`~d^Ēcoh#+ ;%&/>~Kϻ˂I bݩ 6SGc,KNWo^n (UQ_@ݨs2!9̰={2=&i+|2wzQ4i_;j_f%X4| Ȉ? AC$ @nGB _Re iF:x\=laDԞntt1nf~֝xZrM43Fs.* c1|o g?^ Ia;E gAvo7bZUY'R#Ͼ=8uH=RT ֒mQ g$"[1vJՐ6lNi9lI!\?*-~So$Mz/B/!iizI$ݥuKZ&I 0)U;Ts Vqz+sOP6"0ߛCƎ/nK<3 &@j1pfɾcxcCY3Ж0J2\z!FNV$%m0F_$[o6Kl0-w sk:*xbnEtf5G,ŀtsצL΃['}0h\ZSW-n=mPCǏuTyimzmUm*6( #8y =fe30ݸ!*EC#EtE Qr1;N*3W[`d-b)`V#Y_rM3.x„ ٰY4d2lylʷ~ 67Z )(7d7+,)Rh@Z@׆حiD"Ů I)Ô١7@{LV1wUԾlIɷ]wGN)Q8]3cVGC-ZYE Xp7Uns#r_PuܝziK^FO>9$af\B&C6*d;_oCЂYi#'&6Cqr~)b?Ty.Ɵ䩾 Kl4UNN^l\>>IR[|:W)y/;pYo@;JfM[2n1ύ~ LM9lY`RC`Sx8ݏSD㊏  B[_I l4{P6tHF8 fnW?k#- GQ@S!]a#zh=ȉ2[JMp+:wFkX ¦ p7ܞ 'e_ڷ1YMw}4S4 y"OTYLqpmtĬށ$ߟT}@1]3^!_0<z BW pTy=o Zo#Co,|ªp.@1 _txlIϳq5&D"MXFb^-2teP;_'WQ4Yql,2n =­z&-}6j+_OK@4kcIE5 n"Z p)r#8h' |r | NxFvXj-0fp²: wz^fo3~4@G:qz^\zf}yV].eiHOE-VUNS3sBeoBq;dN=cϾ`?dYݵCf"6Fgk4oI7<^W*W8 /3qOmܗ;YG =kF1\`&{TAFŜcpEpF.,.nxn}փɅ0ۗ#k Au Z.y`}ebQPͲ~ +Zġv0*ʹγ+g,_ƕ+ W$j1 3~ @6>pci# @aƢ[w8ROņE[h#eIc.e5mmZ@5$'NΤdNю]A6.2MCtɋ#vft~O{ ϠsoU060rДls*QVTz<;Ҍݴ߂,!)TtC10!q@:,):%cO:x%WMW,>p;b'mPm%5DA3}QnkU[1g D?7ĩkpcנ@ٛn~1'͏?rW,45<*4*MD97ŏ2u@ye,)35b SW0<"`-yWLօ$"ZMy砱{+d_ (Q,]%''#s(|J݁֘ oܑv˦o]lK>>xX8&1kcYA c >P*΋=!pL(о{qx;I?֩Jw? rZZ[Qy ҥU"m~BxL 8}>m)ys %g W5乪c]i}'.Ql8>HU Qn컣W5 H#$/(>ّUZ=E؊vhb7uuDG1gu\A@̶НT?K@䦁^b&sPJR[ᓘ=@rn&,G17%nJU+=B'P]Jtt^ `\(}Eeskm3/^cĖai =enEntٖJt}a[sFEɅDW2L;諹p?CgMr"EkiW:]ʱYK *VQ0G>\n ‚ڝ,Z.q*TW!SO-N*BThx Dǔ?zc[!mWB__۫/NDGml3vI93ʘѱFRtuc2 .q83Jrzxl5Ș#hşELV 9J5a vMo^l`ЇsKƁ8zێ_^]_ӂ<G&k }^]|t$]q˞~aie_Ě)UZpVfE6qIc%y$2#(p]wa&}&꧉,현+*H@[S 3̚U3f~h9VD(``7U.U.vC'%J-'l8T,tnXfnRY^8T실үV8T"Q<5W_ C_hnTԁXSB}{,kV9*\v#y.CdܶWL.Iw֨Khybۙ)Zwڄ=+m*~%V=tU~clGc)NFh?WG=%ׇr5'Z.6~'eҥE򍽊ZVJ 2rG%uHjJk2cp-^)&,W@c*ҏpahq`\$qD$f8V+pүW9VἩ@J>JBQ^E>3p J{HFtٱh ;UW*F`?w .>6x>?ꗎqgj2mEAֲz%9<>2{(ͲR"=g?S wDY䑀^9BGٔۊm$UX/{J[ĞT1]N6=F-F[_wsba4T`dzAbvCbjz3pҰNkoRw_ۃ`eu58{vrЩk.5F9TdĈb tXv=g+7ua2}N]PW.; 89CnQ5 ߩQ5nqފu<6p:쉝#{6=Vc.z}êZD#M˃ot%*yµAGd yJyПV Il=ԄޞN)9`n/ߴp}.X ZiQhًQ܀* F{ωxߚ93ucl(7و@'VhoA*dj 2wx( $&MUy(s$f;3WR㱒G`f#z .t%h-ŭK4cK&f"C+o fk͘Q{Ǣ{-.Âs`kN[}1{u`kse#)H \~,)jcING(G,Hperj6fJ=C,jg?lq{VHd#+XG@p2MZ>R3_U7|n5YYB%eᩴrW< d'g-aCr&q,^:f#PMd7+0h[?עL.#ps ,<>Z(^C0Q,TT&f/hP >*eY?Bx7zB JGTY"wѦD:pQPi( =Jp4Y>+7T d[,V{;Qt$9yqz. E%?OYI쒥ف{N`8Y ]x%0 [Ar7MN'WHʈ'Xr lsVy{aN3R0{VP~WCkfqӬ{2E,1 cCe/ҖЊ@N# wZ6j+m>!$E 0DqEw]i붾Am%2USLY]Ax*;.0Qw~iu]vs7и|f6z&B^Y{;Wξ E8ˉ0g߬^ ҉1mGttk Qa<;(H-aN3Q@çՑ2n=rh]jy/?laůˠ)NfhP d f=)^ЏcRR.h:mO{M~ψ]i˻) d^U#W=PNYJyI.0Y,j4(L͇<,3}ϒ_L9]2`D³K?&}mF;EXX -!F՞$^=( -RΞ!~iƎ53J&P} E:7XpYn2x 4cKp:{㧋ɚR_(>\d4 Pcs)!{w4@Q;2Icl&ڦӮ%d? Jd3` v>a;M#wSK#8[CK؎Q\3@'̮Q- p?ܬr'Tm|D0٧wnگݩ |m=>$Ɵ+uʼqm K9W_Oj0z-hkm7=wٞr1MŻ2ŢXǨ)EY Psyg\ -qD<&Ҭ@n"qq^#R]E__ȹIf- )o- *,j(M,˵鯧~*w#O3&jL"fËQy]iN`q|U<'*n$I/ڃ+)0jN#NB}^(q'!|qp,J9(W{=KĽ >n]D"Q+9-nFaxe߃Њ'vz& QfI&N3ЕO }<'W%u9AQ-_\3a {#PDij9TzmWY6Dí%4/~1=&XXr@R·׿r*ZW=D 0r$ z[05'*`$-AOpZk<^7`fpZ+23Li`C '/TWB%ܾR)цm0miee )n9[);Bkk D#M{y򺃱%4(7|_?@_]l^#J`pgr#""&/|0$nOJ7 I{c7pŔw!/k~έ,c&WFI%d |GܔVm\Y[ Jüfݟ~g <gt'kչgΣ H0 i|9 ^PiC1(s;Rfy ǐ} *mS<t:)କs$r&f Д6 6q;H3cPܕ>.S;C֚^ďѳ?%ݪ@Wp]w.!ls+׬RDQnj!Ldm}8ue-X/:ȃ}aE E~vhD~=I5)mD|hcyNu*>- PoYë$y"pQg(B:~3^>Ip5g$VXj~Ab4Ko\}_k\ޤ+{b(IĊ&d>@ce !c%ns*>tml{l3DGj 8gԣ[0ԑ*,z>’kū~'i̖ky|Ѵ xEN f( XY`ƫn\Wqκg~CM~K\02EłO˻AԈ"ywD2quR; HIfuh\ €{< rF(fYg;ٞ:A۷Q ֟|Y=K#k@wrMAqߺu%0}np-O=AKJ*C灬h;2j}oi(bҖl,q{ъȮ8tX]BU>{/wvy˯x L~s_]d>`D@\)SzW~=ń}ׇoؠ?qmm'j 9)qJdGzv W S.UKM&NbBom?SpjCB;8'F3  jRl mjhi`XO'Qdi= k4+h3gZLli;:˟[&B{=~ #*??v]T ڗט@DjBQfA-d:V7f3T98Sauُ^iai9on|Xzˏo$lmi, NF[8g _Q(Pe{/R#;2E`p2t&.M# XU"ST>aֈ[+T#p/^;7! 74;3w]˔_(;p!{>HɿdPݐuiy{e*ڊFTZ̢0=)$I<m°/ZOcfsՊmZ kB+N#!, :z@}~d9 bLRC|W1MMĨW$NW9Ϧ 3tSE/P_խs0 +שrŹ 7 {l 5\hN53 3"("%6u'lI f#a|) '-j= Ïڏ_\?\HF3vc*ck ,(tl ^u0>g6pMU٨6%qjrU=Q~t[ՃǯZ?pۭՖelUOзMvw p7Mxqfxf#x|jQd`+H3g,)x0\Ujz$N,Wш, Ux5;xKj=?T,řuxJ'W"oSAaDey2V 3{6Bej$D3fc~Xcf9;" {P"_rb|Q ?C[f{d?$}$ L *Fqn,FO#of4ΞBMɍ?- 6Oj߻ *ד1dHºX}P%V50X9'л%nZ"qU!a""!M/ο3^:eRw%-W߭bM'}l9uYjˆR&B#Ϥ$4>%[%f@7| N n\ჾP/Ÿ_WSxL삨=A\;\؄%c@u3ť06nY'K @7{ʳq?Lg6nPKuvvq' B8]EaZm(ya:w njzVOYU{5iaksυNك btko2O y1XTGi>Rya3ACV?~JqehFs1\O.y=-iɫi<{#t{T#hi15$䒼[4q :(ޞâtL^ZXz+`a"((="hj1>l-]GvH?  cKtA[ʤ"Ɋ˾o ֒hA߷8(Qckbh{yt\c3G~ U{dLKB!v)N |Zh74Y췔Z M:hTfwLRo["(ADbw-0LJ'N`p?[# LQ:r[5 \Ͱ6D~Mɵ$ɮS5/V|~̻jǫE9 O%n$ Z?l L2YĹ%gpQ*׈hC^-QɌK9.Mǯe?׾帴ڽ:G[qWNݡg_fRϬ4@*7 >](Y,==A&jDa.6JzF=o_Qyg wBV\H{ t"Nc? M[ZK?>A6GV6> zSd;b3Z-vb~ߨk%|3wd/ &>yT}m#8{ Bޮz z@z[q #b-V2eXvVn:=*R , U$ϝ.# Ǻ(cllGZMj}"`-М#`xlU_@e2L5EcB)NBFy{9ljRics<5h+fB`(~d+1G0C x)客R 1(7Y] S?H}ȯ̈v~q}~QVxk43Im)XHxpktCš.]ibR%|40ڨ_3`}+Q:*?kv5y ty<  D8|A(˃5*Vh|0&8X]XAaA?|ztK.| ^|aXH#B.:XloW ~ߙ+9!J{Tr OD7vTiwpL"{蘇`3 5/OO J5(٠B~C8Z#z^LR_wQCI;y'* XW= J ZsN?n@5gʞtɍ h!u*Di斉9  +V&G_a>OpB8j*OdR \Xlت:/ZnB0@) . .dᔦ.L;Cdo'aByf[?lv ^‹?20@` 2 L'޲V\H {r4;IFlɜY^$^]EVDABחti al?i〹Hu2&`G]#tul(fFp;#lx(q;G->@xg+ui)FU ۇ4Z,U[Ë.][5pP`D5yRlq|29R2kӶ/1!19b )]4r 8L)cXap^r%Xl.ɡ8~:L w_z9~ߛh5\ 3Fu~|1`wHĄk\w̒Lrmi}԰F"sRYs /. )i_/8e6EJ#JM* m|൏FFM >5;y^@ﯓ0ke,cڒˎ*iUJ6QMZ9߼q9,ӏ4cam6iZb%7ÂygLMe + a.KG[&i\]iN+:޷*_. ]6V/c%ɍ:;SGi4(;aNG16ֹ{RgG[qIU NAPH>Lu`#i@j.)D0!θ p][lIRjBB*RDWƔ3u"79oEk-܊:gTigQYDXM+IMTHf :s]OHE$U _]QoE> 7liv#XFWb7jXbDx`E *Q;XE%mdd(`Bԁ(5>I75+bɾ-m;ȟKAO?b$< =x$6gdE,67 tCSr~^daxVnz"jNPǁb64=,=ypDHG[[ZeU@7\ޝ}èӴU[A0uh؝n1'TՎf~y=,QmfG $#lʭΏٲkHA "j:$]Ha|k?Q錵#btI0f\^[aAW43H 9^ȣylj4s"Ccg ? MrV#HxV=Lǵ`. '٨=6*Xk^eu5ڣGHѻEOc@c#8pnG fvZӶxL,Tcn|cЉ%/b%j45j2ˌ{U{0D&]0it̿8vȫeF+sEݣK&ъS 5̛ݻ~buVnR_/?h-04~R"!ņ'+uΚQv,U[#T(`+#zdj ۦ!q7k džyg3-F_[3l6=  0GLO/=~PEh(@ ={"m¨__ .a%rOHĘ|;rqgI 9H4~[c˜ǡ4&j6+aYvF̉FNjtx,DiuP x8lz9W,ehs h#r}B!6;bBsE}*#}̰ Rs`F7nΞFU)|L?6 G34֢HF[ldr n5^—R$^ yLX7-P΄)[D^oWMd;м7XA5"h]Qq1ٚpZ8*YU"$< 0l.J49jۗ8 '>LwAܕק2czJڙMG}>?')~vgyvw,[\SFyI#uzQq*"lbk_i|R楸_Hg ShbjO1xp-SӔ5l [D~ |Pd,͵w`T|XΛ-2ybsMx$2,d}%iU=,2\ "d϶I?q?ʋy >i*Wu@yW\]fFsY5n쵲IED8Ѡ`T~Q8ݚTLX\ Ìa\z#>՚U_>jEU^lҰ9 2p/1[I̪zEYp<Ϻk,1@4T+oH|sB!U,ҿGuE$~/aCpH脹Cg[pʠ׹X^y?;Ͷ[ HvJvb;quvx'Ӈ0`Ie˥ `/rm|uȵ,Í 1y"i߳cT*S's4g:.t`ƬL9M5xLD; spUw߻=4l%ɥ$Tyۺ]&ABޟr\Iᇿ 1hfB]AqOdqʩ:ƧԿi*:gZb(hze x'1^({&]INj0|xz"/(14KpY;C[Q!a,`fbT #@._ &Ov? @{Y*/jd$tDbrf)1֧ePqB=}@ld[ߙF?2%|ĩr< NE]AMJ7Ƀ")=1r F"#KN偤!]L@,쑈/|S 9d$=z!BᚕY[%QU Dgvh$~}LR?y@8O%媓cHE D_/Lo]ןHmG^-@Wk YR(e+Yd:'EjN?IJ^MW`< +;&n˗c^XZAzΫr !c4z\ ,5(-p}EO mjq綀!2j^D)ZTW/e=2 @2?{pT@?yE%MB"iOjĬ  8X>ď {ȶh74RDk̈"K8fl_Wʝ@[hW8վ63ؙb2(CUpbȗM.v}2<\EV\߿$}=)j@hNc36 &VeR^MS+{pj`9*xoiΕbVv &lҢL)h򢿈ނP;.ïfP՛1alg!L c3D)**6mKb#G8Trr]nRo}t`Tgy @ҫ`~8/zN1ؠjc]khupxf3c8:fXxY jih#îC\u`h k{P:'|6Ojd̒t?%  kq~G ׶CdFR6X<-.Fؾ8qt/ƇndґE*&#OP( %W i}'S»Ԡo^5b?@"' wxm!w*2daYh&G/A&ŘdRx>]U$/-k_cn~NζpC6̫`Mg/pVdSEm1 Qֶ[d1JSvqo0VGQSvu:gȋ}~$4C1:*HΕ@DC+ 6{ƺjɵ %_ozK7ZYԴkpN]_rkmy3cbnBlaY_=29%RxQwI&WENY&{G8ߤ?;ǴI3u 'MdNYYukК9Ps؍!vN'WievEe=!G t2\`YU&kޖdٲtĽqT'h s#A- r14 兰 k1J~Udj4m>_]SVkA69@E~0o^R}wءZ(^`'5XG69w4Fi@b{ 4~ &zv1e Wܵ[KIqJ b␔ Ohd0@F NH?˞} r=4Q㵒5j %oPVV; [{UBr[vc#WI!@`BB |Y;)]OPPdAxV57,s}4ǫ|- ]m3(_< #|qDa?_Z=R_4+nU@ iBp>Ur=$8 !$B2>"C`܇X׊nyJڎ)F QB҂?G n*r*2Op@ ~h@DB*t2G6.毇"6׸ezF,U^/})r;Y „po"э.8;{U&\wp_rՈYЪ* kY5lBC:[%P=eA KB3^8Z#zk?Qm;?v!7Yi ?< g'1%EAq U- '̍!KX<\j#|bۏ'!mc`ڡh2 F_;&6Jهf=K(=9K 1lTfkCS%f 9ځP9|UcWܢ7^R޼g|*Ϗ<AD?@v ~u…O$+Wq*z5Wif>I\x kJ7d##NnζL6PD*0hm| R!ʳ`G݊Pjt;s_ A*ꎒ)K GX|QjDH;{%IvuCI\',^,p:`+:+䠼aRv S{>oKV%r䝁f$ +mgEr 签͞v0 h`%> k$GbA2.9!3dIԟWnxP ,LW`zuc^L`ČӒWH{wja8qa U ElMR~4VX@E=[/nˆ96fkS@1JK<60zy;|~LM[ |#(awpJ~X7 B#Cn>]d /Ĉ|at_AO8^GHg0]$. GH~| '?sM1.س"ߜ_p-rN< eRf}nT`20VxWd\v_,O}KxBi]XZD*m{X+\\B[ۏ޷u{W; &Pvm9h7evК9,Mkv%;>F-H*r6^,1%9g1&C>}>e̢"HI5GC㗃daڹ.sphφgfuz yТi߭`p-'̉@=cʻLVWdf\!)ҏ M"U0|SXlrcCΣޤJ>p;'e"QuTR"Y:6jdwwbSI2jKsnфn:P##ĻE1ҤvKkYv+~( RDe B2t}OWV7K!&"fRMRW>+X@ GB4*D^LwsŵXryUϬ68aPA LZ ߑߥ¡o-jVI(mFd8IpVPy|h^R[OՔfJwE-wt~7vcr-Yΐ> G42k| ͅ d? i[+yB1!x1ٳwnyѶ Mgŗ>;ii W- B 8'ů@bj)J-L&O+d\ˡ,-,xf!hNgܶB(CS-`$wWXq dpb=ۓNճ=ڎ2 o"qf]N~ǾjNon/Iw.4S,n+4g CJH gX.A4ƶټKE X#H`5$] x,g?-E!m0czk񞰯zwW(`¡;x h n>L4 )rZQ ԿʵTDt Dy'R~Ĉ#grG>Dȗ *~y.mp:U!U䉫oB>:Y[KI`FgD8|z)|^ pr/&ȟ䤩wzxex|Jы &g>./-[Ʃ z{4bT"8Jb$ZIjϫBo }ec%S`AV,Gux:d߬dcb>Ek)-^d*4Yzzn\lҵڳ>zEׂ7d ED 7sh˒Stz/Gݮ:)R_z T5@l| D۸@ \:ԁz~LiNjre)jEuix𬤛OֲZˌ,(_IF==F9ݠb#l1tR?dMD7.RnIcS`[ Ty83zIda ^<%_x DD^^o،˒ڏ8כ+F\BWFݺ 'Bo !n'mzAfT$9/ҎJy?#A4R&T~5F#' =bWlIX" ױ6Te)ķQG{d6(u7Z~׻0 wL`7ϖڪrժlUzȑuɜYanͳ#Q,?*;6)kT}@y*ÖXh2uS ҙkχvpTxiu_KI)ҚrJS1ge!7֧};(gv$e1?p I'ۜzdRT6ԃkĝ\_)2D hai'hڨ)@poQP~rqzҍDhlxJQ#>3$XfrDp,qOdGk5fN1a'x[{' AJ"SzS8UHfrZ?h,H0vȭpumr,y.Z KfP~pN0'P+ %#Dl#ُoΦ@ǧǃx\O|K2gqI}k2^E;̇_D Z$`)ww`}ͷ8WAcGLw:u:γ>͡52M VjF$u׼ FmZw0y LF'Rחrˌ߃ vݗxeO؏>pv:JYow1~/]ʄn^:mh_x2>v7Wk_(Ӕԥ˹r*pY /GT5W_2Gmz#8Mi~r`Xc:]9@ң Oaf֮H{)x9[)({v(g2S*FջfrhU:HSZX3b_ojBz1Ne.F=RpzonȔ_2N﹧Xy2Te|ױE!^eP=򿃅؟oz]lïAm27{  b/yxy47grI̐S& ;EPv&济]ԇ6>[M']…AIx13ԄkE ,DF6V/aU {MO8a"h ~U]lQ$-d//Yc#J\ ;Š6_}*QDڰO %&”{MN ww~)lZW'4$9k1&cBDYSTϰQc%sR6FoPhs&.27*\KF9,*'n,*ܰ .Jx0mk-O{[ [1`~Wkƅ#W5t,M r } pa t)[[N#)Rp{kbwESyx 72^~ xp1w۴pMk흷:Տ=F02?Y9c7Q'"nY .!l r8ٗ8 X*&W;3ĴBIm~Y`G0^2(Z?a>wx79iOHϬj>}i3d>37ޖ7BYнUL~;rX+ ]P{>O7KuJc~w/{KvǥrV3 Qm^O!>ͩqT;꽯ҊeW~N)_ @m$~sa?SRQ--oZ#I#`$ٷĠb3dEqb!CK@_Q%g<6/4m)b~^5%`#/¡ޥ4iO~8 lC<Ѓ6ȰLC׬ Ē[^<=q$Ykj-rıC߹ͨY{vp- (yۯe)lΟ# 'ALEoG&L@#k9Bß=ޑˬKғI[|RFG~[0_h@?E^#2膛.y̮BBo@H.췻#DCܤ-HFulr0\{?,/HA `h`8-&7~w?~_eh-HJ唃\xb}HeV{Fq"Jxz^g >:޶4R$TyQ&ϊh^ ,6G[To~R"=?Um?#\w<ٵS.hUwl&n[): ɽkAYL؍4x@  /~߀Or6b;+!9y}%+Hsэ%) ǥR505gӨ(HӐܽZe ,Ԣܹ0R~ls3Jٞ>;Y.x#N/F|5vPw`c1Q%=ߌI\"W2}gM,qjId5bm 풳j1 _Ӵ݆hƥ'k]7ݨx؜B^TNZ8;B7N#6_hyzХZ'oce #W`2^x3eO)Ī|NȩOy-GBY'5M8TE `.~,bA ;}Q^pIʷT;1뷯U^'mbb](N=>Zc-@]<^BYk*cp0..[;}ο u=n6)f ~9x7^ÙA\ƀqdI9k;Lc^dܾA]]i{Ch?h+6>Ќ=߽=Z µ~]"-Қ:jǪMI v Y7hY( x ; S(lJ]|, |C{ҁ෼Q?jɸӷN&%?`>oE`ij'(eDY`&'ʩQnL4&Kl͍Ete;a.JGEf HrDXk?T>k^3~x;1k o@Rz[5>R.d֥d@~q2ƎijM[Yzñv jeh@{"@@J w[1&LQÐ(3솟ʒ61c༮ j2ލ].~ w:Nw,p:.Fl- R7EMp%rB"Du)[d\YX Fxۢ)$m/<ꍥĮ6pF{N, Dk&];5G-;r֙P ϊ'>\qo _ ɞrYYdCT$ȋ9溃͸þtG˗3m=F/}zfHa[z!}.1*nXCNU}\ѤAk[yI<2cT7:8;C\#/ȓx:J {{=-Y| Χ'3_a8{[|ȒbN6WvEx\O[1]>?b֞ X]nϳ&jڪ88/ƇgKE+bئҝ* {]5g3r=@c7Tao;+ѓ=z[\a㻏ދE`8}spp̷~~d\ۛڙXD)98n Cd3AvzۖKR1#fN[ lr>젳 8-wА F -GN&-H;W9S SJ=IQ.N.X+)oDqK-%Qkl} "O@;G`CI!JfJCŗ0t57ޘe3gBLʺuoU1TdiJf^q-(;gviUeU` FjvEd$pw/] ؟MGk]7K=0=%}nica U))ݺg\yg@[k"lKsO_`*yxvHFZHB{RzR&@ jj*v// t`IЦYM;PX܀MœzfP M )I.z,WX3bjsoM^7xs$$UsVǩZTIx.iV'3 IFkAN]m3d2ݘoq0;0|U|N4=$V8)UlL~5s&s6kS >ܔlT2OUpd ot6w6#$⚢yhY(&t]Fr!'~R|*b˳o $7wO=khpB a^3 0H<7nXE ı?7 Ym 6ѻ,sM|L ?XR@ 7ÂfQ6L~0B\ _Joۑ2JX"S7< iC8%'8OD!˟H8O O^;i,o#׸L\ ,|Eap^vHܮ;3\g7qWm\pnJH.f3_)\h2`lHŒB(m?}섚۔Xdl7G)XbGV)ߡqU_gّ rӐ' Az ۞a~`xfr ǿBa]cHG ETB&T4WBH)oʛL24ƻ#qE@W:26shL$txV0)̄,"NQ38Z_u- %G0 qQb}fwEMA\H,lJ]ԪP-O,_ȬF0x6#ȏ& )P蘫ƿp۱3ǒ-e]hI@rpcGN7OZx3mL3z)%(|_ ꎱ=6LQs[i@4t{lXeȇ%I><%cT%_AGmk3/LVI2rW?4%}e Pv~8؁]'|[ ϵgqM+Ͳ_~AP 4'+3 蹉͏>G}J^=0=@3FS1J/PS5t;Hkrm;hQ"XmDJإ17ۭcL}#pC#bL#=PkxoE =_oDz1لNGy<1i[ma U5\|ۚ<]]pf6t~* G9}QD;R!$gJ:[5swvӴctQqLN%HtE4ah"Xlq&Of"$c_ x}!_a kM-s.OT#Fe[x ۅk=x׻&pڎ/@o(۵.L))~N4h&vbUŜV~$\Kd$yuLɋ#:M.ÄTtp 0ҷ[l* i|W8Q\Ifʁ zR! v#JJʇBߌ@swwUiˏtVKIL&/ؗW5"e;uZ>A[{{n0-P7ILxiƓ)a͕=iP6͟db:Ib~e֬*xpLBrqwR|Q%L`,g5,<|-/ 5ݺ[\tP7^0,{+=-y|YtA+wb[j}ދ| rvt*n߭&͚:*]]+REG͈@DEΪ)D ( ,َa/:6hw)>>g @{d4}D6:kEQCks+nYNP)@Z(K V-z#rIڍ<`?ĶŔݐݞ.UۺwxbyE ~rg׸Rp2Ӗ\9J-yO߻D*\УN7_ӓF*Y7~%+"e5w򴲲hoI2PtMj+݌<Ϫ}Q:d|Y8ErCϳ߈$~fC e!Qn<2}̱ǮqVx`1@6 e\f\1!+mIZQe[%w==7 \^1k^ RӅ_{,}.XeB$d+~>u(w쯉\B|yĽX0;*7=[G"?M8&)ېǢgœ_O8YDœYhK37L؀ eآQfx#=-c2{ƫ_ӏk[bQw9Q+j"+ܒGv}R6q^$Yo$ rO7ׂA-Gs;~ ݊ްR#6u z>H= /sd~Tr.C8-:HLJa5ẖb^"j) G7\Lf-ܸioz5F7i"f'Z|ν!Ohļzp= l|=Jyw VBo!^0;g}!gFo'jH)Fvy:ⳑK2[jpDr|U4߽갃^!'F<J> Bo>8q(3!|޺;=0HιU&T:$IG q( }{-[d72RS*;}Ќ,OHX+$cXZFEÁ>xT /r!> BF}=eoKt9O$Ni%~_]օč̙ ls<͗ѓԻrŕ̀ I^ZȯK )ȯ>+!Èh\g  iQNe?nE=QC7Wvc_k%cw|Rd?F$v8-8I$GTǷӯAՃ{BW`E,;:d.V+'[6 %HsA]pTc=e]Nk9 8 X zBQZה"Cߤx^{_ElDU?tD"'tHYNk:(4`nRjIKˣJY$ãaxѫZ+һs=ӧM@+3Ө6!lpćV j/Ԙ_Zثstgf<qt2,b)خli|UHʹǭMXb89]u;T\ fS(vVT95a(w$G'fW: |%sdfazzuvcAFޏT8Oog/6uZ-bMEA~r~n5Ol'd3h #1|j|[=朮bW9p gLyhu mzԗ:en .•mo^-o>\1}4A0-8/vA#bZt` 5?R|}kR#Orw%i qcP`O"w2coWb[ fQ~/&5+P&C=w^jL^W:WLդT4a;hB>RT]&ݯ/iM<.ݒR*2* -niI@-yjڨйW"j۽;ħ,$T'1un*j0JLHyTpr^RD41m/>Cu~D6ʛfj[ 5xOe~ы NacpbSLT79 ba&4,ۥ0y\ Čz]ܲcLOzs˥QUэhq9]q_ٿѿ6$J*Rvks?qŀ4ZMywVz ď_(h|&og:Pؒiai<~ ηH;gvab>*l{M0:m$U@_|V )kWzTȏ|q%w81mސկʃnWyZ&mPTБ"*vʻmzYAj\d^WU6򏛓5gW$brk,N9Tט=ƾ䟼1H$N ٴ{=si;*J@[r\S #Jz\?}a:ȷSO^z.g𩻜-Is7?ed&tH8L+3RC9JXl]61abKK*k5oo\Q̰/߳߁',&sQ} ;H`HKnY^ Ǻ=ZwjivWXAhTG:GKmT؆/IYQ9o C@&Z5c :|Wd`8: rkIR`FbnE&ˢ!J@a>[ٟ;~"n(u$*-l6&!=n ^+qj1mB G:w O(8ں# 2[>ucwy/iÚ ʰgLZ C-E1=|eӖ>"z7 {CT;PVy05>4W;Ȧlѩ[k&F6ss(Ymu&MٯDZ|9]"Q)f4RaQoBsIUr+&&MKDG#,ۡV6]`SwOKK ֪NT>D>W3oGn3T52%:8'pw!F2!BXOA ?К$i+?iZ@jr9z6ӏwSN;!!-b!W7v#aSpw5GrE5OngOBRuiN@X {%.tfkq1,Z\B0r6] ЀXhrlȤֳ |]ZJj-MNXfr?sST >; !0bJ>&o<+gx4R}*xsRRf`.fLGX8oΐ2Ax*gFQ 4 tE|' ,|(f 0{[!J2h K];b;_M,2(szw굑F63V 'ubī;zSNP3dԡෞ@. `} Ste{uOYu ,ipQyt2/(8 cϭ-SƣH\$z8zg;vljzbFʣҭòXm|T-AOaɼB'O8cbz+G(~Wĭ d"l <Xm3 Ճ#C.A粲.BΔ\Yp='sQpZN0Q x_xSMbσs@݄*&KV.\4]-#Bpkt K޵݇V:Ç ғrh{;)Z0$۟\ D]7*P=' R3 \}`AÕ*?a(3/*4P2bfO|Stf*\! ] u8 wUPDFTwH93!lET咬V;pkXAB yT"HJ,+J_vAH#[Z*yLޱvO{TS*r|߳@ v8Vb=s.>&aw؍FR 3%06YYFkƉzt6J=)Q.;'u7xk>Z:URJ;'[`fKj@H*1j" \ym+BP^9}7AapM~4je# Y/j3v02% 5Dσ tš W0cA_荧D"+< a΁:NiPf@DjNg&ͭTE#puכlRĨ c?Z\,YvXkuPy&+?s.ϢΒS}{;1-1tƝ6㡨?f}}v{ ,MW|Y$-zLiaHQ R3,;,+^=D? QQ#h=d!Txp7|on[Ar2oոHܦU%4ut dβv c-ȇt4:0h~D0xzQC27c9RW}uI$F (G..^^l6+7gg%{R:Or+tv{OY71YFJ`H1SSr4(9 ?wlMov!Nje+3s .hm퇓oGQ;-Ԍa5cQ<&NNjwg)NЖDuEħK1͙;iϟ4HAr0G;yO-ÂUV&mix@D Dbc&Ӑ3șeD"% ?L;př;2`-e' ]V+ \X_q涗rذ6#^QrxSXypck?^U~ä9{YEw*r@1kZ23RX竘*52R!P£oel)Ќ';Ue8"ҙwƹUL7=pcwq.4KK^S̊YȗzBx0EJtYʬn_hůD[p`Tl{ E  1Θ ovؗvCKI S/;yP{Q\ hoH0^XI ۅ4_f ijc k$ڹ(£҇Qr'\ }1E5iM/q2>Co~N Kj7J_xC6sDkk۬꞉dS[drsԻwタ lzVA=z \PS/3i^mùS^v,22|2JEdDL>v}Q2T^ek%a҉Bf>8FIhbA2,#ovH|:] &0(1.O(5m%/=-6l #+Zizpx(QkTӨ~`u"`O#^03QXT1_XxMNuV"~lJֻfq#^bGƍ!["whEbNEFiTQqnh Q*Y(#ѯ-+Qz72yЮc ,ϑ 6#-\I0MY0fo8C!S7n!ԤX)2SHviy5G_ZF[K%ZϧFZ:-pSՌO39hlT`nVmo/=gLi֑TQvya7-h1Ŵq(uu$A:S2W9Jz1}Mݒ9N]V%bb{t* zV$<<ڋ'c '}\:OGj_6,0\6NEAF2x_ZNLxnj[HN|QWM9]m4A#[vTbb*jCufdGgER8'[&}U}3F6/[Zꒊ Y,rޔezI h[E=FQI q ^b:{֪=_jGG[`$Rͻz#zj^v&}~Uݴ{O_הQ]#Wb'@zN"7xUi --6]cPЏ.6 wAK?G:/Tnڄ$x)Ygʣ#=AaC#A oxn#/=J8IˤCiET͘5!<8 LR?Ok*Xt誣o UBѐl";B⸷4胧7IHxqڟe<y9ޝO4*q3Dcwawؾ 6`Ǥpnw<Ϗ=9o %,0`.+bh-) ]t1Z,cB}֑"ζ ,Ksgz% FƉCZ,KƉ!yoGO&ym6 0oIpՓD mJz\ʒ ,*a7ush;F/*-[XPwL${ٹ"j.zytu[ Ia~5/×t޳Qgb@FT8DfZ~z:6pe;8wbI?;).~ϕxFH6SO)Rb{'"S]纭d6-$y/3rυ,3Rގ,Zivݬ*0(&2 -}jv[7ݠq"E"h/<;]Mxq=<Ԝ:c +Js$Hfǧ8HYV3dN䀑UgcL%e3lϻ% {j޼FPui?Mp Nd֘Xnr:B7ț/ٯ)k~6yF8=DAQfDs;@Z0e 6U0dk?^^3ЭH@juM2?xtEH9]Vɨbxb3WMĭ19xݟoA͂~|",J@Ɲ*ܬz esmY{'w.1P=I3?gòs U7,ODP݅$1{$ҐSL 5 59l,(b{PK_<Ќr٨E"/oAzg;j4Mib$)\ iZ䒍a|wWQa,^jNЊaCjnf U芞 1)Y/dkn\cdj ^n2.|dPYdԯqdN'w;z-L)h?C[E({aG1ԅ܊Rw$̄VHflDQ5(;>̆2Ȣ%B?\DSaKKp6r$-4!竺̬Uꃃ)ʹI|FsMXU*&T(QuiÝbZhd1L2$ϾNPILuiX? S zr`+$-"[ꠈƇ_…GDH`ne~ $E;,+q1h*(!e `!dLvq rӵEm{H[AD} # >qtw9<*-inu:`WY::ξ7o_`X6]f3DЦB$s0I#IJS Y /)2o"*ٮ[Ѽvj)6()ȧIӵpL.#n2<zkVfE;/X7@1u?W/2$"BW~rWbו5}!' j) NVhN:6bm ԰ާ$$ʙRCZ,]S:Uʭ u2_Ss&-w.6z+\B97zKSnJ>QfaHM-2*zM7  Эڠc73ɞFaZE Qeޫ7&5RϧBXFwldې~r>]GŞg36z,#:-=NaYl@O !cҊ{27OHlw~iAo qi@6G-puB^9jf)4r< h _ r,`G-aa3+)= pZk-p[0ƭxW^ܻ1> Gw~A(.HSˆ"V@V;RΓ`#)E&k}浵ژiƌ }Lmș*s}!Gn{M1ίhKӺvM9nY{8'Lh*b1Y 7NXb]^:hyyNYs&,?d7lOv@\ TL(ÞpO+h aX)Ie 9m^ S?;D0:k ǂH>>zx_2 $*Qă۶Ic[Pvf]y٩԰o"hrYWp R5wSq;Ӻ>;HFK9S/U`I L^>Z9_!T%5CPGP72Eo|٥BN.t~iڦuV[ AA⢿/OzP u!Գ(K-xac5|cm-9iE*cL$K, mJqή@k1%܉QPfR Bqj#4[k1n%#7'$NYhHg(Q2JR+u>C0`GDWz4OhcAKԆD#i?Xp<(F֖Q,"]89kY&*ZQqkGJ_ۊn&A[lӊIzw+1&QoiJ@AiTyS/. \VT(ѨI4WC =r!A }{|9hȞ=:"7;kW=!pQGWWGiC"+=Q@@cL snUe|)XU2걜3G2zHm) u|]5-Q^ú+gJ *[H_/qV=5dbm%%h.kYF>wJQUrMEؑ_{K"2˔ybaƒ5YA>vo,XI$w÷v rY*@۱:yB̷m4g7 2K6 gӹ.j pZ0-%Yn?F.{Āp bdu0/?ZwLu=CgJ'*]CV[Pe$ xKw OpTXN.1Nr3NޙDb*;6y8ZR\ڱ,,rFy^1kEiOZ#t ;M72}*@CVJ[h^ F'#P]ǒ!ؼ<Ů#Fr=5L3_FOY۞ jOu1JШ1H$I;ڴ6sn~CYH02pEbqϠnQHܶEԭYlgɹ*Sx୭D^Y D3C~92#(&"lvD?~}jT'GNق'[xd6l 7#XhscT=QPio] c#Ijxu<\dQPe^狰*3L g$ސ@,]݆H8CbeӗH/MactTsK`$AblIW^\[]lȠDd3GTh#G㹚JCC>@Xx{7YV|G peҿgkxV;Sq `ޅ"Vi \`'R,}+~S6Ie~EFlO1%f}ddHg\ mNG>9 ~&dCʩsWCZt"׃WzX5:Q%IXqd.gH#YK* !; Ŀ3W]u6{xo_i1u]4^HJ-h9%+wqzսc|`UNޘJe+/3};tb1{k=?`҃hNhslsI»d{t j] 0̼$|F:jm`R?: x^H&㏰o#?rRwP/rr"NatV^GNr2ZPhmz%J>I> 4;fa߱ƅtEWb-bQքJ'/F\^|JzRɭ 2uxAS߭->kIꐇъrHFM~Yby)V~u'tn3>aH p2qrw5T62KiҬ~ 򼭩pAȁ3}`2"֭WP듬C1^_-bM:-ڼ;_rћ1}&,GUm12dRstjK"]QNR$ƿ$H@*=\9B2 ҡhh#RzU߄'DD:24Qbg EiٽU9hq9z8̠KոuBOQ ecshYn@XnXF,,Yo̔!2t/ԮD &qlٳ297T L!}Md58)u#6z0(+`EAALjVJgܘ[2qMm0-n)Q1&"xO Pp~3)Lañܩ2B#pVs*5}34>&0 K:Y3ڱvUO2D D_pLQAʁahr~$G33a xQ*@«P%/[?'^8--3 1c97Nƞ ML)˶Q/l=$1No/6+UK@HԬk!'bR߳Jata{g]1~g v $mIi!&! Mk^ޚD-iI@EY '"f5[}xv-8%J8@,e&u~!+ g/&E q%*AR"AECش7<3.~7ӜMڄֶQZRmSX@hR=[Ț~0=bg>*zEIɶXÌb:JX\6 &"(M.= pM*K+o` 8̕*ïF>t m)>{Z8T;^h `:k^3uCqA{q:pYWҵ!<:/HJh$7g]CYEG"ڳʯĨF5wUޥ5ÏWMt.v7zv=xk\"9j ]jx}#Mxɘ}=\!x欩%gNzu ʸʱ|W:cY2pa"[~0ޫ*:@2=&ϵtnj@} Ƕ7TVHx^EMqݶr&RXCXڈfԜ=LR+[4_];zBi:l14^iϝen_|arI%ևd`k$QCXӻkί.3wOnKAly?{QXѽSn(u!8О#^~Fڋ$A;ws:w @hݲ[r#kE@?kޫ5XgMy.bӧjaOᄅ,Zb%c|l爐fE?qI,`8Y=Y =q2lPvf|r]`'KrLЉIvqp7(s zW67t Z6j"$ìŋ`l#mQ %VR8P`3fG8CMfL5'hŀ(ޜGmR4-'{s?/OT 4@.w\K;v3ҶAch@卛 < 0.Pr(<5D|BaSKO@oD@\xFzi"҆)" Wm;Q$yMu!32fy 9 {̟ox` o޵6VZj?@*OKeEg[JLrmzynl" YfS?lKz90Reh);mQﴼ -;fsblPN$Us*HrB fK&uj@AW's눲1^G0ȢI+ ܉,= Naamzmx|ѸH0sZ՞ըm4OWp` R| 3??U%ZCv$.q|:Pjyn4XPM9.iD5L[Ru#ӳ1gÈ"}b.h\=-zuhSk|*Nl0^s1wJ umh)6 =#1Uj9DٳkFVל39W#i{"ρj~c'r-(ũ lBH ,/vH>q7dQ /d aZ ۴u}B Uuy4W鍯'mB" Aʲ 3VʌEE\gƘ膼S(͟tX!:pdnsLjQiiIpJޠh^$S#f[Օrc+Tc%^cxU؉aݶ5(Jx I@MHѺzZ 0݀JA 5ӆK+"'B'~N4/zkS:{%ɒX.*>eOBBVw&0t 4[à4˲5h)/pܝ*HtNZGr'mk ݎv %oPVQP ,0*X!S"̩2Ehg@CSR"0/_ehkDHWwe4mqkϼW-U_ ߳`o֊Je/j{r3Y%VZwh2^Z"/`=eLsRwD؎JH$Rx?-#T8MBK[`x|zdKk\崥4ؕQ%(Ԋ>fd}%8-CP.lD6-]djᐑ-a/Jڮ}'fcT\78NHP4/EgdRٔ`x5_}>jgZԓXXӼ$j7~Uh3_}Фvye|Ud[KbTy?CҔ:y"dž8LLnqrg^3d U"/j jC/sRHX/ QmѺ<_lOOz~rT0HweKV(E`FpGZ*%dI­ڀ *ӕs椛! U*њB*xKKF# qR?Yкo-znrwF^~eLLRb] gqѦEY <ˍkAʔfo:=>wvq\C'2G8O CEW$/ :q < l]4;ceWpD ո2VFV@1pA_k;vuBǕ|\h$?~[B~J)+ |/WJDeo@uyἵ*@Xu]g:֫{3F- 2^=ۥ_lee?{IhN\Z.O'u4o;xbT\Ҳhf@8tVcAh&.qFzcYu ߾YvhNŰAu,fe stBdԅS /gpvYG%I=O (r96]vM Edl%$1$)-;M#7V_X4ZPg}Zu,׻3@ָsK;8<-;ˮɷjI;أtzP]۴bXP}a#yc!|))ɣڮZ- UjToeg[p\5ޚXKYh8իQi z#qQN?_`r8F9gzqkPMPN<-˾D̉|j30?}d-"Jdt4} QG;_xw͗'(BĶie= HpC<.Vf5~cNlj܃ #jN֍IflZGEX3nTg<*Fn ?UK9{Aac㈶SZqe?)H}ﵓ|ͻ[ri4#5>׵/f,a> H+.!Cz-3R1'xioȪg2&" BW`3Ro,Şߵi c4b7XJ (d zHU,~f:y\Νl5`"a A`AF$ 2Kea{(HC;&9x~:nY*şXŸRj\$V Jŗh15~Wtci bks~{1EdɲvIy'KBRJ 7"eeJ jsYDٸ~)nOg*ôhU֟× 21Af-JѼu~KNޘt7ɝD':8ȀhEiC&dNXYn0[Fۺ֢)B;JW&µήڊ_vFPC߿z<}4X[gɷ-uȴKix'/@H:h}!e'QE0_K6>xuØұh0>.k)yQVz=<.H3󾛭$^eW~.64D`jbɺQP(<Κz⮦p8d2)m>gX8p{m5&Hw2<{@PR )ܵ߃E6Uqw]Hl߳c+zOGlQ)ECu%m B<<15#_=) uFu,+tAvTDZFW$=>mA݃ܞL@WwG$9ҟf&?ߔ/I\t(B<7v骇FBgÀhDwd]~[0=&蜛 qrPBkcΓH*/E WA\*W"D(^1ש$ i O,ij,FME sZ;AZQt*8z>\ēVHdjD⻍S.g7mNP-_GZ_qLӟLZǓ&Dśo>uO,9:IZ(/]oOOL{%b64A>eokdUuZz(8Xr!Kim)S ]>K6ɳ4EMMl$ڻ]2vk8u#.b.HL7dteIs%sqBNSo+ezQgz;(VS L5{^RîS37綾rGMTlcVΌ K!,, Bmޝ4'{Vߖ-\,!2ѝ8.G H ȇ;_6ŇJ.˜myUg~rPXeX{)HXC)]sbg$Ўjǵve{"zvb_5 u$n` بJLNgנ7s "LS^ |4[gб*݉Ji< 7oBL̥6)bl؅,sP)B?VFQl=79o?Vw5w!L ],1t ?H1tw.M+%vl+9J]VbBbfW$՛qޮܷ.)t%K|afj KB{.Gc7k_P;>`r.)>W&M\v;AiSsbz`=Z`0?+FwqyVd}-bM:ZLo+6 @6XEa(nfa.-ύ;j <(OJ i$6l0_q(HӾ*ЎLXO/y"y=d]p#tr0$03+͢[ºK0-'띓LcN^j[1 U 64e*IAɘѠVn '^O䧸'QZ:S,ҹU9`*KAQ6i8e3 Bwʁ4 n__`yDeKl')ȠT`xz<=s]z,/6T 2$?'r ^JqSϊ+ 5RNPf|4eoˑF6\[Odg0~:bnP9U#wc<#$M$HmЖ_Q_}sӞ+k7߃ڞvI_96)guvM5hw Z, pt$dwyU/i> /L]&-9!4JۭUD{nĂS"yCZd4 ζNѹj`0ݸlT= EoCXԎ׳#E?٫yI! d[P~>0spŽT˫Srj@Ӂ/Dݛ?eh{;. 8vv/lx&#A8yJFY6ff{N   fdsGx4iO=V$CIzoCj+|HGysPp'Diu; #. B=X[[޾{@7G] nB"\U$*1<X/v#_9&G͹ٲ0dKqU5]] 0挥 ;9g O(ƊGj DvU0۾p[ K2Kreh{l]+rgCW' 1qFm.NdD!*B(<688UykMM1]Ko^CS,UoW&t@X|OU`աA*NDo= pD"B*p9]DoCrA_N2Zxr_'IϨEmP-h/[™`d ;`!(@|q7Fjoֲ@SuX_U 7KOF=*b3lJn^:RQ;%@2Br4`˜6^id,`4HdqBӖ^B6[MQ*sP)ħOUvm).R)i~XoOɌ_-Fk'gU2̎*0 Uu{nهF[OE6ٽg=04s\ 5 -g }M({SDza:|V ^|SUdIOIsT"G$*#?{T;/L㩴%´qB 3ɖ!m0!O/G/t&}n٬eITiuÉSOVs 5m?,7#3ϝCz b7DMp7؄mT'A؉F@qpQF=ڇӞTc_>W} :!k{7dR7GWr#*UkٍCT8ȇ¶h%ļ?M󤍖U7+V@|L\6 ղ.@xA}IreP"xI)ӄF_O41P3-9Āk\.T0V1LQ"U1я:Uk6`W*'4Xh2M찆6&?+WߴǖudL87 _Й ),bB['i\-C"l]r}9VZg'A3'Jn>s r4(oT`LyՒcDqف+6AJӻJj崉jDe!=ia܃*qufZP>/1Q"HKVĿٙ25(C% RDevP|媓=;%W]EKFzTZʇMCK=k91{rw8SN?,may<72-ŜҜ7|3V{H 誋AQ``DYXCnqrQ~) %Hn\̑:k<>-gqE={!Nc htJ>$pL8{<>pQ73;R&<Ũ6 z` Rv fVDMݰ\Y# @3x*_o#7|S>:H귋ɔֿ]@% /Q}KoG>.Ț.9e:G*X,?-$7}{yDrfدAGһ48-"߆XDٻazptm?eDL.r,J`/Gp Zᛕs5o^ʹa!^poR^N|AIݍ6aO4OsQ0H _. owQ٥q \GT"v2ʌ Ta.k'xo, G<z5muju@5Ӥ Ętq,R'FڷjfŠzC^2dl W^L5UކjXp:tf ^w#jn" U' pP1bMsi\X ,hx\yb5-{eIrLM3uf 级8~.5 YA|$kN hmmҟCp \Wbj'6[*$~YuXCVm>5ƪ*bGb~@WcT10)Ef-j¬ Jw0+%-"6 CwoAޯ=[ʹ ~vl Hb1xfvtC& Y_|&_] ~:nR{k nױm͜,:GāP`;v'|0M2-dD3F02vpͶ+ܐn0^E'G !SOAcnNaFL 5_k.6)~Yʛ1;ynU\xW A-hg܅'T:I]x]~)9d e\,ieVIoP!\ ZN J m2h'* >J5xf@XfT=laxS5Wֈ"27m~N\֧GP/M*? ~Z1-H-r+$Nf,l@=gEM@e}%_o>^x dF-F'NsU~|˙`/A/>J,!iv<p T@)PfKjO/5~s=IK1s:k.i\p0~x/L- N+j^},y;^eDe`/ G~H(Y`Ȯja0:ryx x]=/i j;vJB_Za 6z"K hXr1s8f_3z\kfJ}zc-`jVeW3R e["1ԃ!zzŗC8K.9p2zbChǶ9tLhb HK5w4+>WoRUNWZYĵIZb?Ҡo~Z$t|D+P*\EJ8,~ B Y ?d %^?VCGB Ss ]*9//(`Xoޭ)aabIA[, 8&?Ygl-VXb"LFuWE`gJ3$Xlz$pV8QSWğ yz9Hco_Seo:*҂Kup?STFiq!|h U]3\ۘx-t"uKʍwN]L?RxT\d䘘P"s;0˽{ni:ʨ)U1 ʇsWzDho[v U#ӹP1݅ aNf2r()؟"=b$}O TC[3HÔw0,ȪA~榭ͣ+pQoۘa|U@%v | c/ja9_+un[RkC;;9P{?T5_Y>1̔]Rgw;ё%`Wf6 ]6Xy+3aTM< Fc.>!)9Sݵhk *p"Y8ƫ.N<\ KOQi{WqyrhӀ@ jn>,r nwsOk=WC`mL 0]FG)VT^k¦Q6_5 }+n$.EtA1:Şt6+ ׊(5BQWs!oJòۭ;%4K *32ۧP#)|eh|p׎DxEXHL/wg8xU@DƁg(Q; ?kWF!E1]R>D썘T3a.f!}-š'h`éW᩻]2};0vA%\6tl=kp8S'GBCjD%qPedlV_?OjSBEӛzt7h]'Dg~j x?&>VLaIUz0;8-}Q5P_"5d~#a*g gwlH3+c|z8q v =)y#_7i$l%~~ܨJTqAH3 ק`B˗ 7reh2Iz|o !%. RB}l{|T"HY% S?_`~Yub ,=lMg 4WyYl]nw#yx7lF"#B_f/y@"vPA;m!D([a0ôR[ I;@ ;6Xg( v M.s~*>tΎ.!|,c47j> yJK|Kd!~HpRNPa7Z)ǻF%˨j/;Vк5rĨ!cOP { rZ/\gcwcTJR:OL+=oώ$ѷ;3<1է`ۖa)!y (>F?{؏a\ꔂ& gG!oR8K)Q$#YEd U]8G8<:zOn8S+ u4X=!](xjM`Ȇ ^>B7 HΒ}b걼++ml4Mx ier=3J66]L0 !8׋[0Z칠мa5;y7\n0 }VG{}_u&==dHΑB[z&х݊f _'fĞIe{:껗Թ: AL_r6ɉ%bmL]k/VM_nS u7nZ=w 9>92EfX{[_5',}7OgVP_3yZx=r{dAYzFt&RXm;@oyC4c;iX)ԝj'r!TIŜ<"[p;lH?q6g$gOIur4IY{'=Mk ag:bVfqS5// azO ?q_Xj|z &wiO8erh`_9pz6 crFU2L/^pC`h`ؠaZܓWdhl֋$5XWuf 5!`fmF@-V<_5YV T}?j3먲 { ږW=aU.ي*=UinYE([Iԫjxro:s: :4If7FjfCܻ|p%a ២R:/f9%_JFA 5Lz)>Y9`%yje!.Wo "q.*DMm7Hh(0?HmT?[r@(B@^Zvi^ I!Y]z:Jy> BTV(AyyӒ͙85GS"}&6t*7 (aEs}[ޢ'Ip>CE[Yd|hrNҲ-lvnj)&m&$` tv i0h!:{Uy9MUL-˜v12_t%W嫖u#~+ȏ>&PPoI`;Q/`SC$~bLҕpTnV[O5#Hb5g)f8;wWN| ɉ uKz b* S2B!|̀ VNAm3%{{)*xgϒȧx W<-:,ArtMvdS)Gl_ݾa%bӘ2TB Lj]U|Gܬ-=(׋|B`&D{fCW`B 3Xς>kQJOv$ufC*4F$ r18:/-e4Ny)ed6 FN0xa[so7,9QΎw(LVԪҦi 9]053٠?''+)k: UOv!0e '~L;4B 2KO,w}(*sħ)A& whHS4z%-'KcLl` 5vF,b3!wjlؔטڷ&%$9WJ2q#˗E#$.^0QDA38~ʥMUNi{sc^K" k|>ʷ4ఝ|B[1_T3z{i=flVu (n&}bbw8&qlXL5ؽ7' Tz7. &00#P_[Ȯsl_ܔ*Ab/٢رO1Y8Gd#!gs6Wάf#$d8?hQpfgL2&;<8(ƐyO{S +Oi-go8͝00_!kBQc C;/ӍԘ}^0Z]I/ATatcJi.}5x.OKc'洏!vr,Uߌy/#K$\bihm}!bkؿ6M;#W^υw7ww{9Ѕ_/Y z7Ƙ 4ydMh 9"|,3`y8j9YӏIzJYߣA&>qҿƇV5xt,OwͦGP(tDzi`=QJP`䭦#벢f-"9߰0>W ٷA1jQ96X+ F݉I}Q^+OpȬssRH^Z)tkA1jJS>MIbzF(;`5 וvd~`ɀTWq霈ίmUڒ~tpg\"oD^wDh2?lgZfU"O FaqVOo)FX-F54@Vn endstream endobj 141 0 obj 99063 endobj 142 0 obj <> endobj 143 0 obj <> endobj 144 0 obj <> endobj 145 0 obj <> stream xcxe]4Iرm۶m;ضm;ضkXkVYk׵7)2 -#@YNVcgced5!%Ut1_0jNΖv\a'SCߝʮvYC'#N\qKWgSS _ U%uJjjB999FDL-dM/fjc`kj  Xm<,&&&hژZ,m,”]oˌ0qS;Ss۞|w27X..\f)(:g3:;Srv&4 fK'Sxσw_?WzU;KGWSI( fn`abbegf:L[H6C2ڙx9; mM},L^` L.N>^/W0KcM}-kd@>?=}{;*35E x2}0-; R2? 7T?d/II;3>_cۿ@?M oۭ+:  %ҿ5'jcoRd m-mȀuK#GJ„_6,(phЈ0ZA\8rrwk, &_Y;+" 7x%L`4Zϴ\|:g"2bF.`s!o#B{VBtl<4_=twG=`!#Y:/`-vwwQZ&(մ-'\nTɼ)qiYSJ6scXS޶ݑ$>_er+Zyzᛐi*Uu7>utdx,~ aZ*3דe-jOIńlh H(UoP=i5G]+a)W+h:\i:xiC%,:^- {喆er#q+\,ӹҙxJ?q\R%nHo-yxQ*c'4 qu}:2$k \QQ&a;-M*!Rlzj3RarN`ǺgDƊd& M̸e*Na* mRzP};X ;h>G.<./Mx]b %uقҍt}kϓkUX񄰯yf!IY\A`wVίv]beoZlX]!hlޓ*=D3h&( <@k}Ϡfmؔ흴̛Mk<'bHw(ޜJ"7XBk8CAz' 4ZU"3) ;D6g"~=aveLxhI VC4So#+$'ߧFqniMD&|#s6#ɋ?Š7Gې-x[Ih!DLi;VS5:Z, M"#? L3ZF:k CMŃw% ¢J_7*ݘ?pP=$-Zo>+n+&k. ʌ/XX-c2^db摦jn܂gβ(N1we:DwKE>P d$o^ڍ>g.BRj=$ 5/zj^8,E-] c1(=K$̙y,Rkϒdf%I3,,׎Ⱥ_;^ 7$t]ˌs\4-ͥxwߊusd?(PF&Q;$S+"5E=@'D WS/u- pBI?[AdCP ѸC`~B.*t$lԖH/NX]Հ-}JG" ^BR~wҘitwcdZvYh;ȝWdqrҤdmP]bdAC(zrw`ZU,( 2PD?>-{?mޙ̨l)i*4j0Ry~nkZ:a=WzR#9XZY/(f̯P &F&|X@ t yuZ=#BIŧT_.T-V J/9M;0Y%'CLn|~`#z. UiQ˘b=RaUN3YdkE>GM]VXq#W:Wk#R(34jFq?2PC2a-A` *C~<8d0fDci' P9lM-6`n/ϤZiA3|uUk k}DׅWAP-:Fx:YCx[~+jڨS_q sk!gZd_ul8tm`GP~d6ΚQ-~CXY;E[m%UَLKq T(8 dA1{aT/ȼ\%iZpJ\{ѯQ gY-ZؾhӬmw?[}#m"DCDIy5^6`! hM]juzXm\77(feˈ h7J}p>P/}Ǣ@W1Mnߕ²<UZhB|tRCg)KAY>`oӦPcp#-Wxlr'"21ձqj,~tR4U5qBt8J`?5]Db]|OC$s{.LrKbQB܃O ٸ419(2a޹CIŠi"క9Q|B+p0Pv{X?0)ek4{Zx;G}1Ϭ!> y$6esY\!$L&d~zG,O@'쫗^3/C$;vw0?W!f>X >mJ0i(c䤃sAr{hԸ.流 "_K =} {,EnPn|D(Rpol[کquS;nbO/ ~&zXL-}nY$̼Buj17IȨ!Asu sBN_a^Q'+J L gRJ].F'+Ysx5'jrh+tVm(ތ%ɧPт M$v4!DRg_7{,t@+(L u›f o& Dˮ RD1չgihp5i1HJ&j[q^9kTǡvRϕ;寂ʶvBXa˗J= %!1W&&׾=ϨڴtB`.X618# !BSFt4 ga?.hfŧCaM(Rk'ܣ [w`&ht)WB-/ 7L&&޶ ΣVJ\~ͪ@ ٻ7OqxѲ'E3U`ey{ X- Fݣ!GxI0\oF/Y4 [2 AwN@]k&%` G%GpbV1K*fK) bck: %-> r&~??eC}eh6ת#&WMZȇvz +8֑L[*mwQ׈۽PW$)Ty]Ю8|<-ppXP4%A%~RϔM8d"_4QpCC ` hKwSkl8݌jOĠWinʚP:e9!c8vz^ ]u0PHSIJIC4zBDjy ^t,jF$^\)Gt10-Iѣ;ۮn"O=cJo@G>nCʻx'SgB =Mu? <H<8u.Y\9ʇ:ipu2+2l3 .aȜspm9&~ gX79@Pk뗡}za` ?k F4CpPgeP"&Kθ6&H+E!ŘjEp5Wh^@JZjD"=wB"x%O43I}AdXfo+@k no$qT}8_iaF[C"ի*0ו|Pv. Khn8HC؂vdˑRG"~Te&^'O6_4lDSx\gqc`En'QC6"75YTMmaFBK{BzjDXvd2@z?$^!j'ОJ6~LMdLU*y춙lErJE>OIW;"J+2OE\t88ӍanjxfySQfTcKL̷UCぶz78"*g =Լ%kffUqCg{~6Of̕.Ui;X䩴]OO 5OULP~W1DoضocGJb|~ZYTˑ6$77v栐<y}CveӉ/Gt/73j}lrܒ~Ӱ]LvԟbIYҩ6X5L؉Q>d_1n_gUTi@4OvJpqh`;#;`*[ɲӚMm ;9$|m;|HM;>Z~T))HG,MG_uG(y߱ MFFn$(ħُKr[o%\юu) H+.?WMgW6"vhVJ'j >v܁)H.傛RС~;K HJ`fV'$d KTETwϗfe=;4,I񆿽ЕN>VOⓖ菱O)z) ϖQ5EBR{/NGlNgGQF|]< 8\çrMEYa )v-!fߘ"U*!QGvmT\U 92I<3T6d5JLjMM|[rD5ĿTXFfPXf<;`xϞͧD)`a2'-*ĂM$9؝N͒&gW+miZN% aӴ#kq=i 9RqYL`o&>5N9#3lqJO[(eC7Y n{r$VۍRKü$+,:9P҃HxRˬ+r׹uRW<#r".R GdQ!:c׮MCpt"$_?&XnAd(OBx4p1zmZwV#܇ߩ頓z{vȊC`õk<='Ċ(r)n]p씀y@,8۠Qs2Un7x ^7Y6+8;ip}1g $ذs|0+ܵ~";6n:\RzqK%<'l0yFi^AGdmOj+1Q@K^^`1B(2kWIsSqպXS.)+po.CQ^.M8tPK^_vpuRi@ܫʡty>[[g壪|OWfVӚo$K,*˱q_EoHC 1jfuHlNyl0 MCaOTiJq?^dG% Bٟo{"(ǻWLx | |G\>Ⱥ59 b}t{[=HPk}Ǚ^ij]AvuE2,4TN aQ>98]Ggg<@F)f7 mY YL"BW?( yet*;Z"Ci? ؕw+>=a:XLԳݗ,7z[.<[Plƕꐄ('"F40$d7,g@6->-~;م@DYW~ro֍iJ DШ6B)ĴvԻ>4a'_%Qf~eaQC{֯,s;U=~"fii?Ӎ "N}C޴ T7&zUӐ`uh[ > BnwKqڟ4E*qh_Te>&t,i?],EuԤy=ܹ :'Eo!U ,8RRѡ$~EkL,GJCkZm?v2&kat"D!/oSO^?HU :}6s6 VS*&sMJe{ b1ǖ#g+cF@J1'Pi 7W1jyX3%tM7ZwëHơp㈶T(hU~% !S/UWץCl| Rɳ]0-ffpUg9Vl% Xl7O定^Tfd 6KbUt0!y@b (մxCMdOѵ۶c-Y?%Huŵ5vCJ0٫%nGoMZ}Q1oh^J6, vVr[k? U(N5 Q$ڇJ lw9=!\EϏ3nE_-t:t=+̼8~Et0bjowImU}ͷӈ`SLgZpTM$>n3ZE('Է<}? 1 -Bw&?|#W`bHNcBNJ~}OJa+ADkiN^h*XXo15bRp.0*\̂.g;/֪#J5 DF>@*pZd[Ш uQ|"1R~k]S^tClAh1]ɠj 8xS}9n/j{|6Occ S`Kf$Gbm`ӟ&1&[;r/X"96u=ćoI(i`efߵgX"@YzTPld—\ _Eb4#c` 2?<ֽ#XFs YxjeO3rջ|1|,%"%fMU\*6 o 92-ę!N\hSMeWI`& CXQ=mtzF%KFFg1z|u IrHK+N?֏38< &mLnyo$DmWuIw[O) ڛ,>ZvszJy.KBJG58. FwNe;j2:F@@oERR%Ws;p.vL[L6A$XHؗLR> W{ #_puCR^W_Ԁql;wOQ'_tz Z%ˇ`ͺ';.9֜FMWQ ̀FT1`kݢy :s -J^ =;|'$O"A ': G'CM]8ƄPXV.qk~/.&M'u̽p G=7yXan2}nMolBlcɝRUͰUM60 q噉#IiNKʡ뙄]k-Yv|hgGd*>FWl[7+pž1HU4rcߜCR=k9?;8֍`~lGPHD 6bk%qNlzuCȲ)0OEșBz&b5 El4BSsB8K7 }RLm): >#x.z|9:4 {y8r\@N% ʌkci'!ؓɥIVhqPUPR"M2p"$ioqqqS./pyx1t㖧(ң 4PDK./f6<ҞĐگNj](NCn hVJ8. ~uv^8_ ܄E"q~1`\` E1yRfL8d+^U0mGqU>WLrWxGuQeJ}{Ld#" &2dAJ~P.iPጯ:ld~дl6gۘ2-#o8cofKTL^ y !Ϭl^k.xÉ?cb 2`;IL2|=\N|Au8!+åDj's.}zV5d ]E(XR3wz>Lf3 E=1?(xq4yCd-j>7+Uᑲbh!~-r5pŶde0Nmw׍y"\Ao;XVr@|3h6"toWہYڄ,bwԟV[.g9xھrHHZ3Bk{ ߽-[WEg$Yl@Ppf}.~Ub{~9c- T' f *$3[2A >PpYG;’{do:xK/\NUu~:X.9B(OTx4:7cey1^y/EF;{@wzTx bQm[~Yn$19:; Ȼf7_2!.z~KǍn#@n5 OݑRoKZhy§ȁ="/>-_6@Ѿ=C^0@>bG!%9VM)͝:S9}pMh)dZ9'f2{i|x԰_Щ֗v- mld*:.V69tU>.7C݇ȖA$1*"%RQ/*)l&3SO=B*[_ܥi1zޏy-}=jQZ}OܐE6imqUކ4 [v7m;SZ(0I+AJ1V2n$l*Qd'TW t1H3+/ ;^h&KϾ_iy0.>ọyx[4c%l!`mův*R/W2gDf*j [Ry=c&~r2_:'GJ6s/b1H?. |AFB7'GT[qs%"I}(5T暒GH\'XBd*c 3H^QvuRl`\@p]-;U*WXO< =ښ)}ɛo[m [_WMGղJEa,8mWVYDzLFZ MeH>1Y lO@a`3 69k>Q9ISzښꍙ9d}9{^}89u$WXYH1K+`n}+{89K{@>պp8LJg-x/t')ϙtc6I,e2{ DhL!J22!~f<)N ]-Fz̺Q0_?aBo XsMmJ#O'3#/%wsQr35K'Z8 5Ejݾ.'vnW!.›! ٯUTDPNWP.j9dw5cJdt As6>և)UE}}:3{F^`I|"9l#.쵵byPvRFuvφ_k +Rl A NHl_W..>fzNlJbSӹQNxV`V.~8.U ^8R@ʝ=n@J6G+85)mBc=!-Av劳1">%! =e؄)89C3;bIe/U3 I>W\a`OZ?2&وYD-2 ~|3m"x`R|\0|QgC_{YGBFwsWXW~|LSsj|ڪ hX" 4|&gq_u']yB*i֕[dY fEOx6X/XNa›Ȋs yyEC{;vxv:Bsd3F)J~uʴS\.=ȹ|,%'Rb50.6kmeb T&i7&E1+QvzǎPc#"Bݣ6bc?)=ZV\˸W5@} nTmG bcJXuZ7< RGdϼ)/Cp=Q+%'< Fg1d0X$CfndGs@ X`$żoe䳈+FXg4c쑝#'rymb 8! Q':qJ͉2)7@DK[\_b^ݶI"{# xy`1Q^~%rC.[P@J]`nq>%iU,Z W{1 hwG`Bpq ӥZǢˆPunFQkzRqOqԥE`(Фmq'uTA"Ρ*ةG5z|#?FYw34.tMm[X {'4^myY(/Zۗ{$v稧fUC2+(ga=W~鏕 ŕ}I<;y,=l}D4":E MΦ}H/vaZ3 칌\/[kcT0Zwu`&3̈́:qL&¿CwKƌhК\=i~dn M*U25,eoP,3)BFì 5Z8PnmE"3}49 IizvoGu 1bo)aH2#^ة\FÂ4p1Y8j9 IԬY9 l:@>ii..>u NUde`(CUrD(oH1(\}+b21%m:>B`iqt%{iD" MThjOK^m0U*,T@}؉*PUEZ#Ds'è-< 7>2[d:xpM8Ph] O^??Dz7iyr ¹SE֖U=I,5&i6O;mK2 >ޑފ (Gs3eV& ۰ =,]OXKV9iW7rƽDTy$ˡNך &TIINH䗝ώ:CȮBFEٔR #O?:G}n{ib9TN4Nl+E_Z? |Z3l{<dଇ/8QJ,"}1@;f#n/|!MyK 9ٞ=n5Sy}QM Ajak~5*ˡ0 Wd ɩ!S4F::Dдʮ]im P0Zۻ&)-7ZT9jZzc]:إ{LH]j UYBJ5x p/&l!<٨-Qwּ7e@%Y-Tɬj}ǂIǵu(c"_؅SZdgF_Y7\$$czݲҮKPY`#J@I _@z^ۉD!&-Rlj%J w׉!xc|+tٻuw!/,vCYbhbf tA:OVaGZlJA|M+Sat2B{0aUpD;P ?tr*x R-2rR sc籙rji?@أ,3}*u}]J6-1 Y7YuX1]E[ `ok87/Z?X=llɂV9V@Xli==4FUſsh<*M),YI_r)؎\.¸s.IxwS6 *ĸ 2R7n*՝kϚd?0*<3{kr]'@ݻlKɖ',\|=\buQw0nJiȑ6%0YV>cZ'R `"bxě<5}.>tw֭~C^#dHUl!YdҋQ'w k wc:W6ђ˦w+Ó\mEu7'K҅$<P-_UݚX]';= ~s(wi^'R96 ujIM:=?}}D0uoqcߣ̞t)u_YtT,= qZt0BKP_^"OwS5TW_?"NHEɆŜZ8 ҈wK]}ܢXv1*q: p("J#{n%:stƃuV2DgĂHCn#m%#d4|m5_(' :sU/96I.q7RX2!01W AS8 8XҐܧ*V_a[MMDs{&˃^ $Z)eMЩ *}]>qhV'mҺp`q|YϷ=}@ӾIE{a.9ʩ84k44e^h+h8hw`.ݱgQӥߓ\bWIUhp|z/ꢥO ( 3MNNuû -Vmz0Uy4v7yuwMDag)(B3"xflӖ2{kMMd*![dFNR_'}|S:e}Yd6ŹWʵn舏 DN axyzg=+|%Xcӥ9<Тā +]0[CaM\j;&U>*ʜk_vϹɰ]Aa$HЦWx.$5=d=5CA$E" /rr|fcى u;i`H & l\p %>CٜۃwSQBViVP-Xk׉ԕi%?-(yߡp^b:_{<5h JÓKuV:4ʡq%~?آL f᝽HQLJ۱JH^Y0!kL5:Prc#J5[HI!gTMSn2;M"c*2b5njĝ\0K+ބ1N_(7 8Gjjc.2o1 ,rSbA ( mP6_6IW[ \omZl X4̝+Ʊ*1RqHaæϝFj5~{);oTtƐJآ|?l 0 Z*/S? *ʯX9_C1,ht|D-caryJYTb75Djw .-k jDV#iY#Q j@nҁ+F,_׾Ɽ ')2z)G"}Kmj| \{ԭ_*¦# C;rf=n-wEZ$Eϟ<&,PtXrװi&;L|*A=YMJ;9QύIw}8O}*0{@%>n9r@`oįdW'^|B6y~<$>8Z>-8-x众$ep3 S+jPJ-0cC %ˊ:DLJ05Ӊ%G>+u` ߩ;8%CI5TW7K{- (2S*1,0[*5 rm〻F$ ÔTcGd;&͸6> 'gQ324QS_@VĊfG1d=Q W5:ad M?)lwbyN9}ɨ7z1>+iǃ4u jFMfɰD P\O"d̶F(p "~-i? :<֚I1IUA8T>b_=Һs>7/ڻ/U*,Ҳω\-6܄[ g/c8ai ݙYc .twKB݊?Fۃc4z#%h cR8"Vƌ^džIܟd⏂q$#$gU[,uCn8_T9jj윸թRr7GU#VP-HxpwW*kN)Өƾ:mA[MGڶeyb52E,ʐQ)2K XB%)ОəU&p؂S8Fmy4X^u8:"(-}_(O\0 wCQ?>`(,~9O6"1I*X.d޾xTZuJ*C݀UWJoO^||'Kļ`+cj "6ahYμp}\uu'}UK!,H՞nRMEuWG"ݕ3f3X! 7]nj=R mI$hS !úo\2(M : o4|i%Hq 6VPepr)24-ڥ1B޴F\V<ݏݶ;ogb:Rj!D;Axˣ4:JG(83K_*}ҧ(b˓QVx;/D#q se:txvZNnw_dB+羸 6DHwg;ɿbOSxs Roz z-nm7C ȡ#|8h,97hso5LsGVŁZ gkrX"oVw]e)A٘Pr@dBNJt4л,V%q5jsg$<ØKYn9=}CtvJ"lەBSܦwwTLӱF5`Vӷ CvO?{力ZU^XҙL T$Ǹ+1w/~\Tu dve:{`ӵɳd!iF^ɲS@_r|+ d+,VU8L0# 6('qc"Q`Rz z07ۣz;붻*?٨^"ևf&K {g+˘%^Xz؃!>%]TQQZp/yr0N S&%L kךJ[z'"}^?OU{]rxa!qTRhzC("/2cIFUlw֒dV[쭋 5Luiژ*.;znE1p6uui ߋͥ2EcZ@-h5JA ?HM\T-LkF\y;5 RH]wṛ֩Rh}Lk?`4S+<&n-*s.Ϥ{MZ)2U~ } Hgi@280Tn"5,9H ZYVp9yiAU&sC>P=|*{& zRRAf9 xRug8碹 G#Qpg{#[!U45h l)3=u*<`!q\ϔe(VfvMgvGCDCZXTC5Ë㾑q1o[~5:+|ӡ-(Oc,p1vla]=l)np;|#XV@e[pUuku{oCSQb:'VN¶k,ȾӅtFc>)̒hkA ªEN&=;8X>5T3W֬ @쑧1Ŝu )\"|[hiW[!̽-1bl#ٞA?YMP1LfaerܘiZ 1p%zT@]O|x"~iP0`V Ψq>E,VUC]YV]Y\+ܪ_H7\a7`-뮂8;T ; '5R|JTNRPQ`X{:w$!ہcW8%_: r cXZ)ն^Y ,t}w&!~]=ԮJ#} 0jif*0Pk !{ Oq,9=B-^O (u?X:j=t44MDa zIbq#3GHSxB*yN(et{ 2OZ63b/ PS8UH{WDxk.> %i\8li;@_ohEk\gkw%I;ךp|`:%~Pk[cM㺈 }W=*K:r5cX|2\+\5|v08Æ+PY ,jROкꗍ~LyՕzFxIk*> $6~C|P6FxA:k z{P= wX{鏑-mS=#b9y>Tjj&tb sހPh!ib*!?\fQ7}zlji~ITI2.('Odu{E&HElU`ȸ+!EsR(j  yik[g RB+='V35/Ք< ɍq͞X-!3돘kMl9~/ N/kWG]Hm$:Hm?( Sȏ܈'=y/ybl%n{4I.(GRAbdAh"KωDO~D q{$b۝Rq1E̻h9gdz@ [Q**Iď:s_0Gb̤;2ўr!2(.y(NUVA,X7d:𾯤t>=<Ixe)I2{waY!}sXt:9+~50{ ;%ŃjwhW%b߄up ko%_U d|_oCQ<]w;HY}$vamuL{ qB%LaI/jzм=kx+?ĔR8vo 8m3g:5ݶ-x0:2Cfε3j?NK8븧d9V&9&ЅӤ?:V8*1m{\OGE U=k)R Ϣ489 [AрžJ9"]6!)=SKPPG/GeIt@R@hI]f;3?Ԫxi+ci-\6ؔoij1ۗyMz4B&a"1{li04(aCA|pZ^3 ~&a[-cjv-T\K:[P*iV[ɵ.Y3TҖ|{G,~ѼuoApjIJm{ q*&R c402='j/ L&`5EN9Bgӫ,>ڧ{b't/ ߰AԊ{ |u)UT4J/`y!\= wHO$<~qirnT֟4-fqF +tF :HE౥\f&< ÏƔE+uT_c-TT ^عfy4Jލ~8'1N*!SH )X5ΉsJ|mc)o`ҤfXաG Ū!Vb$hӪkQۺD-REd$:lvw6Z?OՒf3 Èg`1˝"\9i$q v$ [ɋl"XeKDppN8.|1k:j0 )ꦸM)zL-сx>}L> rjF\.QͰ;kF"Ii0_zavrn_o 8ʻa*Σ׾f?dV}S7i|s#ajOAj#y4<sQkv?VHDUr, x7'<e;>H? md =ד$!mZsπA9i+gS6́2i+w(*`HUǮ} A:pZ3_jj{s`{녧"N^NgrIr2 r^ESp@>}}=j_taotE\4m)@B?c "F9tPcm 4KЗDE h fVR{3I|UFOQh 6 tjj#O&'X:@9[Iqrif!9ڬ"z=nT ۠ClHJkgX%g^W1rε<*dc`]]>Q!~&H~!:HG?,KXoJSlv1597C7|:s]sE/$CG -4[qM:qc,YeHiK8d2lJR_=?Uit _V;l}Kr}P'W Rߠ!+>Ő~VIhA*_߭ HxX]\w,elk{X}e}qzgڀ}mUTZoGm7Sg?=g)5{y$^7DV8e [ގnL]lj]|(ܩtJ ap8GFTUg~Zfyk~8L7P$y#̀sHK+$8HsWkBԿ; 06J73fI47Gz_aY8JP\3z_ $8'gv!-j%'-}~U{zkrJtt[t>5\Pkld2?De4 ]mSti;LiL-]s|sjʇ:Fb4)3 {kt[#7^<,X8}MK3/+ LpntfaU %O߈~ xj6YjK`&7hCO ᝫDk+'[%ï*i{?~&eH`IX\kP4w.+1FUb/OCTfXB3.HKjϣ┇TaPUe.:{ YUQ^<2Pl(b~T\bςWo> wN1ܬ"2qs_ 2i!XhtUoJe3; jueī0 0r"0_q|na34 1Yx^|E!D6bzSY)<;+ -Ծ`Pպ D>a`e{\֢ߤ4hk: ʹ@'Vv#￉UN޽{YʮY(3{[M~D7Iˢ9H̋cBalv$k+WN_#+$}$AôPm[mrۏ(EҤ; zflxN؀-d&B$n\xk)c 3$H HQ~r5"\3T0܆]̚_fЛu9CAb_qu!lDOՋc g-ain US(,QIr^.~eux'exgmE$bRpM̚]-֮C~Bq8ÇjؓG~ZIo6\{\Njcնҥʕ8,xCҏeaR!5:c;h+7.֐P} z]Aeʷ@]?VM*0vWJN3|;!᯼CHr |g1ʈ 1ncWw ˮ){;CH1l\s0\mor9$c+H rݖ*d1]*s[8ѷDR. ܹZH Ӱ&ȩ _Ey"s~~>ѻ 2|j0j/ZAm ł #FZ0B N˚icG6]ЋJ'gr:j {^:!"Ӏ+yb ph'%7GB]8iBEoFX& \걷03q# <"+UC}kPmnO4A)%>3-0oqq2$N+olVgRBʯIcH5(j! wh\JPiWc$/n7.bM5{#L)>t`,_JDB͆kFΥ{Ϸbk9g&&LФ;@ *c=Bu*%k F@q Tߵ_BE|ƴ%"I.^/G[N<* xI^S؅:1qn%{Bx CV\*>wWb{,1Y=Bʦ!ugZFwJ3}|ž~!AKMsnzٚ(cfВaB o ,߂dH1V}x/vk8 H=]Qj.; 4웃K(o}̝fpRf@:f+5=/L1|Ά,+8?n[bٌuE5 B=$ʭb)# t'pű8X :($MĘ 6͈}+wRzFQHӫZ+vCFU{o@/5ܱd/u%s{ ҁU|wowU,_ DX%9/°l\Nʓ*'՟ }l1TA@Y Vs@=|w S`g 7 $'p6{5C,1KED ;g?p#RP֦ѡ/~(Ks&~JU}NY~!/6>/NБ|$ WmF74-0&ى*AHN]`Ju͋{ځzt^םph5p Θ zCЂAvbcW/zE<w  )E=vuj9d"M$|#QR̈́rHI`Wi6#S6dʂ}&>PG0;Bf5(\ϛ,[-C܏YȪ5=&dǙZu[oC)2I;Ŷٱt݇=uftP ZvJ|L躋IM ) K>QyuxLؑW6بBrVtHڋv+`#ƼU*@ *# ߑu(o1N[H8hTw^nG&sv͡[} y\SmR$aKUTU#O6uSAfWbGE4}w@5 ^0IDvmHCŻŎ&$3H#.6Ǽob}倫c˪sѳC >h-&}+@>P%}<Ơ9㔳ԏֳ(l(af~2|qr9~jۢIEuk{ gd-u<E:ؚ>%|˃}Q7IIcZmU߫C1B) sEʮ BN"^8q*&\aHQ<-j1[0phk6nU֐xc(^?ρe'U0'\":!qu>rW]eudrjk^GRoiAtbGQQ0lg)cb=Y*, BZrRv64;#9b)?)K/0ݼr~`rLu2lZ><0{8؏6w pVzyA{)2=g1Rv$p@OTV%!]=ԈAU8C DM~߮yhjVs׷v8qh^$Y'1!y/lzHkIv ϴ}}Q`:K' U(4i64澊N@\% +4@ i< *y.WO>MhSum=񤝞Fu7Tq9Q;O7.b'6{pqPå+ ]Q 'H*iwθ H%6u.]UpG$ꋒEM=Hϡsr}O~yF _ Z\^A;ϛH>@uw#}C=WJJ cA\H jH! 0-@dMWCqԹ6~Ј9QqQ fXAɝi < yvaLyXg-wVn$冞ulv1g CʨFDSHSf.FЫBIZFqWk3uCJj"Lq~ǩjJ-tDۊE0Gz] GJt9Tvq}V:?B~Lzyv͂+XU^aVákRw A=:: 5iSuBmӀ d^"@K TXU4dXw(&+D1=xzoMVNcV_h U 8o*-Rj\TjT~o,ku,]75bșWV {,QFZj- "e0d,]Ξ ֺN.sHIAw jmUXxҌ#ٰŀ@G1Cs߁ ȕ`6<\p\y 7 fWFz<Q&F+2r8(^ա}FX<]6LnT)F}]9BWcT[|Q}/3ZvЈjn:\ W̽pt"`};A[%~0g{L~@k|sԽڱi!I(k,%y g锾@-TYj mEqy ;<_(]3#x34\A (䇕Qya[ãS @d!0cO966RF4OPtyO' QHNi8jd9)ݛ| r`vk'x'@?ޫ3aQNdM̠bkϜ@øm۸EoUí}iI΃yM2~ db":xDV(<7fA*6 Ә3\]6-h_J2{0C`R3(ReYYyP$dt_VLSV80Ȧ|p%AJe{EeTQtm5qR)-m"I2!i\jd;E * x+qӴb"`h&'fC 2AS/oeS8moHXDG_>:o@w. '$mvyh}PuV[$B͚e ì<z%cx)s{,UOjB[2. !Yg+t J`GszzQQ-';w̥<Br5w5J(M0 ]MHn!/!QC6;0 (G^r`)gr:=w"U}] %R*-@8!D:ꆉ4C+#h%*6IIywyIRu>7{6"x4w+WUSˣϮb:k1FYf gXDy=9wq,#/}#]c(@}_C k/~d)׵ DFOnH+?ttcfbY⷏ڠ=:dR`?2` M8`t=irxܭEZ4ڮW&"Y Y u\ZߒB>N)6|s -nuqcOImw;bMX3C&c@~8k[?A#L3ehטy}8yg'' oUm wk9,f፞]e~h4.%jAq>f?w[$9V}q%1&Vo0P&j* ʋ !obg<+ cAuME7|)0%`͗N:r>Mq{Q:da/mɬɷɧgM9監љ2Q(F-Rp/`,g*\CG4_-} >沵>._Ϛ9=r~av( _̗M>j|y8>m1ZԞ#h㍀5iRQp_|%)wa6iK"< ZbRZ?C]d݊Yߛ1)hb3xA;¸_cֲ!s*)pݤ4ϮzCؚpiῙ#UEY l0߷2uՄ UtmHČ!uTiE p6 !Nzqæk*|dXO54INyhpZ{2(1#unr!6uX=uP!0I 2H Z*koF'ASܗؓz).vw5ߒkZf4W&ʬ~O^^1VoNϑE \-yvͪG^0P6KP}k aJ$>(c-mԷ1X2MUl~y5:.P{KjhME>#_R$iWeG5w]Kn` J&fH}S}buǻsP4]B".X 9j0+%ox4ǴMBu em0ވ, 9C:M $L -/ϖ]S -7|]$yzx$r:xyW5]w!=lNQlvƨE]iz~2 pۥ͊-!8b89@^bʛbhW&~sKcwF/s4 2=03ZśF9.Yz~aI;lפUŅ(lX´}:W)t<H\¢PkmF ^|CrY _Qm(%W}KsB% Gbm%~b&=///}6$1NBN~@-ydya`(?R|Efzk ?\&ҽG50ܧ853^]Iŀ2 O3LtgnV,bc SG.jd 8}cK܈-ۯiXK#U:ÙZchr_أygĔHE0 S|͋T aJ>5A'yn8nVy5O )RJG  D_QW֐ia^Η$\@o{WvNh`#TDBʻ-꘮j k1{-@L:Μ>PqC~e&W!%4U_ `j4 gS]}ʇOU\A*!>4m_$F|\(#@w{iGH]?fÃ-mg4(xޮS47C&_.fg;<ڎ7WOnq_DY)bs{T˄ ^yH|n]IUWd_zq4 3Zڷ[an=&͸(x" %cbHHa)F5RJ ܲ]V\*WrVus {PA1+EƖC#&!c@:(Yh4d79ngo#8Q I鑱F$*-'&?j!ҕZͣTD;#x!4^amvtI=D+p524E^`P\,C#۞5ZeH{CQEr,Lj=mxºᄔWVß3HeA4n!&EIܤL*zdWu۟,8R+'.wsƯH6k>2q6FHYpi)5왷q^-tc!ፋ7wnׄO(>+Oro`8pc?LH~+a9~e_iFe/vy_W(wo^ ZU’<ڟw2U:joW_!+K%6oI}Gr*@j@) !.ʐL+|#z3 wt- RgN([rwUaVڐ/~帴RΛ8; l] #* ^_0C!JnB=D$$?lc=DJy?DvϳNrU[)?D{?%ySЛ@]ҹ;WrWyw=zG96ZhbaH؜bS0B0%XGwF@>@S5 mbŸʩ$AC'}kӈߣ|Ĉ !+:W—WVIۧPт8S" Nֳ7Q!a;RuzC,X1HY}0:Cy=fqռvCAf :ѱ+ s~r؝D&8NLj iP,KF0ǹK"oȟxpPSc2%m 4" `jxQrΥfk7q놄c|n?rb[򝟈Wğ6F%ۢᾯI?QdD1Vg&(]@)F*uQ5 ':*<IE&eNC>CBV=SpwnH\b,OK\?P82l;[L̻*{E3 zIGτ 䇀$( #B6[^! Y#m5oi?󳳻OYv W1v^Tah}K3*jS榥/ɩm/r ۙ?(Lڰf?8ZtgF=LEpU}IAB$sJl Qr?Hsj쎛A:˔c]]Jl-QD5ckNt dRvCQ1GEqx>m*v诔61pGtyl/ ݌Ikqt 1QHovT0i#d̜-;ЎUx6-\JA)^֛)$!BO3ɢ|w9~dk^jR?8XU8Hd𴶟-wY{--t7c|)zrAwӽX)'=0ҡ΅z*z#[ɝQTmsx9@1}sfv#&1"syCv\D‰[ _dCMxOAZM–\,;"@|kFc*PE/*c1;/5H4ӳQK<^]VAGUF$M;dLZ0/k |MnV._+]uܹ~H3 X+{vjbG-rE'JfA,EiH,P~x8 iJH NP:юmϫnش!TJ1)!"纯e '>"@B#p~KvV~mb2:X7nTfO@ý,zwᷕä7/+GP{3G|(uArjIcAUxR#k}!ZF}S `@aX$q^PMR?`DP4ϥłs`\TX%{PI^R:H|)a6; D|7䨞AT33 zuGÿpIL@ #X:f&pH. Je3E] -{AXGE+:`Nt+dwJ[84 0DT[DYszWΠQHn|HmgQڷݐiPx>KANg嗴I&0iڌ7!ŚI=IhZ5|+CkS:! ~iLʚW7wKyJ:΄HQ!X6(1f@UW5VaTH OQMhQdE\9b_0RF#،ܦfco8H[=ϵ59Ff$}4͖H =pBS_Grۇ";W$LJy|f%VxhnV_jli>߹O/ f<6ֶ4F{Qh,;.ۇfT9&qUUeU'8}G-&y =QoxEM&qK~03cP mkVj) %Lq4:4@=b`*Ylk2zoٓM_9jlmNl(jܕ_uZ–{lLp.p H5?~RJĕ-ۥC,RѽypѴ}R$ӵ.@`@F`2&IG؜^L"d(Փ_]&&q&_6#ΝʅˌoJ 0WТY߮(nU6$TRTgOz,bMLܺ OVs,\Ĥ~qH{4P2n-LhC#^ 4jS`%2%ޛ^nS0HdϡX@V̀2{POZ=ė8YAEQﲕy};Q ^ԣr8LN@ȑ j9T ][Q*zWڳu!D2stf2,TA۫/2?R.*nY{nL+$_= `LK8˨[X{Yi|pg*03ojT@p 30/u`;g@jϩ6{ImR.W/dZȕ-K;K9AŚױ6NsY@ Kn7&bsE#ʘ{ׅ~Kc,Q42xw[1a'7K5"<ɵ̛1} V} B.NSqsi)'v_߮/ ^m) жȆ0`77GVSo{]#mԏm5.U_NO89K@6ÚQ#O`{',)ӡPA]FqdҊ:iAji3]⚑Ī[cֳ%$_&H\p/~uҿܶX?1qxEZ5%V0cȲZŸiLoޣYv8p9BO+au٫$h nje K")ܺW$CV\E5=s(W/clJ+ac#wL06aUIJ6 MG|!t!,3sS&GY 6BxP}XD ~5{ -ߓGJsr:2iS4OZ2VVRӰ2C")!;o jk?3G~o\y %wW92KiBj?~aq(H/*s]!LuP;?tBD1K3yYr>H7uL#[ّ"I&Hi\W&:Tԩrt^&XBqTszɢ\v !Jo_UL岤7Zͬ[E6R1*fgSdGO2ZM'N1B:DG^UӠym ^Y]ώA:I`HAtG.m* MYځ8Y ؈MCO#9׳e!}Dڰ5ȮAY}5PSlUa'nM3}W=gv4=~TJ"Y[G!ї]y I8jUEwrvtڋxX['H(Ҿ?09{A$݊-(m;!PVcU0Eo[<}wLK> ǵ`M˷K^/Jrb@86 ݨ,z,|n٥ivOb/xHN *]hJrNPZ|ۻǕZ<24Ȼi9DߩMU%T^<ԟv'lNŲ\@`7,/*>b_{)޴_!npoͭT {&AMٵzDXs6c9Du@G-5p}cFxw[z. .tCU@РMJaFHg"䌀x)/t$7?2ؒXҞuI o_COӞ-^ G9۪ٻv-8V}7i OWX>TɤI]ʓ<jۨ?Xġ:Ҥ->i %-tN" M[v؃AR"{J흞@Oa (2ԸlDn ,F:?O7.d Hҟ¸v殢PwL)QC xդ, }+VS!©#Zbj* J>ۏq6=`INò꼯23%utb01U}}MR=:-Ri5Wb/Q?l/`Y>I 4v@MzQ93a^pBmy$0L,th$eI*)~]^&ZV)7=%3³+EVl.,͎bHwyp9OSWç'alCծMPR &¯2ڿ9c<%`Q5 3 m]Gjk*oƝQ$EY*P,RȞbP6y:BAUAH,+aT2>цyx!>󍿳[Ԉ'Y1`VaN"ha%ٓ\BphfaE7w[o)2hT{g),(VP pfy;%IINآ$ " %Lҩ7].ﲃE+L:|8y}Q -CFČ6e% AIp`yAt |FYnH-*2S+JpzGE̩OZ>3u{?OW@9D]qqS+LJ4wN;&hPت*ܭ ttz՝Po{.7SyVv}pmhosY|e5Hpr͔O ;_x V0u -u${$0Ar"g0Ud+ʪgsH/F|>1}Hۗ{jZ(_ Ыg/kUZ: ֙6^1{zC .H zk፡8ԬpM.h=0TaAet(@(0jƶD:v(BM:HfW7<'ɔdB^C\VmlB(E+`p\mW.r#͑Pm57C2~ggaPbybRmc"'Xv/0L]cDŽ$l4}`kl:Q7Ҡb:fU'~m If[?is{Jf(B. NJ,vH+?/rA48;Yc1|G؉3K>(iK({ jԛ'A{i!LJZXQ 5ź2CUG_Cp_ \gX}|[1i2>nLMY Sp9qy0rHz#>pF(nϺ 74kz0-&q(yLV(w\.kh F7%0S |W4K9+ΑlߚC{|J֋ΔZd Kd&/|DX[7?tpr/="Jhޖ/_Y{'-4lH~&mҫO՞mhBGTN]>4#@opTIWL6i)/N%|?Ӫ}KR1B}Mo-/Qta \;[5UbXN UMa}S4I;47^C`JONqk`Y<$p5ֲzsٞ/ehfp<'RL:@S~| kU4nn(wʅA%TάF.K<빝Hlk +ІGE6R0b)5S2 j(#z0DCh; SK^¯NZ}?9uYKu0rTyrq_Elr2_fC԰R蟀Q9$BDbۘG!2 |qu'sxMWK?%` 09\!v]D26! -Za \e^YH(fT.^ʶ $sM>n*CknҰtw!˯=0`vk(yQ[jUDžYq%+yޚ|g@v!S|h/ZTRy8֓c`/_rnqKR*g딊@~JXD 7m^+$kve"i(ED>Km,ڏt ^`a2 c/L FӶR8uB݇{ȠV(3h/E/ Y}^&S~DŽUUIù{FBar,fBnd7PxI+$'IKnyK7+i-@^չ2dtG$(b]P> 1MɎ A¦9)hZ/Mg 2gXPʁz;D/$`_&cXs+@Wg d U6FVLV`w-u~xzMj|PK#Ӵ6譻c߃`TH'|,ܖE6:JSF9p>F`[\{*FSY|Ô|\+cP~`" (V)-SRڀ'S\PLn=⽞0wڼW"+ݾ/''2lLBOwܤ R])} ՚.`B,e 5uVcM<+ϯ#wWpa7A FqS&#@/A0PyAp͗2bML8)Y ǃ}yp-wN# CYٿyC}Eyiv6%jžU cUHV0ܸ}nzSvpn״ R Ae44 su;5ڐi;n R G$) %3IhRcSSpDŽhCuEC=5aq-L"tᯊDj!ܙ˚=) ,-w%?VOVDү<"sCzF.D0eCkA0H 3 6"v#틄JogP O @𿋒Oh틑ҐW ZJ̢ ugB%9蛾Eoz39Z?=:, ,] 4TŶ~bKAYAO;hw3#ԓjGdP=tpS;yn:*@D-pɸNu}k31 &+ 'OeU?_g;ĦF 1 ۍD(ƢĊCoA%ьFIE ]yh7o$FF%>d4qH`_ͺhC4Rb8I? 2dBZl)Kc$B|PΒ V+L|Ep__|Rw1ьd١*eq^S/vDެ-`TY:ɵ^۵+@ „9cghHu>adX2FMc^*x ۭB4JAļt5}(\1?(jt~-7Ŷrwp>U&YNa+ ά(lr=^kYn 'ns3bzOЃG/o<Ţi6R)Jү7%469: g cGL7Oy]疅G31ri |p/y y_l`J2@) ز4R2<S%b!(TP.fae(Tr>{[qeJXcjMR7"*4ʜ۩ω4{.@B,4rZ+fvp@,L3 YNˆ% cZԭ!gxO} TL1[Ӧ"ed-& (lym&9<:S{PX@ Ok*tt^<ͪJ/ 7&#osuPqo* " 7w m)q;uo|* usi_G.sNb)`PdHj24L0 AR!7v:8=JYC ~J:;(k^Ė tmT[?Q˅nHf-1"ڦ'FvꞞg/Zs!= Q\ZQwΐ8r7dƆ&ԃt1Хx!S*1d<8P@c#Ri}l+%0k!hrcg|lѺF9YJߵCoy'7TRWW5mO4n<:8 d_, zd'_Z׬o!iO) Y)x:؟aA;^| .-&P"ݟXw$Hrgx!FՃEF7L9 x$_IJŰO噘i¢>fDw`q'Y+WF%_!YS@r^yn,HTTEQ7Z[|Rm݉3f/X_XfE:؄}fQMq"-r>4,̗FKz^3ỢlAQڹsnPe赲5B3LO+DiÀȺ sA| eVRnF%T,aĪsk[QS~m-+M GT-1ʾ<{%YfߍaI:D l)d>PLK}G1z8xƟ:Y8RJtu=->a4˃K.*im2w1ɞ/l2N?>MxٕH bB16Ս|HAii&V! Xg'Mʳ7[9@3۔zm B9* :oIǼo<w橺Z4ƭ? ⽘apNyl2oZf?6 5Mz3ѪUQ5E%."؉2]?'Y6D ,"#^-ax>TD ܲ'/ x+ӹ |aт+>H{ȀXH[S@Aվ"!S:بV!K%5;K5b./ޖY T|̛&YkBhnc0rȘ;\UQ&=K0L3ڂ({@p5},i[g&٘YrQGQ @d7 l-⸘`ŵ>K ~$iF9dRW%89\es0BF\lz¢uoRv˗ֻ QtkQ<P>;NۺVV B*ܳ*2tN;}iR9_ufl0lWC]b$'b F䲣UA0to,y zkX7` {:V^Έ/=U֦+WSsRC ϸ p%1( , CwF;jeNw|dgIͼ8|&)äFx` v/'Xc͕MS'AarS0D4Wuғ߫?q&-o+9#cE5epM׷%-բ\j6F. )cEu/~Y)t4qUJAkKy+1N M-}$'1 Dܳ4SGtwg @괍}IcV /xp~$ {K,X>}#Uv}?E2+@ZXӃAed9& CQ|{_<9Bi."Zs{$ (]VOyB\l\=H-)5S<,2}G+`>~"ZZ˳BH1+ +)r\as6cj'3:,Zy[@ߟa..`djd` gE:Hau}hzCRRzt:t'M;]6+{4Ucap~8]ZRdiE2~dRb:34na5DP>r .ʼn=D2WGft {+l0y>@ 3ᐸ-{cBA{u{-NBT`~] `K[[uT}uƘ?.!im3S)Y`ɲ!&va*cK)Аjۘ2ZߕVR\)-ߗ"(,HI׷TLl72+ٗJ٢=!lq\9=' |\4ۈSr9ՔZs$n^_A@pOMcwQ,9y 2PWUkL}/j >f%.tDzҮq?~*f*X VQP.hݪ=:IЬAGXB+dʒ=j"4-FbDIͼު˸j Rx)|S0rk;Lh#TBWަ2+ mbh&$hzuLXX<@̯B](%^ %FnJo]R\R_e4{wxవ}:ԀS~ \(q`,1P™El`kw{s/o͕|ǭW| xSa'7Υ%x?%lbHm-{TPĨ(sԮ&fi$ކ&f (4iRaB7w'?wTiH |@SBH㈥OVKl҂k e4B>a۰ M\kŠgT9Pgmκo}ܸY1ߗ$Y Z,.mX)u l'\qwJ* ,FgQ |`aXǽHR/xհXuP* ]DS 8w7 E% m亥5?l/ԩ8t*c<_Y-N[e>&*/]!)1՘9kh6?`ʦ֪#_BDuza~ D:N֘"wک2ǎh-:#Ϣ5h¹y' #&TciY5^|x!<(ش@|N*kN1Kmƅ! XudԱ6RkԐ:&4Q:)wh.8S bp7x-úp>,d}+"Jf:Z8Uk_QQRES k!%#4ߵQf;*}H;_N?N+n&(-MNh~X<9NixWҲCޢHQ4w=79\LC餝Jkw|ƇT#l Q~w1k!La ;Lj;?0H_-.p8zд0noSaHt+6$M,[J@u~Q\u[iy2A\p;G h>nPյe;;CV /2C՛E(ӭ%_Dm K؝7p]%n[m\ӖÌv.w V瀊0]I˦9>2\EqHxXq ^T~!<',C3 %%S[8e%tĕg~֬dePJр- *9þ=K/[+c3G xCUM[dekC8͗;ޤ*DVf\LlIV.>anMJрԦlaPFyM2sb z |FgfB!nS&٦6S?H;( n"QyoT7q= ZVO,ہ[J[ h,}W㮴>"JBtpP#Jr!YGD=M \HebOC^0wֽbt"ĿkaN >Icaړw ֘gF !,|s1~]GBCfu)jF :ڦ |k";ȂBYynD_4Zb_2^Qt )</=\0gz7XYe9.'8m2ٹjR8o~J.F., H7sm+!3[`m݁+vrNLH4`fhֲi ʀ)VTm^=[;cy=Cxh~fң6۷?ISJ/j 9j1Ba3؋qo5.=L,ܝt3,43,@c,*l<p VKZZYiJ"{nIC"| >/m%̋%-&oLRw<Hr3Q&r`oؾ֐}#czg8q}`r+7(#9%X8RG|6qRAe8k`ψ} mR^ph,1N ćS:O˙AۡY gOdcqTGANgv/l84Jv(fS$n;Qy0߭r!v0WQze ExM -ҕ)_ӀaPLuV|Ie]zhwg&˰mĮ,>2:.*Qv BO[]n<>y+K~1+0#0wT b!v fBnkf(),+^xțfMS/=Oxɖ/4%ؒ=ƍHSk/ N7تtcp,[帏1pmSrcr@C&SY_#[i[oDHY*.OKS0 +7 V%rk% C(Ty ;CS le5m S>(~)a}eIg9{Q.ikpx۹#CO=5}x#U3tF,*V r|=v)zYyNݡ?La{:-T>_jd1'ŧìYR,LETfD;Dݓ J1Ւ7Ps^xƘ'\gg ~`}!]"3Y?U _#,ig嶨YtdIꜮF~IX.CQ:SVeLD,EbeҠ^'oRl>noOϾL΄hRbڵF,ŏګT̮ U βCJ_FHx2IF"ی@ŊlBUK2iCvwѳ6\]&,aYĉذW(, ̳@ܵfymBƻNȚkE":5nnz3O@(X3d%yڛf zoP0!'#-ٰAC(>x/cRLN;PY혃_GQJ4R!zkɁrϞ0 ]LPnfJ>,wPFgCO;˜CݢJ8ɐi>\hA2Zָ)+JOVM/qlin H(Uu&"Gdڏ D&.G~-v"N ߿6a6U 6hdjJ&cgs<݀R'Ke*6]^QGjTLJR5@~꿎Jc*.cYeTw#`]hT<7769' GEa63tCzHϜgŞ\ȼqtN "l^Lw34|Bl5+q%`y[4 2V(Gw3AH. p_F+ם)pɄ"m}u skA=y헳o!_Ri~["fm)!'":kwΰE KDEݏp̢+VoLƹR(~p pޛձ:ӻ輷8cmIKKwkb6F6#m#/A(/x;3kg78$R< .Lw;@Zax, f7 ;>ixzNrh2>W@ {\ϰmOD8}^̊g1H|8p4{/R$(ht& ?,&:[p_+l:D(>G!I5B Jou yEC렖8i3ac .L2vHROn0{P&|:\Y椺V΅$Rxͭech>/ x2pCU5*޸+Π4@~ Q}?~-_xdžq+7'"5ut5+, 9Wk=yjFyF ~ ӏ!&Rl]fwm,KNb\ Yn sԑ+ x O{E@: 8?.\эi!Uh;P3'`x Ȍ'gF<2&5wF/&I(M YHy'7-6Qx~Vcj`A.|vWQ!L%k`RA_e}Pm^޲dkqRh?uS.#cJ ./QndNM̿~"$_kdӈǒny7-T9D<ۨR-A%48̵r'!ى?P.dȕ%)rAZbg80vE&Gp熶i*|X%O1M.Xf@6k/4ƙ޸QܒI4".BpTH9^:j ЧbjR=`{NB!4;Ÿd+uT>yBtwԇ&r$B3Mf$ pO~G6,s, o@ڗd|r hLȢ0* mf6*F|R#M$h;vry֘L fh-cT,4']k\P[+ zOްf H* l'Z9m`K!`b$'Yw&'\pyR2\)ӹUG$lXJr W- x 8U )Lp{ܲ@h\ͩuv0'ҵq@NꃸBU0-(k|̌'jZSCK|W:9̳,Q{DҞ/Exg$.w$ 56S͂ͮʜr)=ۚ̉)`Tn {j mOn* oM'k!8H]Zi gÔbrۉ> rSOdڜJ. kہI \V(⩔`E.tCvXG [2!tOVYT7 9X Ŭn|,EQ{-X#N@} $9&R\;^ZQuR,$UoqI@K`{EF00Fj;F]MAH^qSvXߧXf S]Iܥ4:8Pa)V J)ǭg\$~:X@NuA}inTn2{"W4J$tBzL43G9QQ>Yt!mM`e4U,:#YCh[xڤNxgYi,v1w+G)"ONzhj*ue3YBo~$2uK1 r"kW껷@}c?ƥ@k<*_Ib؜=๫#mJYL5S a@*KFs>0^$߯r8slڻ=&g?.6p6DzYZ<ސ7UuĞi Nw0eUG0{yVoDeoS#ͬ(_Kuw2DwZTRgt1iqFz渻X@W2k%V|Uv|n\xX?_&k{ ]Wg1ƼPxaTpeSAC$"od(,䣌yW< #wbhf70HeoUطlbF[IT۟]ΠYwh<3H}o+~Q7 )R^ьvǛr~fTxt$ʏbl%4HQ#jjo;͍PAK)_g?"\?vKwNjI8kor[]Z\, IGc?!<`0'0 jo SDӌ0'y|ʌ8]xq| !X8jYF{*XhѣmG~ ׭KT}jrC AmκݶIt:}D?M+H_,4t{˸G\Rygf枭hk7BIЀ*3 Ou ZxYs?W`% OX(ק6/5t۠Nĥ aec%w]'0ןRHGV젼s]@k_To*,}j%=7'k8ٚN"u\2aR 7_laaIa=N) ~N4y m @}ODNH grbRc<*X-J ǥ@Ν3C_!De$ I]$jOZw\ڊٕ:9v |B4`x6< \r | rk`|OZENL-H:I@,tߩ#vjjMԽG&sXVXǮjb`ߘ9׷6P$s"zh[V= UYU8zLig݊Xㇵ"$ >Kӗ縫dKrL ąE0;\)DKK#H-  i_Haa]G>ւw+.wm²tU(ױj^t=4j.6vNHJP<㮆T}}`~B3*~TNhU_K#ݕ|sG8E˗m)}mrO(Fpϙ['rv s%YYK!jByWg\^y̞JkauRVf!9谲>i~}pZ5jgg0Y?B4{S+9e|1<Мi~@BF+ ) ڞţqmC`J hǜר",$߬cPb4Mr:v&/" c;.4D_l;06P=·_6t*^EWUr=bcSt> Mht 0Ol/XҘ۷ ;˙ȝ_@V 'h<>} TN!$RŠX³sJYĆK1j ѓXK ,5ToOX4XEROR6pvkGR紗ޒ,t #mnK?VOHx@S%9Z ~^] C_ǸJ\^yų iw3jYL^ڠ9 favlh}1=n{VhCiwEAv9l1W/r59d3v̪ P4b"͝#sx1g[^mY8r7QA'0t`~6C p#@m kƞٳ@˄ mc >ǯZx_U͛X:[%|;DKkk a}‰M32*\4JΝ.)U[5Cl"c'ҤBjlL,Dʼnɿ&74F~}%1H}_$;@Dfy~zZL v\A厥%fR ]+pUЕs,?i]m|\B_0q3!r,~WݿxuO}sGu^ Q kyก9p(EW+JH󪧟 /s|tdKY$QjޫqrU4BKP-GQep y^8=\YwOVVcVGwZQt1i.U_Y-;/_ Hrr5>MGڎnuDA)`ÀMl] ,;CCTхwqQ4JMx\dcVW]x$0OYSVP$d}<}B:hB c TE+o_ a= ~FI\'M(Bc^*qRҧԑUWoK@atߢb-g%>FkKāG ݍvv|iR>*]1'{K\oO6H1AYP mDDXJ`j8e#x&}AHwh(˨EJ@Y>Do~>4Wm\%7|iR~ui_eEYIi0iG@ڏ{ zIuC=8-PdjZ=D8q\ C>U>W'%Qz`IT'A HԚvwa'KlECL޿cH "%۠ ~[L>fA=fWVRг;gRS+c5WN.guԃ$A좾g+ DXsUZ':"9@70SKigvKA *>e@Ŷɚ1Sj'_;ZikpL e-UR k:ܑxvuO^]ZVꋐ\sZfɻ;b?g "~PSkC["Uh>XHA>6ujUX,~y^='+2ɧk.U_jl4@R*{zxme! X&ɨKƏMX+R剮c':nǟt߂CO&IBpe ?=9v~(I0RaRlNyi<>._cف`Q58I}m[ʢF|d @khAJXJ bJqNnjw!5uM1??%!JyƸ> J bwﮣ3:.=ehԷ?CL@"Ma &%P}RkUٙ*&gK23έ0RJ4}ֆ,V{}H{TMǾ|'IJ<I iRdV*#+]xS6L3A⣧.c<&"3@{J$tn?3j55%Ƴl6Mf5h.]Qus&TXW'Cn x&fCIcs~BqMtO0O hT+6/9-ik `ԀCp(ߜn2QnF2D$e}u''\Jb[YL.ʢ7W8VW :37lUw}G,\ώFFZri9V"zA+g$S В YwaJE 6]в}/eV >e7~ C䫅v-3^$φֻ *+Ҕڒ:A w@u ^iy`#n1xjirYv>IсtO- ]uCN |WY߻qF&" \7)v2brBđq-/N…-ݸWcu8#:"L[HTΝ<۶,!K טaҨ6n􎋬l]6) qY_3qZWbzdjYYȭ|: BMk02"?f,NxwMHHaO׹.Q|ϷpݬvD:Y@y7֘ @5%]ݚ5F% ͣJf|l";bYf,fiQ|m!zRvwH=3`"Ruj5U5 C +UuGvPqyЌFXL+7,ZNLՇشG(570ڈ? qƜ " B \Ø6`?'tڕ%OXh׺!uRؔ!FXazBpn6'VX=1RX^id[bp'>/0CMS!TNS3]3[7"~Gt)kIr⫴x-/"U !!Z y²˲x2Xsh{綛 rE}zc=L*[!l R?G1@M#\K[ )[8@Ҙޓ X$])+ig0iuYd,W 9 QkDmT8ITgIpqS.c<)_ˋ\O@W|Ŧ-%XLP^KO7jz4a{^ aμ>Pu Fk3z0JXY Mq_pb'K )t 4d5V"&8cJƄM6h;'P쪦xn/ſh1=cCC'QD]Ĕ/7J.Ӟ18ͬ[(c(9qgK+ a35Q7qgh3 i2g3[IE7JOsEHLT" `;>lsKkPYT8\xDf2u{y#0a݅(]Rj7OBcL !l\J"16kK0Bj|Gky 7=kuynЂ#\G!Hm`Z"q֯gExV6 p`%Ebߥ $נ ~3`Eȡ*?? )er^ l?!00<0dpl^'|u:u:fKFz4,tcF#KO>r?nf} =ÇK:c;UE+q,TsW )HE@+$y4mkhSV ! 6zX> }]r/5f&cat_ '9?!Xc߅/5bnhhS;v@'^(?S\FwA?"g+Bm0 끶:u KFX^W;=&MntG1j_S ^P3WFe0yr(Bix[&E7fX䯟H]l YՍ.ck`}pm:6]U\;ýLU7i;ᎵsO݌#b+j!RQ 䟈Aږr*FU: ۊdQ_>xgA =K Nlz㡸&F!y{(Ee :'QSVZ\oᶭjGܸP `OglH)'mq>3VHtb1;rBq6;L݋ (~$HV+oϛ;nV%wJ4KgmBOR8.ޤz'XSc=iV;} ozUmj["ؠDpDT;a<|yg<ԥ<n8ש`6&TZLe}q\埦]Ł˴TI|(SIU鸱LN,* ӚTLMIdo0Bovrv?±e w6޿!'I64(mE!C7. MzNY괺Y\c&[ ;kq*2=IYK )O*$+<}dXzĕW `EN,$]+k\8(.#KrNjw%A`({EZY|<8kT#O=@"uF.(b-%k?gR)C_WܔN1wmAKBN 0yd:(?.ʣUځ|߮ o7BC(iy{疪DL زנyICŎsFL"l#mrld|'ΩHtwSD3gRx)p'#wD5s"rz} >(iiǴ V~*a8\LC@\eRȢdꯟ'x sDHB}}ܩF&L:meb f4 0(Q#+ AD}ᙌlM qW4҈N_ 6vdi>έѬvW^^ RO 3bc= z562DC3Vj=8T= t59l*V JJ#`}i:6[֖1qՋ mL~HU7/y鍃 moT‘PMI{d~v_K!t<$+6b1zލL(aHo2~n,l0qzz,V^,B)dz&) -ݟͯ Nq!-ߙ hAdqV|ZII-_3= Tg7hVzj%:;vbl'p_Jj+ֆ?Ρ9#֡?7V yiEm }!=rƿ@r9MGlHѬ"p"ݕ\R5M 2aiY~Zrվ-~jɸ G8yt_Wr\= Y$cEaVTELʙh*(5M߲mJBRMK*']&51Ƙ@Fa(oz,9ke^!֞b V[۪j>u8{C 7ڲL)Svɦ2-i=X{e 3n*:pC eWKғ>ҾEoY_A; #@gCU PǡQr=nbp]rsID?zUu j70_g)F Tk`ڱ$ҹIo!j!û*E3%lm !jsB?}+stq8 A[xY34Q#En0l izRۘ a<8UWLZ6hOiٲ'EbFqB"EfRƚ$o!齫ӈ[˂2ވzIt rcFIu{deJD'B֕6)3[=V:_4eeygYO^AM?<(Ұ=gjME{ς)ŠqeOf0LbwQ .-Ĵ⅑Hsa} fEi[MƱ Qu"e[M|:=m-+OJ\g3,fv@6HH 2^\qy!.p q,wܒ8洠 .v=VMsv'm&W3L#QECòFN7kݨҒOT%.CBSv*b1EbN✻LQ>H6vay^Ǿ.N^}뜔.Gy2i5R\?JwSjuH!$~ɊgN|-P39 g ?5)R)ɪ#2 L;)7b;wmݘUɌD!S}E8BwEOn5/I[ w[͔Հ 7chŅF+ZǮ0 "p'x9( BuJoeGe)uaRPr3:JWT9x̼ PHԂ[ƒ4J("k5bM0N6yϽNud~堢2nsZ:2W3[bfւzu5?<;  o6CBK tU_eW'D2>uteHD퉵AUJ껗[l_I-!e~j1ތw 6?LPZF4!nM[)ꨏ<`2opKIkC/& 1,o0YS 9:5wDm =/9yև,M|w*V+;tܹ9Zδ 0Ķ`qrw_mH@ٶ_}CMTD.-kp;ʹ{"et^RmFҹji *hKYo۩JKүF՜ճ]4[,5cȂO+$7$/z1 ƅY.0Q>d,_Hn5 $$?1/TAWE''U;/;pلOWY@8@TU֢.[H&PA'N'P 6ՠD$\30:vw@=TXB468GgX([B&о2StD&E!_5}j!5)Gy3Ds?F10/莙p WyӱR%UӜf!ѳ}k+ZڮXm \LynH-?1ʺ(Ff5I<\`7Ľ+m8`41˩+TkeƜO4?NnOXT j >֒K[S9H󕴍DHnOAZ1|ݦz_{RFdL78Θ.+N&GDH'@=^ַ4xZNMu+v65vrBAH3F7no䲵]n/2 오;υzn4e6+9jD,khs3 ,9\A + ҋ j@:hF㨼 WfJlX^DU_ҥ1zIp@d-h0gQ*Vt==V<_g3?x>Y5g>sJm2+;WI'6^K20*K5lԶJgD5I/+ h26l1WI+$  쨷8n ȏz*Gjk7DP/~/AL"ZVg+0w&U+iOnF6b{F3TZn~pCT{9/OPPdDP sx9jH|!hQq&y&Jf=ĢQdd67_+b =AnLFd}YQ|tۢ._zA _|g*j]1 >]@zb>s|$~:,= ̼iک:"HYڄ5&Ώ8f 1E|K>7,dj\9/QXQoq‡[)(e|SBܘ8[Pacܗk㠪\{` )]ҋMqiA_F5+ڗzH㏖pZgwrJv9#K$D2?RL|Yi`.,5Ohi9a(!JFVrx:jQēHS}n ѻl1BhK%Scq}rb]=Y;ӄl#Ņvi3Ё׼%BV}lv,H:a-Nn%|@ie@aPy sb$f_?&O+qW̊Ƣ4NͲmY۪߈dSjX6魷s0gVSSTS,gtrpBzj8nIq3*&Xɑ\SpĸcZ;(̤awݽJgj 7mݛ77[ACe8饎 Kuy- ^۸v2YB~D9Y n'$nV v,| 9l@\R/>;)huQ:yNkCN7];c~/FlF(T.19O#Qe%.s"QX}Y_:%߄M+fJqe>\.Fd#YnڎnV4*+%3N_4\4>K P[^+Zp&kU7L9Hz45G,O_ ?[ lR6CM'sOo%3 b Tsy.}!ⱦt4Dݼq,ZNkEVER>#6~;hT{uRoIj2D")iG\\'9٭l1-#l9VҪ 6#X'W(KŅt.{QAy3~I^\=:5¿1͓j?}SBIo0{Ą Ri5>*C)@Uv,ײcb1`xoz#q1s~PRj mWXCM½`/9R@V v`Öa(\:!˭ /?4麍 h+ O2^b/ǾC?swVK[Z3#wK[Jo~TP;ˎ> }Y{9h98`~}S+(:f]|_'IL WX ƶ2^d ߫m@ՓG33UҎh*e "L=Ȓ׏pmzd@gxndqMe/bSOZFF7=b\"4~ccM]\Bcئu^ u)w']0 qZMh+v4x41kD{=UدE`X% CmϼS4$t>7,H~j,VX 0PGbuf8d~奂-0<{܀]N~No |8I )RQ5G`]K<r*9[V#(Iac m$qZXAAVz::3b0x"(F*rZ NMC! ﶨݿnm $N&E])U\A&TjO* P난q'ԥø?u3ND/6b5Cv\b@ں /8`*?  1mϬA(~%͠8g@H7QZ YnZt˚u$u"REqCׇ @%Cp *Pqo9S&*K\AxE#Gn,Aɀ%0۽ cQlv x}eDMn{:!,zU`:Ij=/t+23@?Qt)anjгgrzV!HUfVvg0tvޑ"Yw]ǰ"qfεoՋ5vaY{n'NEXmۿ8Qvv FNY[A35*Bm q>y\De0rSV3Ы&偤HY1`f|˱(]-eԏ)6bgg`S# "([MIQ^qw,!OC[n="/-:遶)MRf<&] %Fw1)S!W^\8\Z8XCyU$x[%}IXsY%Z@@vv猄ȕ\(L?`P6\F,MuӨj?z(вfTd!UXp#畏,j=U7&U,s5 E0pg(+{=,@.wZHOgř_kq7S ^btW8RBNWR7 7r\EYTC0fgOmGê'4ϳut= c 2<,RO !d%ߦ\k͠|^$3\J9Si`pD0o{ ņ|+F(;MJ!- jP_)*\i) Y . U .c im&`1"oHB!B%!nqnHMXhBܴqj䃢[`a+Q.Lf_ko[׬Tdj{%ebwqTJjeSafatv&'Eq2JCVޛcKV`0"km'zCل߈?I)a'=#k M].WC v4VƊ^-5Du*ӥ8Vm~?˧~!4p3!4Roey4vި N+փ+em4"[!](:)[k썾1Jqc۹G*x~Oae-tA-uKIJANi'0 JBs҂ڢ6u#Y@6Zx4yr&awX"WB̐g"߇TJUZ}9؍Nl c""c,#y3D?&ݠ" i7.kcǯ4d$ecNyF ڱ;7%JXAji`; {toC5&1݃L3>?ZGDڱ̆g|fnoMV8sDfm`r> :1nhԜV/p+BNtDLE'hQO)\Ic@<6'Vz^J%V`/ĨIa,{{Vɑi:C e&&ġbh? ߡ'"GSE]!h RxsrܥHh؞ hhX-u/\Ŗi]]u̇*( Ŗʲ+=#\|bs `eƂS)dUc)P#z ~]6ݰ'L|+߾*ٚ;0XzNL]G,}u \kVPSnέ5Y1cs44MY*M`D#.~ yX1 ~ OԽ_0EIl?QՅYjg, uTpƁE(}~;Jo][0ud'6"/@0U.D{+O8?U7d.<8a lS>. 6nvJ0歇sB = " -.Q̕?WS8WMREn_DJ%`16)x-x%!ʏLиXkddaW`ŧciz"V4C iX 2A>s '<0kqՀJP1#_LDWeB[e_}’q ۚ CԸ}Y_߰l\KJ.Oj&e!hΒ)hkh 9P%0$6X\t"%ovdT`sFKq]uX &LeDp:g{Oz:ގ< * .|8p+pZ;C2;;sxZqQARUt hL0HeQ15iī |\h\&ˣʬJr&7]`ϐ|Ʃ`A#S쳆l~wNb7?\>sdga d `#s5cpM՗rpRRשOp2=Ji*O00{ʡLƖ6Ϡt DĜǬ> WE0єQQpJͲz~kn̥CJ#lyE-D?´C KPn 39T\|J6)Ա۹ G3\ "PZp,3NWrlrɥ& -?[YV8 6yc .<ҢFw9b{ 0>VoDKq ~:eEij~eqĽܫ=(B$CBa 5'hkr/$zH~o)vt"!^q<X-XB$$AZ0 ]7bs&'ڀp ^ s3v- qޮcB6RAj9Ni+ڱ< qIi]k|O@^t 7W]c8i]}41uS[ģ-g2ϰxN͆<B6{}?db]|<ΧJԥ!Lq/!W^/V.z[%4H'QRLyQ`HRM^"rՃf5tidF/~,F<_չ}A\3=P{J<"'7o]uADrDUcɀʳA7J}mЮMa}zFQa[ $k' \g8j5cpOᦋ=ed?GdIkoF=?T\Ƕa*1SqExќxs&~$kBɚdxx^(RG! :pBd :^Q6ntf@XН#ui~82VFVe^}9\DuVTiw y89#̭M4ۯUX3 ~V?.>-Ԭ Y^ ǡwyLBw3 ]N}la T" 7i e'נn c>؂é;rfN q`Ջ8[*n}0gV;w)ƭ>a􇆬cRf9AKiZ9T4yJ8),l=dEߝs`ZMK0KN^+^|&췱d'Jdp Tw|p4ĥ䆴}#{6#QYv2ziKg%ȢVAq)+ۍ*0@ V WPa/KtE Xc"b<_ *w%?o=H\`7tQw 2 I4(0 INֹOZ:=35$k.%FD8A;5_ CMBÊfP)2\DIIB$KHSz2AQYR:]Y@Xq =d._@l8a`b|61DƫP|p^>`QXMZO Yf-KscXEΏ`剦a /]ڨE lڕwٿ oV@uBM 7 (X9 `HTŭ[Hy uҀhY.Vj)Ze .*/T5NQ-2( $s1_h; Ա*[A֧|&}KͷMJ(] {{GmxK)euF0^5W>.?B-4rعyjtmj P\K VW:_s S"a ;p)lzLpυd E4ZoYa,wS9"n;6l䈼XH'q&Ap" $CW I 5s^Bu dgrrϳMI$ GKx5 sNd^&HQы> 3$]|Lɶq+Wh-1J#OCAzml("$D鈔juV,o]h9sD◌lh]+mFT6c% 6+U?UGw݇f[C4.ߨ6h%SWv> 2ZP*XK>xFz7Cv3j@Ierw4M|^ ~JC`@p_xlrqIRd縂QG(EMΖ4z1Ĥ^F,?b>dIrgYAN[SbeˇʱծkdM◛/A?w2jr3Aҁt" ^S޴1A>F0seG N=,x3gvDLam <.l]Z=&yYtӉ?gٙyѡb`M$7"7pRg0Ŝ_+Ϊꑔ8?*cV}B`DjIS|[rAF.a/vOex-Pg(J2A~;7o BO!;}Z$^T~T ʍr}a!pmh`}Bp}fO)w\…R+m'v[cfW>  R]j5|7oimkt8D˷|^F78厊zOQkr:q$Ώioe:b˽ QMrRaO[dN[`0l'܉# Q;tBx8(?0f̥jF x H2Lj+o;wȻjZ;Cw`x1%Sb^]_jQ5TCzXq^=0ڽ_ڹSE~:wÆBtɃ>.uc@ߧ^w %xf`4%)RibHv9~"~~ܳqubԕYa_{_ە SPfPJN%-Nc'L'cUK*msPƤ ɏS@eWb%[~>AZ{K{l ze-QB 2Dh )_Z5CاN^3:~ ]FXH5r0AxSȹ. ^%8{0NXd/4Ɍf^ ."Nqc.+H a}&nsgϢya@. X",^dcJ16o{+[ܕđ9UhSv{Zhٝ|LI*3 6flbLQZ(%?>gdT#aMCI9araRoLp59vNI7j}jD,O~|ȸF%²(_|(^Ǝ6Mn_B)]2RW(QY!c{ҎZZ {%I1VJWѤ-@s/ UjWk x#~]jB|Jr>-Vۛƛq#lp=vQvxWy= T9}T1k} ]o>\(LJ&u78߅a63qId_ܺF_ⲌUo:]ޣe YwDA [ΌHuK^=J>");͛QQjRMSv=5`LTl蕂'gduL6mK^8,eopGT^&|$>ⶳhM֞8)+ez"ݦ> :KREi04 {kf~fTc/vc %zjq !to34z-j̄PΔz+ً(][)O%hFJ  cNx6Vizu.&C3_PtJCCҨJ@4)nlēe# h⸋c\PQH {Y P!׉ەS$jZ…DF{@u6G7r(zEOH,;c1_GKU:+C  SoI4Akt^Ȟ`E7 FnW)hF0h LHg"57'iDb"Nwp ԇf_ǁ/L2l%:H|a9ۂbznvSmZĨY{F 2 A=,T͙;nC:⺆^1 t-->.\W^H-ѠOsa42n?*s+B]$_G P1ܹf~nnJ\/yU# bFZB՟cid s'C>ka{YhsE#J2+G9|Õ{^= #O/08.W2r>iB ,\[TΝ*kZ@%,a!$sh۫m%7f[t;Ơ&n ӰMZWGEnOxb.^[c3:ەG"+{Dy=YkUFŜMvmM(!+ԖšC,n;Q~/Y ^4_y'hzoY2 ej݆41"DU0K` 8*aUw2'!ڪ ㎮pؒ@Wtz`Z*-k8O".v+:C*K5 ҹÅg:pI/y.[gU*$.q |MeYPhQݦaW\L z/u+bt6q$0~5+ȃ2C$ZA(.d?[Xה,yІI`TE,\Kd6E)q8g[eA~).dl)^}n[[BHZ{⑶ԻU>@tb-dXYʶE=A)u'_y7 S3DGA$+Vr3ۿNv.13)U6qՉk@2md%OxbNǠF٫F\޿S&s*1)1[/sĬҏ쪺5 U;At㸯@XUKCH'_PHUkYt9½<6 Ő &_C1Ao¦5Ypd@Bjв +ٖ~nɋjt-h`Hno0*“Ҩ)? #brΜCbC5|1tfɀgufĺM`t{{Jߧ7ؙtM"eĂtgF' *S#<$(((Ľ*?s2ݦHh\E6mÅ;`PNVx⑜}vƙ$=8; ֞H~uDq|dkZjZ"J&V5g?U˜APd&o Nڱ0F$ D' vc)nĻk]q6.LO;U*6H ]¢C"ANZ70C@%N! 0X S BXj7"R6bfW+ !>Ǩ[b7 WN7\EUPvO~ z+s:x)+mG>]#L~ǾAT@sw-2 l@)X@Eɦ8T΃r`H/i`GiS;5MW~ [n 䘄FL.h"VΫ''dR[mk-a]@!9lmγU}x LI#7-.Q[:y)B ` [ߚe57ϛ!0 2f^"c1.l?򓰣!MV6<\8b6[.;xzH=ی_{(=|jg.)(c)}eҽs;#Yy5X,߼3ZBuWfx +e.;PhԦA}{KZ4pDb>5!>D{`WN+ߺ\UCS]f'<-Bq5:!qn~64:xkǫh+XQ$=(D#D\_^!aW!a ?oε{_lbι R21 =Ht7})DIqQjDk^6i^SW%%G|@J>5Ittr!/Qs *ݹP$B#pU?Ǘ+ >.bR+cA ~ Z LPi!\@t4fhW^YU88Bo _4Xߺ ?ZVU=6]wZ R$۳$ 34$eDy8vYfU'fsj[^J7 MUx`& FebUh'.E״ $A.5ȑJo<$$Z),n'@г |Zו^IjEgd܄ч! v@n`Z1ZO,V+fFAH Qm;8 {acP 9VT]hw]f<؍va6]Myą}v$Ű!$!#_'2X/ҟaOP0sOddgv$F~l7̡e" \+֥D'Ӑ39cPT7k{/+դ1 o;/P*R窡HryXWdB۬DsV >=KD( 5mrx>:JdmveGA#oZ`_}𛏥[O偧gy'jsKX칋[b :>enLcm5{idlY,d>\׃5{u'*ظA/y`=qWfz.1IsD>2ZW,G$3]H\!:ؽ8*"+ #)`!|Qx@kqЌh=*8PzZ \'6| rx>H ,AOyr:w? F$qDz#/G;H[y A&OB\[a%C-)j[{O:v}ZM%,;aq|eGyD1W$蜭*~n^y ?j=q EAƔ)"rf9Ց<`֩Ϲ:o~ ( R>UZl Jvx -(һ' sD0dž3CdZX 3O]!Q*v/N cN#h':l)yi#OeԿ:<R5Js>K? ]E7xPGe8>ѯzk2:hl.X5^bu:.Jz^SGxćH&>P9Ny}A)!aߊ6ӌߎ~N.Iʗot[FMt(e4GlzpȾB>n꤉+9HhjTMI1*uPg}0nϱ%ȟ/|bQmnUhxT+{?b)7"cIpJїL]f׽ܷ{0]3y/B=eA[|ɐ3ȗ)]氃 aPەP_tD %m!oZYMIHo'*Xx}?.y~cxdTXN!WQgGN K$p *YPPu)21u 6BgF߮nzAZ3֍A?Čp%@,2nY/:\TO.qAF"\b4ݫ>ŸrEusmvP}{X+ BQ&1/}rn7FTtjIp?IbuɯƙLnP7+dk]c'UItnHR4C"X/6V.B T=e$؆2EyADx;Λ3 ?]?<Иm t+]>QpdAC$8HzFI8!pЭEɔؾrǷ~_ l'OY9buƤDou -PM ӥSZfih3]{ȤUG"Ge蠢F ;5'?u/^biQ!zL j 8WO]m^KFl>eg-uD3jM?ݾ;^Amr\vh$BtH1 ?v2V 8ke?4sF}:lh^Ҽppdб^qfc N攝GͶA?KvLg%od&r5|0{e|bji'Juf}Ye" ;x4B `7tZ7}'K <KTɴ )O;6 }^B1t9b__@t >M.Pc+Qя흄eU'M~7E)F*kAmp9#Y<ˡ4 7[p@@XQ:qJb4Xu֩7ӕ X ])!n[s/a~ѯo볙I#myzJ`nerс>+pQٜBR!i(o y9|k/q S`2 }d0įiuF޺AaB:ßBZ.z׿B;<ڹĎF*6%!j}x"Pe+fAVH>* " 7ĉYWrn7-b aCXţne|_X9`{*<\^2%*RDy4}_-Rxkb]Uv~;nU}y+*ĉHT|(f˞ڡ04/Z`=d}~pg> t+B!~yPib69'_ލ@_Z[5\ *m G~@Q_ӰP`iFw J.q3u%bj^Ɵ)hXu681Zt\fQza"UڒuUc~fَEtDg& |9X9+,3ϭ>xC%9j?b`uE{,{?H.}D*6/zje I[8,V' - 0l{wn}=K ):~ ]N<*t(<e?mt@2 偶JDގ>UJ}+6z, 9xpw#}W vCt9~" ˃]borNJfި,x21vi٣[;Nk:0ӳFXi.bĄzT+Ǒ5C٤(T°D>]58zHioޔqPۨ5\ې7q aPnha;!c3W_{X[zOF+e ׋nMzw*`7#%6kSKl qFPn,QkKmǯ'۩V^xg׮;;NU=( zyn0yFLj 3A ;p\4p-4y_tl&wJk TvOQF®0-ki QFH&R~>$_RhhMt8 ]Xf'@xN3T1ʩ33 ę%.Z 6KW0(a쇥1S% 5d;gكk-]8"F An6\}Q pwDy`粽  *[jhܰ!AUn @^1 a)Ohơ\|8o7#o qt ~YQDjgZ^/MoxAS1y秊Y8&CR5_lɢ}#iel)$۳=€Ѻjh'nvJN *#OJ$l^`kY2 E ܿdx 0 X4mogT&qR-BDe{XPi<XtBAg7IfՏebD=Ev]P3+ N|vxKT倥# Qr"np${TX ^h vheq& XG0g Muszpd+';+ꈤp[0Rit%oGgѓX!#Ɋ!QؘFC:LHF?7*|ϛ KԢ4| ~L{54՟W oTg`x^SZcqI6*[ [YX! !Aj4g_UWk쩚Bkbu=0ztP.wr Z CUFfKbL gB :u6Iri01G ?)%E !$R5Q#)o L1/xhř<@r~00K|K#B1 e'+;=}(W"R?T+e4ޙ\kY!ʃQ:p}_}S1l96%βy˲"IX=p14r ^3%;;#[PZqmؽSVj )t3a+Z}vTRS7 g!0~*ΕӹP $s Z70O)wOWN2h#HT,V>|Hbu,vY:wni[߹9@I0kV+a+:F+R@aR%_!6÷W!K* F8飏(zVKV}А0ٮ\ml"@NM Ɣ!@RNSx&#*zH$N&}LvwiYm!Ug͈ë.ܜV#̧0?8B{Ty,w9e͌@Ͼ Δ29,!V"] 9Fh .KzZN(Н /wo ^Fح>[4!:W*u#6F̘g_2lism'd-Rwib_ 2;AWNM,r¼.pǜB$d@oqRg \ZՖ> h+8p(|fei$_t:23.\2 7MSX j.c1Y^_vdB"A'Yk|E}f +XVrrf,Lh7&X\i+vyܿ̓RhÜef}*)һem {N&{; ϧ{s2[iH; 064`tLF{j!,ZYʏh b_KA;}sQn{'ݔ|cp |=DS uyyU Jgm\}uP'MyTQRt'1],T& n n!@<#/]޸\:|)=zTB&Ɗ&}wG *gf]˫{|}qE: KQ6< s|IH2eG[΀ݧq=EhBfEMv(BҚ9{eVWtlV6#iYAïɖ gw}&B m8ӝC-"+Rj+}w5Y7tD +eI2>#އћHfocjS%GtST :P$ lBLחkEOneVIa=׌({|ƿ3YVƠםq eeXܻB E"c!Q|rluugE=œTvwj |kp1_ZfpEw@|(͇Nj$ Kz{_%ֶs fq-o+]:{so8OH=h~O؋ /ڬuѫb_`7FT;G̹W)4R|[y[}%`[5H ʔg;x `.NZzNxz&ZfܾkfVr:p&xbnڎ-jo0@\CeQN|8J)1(f\3P! d.‚>v]ByN2ީ|O{qWE?}$wH{ H G!T F|g.avV) RM>Уdo8@ a-q(Ӟ `4Οf*'+)㌡oRm@HO:,?q惲3h1NsFXGLn<רmWfcEZi ڸ [N~Zڒ裴VC07"@aeV>|Dˢyz#^'gF.[7Z?nSZu5yT{Ұа7ܽ7[|M@hBjH4jEo#7cW+ybg,dC|YR }W^WSXn=UI&h\QahiPYV? VѫcR~'T3Һo2̉jEQ+hE}fr|DKڎ0ƞ%Ngjk?5* [rO=kȬ>81v`[tq]g7rE&.ceZL FS ;8\ܽӯVtQ1s-6xmG&:T7@׌%Cͽ@#x55̨=%B=UG)AINO:B Llh~qz #*ZG*po(QTlgj Cu3A(JNZ:6'n Ï:'`b1_龍VltL9S5k~lc iu:-ܤka\SdQ?WqMwTM#W ޻K"`m:mqcVڗo~?5xa\A)uflߖI-ۗvSJ q-OB+3sK=.ǟ>fA5I4~9*~Z)f[Dn}.ܧ6ѫ7 rЏ;o$ S)_ \ې}\4E:r+2U'v")X P<[A28e ~^,#蔦vs2AzaTHW7|~{5=W f'嬈uYS&ղQ8+Ń0ܵ#Ɵvm'=( +Ɂ!of}Uvb y2aKCǥ]n@V ڋDi=gjEm2˺vK?x*vjɍ0pHԍEПDK]L*v@#k-ÕP V̻tYQq&t`bzO 4h&+UNs MH&=V Hp_V3O/jͣ*Goa:lˊ S%FP9J#q̓~9'_9ˢvh?-JCv*1.0ݰ7`-O8Z =+x<T.Z5- ?6sm*4ؖB/qr mkUFTR!>[X L8d8)%,tLP"-T/$ IzVRN0;Pg%w'qb Бo"#EkXT TL 懽PjFx$POGCWTBH+ aaf8;ps+gQ@zl/F2V}r/{pG0֔FF@Co|CMp QĘaz ޻ͯLnеJ\)2=ؖ8yR2)1:(*xlyt AC{c4:8y}>fjC A Wr; ˟|OrȰEi'h IQ:g7yX7F-QVVBgc)ӑhj_?8jyԳS9" Uv-ԙAl:D=//X) =[^?3q 纨3 U mnxaTt |fD!%2cF6\>@juynlϽ9|3.4stt\aHQ1\=,UFey=2]ugT˂ڇ@/ kT(0)5pM?A˟-J-)$C,:>P0r:/֙ aBD~i}is239N*7$d2ra!g[&B & %ŷa}*I h3Gɰ_FH?SvԂL8?Z dpLUZ)^@giTgQh4+xb ѥG4Ѓh 9W%S~Jå_QaQzMȚ[_]S֙Ⱥ[K8p8bn r AD.$>,/X# -C6HAku˯_QR4jvensWB@#s=:7.N݊ƅpư'u54IhSɂ C=(i{znc^dkfo^e6iu,Y4fp 8vwx>:i& /Ui)Aޭd֒DeKfX1j( ~\ 1! hCfߏ`l.5=Ȱ$<<[gNBKê2ȍՠPyD!_ulc.y?ڷ;VbvRxxGm^1uw IvoݐFxy} KI簴V#")Ѝ!A<`SɺGr5̉ݳ! MzҎ'v60QYE5hJAlQEo%B /_YJu LGɉۢx?njd!Y U>@oD@v.28WS1r 07ޓƆīWAZƘM|jb(E ٠ z $(KOU>xy|0'^2SH @ɞ{t{Y{>$*8o u079p,8 R:^ԶYj1$qWq`7K_GS>/?Aba^Phib݌K1Oa!{$z,D=^B3E*Qw,sv:,mAWX̿fZT=OwP2>6 O~h+JY!lc#$&D7vR7'Q,:4f(E0[cĠ97hJD(w- bU[#aHWOJuH3XD,eav<`vL4~+n\;2rQH3PmQnO,"G[@zZ94L+._>< .YWǩ Lzz1zyCe2pJ~Uol_k.MPA _tK#w*Es0! g¶ srj&X ν)e;7`!^w?|sJ\%@u fZߧ%>/Y ~݂%o4?4#C [j5іWe=őʯ~\.O373Ա`4>};H\m4-L0-a?OWY5a)|ZF$[y<9w/S|6Ý{d!K0DY * ӝ0"0B dgiТ@"r4"^qksװ$HgJCb\BB]#L&B X|N޳\gG`AZu uuއ۳.+wHHA%wg}6BK!_/rF\-8iˎ̨3HR3/u' *"k e@xx?:)taq8d_q^W(˜^oKR'Ts,Ga'v;-oҪ}H}pX pvKgD@.= |zq|IO|FܪTe6A&[KJ Lau VY_ʗ@˦y:têʜ_8IM[87 10mg1g1bs_1Έ\ڍ(RfN7? z@73)]E٘n0G7_9FR5xLk&&)Q47Op^r (-O>oRg 8YzP\5@!a# M)E^'J{}uH/P _PӠAYl^t_GKmW]2HI m(߽Aґ30D+{XG+LպBj\?AvѫBt.SS9 ; 9ĴrjIPr~bgAl=)w#yB8d`/&1, s2i&'@☝ikP=H϶Mzh^:Қ4 ,Ͻ}\I : mi[ s LFHp3 RPfpboR5/J|^)QrN铇"hRv+)dt/a>bTzI.7cL6FZk*;N:=^ <&hHt`U9]'B +bZsGv{yf {z!):dO2RgΪ. \ x0ŒI8 k+7 \Yuw [f2#&qgU6z;ٗ|fSUkVF֐G-h0n n:IIq 1O,LECÐ`AM{.2bM4tD0Z}Ek7m؍2Y>.QȳI>jof΅)E?go¡NV @t01ts+}yU)qsզ1/ pؖWBLΜCBɪ=@SQAm EZpuiI1yץM0}P̩ޝ"2ֹCxIk J߼GĠq=]87tvR 燗E/+;E>E^(dKI|ϟ)ÿrckDp&/"/%wvKͨ%>9>)G>T=9xA3Nm@DC"[C3ZoN_͂Z'$C;rqeدx_dq zȨ ~`h>Ȋָda 4rAgމH`YZg(҅I΅e|er 9,f@,Pk?lB~: 9l d͖!^ɞjs:<=aq#݉kNuf01bg*YDm`aЊͼa@윢$aH$>=*i;F6tzVbD۔o-Q_|me"?sWRXdSqtv" ;Yjq"EqUnI,(dc^ڢ؞(7wξM֎I0twŨ5ul׭̩?ɣ$ٮ`|QLvcsbζ+T0)H֫cqO9t֡؂;dit7|a07G{5qQS/iX5x~%'`D5}y?tȠnHq=`v8(‘&;)%"~P\y7sZrh(p=2uĖ*ltϰ/8 oT:8]<.]#M^[x |{߻Iś., Wwq)T`:c`bS#A): F"lI9u*Bhlq\i&n_nKȡV\_VlC~ԅ!.*k Pv; \%4X/$^yr9"k{o)S=2Yd|mĥj1YGr;eby7>uDwzKv%|꠮(SE=w)Sw1[a& +"h±~|{MEDC5 8֍%U!\t|4o4 =t·iaKIkx$qFl-?y8L1v!Df# [WMRAW/vd}A^m?&tW0c8hz@3h#|c"T}1h$ĢntqXw R"kDFiICtSx'6tGШb[y.N&X"_)W}g"vUvYWu >bКc$P(B)Ɵ EmydeSm .;2 -@kU\BW3:h1f'UC3qVVw\I +ʫڕ1;RSE(S 2Q\0QAJ94N- cÚ׀0 9Cy?E&ȃ7f(%Qz}-=Ie_P>K'" ѕ,.@#"f> (^ #P+O;Gj^v'C韎 t(9Ϳow&kW[А3Znm,eŒpܖLO<0r .ٳ^Le{0& k(_{N Y>.kpjr _"{4Ox_] D %D&٥{&W(Mu&(Jot.qV,Dƛ,}L$7ʿ^r=16X*V_;Y!"o-;rH[c7!˷n33V9Žs;@2~ Gf*? k6<![Kʖțqo]snYR< ZÐ'% ߂%8|wGYMXu#6/Qcpk4Pچm;:ИVnjRAϔNoM NVp& ]tk6}tpiLOcB;^/ B4 J'{@:sY[bdYNEqJsXx@.RImr nAAXʧ 6RF& HV$; пNg#Z}Gme ečF$=iW C܆}XKӒǟ8\wIR=얥i_> .B@5jN37DE.?I(r5U4Ŷճ!dtmmuiȀTtڛ tuTR wdjB,^_$"sMmMy2a 7Ar&ޝ#)%)hGr|&bOdXK-þN1cF[ڪbx}Gd˩rD {QBǷ{X8A \n:8 o1w_>F bbbRq54h`E cM9o@1[Poe;*W8P)nF5S+LXERgOgU|=®iW{]w2,G%PI঵!Nmǫ5y.[hϦ> [C|vK2H j6sE5Vvz&6dV9gmBi/Md=Oґ3;݆I*%ʄ A,ca^Ć32ªi٫R^KB_]C9O sC}oiXSy3gߘ@ z[]Ix:*MOه >hu1)HyrV&BuN>r)^-p*U502%ʌ^Z :NۨP&ke9M2ko yOV:b\FREnC,JO-Nn64ȹ W 3˪?M$apf7gbC0B""SZ@KDUXC 3@E9!"P;|/v,H-d1X۱R?w0F\ʛaFo-AxYL*Ho8_(| Cp9ڴXG%g &fDА lx%50`e}wbB=̕{ne9,*!Pv]bYpd/ R_hlXe녁ރ#4-#൛ۥ9,C*?@.{y%3 -gg{G~~Aɉ#mdO-Ɖ̰iB[-01)t|VOEF3Ta]sKTؒ#arhzptSlx%Fvobv5/=6^hs}0^21jK /qL__հ Q%4 P-`v|\Gsږ b$H<-[T/2\L.'vj_0jPE"5&vxD_0M'=ZՏk_ ȌcER)}}58۳A-,gU`EHƟ]IBL1kC g;I2rS$F-姒;і(3ql<_;ûhޕ!jm| /ҧ@+3FCƑMI$[w&}+8z`O>IhNf೉l)aGDGWk$;hNaGt\\C _Jo@1|w7(%x3n>{.&hyQQw#gy+ F3pJ3Ykh(z߱LbE#Nk4.XQ)Z)z6mŻk"ϱ҅[S;*wv.4)I1/dZEZ9(i kkFK@ə7NŤ\xS_$J=q ~˂+8:G&^F}S]ZA?OW*`D͈̍gN_cs%)s^9a<@Ԗ:8J`C-C+1P Wލj JɒL3+ cHv]?ZA$w+$0xi}ナކ7򐏩cy]~:⦳&T5k *;Sbx^m(y?` (,~YaO $ъQ/|$}kc6^Nz'nES,[T\ S4..V~q>"ZeDzT ;-;Qv(x j7I9 RőPqnvKjz]qPO  B5_c:>wIciYcqsŒdP%rBA~--n Bj2]xmczxD>c r7nlєIe5U`5qS͚˝d>A 7wYIU7i;ᎵUbn1W2xD u~6D";t$7-mtLd}7!*!ݢA|6PN2oP1>fOt[5nơk  Kڬqjc x:택ꅆG?P1]|GMF5 hŖ⸚{!-Hc gg`:N1рʡ-÷&W5ve`#a?KP=L.eUbXZ:IYC$4ej8 cdӃ D+RM.fGFy[؆iFͅ*~sF׈2j`Pq)y&17F5Ʋ7Y)7']Ci]$_L ꬊImbXS |^@!ِ%o61d0>_aTڵΰFf ٍԹ+܁E*b^U1€!:)?ōig]?AR^HJlY4v1kCs%G '&+>l #PtWkD??VhDL8(۝K-/8+Ei8NBDRtx {v!i8@L$G06Zf.Vnnvcov#=um'+D{(T$LGKI;7*^ GXzL0hGq[5}찦خ`{a@JWB@=ipw~VՔܝOYAe yVDT0iXoL^]xpd Srr=˻Jk4Zmg1fh\9bzxݔk-薗9!n20rGI3X9BnS)wt#I *'U0! I[T9ph~Ȗ 5/ &\(C8A3ΤzZ M;X"mU&B:eHD~ KN6y͖jW"5XӐx-u ϝ5{h&*36В59˒͝; "b%BdD1qً/veoXQzC呸S!z`ҍFʻe]KB==7)`-l-YXC/'/+0B\=`+DLZ^Wzg΃@TÌ`"zʱ~5,1BH>i~/7 bGp4=qG5ք}?ő;?N=Gf]IzO}j Rt>Mjk6,y퍲z=k{LIDzKD\' 4 `(#e7Y½lc4%ʤm` ohə\c+ 0w%XXVQYboGҽ:xY CGNE,M @ QO["pPOkD#Ɋqhb(/#޻ghRkGAeHBCFϘn%׭XYp%t]Ta>;^3Or;+Ԋ(mSɗ-u͏\ቤ`X|oL~6.7edRjq#YN"5L"(/Kvo<_ܭIN[\cK r bp\<4+2,| *Ȯ(fBibul:}g >#E,eCAb%JB@:.Mx\MmbC_c=iɽƧ^ (܁sҸu+-C_ r`@ |~&QdxY g`҉Ц ojYyv gŵ~$DJ2i c"=; 3@eF tw2 MM"o~ h>8N^۞g-_:6^T0+Ҹ$jB=ʹLf&lh$@Zjo wAglw[~-.'tY0io&ui} _s}|O[Ao +6WQg:ź뻅yN_N]/ gP"P7n*|QYWKBS>8<2 !2ϽS {$+oW)Z7xbmlS41MFl H{m$Z En}dw0k4Ž/}vX儢N"]4VX!cFryxe}:lKW,tD'`'NS ;3)=TT/l0Q9qlgeC+9>\PDM nAFq2eleUZ9FMϨHן.ĠqßCAn)Y+%NR%$*Mvq(KN/ECi)&+VzfX^H}8v_%ἃMd-UkM{= :L5D̽}e=>[kB va/Aɬz{#J\N+ٳV:wnZ=}M2_WP5Awxj\-_`b:nn0ت:QAmF*i~;aNF-BE/9 VruTR+1g\Nפsu1隑h#/<GyD:g4UByc PY*ܥ$Ű[K1-L\ uǽα`1ނ(|2 @\r6uzbVx4dss0e 5fD0ZAtH_M݇5a.rtY{fdC䯔c$N"/5O/\ ,:]bXC00Pp1dP=!4'^S3{BH5v[P+JUܺ!҆Rr  P?҃;/B%birnޱꚄ UB`Q"7] D*qJ'=LmB@(B@ԦgiC9xı??RqDE45J h#"c/nUeUl9R9/ʊFfhI-qX|Jbʟ:׻z:E72M@|;piMDžzIsfGSB'Iމ H>3fLCk EtB! Σj vV5vpY'(\^feImr,rz~ͣK&ۊK|Fi(#(MҸ }Q΅X5X;HKǶF&XA@T'(U]̿/^o<ں4[;Ioed"r]]*6V9@Aӟ%z{̬Qn& U +9'-By[ ::vvB"poCƹ%{?ֲf}{ijϿzF$u i3λl./26ꍿS T> 1:xb3|Np y({JK ?PQ[a֥-0'pxYﭹVB`Ja 2ujJwσhmJ)𚰾PV}kRJUCNm~ =\b׆g D" `o! :"Ih [S;{IHWʚNaņSsd|Ο!/b(^lfRkV)ቊ) 0Ĉלn%muJ8 H5X+:ޞSچ0' _aP |ˈj{x|Wv._0cAm0][>]}luEܳPFE~> iZ}>(/D$XץBuYI 6 bcBQj ,V>ՊR 3cb#ܺj=@}L:=mK2PyZB_&9L! Wk,}x rxy&[r&陡g]qqgܯz:ݣxC$Z+6Kctm3!j緝ɹu69@S>x F~B+*˨LK\<^ ϯa:3?GHgI`6x^V@jD [a&F 5ԵvqwN@5ZrL29|T,WNKiJxIg]4XcZ*2(^rʙYUE }S -]:0 ҩv}))>q;&*$Zq mNDh&024r7;>]~~en %pԏ[ԩjݕb[^A,w@x g(Y|bI*Yd%_sF%f4LYrK,ݣ1V9zgGvyn!4۾B$ F:Pê'}qԬ%?Ih{jX_<>aHm[0M4SUUf׈ h5ń>gY??ءMz:/f]U-X[K@=hEHyʜ~sǭ"LAae y6P+?Ć" ?lC+R.y x@/:l lhj>u bƊGư60SA=uaf,4 *weI)#_WA2Xy+^D?#ǓKPSo }E!%0gHJ$ /K ϔ;{_L[<+\գAxZU)N0`xGh{`Zz\7nIf- 1ϰjUhLoAv$ }G9T^/paŒ(N?vf@ȴ~w ř6hko}awcE/t͸Jp9nLW~21vI ( 4RєIFPrON:F 0^)qdOFEXnp,}gDP\(plq#*%SΏb 䡖ZP>cVX$ tV̤Y`ź\41G~\2j9)@¹J97{ǔ/!v(u4$6nkeW{0H}05tK2dϯ<|Cڮh\M@.rW[Au5h cA\,kDfy&5oHm:yԫsD4+dknQ/c̓!)=yvLvX2DdZۛmZ"%/U1MjXU"?6蝰m~fVhDt.)#6c^ a*5~;{e4' YYnzW@jr\16'C27(-sooܘ @Цi=YއFbZkH|Bd@0Ѫa_;R6GĀd+``eFoG=-n@|ZZ!` #Zr*EmoF`L7RM&>h۪{hȨW~\N)~=Wsc(x/Y*7&Ϡw"`]~Wyx\lAݯq׋a6oIF"lr\%׌LQt<o tde{uR\No=8R7Mܙ'cyyh{QF5лz)]K1}P9L\pPD:*T]' of}.YB yE|׿Ɗ`5Yڛ)p;\Bn} - Mͫv3hDtl'Mgl@C?1B,qX*F&(/~I_7X|c .lkg{^NQF|ENp#S./1@aIƝѠ?·2TZd*-XN6dZK;X<[Ľ!I muIwwTϡpHH'Ұ/|XN,Fxs]CVߤ&6`Ygvg38"q'OUd׵)R˛0FQn-젅c&c5wjM>d]ni:t\P<D)I,8Y0*IՏ;n?eg{f`@'&s(RKy)%Owstɤ q|[U&&1˱Tp:h+O.;VB3UpFY.vzRV)zPz34#Y~n FGK gHi-h\~i+;Q M@Y=ts+8Ł}$ ;2:i!-Fo1* j+wVhg}j$]'PeO .2h}Z!Nߚ;޽E)ݻ©ݡp܎੬~ʖktB>蓚`Z:N6ˢ@?fxR]y$= 3?a5ǡ[C56O>Eh|?z1NH1Ñg|X݅dXЪTlJ1U6ݹ ^#1sUNnC+pQby(RŃ^c'թ[EzgTAm@?CQ|r`&܉%CbB1s~n4OK<ΟDj!Bf#w4. "_2ք{" vVLR볖<ݲ^21VߏbGo x'~x S<m=p \aK8LnOFB{s35^7Fi- `B |R:?'tG8 #|0Bmq5cm+uTھ8a~5`)l7oTAcb,'nzq>.1鰾L _ʲ9~vZU3}膕d-x*&J_,U+ H1**Gw4qջHҭG15*ImlG}Q|es =d@&ns0v ѷL(i\`jm1 p͹E2cUz`_ח&}hh2)rSi2U-]Ƅ[.F[G584%B\/;.0M||2fztEXYlfL5֋n:jtqHa,+ZՐ/Ejܘ`W6}n?-r+4ݛ+(0a2;>L,ڽVg(dZ 7'qK8=P]Ro=PUAUH%py?7`3 &4$Dm[|O\e6GA#|x66!Ӂ2)aa!b>ɑ=i{R<]C@fB\C^Y,hXp%8M2Єar4@F؈4?}*84E0@<GyahfR2{RDp3#>Fm Kd=B#kΓC6>?7([ n҃(0*f1=ޝ*|'ܨn%`8[JԹyt@*kOsC_sa hltql!VKܖ8~^Zb]˲mWZ{ʮU }: hj(aM7gN-_T!`,fzGRbh[ Es;o${hBVi4L-U"Qr;e\U!$ xjҼh6)l΋ 7PxObZD'6OÖt: *N.3H6qʠLf&Q#ʧEьhGfR_^7]rLgTl6= 0R4 RWE/IϾofAf71~ Lf8u%ڶɠL:e :#FRvkYL-ȜY ŠxwS;D ٦lʹK葂}X*/uKVP7 "~u(sO)CM~?oid i;vwf^c)jj@Ka'T,6ijmPZ]ZIW6PwIshS5ȆELIv_h`$D%J kjiu ؕn60GhcuS=NCS5oB5`zMW ybv*"ھ{LJS|K1ؽ֌~I*x\,aiUwxѬ]AJy-G2(VO."tߧqKtRM3gsk#B]3:|7kг{3[,`l{WMqg`{4/2c/rFIQ>FbSϬ 7m\P^Ml (}EcwCT`1QٿBaȁFmm3m24 V(PTs+{ݬ+_ry9Er&bZnw Cɮ# W˳6lzF EPRXcwVRz,r`'iXz;=.KbX^$'0'u(ư&|M+y+.| H)yW~psIg#- f>wxKJ^׀EzX0R8[m-7J1=haPi:&~]@+CƣUUE G*QB!9FmKZ Zb8CMfuDQ*<I*(^a;zx^,d9!tDL8z$P#A5 :V{-e&Y*2-|kmIZq*YT|(lԞkۑģ`(n"Wd2^uڈ!{U>BhSI%x!*b)do4}ɭaEg ];; $n؛D_^PkoN~*Ha+"Ϛ#eO5q2"lf8ؾFջץ¢7 lMYF LfTNCC4qXi@I!pIpjwP!M7xx(4t4cx`-›p o*`9״a:UdXna ^쩼JXzӤu*q)*W!y(@ZQu_cP3?<(dٺW z.#Iw9;sWA(mŦuk?,Bꥻ">D'}w)ѢuGKWT QԒc ]r{]ᘛ/ԅ &?I]_9 sk%8UuzOMP[דU-jZtjHFDГn0I7 pnfyXe]]1ai k߿IYm0{.9,E!6 {ܡ7@!Kg~N:& neUNx~,\/X0.3z~Es!XZ)5uqwp3>#]\*U#DO_ww-bD--\nm@![TṪ8YAKrv8swhŨk5)]G?=<KOB1:ҁ6F݈doffhNYew[ D0bsK{N*ů9 oL7U,8W:{/W?3D uB$<56vJp_{j׬Y,}p37 ð+OLۯ{w|>ڂ{xtmb|fȝzz8g+ݛ9"5"uyEؑpm=]턹o ʛkl?Ai,vz뫌K"qr-4`W}CqIԒ}ٵ4PE[h:DՐBO S_O#h$;Q~V\:H OUF? `.ͳMa4<#ғf:εPpb75Bk9{+ K\譶 lIO`P[51kAp lɁD 2@ʕ-B t 4u&WIJр(K678raeS&u TSqPg4Pm0*,CnJ Qؾ\@jp>E{bVqhT,3c/H =4Cq͜h`rd]o\p}#链P+{A>+n/H/~D噒pJB8+Q]6r',XF ޺9pxh?+XU!-71P6ѳV'FqH]-\[eK^pA1:ƻ<xt3˓.>Kb`S,Ms`!d@#tkcL ?d6e,WԎ7a:҈W>!-ɠd:;DOZH!hy@"KTO׭Q(]#D^,~<[9뿩u:$]|–J ^j:yDFc 6KNaqY.: {drv_6%'\ϾrzS Mp. T+i0+0w{{DukPyo"\@ڒ}!m=5 FP~\ Mp]~Ly 4s0>a=t{P W2װQ(U562N`W<}ᯃJ;]Tΰ9C^ `E MH"mjBy4$tf K |$J!jguI_AN}ǞARoS JK %fgs3LDȽáHz6H 2.Z<}6\õbiO 9kƀX7eSK%ALkԧ{ƀ-}sbr.슡eisۢ U dKZCiɋzBC4 z:<ڼV"'z>)kU|2\ Ȩ%6qV 1Ab~N9TIdfrG"OA>ZųL}ey>bYp:!pŲ~ΚiKL^GdkA=+&j_'~x}EuUۮ,JJ`0g/O=i&| ؙ<~+wU#*=G4Į]Hu-CQѩk#U UzsiīںDgydi'z'e ؜)-Y2)_4p:-JCm@1߆A@yOpѥT9Sf`y#CyR}S[Y7ϒPj؃bH|#&.^Ol.l6Y2eY[_ 6{nD1'z2 lAtu.#=M%۬:^!L]\'T-(0בU(CdPCU}(`=5r//c:!C)8z(43.ĖI~$}nPk{3_ms~d~PMX/:_YNi3MūReAkR{)>V)8=4 D79q+BT:j/ChEBmұ1}'yd4z6THgHJHWY+~sb@ (05υoAo8a04nߪ?hC|<.9G 3MkDmk^rWWҊ!M+"3=(!dZi!HwE C{Jdp5tpTԳp_W~ |ѼZ8CJ#wD+|G}ɵE)+;Qڜ X(@= ecF]l!v'|Κ2S4IR˜RFWMg8';dz.LRƄ=M*x~ f 3>Av S!#~~:Ÿpj|s=x ctvV&:-G1]6Yo~V A/ѫ֩Ry٦'v;wª~x4 >:,?ݓ$Q2]hzv,BX oJE6$6#Ȋv0e5yw8i`vAknסxWU NybS[WѾrb+eN 5DCH1d<8 }x(ԺL@1ӈL&mLr7e.3CN)$5}}˸&aҦWrXϺToύ?u߹A2]\g f)y`<&jf׼;(bpC &$Ț :g-h2j2Im%ޅtя x{,nrϙ: 7)s9j xF0G4ĸk ua.PJFZh6?I%wm'ɿWC۲ O}?8e4첯LfP.*1v)I^ů*&z0A:ya=[f+lu N0jg&Ș/-(a U-Ϭ#iu-QEP`{:ϲ]8G*7a5ΔǤ[iXgtpϖ 3dוΗ}O F|KZ)RqWBe謮u[h i7 t\-Vr bXɸG\{R C#N}xV TLD73Iq+'-bIK$L5*X ~j?934vPV_0Œ~|b-" AYZ97icy|rEUlkO4vi"X .jԨo1*} $Y5?Fʫi@PX?^l59:Y℀rGU)<$5\4D'%IYىWuXt%FTBsE\>&epʉF q*7T)_wI1AC't^ Pҳ+AR'MdأB=G]QN% vϿHP:?u_BY"?*lQ!aƧ0c;bAv̠# h~43?C /tZ_ \_oAWv^)y4 {hCd"),2$̧j1"٣l[W[P m=pA<} kV)kshP8L~tGtGr1XmFA64D"=wV]k<]f8ϕ+",b蠟-la^3BtSqrrSS5~=>/TNuUhof^*Ɨm?o;x<Ƅ^qK?'bx.S2&f>ٵYWn-jX.beRNOf=QCj~>t?:x%iJ؊-ٻF'Y,tG)H>U(EB(%mFd0*Hy >ǽ4%KCWO`jO =_+L@9QЇvjurQ׫Ao;鳣Mܮ*EپG-2 >ycc}k,߁!᧠W˱| ]j:{}Pî}5g?q4ϲZPCmg>m(ң !ڹl=d12]>NqPȥo)ѰZEBFi2)hQЉLWiub=|,t,NBm:)}` )a+03&~ Y;4sTy)nL5Z66/tϸԯqEb^ӱ(H^KH&(S5#n>+N:68}-Org$0\h_:Eu21j"W%*=?kalv8׃F 8lQAFrHAL8Aek 7<#l@vni2Z ViHS4ibb u!XwJO+mȴ:B/x'PHG;/F`fbcrLJT5fv YLp}.¹k+[Q:2FG`K7%ݟ(;oh"0xE}?楛 o@fL0UѠMB>"/9!)++=nfM? %(!L&E$<֠=}P%> ,&Q"ʒ~JQR}sj֋r "9oVl*ڏF VJ& ".΍9?sa6 A"Ʋδ(hnz21)/ua1?E1M=?HDu=®Z>qa<1.0Ȋ4 Zk;G7r 5 D)fԗ)5qU[ DdNhe=N]ٰ8 ]:$_b^6fڮY!Z.5]UJxЀZIuOYOsGkjcܧ^z>%9Ɯ-2֌mU!l)y%* cA T='0/̤دCFQwlu7rodм p;J8!͠keҸciѐR 5 $hS4oDXQm(S4*4^+Ek!4Ŗg}#NN9}qv#i +2&v>ء`i.cpY /tNM\! LdN9dՃٴZ?gA RgN@i_#*ş\It8hIS[ 8R?)%g i#l?SW{Fݼȁ ,4WshdX[=1~2͗X CNpPџ%k5rGZϗ&11*fC%a{=P^ "Ǖ{2OeZԲM/le #(6c?%;*5}08`>5r`P8^ha_%u_220ioe+D$f~m,~Ԁ5A|xfBD?9=\|hud+2}LA{ ]R v!ѸI^ZUL!qƖNMXF !|4G"|gQQeԭY=,ї*H>Ʒ|v5Fp]łYQRM0;kGI85{!t[qxgC+(3{ 1+M$zSlOI9oSمĿXZK:%AƢ:]^'?~u2zoZ:3 c\[|Κ;W`U[6qTH+.PmF*[LccDPU}﷡bFnOoEY13n9>v|r]as_{]Nә%L:-X7Ŵo64Ρhnb$?W$.5` ÷?#h\w9h`HRTW٢@r5I) d?QjA GNI`"qMd|1RSX1w0|0[dL 9Tp{'SY`7PNm|]bmUƄ[*@?UtEz:u*J01|ҡE; Hc2^)uk.¸<К(w6J=%+2וai-/6ER??:UوTYyT-5PNIm}{y.H7Y0QPtl{IABS lscܪLiLg4ʫSDF?76z@!m&_.K8dp>AEm6YմHmmYl_r0-g;f i PH$Ȋ4 x8v=h,%v>. V(\c[4"8o=6ǰ^bCxiwԝwfVםoBnG.n_^u@*%%O$뾒KI[][J%Owstܞëaޕ!PY H,yӁrCV3fx53y=5*(;E(ڌtn7uVYꧪ5" )Lv ,ثGicz_טC"WoV!xbEP6/1Wf)weO׳tH 6u'p[yD7McݭwZR{Hg8VR !zQYW<0@T`\ ; O=>!);U?ŷĂ~##ߪvKIqAca/ܞiX)Xmħ9ZZA4n7Ka3Lhh+}΃E'bsbzdʳ:,ub[Y"&z!RzpB91 nbJ]?u7Nu9'fn/Q⟁EmpC5 C/84$hW;1/@b/ط] ~{+96=2yI5Sϑّ~fxU.|,Nx49YWHn*8 v}0Ln>H*sR_aCH.׍09>q/ܡ&Ŏd2?>Cr ']l= bRM`EJ)u҉X)t L>.T/sF .onE& Z`'B?6ˇ*vYEB~gሢaVQQ zv,-kfϟvcj#!)- D _괇V4^kŊڒx3Q}C#s%`tRps3>12 x4Mͅ8Mw($< ShaySLf4W-Q3l.` e7B7C7l^Egx0?`Jpe|8c`Ͻg"&*mO(Jp=yy+"Rz!x%͑Τ qZ.v3tMboa}w:=CΨib5tPhf7lt7s-|:u,sw\^qm4Ccr+ Lvc6E?D4Yw̞O)0﷮7T<"RmVw}ǴE=U]O>y mU}?iE /q\bo2#3H'x*%~#||Xl)-*lpI1p5f@Dc9ƴ]X-f)\sYKN=e`F,ȹ1+W,$`̔$Mv#[]`"3&|=枖65ΓL5 %5 l==·QZ4=$x;MhOуĕtYu׸W*~ Zm%,͗^a 0ϱk@F}JQPyGPpi;1MGR !>_7nI1ݛD-vG<[CbIjD:<;AYݾ.@od](iG1tMm_|nmoփ!)5nov]Zk/ ߆z`GCzUSDikE .a"h-TTqR DO)AB d˪s20_俕' riX)VH^#yD Yg\I-0|oѱZ%y j mAc`RxtpFik xD*R)7v>sO1YSEf7*T_M/w]xۢA 4jTLiW _Sfw7V&UCM[S1343R >8ma/0fvwD'W帳Zs$P gd> 6'h'J]:<c{%q󪁎wQ4~n8sSQ`b:ޙ|Y/ɦUUFq"au_Õ$=A[="[n!m_ bfܾAT|w? <~6eCV^hR4a)2?#{@00#nxpKfU\6kG~N)08Q `|Ã:c+%}]l3՟zf#}+:qԩͩΔкY_v KbO`5|\Y]%O E"ɘAvm Jug~(+cqɖĊeX-@(]w8ӄٴ=D]kzRs)e* ص؋w3 ,8wct۞P<2EcƮ z3+0\߭h;KY~gJE٨U ^]o(?@S@|%[ܙʼ"UAhs) NfS{f!]۫ m4мy6<)+ Ġ)ӌ@}K ܙ=xG ˆzq$PI!NTT#/0;[*WqV{?,Ce/,x*Q I,ĉn-J(jp 02HAD/Χ2G\Ϙm͉pD?*+?&׈[+W:ea7dc+Ւ T~LL .x\帾۹v ixXH+ۨ{ LVY8fΦa9|OaQD@3Ny,)J0]bkXb:SȝNT< Ui J ܠl292pL3 C(S}SC}SNgy wve!yLj@yC QM17O;fۯf-eI8tv5ôyҵ Hb 2TɌ5HR%ezፂ2R$ݰQv o̦vsc]1Qڔ@{^;(w':T|@d|:&R!NŔnhkZ"RP}$}׎>\;O-x0&̪ykr-[f颼--#+t# BF3H_ qD`t4_2 r/F`r;'op@Ʀ 6*S%=yTTwR4(rیٳtqteB{ij~1mdLt`qYՐ-|6 f]XCVVam%4XR~[mߢ*MZc[d)KZ{=$$ΆO.܇&N[I\WrN@ P(l^%cmn\>[& [oVk΁wMLI_^rj~`Q$Yaؠ{'+o%a*'ۛ%Wd &ӛ`/U;;tиw߯4.*pt/ҌO~s8{=u۬vKm{\2V'9n|M^>dyn^0ohSzir m\,QNR>^0Vڎv;#Axԡ̻[ݿl eAްWsmĵ|̼("١V!Z\?-;V!/'oy'~om dA8*0/#p:_}Hq ӖFá[iٵ{ZT'O:'~ qŽiŤY|oc)XM )A9P7bܗ9/TnEЯ)2`E>X֙Sju׾x"kЁsTFn'Sb, MF9yB$,4rC6@@rvOm3 w$K~J-648(#-6meqcDfp<٣Ch3w``h)X|Y$5CMNJ*RϴwM{I uz@7Y`%U_t{56è⍝ `Fv i+N2n*?c\$T͵v%e&i\r'|kN2kްB#=Z ('aR/*ۜIF+ƓX'a0~QѺ4͋>ˇ{["ENk˦f+mnT9s!b Nvc8!&uaހ>tٿ .HxPX- )4.DPHf iK/N*b!hU_jƿ\_>6cKDڇ#BV{ b?**`;,RXg(М1\tHd|hd^3@e*m :I]qR+ 1AæpQn4 u3i3*ª]ew8yX9-+PkA݆r* 3r*H #_-3"^Ry:gF'>k3$ 4Gc U e;/.<n&wpFb7':w~"e/Yy; ܮ"I$v&X7z㒶CW.5BڿTȺl+R `">>Έl\Z[@H:i=4 j^Uի,Usibn\g f֪MZ_M mQ+M@AP !+iY6'xsPMK cZYd@*#M@Xey$%*8y#rtoEF ߛfXK :}nlvD!G,bVjz bN6fd' n2Ju I -gխ,bD&AG{7J%GiO$<н*qUDWyNJ0K`gZǗ?Gq,n* @J|ߘO´&;GD|mS"(sw&i9;Kk>Oyw. \DZQxlq'8eUպpnEo_ER:2Di`"|W KIOEgxQٙ(dr2@ ),̩#D@ClvW.- 1Z7&(N3P(2qð쨠()T* n-+Мau+p]/M{t2 &U%r] WtvwXSj,(}uG< I[aT/k7vWi @TP_3KI@8;`4)IGC2o;Ҏˑ1mNV+;C(~bNm̓r/!,AX[)8SxJ[HM@ʃ}w4(,'L(#5:?:qx5xJ0'³ pBo6(FZyG)( /Ez@8<99:D@MuLm` ml k5|jfKK? n#y:Ωɐ$~ʃT@|ce0?Sb&H#;=+zC³*wEm"Dkf=P543B/#j"0Gaˍ>zgг[ %J', Mq '=U%:zC1WSSPCXek'f%ùsx3\X?t̶47}x:< =Z gD[n߅9b}ҽ%7dn:92 JMѿ / 1c|òG4orr1K{ Ɋ|n/ܘ&^[Ri!BpRfHcOp:-ygڐSP*I4ŧ~,3l {r""zևcc(fqѝ1ЄsB^+5W:oril'-miԿ:x=T_y,zdI|-j D/UzSePEd g?'=Dmo^.;Fߌf_s4HrYsS-jW Q激SIK(h8*A* X~Fii^أ$ xˤ(R֣G_5lQ%H+ ulPD@e2N eU8.I}K |}PҦ˷63! KxQ˽65HŠq_jϢ#ڵG,,gQۦ{|IMWQz♪hȶSYSer׻c¤n0 DzMI.)GYh<]5QOs3۬dxMǩu?D?=" .Xg斜. &MoNDؽ L{$Y ̊XsB=,>zf=_v:,yQgļK#lݘ 6zg5q )c[ NrMό nYy)Uz4w1n([E)2uĈwÆT+K/:4VjlS &!\Gٰܻ Wն^og5f}e;'g?ڂqZ|E'ԍ@M3}QX9U\_T!3XzuF_OtC?03ΉNЮ>_8^YGXtvI m7?S_e8jxRlIЃQԪ~&d:TXywZ!*k{6:\E_%;4wjD9Tӳ>i!ovvB?O0p!~%۱ |)~b Hi VX2~O* rX#KNћٰ!#gAake/cqB :JqSN]TZK]#9\&x\Eoyjk @ilC.V6ܹm`@U3a9pIyYZgUYZt2^:kzq![@d)-Ŗ:%y(H'LLZ]@P1\7ϦVk5)3 !NMnpV{ DB#I_e#ӶRϘҿI /# 9Ɖ2'9֫T-QTJThy JY` Pwaܦvs_b:zEQΌDaRvrDg<_؜o.u BU!>9\ķK#/+Z&j\n>r73MAǑE٫y"JTi6o7 g*u!E:˔mL_z}9:z·I܉Z{5ů%z9^B/>#74'bnk,$uOrŕ;,RZjJMĒ^M^$n#p|yp &dߣaK! N.'2*G+v+Nc0۞n*)5./sBmc~Epw8\:wޮ.rKBV+d1WXYhjr9KR鷸c8`ߨ{gL?Okt{m5P ΀љ),g;*R_[GѨC5\Z%@Ő΋\rrl3j\BNv]E1 2ٱRLQ^x 4-T%qn8홬5XpC =ޢ5׺ZC1JʥNX]VZl=wpEvSHꟌRtbov& r> J8BjE*rOqfڕf: -m,WI2 8(_ܥ/^A:я~RAqƖ崍 QAKGIڛ *ًUi8o=AF6_=G/^!\5uTqpS\)ğenó0K7@CUD8 !F`=bylğ4Б>9Frt5T%J֜NF zMȣ4H1I|ax!x9?'j^ ~e#lt0Tj0oTMncT5i.C# +ygo hNr{ESW47{Vslj8rE}D]쭎s%_]XI%a ZĆCMMovNn`BkhLk@K?)X{Ǿ`@`Vff ~T}H]VΨ_E7Lh'E7 .6W5DȤ)skr۵g/bK 9 VI A22d2T"$0`~,,%ZB3m.XRi4sm$RZUnƶk[խO+a?N]^X~^d} 1$\FD\_} g )?`j1]^B '6^ 򳰸u\;HrMZ={, X+Ҿ+#y6yM*:JS kW8[dmŘs]'%W6%TŴ=SO6yq,|I !Ė:AΕLFLXnr)*k49ʊH{R聉wdCAթteWK5CB>*f,CRQ?)ȶ JSnBǣ$2s,zC^sn2~&K ;ÿTh=KvЃ7"^,0'j RXv՗ZZ2{bxVS"vhRN'!  {p])7jGkOcWeO9`$vƮ#zJ-zXIV)Nβjc8(c>+!u&dSpVchM4/eBBARvL6l:LZj(ϥԂ!~gurI-*IwF!087V5p_9z8Engs z ] E ~1T@~<#QB W>&Bxhd+P6HViiQ_;x$G4[_a#sPU0п?zᣅ]&q 6TV}~疳~%6]V$W@,7e97`W|~/4?|Eʋ9-l'C 85~dt1ebC!?9!XT>[iN 6Jv*3-~V>YD! sU%/f`+| )>*|Ǒ7NIu= cM#qycr~kohi׳zhboеZdڊ;p2'Pb'%%D,lP*9#V{1@UHQМy tAlWuWO }\7 @8v1յr KE_#bv2e&vz)#UC9 cVe 0ڦt8OvOQ88+7_UVCZ,ctNq :`!^/!Qɇ$n|9 B+cp3 )cYab4٫l]_B 28 o"`:DiY(R??ߦ݄[`{(G>m*fgBvdWgz y]65NL7rBrUфVU6DD/4:I` ߂3A_U3vT3N v7LX6 _>(^+N3RhhY1p [ϰ$6u_:欨N 5 ^$4M)U)h;*0z7<=%X:Ze }8d~'WoXV;ckCu#ӛgN'|`3A`k)v /寷i~)Ȭ[N΅Hۍ*:msTDA:GVAIs X~@I;opN-22,A!__bW '<]̪W"'_ꕈ^m=Ҭ}(B47ËOo"+-wxA Bk:OV,p|O&&f ]z"ӏ2Poӭn|a+ۄ_xaبVu:$Tt{ȚT}+m:`:ҽbncHɕ- 'CN[R NlRem.VAI?Tk*g؛"ƜeYwkb\!";2~]7D2+ߡJ2:@OkNqboLޫ6PaZ#H~αԵ$°xYw`svmȘ/R:}FU?H`3|H'+6M]l-'%!0ZDbT'޴\cxYasߙǜGNA]Sx}jfԳ3q-0^ WIզʈ}ܫ= -~G̕sms'rql{3µ XچmԇOO@] vʆ imBp%(E{zsdX 35\ԑ@612?HF ;v@ޡﻔ endstream endobj 146 0 obj 158068 endobj 147 0 obj <> endobj 148 0 obj <> endobj 149 0 obj <> stream xsx}-v:m۶ٱ1I&ضm6&&LlNp}{sO֪U$j "掦n ,pb.&n@Gq7 ^@`Xxx@7W/g{@.v7` 0q;8]-.S#=@hneAPaB ppp0ghn4688:-L\n +@Q ) PPSWSHI}[]n&.,M,6w7s|.=hYO F^]@W3;GW/ԯݬ-R._njcWn.@"]:jtt:lj0r|%ET\ ,<j35[ěKn.Rp8WGdp($/׿_HO P|p|}|ib/97?DӋ 1QхK޹Ӫ60Ծ nm̬,쬌ehpL:mtupRk$o%ߗ6_pL #?OJ|;:y-,PttR@sA}u`vv&_Nz%h?ݵ,&f5Z"V_@/ se5kӢ`nbtPvtB ,s'ӃFM_ؚ*X} 1 @ֆ5 ()5RM*i 'W>\&$~x]Q5^|rfڴ4==OHʥЀt6dQϤf7p:{Uֻhrοn0`\K.a?B;lap?$``́uDA԰0ya<'Tk iTl kH:Nl {0P7ɴsH\V}? FrY+qv.7ί{_K3~ طu )_$uT(sgQ$PY|!3 MQ 1A94Rb*h!?m765oJfGPxN;AQhP&t0|H?-XW= =*j(37S"/dDӘӚ~&ҩc|[=@%R՜}o?w E,p$")ka pƽQ^.ȕ!3SAa}{.{y2R7 퇥htCFs͘gWVz4 &mu}u}U$A0v]u]7Q$vs#^/zpݢV혧Ånf-0_4|K׬zt˻*t7kncONW-;n33NRlWѧ]+؝zk]12Nl=G_z$nZF-\B֘;¾AağQw}Vn NۗJ^(ֳJ=o͊ZFDE܈J͸W- й$˰uwH~h-[˒_d0yY a uD@K4]zѭwpm[ wWG+¾';ك 6l(9su4 2Lp,I &-]k!hw}Cc F{qNp<_%EO\NMölzƭD|.,'{aCnK##ZЬ) + ujc[U-xA-;1Bh&w=caZv+%d^љ@Vd֢'gk*B)m#[PA}O*3CX:Z17]ȄwaҤi3Q~$W Q7\H!u*Ճ%dMf~>- O@:lO R4{i '53_cvGYH^@s~>I[voYŤOJnXSC û1`Fh9G@ Q1_baptO.KhEIs -i4`g ^to9!)S; - ĉh6e UNAVBOsXq-7,\@GI&S{8%+_4Zy| -Noblea9Mf$S1\vj 0u.CO[S|"7/pxpOgw|a%kt70xZycX َ8lmI|cB]>S`x$ڒ9B0 ϧ.=4 "C))H-"rOג o OccDTj$ԻG\{]ۘSX۶ XF~7?'NBMּ.%z=d滗r8$_2wCk  ꈭG_54D[- M|DQ)}zN*wbւ@ :otv9uuYQ6h Ѕژe my%LCNuC3$Ŋ1UlK~@jJ6]Pߺ w>W]U@eҶZ' W~ յD_SyCZ)RB$Lʱ6_dH.ƩiiܐZHoс?E 'َoZ{ ?{T$=QNh!{zzU ~?E?Y_jҳb)VhfY}-*>[3_R/ $Y#v+zهmz6N__ApWKʜv(C8#|*M9Xۧޯ50W1Y@x2:̷uɏB"OS]:/h]E =-969_]1:Pow3D ?!OZ$bg1E*>~?\䐏zhZzE>%k"I;qSLJ3K-{O#Є!6)+9o>KfD!+͎LqvM$u"c~k!0bR`dm7T{aYTJK;0~|ou GM%sU=IMr}% փPhX\lJG!Av18{%#~A&:Zܷ+dj!sYR{~3A0]Sq# ǿK$Ǐ[y 5tX/\ yzK/{F5-_Ns.dINbx-Mcl@9KW#V6U8Y{X N~TPUEBil /[?^nT2 c\5%[E-ܔs3hSإ3kfM㎔EF><^z||9m $>j)uf+©`)G<ʞi^j]0 nYq `+γCRI3{ONWhTQWe!e鈊aioW`Mi!nhiw֞w#mfn: jw/SlU!X`c }Pi :6Eai L*5#0CwGܯ([$ 8@{2f{2TͥBXJxpM]$XE;I.Ue`s/S{h8zW%,u`X8N>[SUe7gDceC4Dx>VIˈ/eʛ]`c悄YjgqZ8*亅cꛑU+|Ϧ[%`ꟾuHTqk8߷g)# TK2B%Y3fQf$JJⱉkۨkz8!_{h%-"OLסȊӠ٥QkPP.E V ң뷼Ղ5&L1)L[l6daaȖ5؅L2f))W,kki*^SM0TJ [Et gUF+- E^ P6q>o*z@gӾvv= [Ը;H a<C|K"2R `T<^Ǩg2  [o}#ѓތ,KN`jU@8 o#O0 Zحs6)G( $!a^iԇ^C6u^WFg69+4HعSΐ_QsZDlK]_  Ny֝f?y7z/ iw!_ZG` 7]n1xFWO?Wp:ױ L6&E:`jLF2f˺DjQU&:y?Pv?Tc6E:!jl*N6[RW\܇5AZ*^%,+ױPF&H符ay`G?j7)%8%hRUKV, ek<`6EAcT~~wkg+E2T!2+Qy}m IQo-fF\=9 2xBIg!3 }%Ń+Qgi⋬2Nr 6tw%ah>٫^'H % 781̢aBz#{:؟kE9Y wmI&ŲKU:{%/1j9QNz8%:q J9EHV! h[s=d_YuޭY#?ܐ.%AdKz{ PxP`3$"> ?]M^5ìqyn+dBaLk7`|&WłeA #@|'a5 n8Vs78?epZV mCsm-tl..2i˵=Fq+p:BtUVeTE4""֛ĖzbNs2n?^a:^us ~SP ωJ]{݇Ϳ$H$*-{!* ㊗h5$v᥻SU.`pjzAGΝ3bŵ q;DdjGÂql4 Q J*4Dȯ4!WP<X4>,W&5;H@=4g' Rk^`b^NVJǺx^cIbm%P.+,D'BZz Tv97LmP)lnlStZ!Ym ΍O#ѐD'ڵumvdLMJJ,DÃo[R¥CŀY麚CI(ܡ߼rXuYdCxI 1t/k<.=@ 4fv{_"k^xL^ژϽ4N1tǞ%55L ĐPQ3f!ͅs6[mke_*ߚZ=&$g{^3Q$;Q0}k3F؍":ʆ(A*&yWh&\y deYu6҃{_h_TIx_':=̕Ne}OФ4Uጩ/Gw(LR$y|N8&x6w,'tt7 VmmԱNrًu:T&|:;Z7֥ !$%b$lQlBS?7 S§u1j.r+A}aib9a:,zvrz iTB.T!l#|߶uVRL0BKPME̠b(̼6 oq J_k{FZyA=jTJ݌-rDqK; Γo\YMOL+*FZ N7ᣋ7y$(N 9-H ED+G eၢK:%`<=v6D${ ЅF o1j,^#.Ԥ '<4Z3]na ŅjX(R|Ȫa%AƦҪYpǞś G9yLZ¤3{+^R0>GXjuEDHm;F&cs? [13*]C[Oao?Lu{^X,\Î~@#qi={J5]6 4sx)dPPZ? 9riosu?OatՊIɨǢv. my]^ԭDNL)(na2^Ss>c ZǍE>_Cr~>0Kj\Gͭτt-]kB*Bi`3qͨ)3߀G5XD嬏^(v,ͻ:;]Fe}dHL(q2nL2cP;z*dy06~ZgyoYVP!ؼHvCU2"} +?i[ЍYJ8{&GRx;vy0 L)4l}l5jˋ3}]MpC侞ihp {BLs{Uʔa%^PtGL|MʽgD*nIԝ.9yEMcgIv&FQ~jnq75H|p')1l~XId g88K:>&#ݒQ[ɲ«TLFl `̮^eiR4-*EQtčk9p^ "AەKͳ6Ր'j:r@U!Ik.sCs ϛ94g1mJ>hde܇+Hm"gU5p8!TNtPB^C&:ppەp>1uV"y/gkH(7QT )jag D™d ɇniF > ?ݚ3$`jP\$B' R+(e;'eh@ Ҧgޘ,AfӬprEATFy!J_E᥶Q#J8P\d rN MG9%:UY(3hMYv+aiy_yIpPff|4JArYjSzAyɍ>RZR )/)ɧ? u?W}|mQF/e?_""^uGɀ²*w0?5Bm}$)1 '>$xD>>l7jLg)%ʠY~qm@"y#@^;GrUuM0QZ?vY >9BNjڊBaNS\&A;<>7&zڒ-eR+vIn6!O/}mJ%I0_Ϗ|1TRDc!so֜qڈzݝo٭=rf  sj4x88߆qgf~a6h=w|7,d~ % [M(G9evoqnH/Ƿ֐})9 [iVZG1E$9 >3 i7 pB(\uZ‡cPapm1bZ#&} |ifo_6,9T#y4 iCI2W@G;)ae@9?!W#>MKZ]D\:ˣ9A*]khT =msn2 kM[id;˴hpp]96-6`ãsϕnـzxz$DǧNf|i#6KK \@`gE2 VcЬ EJA\-ZZ΃;en,q뿋GAޭYrOԕS:>,biBő)spӳ?m$w=֟{d)Dyf82eK%yNJo4]".i^~UY}KX8&X$DGD!nY\Z3˨)|%xEehKc&e x&1q^$G>M ˸f=*JmQWM+,v+HBl4$ɉQԮ~&F IGj?bDu]aT<RcF3 QH~Wڮۡ- 4&ť:yw#pMk9Q_L,d#Qח } /D6<;`g5m"/u9\*D/A|B%/u`7*Ǧm\t$PPTdm{C-# uLAw9 E(DHKWJ 9zE;&lt% urd# vW1 1YO,ٴc9A?<JtF( |6ʼn7ic5?p~iD_G\}Ee7tlFۚƶTv1 /qBWDJ7Y JUj=~w`^-Rh[ZRnϼLn-"id\$tM(@0OOE 9PGhW|\m8_Fg1mRl879\g˭dԹ۷o\s/ٌTۼM5'8T\.Lw[T'%>2Lf?!#{6l63M1Qt{.<ͨcdsZ,KwiAE־KqJ(ֆ%*aگZulj-b Gt@79ApO:ӵ>³chQ-eY?5C&l#]~ HWDz}z2(5Y`TNzUҮ@N/CҒHt4c m8nvǡS܇n78㌓ñ_I#сnf)L)| {CG:m;0 me ̴OK dw'cſnڇ RAY:8yuEI)!m eTkC̍B\FP".6*&R[9qֿ_W*fAQ'}Hyɴٺu7URy,?u%$ TQ_KL9.b{}-bO^(TZsQoUl<$\^bC|Lx=/Kk_afg9F7Ft[%Ě7'jJG5- Adz@ׁpg=X:.ߛ75jae?L{B'fB^l¦/p-Vl2[JL.4ӨP"7`O\ y (˄_b:yt7^}! @6z|Lo.m\ׅy")*('}#Zmbbۇ /Khwh]%~O R|fkAUr/\&Sɿ VLSZd$ Β 2bڃ &ũC/eˇx0y],/;or8X6Rpؤѝ\Y5OP:#7j8=c,)ϫeXO'C)Hm?jO ǖ#hʓm HhUǪ%LT(b7(\ϐPĩyIԡ6n /Lw9;BYf.E+Gxthߵ!al9wTkx#.tI^V~~-]2"& >"*Q[v'լ0c-KZųI3C{ovGf Ms'c S5F\,6/ _HIWlY^~z]NcW}g#[d4Dϧl(."Hag\X#q12XTewJi ca@r8 &J:'t0X7Gf-":{9JeHIJgW@LyWpXݮߕ^ʅjߵ!B=Mx%# Vz=-Q{D`ѩ]A1M9ϖ Ni l=TvJ);Dx5O`I`Ӊvi=-DIu!ԋ[ZŬpd\ă®&("|URZXch6؅eg!Mef'eѫf|*uxkiGTj1WYVlm%)paz=CWIck ~B b"& 8:4mKo[IgjPN2d~}=$X6ѧh/PU9kڡ?0q^ZDRca1u| xٲ({ 0׆. *I)K`/5r 0zICZWߏ$19!T݅1ek >u^z3Z1\D$l|`k4"42@BFW*mId[ 6QQyN,_]|6_$0x(zW79w~^MX޹gnW>%TN +VIl@5PtG=ӘOJt#FֵJCh鸬?zXgZږeu/C;2x?DX]rz XD'C:C*~d3^a'6 ytK:h##R1&/LGH*,Ex ~1)qgFb`9H믙g;Ѽ _S#f+=~\unS%Q)u4R":wa1+.o>UxAJOjPMXh xYkdx\4N[1Bה|`; kʺlAnҷq-Kl3M߳]N O&[i߾~T-yS9㓲`Vy-` kw0%k@S.zm󠻳Âp)e~h^?j2B[n~9DC(ȚS ImkL\), 4N^]&vˇI%2xM?P'k`k_![8bX@=2hi >. K[٧b3{Tܱ÷n[C$L|pJ8'M!XpI3yP $|h9_/nKسLW})gp_qvrI^; Ζh$$:">'۪QkҚ ׀3?Аý&骴ͨfÇM ɰ\.$2{V*_]*R{Z2Sv yKۈ/Fw CC0G;R8|:Abhk[B|SwMjQZPamRߩo*@q2Vj1=e Jn^I6U pQǺIGgDp,UWD/p9~(W.F/\olfP~t>(  HYoOJ4o4(t"w2g3-!6ҞK'خ+0` ?̜!ϻW0VbZPs%LJSIk%&~؂!Xh8ݽG_sR˃]tǎFb뢑Fъ.j'k*M}*堆 L6zSZWT}jLՐ4Eom9f%clщJA _"Gyn` ;rA M,KtMg(j+*L'3 [xF?,{I+Q\@f-ۏ7|f 544uAm-㦡)"+;VF@(J}J6vvL'^mts0gZнv UX7d{2@ZD$R^2JtVi-E'ǧ~^RGu*):@[gҧD=Bh;)!6cCjʇd9چ=X9+œ>͠Wuf`_)w^OtbrAEjk;M0{/2Y5iqJe*>E#x^9NEYSWF[RSFnvP½+vi<8_``wѢ˽(.h7IJ! u8|UX& Rv+',A_׆"!۳2S+XLIp't|13'ww'\Ԇ.QFgG|(8_rTC3*^hWqkw2BDbGͯP  {dx֢ I2U} [1(FhDA՘ASx?u_uHվdF+5^r;X2.|.MP+[̉%Wr7o nXY2!3 x+1 0wg1sr:>H<x6}iIg!Bw'Rӑ%@sY}jؤ),b tyL O@6~N`pA5l8jYa2S7Ok?{yaڦSMm D9_?\aRYB_L_zT;s;k_?M;o#,;[9>3@U v淪>^Feæٱ16Rϛ>Zz'3&kƄu؈+B_Z(DʴZ6M؀Zt" [~T;s;8HgBow| 2L]6zG3'<CLlZl3&XKr|Zފ]<,6"R_'I8"̙:ZP+C~g #vRݲWr7jQ:\RY*ݑ#mI'V/;u%\AzN.gU?G]=^0VY[VW|v+jxa~iGS413ߔb>CFEs-<%Ey ^9fjz|"]d,UՎҌf`6xMdː~)B>ET Gk(ҧx_pL`?ZeczE7HQ3C1r͠N"mBuov8P-G33.BB{]7hUͯUGM.:WWysE믞(Ks-ꟍ-IVЙo Nt>iƔr3<T~vKmɳxwqzcf[žFAGA%kn~\󤆈!?::°[L5Oٝ[$S4r C;{Job[mE ge̞ ji1PJx4CrJ^coa5kTo03bFg"Sdpϖm'UɄDN]"~0@'VǛ~֐ۿifyLi.;Q n^73[r2"_IZ$vw5R@f[-ʬ#F$hl2 䮑Mm>!dZV[0ݰyt(=5Ghm7Hse[Z)pDtޟ̂)ۚʯSx,LvJ4.ℷzƓz}ki~ /v(~"k5ӽ  q8f|>^ wqlox[|4~{X+!7S?o`XpY Oۯ?%7$#?y6sڼ =)o+^э(an.CݰMM:[ރC'.f‰gF//V lww9vA:+-`x3)e؟?R`*~f}gZA"@5G7fgiv>*mɻĹ{Ueuע[ WkL.0syD0}Ja(DZC9ݭtq^em V_T%ZtU QxEoC|s/\5oV{N]H;a1(jNc!,7f&k/'2 Lc@;j]9ReN*4&R6{@B}W"-9( ɸ I` _8ӿHVYL4e& Ǔ_nUb.uwA:*BqdH0G0>{:%_GMii,DrZP楡XŐ(M lokRYabas;T?8TN\X4,7-tWqݘ8U4s_Kuͽ0پ>nq*eDۺbrވF <hs-2E9:m.!YN=򿃮)s2-Oq(/VEƛM3/n\6y3?0dh9M/)WSo=ykGQ;ϢuYf ^XLzFo ^xjSx[ѻ6-N.oͫ˂]s8olaU<-q?a5KNT8YTG_U_'b7[LEL_5]x8Ps~}Z' 1{ a9_EO(?on;}sx}Fqh0Qן7:F|W1 7X޹|Vlf|Kl%7 2[^.x)be 9|2/";j},5U1]i DT:fv޷6?\2g{t76]s *#&Xe@:ZWWY *q} ^]X-!pNQw4@P\$`~63Ij &:tS6skSиϐTN -r #Ht֏0NEC.<|D[_ hC6d>kG.NVR_ׯu8gԑ^bT6V38"?k| ynly) |v '?0=:qBeUV'0GcГ>}Kh_+/ N: g)+SJ/, AnG#d.хta꽰Z?xɛ%Ѱh7pP!" ms,dcayqjT\H[${MwI[vdXlxUs9E^MX ݍ[Bq$~ bY̱WТ\Z#2% -?l z\\_ /d373rK%T _6f2c\Ȃ3gyZXnpI+)SX72hcfXBþ2w{sG;/[euަL$dU>rVYX)Hl\qgNb1kH#a0)gh# ?c=?eU6PH kփ;e1 id'zd_o`җd_sS.ӷ,D]xE2d$_ǐOFC0Pɳyn78ăŔ]?ea0Y/i[_1aut'(Fe6Ee΂*.VLRX8Wr;۰#p v#Jǁ&s7;!?|H;kZ&O.:`U t}ZE*1tOUOU!̏GL *y揪:Y$DJVqDSq}H2~ģXLN&'^=i"yl཮w=4ִW[GvŴ))FQ24JV`4S/Ev-? 2}%aҼQޫ(95x/\k&yoC]M#U\%FnmV?6{6wS1^ZUr#+!RlWg8ZlI}ng@cr1yqORȗfGl\v:/b_`'#G _#uJ!Y~p)Vq #zU)}n{Qb"{ @cz;28: # cהo%]<:P}D^ffH "3x]> ϕy b#6:{ޥ3BSItYrR%5 ME/B 92!ISa#ڊrۓMU4;K;)Z+Y>pUh5ZYĽzϾ5Q\yR&8~@*DD{RlZQQ> <% 3Gk̒G[%L\~&qyzqfu- lg@~;-:LaEn(/VdnFX*05-N#dWeo L㡒c}gp?(r4viz`:۾> =d]JC L <va;T#+bRj/7T=dFe|> A5{x_|7VH;z+/@!j {ڼ֯'&0䗉A&-z?K~ThBx=>iN֓B/3| QU#oJ'JfWQG9I#&']_\`+Dce{dOQC%iL/u rV6/<,vQx~vgy*O+E?j ngZ6#0>J#1d ȶlP"1kÕ0߻TTI x&"4NqP#@WLo[Wk?y1O Y*V"d$.ѧSFJ v-|0De R(`R=e|`"c v[J]2b;0<ĭ)Mim+Lj@WI ITtTxʄ_Sfۘd(hTN`ZO; j[[?Zh$nNǍ< G~OS _ᕵY"=OcN_xpwDx^b0sƉ򋇵a/ԃ CJ* !Rb}$F,Efݮ=\ ? FE1 RL]U2oLCcwr:Ѓ4ԌX3h5Bptkk4)OsQB7e߶"5`q#Iߺ5˕R̀sZU]Ͼ)Yk+#ނ"7L:f.2oDVNՊQsS` 4E_MS%x2ȁ_J}Ʃ:HFBI,iMm Gi}RL {%y=ECJ(!, ^OʗjU7'u\۲k{ *x? `-/f.N4s짎G̩pUZpNٖy3!ztẗ́-RЄg|AWJ6͔}"aQޫnv}0y|xW4!#8#'x|׈JV. :l||<=`aEJU(-p1y1 鏁X,JM%zI {5D~si ^U5s"0a [)]} 6ͻb!?j#`;Ѳ); z,Xmw!NNy\>jk:+kew_ySn@2fjn/S--4<7Zx6TZgB,DʬdľG gĢ1[dUzL,E66Zxy88m(7HfGaDy'j(`޽<{z!ee|RI%x2_A ʽi5?Uc(qo<Ԅ h#l͜QE[br7t %y~}[N> w^ahGS:W?lݽZr&=6-#-?cBۅy[Vz |FbdmF5'ˮbOq>S?,]Szhfݕ|V_n.%kR|;C"+Mg^U%٘o}8mU G&gCáI'ii\㹶 cKK4}GMSXFd(uuđOոg s۸P昊؇k'29!%p.iF ce:qqb3ئqxf 4LF{Q7E ={U-Z5NgcɹwQ A.eWXɦ¤s`WRz|HL ZWkz}5܌FG&?9=(jom=]yNlq m_.4zh~=aK-gzբTt+<6: 9ȔΜ Z".2u]0aqg$/Ѩ#4xD+lHkX*&@~ D϶g 0\Qfc@0i:r%8*۟NtrTzq&W]W#@0BvK~AΔH[̯fcr!1ם]hds iΛ 1![1\9 e1~(}V|^93 AZ6dxy{r tIi&ѲTj@aBi 5qBt@0%"dj#k % )4zӏdFяZ8gQV4uʧ)DMBQXXZ5i+/BP 5֠k_nJVzz&67NDܺ-As5jkHrNKLfU#o8k rg9.mTGvId1Z܀ι◄q4oR,iLv; m q}Py>1FL- !a FĤ屰. r-A*uabQCFdP_jYY\vSFnB雑z*\]oߒo.|#]f|KB9 OK'X701b6ha,!ǝ $@r̽^#Vq5+NZF Gsgnh#+mX$:pOA)t8w9`Na jbߍ6 LDG΁ C/ RxX Y&ޒq.RNO\BkMHLBsj+yIyMFoйr "y.G 1~^Pe%?L}*Ɠ>svaQ.p9r¸F?!%2uSw-cCHN4i $V>P^'&Yq5Deg``)mLr]>3gTrt-=IjxT]{Qv(|7 ])~Ϭn6 O~ ,k=I_4uˢcӉFa= d!H0KqxceH2ߗ l`tMZXs?8(HݸAj|˘5Oϸy-z}r~l1|̉N_O.يr\:i'JQi})u>tw@萷x9བྷ Bo5o18QOXT.dYuʀ8o\(?KZO܂}u:aܳ32If}I(9$$/[rG]۵]{B G}NoU"hέA7H9!aYSed.*kbXLMpOI!E+ť+ L>7 Cnejb ʹ"߾la1AR"R/4LCn/ehFƴhb|MڞW _o|j8$0q q3WWq{͂`by2ۈhZYd6< 7 8+|:&n=sQ!QtONIψ̈́}ӒD; <TMhD~oz#ndR'G;"XwPf72n('d7CGu^u5}X`  C6 ."t_^!9_鋢j^n {z5,;܁[_ZGAtOL'\~4t֟JNIoY/\w:NI %29Uqy倯9$f #O;x; Pi ؞ӢkGt_;Smyhscu'ɵZ{Ro TyάYşx&#PA$psIFf $n9m^aq:\_ ^WFH9m?KDq^ 2nxl=IVO \~%ԊmdX2x%WӺ\3xޯRD$vRlAIѭ%?=tk/ZI/(JWuxz[}nb UY"+1ɟ S""Fg$ ^tj> U4b ?$tI'll?e5sГN[.;.H EEHFhhCUTƸ*d~px1?zC RHP]#4@wcp4 endstream endobj 150 0 obj 32787 endobj 151 0 obj <> endobj 152 0 obj <> endobj 153 0 obj <> stream xc|&o5;[wlؼccmfǶm9f3Շ^VU+ :3306F.Nrvʦ. > =39*%‘0ut_JDM D V ,fnfv6 %9$.N -#™%JMYfnnnEN@s[ Zۘ:D@c֦Vq5@%B?Y G05u;׿jQ_11,y 181ؚ:3#5'8E`?f@[&.j@S)L 7fn `cbdae:Lݍ-y_ 65eog03v2 43{r2t58;`4vm/lj{9CgG;@|tژ ۹8, .&Io kl lL1_s@?W;g_۔:LL5۲\?LjN_Rdjm mn'Kjilͭ:MMj&@[SE;'?=3T[ٚ:9#fjk?`53ښT?acGǿZ ESSwScE;c ˺!<7I~/"6hk=>ylR<LQ1 *mp9pА_xbќnV Шh` |<0T7LqQ'"CW叚"*!vZG Px Njܘ >J6uAP6.i=LJ?> .yݶ"9QY|DG"Fb̆_;Uq?Nr@t V@ӠL.\O,;ڽa(dQ;.D ϫ.]\>">{ܵ>*>!k[2,UmlKpP$Ym?AzAOb?1KU2H^xWlN.O4ux|Y#߿Z3Kgc)"[OvBzM{5='ͨPZVA̳a澞Pl)Ra/= nȢdcLYF#\7ŗskN3D|K{ /UԢT,^ӣS !.8͞+{BJ[ zdU}g!k k!^2(e-7bfeǘU[g<:J;s7&w(ˆߢApr*,NԂ\(DK+?8Hk-d[<(ZV%<(mF1Z/ԝGX[#CGнM}Cǟ#>N XV 68%Ã@+4 좲o5y&t6Ǧ- -a㺂DxhGe zB%LaNۇP 9=p7xMNj!9?ޭ(\o3)Ϛ0*#0FY [7|ƎgY5|fD26#~#[˳U^CBp8p\v)~ui~C .JRQ:;Wnt^|g[M?.e`*wysD9,P 8$C qK[ݧժ-Y9x.Qfw8XC*s4+5D&ҲFd efً/Nś~2кE%rz U+ܰRBL)<V4==bxa`- ؓo}Bq.$ɋ 6}W pdK韇??W ݧfC,۽)_3*`B9/acԊڒrMדq˭bY~;-Kx$$ +;> Yҩuq{W|̼h.0.Fw 0a4J 칂b]EW+`yY~ O]u&)p8+E@X<(:)Y– *sl S*2ޑv_^{=pNWpR6>RLoﺻAcY׍B3n}d`۱+ md jM=Rv[BČ ldY͹e%9P2LL \1d@^tYޏx*]NdQʨ*TJ~~3o%`w`O% uNPgm9=W;0(<1(qTHf_38 X"]bDiL{Ќ?HԪ:VcN%p5YX2 9 !__(rN2h#`pz.O>AB_@_BaQB8S%I*~< 7͈=p#GaAƐrݤнubُX=3Cydžm vwEEaJϱaʼnPea*d].y xUIW}D en">[SHukXTȤQ N //e֢m.rw"z)Y:ZmӼ!iu57oیW{"k(:iu`K'ʼnqbCƬqͶthbX>aTBH[;PgDrB{:w O6 g m轎0Ki9)ƮXFm*< Nmm^p}ȫrbA@=p_B1@T a5Eĺk^{12>q:^廇J(JXX{ KV\K-o;j6V3߹r8!{0gxqM 4JSE:adB |'{殸O&LhƆX΄,Es-S cŖ.NqaTܠ װhNJ&T?!kCt$<2JEII o:?]p\ý jNd@oʪ["RvO^{cHÍ`-F&)-,kw(5lWO(Aj%L]3r*/Eo8 DI88G4x!Qo~6|ͤFcq{2-O4rv[(#*:4D$o+[S9~&ώq|cmڄ I RcI$xʾ1(+l82ir\:NӪ3"OmT+^tW8z ȄZ |T$:*&ݶ1ib:,(x6 zHf% >L\A?ڋPqJ c̽=/{e.܀uoV:#\iͻ<︗'_LxHBlQ1M^:@StiQG,O2Ip54S0|Ϯl*oK[ 6Ii@27r8VSbIuzgrua3cCq"^XZzr:NEm>iLyb ^YM-B|4o}"%ɴ@E[4kˑ2 f*NN1QCvSKgs|>P9-G =)͠(ZnMny @;z5wXG{al盏 2)xvfXVC-q['XTD"2du&/9?$J"z9\) N3U}#-|WV9CNƑ `&EK8?<*CI0srՈ:'\!}ƾAF^xA~&hy囹Rh<$@uF —_Fx |1kpQ"U@8:^!hG{< JjE;%1i90U@aQiaOƍ`}2引V:kN:KV(M9>+8xܧK}V)6QEGl1RUCUR8S G@ck)eA\[F !"e2b&]"v_?gYƩA5n+^~㶡+-Ɉ]<ܡ'6 l1>8 G64vz,}Sqfbv`A@O%‚W+j֍njhC M RwF|9n섣dveIE!͉`[[=֝6WRIjGL&r])3% ~м 83IEy]ǂVy}D{vڄ>||uB:(4W))r똎sdV6RTcs;F<:WB6⸟tXY՜֤*Q Qtx71f׽M}h:W&ZԎUs1E QS,2 bM# a:>Hq'8I]H)Q~)Ao۹3U*v[tvb*yIp !FL; ,4DK?W /3XO.$25P~־P&&h^3|Yxvq@d|L.g8uaQ)!Z}q'qmjfUDlHU=Wp<`I'~5Ag,Ӟ+Ї)gR!-u;ӦLYH#%ug]{ʢ?ŀ0d͍xzƞB}LǍ.@ 7~Mʌ_Fvldtڌ -=q E"a):Y=vquUiŤ.0FnyB`O$=>yOAݑG&JRPלmƂŋ,>7 smvZC*< ?RX4Ο5ٹpR@tF'ֽH`ׇ\~d(tȉLj C̄A N1hҗY^z_Zh{ӝ!ZR5DI誹ig AIz\u"ptR?sy{`ա@Sgt^Luz#MȚJz\(-8Cp;5EK)n&Z]NMJ]2^Ė.Kz!ŧzn ;g a/9y/́):3IêY=Kkg3R>E:I1QGN`} EՋ~؍0D51rw~up}MJ"ߵ ѐay9Q.Qnlz{I! Xo1w6Ĩ;6hϗ#Mc,AQh)1;ce[9W.KWJYsaBh(8A%- LiwƟF/|CY|q7v;GVKEϘh&"v2^~-GBN} t~wR5o$ lHjiהp  kYʛCkU݈#`ŔƄpl>\2!ڂa: ߎ?K"2{kAia"o0M 46w*Ar|?@NIuhLYɈuԚ0Bch*c't4.sH@S\ˮT&g2Ah}AJ8Mo(&NaFϓ!h_ɯe"?$ywBt£Vɰi˟T:i3Lr%17 1Up"1s9z@'-ҋV$' yЌYUQ-qQOWWǤ,ŀ6]}E#kqA#enۆojɺ60OA ɿC` 4~Bv/#d_f2@&T ;Kc}8^'79rpBET:/4|W_hF^8la9B4jM2'lȱ-& LŽjg7uΉ,5ZAE) _%.XKx.CVq.#+WQ)4M1_dHO5F}T0 &tsֱ-;_oufo%fՔS$ Ӏ"3Ӷ5҃bӏLyB0OB"eޜ:Ɂu4^sZvz 8<fTvֳ4Yh} a϶2u-Q;9Ah lBis`],.sJK-{FUEBRٹ|n_I+h0r? D&/+-[3pvjC U^XAA7YN+>Ûh|6pmeع+ W_̅IC>6rfiQyN *:z i+YAS -Q2N!CI>[c!ZZ'N\*H~>-_+܂FN}W+eFN_)6={x8mp_&@wΣ^nkF…tY,K`l;Ɩ0IOa3o|2!'Xy^5aCJe{F(mc,zKeϼp mpcdG@8y官`PLɩY`qK`r\ X.fH  b}猗0f# |(퉌*˰Ī {(AT1 qac X|{|38ni!G`;~\ `F{o_T<jsG05P{m3 ZBZL6~>L_z`zCgMUIa^9 ?Kߩ%Q-pR.@B:Y}(j΁& ܡWr?)y44IuSp X NuZb-c{zaJas:#;tbHJ׮!-qRa\f@t{X 6Z\{;yV]ffu>1ʧ"{3*L lAl FoNijW06BrPHVٳf|~Q], [W^vfQ}Wlu ؙ9:nݭEp! Y)X~ Hsl75&k-# j]go(WoR0Ra a,7i$\'?*G %d c^.Rb|QC&QZCRtdXqruTxn)JQM@"XfX[Ԛ"; (5|4SYP0 ot-p-0wǓ>W#CoddmۑJ>Lq%(/*gEK>bCPHg>;Ь(s-\zK hMzh4# QF-qlegOj》?w&ٙ+r.A,Edd21 ^S)z6e 1fskɮCP7`H@BE|3ǩUϰsh$G3f7A~y=//zX֑EGqj^3(VQ;N_K%޾bZ* .g9#b!C.ѝҎcz*ʻ;AkNq`b?ɔ{JÚ:-4bOR_9?6w%ixY aT\L F7O̘-R,mݤwkb\uK'~N)$ ht9wg}R ECՏq6ۣ։4:g-Hɳľ2'gq#굾є:!^03xvOt.O ӄbmܳ_ÈߋH[n6XI$2Tץ:{ˉ|}DԡMՊρ`;iηCn]CofۧI2u\~o\?#>i}8T.>=XMc邚0wvo;rl%gv#;iU>XhGe}3<(km=_-hJqU>k4spZSsJY*$naН43D:OJd\>!AEu=ez6]H3CX:εx9j~6=(_;(R}z+Y?ĦeVCUmhDle>HDdqL<#f U!;c&3~n`x ^oPi W &M'9,+GqW;n.9(GBX" 㣭32 / SL*~piti-}*+Gj\&QYt [ WT|Rˎj?xj{5!o-4~ن>u*3OAF~FqJN;=X{ ߢ2cKh-CAWׇтUk-rAau>/,AuuiYIk+^P/g)G8hI^Iëdqg EnWf! $﷠/a6}z 1+ ]d?,d8%%1$~SnI=̗,B&l8w^]H-`&tHK֜O0}ſAUaQ^lWNآKԷE[Wg壡&MO\7քuf7-ڰE6lm4nɟKs RK$zYvB7 :8tc^ +nYYaKAǡ$x{7p2# "j4ˡS̶N+a7!ᯑ _`UMsݱʦgN/RD-سNV FF, B59@'x urg%"Pho|3K ͵-r`:m?]g.!k*Lai-?ԇ͇䪏gA\S}?:TO*ƊO=!(tš_T etqXfěu6k1LP ]}-n[e>:g-I<.Y {gB9HσyC bQלYqucU) uv _Hઍ0F W T܆_gqU. 3 8B'=oz_ͷVtH1賿zn [ʷ'+Geͬ\&O+/j'4 8(_TOcX+ ,ãׇ5tɭݒSgkm5{GڙrO 5ASqц \q0OON0<޵j{OHbWzjlQܺ4@k?ME$ Cz9ZˠֲjziX8QS6OՔe\@|s &Lm=`=̦ FVReS$ûww9zR1DݝӒ0b!YErmt qj!"ۄF׭*jOxwD67JLѾ0c n\([C,6m.-OY,,͠m)rf|nH!˟O.Z$ARe=.,3B#$X?mACM3=,Sٯ Z"DW=X3"'J0X- `F`]I΢תot~$Oqw)O"Xqhr%K@UdŌAOBP7aY3\*?5~Z[iA{zٞ8>JqaKF"|O*wi8> S(oL!N(Li,u/mw|Q_鱴r\r I5?P%PF ꏮ00|-N _SX,b>yyd8"^$;7k{AʍѥV7Wgޟ+q[ӽcv;U0`$< nb3챉~l x 3MM?]w;tŐYr x>1ws'rƛTY4H ̵ef'!;,3{muhFA#^: [lKxŮ SҴ#ʤKwTx_ytzzi3~#=e?}p93 Wm쉢+XS59BщlxB \YcR $N=`I^|3zy򄶚劉\~cMn=6ȅgx3Fp!Iȴ/BޓȺ⁁I:,W]۰+ awc ǘP؝>$6sx0]yn@"-"=I#=Jo z',fKȫ QF丫a?ײ(O֥ENIl/Ph\D3OI%tbYGWk'\MP[IJuWyrn~qpɑ&?!@ 1ai6%)Mtp!DWI` d[u[{.w#bAmCEBCpquZide5kj_ Er} Cj-ziݪ2˜UݧAUtVUIDysg}:e{+P+$d,]N@nݥtK:AdBzK0eHS]FxPƣF'lCb;Z:C T̊gBt>p!Mሪ}>OGK 1 \ 8Gp<GP ~Kp8{0S* 3]&P Fw.(,7T !%OlZ"7-8d|{ӷ2ҏkOEc^pJm:#~F)@`疽L:Hh0"W\CR7jxAzz!!XFQx&G_D#ذ'ńߊts(!bdgg}z"{x<.8_-ɱfJ^LR|K\6$|ҸnHJ]M=qTN^ Qy"R: Pz5Рqhx81eU=Y;,GY#d$S0wEKP2q:S+/sjǓz89̊(3O V+SI)A/]T#G~HjW\:^8|_7hM`f7O3:RiY|D~l<fB+jԜ9>Bo[9 5:*] 6~KuFZUN~{W@߽izB=22Q3n`}&b/3E~4.EPq*TT6x>spdVUAcj6pTiZPTV?6_>I#QI~+]tc}p(<%8Et,wӕ"PfnS`|n_Q!dj7lK1T ?QO̸ v/MAu֍^PðZJMA -ow[Mu$`6"Bݤ~rKE J] ?αɅ-ʽf*MeÀ%0{)`kL2Hj²?~z~3L^"??;IƩu?;@Ŀplpwܒo'Lֲ؎Ա5^lO7<(#KT(LX))V"1_]'pu9ޫ7֯/Ov[^dR`XTtN{8WHԻFSA8Lv$ޑb ki+ko vk ?/)p<= e8jn*7j!ObʇpWy}]Xkf[OϯdM 9VyS!, tvj h\acٿ_SF*}>RFmR Ђmo eEtL%Vuy٩_N-gL'@o]T(C~|?9{sejgL+]4p!9qHD>cgdrZ;&Lp-g*/ġr%6;WNQiؘ%۷tL[Ώ$3[=ے;1Ĵ`Gp XU':E"U|SN%8cֽn \J!b CA]ɯfW4=QJ}'yVLtKJI{ 2AhUT>"8D}7Z*SJN *HGqL׈*$X[KG3u ga}Xt Z.\b f>)QA`Is hlNdLvq*IH5B>r$m7/+i];c5C,Čg2_X҈|@ȾZm YHk:+&fl+r"BB9ɣ 2п7q{UYaAM BA`UVoN(BrաQ)yFq8'T%ߍ@EU{|zIocjcSjA:9YA΃sx!&bwMdJCjkY7c(v֤NDGEЯꌀٌWTAؙqT-h8< }Uާ<O~7<;zw6S|? \;S2Th5FWOisI䢺^ҏlxt[] lJf*m)OBL4z)+qk߁C`"ZL˝C ZKFu9 ڭ:.[,2j{O@QCOe{,! .Z75 YFP#R$1r4*w_XU?!gmT#U^g ^bӼ PjEV4Bj`((zwTOȲc X肓N&Q'1D-5qm&xSe\4"wt`U6߽R5iHǰ: 3P(6X`s˔W5R{|l4ꆳiƈuT=l#1[LΩ=Y]Gw iӉ܏o||6FžiƯp2,Sn6د? g2@G0I9cQY,͡5OOZ^,:z3켮5Uk۴d&1w@+2f;lu?WJ+q>^N1)%,LGv3UveX%7l-gv Ι|t)P%/Rs9UͰoq{nsfEΦBnxF}.Nr+5 U󷧡 55)6Q3 bSOxi 0&Dм*ƻ /tcts,[q3HgX劬Z^KUXyc"O+Si3ǶָoQ1d>ȂU-ɺb=t$N%˯46(Л(ũ-/A"3qtܫ2\NJ`Y-6)ӄ7If:W$!%=F7hSP ezkS)`ҍr2'Xl# }׏-!Fw9CzRPI~nq X,h7(N9ƈG5_ rϛmz^7ç5Tڄ)fw E& =}'9)ec$,)+lكM79 <~1y/L@|E.= -o&^ M!d `ݞtxn\燺%Նm9G2lIk\/C5 |af#v_S.a勛wR <+^.fvݩ#i:Uf$0stĽ/ g* BPod2|Zi V7뷽=EEeC׳l^i!V11bµ#N͞EԍgW 5PcD\(:D\#W̩D9EcMS@#df75?3!܀Yܾҗ#,jÚSX$xR\ˋYAiëϾJ@Om BY^y5dǼ<_}C7"Xz+F_7BۤG<).RrBQWb/v[}m)*@,O1mf8q 6P`ٝ꩜kTief+ge6('ߓ t5:`G5&wa[-eWVT~hAv9l1p}1L#5%3%IK#s|_(JOoIPBH$ ;^9\z]mW>Ĩu>W a+l 1їWkE~oyN2r{!&6@kp\+AZTڸ =duZhW^ D24q }g`J޿?JÎwow^*âLTmo..Y Dzdz{[Psk S%: \oСx3z"[y=uQJ@(?J&?H&<'ߑ/'ᖻ?a+$Gn7؎!8CK%uDLH9 .:G;\aFvK!ʠ :<]Biij$1M fץ+Z2\>e*k鑒WLGiJk[>9)xS73 ۱8ҧ1X<V ,Pn*y[M3ך j3܉\d ҥ)~GhꅿA1[Jað [>DxO-KXO:J8{;9~}灤=<@"xk0.ݢޜh#,MH]ӗ5.R>&YR' "-ʜ$|(&/.gD1#RsO8?W;x:xךD&aqу$2-4kSw_*E#X\e|j;Ӳ(3Ҙ݆_`·eI֟NZJKz W2\85 ȩHz6B80/ŧc+)Np n"Cdp'߻= (Y4$XLʰaMƲ&UYB=|.|&:sU;P' ؝h@ƇtBʽ3K_>(B:FW 72$),짃˔" ߁8vyX(eTf%@^Z`xT*$S\gRݏ>I`>±U@Tl1l5'q/<t۵F+Nn[|*ā?/p\HMo\q:GQڴ8gx ^+*A 2Q۾pQJg tsQY/V>8)2i6X QV!Tk65"a'; C؉3]KҙdclL#A[˜ 4+v r?F8弶\zעQyeƲ8 G፧μVYA=gy`E/\>Zʜx@r8|Is_3z۫bޫ.vÌzq9buK|mEJ.Čp6u=뒒KY SdE!#i H$cM>zeXDkb} " _| wb2Q9_K;Yq۪TA.n."I.0> D1?0<jid%ZA«-u}mqWE[>qόHuK^=J>t8|5tQ_sg'@^z )tnxl ,Pc[_ɞcLZ`!.ŹFzpWpٮ4ωgw_EϴM{f0sNc| 0Ч3Dhfuq6WE"7ShAWʎ06Kh dF~k簳a~+P=w͂˷CL=݇2`FjL)#nkNj'.g\:.?a4PMK^XDcsfLF{D ,^EhcTb5s<[CR$69Z:<Gc;cN]C=>ف^#7`o0 C s\#EEb.[=bvf\ϼWo^';ye;wTiln V4Ķ{AU8 8B: xQ$PAwK )OP A),,_t z3^Zu Z⢑"Y~@NnYW[tdFuSr[\R,Τ-tpe*,{g<ɂIתKDu ֐#\Ϟry"Ϭ=RϏ]Lԋ,I\ Gk)jI9(1}k;%, ur.abջ*grҫG=qIK[JazUnr4~ שٶRtg~Շ\=\e+JzYD壽e|?8]O"] GlPND}^CDsf|3A"c,FO #l-Q[zĞ qO~1^#OGY=b9u'Ykj;*7t<`?Lb t&͛Ráj (Z3 M=+Efd$ rbP̓r@K7dWB3o}jIkq\dTX5tٞ \aġhƁa6JpMPBۏ,3͊'P* :r#[Pjmr;l2֩mƣaoeV@+pMN [Q y#7xkC+myf9l yW bWɻ7>e; {f|~CUIϤO *^|-S*1+4!`n_O4>,:,9WC`MXD"HҧӬ$·<AkhEԃ_1\;{^p_ .NLaCBXu SbDU܆czSPUɄͮچpmы U U@S(Kt Y  /!mB`8Y:f` nÂ'b M0| ]Ag h{~fm$l?ˡ]NjJÚ)!=[0E`%Ii3 `U@٧ϽOw1c@@J`x?i3QiKfgUr %1";)+pTUo9 x7Sv^:";0'.ͽ:s_ vg)>mHHƘb9Z5 ՎS[. ǢzF+WTo:V)X_/4w-Q"`}6?ޭ v4Q)당=Ys\EFPkL% u52D[ܔ)!Wiΐ"ATY܅,^ݫ52eQl00"r˪J_\sLT0kv DQ@[g~bac4ֈܽ#İ΂5{0*,bxX[ )&ecWb.e@btv(/I>b9 3c}Z4bFSsO^a&"}L 1-JDjGe:6mh&N}:MTxLi NÑӠi- l[\b(,(TSj؂"ugBČI&qc ^Z|h{k!7yxܼy |o-UmzI N# [jR&m*bPήFhdJo K$ȀQ4LUlva5< Q^Bw^Ӗ Ƹo箞[Uπ$ıgJhj G-鯴 m-TӘ1 ӾeyTuwfJV'f^K.3u7rH(Q!Z7ڼ$~Rzo$ɧxmmTkr2p V;{8TnE=Po2O,Bq,+y|3K$iLz]G9ep!|4Xr*8DГ5g $ 9AP 'xCnpJ0tF4: $- @ ,*hVe&:1!EM 7P}HG\A5ƀlIwW`P螺)h ud~;xO};[ig{q|8pw77OC0[|O숥x&67䡖osH҄|"K,MP?o Ht-%ۗO{)ihvoR,v-o'1˛&v#OSaLXDw5Dt .mg}FHDV8UyN EQ)pUK~{ab `D 0ECaU_gg2NT,ybt)(Vụm >V\Ƨ~$ {|IZc;G,J7|ED!ϲ4>`8]3s(6=cb+V)Hh_`'FQOˮ >z{kj"38*V%~F ٦ ?\ɲ!O@¿`ˏPAmR4 :twka'p+#5(-RHݩi6Jס5`]" ?Yz{ާTC#C]5 D;%E4qKFiNXrf9knC$]NeUz#$.3sppb~?E1j./&gc[F]q]صdv4s _ icxDf ͖Pݝ)v(&6ΓT8"+ucRlfIR/#ype@ T˂FT_rkUmj5FUfL"ņ,=Y/X^xm45E.cM}Y_eavo>7c2Dt 8I)ﻐՎ}1׌fZ~M+"5EA&<oA(>s1u▃}!\=BH>Y wP8eKn]-v-[i}y`RLWsp .9 ̓ g }.oaʫaFD:ߏL+TE~ti0u> UMdE7 ? о*3ӽ"Z x3)Pw݉M%@1g Ϻjs % KYJ; 3p EXRL^?VT3Q\|uV@Wf>2ut ,[N y١+!6s@iL { Sw@D sűgXT(G Z#R 2mpg Wc9 b$L2SX }5ܱS4cJ\;17%rA:WZg@oQ|]~}OZIPL˝؀i^*uRn/+sCDQ{ﺏCn4nQ Y%4^ۈgq}vbTJÄ5׊Ay|Ag56V4! 섵$dU@FC:q miG{qx$ַacq3UkHSoTp),&xW'k7SԓTLM}ʪW5 .X= tbG#ImproΖc]v^i)Dfk815 }>3-.Zm&g e7R IX [& 1o9HcS8g5fܭ!:e {.sdPGWiN'g2acdQ9]4'}scC3T;i@=*{Ł.Qx|c$am?Dઐ{+ُ &0Ϣ,6 8}P!p$(l{1Q ޞ]IoTzU40L^>͕^$y }*6t::s= ё+oXԯBoz0Xfdh&8IW^inAL7VtfX"ߒ+^7R23"ʢ5)ӔP5Δ=dЁȬ_s? ֔Fsş[PalF__/*AW[vC^tAXёa / YtQ*2+J`A0Y1 k>Z{ض!L[b ~q*{UMүSjBмU}&yBan*_ô!Rז>!{ΕwwY)Cd2/ĝK|JpܿM4e{|l8ݐP#4P,?>W#Wb^\ e_1b3ޅ~"1+#K^nQn$9\w D&0xI.K5ݽ}:YiHh?T!hC|0 DMV!!蓣^ mOnCez86ѦAkg+ðGݦ0"-n[!%JwtI0#(t!@ЗhCV u*Su4?1,9v$p&6e#K G| C2AT:/+ h.Z8οkWıT3[= `Ηhג+|`hs:vϷ%XWNK2#%ז\*|I˔D]r`N ^]{liɭqsIK!|k,Qn*t,-"?2|/NU(5`<^_ ژ *_o0qHE~R`#0TMXN7śSq֗rbi?mu_f4/1Flל N^"47&/MK+On!V@b AMr.mNMy&􋬘jզ(5ZztF[TCKZ.ߊBCq3TWXP,BWug / zg;qN8h9ƌ:N ( l }D5kV@?XѷJgvxVw[a 6@#%G˩+yAQ F1z^$u>8݀xUE+7 FJ居6xZdi|3 0.z3]K 5Hg1Q I+m[[\3{,HOJ$WhM4Sr啡C֭mSMvą&L*MO1|)~U0O3})$dE`E}0P Cyy%)| N'#-5` uɂlB\R{:rw_~цTH&YQ D[#l$NuLFF0["O+^J2e !%? 6Tc3'rE!®' "an!ta_.mixwN*~&<")(%n%3)?ZVM)pȱQCTLgs$[O>j9Sv>}_֛hVC4] K Z<|4Ʊ 㲆,$`/ĭBCxj`_6TvoX ϽAbd9:ssmg+s6Yb[<1q^߶8nі} ⸪9(W:?-6gWTFNICjcȃKA}aFkO+0{s[Lk.5U3 ޻;UtT$.l\R.϶i#Q:IJwf;KDG=0\˨%TIkF͊Mp)A H T<1 ?DQ5F#GB݁ v +2#xQ AH `xueiKdOj6 *G*y5<`ڍ/Fk(fn2@vPַ꒿r8V2E?O =С5S|>1 o ih[CzҴQ;+ln]cUV">>= )2`@ fIpRu_AQIaARFKtwAңɃ%r'GY𤐠ޜ3&}ƛՠ]@aIrtwe[*ѱq´|oa@s+ 7_~A\!FeZ,f #K0DSL>zplD25qPXQDO&XQt'dztz7RAab$x;ׅ8m,F fuX_p f3L Z24h8[n QZ#/[}'_gun_zl<~|taȭ3+H1{&S?}@`Ĥh}C&a  BoXu%>d4 sveI^s!u)`R94GڍGEc._SHsHP/ER7l+ b4ǓGu2v}z>Kʾ%X_ܞLdã;L(]Df;]MB~/Ra>@N>P\K¸ Ξ^[l4oghB ?xc4hnkl̜%Rg mB2MΪmxLDwlG,ʢ=Sǰر`'r(w?R_O=1"vmŬō/\>7{]MRbR^b\ZѦÝlEQgȦxz8,g,/ ؞`\(c9vcjS$/hUW!Fs :|jCǚ,o)l%h 9fAd:{Ôn/.#0zލ3\wh;D`bqi+o¥GM7%A%#8`^% tR V;V#i,⯮n-~X.]XhuXr,PId)7ZvG60hEKmZjdH3'|X1%mj;?*v;]qVj ]N< 幢O{,2.O氟!jsCX^[8E2!ݛbZ=ΐZ;tQKvKpb! wy׍rHM I pVq+mt\r'3c߫?Kd))po;WG'j'G-&+]%(3*1ag cæ HC =eՀ n@3&])jO5=f?:1鞺|cꑋS?ysvbLnMc mDּhK&zDR{%s GV[ǘl, SZdh*.S[:ƎY2N >KL‰B 8'1sh68)RUB1wY\tRF4d;}}?r;?Z햷zHOPv#O:^2! ⇠e|-iW`VEtf#.SL31jc M'KBKz6p_L_.z 2܋q(st'5Le)f ʼnJrP"*NB7,茵N79k[7QW<ΐaY71'j }7`Iڼ_3^,\(c!jfu]49ng-,lnx'؅} t|U~˃wNv ђkWGR`8=Tq`{dN`nv)wbyi1WZʎ*s0x,x- !!uG3T6DWBKɋQpbI]9&XR6g>Z8+f{w,spls l:[<Ӈ͵?Rx^~oDJPXrL JQ>cf#6Og.[P{z9p8(i01,G~7me۷z$Jp / {i6YyA]Rzҳ;Z-k2Sք}|5zIibx#ǁ}߳ AeK"XǿuOd^Bu@~Jb Y ?9B֞fˣ vȼ ͘P!HwHGn}`W.!z6j·˥A[s$ H%~}bbmbm9!3ZL@s"*0(P`&:_PRM6қC/둥Q5 Н>KseijV]/[xXk2RZ5&zwOׁ(aVԅ \_c'K hY^Hu_@wGQNEO`n(4Ƒ=S@h?aI:mbøj /}znTfb7EmseG F =#߇Rrx4`UL"iS,9|t!$.T ~P?Ŷe'~ VnRߍGeE[ǘ𡈙nH$͎-u$"tr͆21>U+tLJ3nJGR.3ZTE QZLiD'f4(k{.l2vt|5H%ؐxvSƗ+B3XbEVyUh FSB6(1m//;E_gHK{J\K$\[kc^-TAQXtlHoCe*?u}H @:JkR'Ia(B'Xx/o.Qii3vF![t3m]<;ʜ(P4)TTka =t /g(쑲p m; USH]|gi ݚ pCˏ_A",1 sK]q;,bŞ Br'6IJX]'OuS+w@@ ?Z]0B{3%#kEY janc5,)LSxQ ܃sf0u$! @wd&zE 7.DossK?eR }p?(ӕ`Xr/3_(2}lmT!mua(D-f\^{M064 Y*؟z.f@V, b9S4[@d|4T}JӶ="ÄI}Pʾq>;xXV˫`&=@`i+. ¼ِ ^˼LRZx#74LV*?ZdLi%Z< #׊$0๠Uʉ?*~ R1&?K)iImW!B㛣`5j;V:tƕ+ޯpNͻ++ ~g_+k7O7W &ŗ/RvQx8dT}wkfܴU!ٜ`#R7`4DuNQ./wQd1FN;bTط8f/jezϢ36SHd" $BC:DDDFDV xv{̭;ծ孁x ɔmDP>3> 0t`x(.mRCAizѸ.c4H lnx=[nRiٕo!>jHq@$x)  2e%(N_7 z0ujIWD4ջM@ $ZDn~rG͏{KJnD3+HΛ95d7tD TֽHxȆ҆u>6az Yd)}&aƜ *ZW<[NWFS@%*}E*UzR~ Oy'y+ȡ SAszZw7Ћ'b Ajy!f[js0uBc\foR *SP5npF )Tc2ТA8muR:mN1⦽?U6i̻i7g֪| ۝Ag3Ӌ^D_WЧzmxܴ(/5tRrP'4~暵,jmL7ۄr3Cpִcr"ao]':n0^ /4[ ըՆA69X 9.g{RھG 2/y J8YUaQCRyk36Rc5j;rzL ߢExZjڀHVUOzm|+f%H *i6-dY'S [;ij@bu}r9Xf 뙏'~+5%*\ Pһan:3K v ?< c$rӱۺ,8OQ-@ od#y =>U4y͊\_mv; 9EƗp50w :&Fu87fP*\*5/aB6D+aMiy;4cԁB]?G|Å{nox%T!1׫d8_/@dOdG1D*$stհC)=gkD~V̈́BM_Mm0$ݴH| 5¦xI;U]F(ǨO-+,{;>6c]8kdi@h)ɕL;iû,1APާ61[/Lvd#%y(+^'k,X ^oPs,3P*<@ţ544rI lS*j~] 2;oz>ΗF'fCXt!}Zs蓔IR%+z`t aj&{ƬlIaGM\b ]uij<(;h}Pڕ馒_:4=5wd β2}0EN \"@K.D!wehe  ez/",%V$ ;aK|gsZ|S_ O[ ZhY\PV4y#?mr^| 5d׺\∙ۄ懱:EC3z, f%L~CT0=g֖q>7>n>|.%Tù]咭4}ӷSƊT)WEb&r2Ue-!K|2xSS{^v0jڭ%ɚyg/ ?}mQ~0\*0~Bw6:@FK?q3L#]H(2܆eB !e4"Suq"=u]L< $#VyrUW;DMF(֐yQ"B53dl'1()_IoGK聒SY2HzŐ=)Ր 0n<b(cZszߠQP4RCƶן)E^yfmC&O Ι*o4co@H MiAa$ERSk&H#1Q~Q*` 0 5,鄸ɀ,xp{Ji^`lvQzM@dGOp܋ȦYFZ%z_ _7XbH?_)-힌:QJ`*<22=E {2_(\IZ3[' @L4v[ixe\) ( 5s>yyY)O fUmd3S[poN&P M2͸TLDi|{57bτ@!?6h۹Yq"^ٿ}{*_  ,}ޟ #RW|?\k$Rs|Tlw1w ǷP`3d)s 5Rmx/p$0{|6OZ KQPkqi]K^aab>sdBvW^DzwJ1DbuHHf/Uʆ#{gj\T2U^E ( 8cBFDH_F|B= oJVRlQ*zkB SVukbDd>F}1o9V(^7ix ::NecU#s7; Rv&/YiD3뺷 xںv㩓\l>ʗvm95E;A@\Ga91C֡mD  iT8˃YF3$Px.D ;ᚠVc\ee7-A-#7g*4VVOq=%rՋ2w?M/?~v;0'FfnNdzQ7mT`:}W#MZVV>=U^|i'klf19M9}=kL0YDCpC#&w>Hp~@)L.:]؝ף? QEX`[0žV(%"(u?^8T/8U8܇a_yRTO;Ƈvs4xXJSeveYUL☼8.FA6VoQd$&Fi ACfh%^Ō$%0="QðWeO9꟔igTUt9`Hr@m%vi%Oy83CCb eHF~4"DlYv&T@=?D; 3U!8R`$yǕK-[\oH.J,]r|{rϽL]#Ցw`cą41cu6IOZJQ"DN墥ߎtH9O9*OkCB Ω8]G@o*Xo ڼ5{EJaUy' 'SwWT(T.^-O|;.R,Ӷ<057'\X\ʫZ㞒θIoa :ߨ~YpS7)Rz8HNxzW *01T-ԁ'{XՈUG'n 1K{?]2T&1Tm!YDD5x O]cԂV&ODpٲ՝ӊB]ps{UcBd ́n9AzH օ|LōW"5z\gIW|liaI2&b>RxՕ2ϡ)k'U@| {#dw,UMvߛC*@*"p[2HPft=`Dh\$~瓠DJg7Դt0iX$rq;(?$sS\aWr 3n-6(*tQ(sOb Ćl9̓na 0 ,yw)wTWDh>ּ%݅Hv@ha nGh5E )1mQGI?&JګjVʘ]VC  iLy^DHHK=M3\ŒAS[c͘?-M #GEkuA 0IoJMvr羬UG-8L V .!8K^H8xNL^Z`r+S/;`6`yaҼ 8 "JķHLc 5 J(߅u(^EH1˃6Xʇ;6[˕z6}-"؏GTZ~f9EhmII>ҟ0*y6or_#梘_D2:/8֓4%InOLIxmm>r@Q"y5c@$!zg@ G KGI߈!Û_ .tZ6At0 svy2!<3 W_ -%2GWUwR Ջ%Nhf2 y{OW1bx݊l1>Y!& _nzҭwe?pX|O6ؑXM5<`:2&xĪc669\`1Z0L7R9?>A cnT`+%%*3|ϵ~3 e (|nCnzie;=e'.7T*5ysx^<&vhǴK)H+|RIhޛQg%xeH7y : x`۶9g𷙢+,[hc G:/Ђ ŰyX*KĈ:t=v{Z)Y|9tؕokNjW YړDuK{ۣiP-S^C6xY%^qFlP Qh!Taf-[WxA3 + (+L-QIadT_} kw~w9ªwp TaLо\Ut0@ ZʎS,W<^v6$w"5Ѻ2ת̨FEGlv5 4:*b_2s_ft2) +n'ĖP<@Ter8NQQ ^=/H܏}`,O˰4#ݽQo$W۪^}kefwW\y-V<-+ /ܖ@kcXFu˵8 \epqw sσbraCjOXKqS()u T@ΙӉ^A׾Y5|q\漖#9 qc!-j>-/mȘUi/ ʾnf4HH1Ρ]4Tk/Sv4ҖåT ޞ,ϷAa0K/6aNgfNP=dMZhnǡA% n|~ r蕅;Ht۷Dx3iy¨I>zm(Dκ֒F$:V ⽲T*a;яb9ag l@[ϗ=vu%=8X6"<33pDL0t Mbо1f2@L;j2|04>3yz=J92[ff%~_QW} P9X\BaS[j VdV_'Fx*I~%کN$S #פtMͮk!b. &r&Pa(\?@Y~}?a(/nFAwUzin%-Xpit }dFF!p䑉%w5dKC_405ͷA$UD%3HP$mM*VHV '<"uӅ6ȃ[ϳdZi qSs RrwpDQV0H-\l,lIOcyc[e:anV$^v4(HU{h{Z;!8*כt^( vどbS\EքN'èB/JN]E fX5jmk"Q{™U:=Ks_zo9^[FEޯr]@4GC+0 m 7_M:gX}$MD^uf_4?]G9T$b awXfɽ*.2 ͆(ឦvGB.jX1 ƊT+Ȫ'=怢&҂}a7`1!M5#BiZqR-%$΁@F5g<*L.od"z1h !ctMzT1{Ǔ^ZXd<9 IpYa4o-aRsp_2ֽ\eBdkKocVExjCWu0rt@nhF "DL>KЩR\d8˹FµSϡiʼn3:I̻9`.-K\(V,Ux(4re}7F܂fY-B47Q~peyF#,.}dsup&gA hSYkpWGSWj !ؠ$Js4gziAg7/фucw!716=OHU@j~͕o& G@7 [4D4PRIQ1;EvՍVͳ~!Fm/ Ig4"zVyOaa~]Y~` 4;:-[d2& nNw a/ HN7x0i TX5"T |q>ۦJ &r蝽L|p䲾*OY[&(#)̵g@K?!uq*>:(^Ba\.Se+d;Xm`t{_\إϪVyvߚ6.H&ȯ7Ji9.XUލc;=GM$`}Mت ֓މz/Zzϓ4d"2u}muFR5lvsT9UFh(DC4 LXǺp1"pmrTDwMRAn]^_mȟl̾a[ wBܝD) Puh:n4Y˅MEYwx%A Z:~U6$p?ֈ( wPOpް"G{RWwYdk9Ashd;"Ry_7l:y'S(fxgFtM+`2ifO LD9-y",B`[_Гi6-!.xܾm%GBa7_*^f^&"QWsw2vZ ğZ1㬣ýh՚mX#RZ4/ZY4dy0^y:2T AL(J?vJ忧pf!rvޭ`>*=`[8T\6Mg'mߧK.B+ +y r0B.m3 !oJW.jZs=[$Kwl|9;byNb 9Jh`$EsOLЦF꧀=q!y3굎ݥMSk_D7Kݯbv0Nxcl\J}t9Aq(%o%٥څͤ#$#'v, NIƱ:LrJ/!/''\p#=bӠ[,恰⺠9bجL؃k -6j-ӝC[d"_R@BXJrn|_ uqT,3#<(̌ dsv .Q4 =/? <nJ_%1"DC`aGQ@\3N"$ 65ɛ)jBrм)۞R 6-N :9SٰE 5՝{QUSQYqX [gc$pޤk1W— 3~^jbeudHZj{!NܗkJ|8]+(CmDXڦwh3D5'>9^I =^/j7?t6;VYmy72nxlJ ̫ȩR0z2v: BBʓ+/_ *<%ɢ"C6(u+86#]#KGkːD3#ʼ[M>SLb܂]6ЙR=hWUwVh0gA \߁*1~*—'2(wr"`9ZzrfV.tҼ3`A dhZ#!PI*ц&Y$I|686cX:;ϬH9jm##ƋRg.xR(cV\{6~IlU3Lk㑇xsIp&d,{4 m$T!rX->yOpGl{KŹ,Ɖ:Uè2@㿔1TL)^@\NƣP‚g6s {lyjkq|ӿC] qPe!^> <_ nG - {2DQ/Ul+d<*sznM%) B^ٮ,cՌbȘ)7(\%w\At=l߾*9$d/da;(:•b*5Ai ,*yIfʨ sd₴-"}"fm#q9}'A†W'GZ4.^)-3;MMDd7 |B4`IEc~5)S%o#Uk:mm)U : ^ -ǿ4k0!#hъCu 7ͩT[ ͞nJDʖ2bײN\N[V>I'+?>0B~,ct[?]?O+3*L{JkN3̻x (;6'O*7Yf<(.IͣNϰ 6x\oX患aWFHF|\)4PQ׷s<5osGlaAh./"OgZ8`%8=z>D(LJJQ|ΟA5C{EM6sA׾ ͩqڪbԳ&f>3>\ x2v'd?vxhWll`~~¿5AA&GjuY˼_}pd<酘" Œ<-#PݒRq`PW8ӗAգ1c:L#4&*Hb-G۱w@K|˦qQS<4t1cK}06CNJRSJ bW^c| bU '_^+'H[>[_ze &N: :V( .IԵVZäXYYl>חVtL^ oQCs_}lc Q\45ǍI{ȓ{acϟnq [Z%tmq ՔRe$e 0tKM~R)rFGL l]&b Ŝ> Eڶ٥qhk3PBRtR//LWlc2#g9_)~ȊՕ b`*Lw5Ի}h#3ŕll/9'Ν#5ͺE-MWù\hDC[E"FU"U~+7m͠ons=rccn6кyd/!`q jWe'?aZ:E,y"5H v]bCj] -)-kN%8j`8M>3Zf;sjXSd/@q w蛔/ ]aM9i Z/F֮xs .fcGû'N~OO0$eTص <~$?@ˍ|(uko 9\β,~ Rz"PVj(XPygDtlܒBLEtf35؋WO/|Yg8hYok!UJګ'9hFR'dsb Zݕ>Pq))D1_nߘ}9ڸL`Hv-XQ%ހDV}\<*1*~:-KX<,CzrOk@(^WviIMyQt=kU,,?‰q>U[ybE;[h;]kG=lŸfYHd$ &?⍢)?,*gcX)|}nP a{bK)N5'9xIąEm~@M õJ`fH`} GWZ? Z3k`?d쁋4fQ+rhnsX[gVt:^ӌ7 ,-G [SOQbK c Ht ڥWֳi'KҊpQ}#ۗ.p >c+LmKta%yiGc]̏]eFxpzsw6\]cY⺬cw^obT{fKtp  h<5msEٰyP/oո8Y9%_<ǹ| $S. aW-(稣a=p=jDfa_ja,="%%G75QqzeQؤde379(I,>:B(ɏH3hIt yye VMNs{SWMTLi(r̆]}!ìLRÇ')L' nSGBï5Kw\ϢG:!+ؙ̋,3(_Tq3 x4DY O]FrӬ7s96 =k愤:ymz ;NhlD,!;h/S"u?^8Bݼ#!R#Ǎ > JB^ 9-yW*?^XT~ bNGK5r}e&tQB|ee3b@Չ%W ެ @fZSҫޞUa5CSnC¸i";(x}G(mry(r|#CmC>`ViQw:ϵh7șx˥R/ aNM?\uO)48ٮ8,yv\=mƢnӆ%l(8AU714xC36ؑ8:_vZFWްW.Q}6үn@t^ $?HirUfs" - ݔ5Zp\h֬30+&LKEܡ_\>,rij(j:.>k6}f#LC k텃Vu͛rX5p7nˏe(3}U7DF^9sT^I_'I} 7 GcMMӱ$ŝ.Kq;$zGQ<,`[;X$8qPPDU]X"'KfA)"4Wdя+*AxVԥYC,u+e;%dL:#4!lh8KDq)ei-=2IuXKLLE lw)΂:^)PN:RbDTjuơ A<nS|&n7yCʞNHYfRCz*F buCxLdacq̔jjʶE r' j)č2#VIm, ~[b\9Na k-B}ۉP!eFSZz|z*/c{ "Gȷ;|>ujnG ]Z(DPZ&Xmi`VRStz`QdEKuկU,YCUI4R 0Y7F $K2hkǥ$\LI{Lv -"oϐcS93#3Ϋ]! HNd79nV2fiK@ 17DP~ېMN~ZHh1vƎ'%(H) ,{mfPN.saB#G0Uu{㦱3BQ~mY]Jt`m/0Bs&0L37IvôDy-@JJ##kcgZniI8>Z"X')sxj>~_c,OLtĬu6 |1ԅ4-HI$/ƅ*<2+Rv̀7pۥJA Oh4q*Q]CnaW?҃g[D up}LK>ɮoGtO\Uo LO.E:,D(w&5 R(BP95n7&fE/R%uAZPt`/±B^Z/3ֵ lM[hOx[XAߏ}~"(nB>=h?؏n0nrC+7|y@3x59>IUk5 Ҙ[uC]ĉd-C4Ik m1'H7" PUT?Ein|)r{"}|X o2wSW`|#pd.r>Aapb_[#(jI;vKv@cU#S5¾wށ9oq=buR%" qB}?kn"ri v;qptÉI=#[BE鴮-ǀ>A7z_a"沣 ;e-U^zR83$,D9"%,8RX@9)r;MGn8CWEP||.=SMx4 Gl5boF/z-`? pFX+uZζ]++TlV3\& F#,Ӆ(Ϡ3 W]Ic9n2?D7dRBCX2:rᇒw[J+΁^8ʄm/p츩Oޏ]w)H;LJq{8Qd٘MS0`еtRrrڍ5mt "=%[کGM  /[bF-'ciGx+DRM{ᛯTȀ5 z[["_*V2^ uv q΁aԮ>>枊#spy)!}ȅdGK,V?X\(q\(GQeq-DZmQu%Y. I[{rʧ2&z ɛYk wcg6uП7 bO}zb\[ у֨K0w'[-artZaJ )QxP) H o#q*R|dJmĆ2\oƒAWـ6G~ME=WzZLjr^rZsʽAYoGfW57N]"&]8u)Pey=`8xjO&F jvX*3`쳓h^gqF6fB3xZW\ؼ&/EUTg*?<{mBiٔ΂Y`uvV&3 ΜUAG[W~2Y*U;&" X6G$`) p})Sǧ(mL~H!%BռG~i 0.ALÏ]CMًGj9q*t77oծ@<繞xE : )w(f4\zLiWCM8{1zǰ}dfC)'%ׇӱ autC{>.[1<_M\?3l4*NZTtlğ'!c Yk<, kO!$_Py{vN:Q vgYH3L'6UQk|hr,V·M,֎k?624-ZaaU bj[28/*ޠ?S-ʽ3Ǘ/u-;}\و2v:)WWjq Pll5(&9OӤå'wKr .HVG@:"!x؟6?cO%)[Jɛ$ɳzY^SI.54 zD~+@芶FF^!L[eR`GlB ~i=w5E!+ cw>z] RkBRq 噽7Ezb-U& B-;R|yhfIiTgk}>}X('v./iRzN.@;nṕtl<ϼ3iLD3+U:[szY6V#<Τ) k3Vrq~3[To%q ӋxFh̀gi m8m70e; )gJbRwjG~@|zۇjE5 DO48]ވp'G0uJ&u(Y ʦ2 ^R~)Fm2[~o*DnK/+$=^U-}Lh?'NY8(O%{1砵D+﭂˚*w/%Y4.}^ ~ NpHMgHdb7t٫T7@]ltX|gpϏy/CgЛeOzjD\Ԛ{S8xaM}pʊ7%/; Wu0o0s~nW| sNpbMmK1,rQKˉ|m[)hS@u^x[nWJ^hh В^*36iE/aD%(|vgkG0͋Lo,f7_/* cq`0ZJܩb??.ҜdVS,f_ohp~.-|0n|i K YiXƠ*`^Zn-FZ4 z3+8 Yf6?_w_tbsrET#WD]lAk >s@}7'b"G \.1y;8T`v\b:Veribg(<3@j*n4hNF翳W4(W}ls#G՝qGcAnwUt_{ nm\؎ .5.b˓OZR8mJ r'.͢-L wCǔui`0ݬ4h&a±{U.#[UMyŻm)(ZQA(n 0o\`M2Y Fs[wE>( 5̷EÝ_&ܩn{ŸTz&Aln Ct|)_' =i ET$H M%BLrfT [ey:w`2LTy9[9ӻcxc[$Tp%&#: z !ۅ68tꓫ E0nϖg>x4k'-M`j$p5(uͱya&* !)rzTC<>9L(#h֔Y qY)]-!̜Dӵ3ClӨcZe~dʕMx/+ƽQ|4]COS)_(KtlFXԃF%7/s 6w`c]*sw/욽/#+c;g8F0NdZZ5zrh:jA1/`hz,dc șλM+MBE%zz"7냓i`s"Pa$jRSOaS>{;~cEXBAt=1"j~vTuOrtlLJ!Igj741m i s叔D+00 9BBCSUעl<떽m 㑙#8P7rNr'"ݡ:.GBX;}LP&dA(9V}Vxani }O?>EU)štdn/ OlFBPP3f|c1sT[ rtZ}LW殭Rx\}+]a$Qx/)e}ne 1ȑO?Ώ9\[45DX+Tc!;FӶПaԇ8kr\h QyJ2ꂽ~%wFSGJfxI(fq9kū>"ϢXoCL}Rb~p!k+J}[Tw ,_y?p]}SbC۟*=O .!6{Ep =I ' X.JKP)Rc? E!+#W[]°)Gzn eDaрN M3_Ö0VKUI%AІTAɫ%û [f\K?= qHٓ+44iH4!<\'bvOMG'E#*RLe6gJ[i?04y(8NN|9/bI*.OŅi46+Q۞8P) 77mg72him6gӚRKR`SqX~cMTbtk(Ss]bg-u .- B LGCT4Zb>춢L79zgL-6U)(!"W uKFL*u8=j]qptS囡HD`#¢xz9(2Zt ВA,+bQ"7pA }aJR {,IUZ:|X 3ܹoԯ[ Fm!F%|#',R5Pu=Hh*|WKJWyV ' =7fϩ( $?s.KC ۏ]MDv fGH3U7v#Y+|̘$Ŭ"*^1B9 vjG  x83ʞQ J/j1V5E-Bkw0ejrGͷ*ܧQJ Ir+,o k0t_Q;7 ѳdȥoSy.!gu`'q呖VxA eA̖lLt(tR~ZX4ybFX}C LMۣ.)] p5Tz3j8$XKTBY_t0+X//?B(b4[LJִRb"P컭egA}Y6I_˦<)[Hgۥ0LQld(ZH\cٲ}[{Rة|'ΊdP LV V](YqC1t톸p!"$GfGpQp@s} :3h6K0#1 {.PDKG HΤ{7f1w M|#F!$ေ ;XY1t,@23uOHnwD6{8 E@aVj006 simt}.҅kt 7c~p7h.袂~N)t>ЯNI~2hu;ěE5h6nS- 4JpQ4>][ki tϊâFEW8Gh;NJ^.MiD7M6lʼnDBj({ŧ+@y&.`CE s;^fl)NrD#z< jh.n=GKWo U?}sMPѢtC#=ȳEP>yb"4H1ԴggZW~Vc%tDzYO {SgKM?%p)C;5?i-5lh+gci,Nʭ+TP E)HXkjyy|:y#:h10!AP2mWV:jtN7'/ D '˳հza)c rD^܋v$%3a%v {ݒ!Q m:#mqst=M۰~=YfX _spq 3@Ox9lD*PhŻ# %iϻQ mxk@rps)~H7%W7\0\vJ> P/!#C/168$i>Sã^Nr)`Et^M.Ҷ KV,0H{ ; LY{=j{TIxG8y E`Yu&yyՍwbVohXF\&'i"$sWblwcԋ$ՕO5H An:9}nkgslF"(f1nv\ U[Wlz|C|r[l2m.@|~ q1 gy2y+a qHͼmjlz2ա_iD*i#$aSPw3~;\ 5(V!-ߊ0oNvy UwB3rRsXsMtiZ{ވ@]e{uo$Ig7ZVsَ}f-:$wp!tuoi*-wrYZEnV<2^ OsH=Y7U0GhQd&+ \ k!c/ݱkKm=;$5R?tr~ݡL2w3Wٓ=6 (NQ:0vdY=/^<\0_qH˜Zɶ+mUUb-;hO9hkX=>otRѪPJbooͤo@/qIưJ*&7|M-Gk`41 Lqފ]g{5mN$iSIlY, }v<o$v.8G'Afc_O |B*L½+ )X-V~(vq*Cf,2=hϒ'Poq4=sBT{ϣ;.0 ]Qm+"-7{[B?)Ҙ{rj&$wx3xho*֞Վ/i CG᠖ ĥF=F /Qv,j 2BiE&D4P'iH3A.r X;i9n)n(n.f1Ń : e\`p*e%`7mO[jtYt6Oyy  Fg#0Y;odz ̀2?bd^]? J s~.=IZ) .oDq'FElw="h &t"Jޑ/6y }Gw<]qj1]?D8үtwl<񹎯P+ϖ{_v#U~clcmqۇgb9yIG;0Wt"g\DۛT"}.RC9 :^ A1 $;h|R> AɼרVqa=kK;1"T`AzY^1+1UkzY85`pB1vORPxIМY΀Fh0֭@N?9 щ\T{ pa%{*sݤu?nPw@{1 w즊*|CE$͚w!fp89˪ +)L#}"ւz^;+(zvVWE,۩h4@ww}HK QVtTL[vFh i&}lјL/q9:MW2$W%)({ᏀpҵCD«1?#᧹<ը3om~c!\uΐfIɥeA8 ,.Т$ JX `oya K?TuW|I>*&#=lkiZ%XtWŲj+"go$1jvIglkhmsXRj ]dϛNlth܇Ala!/)Amȏ@+,#RkZu"8GQ[]XHc&귶ϥxng.g( `| StֳJ4]m$DS"*\>RuR j{G2GX.aMLۛ]\N&,YE $i|@>G^-Hc ̋E0YC݊* VN*7ɢ;V%RjicǢc-ͳԿ'Jl{ 'g])8kO.Ǫf Q 3FQZ@Col&|H+#EC9c'Գ&hIN:ԻK d_a6e{$:'c EH$:8Y>6-ssȡ=0S,XȪKMRf%(CT黺Z$*b-zYzSEH42ފsuמSU<l`x;!pOTJ!I굉D;~E{. yLW) ;A2b47ӧ!i1AIBLosV? ypnЧ!IX,:JԬ#X*]ݶ04a#=NHM:֟9c YQ2cWn;0*ۭ<@v4ZTW/<a^38My*Ԩ2#|1Sj ^ A*@7{Z+ļXkW܊V[8uFDgCh&GXVH̻(wNnd.!yR d^Co[0 V߭gFh( lUS-io ǤNJWC$T*{Y,uP'HTbh)jbdشΑ+6P5q0^" }(:٫hMï|v{Y*}ے]KFuc øMy^yu;;)5'ԙ1z_٪e0KlWѷ||Js!<Î%0[E*!Edџ,m5VB"HpY Bqsij, ௉R/Sdx0Zؿo%'t/wżt.2 cNro,ȧ>gaZ]4.ADo;MydJf6SQ%Ն?5\Ff8 t7gſSP"]/ _!Ƥ0زiq1޾e[4:1$>@Qc`|pn݅fv G\Q(p% axS.wԷ:Q\*bR),q`5ԻGK蔃Ea/g>ܤФ*ݙcmnIe#;(.j<3ȧvkb"uJb'f100s@W焹{,` H3%J>yv=,1YVX#Gi=ϰ1L;Sh3-ɢ F ]9mPW7ru2 \uc\[ (h2M먥Q^!M"m72ɸxAMarāC4i],{S&!uW pL޺̾-|Kna Q?1' ?n)\Fb윒fUAdȕkLˊEBTfp@!>9+-a5|;U@\@NSM]~Y0SQZ2S\)r o[>]◛!ֿKię𽾕*D1G;#s*'(H0IVObw'QP0YTCPLBS+NjJ$Ŝdk(*˄$Ù> KreZ6 ,$fSRqM,O ByE``C1. zN?Ibq:]tn~(9PЦ1A@rrU b"xsl0>!M@ocIZ-$]S7mUP{- 4MX~Cw|uӒm1 t^ɇGT0~]T֏bJy\Q7S3TMr3r_ЋHRibyvգ|C%0q 8{wy"lDI%47"(nX5Cn"xeF?O nE "7^+]'+L4&R&{G_9@`˃Ņ`sbi`f# d~:<">TٜrÓsr趸:!"Hz &^\;=¤䐔ip=:7cQ r19R7.:}9@BQ`3ೌ D]l8W,cna_?1).0G%A`f[%r{8۰K_O9Ay'a5\ŋոSN!"=Qͣ!U<]™=3k [g?V|ݶJ2^˳["a%fTrI1q}]~v}+3R yL`=5i#|nj /W<)mϼ7*sQwwej0"qPAڙ&4!%OXycDjP:E!seoy1k/kOPʓPn/m(DYZRF ߂uldŞ]{x"9]-1|FR*wC9$-LK&:;c.Ĩ]0*R^oԳ0^v6y.vL=b#W%@++\v-$U" s"V 3_쌘ۉ '"QkC+!r(q* C{6ݢȆ哳XHG*Ð,Y 7u+%JИ뷍~AQTeOQ)l9tjdJ{r|{ iTgh\0dz߅M:j,7olK?T (d~tABo_U55߉,"iد ^I(@vm֚SS N{ὦ|m8Nym{Ѝ:\|<ȭ$ ʵCRv^D?Y9;$;BRDE<"؊(#[\B4rg9gՊsv@SƟ<*'U|XN.sBn%{ d5u*U XOuY}krjoX+ʘO=Y۬fwe+--sny?Ӱ2#cPddzleT E+ԘͦBͧ>HBUseb3*tXYDSAn$Nd<Z<)+"a Bx+ Qp: g,B>Z!nh[ uX٨DFD+y$>9sۡ֎ݖ 9 PpGt 9'gv09vFBz M_fg*%nޓހ_@' \F#9_W;ȱ%pzHڥMk"aIL:DQ{f8Zm)«NYM<ꑥ' a!?'> ˣSjd,4$( n/ r>!2fB|xMQIKv ExR$ZAmnZ+|)Wq{Kųk8*n%ҫF죾K 4+JFO ^x4F=ŲC+{Gx9M DžU/aٕyf1D9gk'xH}XJDF9[Yy:M.ΥtAp:rzr'k9o=H8(g\y@"?\[,voAГ?۞za!bd̰FHi2AIKpXQ"PA|kߑf 'mrKgΉ 3b/%n,]s[ݪ}{oK{Mc7 7*,x0|Ax*E{В5Ɔ619y6͖_B*nK$FOQ 98=x/5]}>d}:]* E=]7ׁBsdmZ!#vGASN]cR6M%Lz"oc^ 7nqı/鮪 MȦqK?Wˉ:I%xb 0$}(2>s$AC3$UPfG_YнC5ZL%vդ`>|ո(pnW +G>K XIQ7mj#+`r lgG|M tEqWWg<\G(|z |mխ.uJXKtᨵk)URk$#E*el[1zAWWʊ7!ʚ_B2c~r} / p!DI !fdm7x:sngߠΗr3K&;odl- 4 0E"uKlØ7ڳ9).|s n#pwt} [Vhc~:U༂!64_P/I^p$ 3Y}̥U^dI7 L}(i7ߚNxƢ$6thyBݥ(8Q9EK0;/ZV RaTe ˆi_ a﷪Ǩyxd@Iih0Qg] _pi_U5\M kg{R.#ﮠRׂ7umUեtŬ?e CK~>jEld#3*HA~bΎ 3ݟU9- Sn%P`Thɿ3lm`**JYcIY U@)@7)WQg5A=ϣ_7Hw-C%^Nct$K(H2]'wE2E0&Ȫ"[Ix- RW@M-a:UE|tlUQ>HO!mm 91|<-Ny7@lqZJ^o7ځlV5#[l#񝓐%7gͷ`~k}L蕳?^[Όh|wC<م/RIuR2v=e}NSԨ]lXDV3wWbBԪ"ɓ48eM")M\X.%L;Nי t ƪP_*"[{W`M6ql0ޠq+#K5"Np"ʾ.sͷ>#$2!Ey7VÌ`&T$ OAl:Rz$;\8sF.]ig*2+k ,÷A<8U~<ٚcwOYM,[_97ț a3=[Qm}AӵFހ} cfĎqA]tfDKOa G)+P1x>fګr@_o""%MY^5}vhxji[X*/XLF#,zOdL[[͜j2JU<1吲>2aSӹV#EhyDRWRZKf.=IYXI ,ҩ*[ o1b Xz F3Vj牋%7-ⰹ(0C2U!b~<'z;G"7nU`c\PRz1|+z_~-[V+Ud\AqF=Cd{^BiE2aظ6 ⧵(a5[813D4a>^{]Wm,aN2N|F{<6Nk<4l5꼠͖%DvW! H[a3W#ȌQĸN@bS\b4^ 9a/#@CLMAemF %LJ YBirFsRgmVGIqTR]ߋ֩I ! z@{)Pbgx|Yg<C/k嚧-gKihֆbV m3O`OvͫEO) 8-L%A{UGH>ưn)$K9ミ%p9"*}W]y)?ڀ2906TLuZ=I@b| 7}44u͓dTsXy{-K@rP#!\ϔad?z,د>^{Wʎ2Nv :i2JڋG-ߋ\rpՉЊŜȱ, ObGW5![݌H:'#CI7yUkq9QMCm`Wr]0NP9) l=Є 9mL1mMhE2ǧ)na)hA $k 0ST?X6P-K gțc\;`sX!c =4T[:Gm2P*т~QDP5;mF7ac^4.9$}&r"?:B5R.U| J P7Po 2X*fPY؏еaM[]cMG2vpџsӦ¯z̩0 ݀o +il5EDl2$k+ Vx gjq ^ n ܀V܏exNIZXOkz(`We D3x'\F|ii1ϓg@vr2Оj܅ ƫ0!7fՍ,qa&%[:m+谺D4d;رUbD:bftp|+Ce>a""8Ф.e¼ksr= eE†Z[_A_n;? BpQabgn,&'-ZByU4l ^a=+vVKe-\:x)ECX'귱Kv4Nw䭞|E0)5\AF*: d,`O¸JHLm$Ыq9q&A+N * 7ڇvI_9U9<~dj7%,I~:= ='Yʬ_ơ_MHJhd%A&;Nw"7h;yਫ`!tϋZ:?w1 jS \)`b-_"\=#]9X%fi;VzZ"W.Z06O~Ivn{s@5Ѫ#~u$Zhe-=RDPL#aίB% 9Yr֖7@l:L^7c9s /SφHm+sv~Qx)Ls:)pek^@0we@wkl]+rg8DEj'_IeLIo u>((0--3MndVH+8EKu2/Y4fN#Vcq¯+9y[s?d?<6*=@ MϥNIjIP Ԥf%$#L QnLfCјyN>=.ъ|/)rǑsOI-aԩ1haC! 7UXBLIy\L>P\X ~Ayvbˈl)T!VrL@k' !]_NC_#1V6%DlXC巘;k[~Ԙ_`#S#n${etq &.?y$vcŐ3ddc `ַB>!J\ޕ ]/K9_uVVѼ3Ru+KYWSJE*6]{?g%Qs|QaFch'H?eyOOa%APVi6a34w[+rڥvB(RP;Ix,ڷjJvxbJ=LqvoQ E2w^ U54-7^ z2`7!UD>r?GN.:Po;d"דI_,"zFuI]r7T[ow bǤK<{% k/eL_cXm[<H?0$Wf# "l%wr+<3T}/|/"ƨjm#sńτ(Ӂ,/("ULޡDXۥ:inݺg!uBx)Nj9sZ.ZBIxV^)\Gh̗F)SKIxmx cG!)Jx3L/t0XӍkd$i LCB\;r,n!ko@ՑiU[cRNNB"'l&1hʉ3^ԋ5Rj]:%8C7ݓKR&'į)2R Fb\ ?r?&{I'q,@sQ}D Nq,8tY]1`r%t]Es$u>ĵQHȯ &]Fϡ6)^Pث=]wp} MsӢS@3Lۦ!%$}7kTsQj[*-;׀ʗdNLD7u~Jh VnDAW- ZLMƏn7ǼLQ]ӡJfAen⣤>1Ԏ}SмoUm BNՊB՝&/-ِ Uo-b{!UTf7ؘFڞCy"3rlF};Kjな% #Ni"gDUچ#vC045H\?Y_ "|JN{QRV2sٚA+mܲ"<6W."M;׋Xj|99)lg|[aXKk7S5w~6kg2jL׫_|f[B`h-řPAvl,r[%saĕ̼_&s-\a: UGcz ƫN`cYaBSsY ]+N W eq4`nOθk!{d6_Df(A}5 >_Z) ۉvK8Vw|)"JrF.H9s{^ ofU q*4T Ǝ=wu1R"z|џ_'NU3ړ5>0`*UV ' S#i}o\vl/9resQ=]Lxb{VZv+ŽYi+`&C2+0\S=6w0G}`Dч΄fzC#}>|"GK<1\" EO]m-i&I4MyNȊvB B3ȷFSPԡw8B`Zf+CIXvofo)l`b|Z&K |_ 1Z]OrB ;A:HN`ٌMWw6ҌA7Dumȿsü}:=\j/umnRRh$M /\ߊkhM3OX'JQB@O ~Xf$ҊhRNulםeq(|*C5тN2YuT0cHAt飄",l DSxiQQ'pݭ'?+'l(`Qvh#7}^#_ O[po zKff}৲R%Ȁ[U8KO]?W&A| E\XABtz>Ӥ::SK Cpaz0z;lp Xc'bS?uJJ?e)hm*ձW`nUӯJkF-Ww#AW)8o+C腱x{ܹ$aSa̛.2S !(cRn#hPv+.zfۜ4^O *vW ['x덈^i'vΆ/+zr|.`~~>@[}?}p"~gj5FOů6%,QFr` FYss{'Ջrk$g|VBF1L,L3"nhZ+_efhl\OX*dXvޯL% $ * ê̇kB5`OTc$Mф{UG޻R5x|u@-{/q`rkA/vye+7۞;!:`2%8ֳJp}*bmk[,fW`Jל2+8}>rldjC )nQ<8.v0! nvWvƎk^>A9ڙ 83.DڒO~9^oI[] U:^C]+@}'x*A EbO_tjp-Ą4uЇ8?'cT%dd(\se9)a|8,xsd uWncc/pVLkeAEza""j(K"d‰BX]Z;&R}+aMkDGEZp:mv0?Ҫ[3GN)QG헸o՟E1G-0:d+]ex_̂Bu;jXϿ=H`,G1Ui9=c gj}~о?!h= pY#Xy{n$'=M`y[c,lq`bkM'>5,Xݫ7[=ߚ*8C{Gyn^3rYe>*gv]1ZCEPXJ?YcOCd چS<H¿Sn֊L8rB-&sʃS1*:ozwu`7 Τ_(jO}!d(Do;"Jb=]:s8>;dTӎk*R%o%e)cgq>(T 6(5m8 ~ߵ0 xNCX5^CUګʑۼ.r\h`xF{ys io2h*2}BX2x1jhx,?5ưta1ۣĸ9HwX T,pڜoW8pU46zڪ/*LO 0Vr%^$IZ,`mW~5I v\ l  -|%f4bJ=m+I_^+c ;06\(%/\Z*fz]iXty,Q!e@"A}Rύr4 'ߪux4&˵s@wWIc|bBThbq?5cVnO52A):yTR^Fv2 E[R~RB߻8aL]'}uΗՄ4<²"Ș2v*n h ܨ=[( d*Cߖ.늋VU$NM qtP2>_B%lLbu!Tnm3GRSɧCCL۾1LE;~}RXD!T„&7ף \t3W '}-&8/ag^ "wMLO7U"jѲ6`h;rG^03.=8?Ι,+oH)P*_pQ[ .#t^ɒd fB?@C2ncIq'J.6fa<Ô&iHr5 yRQZ++@-;?6{ =^~U3#rħ2s7j3 o$, @!E1]y Dȩcn~DopWSv\.wۗiV{F>ZZLw<=tbf^MFE_xQltkC.w7/@{nFo3-q"9s|~(XJ+pY"3p8zqy  ,!F6 _CDF/EJo$aq)zb$1p2=}y;M(\2mz*`+vk_Ri.&#?c3*5ZpZX$Z]'XuκZP*T+A. 3Rgh2jӐi+ûʖ+ WOI,:he?Ӌ1n>j+ɲ+@s.Ĝ+ouSPjJNnCGiM W +ǔ? 7ZbJY߆U}?}erGZV9WD.iU$YZ[/ϖ{'&3D,Ka}6G?Hzlᅰ[K`G& G V5P0#->LHw]KxŀLIMb$O0mn'(Ygҗ}t%6sYy0®n_obXb>U* _Vb39@F/v^=3m?e%qI)OВŝǤuSa.{yrtK|y^\Pz\G_֜Et$ Gyh0[݊o6N`ydvZp`!P ,,[=h"ЄS}@- W$( ~WO Gk@!_+,M% ^ F@PbeÙ^u˩ .=Ep6l2iTYEZ=g3$ pGgnj=Db`&سGQ4<,z'phթ P)dqfFXP?Iڈ8k8j"S JW0TC>GSGOÛt:i2{~eMaDe,hJ}gP$4X>V#X|IeJhg 2C~KVsg;onf}~MGG&ٰl@Ko-B r7a[X3_T[[;inZ͝4_zvhK-8n։MTyF`e ϾK"eJY]L BfG|2m{|'#<<٢)#cTXWx`DtI u li{ra ycTpP}v+d+ 0س2+}%;*yi:(!A~=q]_ggl6s[7?r ަln &FKɋ0]iLK(7vj~mʉLjJ`DeP tl ~4j/emsgm2G ԮL=xZ1[$ \0~Pᅐ|yhݱ_ѻq:+յ xN{sx9N_7GwG;+tB4c6*ob@ЙS srDxQlbOFu;rk;ٳ:{zw[>MzW1&t[6@KK&2?LVN9<} ?+Jk_f_+=v]iTaG#ɭfj|(my:qSmX9W^3pY|Pp'`aߡq%-ggHP3=?/,vXq;G8= ; 4`l^K }Y5%F5d\~")g#du% s`"lߚ:%:7$<$<ͱ@S e>yIZlx;GC[AU˨nvSf_bšMe8f5H(ɌØʞS=bTL|eK5Lu/fЙ&:  ʓ 4A& \oSH|p Q@-Ka<U}{N`} Ľ/3yФmzKz( 5 !7U,Xڏ3"kئK[z"ZgޢF Ϲ˭'a}4HG^!^j}% #y ;J}6B-GtEiJ[g^MLMAŃvσ &1y#pg)4- )V2q[X $HT.Zow֔o-OBESuhB9ȝ*+MzuKp=/ w:hnby}D-fu^cZvRV>˭㞇G&|RP7_VHbBr(U)uIg `:tꚵ K苺az!p}ZJܗZmƒFG`cH2,Vn_IKR5n㱭O,Nfɿ<ɟ)^t 髠ղpC&h mhb{Bix]R4%:Wջ<% `6$(CNR@o 1|bb˥E<2։^/_X8++6PОr+2nf9`%#sx=cXZ etYPHpzH,<'E8 \!<ʳkbmctRvmMtEfHy*wGJ@ҾEhQ MAsWp*s㹸k ,b`82@Sd{ؗH9R=7ta*gh%ؖߌvsvR蘴M[j3lԽUC t<蓺US ?-*Y J&Ō.+"R'TOf 6 0,>h݄zNs$&#Mqcx̱h:w6I%B7h!0T ('~r=/ H ^kI#_,t1 tqU5ϼF TjnY^){s!քqT}`KknoA@p<ޣBm,LO;K{znI f3_bZ^_~SY3y[5Whwl>O#.p c *؎ 9h_ʩ}0zjgUPڒEOij-">0wAK@0!=\])KjBhĮ⽍>!SYǃTBfTmsSQ;} XSqf5{KԠ|OIGзe=i0xB)_޾18=x i T{uY.2G >4kB!9pDcĸ? bN2B؎*mgu#`aV (E9D)lb9Kw&hƮXIV"7?[RGM-m#"-*}nj\z_\^ӊsnWplPpF$ao'4HqRIu৆M+i`Er-o8;B'x' %0͉C+~I_)DvХsH#yZNX,2; ԾAXQSk_ra'=Dʻ{>N䮶b+%Q<"l/Jod+TN}mvV(u,&n}BUr?ckle V5fp8NN33Ӱ\Ρ Rx%.#Lȁ[Zmpdguqye<ݯc9a3PiDk &OV\;{kSz[,afߖ ʊ~)rځTRM31 T;lMW=A5UmS=y/U?'Fҏ+9Rmvs8N3Kzri+-x Sj晥ϫ'0bK4!xG9D3b*!%PI)R8ܸB|7^vB阽Jq&r`ɴ^ fܷ>&4<tm¥քDР?vEuëIVkR^AqÁۨI &7_~Ի(s}ˊTmg d{ "QX5n:+6!% FcES-4èft┠%fd{Yha{ۇ3 toL]21 vl`eA&3ئQlGy6J@քWg{yLĺuxJ]MEŪbW+d>VbCErƼEޟ7ӕ|x "Q}nJ=YD't|ŶgUn[* OoYKL਼3Ц#Q(%!,,4B|\F 4)mf֞13kt2`ȑo0\"KBdL@#ysG%$[s(GQuۘ$Ka 5 ew /쵿=́ORP\y1\&GzNi5-Ӷ3Y8J$8+Л1Eϟ&3{N/[L;UjeK\^Q:%9݂).Y4z}ZYe^PrmŎR1Ed@c~Q+ z0@c0}'$lw}ߎ1C 7A\Dԛhq79M7zo X|ܔ[]ΙR|#ѿ&*;({ccM9(ٮu={>LG:'UJ]ZFԓQAXs\<pLZ`atp~WS/Rm V~EB:k4&}qe)%7J49v@6_FjGwA2!^^ճ.3׭Ć̪ FFMi Qj)}.~o e4jL_4$4 HP-LQFբu[ Qt72Bͪ9pFyP:19Sf%uLnCሽ$4G" ^yk?#'"<5*\O$ͺMP)&/Ba[ڋ,k£?[(~PgAT!"44+^D~/./&k6 wT ra TddQtî< h)a2| 31Fm.FnOWVvəZo&gaVKcu+c \tmE{@`KkD-Z~ lxdz N:RH_ǽ 4"4 ]N )ɕ|_$.% _v45։a"Nѯ Cu(vU4ЪNU@SZT-m2.,ӲFDr]Ff.0%2=VED{EfNdY2n \C*klQDx\0.`ר9!|-i(P5@Lߛ*;TK Ή!?YL:x #eg.Qnԙp :6%߮1dwRUF~sԿ$R TVg;_IO87 x+}/| a1AsHnḱ>+:ht&rRֹMy-(Zpx9hk\P֝SRz َ/] /*ėN@ęW1]HEmoTƐ15Ҕ)`m<2*cx$ua{a9m7{+fG͊j|w1$ 52 l4m:G0QO\-oi!HQcq3z7$all%I>V3{B o4LTo5c]ՎSJT㒳`X4J6Iz*MbڥD^RbI|Wj-] G;)d9,Qc„(.Oht2vD tpL lvc^66L SF Bzַr5h#-'= | du:vl;wk߆e1W@hCRƢuWNam_]_kl]3r [$R eYalU'Q) pGl4ǺrW=jRPZB)/%EPT:LF9S &|K{”ٿ[Mlk1E-;5K[K(Yv{cɿ2 M9õ$ +ي.^IگZdK[/4ckA[yqO&v/.+G2v^.W1KU41 (tTx:`ѨyF }(mW-)-=)XnóNh h*kkUut s[M-ʕCӊ?evL,U>C*:t:.uDS:I}^-}0&{KWY(XPUS]5ckϳºl ?d*q+rԼRk,60>VoDK-`M7H72莌&>&q+8Ws٧W:rz(z|L)=ȡ4>tH{j})VB<`MhuKjc<)&qA,N}εlͧ^<6 UqS++쯷f,C0V?Ͻ&3n3*7KO!cgyخC87L v^ʳ,?{g^=ޥ,<`pԉ2x#S"kPr`,ѥC[}hVH7q񍄋: {^&NaɄ՘ߜ_X[wtp8&j,[Cdj|Dž_ibD]؋e 9'RzHJWx7:Gf;ds-6 mY5;R$ x(xc :y*(88M1XVx@iw Xayk 3i`ҘĔX-n$' SO7^Lή43:'Dq93Z9aWٸYCԪ8O<;'(V!in՚74O|(=>tSzwj #׌ ._nnqILwq#{q1.K\+"eRHuzfOr )T21wF W]Y4:E'bh=NDɲ_T^M"mA;e( CPѡeHTmßC Hh} h'b^2Iw\X lٹ_6$,&($]丿"r.0C$f7> >B%ژ,. 5}ާr{~x6&fVaL@D3 ǖDhi\T{d5A<^@6y S4-j*4-Z5')K MjۘlL᪢Zwhv%sR] ^,$`=+ [8kK_~FAQP= !:fq] ⊼cK0A$4$Ο jd?KEu:`宻)Oڼ@#5Yn֥@SZmTSC)]԰Z:2,YH$fˆͪu)9g@~#h;^74^ G D C\ѳj%tGr!u~tc(U*ҋuᅜ$sy;9Իo,4g^ WΕE)jX@bC+feǎp4pɚ՞bum7x`} U{ߓl uZl3 `P&~ P{nV"rRgJk ̌kCZmP _pCGݨӶ7f9nos-֝ƂdpE^?~UIZ3翅(:0ji^'swKQ^\&IEX>f>dn ?*P&h8,='3riH]9+sw&මvri(2nP*m~i"&ŽC {דWرh/lKAcM\ v#!.A(LI̘,/>F!\3G6u O}(|1_8Q5pz襎ńjM2!ᇾ'+|+Cyz,pC;Iӓw-k_^>=FG۝ꑅ4ݸ'%Y!m)Nr)w Z?]\+B`dS[ //~)0mp6m)%4L!o*Lz )2QDmH5(S#Mt^5Uf΢T#_ޞ澼(,i, iGFe^VU`OW%SD}.x#'jE:FNv_v/fS"ݜP([+h%z@oöE)|rebH}7YHvĜG+F0Ejx܀򑶜Z 暂io}'ɿJJ29H^nugLs%,WqPWb$V)8dw# 47o5F^k VDwѓiq .%PA,m"E3*$_ GS>B65J V/9;cp b/˩28ӌp7wP6& p'hq_D=2}Lm_"abMWm?ɑZvȺ܍k;>0Ǐr7nYp(q 1|"z*QEgj *B?v\3M Gρ0vFKM~)g` M=HL+1w%}%m߽P. q If7ӎC&$N-sӠc*V؇x<|:iBt6b_ʤlS Uq72 s]b%gc#"׌z7QFs hIy?Z;u_dVnccTIͰ.6RarٖDMz^{/0v$4GE%5L7:N~l i+BH BɃƾR7;$*GјzI `zsKٽ#=>yk[Lc2Ad>5ځ{Y æ=ح܍[՗uIiKʘ&@qC(+Kx\D\U¶旚KYd6j^bĦT U嶅͡j hR!^N  _[jζK|ǐq= z#M%1/JT1u$.=mRY Jem&H>w$~hW"^߾ƱȒ`T,0sm5<3[/.'ѵ3}#y(>#}_i'XM6p@^r;EUǠt"יܒwoWjha5hb/'1DKmӦ; ǩ2OsbɥKq68Y|? -L5m&` -+\h\ X+ŽFqR,PDc:(a(9'fD8w`n]VB5,wtQeɈT]P+YB9?p~F*4& J&l0@ V2Tw"v NKΙӊ.sfȅiZ`?/VPRç.T.fݚaE8DS=1 שR)P&*RaΊg1KÒ{j4j;ڮ" -2NC%HU-5x|>@ϖSeN\ <qf]^vˌ )&[CƗ<]`?:G q&!Wm7M*=RГ(,dJJ˚>St- R[:!o\GO܄=_|jַkcϪu}A`wqDɧ ~09d =7N!FDC! r>ULL8ɃׯҬ ? )L08O:Z`L5YZB揖ӾP;_1 1`[eu̼]d*c.ů~iT_łkvBt ?g3an!i-0,P"D ,a%otLt>W_'vtdk-ȁ4EudUw~Aln]?Geky=-!0&)ܿݧolڢ`gb p;!`0>и+,%E!o''ȿD׃yNűrSq <)2y.)2|2>p[L^:wQ3RqSC-㥖ZE2j2"uۂgwxB/jzYy  n|RDeOO줶V,b4NEa;) nRȾRf3#: #J6*8n~ϐ,lF> J(sS2bDS|׊Dr?TE>Y5EhB E菰xmuAY?X1&ںR+`6V)Xo OدNzz  S Mͧ5qk~=$8AsEz%0LDiy\XDWw'%> -AE"Cӷ>O*/:#氭j96Q$(ExHBP>13rpB8#֋'X5'6 DM0$S91%ubNN ۾2rHEOڹ72@z" }aCqAKWATyݝLF$[_r>qU?77FwVz Wǁ>IRP^ɍn6SG*pW}"o 4ĸIȖ)uRGNޅq!M0b.K;]da.sӸ>f0QSn_[2UВ˾"%w0&,ȷ5=2p1U7ErI6k v%#L<L Fin Qԫ{@e†%} AQ]nZ?쩆CYP׸ 09~C5~[|ZЁϱOJ ~r"@dO;{i-~tyg&d ~ƞ`)8O-ĈJH$U,(H*M@K#Mt"AvJfzB15@G ̞n}GJI(V}˾+j)gK :zճl:>1s]g8Mqr%$| .-eg-0q}5C[7융a(kPUPN| ;,i*I!E}(3zn ]*]W-3ʶJ a.68@j2XP€k49(?|if`PT2hדB׮a\2SkZvDȱCVCS@ک#D-ZnFPfU۹g>3ٳ5b|๵!.^ĉ9#TdZءCS1x06t5UZ~O |`Iq@Z(M-+%̨&Ş8*}zIݺSL=g7\5mntTџNgmDw܃mM+鑦ᵄ#w_Ҋ0 Xj4p[_^$'e`ē?z߼j>WѠ0v`&RL_l`xB&lfHIq"všꦶ>;M)ROF\yI>i$-xhlpY0m61#J<҈QSs*d0[oegur$& E-|+qZ ZO%tGB A&WRĵ5B߭1UEAw"m X]@ O"5kd+ڥJl/q)lۋ<8n !mMw 6i "\GvӦ=Qg + z(b _6 ;i?z7h^Tf7;x,^_* `Lg:RĂj 8Xt66jN  ks:R :]6$L1~\N jXJo31UY[7RtV,V:mfuX{Y{]L'vTiY>:^jnJUHH$03L0u%H6@rQaiG"<]=듷*q.M7 C\OPPĥ;>1Z{72fhҚ&\lt: %,ƤɺBؽI:C^q54yz_NJq*_5+퀫*1+W- ^ԁc3YTkdWxzm&,%i>N j)V9hȞi-4hF %t*c85eq"7k+_ yN6xД@JWBeA~ڊyt*-`f̢F0]_&A5C SGo2ABTf1 >} Yl^fE!?[dNAoqRꞃuPt)klQD'_`6vI1d|"LxGqX$;}7R#vz}xӏCG"u[`gmk8:tϯRCڏ@T:v8K?DkY qSʣ-Sje^ #JVԧJ_X{ZRs*ݜPLeV x{`fjQ#rEp"A.M2&C7šӞ}AЇf0j~Ju:1+w7@>wӀ)j.Le %.iy!QkZL>N]KaNB ,[[qx 8 B8VUoOK=./"XZ ʽ`/f[v˔;)ciK+!.TS%e439_[T_52jA .@onڷyqdB%{ZyR(Qe.%["K*\ط*E[`Mn421 Q#S͔&%a'<+JQ`L{]de^׵ \}A9~h` uJZc! )POQ5D\fݝQUR:Tp"SR#e{-/{fָ.0oV]E?*Aa0VUY _ulD$D㓣eXc"〦!tSfמsZ asst;-Q·1ɴ4P_.gT8͹~H]S϶eFzUM6 f^9|m$wa^F R2‚oN e%D9M #j؜уd" ~V%#k G+W/rb_,V?`j߈5~"߲L9Gk Ǒ* 59򞫙ZR D\Bum*y 6UayYHQ}@ńf`K#A)*nM]rGLՙ{X$]ΐZx h$PY.<>wc>ܮ9ح+j x^3)o=Ё 4>m]> (q\+jvw:tF1؝18mhb,j XUctJc%-)s~T%%\jSWށY:=9{-xJrp%DmAjH `@Dig̠>[ؙT\W9$ ^*@3I>PT{uxh- b@%zcݭiDPç]y~󔑩ԆG swjjIqj45y-)L}WK; ,,1ٞ_zŝ ruM\譱';$+>Qhc| g=Va{;QN,yh#y~]p>ʩ?O}ci[>8SV Bl8U7 X4=DUBD&RX^0xڦ+LaNciXs^OYˮ̎2fIb&؞.`[V5͜+]ؿElg]PL.Y(`C_n{;fZ::}",zmtNٗECscv f .0Z*7UJֹ!yqĈč'dV:H0d̨D i>25?%|3/+va(CWav`O N58/5S+הĒdr8} &"\Vu1ɿ`cP: x|vn1on4e >CZVpf/VON!w&[ɧzEụƬ1ug^< U{60nd^<47lc4J.>ܫzJ - .NWD2/9Nsh-ILa; O1˘?7DEW:PpZ~r%DS(v>V("sw!v*Ya}/Kiih&5^Ksՙj%KKRW{X{:* L.GJZ(cU7p[YfgPXxE&=5re8c?Riܮ 6syQ'uKZui ÚkoxI2Y#׆ YC~xy0"L%qx)M)L^! Zf d#bL4亍2 $4mb+[R7),W\8n D⼄8Uu蜆zSl=u='V_ah"Vk?eRMߧG8I9p)].L)D쉵IFn 8 pA_r$~+7  !qp٤]wɵbZ ~L=|Z&ku|8~ցe$}B޸Z#?ni ]Hw%]hNͻzm]b;l.*s;y=$TǾM-u4eBC9>:ά=TߥkLI(HBeH3E)H#bBxEbNkk$5qT0.n?e9 IrrM"X_|?蘴7=S|']vJj <-Ra#`$gCvٳ^Q$;.׀ssg_Nz,E [~!Jy>DZ Fٯ@>kAY%el.T8(r~hS<ٻ#q 䮺-Yۺ$329pFa$D%yķ%e\H}B<~?3/=)^{)) |'@:lZDLR'"{E:q B zBO'rP `d-vجL`ѿCKv7H16J+,Et.[l^!MgH3b:&d8ook:˷ [O3TRhxܷmO y Hp (lb-_].fxB.U7GNzDce`qK9#WW**9hzj4_#,gfo G$ ꍊ%, ˔h&nݳUom>O:e؉]P4i AnJ j\HKBQN~A&/_cخgf\~c"a~O`5cf܁hѳMKEntzt4 806i+ɭ&zPhZfreXpmȇ UkK/r./(ʫ2VLpéƳ+(NiV\"&mdyQDpI)];kQK 3ϭ5ra"={,/\7k$g捡p1ɫw[ɾa%8Kol4x0A8"&z13C&5]ٌoHdU(#@ s`| l=1W "6s%#lSc ou\qjBpclItbլK le@s- VyfM]AW2s;=͎E+t4âw`)Io9Ȧ0[/'y) ѕҙ\5i )@#M hnRe!-N,^ V L2t*Z L˯FJ:"ӿ t QA`e ?IH=H[-fL#c |uLVO{ qkXܯ^7zЃhQ׌Qi&Ńr²hG$'Sxf1Wt4]\x9 'ڭ aܹx-$9PS%ytX܋%Jcس@#~U`i\' Bt Y;F}Ƒ1g] BA=^W:3eXD5'_bB?MO l=eophtSI8P׾:Jj6h9-v->OQ*ᱸtGXx6\Q^ٜPY)p-I lWX:8)ұBTE 'V$"™5F Pݼ罏5ZQ8b$/w+nKU ,d)ߚF6z#%4}Ly!%lfѹTYa9N<+ܧ) Y6Rt?~oiF&Dǵ};zƑE=u˛ZhZ#@^:#_Di!J{վ8ŻN.Wlm9 =LJn^a 36~uea1@"C!(X<^ E$JUuBhn;T2 <j=Z0 !F0X OH]; zfK{h)X +"$7FVuo #\J=A0ɞ-Y4%s)bY@m9?y] {8Qo5l΀TW):~ί/J.)6J}RA0zćVnX+ND[!w3ȾG\Ki]gRG;iJwXޟ'XL}d*_|,6!֫>Q"\{=ew4B,wб}VZ uHJa&~ ن>{<{!Iڻ~pnHB۠C"kuך2CP6vһ' #K_qt~1~+S35RS\%{>u*C'u;v#/fL{.7I~S'pK4)À @{uF^OC:*#|#Wj_Q<$bfm<7L$,q[eE[YEB-YD.qiulN?U:c}F *J9 4w fzQ!7 ]"H^PU\J j~<Y:,-XD+#ɬ /o M%B$E1ћ&'^pfx-cQa583Kt,uKϙt2-xYZʵL] vߙ%g{NBF+tF=^.I)1I5j甕סœp[j -lRٚDAVlBwhB*^o O6M2B鬋(:)Y@N%Yw}5^'dK;MmTB[aqD73aZ.:#,?"bͺ卒ag 7~>ؾ*B ʭ_孻MwoZEuQsCA6f~ (^/uF`aʱ]JpucJ9V:Vt<% xnÀ-[38~)d쬷(l<}Dz6֮(o[!-9bO-En JKko(6.ڔ1k@ԓٍge/U|%7>/+`;8+]/*CSK ({5O$>mA-e#pwi&p:^]䥊,j/ӟHs ed(A6!%ZwOܩLHKln A+!ȱDx1A_բ7&;'./Lbޔb}0-5Sʳšh=Yi:8oQ^\ퟚ&6 `yibVJF2yDT3j44S т SVCz q&?r0-]1a(3/*aA4m'd.wV^yCϽ˦|FAԮN*n$ÈwT{[^3E ɢuǔMj\wOrF'>)4CuU}F} zxʲttp+g-B4&}nhJw_mBcg,wc"&G}{αQÆE !*ݱzo[ ,o[7 )AߴjQr \eQ "chUmD}) Ms'Phq*ܗ TQ8"'rڿqaQӧ>a7e׋*#~Ey'm`cet'nK)!Mi=@a;ZKrP*cM2w>eg5w[ǖrcAcB}C<_*`Ǖ{@L򅑭 yN%-S/yx)#/tFk|ub/^4{罰,+{8j$=F,X{+C ۠@VUP(f2_n\j"Tce9?2RQh-Nbi?u Z FTJ}"~"oI:|{6q{g.T|nyPD)vtg٩\jϿߠ^A)k̺W-<>BI}2*Wl){)o~cT9+ɔ~ 3Fq:AljPvkCH3Ϝz>ik3m,jRï4Gf@'2݌IdhRE֔ 8$jR#eBZ#3Xٞ*3V3Ǭ 'qu]H|wk+YZ[79f}v7 SќBQ>}AE6&Vrâ'T#V[/.-QvơRñH-M˾?[_w֕hgEξKGKSd/6NjEʆ W׵ѵDH%x@_?pf+a58P~VM&r:"&є%Ã|JpRK)CIY} i87n9Մ#zN+((\ޜ'NY} ʰϘ~S/5bO6HPnͅLTRm6z LqK/6h&Óe E([ #EޮRX{`]dR SԎQeZS5]BZY 2)-1A,G:;_QZjyy$]>EO˟toG0wlk%^A9pTɁN&(؂6 ׇNQx~ôQ@f_@Zd,u9MleGPϊ#/OYeFjpm4qBΰj͊;N< 5 tB3)/* ZBv-UPeTdm=e)GtM5©@Vv6xDh[=ɓެ9@ngg 9F q=*tVul6\a1mL=,_\V "Ʃ*H϶2~c<{fF:.l5ٳ!G#oj i #5ULǷ9h| cbwm^"jP4_tƒ (EgĻq)h骭(pyո9{WL[h)\*[%PŇ¢'VGSV!-9( /^/ %.ϘAnph,m*E1紗ªv&i'f|5q /F9d_<.Xt6s"MC,U#,#4F xlؔ*scήC7"ŁT-\"*9lYK d>ygUw0Ҫ1ծYI疬^ȶT$/U1cpq!l1)ã,Z Gؖ_'3c._Q ܟ (5ONZY @/\B1A1ȍw\~-B/ 4|KI޴lك,[M97v d b,fdLӜAjvIֲT߰_c^ȣS{yuSFOգ *T Ye,3akotژ:Xi_cA@7_Ǡ݊$ʪjFʕwq#\s!snL\68(Q *QƛRMQ8WF\%pF%ȯ|  ɕ2>-V`Y Nu@dds43 +?جv2nOPixP2 Gy AY奋<ޅ<? l(lJ'wH:gt:- xbdLpT2R!aGҋ PF>nhrdWXOwZFx.pQb"BÄ i-@/uI8,і5ߚPFLAyZ=XTͼ{5y0^QE/ "o!F=< 'Ps1+@fK }4v1gjƀROZ(ejX%`!i_}c\vD),2[.+%bXBу}OZ[T0Z?QY%UZ bL*/;S׈D|L%ې4^}r Nx;u΅K5lּcj!_튩W^gO*C<#w% Y@="=[i=ԴyiϜS|p*1ųfK%A3JPhGqE\oES姲 k7|>w+eY:+* ه(Rk#aH).)*ѵENe5)]Mv;m$O nR}bsbdu +eX!zŘ)godFk 16UZ" Ahn۾|܉/Ũ߄λfl8(bcЇ*ڤ?^ovQNN`?=,ĭ:z??MoVg9G&&b4UU6W.EzG1YR"8*4%Avg,$1v4q{'!p6|*2:"Xq~FޑW Dvah0%Krm)#;fؐ En04=V X&OuY XJY. 0 P c##9x$,$!6g[> N2>GI5M@T1PD& Z1Pu f{Wkc$|.dXmwk*#{^Rh+O3B#Rh7=%ESqeh?M2\iuU4=%F-Enĝt-}¶Z vV3܎Ot(4"`^Xen4T,̟TZB} wϓAΝR7paT+" Zco-B_WH˞B쳸NI0q{}oy(Zȩ7TlڱW2 ;KF(k?ls-k׌akYzdF,"OҼ_:|:Yp9|ɮYH !VA r~G\(8{k W EziT- h@ ZɊP@tȝ.&{Y'-J2/ےn̛-8)Ä)dҗ^Q)?=AyC96l9u%-_ \*EJqR2Jr)GɔƢ0U޵g: y~uyC9aI ?:սwGQS=Z$Pπ(#~ˣ/v?hGwJԎoKLbⴳZ&]nUWu^IKtw\COxߴJ<]R&W:;JCPM,;GII#f>5ǵ$:*lO'}O\H/VV}tH /sbnIm`rܣݷۉQެ*0ˮch+Er55Ňw бs#Khn5 laX6-"uK fت$~sR2e^-¼*qfcATA_uDoo AM/Ͼm,4](.N۹tL;bIPczTf^iT GL6(<Ϳ$XJEU ԅ"ߞJި]q˛SJMb+|MfP@r0C$e;藽?5WJI=z|!̦$l|5QނDS@Do-5Y0}ޫ6X"3w&qDdջ& OSP&1\mJ&co7=OWd/:160:5E[zJ!7\?d*o(\_0.@x*%&aӿЗ9xWT1|35/WdVM' gsriKHd^g_0&.yRu;sLy2X'{.Ao9LYZʼX'y@K^TQHLxҷ6D]}e1He)o7솉'cPΚS^\ؔuG)Zfz-8t`{m ȫ|Ͱ^bۤ{7' %%<>SJ-)wcw3OIW^k6^]iS̞޿_ s,uJ}7{M_՞֥.]ݼWCklX3ѳ ~_㸳?uA,,ƞ٩M @{sTR{sȵC&Q b>]P)i#C.%\qp(Xv/k`15(CȈrQeĹ Wj`O&І<҈5  V .PnjS:ܿiY@P; Mjp'44K)b)zG~@`D>vYPŒxzJm*&[f{eH71QmtgDYh/Ǭ@ qo8>7tû e HȒg%x"|i=|4 礩G9.Tޱ0ӵ %&gQD m:E5Ą:1GY`j/!%d+CٷM"jHkwb. =)R6\ P>y).mx<-A2"F*׏)Qg{ >E.G}C!I9ٯs.xL)*V=" l ĭ^r3ڛ16Ϻk1_uk8ޜ`HC0ީx(+XV}\Olj+ǡ]l N@n8Ir zV+TӛD%,yF 1P`E}<wR֡d s U'RzYߤ/0qD!͋O0.d9c]kY]\ n\KW#tKۗMKƵ-1a0K,Q$)Xv~ڐ&6%u9 d:YPDHKo_<Ԉ,wHbƾ>,>KpgH9ة/_W™X ̮aY1*UܤR"m9 ʧ{O"'U`,J#ָ ]g"Wٵc =mkel02>iA^wa_:.3ׅRރm>Μ6X7*#h]X݄YR=1b!ǺP WhuRo"F ]=S0B/,^>`+>AaڝijW=Ӈv[\%"khrN _{vW$r7$O>Y[9DC k.b G2T?ӻXkt?79w)W!>7z)b@*֚I{V毖mS@Ko蹂q o֩ۚCY3Sϟx9 x]u+{kN*Iq밦bKjI#Dn_ROKՓH^Ev;K K *Or/i /Iwe?*>lgrad6W <5QH7-z(;'<⧣K'jbQ4LCNՍB^ܖ[ԜՌM{.j3,᳗Mm/cmaeLhެ[UdZ0bXb!IN؝x} aAb\.PǕMξ}a]`zBY>^2HzRH@N4$[ q"0j,T@utq1KF;j~-c'?&{wƶW'<)} $ K6AӺBNf3."*ni jЁ+Tkx$p^{&Tz XQÇ.3q1F&8,{_l0EBCJ-{XD]23 ߶H`:&I 7t5t1x;l@_*dqy@"dtQT]cO/>(Ƙm4WelD4M;Ԑd:o7GyuîCfwIkz=+ Q4 ъ+SW 9 VX1:*{{ppu;rPhwIJ=*kik8r| @ɂ 3l+dE?KemGX43f1yu.o[UIc8M5L`w&b 7VQ"^l= wlc|L) aZr;ǡJSBQY]B/޿nQe5$źIPIkK:1&dZ"k4DK$&WCZ\@x 2%8Qy2VOCITvEm~D!:T,`ٹ˔ HFSQ c7YDwYT IypǦ:!R"\)N ˁ~~U8^{s?QhXwSJqF:6JN >LaxcWFhm7sX?fUKJb.58|%|r8P;hsҁYr]˲&lF@/խOG!}m TLPH>?ki*¾cԥZ|EerOJ|*y8(Y?.禇 s[Ȧqd1`8NT@<ocn3YC=ѣe6d "̱jAn>JP4w)`;JB_~v IBP fEj mzugZ8E(^q޸C4 4 1 @絇bƥ7ag!BS$AN D|IOF$P3rTPYЀ)?kM-G%d ~X~Q_|yez}M\4@]*龤ǒ9,)Mѹ5u>01l9,V`6W+:I:Uӿ%<{$v2K DTŴX)co'-ՂRUi?x 477gFӲ0}!Ht tfg],_'a\@W%-oPdxy4mmb2e݇R[XURMYuXu }.* cTVPcL>lnte-"d~yN^5z-\>nKɹgfrN!ky |zXTYLid8IK;Fc?b1@!2\0GmL^e^ގI0B9gf7ljU8Lgt7\Iy7^8f {P߀"u +xBUW,iu*\Ý(!A;K䵐=I9_Y]`UA.?Z)AoSI  =jhKTb M}N$Yyz[  $,W!Gmn^`eӍt^O}A!-fL }H8f`'/ 2#VZ,|nfdcP@pv*'C[9;Zo㇪6}8#~p2,bO;w z" ρq#dWd?0wV]|(sOt1C*jĵ13u#8=D7,PJI uLz%&2?||d5XjƅSFV]S1  "hZ/!~"bBOxgj"PpQ?>IBRkNcrm4to"/%I7Io8YbGŜJrzᬂB"z YKGa{1 TURS>GPy̷=1+%B4j:a;Z%v| q )E m |OA]G N[ ʠ<(sLl'A2{~Q.rdSH qn,- p[}a΅ygl4q?0]o\dҗҩ>,ٓXҾU/*i a#c%!JVgbr.OXE]ZAڳk="(0ٕSnW7$ 4Il 1*7ޣ*:w"; ̓_r|5| +9(rO3ͷK6ۼ}%ZרXN2>O`oNsiAUrā/I[qc96W> LP `i`kN^GZ{P<~% /4C%{QDU\l~:Njɦs@&OY$[ì; 1kn2pTf8<6M89F5 &ZE+ ͍y0u1۞ocB$}4#=jlAsdA˙S>DGZ.)dZ4}FG~CIt)[{F(?3>?) uF ,(H&ߟմ-CD *\iu7le r0WHSn7.0&-Esl@ cJ!񔎒xcFgFtHhB,Os]8YsuL ێX@\4O}@7|UblRaMp ͧ%!*Ynf; ˘[7s2 O_lB-AVzDBW4Ǝ;8oIj yf7r_ˠyr,A0#9]bV 5.B?\gk*x@; 4}ԼcNcs!E_s-]Lz_6pct_zY4vgϥi[*ŏ/e:`AUCl 1پ!Ʈ԰nS]L=Pesu ߵ ǘ^|@>EVAh5[7fuч9CM,G<4W^zDRϸ _qc*5.!L[Fs=0vȆ-z]jY侕7u4sQ-ޑh7-ǒGXUD}@ tVs/;_f`u|U&}kfOj'V9Q@kшߠw8t'C1k=l 4Fm}HP0q0- KJ|m27Ѡ;>VM1Iv D1=]S=Nvb4qØ״Z(ݛ:Wfb;7|h I_+aIc(WLfXBWk[uVq9B}D=?WP(߻ .>TZ,5@++5sqz֫}] +)ZڲV#H4Pc !D1묃 f|C(}HU9k{*Tܺ`LR]ݬWF)9Q?!q'QHVbև6/ j<ɢ?pt)[F`2@F򹿟yM(QykXqKF:#7VxV}@@*m1V6f4Ixšթ{WcIi^rFPnivhℐabR.V?F4R1ү_H̰k\}vi`sx/ wjv!j-twMb!ި vcXhbˌEn,|} NHJ!#d8Lc;eB'(!lD?BI1t):hscfp(N3DV]v PEfkN`m d&!$ ΃蓛nʱCaZNcXA1(~ZvhU%L¾;`Mt%¯1ɤ<ܣU')ޢjV*]Y[Ŗ95;bv!Jw3za aٛ.{uKHE3MjM.gZ,u/|t9.98,R}OAsNDF&ZQ[!fcؘ'9ؤ+gdp.y+h !Jӟ" ͵ %ê0JfnT֜j7cz6ڮyhkG8X`U ґk狴u~b$h:~!խ}T b6aO']fثJ,8xc5Cr D9nfzं.IC%p8.?P,`F-_Rd~`?b'f,Ѭ?yӣ?;- 20Ma">_j W`4(")mI\-DU==kCd\G3yMrN!vO2vJIhԯ J*͗>j bg3yֽ^U$ߧk"Ӳȣ^%k D:[3Jb 29Kv4>|DnrWvo>((Mu§\1$ \="9$T;pm4mWe^Q,&$8&RyxTh2݂]2 $ڳdIU/3RVG/^kt{Z&eHݳ]@\7cWY]+y00ɟD7xtqgxA= 1!@'P;з0JJ>sЩY[hA擸XЅn̕ѓ*hz%g|&U'k)ckT`m(үùZ jiN'k`y~V:F^|~A@]px#joho&ۑ H7D"iCo>ET{T؆- v; )<>,Z]a:@B" + ' T6?)4ݨqA\ݺw4[NjZnʓ˥Ik+Fd2P{ bla`vS+_KmLķi3Oz|^eIEps,V.e![q]Fx)P-bR ӷd \|av0r*&Q1^S\;`9Y?G`ŠpQ:DQ D gO7W& n:p+cSF ?<W޾Pzz헶*̭ꐾI,_np Pd]z1_9 v;.3ID0߂n(?eo8W|sUS:wg.|~5[%qkg+9ɵt_N:!]90p hX.p7|crJݜC^k}`f!#v,uIR9JUⶉ ~-td- IȘhNKx TRڤwa7tW=80tJ @&g_"_O1JKGD +_PX""d`ђ5s9_==4osp7^~SoD%$ T堡+i5m98e +{ZZ C 4bu;]L@Z7s>\%xqݶv$c:pŠNOGvpVL"i2ʨoqC(Mif䡋^B4[@Ei]LmM]R~r;kVNP ⮞TBH- '9&%8R;@;#IdM;:HFCҚ:@ˊuZ$#s?Tѽ/-=`@pMAP}/B %|yQg z&汦1 6ƞJIQ\UL!yC;~niR}YKZӑJ|쾖gS@u]-'@ StyA F d2oOYT~G3q3)O3 =(Ngcݱ:Dc =l 5 P{?ZZ{^ 7V@Яԉ*xݏ{E( j<@aHf)qa.לzjMO[~ Iks\3FVP5ybyt̅J_( 7RC꼛:-Azzw@}gnyIہf8Whtٯoe%$R%OrRhԖ7vj4z(-wx^-6^R#*d!fc' ٱD7E=CfX9{d8{ڶՍ A_.Qd!W'2i5 `g2a? ςcT /˗jOy`Y[EIyU&"&:[Sog!K9#̗슈?/i0rHFkSh|`,ݐzJ#&=YF e)y8wU(޳'r@N_ 1;z 5UPJl{K\Iu( DOڔBA#/I9 UeISР; cIͺnaAsҦv\nKP]XJ6{~gAT ulV|Xz^:uȒ L*瑘@H@9^iWG]iQ?(cVPݭ]!tI.F@Dmv\z{UlI~w"F}M,=w)h+ט`kNyCc(i_ ·bIL>u *ijBqNX."Bc!KCs k2)kFHYF,k hh0}/3֓|d疛5kZ(?%TQG V֠ Gt01+^RIӋӰl/pf0](@SlS+5Ȇt`yVq?Iz딠D&Q1MĐ1@,2VFmX56p9 )>=[W|C6諬r!w Aet;&M eK@LT_fHl%ꩢ:X7XR n+<~;|YO$5|䉚h4K^}o(eEctN:wL#z ǵـ`H"YK HD2c!]L᨞ry3d!}Tbt/_GED/Cqŷķ&1I:%ɬۧXσ%rI 6ij%g\7p[cƳTIT~u 9ז_UiRF>S(dg<'uuOvrztl)%ƅձH'Wr6U ~ \^p2lp4.F~%SOY-it@p\ʚVY:d- vasHzYm^07Y=,3.~,YW xdQ=X\Eaڬķ(/2īT2D]ֶ_D~ѹ:a:P^T@2ueY^|@l eE6)݆6ܳl nN:1c"d9w }uC}rHTΙ2 6h!\۰B,C7zie8 WU+3P1e;eRG[c1CQ̿ئ5jd;;-);><^O~Yү\+ץ85GyٱfBdk7%4YS}q%Hc=m^P*8q6ݱ--O}<^s?jWs\Zwk Bʹ7QWڝPTh_8kT؝irn@w~4=83& ;fğ|Wx}W>>CVj fgm21%Lf' F!Aej"H>y#QQ`VL>nđǓm˧+P2m1YlHGn6&l\DOas5~¸&5Z03gDg 3>j*>β꥞E [; Z H[G2饀}> ){p U wCv zPxL,V@C]9qX'sx̹ԅNKʕHN"i~Dzx߼' I!h/h2ce8Lw^y%$s+M(^ ҬQ9Mnzߘr%aZH<56L]T4 *҈S?Qsn$5߄F@kVPWB4b"/ֻ% RIUʦg8 ,G$+3L4!J[8 "U"<<*&r#uƉFg񖨁~Iy{ If!yHxJ'+{,kYQz'%ptR1| g7 bS @&3AZ6DzLDx : P[!YbmAq8:apY0Gy:Wa*5 G;zB.DiZ07gHשkYKcGNJkoGU7ϩŦn`.'jFƁ`ڳrSLϥKPҴRM&UcfʭAُ<,ԗDRXW߱;1ʷw g#'DDqn%=e"1ezv`o6ޱ%}Ad<"N&1铬\Cw6*nHųIAM> 5(׉+)#yiE;qyWYsz}HLH0Eo;UҢkmFi D@dmrZSQa/Z0ENHYSE)M_DS>K?8C˛6+J:ADlG/#a>ů=vMsm$`-6c?x삅GDWf יQd` z-  n#a.}%@lءXl'0BSo#'OKyc !t?GQoZk$450tDHtk yHe'pZx J`rzup‡J{:xMGTF]F{ 6镠<_T ]倢$Rnˊ@'7ҢjX̳Xa%[FW'?g%5FO|VQ:t4Fv^5_1na^%S,= 4%NkO8ԼXX ̵?>ôxHJS1g1]ȖJӇ5S%T%bߢElAhʚ cnܣ>arnLJ$Be.zqdB7ix@P ztEl$wXe?dxnc燻ǾV4+ QX;zl 7~#KC7T:1oz[3l^D;xJ?:KCU",UwlcaQ]fkphtG=ߚ8Qb&$htJAJ e-:5uЖUof8֔!5S<@AҗRNKtNpĬ Љ;)().1qS"]t8 i  b0]*˱ك)FϋJY-)na7u8dbdQ2瀙~Xfc]ry8./LrRwzz#‡.: ?*E{WIGqwEg(GlJq\ws|0A䚸FC/$e&ʗ,Er^jx)Փ;['$2r67ypNQF`CJ R.6v\ TtkW!1n5a@T 79/S{f0]6NGPtR q $Ǝ(T%!K&Ƿ |cUVc>E1>RoUHH=:X0[Bpد<8)t{` chaШ샾[d[v4nL+)ŷ53G:}T f#$GaOη2*OTġ )C>:__ѱʴ7qd ,V7[m7LZ˭0*9WIL3c ~VJd`Vr(уuL$q8R|{MhGC'xs!ҷ]mg8doҟ>C˙`Y&'o^л94YnD) #?دzJX\ߊOd9љdBɮz!pUtnkC}|Ewlt߉نUM KmueԧP/9k1jWYY(ee˾nj1AÆ`mF;5n8,rV/\|GvˈzP$c|Ҫ䤰\6n"cVTgs \#&;ZÁ h5i\dзO1W[HUVUijA|B;MA`lϲڴż#/%qH-jlŋt[0S&/vqܺTͯVV~(sn`>zY^`| +1$&Jk=] [THScɇ/l%ʡw4UBF1;ԌԿX][Ri3ZA?ۺCWP:S}JùiLЩDwV1f31oG1-n}ESHR_,39Z幓gWy,zH">{oݦq ^'\'bl{o&oA{kLʹ1^Њ:_,|ܜmEAfwu`ŶsKl B[xWv&1qoCڦ.4MiĭBU2fPG}9%>^a\0_&6=fLq+?ƊPu Lq:kJP]QݧIĎT4nХ.&&V$O4d&52-zQ c|zh"ۄpwvhj6w;h})Hya彿WcSr_ZCv @6q<̯`V =$tЫ cYZ6Rl %\R4Vktv $ `~ގ}-"5[-x)FpىLixՉer~cOY'O_H"_NB3{'vNoW>cC=z4"24u"kxR}TqYA 8U,:By H4H=G.אPb^([] ڬT fpMΥR3pN} F­2ToEGpREv]J訐aCQjg뱀a-!aѓR:H5l  9 nQܬF}jUn?c?a}Sq8d5!wetNkܒ"X!rwoZÜ-y׏k9`4UGI%G.Vgԍ0#̠3cÓZ-5ѐHqG 6F|AҘ?w)v3j.ݿVqڻ%yK䈤tbÜ99OO 55Wł{"J`*Pky \*x`3ausSCHLi6-gmjHcS/ P^Wr&_"F̫.s|Mf&Mo\M[]cg"U@/tjViwI ksv* 2Ho}6G"cU[X !^}Y--K6X&mEuO_#YrL *L7MTJ]4 O]+W npN/I$8yBD (fxʎpfVUOҘ-Pe~ma9&a) >߅ 'p(F,_UnIoUQ8H0hӅ%Z6yUbe;i$z$F0WpLWn Xf6ZFk%n48tm- -r.p/J9K_kU;.q>ȴDi|a߽ӴO*4C;hZ@'8/W 9\! E|x hn½rn-5o>˵TwdN%;7 jw/mṼh1-o)y3fBUdLbZe'i5i({-BV y)ߤOU h@&.SsF[J؂<b;?o)OIqc^SMbF ܤH%¼nѓe J=IBlmlO],joeE@wKR"5L+N`^oA" kx6N$LFE(D# hyʒ  _58U26 @՟M2tu4 S_ d_R#摒;@_r>o n~4mw>uu)8xc5Danh'zDG]c>4`^!'%D 4AyļN7WtFwUu ڐ@O}T,+@HxوW9;#^#>$nLXdn>1FgHG9w9!Sب_ruhy!0. kb9_15f9TK%o` E@MRW8 sg7X 6ɣaZ>^) j`R/sAC Pz0R*4GU#()c:T+H)^q7ˡPل_pt|B '_y\.g"5hCéz4U`Bmsf(1#@.75ntq 99'y"YWbc5S0`{Z咭oX&hR4LtF&4UnR0XaiClk`i?\Ŧ4fQQ՞u3U{ c@`rZq?sKlȓdy]/NK5Ow{KWGer\zn*v5u;:<$)?_C":)=fM!z@^0 T/ORJT0zVQAOϟQ] rƌ s!rlyn 8X~x1 ! Vf.몢&^E@y{/3UɡE|RNF-! bfگ'=(cjIrm3q@sE'!OX|()ȑE/)6ZIL_VkgZxX|ɓf:(0̛Ѳ/t_p04ff UE+=2>od,P RP-؂q9ʓ=˘<&B͈=?' VҩEV .'f3);]i̲͆K k#I9O4:# ի`]x}@ᜢ].ʼnƟ[PQGY@N7mT<(y͟BgύU1RikKSGF - ixM˲ L-ϩa,n |Now0yPXt*a|#4k6v&lǸ1F}IrӱD|{]䩗hv5+6&u+R/rwUʘȌ4y=JhF:ɹzԭ r4gLZ*;P/hUɩ5ǂiJ%y̺o!-XS̢k.j[OO![fl5 r%IXbgOZ^h('7>O|m F.aO\[46D'6ig_)L=~i|^'$T-$8*ϝ'DnXRȕsSG d-XQeƘi$nɍ 6b`p|agJX]&jDvR TZ*:ݱW7e A*pٸ;k I[RQbCwւHt=؜X"łCԵ^-dTbnhƹ3 TiA$H0gVl.gِ? QךYmpoc5\Rsc78t*ϭ[E9XU_g(dM$Ԏit@#9-ѝQ.V4i6oEVVNGI,J+>o%=`k݉vʻ{fv@1s{ q4γfȆK0K=4Ê'~(ץ'I/&FS{&cqh'.(,rI! QW Ry?(TYGI2 (F=I7uCJmՑE$V0YGZIF߮Lݥxz[p_;_(+.zB)m8|X_H>XXϔ}-TO|Ar>cb m&2÷ĕDRF~Кe"0@I{f/s풹|8wp 刳Ac3P_s;~"tiRu4\%8`C]o7{zQqA}+iH|[=]ft|dRzU(~ o>'%w&S]TXt1C"ɊpT~  L]E1S~cy)NfLo?X% 2*]TUޕ!ܜ 2ih#9֫@THsPgkƎ9!ɞNbg(987q؟BO H&o}-%PparN[XE~FgF44哸WGk?zN뱳s=. ye"6N4|2#ֶ)U2Ȑ»?^Gj#8xOQcDgxYŎNwgCzOu'_q2]Eg.&k;XQɚ/@c8s:p] ij5*&rhy?҅ڝ]rbs)NI 5Ok9u^E_=˷&5avS%ʧ.hN'&r jALq@_WlcPo5R:S%qm5m̟?嚺vgd *83Љ;bw="[Zw:-{J޶8FSՏݱgRss 䴊0wz!)6L4Fv^ JC,/F ̳|&Ѐ&(TX9,9z84hi//'44al9Ro"aY |THrKbAfM8|1j#M@xA_ǒ*SndqpA7sry '6IcƔ}`F,KQPNGٝ'lKH$z9~j/ Wĸ, tK2h31rҋܤH:jׯ.:pRIg/sQ+#ټVZ>],eod]9cnmtW\C 9 ":;\N+qfW/'; t{h40 Oc&o*AVoEi{%)0@t^;3=Y[ȟ!pǯ}*#c>.$lD^H {BvwySO`~9pXk`/%TP#ZCdi q%"SBX(D^r6#7;q@әyG?e&Ns4 (;]"E0 kYeo^%%HT}i:_s6ui%,[mEp o6h`%Q|7{.,}o1(kA ֎`#dwT,|T%|QK=#M,&?XXJ^0N)P>,0|pz=o*|܇ M8ClS@4w;D111閎?j@8_ _J g*ѽXYXoúϗ=pxm9O=3bqk* kn{p\SukGĿO\I"S {\mCv䴉?:,h9Hf-Å}_hDlʂ\ʗj G|[Z:.]GŒȶ]"-l݇&W)PCېTi9XrbB^_S*U!h95"Kt$t*iXg#Դ =<:@rnyS-J~ -Z2dK6n]p譊`h|*_ P)Pw) jST+<׶VSaf <Ыku-lrD9$;S)ԩ!e[6oS<$6C9ܤ='\R9L^;I.nHRM&%3hGo VnfM{ϐD1A$bQ Wqw @mOj焿3…tC)%[8\8a"@)Qg5=ړk2 @`14mhg IU"}cڛgċ ԁnԫXDfQc =D^""QcgֳP` yT]-) bg`Y f*R?cxu_42"9 M@t8scĹ-Rl@O'G8A嶲N)ol帀,k`'ntʿ#E UʃV#7l q`ŘUm%qBVǎ;}=F{<^0ƛnI0<[X?5((S5:5vG8 ̾zؤt"|Ќ"hOHŤw1}0OjY1W @In$X8~lf\+JIFz>i{VD&?D 56TA،CpEW,FCdڎTH}m$I`j/gI B ϗi=E Qm6m1Vfl]h zz˻",8 ˭l2!3';ϙeh@/FMtG^?O*r86ahAc2q`|VЌY?iW;q. LEa9fpG#\$/nWU V P2p rQ,3YeD|cх1f_}n5x} 'e0t/|1oJyZ^5kU.mhY Հ GU8#-qPvK:F]nLcrvSW v>ŅItIB` KҼ+'Bweթxq`@ ۢ7͍AOT)\✚'`/ o1<ǜH;!KMĠﲭ΀ "U{#3}Ofcivʬm ZJ£TgEwUXx= 'mI+I0-&3H7DW " Ol'wi_UQԉp3DaB1^,qRMORpQj,K|>tS4pTg ?IUTY$,"V" 6*!łAM?M O+Djр8i_NۢLGpTx/ĿURH4 _fΆq.٥Y|7맻ް@֙c{ﷄW\? cDê#9t9"ܕSaA 02e{N#AwZBk_dRNpv@wTvQ̱/-^1 Ɲ+V•AsedX8htcroJIQT~=o6ЀujJhKd+@Y.P /J1ГęUzHD/+:04H'Rha]h [ȴ]9 -oI :$aq%ٙy5v[-mGR`f=yy>uKqSC%"9-yMke&3ό~`2Cs`Ĥ4Sm#K G7|^ԝ <|"[̞0A'ԵVs%y/`49Fx$Z] yB#`/2 @יC1UA@<Gute- bd + endstream endobj 154 0 obj 148466 endobj 155 0 obj <> endobj 156 0 obj <> endobj 157 0 obj <> endobj 158 0 obj << /F1 120 0 R /F2 125 0 R /F3 135 0 R /F4 143 0 R /F5 139 0 R /F6 130 0 R /F7 144 0 R /F8 156 0 R /F9 115 0 R /F10 148 0 R /F11 152 0 R /F12 157 0 R >> endobj 159 0 obj <> endobj 160 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 6 0 obj <>/Contents 7 0 R>> endobj 9 0 obj <>/Contents 10 0 R>> endobj 12 0 obj <>/Contents 13 0 R>> endobj 15 0 obj <>/Contents 16 0 R>> endobj 18 0 obj <>/Contents 19 0 R>> endobj 21 0 obj <>/Contents 22 0 R>> endobj 24 0 obj <>/Contents 25 0 R>> endobj 27 0 obj <>/Contents 28 0 R>> endobj 30 0 obj <>/Contents 31 0 R>> endobj 33 0 obj <>/Contents 34 0 R>> endobj 36 0 obj <>/Contents 37 0 R>> endobj 39 0 obj <>/Contents 40 0 R>> endobj 42 0 obj <>/Contents 43 0 R>> endobj 45 0 obj <>/Contents 46 0 R>> endobj 48 0 obj <>/Contents 49 0 R>> endobj 51 0 obj <>/Contents 52 0 R>> endobj 54 0 obj <>/Contents 55 0 R>> endobj 61 0 obj <>/Contents 62 0 R>> endobj 66 0 obj <>/Contents 67 0 R>> endobj 69 0 obj <>/Contents 70 0 R>> endobj 72 0 obj <>/Contents 73 0 R>> endobj 77 0 obj <>/Contents 78 0 R>> endobj 86 0 obj <>/Contents 87 0 R>> endobj 91 0 obj <>/Contents 92 0 R>> endobj 94 0 obj <>/Contents 95 0 R>> endobj 97 0 obj <>/Contents 98 0 R>> endobj 161 0 obj <> endobj 162 0 obj < /Dest[6 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Next 163 0 R>> endobj 163 0 obj < /Dest[12 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 162 0 R/Next 168 0 R>> endobj 164 0 obj < /Dest[12 0 R/XYZ 85.1 708 0]/Parent 163 0 R/Next 165 0 R>> endobj 165 0 obj < /Dest[12 0 R/XYZ 85.1 546 0]/Parent 163 0 R/Prev 164 0 R/Next 166 0 R>> endobj 166 0 obj < /Dest[12 0 R/XYZ 85.1 492 0]/Parent 163 0 R/Prev 165 0 R/Next 167 0 R>> endobj 167 0 obj < /Dest[12 0 R/XYZ 85.1 318 0]/Parent 163 0 R/Prev 166 0 R>> endobj 168 0 obj < /Dest[15 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 163 0 R/Next 185 0 R>> endobj 169 0 obj < /Dest[15 0 R/XYZ 85.1 708 0]/Parent 168 0 R/Next 172 0 R>> endobj 170 0 obj < /Dest[15 0 R/XYZ 123.3 431.9 0]/Parent 169 0 R/Next 171 0 R>> endobj 171 0 obj < /Dest[18 0 R/XYZ 123.4 510.4 0]/Parent 169 0 R/Prev 170 0 R>> endobj 172 0 obj < /Dest[18 0 R/XYZ 85.1 317.1 0]/Parent 168 0 R/Prev 169 0 R/Next 173 0 R>> endobj 173 0 obj < /Dest[24 0 R/XYZ 85.1 625.4 0]/Parent 168 0 R/Prev 172 0 R/Next 174 0 R>> endobj 174 0 obj < /Dest[24 0 R/XYZ 85.1 196.6 0]/Parent 168 0 R/Prev 173 0 R/Next 175 0 R>> endobj 175 0 obj < /Dest[27 0 R/XYZ 85.1 616.1 0]/Parent 168 0 R/Prev 174 0 R/Next 178 0 R>> endobj 176 0 obj < /Dest[27 0 R/XYZ 85.1 362.2 0]/Parent 175 0 R/Next 177 0 R>> endobj 177 0 obj < /Dest[30 0 R/XYZ 85.1 756.9 0]/Parent 175 0 R/Prev 176 0 R>> endobj 178 0 obj < /Dest[30 0 R/XYZ 85.1 193.2 0]/Parent 168 0 R/Prev 175 0 R/Next 179 0 R>> endobj 179 0 obj < /Dest[33 0 R/XYZ 85.1 436.1 0]/Parent 168 0 R/Prev 178 0 R/Next 180 0 R>> endobj 180 0 obj < /Dest[36 0 R/XYZ 85.1 756.9 0]/Parent 168 0 R/Prev 179 0 R/Next 181 0 R>> endobj 181 0 obj < /Dest[39 0 R/XYZ 85.1 690 0]/Parent 168 0 R/Prev 180 0 R/Next 182 0 R>> endobj 182 0 obj < /Dest[42 0 R/XYZ 85.1 403.6 0]/Parent 168 0 R/Prev 181 0 R/Next 183 0 R>> endobj 183 0 obj < /Dest[45 0 R/XYZ 85.1 512.6 0]/Parent 168 0 R/Prev 182 0 R/Next 184 0 R>> endobj 184 0 obj < /Dest[48 0 R/XYZ 85.1 627.8 0]/Parent 168 0 R/Prev 183 0 R>> endobj 185 0 obj < /Dest[54 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 168 0 R/Next 191 0 R>> endobj 186 0 obj < /Dest[54 0 R/XYZ 85.1 708 0]/Parent 185 0 R/Next 187 0 R>> endobj 187 0 obj < /Dest[61 0 R/XYZ 85.1 251.8 0]/Parent 185 0 R/Prev 186 0 R>> endobj 188 0 obj < /Dest[61 0 R/XYZ 85.1 227.2 0]/Parent 187 0 R/Next 189 0 R>> endobj 189 0 obj < /Dest[66 0 R/XYZ 85.1 732.9 0]/Parent 187 0 R/Prev 188 0 R/Next 190 0 R>> endobj 190 0 obj < /Dest[66 0 R/XYZ 85.1 492.2 0]/Parent 187 0 R/Prev 189 0 R>> endobj 191 0 obj < /Dest[69 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 185 0 R/Next 194 0 R>> endobj 192 0 obj < /Dest[69 0 R/XYZ 85.1 708 0]/Parent 191 0 R/Next 193 0 R>> endobj 193 0 obj < /Dest[69 0 R/XYZ 85.1 341.4 0]/Parent 191 0 R/Prev 192 0 R>> endobj 194 0 obj < /Dest[72 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 191 0 R/Next 200 0 R>> endobj 195 0 obj < /Dest[72 0 R/XYZ 85.1 174.2 0]/Parent 194 0 R>> endobj 196 0 obj < /Dest[72 0 R/XYZ 85.1 149.6 0]/Parent 195 0 R/Next 197 0 R>> endobj 197 0 obj < /Dest[77 0 R/XYZ 85.1 322.2 0]/Parent 195 0 R/Prev 196 0 R/Next 198 0 R>> endobj 198 0 obj < /Dest[86 0 R/XYZ 85.1 756.9 0]/Parent 195 0 R/Prev 197 0 R/Next 199 0 R>> endobj 199 0 obj < /Dest[86 0 R/XYZ 85.1 600.2 0]/Parent 195 0 R/Prev 198 0 R>> endobj 200 0 obj < /Dest[91 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 194 0 R/Next 204 0 R>> endobj 201 0 obj < /Dest[91 0 R/XYZ 85.1 708 0]/Parent 200 0 R/Next 202 0 R>> endobj 202 0 obj < /Dest[91 0 R/XYZ 85.1 653 0]/Parent 200 0 R/Prev 201 0 R/Next 203 0 R>> endobj 203 0 obj < /Dest[91 0 R/XYZ 85.1 585.7 0]/Parent 200 0 R/Prev 202 0 R>> endobj 204 0 obj < /Dest[94 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 200 0 R/Next 208 0 R>> endobj 205 0 obj < /Dest[94 0 R/XYZ 85.1 708 0]/Parent 204 0 R/Next 206 0 R>> endobj 206 0 obj < /Dest[94 0 R/XYZ 85.1 653 0]/Parent 204 0 R/Prev 205 0 R/Next 207 0 R>> endobj 207 0 obj < /Dest[94 0 R/XYZ 85.1 598.1 0]/Parent 204 0 R/Prev 206 0 R>> endobj 208 0 obj < /Dest[97 0 R/XYZ 91.5 744.9 0]/Parent 161 0 R/Prev 204 0 R>> endobj 209 0 obj < /Dest[97 0 R/XYZ 85.1 708 0]/Parent 208 0 R>> endobj 110 0 obj <> endobj 100 0 obj <> >> endobj 101 0 obj <> >> endobj 102 0 obj <> >> endobj 103 0 obj <> >> endobj 104 0 obj <> >> endobj 105 0 obj <> >> endobj 106 0 obj <> >> endobj 107 0 obj <> >> endobj 108 0 obj <> >> endobj 109 0 obj <> >> endobj 210 0 obj <> /Outlines 161 0 R >> endobj 211 0 obj < /Author /Creator /Producer /CreationDate (D:20060903164214-07'00') >> endobj xref 0 212 0000000000 65535 f 0000732881 00000 n 0000000019 00000 n 0000000968 00000 n 0000000988 00000 n 0000007830 00000 n 0000733062 00000 n 0000007851 00000 n 0000011082 00000 n 0000733208 00000 n 0000011103 00000 n 0000012265 00000 n 0000733355 00000 n 0000012287 00000 n 0000014705 00000 n 0000733570 00000 n 0000014727 00000 n 0000018133 00000 n 0000733718 00000 n 0000018155 00000 n 0000021871 00000 n 0000733866 00000 n 0000021893 00000 n 0000024967 00000 n 0000734014 00000 n 0000024989 00000 n 0000028003 00000 n 0000734162 00000 n 0000028025 00000 n 0000031140 00000 n 0000734310 00000 n 0000031162 00000 n 0000035043 00000 n 0000734458 00000 n 0000035065 00000 n 0000038120 00000 n 0000734606 00000 n 0000038142 00000 n 0000041551 00000 n 0000734754 00000 n 0000041573 00000 n 0000045718 00000 n 0000734902 00000 n 0000045740 00000 n 0000049004 00000 n 0000735050 00000 n 0000049026 00000 n 0000052739 00000 n 0000735198 00000 n 0000052761 00000 n 0000056392 00000 n 0000735346 00000 n 0000056414 00000 n 0000058616 00000 n 0000735494 00000 n 0000058638 00000 n 0000060009 00000 n 0000062483 00000 n 0000060031 00000 n 0000062461 00000 n 0000065478 00000 n 0000735642 00000 n 0000065500 00000 n 0000067049 00000 n 0000067071 00000 n 0000079875 00000 n 0000735790 00000 n 0000079898 00000 n 0000082311 00000 n 0000735938 00000 n 0000082333 00000 n 0000084900 00000 n 0000736086 00000 n 0000084922 00000 n 0000086099 00000 n 0000086121 00000 n 0000109526 00000 n 0000736234 00000 n 0000109549 00000 n 0000111235 00000 n 0000126053 00000 n 0000118930 00000 n 0000111257 00000 n 0000118908 00000 n 0000126031 00000 n 0000137571 00000 n 0000736382 00000 n 0000137594 00000 n 0000138993 00000 n 0000139015 00000 n 0000142869 00000 n 0000736530 00000 n 0000142891 00000 n 0000143715 00000 n 0000736678 00000 n 0000143736 00000 n 0000144519 00000 n 0000736826 00000 n 0000144540 00000 n 0000145405 00000 n 0000746542 00000 n 0000746690 00000 n 0000746839 00000 n 0000746989 00000 n 0000747133 00000 n 0000747275 00000 n 0000747413 00000 n 0000747559 00000 n 0000747709 00000 n 0000747861 00000 n 0000746259 00000 n 0000145426 00000 n 0000146208 00000 n 0000146230 00000 n 0000146423 00000 n 0000146734 00000 n 0000146910 00000 n 0000149836 00000 n 0000149859 00000 n 0000150056 00000 n 0000150433 00000 n 0000150669 00000 n 0000165130 00000 n 0000165154 00000 n 0000165361 00000 n 0000166011 00000 n 0000166521 00000 n 0000176112 00000 n 0000176135 00000 n 0000176348 00000 n 0000176783 00000 n 0000177091 00000 n 0000188424 00000 n 0000188448 00000 n 0000188655 00000 n 0000189198 00000 n 0000189603 00000 n 0000289120 00000 n 0000289144 00000 n 0000289321 00000 n 0000289992 00000 n 0000389169 00000 n 0000389193 00000 n 0000389370 00000 n 0000390041 00000 n 0000390105 00000 n 0000548288 00000 n 0000548313 00000 n 0000548491 00000 n 0000549162 00000 n 0000582063 00000 n 0000582087 00000 n 0000582263 00000 n 0000582933 00000 n 0000731514 00000 n 0000731539 00000 n 0000731716 00000 n 0000732387 00000 n 0000732478 00000 n 0000732649 00000 n 0000732787 00000 n 0000736974 00000 n 0000737033 00000 n 0000737194 00000 n 0000737357 00000 n 0000737493 00000 n 0000737674 00000 n 0000737879 00000 n 0000738075 00000 n 0000738267 00000 n 0000738507 00000 n 0000738638 00000 n 0000738769 00000 n 0000739020 00000 n 0000739227 00000 n 0000739406 00000 n 0000739705 00000 n 0000740023 00000 n 0000740297 00000 n 0000740456 00000 n 0000740615 00000 n 0000740798 00000 n 0000740987 00000 n 0000741154 00000 n 0000741329 00000 n 0000741519 00000 n 0000741710 00000 n 0000741846 00000 n 0000742032 00000 n 0000742310 00000 n 0000742605 00000 n 0000743131 00000 n 0000743406 00000 n 0000743542 00000 n 0000743692 00000 n 0000743943 00000 n 0000744080 00000 n 0000744206 00000 n 0000744345 00000 n 0000744536 00000 n 0000744650 00000 n 0000744825 00000 n 0000744929 00000 n 0000745046 00000 n 0000745152 00000 n 0000745335 00000 n 0000745471 00000 n 0000745696 00000 n 0000745886 00000 n 0000746028 00000 n 0000748016 00000 n 0000748150 00000 n trailer < <07F6ECB698284A5D33957FD1FBD27739> ] >> startxref 748427 %%EOF phpgacl-3.3.7/docs/manual_html_m770a5a15.png0100644025754300001440000000400610005475146017321 0ustar ipsousersPNG  IHDR7 PLTE'7IDATxn8 i`r| .޳@>OFBS.8ivY 3UHEQֲ̥\^<]1_SVRW7,xBȣvGxSQR5%?w{*v R[=OUEiVœ[>&oEEX*ۖ Uup/ڻmx{R G[ѭ*=?'U"dEK|W=T2bBx lsxf^Hj-^^dODݒ z^yO O434I | o]w2 b1C ًr/k+ t2S?d,<abo+{O,{dN,vN<;3H=[ ssiA^^}ܘUg>R\l9m "Wz]PdԳ[V5:_qF9iOu8d O:Lor]s)qQ%Rg. +I2r&9<2OS{R^<w7$gQWẴS)czeͼ>m)IuhtE,6gI|'U,di(1M{xᶉ5k孃4`6l-;Nl8Gfg.[Z>5S|e-kYZ#Нr:Xqb;Rti{pԝ'%F;gsxq3y6?&E$ܒPPIaZ#M(9B j VMm.Ivmcj},T<6YtSЛiyy2ܺ+9"{̓PWD f2f#Wքf$'E5TrGvrVH|d4'Gxic-# x96Mvdn&f>Aec- -`uf6I!Cʺ '#|PfƯdWܔ+ s^Zֲ%a]˲<2"w9ҤI <&%x8N]X)D1<%ޕ=>iɮA ]yr|LWA7#cgy|R#V$nT:~kU{:~>'o|]?u(Rv6c>/,Ld8IENDB`phpgacl-3.3.7/docs/manual_html_7dced6ce.png0100644025754300001440000002740410005475146017465 0ustar ipsousersPNG  IHDRr0PLTE%%%VVvvfgf]b IDATx;rqTĹ7t2O@*`ko`ud&|j4fa@S(P&P&P&Pd=TI-"mjwjj=ACLX X 0L_pB5(S υB |fqBr2hh i3Xg: a*$-Յ`hx}R6.>e3iŁB5PU: ZpP z-Nr瘩j)TiM-\&Dκ=H*(vZUuꖭ;fjZȲ۴xT0 O&p~j^L6n90 Ɂ~:V][S5)TP5)TP5)TP5)TP5)TP5)TP"P UjR UjR UjR UjR UjR Uj }cBPT|ͺO`7d&*b`>0AGGGb -p[CA5ÀzޛZh~PzdGXfpq9:V)5 0P`Aiܼ9CIÐ ^ę{\:e>r2`BLnR ,X.p%T އ -ygjs<P"'VR `d9C q:4 P,Y:{h mPANc4dzc(T] (jҩMP><٨KP7j?thP*Ae>Ajs TT1PTH I}S3=v{Pzj7jש0HzM}{ϝ>? ]w %ǕrøL&J MST*KEy?Ec(jO\IpA@CM܌PG 5Q{8BդPBդPBդP?3zMUkϤ:U*P5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)A5qJeʭBGiI-"mjZ% *(Ծ^8 %oE\6>P96ėp*F:H P9T A&:fjjBm PTuC訰qd^PYRiET:*WjCGVVZM; `j!tIq RP`^Wu^vBTp*.:UBU UBU UBU UBU UBU UBU U*P&P&P&P&P&PPw?Ɖ+*$Gbfk Nl<.7'7 qꚡݡښk`P{3T(rBMP=#Q|,38ʏI/ i# +h N6,>k<:Ͽ;6C纹~b;NR3zzmgq8:c Frt'V@@0 c1HA*F/˱2i O2>Lh< PJYب%lٞlYP P#ж$# *Vq (ڠF,=&-"*Tkd I@5&TŘm!+H X1#Cuְ.C]-N*JPYs2TWkA>TW$U" Tu}SSnj۠t*PCZX[S_?g ?K?Ө[{n6Sp>$r $l~!P#KTU>SO96Z3bBThwa u6I~(T.荁]W@KI@Qfj M *|6TThtї7M !0> (#G|} T#NE-w) 9?7FtOwXϘ<2h!cxٲlg|:FlN6SQ~kPm{(+Ma=L<ހN%!x)Ce{B) ƥ*:ءںe1:Rt _6H `swbE~NgPs=(P0L*-*][;q2Ւ^$B:^%맶Ϡh ;@vVmM{|qLQ}:eUeO礋CTM uxPLeAֹοMǬ@oZσuw!@B?z1_r W kY//炩ݥصZhO0$Ie#nv;?FAfs6>Qp.Y S;} |lgKKsH&րv76Cmu;4LE{+#V jwFyr/*@L.l67A_ @uYύ66CRuw[AMuj5;:Pu[w/Ct2䆪?R?զ~jѝJ R?՚oLGd})TITvT u?ɬ7TT͕*ԃ5RkToS5~6YH:NZ{ER~nlB5DBOP }x>R'> {_lCfwBq<%dOG 7w}!yn8]2T 9SFd 6y ¶  P>ՉJ,Prg%L!s8EW-$2WL,P;<0v[T[ҩnNqA,ldmj#MWnM]_cc#Tp~ZoR? Fk&bPqZʿdPB kd\p}@ uTPnMwCݫPfTGÓ=X!Ub-oSaXg/ڟ]EA9܀( N~DVe>1z"CSMPJC'T[2뗍IRӠꐅ2eIb&c*:ShPMֿ?MNu-[6s?=~*ROu)lȡ2|4z(PGF U:_1M P?~?:= IgP?:P?P}}BF/d=?_|] EJSZ=C~SX=@դP/tY=v(mnb ˗/ox;gԟ_[ #_ .3jPwO?[_cb!Ϸ|.lxPH(3?ρ3N+Bzmg~K*-l:[&i{+p3w_ϐ9THPğaBKXSڭX$ !Ew뙷2v/O/&/WVJӍ,[*9+oTjA xzP~?5l *I8,!P39mTѩ\BQA ?IR_0V_j'NFYDgʠvn#w#[$Ԡ6ьTz?'''gOuB\vZp a\>ōs>NjP7=B= UBUcz{cqZzکӇAnaCU#OP/w)_ z-x-f~}k5&oo/o/VE?NXO^N-pk*_1ǙAEI}At r8[z#|!6$ۀ^%BτaB['PJ Qy{UelARL/ʋ^¿3o 5ˆ]V nJʩ@3p8G _ga 9ٙdݾL(gw|5>~B@%@ Jp5 9bPi-_ġvԳ/m(C\0ɡI\9Y@8׳5To_"fU _U QRC畿i@evz$~ 3aCSHKOK@BQNj}a4r~P/kX #Rfs,W~ zY HP5 eZ,K:S W^?02A]{KgjZP\^^r*I,s>&PQN$y":W:dԙP$u*өY2FNRC5g;'}=%9XPPӥUN%OWP|eP/1($t犚|ihFt<שWWVDVOxۂN^&zB@{>Xgf,+6+ԡC zrPTRTPXVuީmmPWDtUa1v:B:vY StZFW+k\L-s4uWڀð m[XY.֢ő/+~+iH*Vw iy6Ye. !0I%B<+ꘗVhd@ bØ9,@?sɖ!K^@W4q4K7u쀠l(ARퟑW 5KՍPwCEfx*Dc T{%Tg@-TXͦuk:/v:5ѤSЩ̔%V0`Cz)ƭt* :D@(*kY=\Fweڰ"suR4oXPN *0iC]7Bu] j|6eK?zIPhHXRuU-RH*ô<2l S5`/f *C?\)IRcbehؠwO]S0 8ghQ&N>Do%u̒.tZ d^WQ rjI\=KT*ԤS:~Q+/p._af7tK^,AfE4^J] uECF &%B5Wz3į5wE*߀VTJu1DN]3 5TU~Qބ"p WuRK_~[?i*ܫE."kuK_QsPS f}֩uF9UeМXY&-x,ʙs6_sO]%{ f˾NU=.MYu&UUŨ?W#&:kzRw|u>o4Ң5o%w+>a,|M3#=B\'_AkA)X#H2Tn3 Е]*_H:|+B{5K1_1-ijz0.\3Z@?:!>& raFZe|>A S7\/WNnR~@͜oֆA8DIZ1?ݮķ8zQNޅHX66,1=>Kj0Vq;"J)Bv/T󿋩tT|w#Y ^ތxoRƒ=Өփeԟȃw6_=Cjp^Px7zjԿ$5 H}]:zfԟgT$:/5)(uF9>qh(<+ZGQ@&^+ sW+q?jEuNdpʙ W35F]!HA*o9(80o;ЇҬuqM?h?P㘽 ?U~5flPWS//+ɬFjM  *fzSGA=Gǰ:KM]q9zǯ4@pq~xVE?xNH}7#n:6svbqw,TNj ޏ!72ΌTR__0s@Va.hmì{6Q*n'].P3¯0L 3Fy.K&^;:s;AN4\cC"5PZO;ch,2Y΃4ҕLc /s4?/z{%O^_%+&xQBa_~/҂d 2*bd TZ^3fz{e^"% 9 ߧ_a!lSY~NqYN$|NNp8 4GO1Nb@,N¹F'{@RI/zr¼LaU TE={?-5{;Mw §*m8TY=uI8xIDAT/_wͧb[>~ȆRbԣ QPob:f]?n Ÿ@Ȏ G1m,3 Q8_v o2anlqqh9 =C@co G?"K_\`iN+?B63o6o7BuVy(x 37@(Ưх&A-d9)>Lh<՟NOK HQĜ@yT`i;-LARlq/JPT(Qu}L*jR"ld9ɵ%g8p/.p``PCCU5TtKXiToSV{u SMPIP%,9NuTj\T:I:S0zDzLB :Ʃ)TڨS:}Ub?tngDm~Im]Ko0&s?B?:JHSx&ɖC yOuaX^m Q@$9V}dE }oJ*~zFjMjzFWӟ:Spيf ŽB+1AuaTn5nJy6u5&ۀݩ qJ\t ]B6xɰlyL;p@2F4A܃,I-c BVػ+ԘM,G j́n eFMs綀JZ \T990F"uj+It2jK7v=7FukjK:mS[ntȠF6M6+f6K=WYJFw'^!Nz᩟&P9`ctdQe jIF u/}78_nL*q BդP&:SxY0~QvuFi8QN_oԟa"N_cԇ8>z~| 4@.?L{}Gs{>I|w1ڎE{p|Cp|'u>v ?HaO$|'X>?,DD"?@W7;p}GlU#Bߵ;k~(ndMi0uMjG 5{PYU"Z&f ]7Y'.,x]5fܦ %CЩ,:&mr GXc/V`<,,.Yű?9IE+VPɢ.v/All2l_,궦L@vtjg:uWT,eAwש,ˀ}N d?JBU UBU UBU UBU UBU UBU UBU UBUc W7WẀ+i?ʮor8^@Rm%(kGDd%ʳPh,fH2T?|+B-H-?cA~l$&WV"ʚ:Br PE*RS_d. u  P&.MQtPE%P "T@GAE*TM U*TMP1Cc_,HF "يu \WoyM@i@ܒaKii,uDn8Pi\['d)kcgTkv [BzF bo\_C#1bYt XQ$ m,q#TkH.>MPDleCl~n(qL:fz!R )@8"x\ c@dGu-s $5AqyPs5@FɿdTsӕNĵ Tx?9H6R0xl{%P8\1$w y| *-a*u+԰]iTcS8y w("[.eB#T BTn 5T5P9} TeX@Ud8YT5ib UNutkөW7.N 3*[D6P֩N:;Isj\6ϱY$~h65TRǞbHˆAeJ6 qqߵ[zj7Fj .4&S02O10C(PS?AH+P:u)g6U&XOOӪCzV~헽4jP]ߦZ?6Mӱi]qjIU#Z'6r۶Ľ^Ŀ?~?*zW+TWjյP&NmLP7~ޠҨx C G҃|xQX-"Irzr&^;[zxGȰmN1CX}Ӎ3y0׋)AMVs`_vQsXF6DwrYWdʥ7_Fq\v(< a2^OYq6\]DE]Pӆi얅LoтlKZ&&ű Ylf{Je)Yҳꕡ%3Yf橮)\[d̠2ZAoS -ȡB•9(G;@+f)߬0be6CP2ܔ7Ig$ӋkC \Fi2T63T@jS3ò1 P,҉ihe*1C>oܘR?3≖Åf?02njx6=QDV;4@u 63FٲM U2Kj6h͍[@u9Tȡ}ܢYҩN-Q(Bѩ77Ԃz.TV, =vjupLVQ/RJRxG!rG\u?̊w-Lj,:?bOmӏ$[֩Nnizl5#zWS+w2TYꩴ5,Y??~*Tۓָ#xa~m2C\S֡ߴ!4BP;Z- Uj/?o!IMIENDB`phpgacl-3.3.7/docs/phpgacl-db-schema.vsd0100644025754300001440000075700010041540457016767 0ustar ipsousersࡱ> 345Root EntryRoot EntryFp4z'@VisioDocument6nSummaryInformation( SDocumentSummaryInformation8  !"#$%&'()*+,-./012789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+,D `ht  FaJo.deG phpGACL SchemaiTracking TextiDynamic Connectoro OptionalonnDatabase ModeloEntityeRelationshiplo RectangleipTitle block retrooTitle block classic 12pt. text Title block contemp.osoTitle block notepadTitle block elegant 8pt. textk Relationship.17Dynamic connector.18osoBorder smallctoNote box classic.18Note box contemp.18Note box triangles8Note box fileglNote box neongl15 ruled column ZeichenbltterMaster-Shapes(`ht_PID_LINKBASE_VPID_ALTERNATENAMESAOh+'0TS PXp(phpGACL SchemaphpGACL Falk John\C:\Programme\Microsoft Office\Visio10\1033\Solutions\Database\Database Model Diagram.vstMicrosoft VisioG$RaE  EMFRl@=XtHVISIODrawingL|QaEbF ??d(P(bFPHHH000000___>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>GGG>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>222888WWWOOOBBBlllGGG---PPPXXXlll!!!@@@sssOOOXXX描GGG;;;;;; XXX描$$$@@@sssOOOXXX描GGG;;;;;; XXX描$$$@@@sssOOOXXX描GGG;;;;;; """::::::::::::::::::描$$$@@@sssOOOXXX描GGG;;;,,,___pppOOO:::AAA描$$$@@@sssOOOXXX描GGG;;;,,,___pppOOOGGGWWW描$$$@@@sssOOOXXX描GGG;;;(((===ssssssllleee+++HHHssssssssssss}}}$$$@@@sssOOOJJJ;;8,,*,,*;;8;;8;;8;;8;;8XXU描GGG;;;888MMMfffffffff@@@UUU;;;```??????gggoooPPP;;;$$$@@@sssOOO777GG;__L__L描GGG999zzzssssss>>>777999999999999999999lll;;;;;;$$$@@@欬sss]]]ssssssssslllOOO777f描GGG444 ff Yoofffff;;;;;;$$$@@@ppp@@@999OOO777__Nff描GGG222;;;;;;$$$@@@888@@@@@@ ...OOO777``M描GGG444 -44000;;;$$$@@@___999OOO222  @@3@@3@@3@@3@@3描GGG444 !!+++:::%%%$$$@@@,,,OOO33333*33*33*33*33*描GGG222 f ffff...??????PPP///$$$@@@ZZZfff333999OOO777JJJLLLLLLLLL333333)))333333333333333333---&&&333000000333333333333 222$$$@@@VVVVVVVVVVVVVVVRRROOOJJJ888888888888888666888JJJ描GGG*** MMM@@@$$$888///hhhXXX描SSS&&&... :::sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss:::OOOXXX描GGGssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssHHH$$$ sss欬sss]]]sssssssssssssssssssssOOOXXX描GGG;;;mmm;;;;;;;;;;;;;;;;;;;;;描$$$ sss;;;```oooPPPXXX8;;8;;8;;4778;;8;;8;;8;;8;;8;;eeeOOOXXX描GGG777@BB8;;8;;4778;;8;;8;;8;;8;;8;;```sss描$$$ sss;;; XXX欬%%ssd}}sssss%%VVVOOOXXX描GGG444ssd}}sssss##sss描$$$ sss;;; XXX槧3@@3@@-883@@3@@3@@3@@3@@>>>;;;BBBkkk555%%%3@@3@@-883@@3@@3@@3@@3@@sss描$$$ sss;;; XXX欬5>> ::::::::::::$$$  5>>___ooo描$$$ sss777欬5>> 5>>VVVOOOXXXzzz777lllSSSeeesssooo111444描GGG444 5>>888@@@@@@@@@@@@@@@ ZZZ描$$$ sss000PPPJJJ橩 *33*33 &..*33*33*33*33*33 TTTOOOXXXfooY ggSffffppZ描GGG222*33*33 &..*33*33*33*33*33 ---333333333333333```描$$$ sss***&&&&&&333333333333333 EEE欬LLLLLLJJJGGGLLLLLLLLLVVVOOOXXX>>5߲ ߲描GGG444 LLLLLLJJJGGGLLLLLLLLLCCCJJJLLLLLL&&&sss描$$$ sssUUUrrr@@@888888888888888888888888@@@hhhOOOXXX>>5߲ ߲描GGG999GGG888888888888888888888888@@@kkk%%%///888888888888888888描$$$ eeesssssssssVVVssssssssssssRRRsssOOOXXX>>5߲ ߲描GGG;;;WWW}}}sssssssssVVVssssssssssssttt$$$ 9993@@fYoo fff@PPUXXWWWlllOOOXXX>>5߲ ߲描GGG777WWWHHH3@@fYoo fff@PP9;;$$$ ''':<>>WWW::: !!$$$ 999f UXXAAA::::::+++ eeeOOOXXX@@388-@@3@@3@@3@@3@@388-描GGG444 '''VVVssssss+++HHHf 9;;$$$ 666Zpp=>>~~~>>> eeeOOOXXX 33*..& 33*33*33*33*33*--%描GGG444 ...VVVsssssszzzlll`xx577$$$ +++::::::::::::,,,444 eeeOOOXXXLLLLLLLLLLLLLLLLLLLLLLLLCCC描GGG444 666(((::::::::: '))$$$ ===555555((($$$555555!!!ddd桡pppbbbOOOXXXGGG666888888888888888%%%%%%xxx描GGG222ppp~~~aaa555555)))###555555!!!===$$$ lll???===欬###pppeeeOOOXXXssssss描GGG444ppp###恁NNN,,,$$$ ===>>>###!!!<<<>>>>>>'''KKKsss椤 aaaOOOXXXssssss描GGG222 欬uuu>>>###!!!:::>>>>>>'''DDD$$$ 999666HHHWWW欬ffffffffffffffffffffffff eeeOOOXXXssssss描GGG444 ffffffffffffffffffffffff旗(((;;;$$$ 999XXXWWW恁sssHHH999999......999999999GGGlllOOOXXXssssss描GGG999LLL999999%%%777999999999HHHsss描WWWsss;;;$$$ 999$$$^^^Zppf3??3??fffVVVOOOXXXssssss描GGG444 ff YooffZpp描+++999ooo;;;$$$ 999BBB SSSOOOXXXssssss描GGG222 sss999;;;$$$ ))) @@@ @@@@@@@@@(((???ff5>>VVVOOOXXXssssss描GGG444 ]]] @@@ @@@@@@@@@(((***$$$ 444HHHHHHZZZ Zpp3??ff)))''',,,sssssszzz::::::$$$ f fWll::::::::::::::::::(((HHHHHHZZZ555$$$ ...AAA---333------333333333 )))''',,,ssssss߬kkk555'''333333&&&333333333---WWW,,,$$$ ===555555555555555555!!!ddd***aaaOOOXXX描GGG555***恁555555555555555555!!!AAA$$$""",,,,,,,,,,,,,,,,,,,,,,,,999;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;bbb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%%%TTT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,,$$$'''>>>ǿϿZZZXXXTTTooo(((@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Visio (TM) Drawing n#{kmdRl !fffMMM333 U8@ TZ Arial?NJWingd{Qs?wNPwMonotype SortnSyWmbo^NTimes New RomanUJ:DT1EW-hPT8* z/U~b b0zGz?)@4D.gSb}}}g !k5@|'ggK,K,  RA& LQ& , zO(?&/&5?\.?o ATg,,,'1/C% 6T$6~ (}(y  ~|~,)~P?"  } }U }}}}U}}-<%aBEEEEEE@FOXN}OJ`T.BEEEUEEEE-U}bRrQg'?5FA#i; rQGfGfrQc d?i SoSoo' `I3g:|b:FIpTe#| | | Y??l 93U4|K,K,IK,J(QAU M U$Q_4OO __0_XOjOR_OOI }-H@L&d2?͝ޘ 3ΞsUď6ҏ,>P\nRgߖJ25 QrT~}}}-i| 5 Oe'0ULy?O!D U#T\Z Z Z Z0L+{,9TQT ZZU3}4}5A&6%jƿؿb/&!3~ƧD"?4?bF?Q~?!t2DGf?7П-Χk.Y?e-%1-(gp { A. o1->i\?#O*D)/. ??T?O-B¿z<ߞS=t߆a2Gh$+hasho/"%)m!^-mee+//%/m|F%U2q0??6F#Ug6<<7&/Rӌ.@1 QUU&O?a?s?4/F%G4=d/6 bBV/// q?3?g  |F#1@B9LF"]A1@/?S[af$T.X!cRmuC0¥`hV`=@:IetBrtrbt.2tB1AtDWqWqtT>5Ft U$Q5HtѵLrWhqY(uqZ5qA\BqYPBc\&qdiWrfvqHalA5O-PO\OnOOL%OOTB)=O$O贁Nk]#@:_WrQ_4Q_h)`ooo#D!mÒ9xra/<@Џ܏ ??A?L`;elMൊ1"4FXj|ώϠϲ'0Oϳ DVߊBTfxceFXj| BTf#5GYk}֡1q 0BTfx?Ϯ k/ /!/3,C/U/:OLO/////1#/ ??0?B?T?f?x??????5oO,OOPObOtOOOOooOOU_(_:_qT_f_x________oo,oPobofo1C^gy@oooooïկ );ewGO贁NkՑϿ O͑]x[q$lPQ}E/@ݱsҩ-ߤzGCzi7c:`boX.0Uܜi_>6H:6Lu@Ͼz .͢L#oj̣ϨϺϯk6/`҇ȧ!˓L1Xjgyߋߝֿ߯ҩX ￯KƁGn_cu D0R}&8_ǓZ!&8)6)j[LQ!*7A/}e,ǟٟZlwƿؿDVasϗ',#K5^pߔߦ^+@/0/i_f+|jS-!30 |9b CURdv߾ -?Qcu^p> 9=<e/w/(VO?a?H5E!:Q//.+?Rdv??/1{R0/B!vUX/??////C5T?? Y@ ,1 <N`rUFDf h>$^T E44UIjY?I` ??EL&d2?0R4"11 4bab1$P1%=!? @Abb jBQ,%,'bjS "1 f, , '4[+?-..(+sU167 "?~}:oBZ G5ũ1"f14` Relationships%]`I%bjrbbj6(6qb6 12 >e4K?]?,UH*2u 1I  ( p}H-cuyvWWWW/""*x*//w""""j/"T YY9 #+2AU@jYѸ?@` ?@iF?@Bd_ݶ?P} 44u#`h?1u+&A+HNHn4uL$A-A4>b .m 4b >0zGzK?@#4'<#A3aS@*|RM(rm!s𭀏@"p/9  g/.M%3r! 4(2%86&J8V; 4`0B 4111v+I `?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.l> 0>Udd |5) 4 4awB(}@P4/:T & &"F8Fl wB44DOOQg%^)_I7QQ%UFh_OW52O;!@0$bc 3 *Da4o$c*y4TwAedrr7K0bg"hh"!!2!! #̳dwA$d%,d15^ch$@1 #310 = Tracking Textar7j3 Sv`o @!)p&3! &!` DB Engineer` /CMD=1003]`@`PinXSra31~rgYrhv:xr{ U @.Oldr8MsqzYuAs$rg%aa`Arha%0j5d^ca?1 vEkwq5!NM17Pa|5STa9{"ȇ5 wFab2|=36P ^qP+kyc!t`guard(pntx(par(Sheet.I` !Controls.X\`K,Y)rb+=Oaspyҟʟܟabvfe@Ascb\$m+*og%Qi#wqwq5!5#5j15L1#v@ `Shape _ID tos0BEnter the s| id ofҴthat you wantҰis t pfollow|@b2/`1DV;3b0Haar7%xc` Row3wIdentifyҲ'red'ְpe's cpoinby erqitrnum-bаwinҲs sec1߻ײ 2$6HZlā,((1a(1i qa%9 1jq.'R"A5@U!|5oqaH'w-/ ? d F<{@ #dXQ? w@ddoD+l' V?T${kVa:_/J( U'YC4*EA UIN N[ UOh Xu U[]U^_)`bUegUikVmnUFDf hz$T UU44UFA_ ??F BP(?x Ts4" 47b1$P1{%!?:י @^Bb AP"1.Bb(!"U  4[p?r-R.ؕ.y(U"&7 /}> g`]?#y4T @4pM47)TZ4x*g4 t4 4 2 244mW93 IY`0 = IDEF1X; 20Relational1B21 1793a;?1a]3SOverrides the r4ship not2 specifed by^B3 or8oFs.DJ C3B1x[/OAIA0)Set to hZ@ allbJs v@_ApageEKr2RODF](3IcJtext@12\5Tp?>J^43*IPK, FPand IndCexPI1 5Y__A0'I_APK sectBof EntitieU2`oo83.=mDar@ypeOff@[l2(5_G_f3\=mV@wtic@lin\@seper0ng"^BAVRmso4oot3j=mhorizont2t:}Primary Keygrom^Bo_@r columhpwitWhinZi,4Z5?eR%]H.nUC1HoD8dvL11"j i!1U1`A 5Rr`A 4 ! #) j????HDSSuqob|wwxwwMUse the Relationship connector to define a foreign key between two tables.b?0,? H DB J# ?h T0PYY9 4# AU@A ?P} 4u`uY`bA@ u`u J4>3.T4u7` lu|8AIYXA7 b46@-""",/5G&Q77'`-4Z"4a)%i>0"K? *AeEr r.T$j!4b*{r,$ D6b Z"sb$V5o1)1/m497D5F(2(36)&(2~?D5M0O4Vk6<11-#&4997D5*M6q&7-1Ku@`079D5O44A|;C|A"b"!C@AV?@ cHE$#&GF#BL6#B&`'Bj#OWrC%#W"#AC#0CVVB"V *V VQ@T} U( !m4`!V1L$3` DB Engineer` /CMD=1001y$"`User.ManualEditsaۀ~$`bRITextRefGuid#Ab`bCardoadksb2 /MODwEL={d28/p?@rq?.t6u? 1yu+ar'1HY``A@@b~R7@!#5harVru 0@vsrx8Bwar1u}}(MQC77E@CXD mBa aIk4`Vis_D200.chm!#Հ00I)`?Copyright (c) Հ1 Microsoft Corporation. All s re`ved.ȃD` RelshipsB1l|@8AGC29rq?#?\.?}"#=}l>AUd| r~XT=DJ(&`zu!Hm HB^EA1TIDEF1X Not}D%GTCrow's FootO7asTOalOBH8AaJM` bg@DdaeAȁȁ"+%7-1-1BAARO 1 1 r117@hhGaGa21 2ě11|F!F!1_"`!!5"6!V1i"j!%19Ԋ!:,1(=C@ UnAO (Foreign Key)!7/; 1%Unique identifier used by the add-mӸEtCaێ8BY`BSet to indicaterTis an 'Iy/ing O'ԁUMC25=exactly one, 28=> or mbЉ?9=zeroK30rѺk^ K0w = I; 2࢘=G78 u]SOverrsnQ ?specifIs+,4F7A_[j!- opԛC+O0#1s \R\oy Database | DocumOs _localfth.!shape2#?5 ʢXNXN\1ow free-6m e`=(mb/re maintinmodel.lb>K?Brv p@3A`-bDBPpKJ2/M5`cosuX_#qi/F`"gsso cihd^2Km@Yk}|`!O(޷UuJ JbCáF<??FkTB;@@bAw5 LDrCQ(qc?K@FՓ?$0sEjGb'rBXCT2b'ryA`/Z;--DF;DVfxۏbcbc-?(<5>?Fr!jzrUrcQ!3|@ʙd2q3@='L/ieieOgritГqh`L 2/rUadoOD+ o"?d'{o-UFDfP h> /T6DUI??? ?3)-i- b]b--x$YP-%!y{U @A\^b f!P>M.,.,.'^fo - *, ., .'e!Z %# UHI ;߃UT?|15?666666VW?WWWwww|ww0w'~wwpwwpwpwTww%w/mbȿ?m:mȿ@ t?@贁NSk? ?HD I$?hZ(>TP9 +[U@?uȯ?P} Vh vu`u+` ? b[d dh[>|>=0O贁Nk?<^bzBL5 g#!#!5 I`?Copyright (c) 2001 Microsoft Corporation. All M"s reserved.Ul>0>Udd!! a"% ` ?82C 7> g_6B>-?JC??1y13;??Gy1|???7y12?5rA^BCH-  IWF{--#|Z.!-F d״/De̽do+" o:?/-{oOU2U !"U#$%&'t4 "H$0@Y,b'@ .{{2+}G-ԁx7EU U U!%(U)*+,U-./0U1234U5678U9:pqrst4!"H$0@Y,b'@ /r2oG-$xf3WE/t4"H$0@Y,b'@ x3}E-Ԯd7"Et4"H$0@Y,b'@ x E-DOdEUb U U!%U+01;U<=>?U@ABpqt4"H$0@Y,b'@ $"W [RG-_xEU U U!U%)*+U,-./U0123U4567U89:CUpqrst4#"H$0@Y,b'@ WiuG-āxr5EEt4"H$0@Y,b'@ x5 E*-t37;UIJUKLMpt4"H$0@Y,b'@ C6}G-Tx57EUJKULMNpt4"H$0@Y,b'@ C6G-4x7 EULOPQt4"H$0@Y,b'@ }6G-x7+EUJKULMXpt4"H$0@Y,b'@ oU7}G-xo7EUJKULM[pt4"H$0@Y,b'@ Lp7}G-Āx7EUKLM]pt4"H$0@Y,b'@ D}+8}G-ԀxB7 EULPQ^t4"H$0@Y,b'@ }8G-x7 EU U U!U%)*+U,-./U0123U4567U89:_Upqrst4#"H$0@Y,b'@ di8uG-$xk9WE `t4"H$0@Y,b'@ 9}E-x7 ;ULMbt4"H$0@Y,b'@ ܀:}G-d,7 "EUKLMept4"H$0@Y,b'@ d}x:=G-x7EUKLMgpt4"H$0@Y,b'@ }:}G-Tx7 EUKLMipt4"H$0@Y,b'@ }E;}G-tx\7 EUKLMkpt4"H$0@Y,b'@ };}G-x7 EUKLMmpt4"H$0@Y,b'@ !}<}G-x*7 EULnpt4"H$0@Y,b'@ z<}G-Tx7E<@.{2KV@,.{l3LV@l,{3GV@$0{4HV@/{4JV@ /{x5KV@/{5HV@0{;6KV@|/{6JV@&{ 7KV@%{u7KV@@~7KV@<@~H8JV@t@~8JV@@~q9LV@@~9JV@A~.:JV@TA~:IV@A~:JV@A~b;JV@A~;JV@4B~0<JV@lB~<KVH<( H<( H<( H< 0H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( H<( <EB~> VEC~> VELC~> VEC~>VEC~? VEC~? VE,D~? VEdD~+? VED~8? VED~E? VE E~R? VEDE~_? VE|E~l? VEE~y? VEE~? VE$F~? VE\F~? VEF~? VEF~? VEG~? VE YS##R #X#N#7L^p$6HZl~(WC.*+ZL+E.NDc! bU/VJD<r0g*@>$av.=f:ຝD7DR(fJC{/ uGHKp";6OŪZLLFkLs/ *ybkDx4ߚz~L/ _eJr~J&pyuxl,rZFJԖVkj/ lxB.w P{@,ߢu~5'Li(epX/ O~֠GY/ &ʀmC 荩߁%$dF9!p#! -R?#S ȈDNGGz?H#naGi?j#ɫC/ĻX?#n]jL7,*S?#o9,H(CO0  $xiM?3IÜҥBOV!F-jO ΖBqlOh%Jo O! -O 1- _@Z-2_!-Z_0@-_B!2-ƪ_!_!-]O/ 5ᘰJ\LA"o</ NBg.zJo.!-rop@/ =o[/ ς1 H8bo^/ NIoa/ rDMk+hTd/ 2O +j:g/ .Oqb0/ -m/ w6Ga p/ #2:1R}Nq2M.A=1B=>1MA=1*}&q]TL^i\Kv/ f\5J_-ʱoaH_hBc|/ MA=1ma MAbm^a҃L HB_>83 y7oA6a}qHFѬ qz}vqZMc!-ځʍ*~R1z~!n-Ƣލʯ21"=0W0261.Arial  &H; &DocTitleLong]X &PageDesc &Date &TimeP/ 7!+/ 51AC_ACO_SECTIONSNAMEX&*)&* RP+#P##Pʏ #x Oahas);aߩIĩios of_ed$i*-=JM_•qvϚϬϛ߭3r߄1WHIDDEN׈_–eɉ$š*̴P͖ȸ3H?mNUMBERP3v SAβ ̜ȸ'&RW)f* +#S@-5=Oņ_L]o,/>/o/U//Ykr/// ORDER_VALUT؝_‚eɞ$‘*__57_ʶU ;o*lQ̝_oo `wLy-? ////v//Q? ?ܲ_xeE$|*bP??)T`4=6 VA?RCHAR2ko1CaXl@goQouoo5oSʲ_{imLo /Ae0B֯ů0B|identif?ied by EID kS_ne"r*̾Pُ돷e/w/$\e//hozo$// 3a>?(?:?zL;'L)JcQ=?WD0-t!3pF9߄ 7OIO[OmO_O\_n_ I_7h3_E_Tf__) UE7()FJ\`T-Wio%$hBHKEf VARCHAR12oʟޒclx1oo8o PU,DGuptyՄ ג ɏ]oLidentified by̿ID2Q 򶢂'Z7()E[J\ET-h`r #$ /EoWo$ߍo(Po:$UFvHЀz"iHZU,ZCy"79z2hzߌz)?U,Cyv61φ!`-wA`ܟ|z~?U,CyHM_x N7wM_ 1DxkHt 2@V%6!8;, AC_ACON#AMK34;%0!2A5;% 1)/-j/G/Y//@////(TQ3RR=u92A? //\?@/ ??@ORO//OiOO//OOOHIDDEN_T?WR>RBWp)BH?wτ__s>Pbm#oHRIo1vF|CyAoo5 )=+????<_OOOOOO ORDER_ꏬ/_a< }* E2.n__z@HI__HZ7^[noXo|ooI2o -é=!);MqG|ѣ5GYd '|)+ -,4FgTcic L  "4;<hasZl ORDER_VALUEis ofhlc (t \"@["x"!-NUMBERPS// &/$9?$(pcu \!Zk??w0] _$/~OOmOOOOOO//),U#9"^?)$L"bR,YVt 1// l$`% VARCHAR2b?(o:oPNb#O<41scSc\?F?oj?|?f?u #9aoo9S UOO&O8Oy_\O'_9__'9sPidentified by<IDF bR'.89"T?)9L"XR,t F__$`f/y/@??*?埝fmY긐"ٟooR0\u 89Wge6me\!ua؟H& zu V uDVecj6%`$# ,& o Co@Uogoyoyԧ*vtY% AC_AROGNAM_/*̚-͚Yk} ߋa>P߀v߻_ߧ+b_t_1$M!í2lS77I&`}HIDDENK.'f9"g"L"ښp-t+ѿȫ OX`{cғݩ0I@Ze)5Aޚ{^aiٛTWd9"û3//y/////? ORDER_Z_&X{RŠ|Rƚe<͉܋T?OQOUO˟eOOs@{yO _3y/ /2/D/?h/3?E?__ ?_o?o ??+o=ocol?~?X삸ɑ"f͞??RO<π`OOOOS'_̐GYKYi_{___o_oo4F{o#]eowoz SECTIOQNk?oWHE[ͳ7nI8gpk}@D1$@v̥KYʏ܏7ݟϥʟϼ1identified bypWIDhߣ (ȒVHN [a {z ܖPDXʦ%O?Q[OmOOOc~ؿLr~-տ{ FXb8%9 )O+d- :#V,UfxJ : )f& VARCHAR2p \>(j!I, AC_AXO_GROUPSONAMEJ I`I` JJLJJ J $/6///x>//+has]/o//-}-is o-^[` sD:8F~5?//OT/?O(OmO)tBOTOzO >VALU?@ist vonO10uE~7F2I~5hFJ _`+,*TEXTR___=$=:_o'I L(~5A$, HoooJ]R99???_?OO^pOMO/<RGR ?D?.3RaF21`9J `_(HNUMBERd2! X9 8o "o4oFoXo`o $, 뼟ΟvImooW<ү('cLF')Q^pmxߜ߮ߝK@]:tidentified by-#><*r=ʲ!3#r=JN`eӄ 2܀=??@?(C f - W?6HR ҍ><)?OW5(UaQV?h?z?aj-O>єfOO O!4l//lӄ(~& VARCHAR2(___R|C_[S"_\5)AC_AXO_SECTIONSNAME/X5!aY5 o //oyoooB|.T=7dtb$WSg[ʲM$$+i;nay(cdovooooǏُv!`r E?HIDDEN_'jbSikʲfl=x/O_dT /_sC,?>?P3[I_Я%iOj?>*,e+=ZhM:OK]oßp,] CtGY`rϘ ORDER_VALUT^(NSiRl=-HdTԸݸP&?J?')VZ> Q C:E¿Կud hasVALUE=is ofD(&:H L 1 ๸øn VARCHAR21E) UiJVQ|i'3 )L+-GR9N; p \,  2///// ?jidentified by/ ?3?IDF? Y6 s: > 3 ߈NUMBERPSzOONK"H_m#M/OOB_ =w__R5-HdlOOO`_ h"[;oMoRZ"C_bYJ-O0 R@  @C#goo AC_AXOGNAMuC!uP/b/t///??X/5GmďֈtYk}w Șw܃p~%1"cC! J. .@WЏtHIDDEQNBzׅyEؘ| "OoXWOrڿ쿌OOC Y@7[_QđuroTAuՅğ֟* ׯ鯁įpߪ߮ ORDER_Qu y|\ H?Ҷ۶c7IOmO]Gώk}Ϸ *));߂_*<6"4ZcuOk1y1|{m[q!xHo|//);M_ //jY|l!U>?P?B `r+O=OrOTOO\nqOOO S?ECTIONbNyȑ|^{m$./@/h5hb/t/__/_/? /;o?(?@m?}\E`9ooB ????._ OOOOyN(Pidentified byMID_Q BM+E,Xr|]9__0J~ȉ%?Q[UgoQomoۍuoo@)o}\+`9=Og/Y0cRʺl}\մɵDeůulK%Q__ӿ o-o`ԯٕ $AC_PHPGACLNAMx_#砠ٕѡٕ(:L^HZ"߸7|$[mߓ߂Cqx.JWǷBju8?[ϰ& 7hOOTf is ofY&Z $ >VALUE &Cg u@H, VARCHAR2s )``i8X' Q ),*$<+VE-hpW`UkA`9# UgP 6/H/I`HT m/X`!u1o> ؐM?_?@c?/ /25 &ACLLǀ5 5 'v!L TOf@OO2jO|OOOKhasOOO UPDATECD_gQQ_w]7x17Ad]O `?p+5NUMBERPS?AoSo}3!z6$?1DoL.ovTfдe/Xwo FF = OO1OCO_gO2_D__@ __,>dGNOo__ Y1ù\O __0 ׯI+?oac??eoo矤o/(P J{=j||-^fxJ\ R?ETURN_GɏTۏY1]O 6H0}٫kϳן1FP J{=⯭2D![xENABLED*Y1\ rO dv͇ȫoE%lo~oc/H=Sat߱QLy=/t/ALLOQWM/'WǠRYEȳR\`&-Ո00B_GRծݮi/?A?e?)cpM?q\y??LyY} -?/c./@/OO/OO._//_,_R_ SECTIOq/U^[//>߰gύooW0Pbe??o?_O.|hwXOjO|OO_O}__j_-^^Pidentif?ied by;M s]I`hR !.“4-}6oHo8 INTH ?̟cZ?l?~3ao +kIU?PF; ɯg9Y !S<ACL_LnѲSoaSԏ(2=ςVPpartly[mYt] ?REPXeAXO_MAPJ{+U-ȿڿ<$6M~ϲhzQ_#5G֫,b0e:&ax(0^gkoF k 89% 'J#-"!J+m /m"m# pn]#n##n G #;M.Tis partly identified bys- SECTION_VALUE/s!,l %)*o 97~%//!+7I^m&% (/ -%6n!)2CMf VARCHAR2 ????7(?[m)0&0f2?/'4/o )/OO59>AU?5*111IOO&N' Oo A}HH_Z_5/dN7CR?d?Nx4X&16NUMBERPS_/oAo36wP?_otm <?ACL_ID=?r"ca  /oL^h _8B 1/r/F/X/~//+has///= TABLENAM#5GS{Y fl A: ܘqyO*Omh ??ӿ5 DATJ20BTe8I`O OOOnO bd_&_8_J_on_9oKoʏ&oo"o6? USERp`+syooLScY "fg= ooHɱ?j|=OaOtv= ֩WM$CONFLICT_PAYLOAD_TGYPEv" {X9?0Xrτگ𫯜ϡTlDsyu&K:GXigGl INDEX_FI'ELDCY @?QcuRdvC;/U5ܠK*TBf7lED_UNDO_CODE_N/񧲠VYWj|ߎߠߏ| ;fx@'9_U+1R#B2 7X`rP$F]ϿNA)D[1Ao+!CCollectionConstraintC^EA)!0J]Lew2?D?w!?[??asx???SQL_SaTaKTOl Hཹ*A-A98p>PCLOOOO_?ed ./@.wNu/Q0DB:+_/WTfv"4FďW@ p%(1LUy>']LINF\AD#T$__ooX6oTfoC}ooҿAS")G>5LOCK_ I.T/دN2@R/iߚԿ濆ߘ߾TD-)W*#xd]rN5rd V37tSU@/n#q6I|7gTDB-  -? K@i{J\ has! TABLE_NAME2is of n&o& A&|%"'%0`h VARCHAR28/0/(B$  h$I%8S,{/U'-&+.n)a&,>[?-?y??????O%8} FOWNER\OTbtoL:@Nx?ޟH/J_\_C/_g//v__///@'OEQ _YWM$LOCK_INFO_TGYPE~V_%&oT'FC[oEHO?R QC'_oL^dpt__uwoz|!?6X ?%'C+}h'2.TUe@d;YxZJeu6Fk[, ^N(=,$9}hgW|y  5J1F[x0WGo+tV)nM<IZk@jloq^`Y[{}BD;=GIqsvxUW#%8:PRDF')?A  QS!46LNwy*,#%/1bd02EG]_*.suACVXnp-C@lgi+T4Oracle 7?db001@Ld;r+0?251SaBTPORTALٯ@3SQ:qVE(0O5JA?QcuSe93Arial  &PageNo,=*/;1TIONS SHDOCBASbd9d;QaCTimeStamp37Date@ last MergepC37ValidatWion37?appingŖϺ&8J\ߖK_HIDDEN_AXO IN (0,1)R1ץgP*+2-:1Q6Ұq0ȭv#J3gP~JCЯĿֿ*;M_q@ "Ic@$c+175<05z _SECTP< Yg82@^QNULL~YE7:6n1vQ7}8lK@? n9|z'I.D;MXGS.Nt&].X&g.b@h+025d:;16GID  &J}hss=P/+VALUEs/T/'0// /??; ORDER_%?:w^=?R;/@s??6A?tKNAM?h?hR?d?3OEO3!#CyO+[tӞG2n4gAC_CHEx`PH_#6_lJgm8@fPPRIMARYZ@SF7AKqrooK P6 oE@ku!QUNIQUE3SGWACL_q/w(0ĭO QU6 B%28h4Ocuχ@̱̳ϺϵW?v(`2p7ODP"VSh CCMappingGroupݜAs-ڹ߽_dhDV /Ÿew.@( PHPGACL SHDOCBASE CRolePath%~=m EL f  * CAggregate<[*CMappingNodeArray *^Inst* List Zj@ F NAME:~<XvQc@uww >Z+VALU //) CLogicalKey Z, &AC_PRIMARY_J$#:~ l/( GLPH1r 1BTPORTAL01?#@42`Y2 3 .O&O8OJO11sOO???G@@$c 9GROUPS_AXO_MAP\  4?XANULL[9%/QK/eT Q~cS}WPo'ofR_IDW_xl_~__oVMaMcRo=vQm-( Forewign |%U.,4? I4%WG W?i?{40q???GOO?Mŏ׏_O P@btOOOZAP3_8ooo2)BT̑]cT5So-o}e}ïկc}(c}5G c}nrou!ضo̟/'¡£#ؿ SECTION_/:9@K]-%J/H0 -44'O ORDERϰ-mm)Fܝ߯~,+v?HIDDEN*E C?f*!0CHEC=J1H!]hhi {4 >$6s*<~y 9|VhzZ!2C)LEXҕLQНC VARCHARk 1I@X\Pp^!gLQf] 'hzNr^)Q/(૧)/? )(?:? E)a?s? ĩ)Q?? ͩ)?H?˿p~`{ٟV;GETBLOBE/?/Q*!h.b=OKtѨsOZ INMPTEGEROOS('1'3h.dO[ALLOWZ`G2N4K_R_RMQ(0,1)qo~ֲ6__; Y_k_}_W`1`3h.f_k?ENABLE_H_Sfded$g>0br~ּ@oe; `xo oT13h.ho{? RETUR_Ph; s V13h.joNOT0lUogoFXS A Ch.l> UPDAT"pf`K]L\nďuEGx'1 2Б>PbDVhJ¯ԯޯ M@_qq _TSt ܼz pOy A/x$Ӏ)-4۩)EO{/E)ϫ ;)Q4) $4bB$DVߤ0O^}TO)YۿH#*<$K]weJُ .; K΁b? ORDERxeJ  R! Hh$%e!39,1r//oO%D2$*55C COOOO' 1RA%_7_zOOO|W<5$c9EGACO_MAPU SHDOCBASEJ R?$ ^QNULL ITf13|I@4R )A~aQgdd &j}bE[iVoodei`ot?𕬥ID\_Ƚo"mCDoVha]s|\ t:f8$ OXf*q׆A2U9 4Y4m?ܱ|O6OHOZOҟN_`_OO,>Pb_ _z___]QPF+_Mu M(Ыp"oi Ï{d i'mdiij0 1f*f}b%i QcϤd/i*(ϜϤd9i4 dCi>OufZoPP|f8 &n ߻|t0_8$6ﺳ| ORDERtF:t>Pb|M NAM += 44ʾRt\3w21~4DCHECK[7O!\3 IN (0,1)w8T#r1O9{>4j4ğX/ʯ ^/////x/?a/s//ZiQ?Sw:6HX~`Ut+p OYcnUA{dki1mdsiYC:߸0E}ixOHnˇiQOOdi_1_diX_j_YҚ}L~?pK(O:OLFACB_N_qkR_K,o>oKGQS_Nvo){Lo ooo oPBWQWS_N.{trSmhooOCQS_N );MS]8qǍ/-YC$DS)R@Սeu2!PRIMARYPn"K''Kq39/3"x5KC6%_!.}e`@9QUNIQUETsn%ş,ڟ"4FX |.w{_keFK@5swY3 ϿRkueΖ4ӯ寒 -?j t貽nH@:]oρ|ߥϷ|^ mΤtlVMAPſT<mu^jpeҖ@{Ce\]ACLG_ID;e[Ap}J@9m?x/$(KK./g߻4?F?+  1(CU \\2n@${cnARO_GROU?PS_MAP  SHDOCBwASE zn%^0Pn?NULLf ғnIH]YJm : ԥ ))$/-/JJHօ&ACL_ID  X*]HYr /S!S#ԅ/A6.Fm[Y^y<Y̮!ac ng`{' Dm1rE1vBTPO?RTAL01/COOOOOOhO _LO^OpO |@  X9KW?Hh///o!|@9a-?" TQ$[g슿@#U8y<Kۋ2y91l,P`@4#G8`?4p OO/O#_5_wOOHߋ@ϯO_a_s_knЁ$ۯ_Kvx__rMpnЃ"Am#uio!z.X? Y_2DVzE(h?1v9>4я 4h/4PM_qew߹Xj|%ߘ+ߵ߅ZPnT!t^vXp/4:!Sd[iP!koasi#̳kkvQA?S8kiz??`tDŽi³??`tEфi??`tۄia%O7O86:ň/ȼ/-@p/(y1y3.F9_KR SECTION_W7O(RO__"1@3/G_kV6Ȉ_xÜ__#13^ok OORDERo_6?oƝ_goyoo"$A$C^ ov{ۯ7o Tofo5 G#]A]C^ {-HIDDENwm24AC_CHEGCK_SS IN (0,1)a@umX t,9P=ՋUѵ  A(~2D؟DV //./@-O- hTS@pת_/q(L@Z@O/HdT-~4QPD%ӡIDYIcO4DIcɹQ^ֿ踗;mɹh!IDwɹrHZIDɹ|ϓςOtOyd UgyÈ2&osn!YkxGGBRoyx@6$6ÈWR 5È!R 24AC_CHECK_HIDDEN_ARO_SECTIONS( IN (0,1) &[u PRIMARY. 4 q BTPORTAL01>@Koe \o#UNIQUE_VAL =70( /L^pW (mFK@2#l!d .% Ho#,2^*\71r/P/?6%!427$g%Z%3 V=OO&O8O1bOtO???G!>%$ǩcN)5-/MAPU SHD?OCBASE u8#?ANULLJ9|f_ -2Q#/@8Q%Bf1Q7WT /%SREAY<_oTKYF/oAo/1eACL_ID"B8_M__]>_Y.acR^/f3f *:oLo!F? Vga=O5f~/ 2a9-*.\p]A#c.R(, ]Ck 0W78  вI )!!VՏٞYJڿ  (1?L2/@D/V/h&Yk-'+07);6_q . *6̜&0GROUPS\5I<򿾿*pؿwP* ;(Ϲ޿  :.E%I[mh& *:LFT_RGTE;1ZJo߁߀߆| ARENT&JN  XH؉ϱơuIڇA lɚdxdӇ䵨39 1j=HRͷX?Hxv?fx5OO?;NO>PbPOOOJRVPT,_2R-<]`o__?Tp!!_RY!]DQ-52moGGo9 ??o5Io}/j// ?bO-613R@>GvAKFT:LDm_ElMqn`ƭsQR4 ?Q$_/*/l_ oo1oCoMoyo___D/V/Th/z*R V xԬ/,B+_yGOYOkOvtO!RqOP4X9q*h=4X7t?-Q?y*62y=O4Eyv4y4J1E?SFOF`QR0R_r<$<~UO u䈟 ӟsuu~Wt 1DYk}s~Yït$XQN_0Bv~[y)NAM40!MKBy $ ~].ϤPOTQ ޜ O^_!@ߧ6is Cof  )%lGR~Vd»++  W@[{qK^c_ueKiUQ`a?Sr_!C6K]ob ) MOc  AC_VALUE_AXO_GROUPS Ph)FK5P_ID1_MAY  E WFM)PRIMARQYs?F\H9]a ACLp -[ż'j9  CLf  jq BTPORTAL01#/@K{ pT" T!rENABLERh //0/B/T/f/ n.)SECOTION+////? ??-??? w.(UPDAT QA 1l1Jis ofNpOH PWYZ   C0^-.:gSV+ OG]E:?ʠ???:o?? OFG_YSe_hQUNIQ/oY:=_Hoo)oMo_oqoo q+ )HIDDENT'_Y=3QXx_eOOa0^# v7@EO-y&@ 9QGQ/`{^K`5t"_K ҁhׇc )tzpk  xsFX{ʟWMS+YSЃS׉RV<Ѓ8׉7e@q;ПHC(ɯPK+v NZ#Ѓ+׉*N4[QJЃ׉翠K(9K(#rτdc Y] pA?=WU-KI[YWUS+)'%!!531  }ljfdbՀ~|zxv?+twbeg Oas'*DSN=db001;DBQIA=W;APA=T;EXC=F;FENcQTOcFRC=10;FDL{wLOBcRSTcGDEiRLiBAM=IfAllSuccessful;MTSiMDIiCSRiWhPFzTLO=0#5GYk} 1CUgy //-/?/Q/c/u////////?`?)?T.lBarP `TEMq??RINDXx??USER.??Q O,z t`(5J p9,6DN[@Ped ft VARCHAR2OOOs aSCw$qqb&Q bw:s07R:Nsqt8QOTh<jENUMBERP?__5tMappingpCpWDate of last MergeUW jValidationUW k_oo)o;oMo_oqoooooooaXIQUE_VALpACL_SECTIONSTy rX)p SHDOCBAS.rF&YG2(hD[qR QQqA?srp-QOh+'T4:0GP,4rwTUDGPGP,TNP4 4RAGPGP GPGP GPuqGPȤGPGPGPBGPaLq48GP>A%ML' s_;MǝT1.2iMicrosof0pisioXqCDBTableL4ѡJ4(.?6.Q ؃A_u)QL2\@pC[ X_jWN'AME{_(BQ_OOOO __-_?_Q_c_u____ο^r.r4 pCHECK_uzE]rr5ppPRIMARYz3ID:{L2pA $YQK=VA[dJ[C@UN~Ϣ}x.5GYkJϔsrsS .ٜ G rV@r9-srEgٕ<7pS %2.6 @${c2 I SHDOCBASEAC_ACO3zJ&Ȍ INUOLLID6 :JfqMBERPS6H "X ]M j SECTION_VALUE(Nt VARCHAR"_HOlo~oOOC&& CHECK_(cqrD& ePRIMARYvID{(rTP/X_Fkq+DBTPORTAL01oJKPZ 2.IGUK_Fw3"^!rbϏp gIv'dUgwQ2sFUFyVS4ICJQfFe -`CDBTablesۜ(Gjr؏e"4ߑ_ͯ߯.E/r@.@RdvL  ColumnG B2, 0hHNTpTUTo8{ ωOϭOK=< o"_8 VYI_ϐߢߑ_ߨ_ĩP^o/ALLsOWGߔ |CIS>Wiϙoo͕ٳdeahfL4Nijh.$ENABLE 1sJLo@hhqf0>qճNĽIt^O RETUR@x SE_QB OUTm!3@ߨߖܼƆQL_OTSETOBX)/;/F_/jφaUPDATk!@ahz,?>? hϳ: ٳnstraint.@uLPbv(rJ\Oޏ:qf6? GO+O=OOO;_sOOO\q79tO GOH__*_"oN_`_r_a ):*_ GN^_D_L 5E:0y ;8 o|>Pb BTj|m=ݞ/HJqa)Zꠓ ??Q?c?u??5f0;^g /߹d/ o/~; ORDER](ppyʻYk_qwωϛϭ18Jϋh} ߂O_MAP;p/j#8Ð"4F0L_Maȳу-? ˿ݿ֟UR К]mJ[%Bn?Qʯܯ:\gp0 VALUE 0qf VARCHAR12Vh|' ( AC_PRIMARY_ACO_MAP ACL_ID SECTION_ SHDOCBAS#*TFK P SID\,WANsSVZPUEvO **Hlx% rBTPORTAL01s/D")#////&!'?9?|///~7\1&@$c)R5})S %u#!&&||%&NULLS2~ A(NU?MBERPSF?OO\1#!Lt1?K#GQWO:__pP~DO'ȂK^Q0!_3Y? ORDERE_(]_fOooOPOOO Do_+o3YNAMToH'qpor_____:D0o*o2ZHIDDEN]h/xoK>oocO  ~CHECK_b^:J)  5P=}%1$AZOq4CM/_/S65K9%!I -UNIQew'ǟܟ$6HZ7`j:&#/P8/J/ P?b?//nπϤ?"?/A|????/vRO\mDO!O3O  'oڏo DߵOzߊ-?DP6/_ަ 6HRdv@uDɭp>8oJo\lFX=Oasn:DӶL #%N/@`/[mnDا( ;PbӸ(/2V?h?Pz".ROJW͢q~ROrԥ;͢*ӯϩO0BTZKA4F씨OYXC-_OtOOO _OOOf r4HMNZrQu|FB]3I눲R26mrʿ߿_ K]hz);#5GYߵa~aGROUPS\u/pg03?E?(h??Ԟ*X񽤋/ PAROENT_ُح'9>PbtbOQ<LFTx/AL^(pW cļίRG 4FQcu*@Ȥ /Ը=/?Qs////_K EbI(Ksl=O;fGEb0r___om___h_s03#8J\VhWaߗ G~ rV BTPORTAL01U %o2g ~*6 ^p`>@$c 4 SHDOCBASEAC_ARO_GROUPS_MAP\!&ؼr@NULLAACL_cID  fMBERCPS(m//4&>- !+H܈ -!/!?/Q/r??////*R>-PRIMARY%+6Pv2]FGFK_#O% }AL@VO`kIxC,5ON+6! O^E0,>_2D_ oo.odovo__^p 9/($CG??/).0qO?a?????8$C1C}] SECTION_VAgLUElH VARCHARa͏KqKqS_ck@.B:$M*p?$W[w|Sȟڟ)L?4IL@jetsNOV2ckIxNvO+_5fkIxCSV{ׯl;@UEROGTV.JVRT_hi_{__꿁oo__DVhzAoSoϭooootS[?$(HL^t)Z$em0Ѕbt߷#d$ow붱0Yy ORDER{P +=O*n$yx YyNAM*%7I[8x$X{HIDDEN#/5/FXjŀE +@CHECK_K+!@4ILPHJFP]26kAqʿܿ? K!%9!ư!UNIQ2/ ?5?:/?pD?0Y?k?}?O???6$/?'?93<FÿO_/__ ooϛSoeo__+=XO+@;Vߞq//5PZ/l/kw " Qcu8#o:L^p`&*h>şϏw*A**܀Gïկ맮3-`?[} .ȿڿ՟ uCH1H1V_H/ ~/+Z(.XO%`/JB)F)XOd"?46gJBChDH)nNULL"o#ID=!f$ MBERCPSV//b&l 3!4)H+)0))  PARENT_H/@1e/w/??////4?K?/?:*gLFTX?B(Lp??OO????*u4OPO+OL=OOKRG_OA2Q{OO__OOOO4_R+|G_<(NAMEh_BhS_ VARCHARxoo-!-!b"ntc.)/l| PRIMAcRYIDJ s:rBp6< @XH qB#]ocKC:DLq]@_ePW[CePuwEh[ŏ%MqP5;ivN7^s)3@_HZ`r*/ooo N(I3 |Io>=e6! //5?G? Q/c/u//N2Yk@y+i #Tт:rrC; GuCSVDO?UE`BXOO BmOQW8W_BT____o/or__,>PbS;An/8fĐv rp/a?dv<N`rNu@IKo؍k///>!?j@|W?i?{??*JIĝUxk0 ORDERJS}PNTI_ NAM>3Z\@ȏN^Ii ?HIDDENG8_i2opßՕVe*P5 CHECK_LjW4F`h?k6ܽemgCOðBHCA;"[qs7_I_=fzeKiUQ<3PUNIQ@GwωHO߄ʜp 2D־!kJϦé6Vn _"_4_:oLo__Xj|_ o 2 z@$cO ! SHDOCBASEAC_GROUPS_ARO_MAP qw{&ȩ #NULL`_IDn  HԘfHMBERPS0 @Epvt~pD g!/3/8J\npp ]P?RIMARY_ \AR"x']FKfID/_ ]gM ?{?_3?E> ^3_d? 5la"b0r~S!BTPORTAL01c/QO AAOOOOO!% __^OpO);M_Xi{HXlQ/`oroF/PX/j/|/___P oh:oLok}oooo}/$/+S_*+r/"~?R8 SHT1+r]2Xc?Yg=;P?u6OO(O:O._@_OO(O_^p@şZ_l_~__T HPGACL_ǫb xNAME08HQ VARCHARqqs㟍Գyu`+|= iVALU_2{Ϲÿտ!E  ZJtCDBAggregateWMSYSWM$LOCK_INFO_TYPE{} Inst TABLE?_OWNERiׁ€KϠu(@ơ{! f+`فW>P@})WۨlX)8.d@{E{wq,яܚy+9ED_UNDO_COD[ߨ#AINDEXXcl N}INTEG%z /rqy IFGIELIpe/// //./ HQ?SQL_ST&kd8>VuCLO!B/???/?ed?P+8TOOOgqaCONFLICT_PAYLOADci USERST3ATk@P_b_}TK]W hqHTIME PM2WD@ct@on@rCy_ovp( oSo .Zwooo6ooooo!oEWi{2vm-_ D T!3E]>_!?GpaA_WORKSPAcCEA_o0?_l3_(_7 xY hHݏ՟矀u__I_KEYd”lğ ۤ$৮ ?OnO` %7BPni~ЫϿV@sDxEPg`SePQdTObjM@pa8PA:дwS%MPdl";,:hկ?'9uIWD.]oPumnP\ 5b p:ORIPfPߑHܬh9<죪3  TUUUU !U"#$%U&'()U*+,-U./01U2345U6789젥:;=>?@ABJCDAFUGHIKRfaLJ@*fafa*M/W/m FoldersQ6 S8Zua}m{enfyUsugiUomytoD>39\DBSaphtDvicEayT)6? VsEreudObWjMDa:K/%Ml"-,5/-կ'O"I ]WD.ouu}? 7$ ?($OR I??$?<  R1UUUU U!"#$U%&'(U)*+,U-./0U1234U56789:;1=>?@ABCDEFGHI*K0LT9 8J6Q"]6QP#\p____@___0%g! o1A(:120@*g$/pTL@/2h@!"#$c$&'()*+"@./01234567Tc:;<=@ABCPFGH"JKLMNo2PQRSTUo" XYZ[T]^_`(!bcdefghijklm~1pqr 1uvJwxzU{|}~EQT6!i2Qai1*o__jT//(/:/L/^/p////////??$?6?H?Z?l?~??????? 2ODOVO`*o_OOOOADOO.__Qd_v___>ϟ]+h#D,BTPORTAL0ܹpOh?+':,6뱞qѮq qJqfNa~aa"a&q*@.q.Nq2q6Cx:nq>A%办L@ s5G"f1.2e.Microsoft VisiяO$  -?QcuχϙϽ);M_q߃ߕߧ߹%7I[m!3EWi{̇@}QE F}?Rci{ewkpp`EUse to add a tablyour Entity Relationship diagram.s?chema.bпrq??p= ףп|j??RQ迋Fy55?*?UG DF # h><^T$E7#!U@p= ף?@|j?@U1>?@c^q?PoC4u7{` ?u#?4Ap Ip<7@O].@%47;=radL ]4aa4U40z?Gz([@| hb\:"bB)@+u@+@+bB)E&8+4r!6= O @ a !I,x<1?2Zf4 %c9B)&3&&!` DB Engineer` /CMD=1001[yU02<22 /MOD7EL=4u 4?`0@c1DA13x9CVis_D200.chm!#28127I4`?Copyright (c) W@1 Microsoft Corporation. All sBs reserved.e7]-/#!!HF(D Sg4`P];_+S4TAJAJAgg"!!"!!(2!! 0TA1T QQ!`U,R-Q:!^0gQ?A -Qk@EntityQ7 1g$@b9#b 7hC-Ide)`fies the shape's vwers@ tosbadd-@.c%a jKcdk@0 = IDEF1X, 2`Rel>Bal. Z#f6g5 bQ%2Tah#hCSet for depenja tables@-Qw(5ptLcbly se@ta<^tingdRHhPA.@7Yrdd yaGsayour0*afship d?iagramdR_Ɯ1 Ĕ`FAdd,`,g,΅,objectU,,Hr,(c,,ER,c.cRlTɟ`10.:@jfZ%z9gB 3#4 3(Q04%]nXO{ %EvՑ4E+A0X(S"`0`ΆPrope;rtp`...1?:7{ǽ9+*JA`ShpbpTIr"2401g5O1]O1iƯد.rt;+P'0rq"4%-Q]~#N5ƣmОHD: )# =h4>TE7#A 4U@@rq?@U1>?@vKPA4bA$~4uL`5A-.,-4t7` M4t#DuVUNA>5L~U;40zGz?<7N b!& ] b)X&ᆉ͡;??34p    b#d'$g 4`0'q7 164IP`?Copyright (c) 2001 Microsoft Corporation. All [2s reser?ved.!0Ol>0>Udd"5=j !-0 94uA AB^LHy c ywB4`7OIG?UOQCAt%NOOWA%EOIO!_A"%2'O;jrA zS4QaTableg8dao' +4T#H74"//%n7>= 495a 0g0Edit R Name..0 `@i#~'0 `UH TuD R # ThY8JT YY#AM 7Q4U@@rq?@U?1>?PM4bA4u#`4t7` $4uLCM-Q.SeUMuJG2q??tRIU40WU;H&=̽0z/Gz7UO>p  "   m#q!?!J[++b)b)(6>G!U1MpP17$gNUD`0'Rq714Iq`?Copyright (c) 2001 Microsoft Corporation. All 2s reser?ved.!0lJ0JUppQ5I7r  !(;@ 4u/tCBuHT0#B"40OG?OCQe%^8_J_WQD5U_O[p52OGarM `^9c4 jjMjD5nI2 #oo!3bKA f<@T4"kyoToQ'i7 T7 : vv i#/'k0Y ?AQ#=OD #e%rIIG!P 3r IT.Y!TrR 7 @_aV7[!|Wt>BU"4FXQh!_Ɏz_ Y0BJ)T&^[#0 -G!r>##y_p_S]qrB]`@r!@(0Ӛ}៾ Sۯ #5gZdPpm߿Cy4T]aG!G!Q!Q!7<*e"[![!D2p22QQ2!!]axx!{lT@?21 SvʠI8`width of this shape's text7=HZ%3he2džx2e%R t @a43*Sub~ ID wiPnOe Entity group2@D5r56E bb E@[b #xCM 7G C39specifienumberLcolumns tablprimary keyp5a! a=CWv`HSet to hidhorizontal line sepeAngPKfrom*@tԛ5c KA0& 1st vertic$&Կ5bt 2ndvƒɱ.Ind2t'2@Cuكdx3rQ#('l Fo)cӞ n$Qt(')zEW{E75a') }0`EdiA T.2@ M!@%[VYd3 Shoow LA`U,@.DBHPKS-oYr!0~P`&(Tab!1cI'S/ne2iqI"v#822J h&rRA[I" "_Hu"/ rWjrD{ ^FN~#j? iy:Ka/I%  tg`]]?M3y4T 4t47)T4*4 4)1 4Z2444qW3 M$`0 = IDEF1X; 2;@Relational121( A73aK1a]3CSOverrides the rKDship notMB specifed byB>C 3orIHFs.NZC3aBA[}OIQ6@)Set to{ h@ allJs @ApageEfK2V_Ff]Y6@;YJtex!t@A2\Tt?Je^43C*;YPK, F)`and Index/`IAZ5Y__`A6@';YAPK secBof E?ntitieDe2`Uogo83C.vmDa@ypef!f@l2__f3C\m@tic@lin@seperM@ngBA;f/b}s44Ft3Cjmhorizontt}Primary KeywromBo@r columpwithini,4ZeK5]x tRU1H!o8ďTcU"A"A" !"A)3` JESR `)2 k ! #) j????HDSSuqob|wwxwwVUse to define a foreign key between two tables in your Entity Relationship diagram.b?0,? H DB J# ?h T0PYY9 4# AU@? VP} 4u`uY`bA@v u`u 4>h3.T4u7k` lu|8A$YA7 bT46-""",/5G&7(7'`-4"4a)%>0"? *AeErn r.$j!4b*r=,$ D6~b "-sb$5+o1)1/m497}D5F(2(36&(2~?D5M0O4k6<11-P#&4997D5*M6q&7-1u@`079D5EO44A>;CA"b"!C& ?$#&GF#BL6#B&`'Bj#OW rC%#WH"#AC#0CVQVB"V V VQT } U( i!m4`!V1L$3` DB Engineer` /CMD=10_01y$"`User.ManualEditsa~$`bRITextRefGusid#Ab`bCardoadksb2 /MODE;L={d28/p?@r_q?.t6u? 1yuar'1HY`s`A@@bR?7Q@!#5harru 0@vsrx8Bwaru}}(QC77VE@CXDq mBaak4`Vis_D200.chm!#28129I)`?Copyright (c) Հ1 Microsoft Corporation. All s re`ved.ȃD` RelshipsB1l|@8ASGC29rq?#?\.?}E#=}l>AUd| r~X=D(O>ayu!HmHB^EA1TIDEF1X Not}D%GTCrow's FootO7asTOalOBH8AaJM?` bg"!da$c/BTDȁȁ"+%7-1-1BAARO 1 1 r117@hhGaGa21 2ě11|F!F!1_"`!!5"6!V1i"j!%19Ԋ!:1wDŸ!8A*8A(=C@ UnAO (Foreign Key)!F7Vb 1%Unique identifier used by the add-ӸEtCaێ8BY`BSet to indicater{is an 'Iy/ing O'ԁUMC25=exactly one, 28=> or mа?9=zeroK30rFѺk r0w = I; 2"𢘷!=G7_F u]SOverrsCnQ ?specifIs+,AS[m7A_j!-4FopԛCRO0#1s \Ro Database | Docum?Os _localfthUHshape2#?\ ?AOOA=Ur@cQ!3|@B+dAgYqk,30øɰ'LVrui egrwL z,2R|?+%7I0d{@f@3J3'Ƴ}cardi8 UJ2J2AyftiA1fuqrCUo'2rq?R1 qf5uᵅrRFFnr/o.5cthcq@` SE6u` D1B?@sUg=Bj  kt,7@73@6< 3n|P^pҩ/I&r<3!3q!CA`ݣI!`ۅ Properties...0Ѝ` DBsg! er` /CMD=1001y@Z°°Q[۩([hs9*m!1`Ї ~&3R=a"` 2Ompridewaw"`r.db/Ebvv#4!r?,0 r"15ש .haa`@I/[/m///!/B"//Qձ( /s:+5!`U1jpZ13?B@I?[1:a?s85H1/ U뺑]й_NvFV~#V? $ bca|doOD+< o"?\T~oPUFDfP 7h> /T7 +UF~@xT  ;7AU@~?@FM&d2?Fﺼ?P} 44u7` ?F/u:[ 4bir rL A  > 7A|AE$''S('F"('>Ey/>*u8@Q(-DT!? L60@"/ 7L#06L00?F;:4]u`u!" d} b 퓩0"44Uj";@%3AD;I`?Copyright (c) 2001 Microsoft Corporation. All +Bs reserved.4`Vis_PE.chm!#275120;j%?l 0l> 0>UddAA20 ^4ag(`3F5V?'?(ZRF"w R 4@/T&?_7%?XTQ5^%__gQEU6VoQ_)oQ|2/_; 6rDbc@i4A^E9 G3?'o2q/p?=A.vq(@^i pA_ @Abo!o6:qj%{&?yo;5"r71Ko0Feoe#q5Н&ijsbBHaȇJ:xBTg@da74T7++j"Tg(ēAg tiCjRectangle,Drag,ad?d,connL@,points,text,stays,level,rotatedwing,Tool,graphics,architural,dQs7cmT;$@ʁ ]Ehah ©H'1/  F|Z~X,#sU16X ?:B8u% "eZx %#UGBu +?  w^ qs_BI]wwwx?`Graphic text box into wh you can type a titlnd date. Dragselection h# resize.b@ @BP(P 2kӿfo?L,a@ޱNP?j6 b46?ĠUG D # Qh8T PUU4U;7U@?j6@@ b@Fx(_>; ,{؉d(*<D/(!/3/nW"rv"|#]u7`d#u%?4 V4b%& !4UF?? K) ")"X6)!"/@ P92a" d=)4#"L@#=ABD K4`Vis_SE.chm!#26880I]2`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved.I c#Hx ~YH@L&d2?1]BX\.ϗTX2^sUzQxV_F` L%Ae!$ZC!UX"UH"ax.d 9g*h9cgUP`]ovR4%TJd"Kd7H%@b.BMdBdPB!!R(W`d@EA (MI[m*bs` 0rbdraa7&Aq77s?-s 4ӏ3D5GYk}?ra?bz.E!c&-sYavPcrad? %b ֌Q b+%zE9Ҝ.r4tQ$ t`3(qEc=AE 5D`xTitle,block,retro,Graphic,text,box,into,type,date,Drag,selec>A,hand㠴@ize,Borders,s,backgroun}d&@ s AR!uW{=&@ t1ߒ՞7}#7#%* Bd 2W'Gџbp &@2]2bWnR7N](E| (7GEExd C`&R) to fit sWidth` Sc@ch.X2=~&@`HeAUY#&!B&sZB{ňM~4TBYY9 ;UFwa?F BP(?Fkw?Q} Ut` bWA@it >K(( '@<;D^O^;D|O|;`YDOu V-@p Kb1O1J\1hz11u .?k$}?%H3(&5  >-U0zG/z?@#"!4##<?4Oq#o1t4I `?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.#` 2#0@%=B2SUl>0>Udd-A-AJY1#@ aB@J C(1xk y#Vu`?B)@4Aܧ H?F Fͺ?FZ-?FȌ?P_9@ (&Ū'@KҔSBLڱFEy#TvըE_%q@:t" W,I? R@ }|\@ 4Cޫ?,y"4(B"__Wr #C5Q`5U}I>?F}c{R?FR1=_O\L(={\Ryԑ\?'_oo^r_on'#/h5UbS @O!?F$qήx?FQoߕ?PfQ %owO\0 M0L`z^{\\ݑ\RWg\Fߑ? uH:V#t!gE#/h|UMF >*C@GқO\]OJ~Y?0 vT4 as^RInc#/dHD: )# =h,>TYY9 7;7UFx(I(7 '@<;D^O^;D|O|;YDlOu V-@p KbB1O1J\1hz11u #գI'J?%3b*&  >-U0zGz%?@7#"!4##<?4Bq#o1t4I `?Copyright (c) 2001 Microsoft Corporation. All 2s reservoed.#` )2#0@l%=B2S(@l*>0>Udd@-A-AY1#@ aRB@J C(1F47? y#pBkq?4AמVm^?FRk?F}XyI?FRw Z?PfQ >ow@K|PI, %8eUy#Ѯ2reU\_VTP:t" \Fߑ? ʢuH@ }|\@ 0Cޫ?y"4B(B"__:Wr #5QEUXyX#!PFűIP!Pލ5=_O\v'Wd]LTs=dVTjTE,_oo^r_on'#/h5UW^}?F d?F yk ?P_9@ 6&Ū'O\u ?Ce\Mq\{\ar\`CgW,I? :V#t!gE#/h|UNF`u?F_'ԛO\OK~Y?x7êvT?8N~qd,[RInc#/d#rQacd7KcDKH D: )# =h4>TYY9 R4tAUFx#U0zGz?I@"+4#S(j(P:"A2G4#B$/&+#?\.+?#RYq4#rC1H4I`?Copyright (c) 2001 Microsoft Corporation. All m2s reserved.#U`$ 424#0@+%="SW@l>70>UddP77  j$Da`((]I&I&#BB^(oEOhOB%NOIAA%oN_O2W45oOOh_RiO&$aU9 !$I'2U&bB"009DJJb ; b|=jK`Of!3B'a+%)4m0&c-d7oIo[omohrNZB `@JZB=u`@"3#!"4Xv5#!R6!#9bjAha%9 9dj'/v!3rAv)C1=au+?#1kdm &18#aTitle f4@RB!|(GV@!z%PCz,VA#qa#q+%VQTdwBXK~CE([bIY_HZu1/ ݈͞d"Am_D'F]~6#D ?: !gad"D+Uk[? -?QcuЇG[~bּ//&/8/J/_~)Pa~?-k"/@/////Lc F2$}"UFDfP 7h(TPUU4U?Y,b'@?#H$ @FxsUr1p6 ?:8 % '"e Zx '%#UG2u <>w???  ( :>B{M h83<(i`Graphic text box into wh you can type a titlnd date. Dragselection h# resize.b@ jZ[?? Y,bw4>=v UG D # Qh8T PUU4U;7U@Y,b@@>=v@Fx&@ AW!(7=&@L !t!-?QWyj՞7#7#%a*8br?.Y@2]2b6H3W'5p7_{%(*FY=A 4 #A&Ab$b Y a)&"`Movo c̠geature ա@5C7nR}(7GEEd YC`&RԢtfit l Width` Sc@ch.X2{~&@`HeAGYS#&¸!B|!kOHaAj˯,#dhúH D: T T=h4> TYY9 E4 tAUFx Fu5ub  >V0zGz?@*#"#!D+4#(4# 9 Ab!`B$/6+#?\.W?R=37:2@<@<@7"+bx4#!%q4#14*I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.Vx4`$ 42$%@@+%="SW@)l>70>Udd77  t$DaA((]I&I&94DI4Bh(EOhOR%^-_?Y;QQ%Nl_OWO&__biO&$a(e9 Ŕ I80'2UbB"M@9DJJb ; b|j`f!##!z4G5!?bI3fI2'B) @B2u@"U3f4f5f6u}#9b_a+%-I~b0cԋcAroooooo~v4s1CUgysjAha%9 -Ia@i?!3 ccA1B=}a;?M?_8a ?E/}"6A'" R, 6 < B$ kTfx$\##!83aTitle f4 B!|(~V@!z%Cz,Vuaq+);aTd8B8hVF_&@nC8H?_1/ 6L_ajIH>XMFd~p7#9? 6 ]ga d:D+ykA?Gc~X>*<N@`r?!APUFDf" h7 T77 sUF~@ǿxTYY9 #uAU@bX?@z^?FM&wd2ټ?FKP~A4j4r4rLeBYAYI >Hh4u#` RQļ/>HHhYQzudԍ ]b&? b>74U$"L/@#!4-#.?@zy?zA'D##(Y#=Lz+Z"ĕL =-#u1z43+4`Vis_PE.chm!#26204I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserv?ed.o7 30Wl>0>Udd#A#AYD"1  ^4a!(4-# @F14'Az'?HrC#Od7]IKD"#B5DF+XMB] |OOOi4AA^59 $%y#-/'2U4b?=8 ^Textg@#Ada{oDGE4TY//$"7f2EaEf|!(9c?}1 XB4t`tR*ta7,q`"V XA4/`uA-L r +??=L u);rf5/IzW~Lb9t5cu1Yrn3`x12pt,ta`,Drag,add,block,font,Apply,line,style,border,Annot2s,su0yi{ngual,Ses,definiWheadcsO,red?c;r|v$@Yq8)e^|r9D9ZUr]|Ahah H';1/e  1Fi~wFf#`QG? HYa doD+2 IN|?Dh~P3PUFDf h>(^TE4U?Y,b'@?#H$ @Fx sUr1p6 ?K:8H % '"eZx '%#U*G,8Z:ADOVCu 1 ??EwwEUbqpgTU`Graphic text box into wh you can type a titlnd date. Dragselection h# resize.b@ jZ??Y08!O?h|*?HF1Q׿|>"H$47D< UG D # Qh]8T  UU4'7U@|>@@"H$@Fx@ AU!Z=&@ t!$߰Պ7 #7#%*8,pSbi`@2I2bVZ䨨2C'nR7QP]E| (#G1Ed `3`&R. to fit Width` Sc@ch.X2{~@`HeoAY#&!Bh!]uJ-?Zpr`9,d&qZUHP5D R # 4AhZ(JT@aa7MA=:MUFxJa mpa43tb3tJq&4U$I`?Copyright (c) 2001 Microsoft Corporation. All &"s reserved[.P`X4#0zGWz?H@ )>2 LI[Sl $JUlAħ%I ! 4a=2a;F `0s4 1ߩ?p,4:\4gw8??!Q/??*?ON:OF9*:?0BpixxO9B)7ho8UHXuD #  4>h4]0JTt ]]M>UUMUFx2!#=0xL%IZ SlJBiUt U[! 4aU2063p BU1: i3(&07FSN<?BJH"  qF46! :O?SOOCAz%NOOWA%EOGO_A.<+OrMd&Sn2Q(|7U% 9.!#Q6!61AO$D?F?z^UPV#$ZcT_`aToC f f f4eThg`frood%zoaz%[ZonX +ig@1qx'4T7[["BB7(sD;? lFc7Ubc7}jMc7jMV͈j#42[7ZsV78>i"1Bn2U___ oo/oAoCV}bXoo`WFtodo1CBTdx(o1yӟeb@ t'`WDkεl~ʯ$Rd$]5GYk}afe5aVkf-?ϲuχϋؤ϶,1q}&DEUHP5D R # 4AhZ(JTPaa9 M0MUFx#0zGzK?H@7)lJA$JUlPA%I 1p% 4a32a'pJ5 #<lK2KQ47W64:n:i4n ??!Q//? ?2?D?V?CIN?t?7 TYY9 E4 tAUFx Fu5ub  >V0zGz?@*#"#!D+4##4# 9 A]'B$/6+#?\.?=T3'52;<;<;7"bJs4#!q4#14*I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.s4`$ 42$ @@l+%="sSW@l> 70>Udd77  t$~Da((]I&I&44D4Bh(EOhOR%^(_:Y6QQ%Ng_O@WO!__ biO&D$a#e9 '2UbB"H@9DJJbo ; ob|ju`f!]##!lu4B5!?D3~fD2'B @B-u@"3f4f5f6e>#9bZa+%QJiyb0cņcAoooooon֞vr'9K]oxjAha%9 (I;i/!31^A1=!xa6?H?Z8a ?@x"E{6 A'" ) 1 7 $ !ka*x$\##!83aTitle f4N=((B!|(~V@!zbCz,VA7a7+)6aTd3B3hQ: (b(F?.58H?u;_A/ Y^ݾu/A{aդFl~N#DO? 1 DPgadoD+0 o]?Gk~ Q,n~Wtm~sUr1p6 ?:8 % '"e Zx '%#UG2u 8AxpJw^g{TB_wwpppep}pO=B`Graphic text box into wh you can type a titlnd date. Dragselection h# resize.b@ jZ??i/,PF1Q? g?N4ٿ|>Av4UG D # Qh]8T  UU4w7U@|>@@A @Fx@ize,Borders,;s,backgrounduttonBsvU 2$@m77!!Va(+8ʰyE}EV`M@`NH(CThe theme foris shape only@ 3`}me42`*Classic;ContemAry;Elegant;o;Retro2zƒ&Gƒd7d]](77 Va:<5c?dd0@dAub죜 ;t1r>b@: 3Qu4z=b@ 0t).W9&G3&G"3H.57:V8}Gb@22b zȄBM"\#ntZaqENm Re+8sGE~BC`&R to fit WWidth` Sc@ch.X2~b@`HeAY3:&B.}JaD2GSO\5W,d~#TqUH LuD # 7*h -,JT  M7nMUFVj?FxT  9 AUFjZFz^?Fx$   "8 #>U0zGzK?@M#4.W#2?3-t` %qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.` 2W#X0@N%=/tU S@l*>0>Udd@11 1W#@ a202J> 3K(A0oT?k@B S@lB4l!$D">OSSLLG(&D4@BP(?P rq$OOOIT#SE\D#PqFiD2T^ UQS@AI5E5OUWUOgBA|&O PWRPrhARcUZ h]W_]*wfHD: !# 4Aih ,>T  9 AUFjZFbX?Fx$   "8 #>U0zGzK?@M#4.W#2?3-t` %qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.` 2W#X0@N%=/tU S@l*>0>Udd@11 1W#@a202J> 3K(A0oT?@B JS@lB4l!$?D">OSLLG(&D?FsBP(?P r'qOOOIT#SE\Cn#PqiD2T UQS@AI5E5OUWUOgBA|&O PWRPrARcUZh]W_]*wfHD: !# 4Aih ,>T  9 AUFjZF@ ?FxU0zGz?R@M#4=.W#2h?3tK` qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`2W#X0@N%=tU S@l> 0>Udd1P1 1W#@ a2t02J> 3K(A0oT?Z@B S@lB4l!$D">OSLLG(&D4@BP(?P ?rqOIOOIT7SE\D#PqiD2T UQS@AI5E5OUWUOgBA|&O PW2RPrARcUZh]W_]*wfHD: !# 4Aih ,>T  9 AUFjZ?Fxg 0 "8@ #>U0zG/z?@M#4.W#2?3t` qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reservoed.`)2W#X0@N%=tU S@l>0>Udd11 1W#@ aK202J> 3K(A0:oT?@B S@lB4l!$D"N>OSLLG(&D4@BP(?P rqOOOIT7SE\D#PqiD2yT UQS@AI5E5OU WUOgBA|&O ( PWRPrARcUZh]W_]*wfUHXuD #  4>h4]0JTt ]]M>U[MUFx2'#p0L%I!k S lJiUt UW!t!a,B2@6/p >!DJ' 3(007OJ<BH" k F4>7(S? ~! XCWF s7Ĉ rs7نM!s7ن(bMņ<نw27ZST@>8؃"(1Le1oCoUogoyoooC}?bXZoWt1CtjRd1՟ /ASb#ЯWؠ<{*ȯگM_p&Jπ]ǿٿϽfAuDϽV`<{Av-߈v-qKr@z*_k:HD: !# 4Aih ,>T  9 AUFjZFz^?Fx 0"8@ #>U0zG/z?@M#4.W#2?3t` qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reservoed.`)2W#X0@N%=tU S@l>0>Udd11 1W#@ aK202J> 3K(A0T?@BW  lBrF4&DG?F`P TU@?P>OfCVOFTSEe\CMFlAaF1T "S@(l!4OTSOeDAI5E$߉D"FE,d2Hqq$O_O 7] P,_>_ BMa[Q|Q4,OrAcHD: !# 4Aih ,>T  9 AUFjZFbX?Fx 0"8@ #>U0zG/z?@M#4.W#2?3t` qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reservoed.`)2W#X0@N%=tU S@l>0>Udd11 1W#@ aK202J> 3K(A0T?@BW  lBrF4&DG?F`P TU@?P>OfCVOFTSEe\CMFlAaF1T "S@(l!4OTSOeDAI5E$߉D"FE,d2Hqq$O_O 7] P,_>_ BMa[Q|Q4,OrAcHD: !# 4Aih ,>T  9 AUFjZF@ ?Fx$  "8 #>U0zGzK?@M#4.W#2?3-t` %qW#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.` 2W#X0@N%=/tU S@l*>0>Udd@11 1W#@ a202J> 3K(A0T?b@BW  lBrF4&DG?F`P 7TU@?>OfCTVOFTSE\CMFlAaF1T S@H(l!4OTSOeDAI5E$D"FE,d2H?qqO_O 7] fP,_>_ B`Ma[Q|Q4,OrAcHD: !# 4Aih ,>T  9 AUFjZ?FxGU0zGz?@TM#4.W#2?3t` RqW#r!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`2W#X0@N%=tU S@l>0>Udd11 1W#@ .a202]J> 3K(8A0T/?@BWh  ZlBrF4&DG?F`zP TU@C?>OfCVOFTSE\CMFlAaF1T S@(l!4OTSOeDAI5E$D"FE,d2HqqO_O l7] P,_>_ BMa[Q|Q46,OrA:cH D: )# =h4>TYY9 4 tAUFx#U0zGz?@*#+4#&'j/=?G4#B$/&+#?\._?#Yq4#4194*I`?Copyright (c) 2001 Microsoft Corporation. All ^2s reserved.V`424#a0@+%="SW@Sl>70>Ud@d77  $Da((]I&I&c{B B^(`EqOhOB%NOIAA%`NOO#W `OOY_RiO&$aU'TAa%'k0UbB"0urRKB Q@KBBeQ@R34]f5R6!9ObQ+%34#(>`2cgQ'jAha(%9 9d'/6,v!3?r1Gv41=a>u?1koom %"8QaTitle f4B!|(~V@ !zb,ACz,VA(aa(a.VQTd‚BXtlND|A .8H?;_A/ p*XM ?Fx~f#d`R_ih? + ]ga biWD+|keZ?Gv~olz~.q|~tӻ}~xD{\~~~<  P+=Oa~   , ?UFDfP 7h(TPUU4XU?Y,b'@?#H$ @FxsUJ1H6 j?:( eZx )]#UGo2  F??? ?? gl w[? ` ̀mh kav[Q`Graphic text box into wh you can type a titlnd date. Dragselection h# resize.b^+@$oQ@硘?Y@젬eu0?j6 b46ĠUG D # Qh8T PUU4UW7U@?j6@@ b@F[@FX,+O?FPy Z#5GXPb(ff { ؉P`b6/<"vW"4u7`dv"u%? V4b%& !4t!brKM#t}!")4U??? v"|) v"D)t6|)!v"/v$;2B$d=)4"U2L@J3YA^D)K4`Vis_SE.chm!#26875Iy2`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved.&F c3x ~Y#H@L&d2?M]^X\.pXN^sUQV_1F`&L 5Qed]]7!/d.eH:aBhQcg@`]oWE4TMd2! !7A@bJBdB(5R 89c& ka4tQ~ tMR9ta7-`Fx< ?aA %s #B@ "W!'Ϯ7=B@8p)(د 7 7˃=55`r?FjZg?tb|(7kp@(2y2bݽ:'nR7]E|  8SGaEd kp"C`&R to fit  `Width` Sc@ch.X2~B@`HeAY#&!kpbTI-D^N_Ϻ,;hZ8BTUHTuDR # T4>hZ ,JTP  M MUF[?FX,+?F멷 @F7K]??Q 4t7`  ?A@tU*4bbp K&4!Me.RM 4u 8R u" 44b3*jA& 4 A '4Ug"WLU&_!4,F5O ߾+3%% /#;"Ir%826?q2 $ /'/@Rqp#1X4v+Y"`` I`?Copyright (c) 2001 Microsoft Corporation. All #Bs reserved.>2p#s@Lg% "uS@l*Y!@Up(Y!PY!? B "t ~Aad(zp#FxA?A@YAYMU+3 3rbcW ;? ?XA0Yb nR bSbRQdE'jCWUP33 sPgSbb@['??;h`FTxS{SU sTRRAhH2WTuDVTUcaAn,Tf l f3ooM~`%};T_91~訨gx{ { ~5T@z{x`U 8ɟR`pnȍFˋv> PAUPчASeY!D]Н j|e'{ŒԳtR%sVA .]] \ weU%]e \ ZU 5f]YvMUnRf rS]dE?„" KRxfA|WeVB]f!KYauwVe"3&xct.4UUqZv&?cVVY!=Cp#%:tSETY*m_m!9P U&/# Aчߙ@߽i%:UИUZ ИO .3 3s 72,v32ݱAM9] P3 갵6F5@f973[313~m hV3eAYwI2B^@C~Ff@&9tA}$@ 3i3& 6-^pExK2v e~6X+E7 B] D%y2C9^D KoRC9-^3_(d3wVzi(G>SF89$Ah_NDO)3"-F6_NSlCa/B?T2z Rd!"T%PM)Q(P Ŋ BP(? %s$>QZÊ4\4]󳅙3 C.FgA"p[KRbzQ@bs!w@jYL_.UT̛KX*^H D: )# =h4>TYY9 4 AUF[?FX,+?@i n]?@߾3]?P} t`  ?A@t*{uLbdp ~=)-&<Nk.W$>3  uUub + &  >V0zGzK?@>#J1"7!+H#((:"A"v!V$?6?#?\.?J= 6N2T<rT<T71"b47!qH#14I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.4U`8 42H#9@@?%="SW@l>70>UddP77  $Da <(]]&]&M4" ]4R|(EOh _R%(^A_SYOQQ%N__WO:__#bic&$aTYY9 #uAU@H$D"?@|^?F\.?FM&d2?F󴝼P~A44r4rLB>> >Hh4u#` RQļV/>HHh>Qzuԍ b~& b>74U$"L+/@#!)4-#%(@z?z'D##PY#=Lz+Z"L =M-#Iu1z43+4`Vis_PE.chm!#26208I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.o7 3O0l>>0>Udd>>YD"1  ^4a!(4-# @F14'Az'?HrC#Od7]IKD"#B5DF+XMB] |OOOi4AA^59 $%3-/'2Mq4`?3f8 ^Textg>daToDGE4TY//$"7@f2UaUf|!(Y9c?}1 YQB4tm`tR:tq@7/I%  tg`]]?M3y4T 4t47)T4*4 4)1 4Z2444qW3 M$`0 = IDEF1X; 2;@Relational121( A73aK1a]3CSOverrides the rKDship notMB specifed byB>C 3orIHFs.NZC3aBA[}OIQ6@)Set to{ h@ allJs @ApageEfK2V_Ff]Y6@;YJtex!t@A2\Tt?Je^43C*;YPK, F)`and Index/`IAZ5Y__`A6@';YAPK secBof E?ntitieDe2`Uogo83C.vmDa@ypef!f@l2__f3C\m@tic@lin@seperM@ngBA;f/b}s44Ft3Cjmhorizontt}Primary KeywromBo@r columpwithini,4ZeK5]x tRU1H!o8ďTcU"A"A" !"A)3` JESR `)2 k ! #) j????HDSSuqob|wwxwwTUse to define a foreign key between two tables in your Object Relational diagram.b?0,? H DB J# ?h T0PYY9 4# AU@? VP} 4u`uY`bA@v u`u 4>h3.T4u7k` lu|8A$YA7 bT46-""",/5G&7(7'`-4"4a)%>0"? *AeErn r.$j!4b*r=,$ D6~b "-sb$5+o1)1/m497}D5F(2(36&(2~?D5M0O4k6<11-P#&4997D5*M6q&7-1u@`079D5EO44A>;CA"b"!C& ?$#&GF#BL6#B&`'Bj#OW rC%#WH"#AC#0CVQVB"V V VQT } U( i!m4`!V1L$3` DB Engineer` /CMD=10_01y$"`User.ManualEditsa~$`bRITextRefGusid#Ab`bCardoadksb2 /MODE;L={d28/p?@r_q?.t6u? 1yuar'1HY`s`A@@bR?7Q@!#5harru 0@vsrx8Bwaru}}(QC77VE@CXDq mBaak4`Vis_D200.chm!#29885I)`?Copyright (c) Հ1 Microsoft Corporation. All s re`ved.ȃD` RelshipsB1l|@8ASGC29rq?#?\.?}E#=}l>AUd| r~X=D(O>ayu!HmHB^EA1TIDEF1X Not}D%GTCrow's FootO7asTOalOBH8AaJM?` bg"!da$c/BTDȁȁ"+%7-1-1BAARO 1 1 r117@hhGaGa21 2ě11|F!F!1_"`!!5"6!V1i"j!%19Ԋ!:1wDŸ!8A*8A(=C@ UnAO (Foreign Key)!F7Vb 1%Unique identifier used by the add-ӸEtCaێ8BY`BSet to indicater{is an 'Iy/ing O'ԁUMC25=exactly one, 28=> or mа?9=zeroK30rFѺk r0w = I; 2"𢘷!=G7_F u]SOverrsCnQ ?specifIs+,AS[m7A_j!-4FopԛCRO0#1s \Ro Database | Docum?Os _localfthUHshape2#?\ ?AOOGA=Ur@cQ!3|@ @+d?PeYqi,3ǰ'LVui egritЮ x,2|?)#5G.bπyd@3J3'ijcardi@8UJ2J2AydriA1@dsqpCUm'2rq?HR1q߱@d5s߱ߵpRFFlr/o.5cr}hcq@` SEmu߰` D/@?@sSe;Bh ir,7@736< 3n|\nҩ@/I&p<3!3q!CA`I!`م Properties...0Ћ` DB Eng! er` /CMD=1001yXOBYۧYfq9*m!I!`{~&3ΖR=a֓"` 2Omprideau"`r.db/Ebt#2!r=0 rD"15߱ߵק .aa` G/Y/k/}//! /"//Oա㇑ /q:+ 3!`S1jpX11?BG?Y1:_?q85HA/ 0\_B:jH F #Ը ? ̙ raa oOD+t o"?L  oPUFDf h>$^T E44UA@ ?F B/P(?,04"(1 4bab1$P1%!?@L&d2?\.YsU /:/4~ eZ g%\bUHo4k/}/b1"14` Connectorj`sbbjrbbjY6O6qb%Y6 Y1"2 (: ^   -}|5 p`I`V?}`xa833ސ]3σ3u 33?, ?Gxpx^& pUse to connect processes in your ObjRelational diagram.pr Ctrlglue shape-to-A. methods.VbOn"~j??7B-D?6? HD  =hZ8>T PYY9 7B;AU@X9?@v?@On?@"_~jؿP} 4u `u A@u  4hbJ4u7`h?gur*0*0+ b7AC@M>4U@B`"?@h|?5?@@$?*&)L&)@(Q$C?p'z-'(4r'\b6U6 666 61Ur67@# LZ$h;?bL6 h@2rqǿ?@I0ϵ?45? **LA-0~!7i hBu .>FnShNAsh*+$'HBA;Q@TIR`?Copyright (c) 2001 Microsoft Corporation. All eRs reserved.4`Vis_DoP.chm!#29881& FE_? l>,>UdYj U*JR47T=UGDF # h4T#PUU4B4U@|>@@U*JR@@\.?Py 4u7` ?u V !*D*U4*DZU\ .&j00D 4L@4%4`Vis_SE.chm!#26855I4`?Copyright (c) 2001 Microsoft Corporation. All ,"s reserved.-83ʎ!B(@L&d2?=8)8>sUBO1M6o?(e@]-77  H14U3d4d]%| f?@p\OnH4tUbg#]OC\!TMDEAECDm"T 9C+? REACQtMRPTQ7j#_5PK4rA? Ub d] A\U8SDQm%rC% #{Border,small,text,box,used,create,no(`sidebar,Drag,selecL!,handle,^ ize, cs,Titles,backgroundb`uttons,t^a,blocksaR%\rW$@2RB( Q]l,klFᏊ\t,(A]AocHD )# R=h,>T#!E>7NAU@\.?@|h@@ Q4t7`b?A@4m ?t./4z.rV7 4iu4u/>ڦ4b>UQ<!@4q#8!=$I)`?Copyright (c) 2001 Microsoft Corporation. All b"s reserved. 4`##0z_Gz?<@72# @=3Sm W@l>8>U#,1,19 7 <} " 4|C2?n6 649=f??3V(T4E7'IT$@U,43VVA% E93VNLAEZ4`FrG'I@!0=LOG2 ]?7[?1A<(?zFA T# YY9 7 AU@\.ߗ?@fhQ} 54kt7` uLz7-@m A4\h?+4t+4u@5uWbI&4 bp  .(jd DAE) 5  #  $"4Ib]I"O&P+>#4U0zGz?<@72 4#<!!q#!$IQ`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved. 4`O2#u0@%=S(1@+#1*'B ÿE "PkPeqkM_ 1qOhH&&Bg/.G#2qFP?_!V#?+?h,3*l>0>U## E 1}(^09hH!GP?RChC'B"4!@_nHa_a(VTFF'ITk@WbC!>CWQ(-DT!?% @*af56T|hGUWj}a\_0dgFI"rAX0W#esomi4#a(qaTitlef5k8BT_HuQ/ sL{ZF| _# g}>? D ]^a g+ ܁BFG,W e 6$ UFDfP 7h(TPUU44U?Y,b'@?#H$ @Fx8^T E4U@͊Y1@@*.ޥ@Fx" ,"L@J4G#}!$M+4`Vis_SE.chm!#26862I4`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.G3UtJYG)(^!1"c`J,=% #b30zGz?@$7_b8@L&/d2?=8Y8>sUAFH;Oq8e,ZY5U%,4U HA>%DG7DG5DG/5D4Cp6Z/5MU%;(b3?6F6 }! 4a*#&:Af!( ( 4&?`Move to change feature size 6$Y5 b%K 4{Vre `d=_P*>%uZd\od4t4brc7btohgpg`]T.7E4Te a>"1a1a7Myt2At/2$$ ;( Xs{VQ`R7 fv uDv&B4q7R[pdta`|q5`d? b Fa az B^!q59spӁ`QnRc1cq/5Xs}!F5 x#nNote,box,classic,Dec"ve,nds,autom cally,Qd,fit,text,Borders,Titlbackgrounduttons,t,blocksq5sx$@rn9_K_R(;(w'%d *F#`&ReQQ ="`Woidth`Sc ch.X1~f `He!Y3::B8$]R;DE6jś"eA5pA]pMHD: )# ;h0>TdYY9 7 AUFx#4U0zGzK?<@742UQ<?B# 3L<# @#; AYS@q#!$l+I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.`l>YUdvf$] 4a250X06G4j2AX  1F4!??OCKAEWNpOOGKA%WE6OOOKA%2?;rA qS i4o1a_WHD: )# ;ih ,>T  9 7;AUFx5>34U0O/Nk?<@7%4! B<s @x#;U# _bt{ Sq!K!K#~ IS`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.`l>$>Ud Ej m% 4wa2a30:&'4z p '45?8??i4A a%9 ~&'2q@"u $FHD: )# ;ih ,>T  9 7EAUFxJ/u%u%4%j 4 %> 5>#4U0N/Nk?<@7)4!Q B<m @r#;O# bSq!E!E#~ I]`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.`l>$>Ud E g%a2a*30:N&4z p5'45X?8??i4daMO_GHD: )# ;ih ,>T  9 7TAUF2L&?Fxk>=4U0OߴNk? @7#b &4!D #$ B< # @#;4# bSq #!n!n#~ Iv`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.`l>$>Ud PE %}a2Wa30(q: #!&4ғ p>#!&`45?8?O i4A^%9 &3\/'2q@ " @FHD: )# =h8>T YY9 7QB hAUFx>#4U0O贁Nk? I@7 4!,#!$ G,#2q ?/&?\.?h=RI@۪H?@M4(?&6 *uLdPB0Y7-A2.\66&d?h9;!" 6+b/2 "Bq,#\At!t#~ !`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved.V`B2,#a@@#%=.Bgt S\1 @l>Ud% E 4agRaoSvP (4,#A&1RA&40\_,44/^Double-click here and type note7s. I`jA!^%9 IzBf#$o_ 3b QVm*?A!iVoho+$'$:"kooe'dVh-f ?A&h@hfLbP?0 ` > ! T>W=72CUgx7v0·2<2_FHQ/ vd o[f?jGT%w l#j}? ~aaԯ oD+ oT?Gl 6. dp$ :<  L hUFDfP 7h(TPUU4>U?Y,b'@?#H$ @Fx sU16?3&`(,%R1e,Z 75 : HHA%TDU_G7TD _G%TD_G 5TD_Cg4U`]OvE4TK T"7M:T"GT 22(SFxD !c+`*T$YY9 7M B|AUFjZW?Q} 4t7` A@MtM 4 ?$93uX5#u94b Z4 .J&AR >e>#4U0z_Gz?@*7:#4+D#eJJ KD# :&: !S*GD#2q0??.6;#?\.?9L%!z/Q9$D#?<5 @~&3?@I0?666nbBJ*Hu9:D&uL#Adp Y@@*XOj@7-oF""8""B6BqD# Q13~ IJ`?Copyright (c) 2001 Microsoft Corporation. All 3Rs reserved. `d1a 59 )D#Fx Ӡ bwj=4r!i@bC6G'RGBoo+bxiUdm5p4XaNr TuTp8(`;9CLL4:T90B Cz%6}/Oq 5u>:ug5uLg7o7ɄsTA!!;"r8(2.T? B\gDc`BKdCi__T YY9 7Q sUisu<D T 4 >#4U0zGz?H7p47#wL sh:#E"<?Z&4u#`" # @#; bX"&YS  ر@>2?(47F!uT `bd%7-1 62F.6"h' 0q8"whBG#$;OMF?\._?hI D&q#IAD<+IO`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved.U`l>0>Ud@dEh9!$ OaDB#Fx<οRp K9}7h4`?>Rl3h?_`Ww%4DWS?_$c2Yh`Q%BYIg_Sio{l`5___oj4daMt(qQadA!a%9 &] hhi8C%7HD:  )# h <>T$YY9 7M BTAUFjZ?Q} 4t7`~A@tM 4 ] ?$n5@4uX5u9X3b 4 J,A >5>-4U0zG/z?T@7#b E4+# J]a @%=D#m 0S*@#2q ?/6?\.?9(=D#46?3F<F<F7!\//>##:5E$@#?@I@?6F6nĹBJQHu9aDuLJAd'.!E7 DD"2@126Bq#Q13~ IJ`?Copyright (c) 2001 Microsoft Corporation. All @Rs reserved.`d^%9 )#F?x  bݐj=4rKObDG4RnBo&o8biIej4__@ ^%4031r09v5/9*l(>Ud@1Pi 4}XarUup(GT6S?AOr\CWi4`;9\CMG2%:T9WBGChNFg@7da5DXaXa"(#TT? igDA Qcp Xdioo3?yp'E5*" ysіk~_'8YaUHD D# h8TB UU4 <U@FjZ?@͟[?FQy 4u7`b6A44rA"$ V? Z ,#t` 4ud`?SudT  4 #4U0zGz?T 747xMLI>MB2(}"zG2q ?/ $?\.?d"-"Ix306 a }b.j=308C3 vg7k(G_o`HD ##=h(>T9 73#U@FjZ?@۪1?@~XOk[?P݀>. > > 4u7`bAU$u3`b:'4u"bu p4%)FtZ)>74UWL+ 4 } @*i`bdp /7-6 )zW! s$ Yo%4>R^*A00:t! S\q!o@l>0>UddE 3UC0*9'#2#H45&?G? OG!51N'%JO\OG%A|>OOO%A?COOgN4K^Double-click to type notes. Subselect "Title" Pedit the tQ.i6^| =#f'0q+`?=j__:dg2bo!3bq}1f=ye?`kP4DoVo:h_FH'IQ/ *6xJ G# r}? $1Dxaa\ oD+ o\?G 8< P ? (R z 54  UFDfP 7h(TPUU4\U?Y,b'@?#H$ @FxsUN1aL6 n?: ( " eZx %Ra#UGs2 qp'9K]Dz~@OOOOD3 8 'HyDZDecorative box for notes that can be automcally sized to fit ?text.# e shape.bx sUBA@FbO&7`,%1eHZ03~ Uvd"U A S%TU%W7T%W5Tv%W5T%W5T%WETE SxT%WET%WvX%WX@%WX%Sn4@7d]5ME(' 5v#`&Resize to fit text=`Width` Sch0ch.X2~ `SHeK1hYt#&!pDrooH(ōCFǟx<~ 1 4Rag?2A!b'&"`Movbchange feature a05 K4vr 1d7]=neI@w?@,"@[4r.b %2tv7v@b؆3r>P1 b)`'pt/B/v[.}pjC.%C%& lr4 2$\>#gU`]uHk *E4T*."DD7MB22 (ȊTvq @ lN/D4#X&2>#b'SQ P RRl'l&l&v&T"p,v&&'&&&Q,R&#"˧># l7 !$3.׀EH? ob duoՐI v"l59T<tQO҅RDl5r!15 z$3pNote,box,triles,Decg2ve,nEs,automi0cally,ad,`,a,Borders,TitSbackgrounduttons,t,blocksalEY$@BHTW`΄J8u,uZ#HD: !# 4Aih ,>T  9 AUF@ ?F\.?K@Q} t` bA@Mt Z ?1>uPfpu1b3b  >U VL4##??bO<#al @q#;N# S:"@%q#!$+IB`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`l>f0>Uddff $] a250(06GvW21`32  Z`F4@4???3A]%'N@OROGA%'E6O?OA|2?;3rAAS HD: !# 4Aih(>T9 ZAUF@ ?F\.?oF@v??Q} "t` p KA@tH ?*?hw>u^t~u?X3b5  >U" UL 4<#D @I#; buS 0@Jq#!$+IP`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`l> 0>Udd 1P 1 $w ae2k5k0(06I0E21nt  g64@4???31%>O*OyG1|56cO?O1 2?;3rASc HD: !# 4Aih(>T9 JAUFx?"Q} dp KA@u`b" ? *u`b),!tZ 7Ih>3T'  '>#U\??b(W4<: X@?#;# ^Tt# Sq!$Ii`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.-`l> .hUd ( @] aW2]5]0 "w(2Y K? g[64@4rq?0 901z%59 O=kG1|2?53rACY HD T#=h4>T#YY9 7Q AUF@ ?@9*?@*IjQ} 4u`bAo@"4t p K4t7 ?4u/`Hu>X.r  >Apv 4u6>3b*&  >-4U0zGz%?@77#*"0!47A#8"KL #A#A*"Aj'BA&? O> `bd7@ I@R2pb9 U?f "")qA#@AED+I`?Copyright (c) 2001 Microsoft Corporation. All jBs reserved.)#`1 2$@@8%="5S`1/@l>0>UddE ʘ$WaWR]U]P5(V!1V&>R3X`4~U6_7?_W%^#5__KgQE~^5o_YoQv~__oni4da'(DqaTitleHDB *#=h4>T#YY9 7AUF@ ?Fv5?FTn?@!拮Q} 4u7` ?A@4th74pKb ku`87)  *48 u`Z4uސ`b tA`_t .E > >U$ 3b&*4& 4 >=4U0O贁Nk? @7Y#L"b 4i&42qƺ ?/&?\.?=3L"R!@۪1x&>iuLFd8i0@ZI-`h2"4 H<~-B'"&+c#BGBqc#ADI`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved.-K#`S 2c#P@Z%#6_BS`W@l>,>UdAE q U^6aRWaSPW(96c#?7C449T0>78^5b$g9+o=d"x&b@E]9#3n4u1aDouble-click to type notes@Subselect "Title" pedit the{ t,q. j@A#a@E9 Y9PKE$!3r0vS D 'R!iN`3IO'$q"P9/">HD: !# 4Aih(>T9 JAUFx3'  '>#U\??b(4<: @?#;# ^Tt# -S%q!$Ii`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.`l>\ hUdP  @ aW2 ]5]0 "7j(2Y ? g64@4q?0901z%59 O=kG1|2?53rACY HD: !# 4Aih(>T9 JAUFx*R3' M '>#U\??_b(4<a: @?#;#- ^Tt# [SJq!$Ii`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`l> hUd  t@ aW2]5]0 :"7(2Y .?m g64@4q?)0901z%59 O=kG1"|2?53rACY HD: !# 4Aih(>T9 JAUFx*R3' M '>#U\??_b(4<a: @?#;#- ^Tt# [SJq!$Ii`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`l> hUd  t@ aW2]5]0 :"7(2Y .?m g64@4q?)0901z%59 O=kG1"|2?53rACY HD: !# 4Aih(>T9 JAUFx*R3' M '>#U\??_b(4<a: @?#;#- ^Tt# [SJq!$Ii`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`l> hUd  t@ aW2]5]0 :"7(2Y .?m g64@4q?)0901z%59 O=kG1"|2?53rACY HD: !# 4Aih(>T9 AUF"H$@Fx#U\??b(4<8 @=#;# h^7-S%q!$Is`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.`l>\ 1UdP  @ aU2 [5[0 "xj(2c ?# g64@}4q?0901x%59 O=iG1|2}?53rA3CW HD: !# 4Aih(>T9 AUF"H$@FjZ?Fx<Q9@dp KA@u`bW,t#  ? vu`b)"@H.ahhAJ M '>#U\??_b(4<a8 @=#;# h^7Sq!$Is`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.-`l> .1Ud ( @] aU2[5[0 "@(2c ?g g-64@}4%q?0901x%5@9 O=iG1|2}?53rA3CW HD: !# 4Aih(>T9 AUF"H$@F\.?Fx<Q9@dp KA@u`bW,t#  ? vu`b)5"@@I]anh>  '>#U\??b(4<8 @=#;# h^7-S%q!$Is`?Copyright (c) 2001 Microsoft Corporation. All "s reserved[.`l>\ 1UdP  @ aU2 [5[0 "@j(2c ? g64@}4q?0901x%59 O=iG1|2}?53rA3CW _HuIQ/ _?$aLJGKF #w}!?  ]aad FD+ IR?G |d M$ d B "O$'ӄ +PL .Q, ]0$  2$C4 O7W9XUFDfP 7h(TPUU4U?Y,b'@?"H$ @FxSh??P?4H ??TY,b"H$4D< UGD # Qh <T$UUZ5BU@Y,b@@"H$@F|>@F/"Si?v??Po4u7{` ?Su 22<L<`XU<t 9 9ZUt.88LL``tt{rL"=R#e4U^8A4v% l%&#w#0zG/z?@7R7w# 14*I4`?Copyright (c) 2001 Microsoft Corporation. All 52s reserved.4`Vis_SE.chm!#26865Gw#; M"L@913%8@L&d2?MH\.g&HNsULAaJFlO'B`hb1Kcd7]-(&w#FǿxT@dYY9  6AUF|>@F`_?Q} 3u`bA@3u   6t kbt  ?=HC=\YYA>#U0zGz?R@4:.#</!ʴYMu >Lq!${+I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.2`:2($/0@<= CSV@l>YUdv  uu$ Wa250!0=X#eBHdX  F(:B4`!1? G?OCPAl%\NuOOGPA%\EO OOPAl T  9 AUF|>?F/"Si?@ݼk?Q} u` ?A@u^`b  bt Ft "XE ?^m8>R  (;J"%&>#U0zGzK?@4?U@$"@)qJ#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.4`R<2J#60x@A%= ESX@l>Udp1p1 J#@ a250>(_!6 WBH45 O?'O9G%KNz?M?dOvO,3Ay@$84<R@F#?@LT9 AUF|>?F/"Si?Fp@Fr4??Q} Ao@u` ?u   b7t  fQAt#LqXHG Af L> ""(=U"[&>#U0zG/z?<@4q#!$I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.6K`>2#@0@w%=YGSAW@l>0>Uddz1z1j :5 wa2a30t(!t?F9ӳ_u/?B"04t ui*ɠp?F3~"?P- U& ~|g?K (L!(EKW(E"ÿzY (@: I@K\.? qI3@; ?" 4BOOWr'#5h,(.85)hg@]FKpgk3ODAA|MEw5@FyrZ?FT bPVZ6?PN5%S@cdnTMd_D(LƣVD>8qa fDͧvEΒd,_>_\mooIWY^#pQ 9UK!o3dHD # =h ,>T  9 7TAUFIBB??F^',?Fo~?F>8ռ?Q} 4u7` ?A@u4 4 bo4t  >_zEtPa}A_\K m\j򚲦k) >k4QA>3U" FUL4q #B!G$+I`?Copyright (c) 2001 Microsoft Corporation. All l"s reservoed.:`B)2 # @<%= KSE@zBG #2q 0? ?6#?\.?#l> 0>UddZ1Z1   $aU250(@ #F6(4 W2A458?? OG%-NFOXOG!AE>O?O!A |??OOcN4&aCompany Namei(da_WH D: )# =h ,>TB  >^AUF|>@FlyrZ?@U^?@voKѼ?QAu`bA@Mu   b*uL dp @-&?Q.Zth t   =@~?|/A5>7U " :jLq#If!k$+I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved..`6G#27q ?/& #?\.? H=2#aD0@ %(3SZ@Sl>70>Udd77 Չ$DWa250 (@#16<P0SB $HT(5O!G?-O?G%QNjO|OGEA55>O!OOEA |?cO_NT(T7i^559 L9#/o'0r"?=f__aA1(~V@pu zb-" zjydaoBk4ooZ>hah $&VqwTdLqBU^-V?~H+ScHD: )# =h ,>T  9 7pAUF|>?F`?@lo:@@K%H(?[?Q} t7`  ?A@*{uLbdp ~7)-  &'3.03u 3uitTB| >ںbS I >qU.!3$I`?Copyright (c) 2001 Microsoft Corporation. All X"s reserved. `E4+(#VB2(# @%="S0@G(#2q60?5?G6#?\.?E 3l>0>Udd@11  \!(@(#16<eWBA45 ??O*GEO OO0A|?NO_rN40`Double-click to type notes| Subselect "Company Name"Qedit the title. _\HIQ/ Xw9G F '=E#Tl>p?  ]aa$ =?D+U @S?GG[ DgD UOG$$yJ N P4, SUFDfP 7h(TPUU4 U?Y,b'@?#H$ @Fx 0 1 b  pEpt/S/Z?s+=i2C.CC&! 2.lݏ42 B6#gU`]Hk E4THL"bb7MQ22J (0vqc!4זs`@'*v+#`{7t0LxTf? xb \dx7 p!O©59]ϚQXRϨb{5!3#15 uB3kNote,box,neon,Dec2ve,nNs,autom0cally,ad,p,q,Borders,Titlibackgrounduttons,t,blocksa{Ea$@BHTN~AO\obfk,uZ 'HD: !# 4Ah0>T@dYY9  AUFjZ?F\.?Q} t`  ?A@tub+iAuLdui b  YA>-U0zGz%?@4##J" <?-$ h# 3L)<# @#;,# AYS@q#!4+I`?Copyright (c) 2001 Microsoft Corporation. All )2s reserved.`l>YUdv3U@ a250l06jB`%AX   EF4! O?'O#C_Aq%kNOOG_A%kE@FOOO_A%2?;rAS HD: !# 4Ah0>T@dYY9  [AUFjZ?F\.??Fx<UQ} t`  ?Ao@t bU "p ]K cuu7 3b  m>m K >=U0O贁NKk?@ #&4#"KL##e"??P$S<# X@#;# 7mS@q#"1'4m+I`?Copyright (c) 2001 Microsoft Corporation. All L2s reserved.K`l>YUd\vg$w a250 (0"FiE6BAH  ?"hF4,!,Ok?JOFCA%NOOWA%E#FO>O_DA52O;3rAS HD: !# 4Ah0>T@dYY9  AUFjZ?F6*??Fx<UQ} t`  ?A@tA` t b "p Kk uu#A 3bj  >.#J$ )>=U0O_Nk?@2?#O&4I#@"- 3L#I#"??$OYUdv$ a*&B,E,@=(08TFshBsH  q"F4^!^Ou?|OxCA%NOO:WA%EUF$_pOH_AE52NO;3rAS$0HD: !# 4Ah0>T@dYY9  AUFFU23Z?F R|?Fx&k$& % 7&.6/#Q&#:#>=U0O/Nk?@E{#&#!4#|"P!L##"??$<#A0@F3;#3 b #J Sq#14+I `?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.#K` l>YUd\v$!a*bBhEh@y(0D 9BA!aB  6"F4!0O?OCA%N_'_vWA25E f_O]52O;3rA c`0HD: !# 4Ah0>T@dYY9  }AUFjZ?FU23Z?F R|?Fwx<?Q} t` A@)tA` ?YtKp 2?W+"^ubku"A3b  e!A "M&>=U0O贁NKk?@/# !?&#!49#0" 3L#9#"??r$)<9# @#;# iS)q9#D1I4+I `?Copyright (c) 2001 Microsoft Corporation. All n2s reserved.#` l>YUdvu$ WaBE@-(0DFXBjA ""  a" F4N! NO?lOhCA%NOO*WA%E@EF_`O8_A552>OD;3rA S0HD: )# =h8>T YY9 7R 7UFjZ?F}>?@9*?@*IɪQ} t7` h?A@,tp KuZ"u`7 Zuh`Mu ]MbZb  `B \@T ! ># VBI"8Y&'&$B^/p)>-U0zGz?@7#47#"! L"&""BG#$[?m6#?\.?h6##2&!?0>UddU 3U@a9b?e?`(!1&gN{bCh4`e6qoG?ogEnEoo-wa5`no;a`oo}n0!70#T~TitleHD: !# 4Ah0>T@dYY9  AUFFJ]rc?F R|?@Q} u`bAB-tA` t ?b5Kt !?Q Wp K!5 uZj   >&&>#U0zGz? ?#4I#@"!KL(!:"AYUdv$aBE@=(0 Q!DBPVA,""  +#vF4BO?XOTCA%OOO_JQA%E1O _UO-Z!52C_;#rA S0HD: !# 4Ah0>T@dYY9  AUFjZ?FJ]rc?F R|O?@Q} t` A@BtA` t ^?bK!? Wp K!fbubuZT   >"&>#U0zGGz?5#E4?#6" LKLu(!:"AYqUdvu$a2503(0P!:BdLA"!  " lF48O?NOJCA%OOOUJQA%@E'OOKO#Z529_;rA S HD: !# 4Ah0>T@dYY9  AUFFT?F R|?@Q} u`bAB-tA`t ? b5Kt !?Q Wp K!5 uZj  >&&>#U0zGz? ?#4I#@" LL(:"AYUdv$1WaBE@=(0 (!DBVAR,"!  +#vF4BO?XOTCA%OOO_JQA%E1O _UO-Z!52C_;#rA S0HD: !# 4Ah0>T@dYY9  AUFjZ?FT㿡?F R|O?@Q} t` A@BtA`t ^? bK!? Wp K!fbubuZT  A"&>#U0zGGz?5#E4?#6" LKLu(!:"AYqUdvu$1a2503(0P!:BdLA"  " lF48O?NOJCA%OOOUJQA%@E'OOKO#Z529_;rA S HD: !# 4Ah0>T@dYY9  ;AUFjZ?@?Fx=U0O贏Nk?LE4 #KL#E"??0$S< X@#;# 1ySN@qI14M+I`?Copyright (c) 2001 Microsoft Corporation. All ,2s reserved.%`l>YUdvG$ a2 50o09cB~!H"  " HF4 Oe?*O&CbAn%nNOOGbA%nE@OOObA%2?;3rAS HDB *#=h8>T YY9 7zNAU@Fw5?@۪1?@!拮?Q} 4bA4tA`YtԐ4u `$uh4V4u7`F[ubY Z4 .E*A* >$>-4U0zGGz?7#*47# "L"2q ?/&?\._?h=##X*2A;'B' } *+p `bdpp S7 @<D1R4LRgO1WBl~u"'u"Sq#vA{DX+I `?Copyright (c) 2001 Microsoft Corporation. All Bs reservoed.` 24@@ %v#u!t1S1 @l>0>UddgE 3U@ 4aR UP(9:97hR3X4UO6_W?_W%nX5 o2ogQ%^ko_oQE_oo=n41aDouble-click to type notes@Subselect "Title" opedit the{ tq. j@A#a%9 Y"PT% $!38Q4 6'i3R/'$ "@_H'`/ O c@tAbF wW#}Y? ă ]aa ]D+  ]U?G| OZ:4 `g be (h j mWt pT ^s4 u$, px1zYCw}UFDf" h7 T77 KUF~@?xUHo4/*/b!"!4` AnnotationsF`ObFNbF6NM 5 1" ,S Ef(:LVovp ?  7Drag onto page and adjust height get 1-15 lines. Use wforrtssts, title block8bill of materia}l8etc.R b\.~??Da??i4UHD<  # U4U UUh8JT \MaU@~?F\.W?F g?P uU4:4{rLSi c4u7m`?uUԐ4%aUb .R]]i BR*\,"'& F4U@  Pc'%%r,##0j4iC ,u$u#4]#2qB0?A6?A6?fA`4A6C<[7R]#T""d@V"=T% bSZ]#:A?D;IE`?Copyright (c) 2001 Microsoft Corporation. All dBs reserved.4`Vis_PE.chm!#262053]#0O贁N%kS3c&7S#4Fy7D|d@0l 0UEE["0 5aQ(r]#Fd&<#[/'#&7a@%R'"R'"]HR\Y \]4@S1QW}SA7rB3\U.c5VQ?V2SAVBVCVeSEVYSUGVHVUVVVTQ!E:[S]SSOverrides the rTship notR speci?fed byzrS orXvs.νa!csasasasasasasas2sasasases qsYsqs)qs3qs=qsQaPa}5SP$Set to hvp category discriminatorqQ9R%1WeP:PK, Fand? Index!v within EntitieB@IPaP0H׀aЀKey secr8QqW?SaP.6DapypxpHB`R{WPlSqb{qrpticplinxratperˀzrA-!ܕ6mR -horizont@HysPKfromzro{pr co7lumyof!>yzRHTRoua!U2 6T}5a!`QQ3AQ1P`ToInitanceAPf0۲ ᱍ}5!aŰComposiTɱsٿ!Ea1P³Ta#bl̿?Ą ذVbϱ³ C˃D:腡" ³ Uq @@>߽Ű?iٳ ٍ3q߈Ű [h aY2T%$dF9!e6T2ttMa!& ! @amQA1QHTXQeQ1Ta!)!/a1o1*9a#)*3!@/=!L-Ae}5s~!Es~sA~s~@s~seQ~s1~sa~ s!~Es/a~1Qo9a~o !~oMa~o3!~o~oAq~XQoua~oaveeQoaoaoa1oaToqEsaaoaoao&Usa'oa+oD/aoq5s qo4rb9aoq?ovsVoJoKo,шMaoMCoGoшWaoPOoQ_oRoSo3qWo=qAqoW{KKmQDmQDDa qD qVыAēAYATvQvAnQAc_Q3qbQQX_kᒡqTqQᒣ_jhNWN#%kąh q!)@@g`@@۪ߔ?@ n[5?de?YAk(1i YU&X XŰ0aVQ #!w crjTf83l3N3}BTgUBg?#!N*Qk''T(1(1RKKWWJJӅTUJ(@8J(\xl!,w8.d`&{47E37599-9648-4BC2-A6D1-07BE70223BC8}(y!Nkw?rq?_OI$UuGHKp";ȃQ |zŷUd88NZTfqOOZaBT}QE&wTDatabase Proper..A_Show \ed sib#3d$iBx?lS'l!DpATbUZaޟmp#ea3d;keno"YE-e`Bdݶ#?bkjb^@tͮaUs^*t͈y =jGK^ߢ!!^bx?+ߥ~Ŕs!jmDVxx1db3: EF/Ps\l/cAU6oXc5;Bc[ġ_iܨf멕~/Uf53`eAXO_SE?CTIONS;q!ehhZK $oME@AvQTžUs#egA3d˭xBZiOas ަ5s40zGz'D,xhT1i 2Ւq?!? +N!ҏck}s 7&d.c15$t+R/p//UFK-,7'cU?g?y?s-x?K?GOYB;?OO*O?FD$/dWӁ&57PK ID U1 VALUE ORDER_WUNAME I1 HIDDENZa'9K]M"_^GOK\! U'Sb^v~'hozm_ abe9`3bKb%4f5a6Pc9baߥd&5uooooo{d8JEz? cs`K'?*Kye$wP=>ń&2==5{A{A7!! qߝ%E&5+{7Yŝ@̙I$kA(A(bZ6&Q7u(<ɬTjK@/?3!R3Ёv 4%@)7,i quU@'@@3xǙ-"@"Tx?@K߀%sTx~=LZӔ2ȁ} a׳7}þ Cc$av|QϚ__Yj_UPaL!M1:V-A(TDatabase Properties..5Show Related Tablesh!3A(To_4~ЁQĩL! PaٕЁ%dq@)ĥi"Q 8SϿ뇵}Bdݶ'65&5Ì̀?;Q;Q!"bFqFqVrEQEQb/TU/%?e=$v@=q  !C!%eBq <Vuq e&ᘴ <kT=J= ;P7EQQT v1GkL@qe{QxT{"aU_]W4=amaa"oU@M&d2 @@p8@U"?@3~CFc"%jkybe+oV.aa F&aeocJFXHSxXuL9b0h2VX)V=@Rq^p~e 7VpVa0VqhQCV,DIQ7CbB ksMbAPattV%D7aaHᤢ@!!b&&1DBUPRQQO  G=ea1:t`&{65D75FB8-724A-4A7E-8026-70A57975E778}!C?(?rr ?U)H_eJr~J&pyuxE? ^r Pb9oI,a ^^}7-=/3TDatabase Proper?ties..1-qVPShow Related ?Tables9`K=ѿ*J4DY%7Aq$oqS5%1QqYqi/dM &Bdݶʗe*"V 4~! ?31/4ͻ?WD$fOM#T0<ń@qJIHc.4lBUeB]h qh&'e=ą/-uѭ1bGROUPS_ARO_MAP'9M1|eR!NNAs1I<˲7AS1E!߱ )E%%љSwbeQ-^UޏF 38:0zGz Hyh02bq?*ïկ /ASG^}Q߃)e$E)aU/2D~e+Fo &eҹ_Ѭ>$!Q^ֆK,FK{2 _ID 31  eamooomehVo@/h^pɈ!%.UOο"v-GG/| 0F aB2H5H0~13bP]24c65c1Nb6Ap\39b!?*?TTT}eeFdd!ҟkр^0gEe@00gdl6aoe}dk/bcFBdksbc!Bk/dkԯOOG>FT7dUp$)vGle#!!t#!w*U5!AU@Ab$ @@@ o@tm n[SvЇ`A>!Q / ANPn_rhQ,k,pGMAٱ>c0c!e*(/$e$!с3“eДۓUQ!! ^_]Acgqgqb%R~a~a"B!!;rAA~b+QQR1333)ͱ"!a5b"Edoe~l T&0gT`-Ub`&{C8970EFD-96CE-42CC-9171-A6FDEF6C8206}6a3o2a!"Eq !5?r64?!3d;upΖBqlf/*/k)e*]/o%,q[s,0^ ^-"TDatabase Properties..}ecShow Related Tables(,>%/?NؔѨ1ۑt=nuȿ}.Ѩ5~a 聟/*1H;~aXYE-{BdGݶUp桊e~eh͆)uUQC 6-`"A__o#f+/oAfhA=.@߄OtIO_!W}x1.$g8"A~a[q "Eh}~e&eSECTIONS%[m@M ŗtUY"A97[1)hۑt u]/yq 1~a8/D9K]&.ﳖe0zGKz] tqԐ2q??6 ?Q`MUgy]M%!/FNNM0$Gq^KUSCˏݏ }qtdӟl"4/d4?M`5!z5Ay!Oa{=>RݑݑQQ!AA"汢v 1% y/Uyew!Cw%@u̙IkG$*xxʗV6a7!_$ӳ"B>L/1 4ѣDZ}sQ `/l--!4y!)!F!_/U@.%@@-@Tx?@K%]Txh';62DA9d4 :ֵ!g?㾶8-MDZa(Ga 4):vqq_. }bާz=exrzQvF!%`)!q 4q@zc _FF%QQQBF%rrR@џu_1"-%Wq"!Eڸ]vuݑw ۲D`&{9E6EF68B-DBA9-4761-8FE8-F67FBE69AAE6}%(HW?r2?q!$naGifqyzu !_1@^!-Z-STDatabase Properties..Show Related Tablesx@ S>I< qz k!®_1\!õQԡRk!5fQ;HSqgBdGݶвG6Uv<͍) &8[TA~U5Ck!\/n/Iʟ5_jDQ_5kbFMf A1o1QohHome[ n5cEnGk5_UqvVACO_MAPS/O _//vNvķ @?~(@ a.ņaղ]ŝkI_Z#g?y?8) ?f?7?t0zGz?Sf؍ 2q?X9Z?C@١võ{Qa______ooo%o)Oo@N|o{Q^$ЗȟޟUFd%FV^ I{Q7Eѯ˝0e:aL^p`?2#rLjMe cY5ѯhPK,FK1 ACL_ID 2 SECTION_VALUE 7?QcuA:߾DeaoU`~'ҨEv0@ a'--S3bcB4H5H;r6z@A9bgO8!3EWiߪ-?Qcuu5!@Aoo_bd,_P_bW1?e`3A@1?O1bMysC@}OO=>200@/q/qrOT?OE)''D`@''5[+1-!}/%B[+&"#@[+j"#uFᘴ &$ktؖJ 7/qw;tOja_D|reqbte"6vwu1}7wq4'1ɰ} U@X,*@@p8@?"?@3~C0"ETc+qF2 RqCJ0x͘d=X_6#0LPRRƤxIF3'#@t2aagOO0NN γUؿHe©@5N@ pgN0N] ,kooHJHg(&<`7NjuZ+}:1RC_-Q 31 ju +U@f&@@ZV@^ͫ n[M`qs}a(bZ8 +8pXᠰ\RqU117F(c1eH?J⵼eF qᬳe H!ų?q A Hn}QQb%<Ğ11B %}a}a2+pqqjr1211ѧō洁EN@h>Fau`&{DD91ABC9-1E03-43FA-BFE6-2FEA15C458E8} 5AEAr6?ϟH!N%ZɫC/X1POOUI|iJGOYEE,+H!H!-TDatabase Properties..geShow Related TableszH(O)n´H!Qű'۝U1ҡOJh%1BYE-xnBdGݶ?eڤΡ5گ죆pIޕ?q-5 >s ++R|a'*oIoo wgbc!2"1E?@[?m8/=l+.R#g>;5/18ACO_SE?CTIONSEW*m 偔@.F4wC@űm1GOߙ1[n-Q81X.#5GFNlȊ죅Ϩ40zGzD 6󨘑>2q?_V? }q7!1?/Q/c/u///G?///mE ?Of8?pn7!$g[Hk&oDooZoUFKԜmlwg7!);MGm`r5-{1m=Do<(Շe7h;PK ID U1 VALUE ORDER_+NAME I1 HIDDEN{ 1t|Vz5kbzw1?U3>'vRS'bDZDZ 1 1 QQBݡ_qA|ELJcecOa -aխEq@_‹̙IġkxllbvŶہ71I4NHD:RQgAgaJOVMݡOJeEAOU@# @@z@@U?@f_vP?BJ[ RI\ Q_87φAaH1 ne\n;lݡ1AMD[cw ' rI00%;aa{ݡݡ\\b.._2I1+jBjAjA[1H2Vq{qGܪ_5DZad`&{28D61403-BB57-439ED1-1BB07F2E11B2}jEq(x${0/Sr6R?{14(WC.P n}՚ŕ/C_1!a %A-(TDatabase Properties...Y4{aShow Related Tables((3\cAf1zIQS/1EamQ!$V)1mU%aIQhK%SIB?dݶМgfft򛵁e!hܙMa!hzCqEc@p1??IoؿdqEu]yy)!\aDQaʈ2CV{%l~E^s\eĎ{DUE&y}$HPGACL?Voho?? dQq!%}$HM(9N#ݡjfV:N6}dշ% OOHy@ _cv(_:Wy40zGz%cv;(w:`2J"q?N`Vj? `R a'oo(:^p*uf a%$¯1U<'aaUſ@׿DP})R6dvψϚ xAя!wPDU K NAME VALUE!y///-5a/8pd('VUC NpevH_ a93b-%43536P,9bDP!3EWEa'9qoBado;ogW?`QuA)_)\y#uA__= >DREEx򉡉uBq!q![odAo ew+vDU/UP}xuE@V hd2`vkSؖ2!CE7+Av@wuGh1.D="c6܅DQ 7Ҭ]uA[eU@i%@@lwR@@۪ߨM?@M?4@3歛5ʟ  %B G/C@Qx^SpTw1aFNs @)B֤J(I we"[5S+T3VQ r]aQ]!!%T@[["+T``1TRqe|B((wE tC"`&{A6251168-BA10-4AE0-960F-94060DFEAEB5}}y(@Ѵ{rX?,?OQ2Q%h%J UO?S1Cs!u3*qtp5uw1-xoCTDatabase Propert?ies...)Ё Show Related Tablesds//xϜ3TOQI6Q49#8=+%T5xattMqyxr?}xY50Bdݶ8˖:2KD ?AKO]CoD/Z;5YQe_]Q qXjhFvfÕv2I2bVhDӤ`3k?yq,>^Tfσ6&ˏu<Õ.,`tςvp~AXO&8ȏ4Aq{uMt \ܼ:K5P<ꗁ6Q2Q~4Mq 5<9~߽T5qtt+%u9wNEݟ ]C(.0zGzl Xxx!\2rq?,f+?ppAT`¿Կ ϸ.@RN|A2uAqeqUF ]{  -d0BTfN|1[4=' aLPK ID FK1,U1 SECTION_VALUE # ORDER$ NAME I1 H?IDDENdql~}ugf?x@"!"UC޿2@vȡ?|:A aBE@A3b`,B42F52AMr6Q+C9b14Uc?O O2ODOVK!AdY:_}e6VU?C```@EQl*RycEP= dR>dDQDQqq|UUEAqAqZAЯ⠀ݥNvgUk0uhks|aŀoe|oeE{@gUbڨf;udkRdO_*((1WBDP7XUd@B;KmL vq!#vq@߾ 4'vq)|}9%EZAQ1U@Uv0 @@,:@#j?@~X;(]jυySRX9 ȡ(a(@a(C%7% |.(@P_Q!rpÐɒ&IxSvc!4U.Fm;Qrp"bb9zoe(/*UA&ZA4PP{@UqQ! _ԭá]SQqqR%Raa"ZA ZArAAb+C_Q_QR1]zcdq{b%lAxl'6vgDQPsUBrl`&{9E80CAD6-D16D-43FF-A40D-1EE88DA9D?F81}|aoxa((Vj'?r$?+vqqXсudʀmC 荩߁!#R//>9eR:0?B5rq߂ьń-ȐTDatabase Properties..USShow Related Tablesc8rȥ?NqQ7*uaSLdaI%؅΀Bdݶ-Ult`el~e5ǯ>FP__WoibhFuoef%OA߄_}BI1_U_gW5(.aea / /2(O-̂Ş%,.ewn!8eh~(ACL߈*MtmNФJ?pZle~~a$0z_Gz&mq2|qc?OF ?P@a%p*u#5 U!$6HZl/]5?V /^ $W~[W5_S_Bi_UFI l]\gW 8o@Jo\oV]Aoo*uaaAAB!!w23TVO? 5c? /њ+=` =Eo @;w5kQ4Tf*JJfƍ7!%$'2C P>yA!$,1W5? =jJ7,A*j:%o59w1?U@z!@@lwR@RM?@M3M*;2E5?w~qQA 1 A;ETO1C)4H qRt8b we|wPw18!{wʼnN )ݓp,A  w% $Y@w1w188+?$B%1Y$424141v%"2oM#߆Es;T`&{F8C29C6E-6A5D-4CC2-96A8-B037F32C2A53}uM(_RW^?CrB?WN8A UF ]{ bKK׋e r`8eS|`߲3]N|1K%~!reL7PK ID FK1,U1 SECTION_VALUE ORDER NAME I1 HIDDEN1.???MCEKq&%OqIHtI1=@RRQqUUumuv&q,  aouu3b 4526p}9bUuEreocZl~ sqBQcbadw%?` aQW!oEW"ly#Q@ =g$b>t$rb.RR.@peK;u'b2;(re;AC!.*?<5U0?<5Un;@:7"8xf;B$k$SeGGK֠7!$%”3%P D"7/*q"?_Q\TaBVAqUraiGW$!˳=ֿ"'QU@X,*@@vn!@T?@M(P"S)U׫.v.2S! ّSV_ Uc ?d/ap !,x0 `bIE8aYv#6Ae[7IUuvG5ّcvdcR /ma(-A9#$! A Av%tX""1"1V2ݑݑ"2+Ht.$"1bt066b*u‘n1"VL<8"<'X[T2ʴ`&{78C7B96C-B085-4208-A010-2EDE77130BA5}C!0L?!(S^A[`VAgr ?OVA5!5”lxB.w +(uWoI[X51yT_ۀ_{{-STDatabase Properties..xQv9#Show Related Tables??@d;sQbDUBsI걊M5"1E3Qۄ13"1Ox!WB?dݶbǨEZSRvTyQ"5_STͷEomKq< C/U//&89-*TKwIϳ?d:ĆC;OB7"1h~߭&R*ӓŊ"5|D5AXO_?GROUPS矍/ !;D3@p@QmqEbD1qwEIpFݱE?!dۄw"15I%!WDuf_qD5SX0zGzÉ;rh߈RA2q??׀U!n0a);M_qߧ߹d%߯Fa3G0UF? `r[['˵/#/"0H/c//2+p////C/N?|1[$T7q>@PK ID FK1,I2 PARENT_0I1 LFTARGT NAME I3 VALUEˁSqӏ LTv.sORYdv[(ve}w$b3bpJCR4IV5IQ6BqLS9bT+_=_O_a_sYveTd8____vVXEv^oc abe`o1oCoySpcd_ooooouS|DAasOBd0϶\Bm$u?:s`IDZӿGJyì#^=r11qq;;a7ITDżuDTR&HZ&Z@XтAf;k΄qIHIHҭw[&vp7BW|Q-GKv\7%ADPADCt ,V@4:GHI]P9aU@.ɰj%@@{L"@"3~wC"KâreUA' ᠤC'9AHtDMMD*1KF"Řs8AxBap̹ImOQBboze]H9OKJ UuKFapA'^Ôu&ƄTA (Fѓ4))rttvEXBaaUqUq@7qqB1@@@:3БXEj@FqԐuX`&{7BBA1A50-F3EB-401E-A7CC-1C2CA2067589}!@j(UHЪtr?tɁYP{@,u-AwզKO]OITJOEl;A@AA*M==-6TDatabase Properties..auѓShow Related TablesH6 Y#o]Ne֕bﳭ?ϥ*Oq5NIbM\DBd͏ݶ7guuҳxYbp@ `5S>>݂TEpasӏxϖy#iqN?QBy3%>*K?8b;*9AXO_GROUPS_MAP6//l~}YɁ1YA9NLM%Рۉ AOqp_A=ϥaN,u?Ɨ=J\nf nbҳD0z/Gz"3nFч.p02q?of:?& !yАYewω^1Af?x?????nO??O}e2Oo8_O~^1A$twwUFd%FV^ |3^1% }O$A6HZlۏJeҹ_aFCK,FK1 ACL_ID 2 B q"4FX!}$EAZI֥AOUx@^%vc]*q ar33bFJ456] 9b%ʹ2߿'9רVϩzόϰ!ơOO/Ѣd?3?E7@{d? #`F!*qԱ"/ɪԵI)y#*q`/$r/Q=xs>ѳђrwwbqq*rQQR/# a/@%esյ$t@P2u;MeO*u(OUkPTdvtti7QԵ%T>p2QCW1aTơҡƣTs_]zWԱ_ZԸ^Q_U@N&d2 @@vn[!@)Tvh v3+`<=lMbuL2r{~a Fѓ  Prae~ocè ƭϤ'x K  "ͦXPAFeߨͪ%bpͦQA*QQPK ID FK1,I2 PARENT_ I1 LFTRGT NAME I3 ?VALUEA OO_]]Ue6?_cXj51uum N[9Ç%®wA23b@JT4Z5ZB6O1]9b*<N`r%*u50iv9Q|#*a a"% 0/B/T/t$/////.Ur`d0я?qmm55?K3`Z@qaqX qya"4o@=D211 bbסס!HZڱew͵W5weTc7[%@q Q Y_kUe7_kUe[@iWBtf;TkD??ZZҾ7607סSE fOL2iR^U aU1=ggQ4KaY Taह!qU@{@@GR@\ wn[Գ\u f12n75 ,T8JþR4U ^;AP$\Xy3SIӡSE{!0A3V}bbbzenJ\ @15\!0-o@ڃ5(Ae! 1O/WMSE:a:a2%jBi!!bf1f1aa 11SB1QQQADՕ㡝QRi{\XQ\W 1@EiR`&{2BA42A98-07CC-4C5A-AE8D-2BBBEDF_B2} QQ{_Qi amr?oaAejd*+ZL+>~\n eU QsB H1 DD@sTDatabase PropertieGs..{!5SShow Related Tables_o=#?nAdUe mFUeq1jaUvL1 ?ݑsUT@ACL_SECTIONS=sυ=`A8jd)XD7< qdjaΦeui ue!U3|WD}Qcu&.@ VsUsx0zGz%)uȈ5a:2עq?/&V#?-AP`Ul~emuM%9 /=Ff>eH$y7 y7???UFKM ::2|A|A911/2{%l{ՠr5¯ԥ9ՠԥ/5@ҧ̙I kkhh'F8Na7*ynUi>-B@$!71aac71nxǮsq4gli9syU@.%@@3xǙ-"@Tx?@K%uT+xC+R"5h\ aC$ џ%/AhԊa,8X%ahҊfla+174 B?T7a2]0f3ehojg0f!ؐAM[/c摑a! ѓ!%bwρρ^2UU1+srsqsqH)*Jqe7vl5:Ni8w4`&{3A96663D-BAE0-449D-A8A4-E5C31337C8E1}rsun(h$7103r"?!71Uw*=f:ຝD7a oo4yHz&8uh,4uAuABv-^TDatabase Properties..71\ѓShow Related TablesYxhze,{c/1f62 ɗ1S-͹/5I1Ɂ 1THSBdݶG&:B.Ѻ:Lї5^߆uv~T۟MMUqWkI%CI'OK]D3Q%R=ܡܥ \AoA+o=h_ZmV[l^%^S\E7n[E_yaܥӴAXO_MAPVOhO|qӴlC$IWnfAsqaɁ:.oe)6asu]IPO@//(v}~ ?@u(?:7ܥL?LӅbd0zGz%CV;臨:@2Jq?RdVJ?@6JтA'aOO__(_:__^_p__ou_y_QA$t-%;UFd%FV^}ffA5 .(S•n6aͯ߯N2#rLjUfĶPD5: kPK,FK1 ACL_ID 2 SECTION_VAL3UE  AzpDe0'aVoU'`wn pEvsK9|Ku! aIq3b45јb609b?ZԬ(l~ߢߴ\6+=O3hZPl~+=OaEAK'o9oqOVdOHOGS1ctmd?`1u!Y1?NY1=y 3u!??=>$RR u"aaIrOTO Eu D@뵸T1 u%Iu~/Fᘴ ƃk#tȖ?fPR7adYAeEr[K1HadV1eom7w[4ADŽ!O [Y}IqqiU@<@@{L"@"?@3~Cs"J5߱{r+&\q @6Cq H%?KTĠ, @BREFxI&? Ɏed@πRĵ&ՀRIqB3aeS&sTza~SB[ ]Q q!!&%T_IqIq"AAmm11[[[R`eba_O(ƄR`&{240A0EC5-6978-4D96-A93F-3349C39CD2A5}![(\1U@Uh5FuU$FЖ޿BvEY>OP@ WaBE@NA3b`B4F5Ar6a>C9bA?_DĘqOOOOOOi8O;O __0_B_T^@]X1FX߉c2d;oſ׷e We?S``fA[:fE۩yH`= tb>t\EaEa$  Ē񍒫ѫh+5GwweG@ww\|}q$uĕ{!vrs{rsh{vtke_o*HH2gCfE`7fEԧ} E50`Zj4U4IFd1X3[\ ;fAI fHwhU@ `*@@D"H$@?M?@œ̕$磴bi18  :" X,vAU8`od1Xa!s_6e хcwsL1eq8M?_:?e_6h5`6`e"!5)ah1 ohWmfAb%bqql2hhQQr$`a`afBz"z!z!zŶҷ|rl5u@|6ww$Eater`&{A953DFD5-C809-4488-938B-4EB0A8A84747}}qz%yq1l5Ϟr6{?,E(тS ȈDNGGA1_?q?9u:?5s{,!ѥjХ-veTDatabase Properties.."eShow Related Tables8svO^)E,$8qT6vCĚ a  S3b"4(5(Z26ͱ!9bݔXeK(:L  ځ//7dO0lsDL?`V;b݊ﲆ Ay5;ذ=> Xb::pgagaBKQKQ;77PDElkXer.E;@K_f;kkHĨ߈߈'4f8:7N1Z43 N;H>(o%q17ځ1Xa?!^hѼA5!8ЇLځ!YEP! $Lqj4Fc @@t)H@@M&d2?@0?oM6@kwu`u`눲b:SP@?@MY2 ]cV`ufezp }D A {ݢB EO@Q1CBEEMwwԤ#ஓlwЮ tN+ذzs lѠ.B br  ќ83E<4?@` tiroK&Yy3ÁhXa(Ї37Xe= ,D&8AR!T/7/t؃/.N1Z4*ybkDx4zC~du@w ݊ /h԰ ѣ11-4wXa5B/4BUUڂϡϡ B14K~$~34I44 5Dx6D03YY%8D߱9ED:RDII?2dd'2BLŮ:԰i8bT`&{62792A87-6BAC-449D-BBF0-78349A7A7EA6}r7غDcOQ_>ߗ[püoTP-DT!kI_nš\_x~fk ol3]fEm6B_kPB_\X]Bo\TӀХ!x1EQhѦqӀq䃅1⅄A԰Database Properties..q`0 Wx=W?0ځd߈U1@Dd_U宏, /vҪ(1*Ak,AI?lÒ%So+?AHʜsK/~GT?*ʜw՟4mʘias߅1=a'2ذ :YY>3EWi{߿2)@@نa@@Vp2?@jp?e uIC`u ҭ;(@@1y+@@ `*@@.6 ZϘH8Q]w¢]vl 1CUgyM`1 w +% /AEU@¨j|bt ///B//f/x///5ᘰJ\LA/ ??.?@?R?d?v????????OO*O_P_b_+X_O_`___j#< /)oc?u2,Ą?lo~kTQMEV?oh/o vuoտ|O5z^uENwzzݎ|?AHP}h_OQ oF2XLU /1CUg{oΏ(1 (@@A,@@ ri@M&dW2?de8HQfIip?EC'gz^Ww9z0񔯦ʯܯ$6K] 񼳵VrֳFI Ǐن (QUgQd'9Kz1OCOUOV H+R~v߈E.NDc! bߵ!3EWi{ /AS2EF445CD-94EkEC8-A544-63F4C3210C62:%^ImK Ҕ߸0d55 "4fxI(I//+/=/O&&7b"5?G?g//,e/If?0?T?9fS?;`m?;,"O؟XOjOA5MοAk,O6ZSo+?O6ZKԿCA.$_6Z %a.f_x_W___];_W1o2?'o9oKo]oooojOc<@@AN@@ҭ/f*+?45ool- Bu`uS0ey"$iɷ`<+h@@z^@@p8NpzOKv,Qs1[yLFarW%7I[m! 1׃VRV_of #o5o9U@?dЩ7^pd ǟSe!3Wiïդ~5'Li(ep"4FXj|Ŀֿ 0BTfxϊϜ7E0E11BB-2735-4CF6-91BC-692865C670B3&8Jznߞ¯msm??syk} [~1Eh1>Pbt!s!+=yg5Wg!>:L^p #PbtΏ(:L^pʟܙF282CF0A-31D0-4809-BDBdpA38806299A1 @~/ASewѯ,޲ rVAe٢Ũ,~@䂿ſv㿔b+kXӝk~ϐϢ 2DV߬@&??Q<397? uG OO-O?OxnO%7I϶OLa6_OZ__:/a_D_p/h_zZNI______oo'o9oKo]ooooooooooo#5GyB10FE780-E21D-4E^p8E19-C349B287ADC0w._R_=v_as_ԋ "aҍ_ m(_[Zlooß} 1CV);[1¯@$HZ0 dG~햏˿]8<͏ߏL^A Ɩ?ASo+?ςk,Пd8gS?AǙەUZlKߢߴ>&-?Qcu86%@@M@#7@@(Z@ӿ˴ӵÿl@u0`uG Vj@@?ɰ¢@@|>BTkxϮ*+=Oas ))[2G,ӷRdvXGY/(~/2//V/y\//+rDMk+hT//??(?:?L?^?p????????OO$O6OHOZOlO~OIAB848DA1-72B@D44-BA90-6B862B68548OOOO__+_oO_a_/_/_g-DT! @"_/_moo3oEfm=_oqo/!r8oo?oh9O \M2DVhzv r1| 8YִKS[u̾.@RdBߤ6!uҲ~>4% `býԗϒj`!#5GYk}ߏ߽߳g22I2DV `rmU@IJj(-ooo?QcϨ2O +j 2DVhz .@Rdv8CAAADC1-32C3-4F8D-9393-0D81ACAEA/O$/6/H/Z/l/~////??%2??V?tVr?? ??b?0O-FQDOOrOOOOOFWB___"_[SIC_s/O٘__7/_#kIo/M?ToA?M+_?oL^powSo+?AłdOzk,?A"EZN_zEٶX͏FſOz[EFN+=Oġcġ_ȏڏ28ALE(@@04ϼ @H8?Td\e ``uP~4ٺp)—@Ǵ'@@p8ː?o {Hޒġ3Rޒ|Rdx`vݔwZl~ƯدQ&rrZr Р U@8?-1C9)2D??zόϞϰ -?@R .Oqi{ߍ߱ /ASew1A2EC30B-849221-BFB8-AD0F0171EDECIYm?3, Vdz <ޛa'n?һӼ,>nbt/&#Q("![U/ g//P_d%% Ve////8 /?i0r?;]SA?n?Fz$'˿Aa7?w*?;O:J?A`}OJ0'ݿAhaӪh?/OJH1 $R-?H/__7_I_[_m]`_!_?____ oo.o@`Mb"@@D !@@h4F?@t:N?45woooog~߽0L0@@BP($@@nv"oo_O!3OWv!dyLFtG*Pbt=OΟb(vL",rZFJԖVkͯ߯'9K]oɿۿ#5GY722C84E2-A95A-4A46-ACD4-96569ABBODC6BϽϾ|*Nߩr߄ׯ߲*۫|/`(٭J-\ ۿ!-P! 1CUgy 5 >"4FX< ^{ϚdפӢ78 ohz 0 ?AmɁ?q*/ewS?l/~/Ϣ///-q/$q ?-???Q?c?u??:!(@@ߦw1m0=@M&d2?*E2??U3O>/%e) I& WOiOOOEqI4'__)_;_M___q___^" __q=c&6&63"Y&?8?/gT?f?]o>SGoooo^!Oo3EW{OOOxw6Ga&8J\nȏڏ"4FXj|ę15DE77C3-3600-47C9-BDFF-D0F7D7E3F361*/+&q8)T T>"tN/`/r///////. >a9qц3f3fM4fxx7?m :P???Oլ&O8OĿֿnOOOO_OOO!/3/4_FTHFѬ ]_o_________o#o5oGoYoko}oooooooo z61AECAA-93.p4648-8E04-D1AC0CEAD41F=OasO_3_'`_Tfޏd[BOM䌉AR B֏_ 2boVhqѶomqѯӟ ۮI[m7ySsإkIϯsU#] ~f2ǏAz$'?A2bPw/xAq?A2p- U-Q F+=Oa݀uTђ"qi%@@PRT*>&d2?@P+ٿk6򲛽>󱰢? ` Q'Kxǎ 0)AEW ۢ֍ sIǽ!3EHt+=OP///B%L/xp/(OŪZLLFkLН////// ??-???Q?c?u????????OO)O;OMI5AAAC54e@C92fAC-A96B-BAEA1F4CA8D0}OoOOOOOO4/_X/C_|/g_Eo_/_'o_ 1tV_?oL53=>oPo/to?of?o"Mo%7Iv \r/AasƶO̶'OKƶ24MO{_ʺ_я_ʲ+_[3So+?Ałd mjk,AПenܟjǖey?XFſje"v[~`rQO̭?B4?,!3EWi{0t z@@#vq@@n'6mĵlӂķ׼u `uȍp U@z^Lj!ڮLJI[pϑϦ?r>ߓ 1CUgy `U!?ͦͦڒͯ߯P / mSǴXj|^\Mo_o -QcdχϚϽ]TL?^i\K .@Rdv*<N`rACF9AB5D-5415ED-8C84-5E6988B95C4B? //-/?Q/_u///?! $//Q0AJ]88!>C>/ e>????8O@8OJO\OnOOOOY_OOM _AҌ)!6_H_l_h`.k_kv卺/_kI/?:opooAɾ}h>oNz5f>oNz^o=kx7N<*OR\ @ڇ`? T! u`ukP0X?cQKPVTxf@xooҕQ Bg+=Oash1Q-vrAv+M'4mvNۿg?y??#5GϷkπ}~aϴL HB>83 (:L^p߂ߔ߸$6HZl~03F583D2-E24C-4820-B9D7-42AE3E383320,>PbpφϪ 4 ?Exf4a΃oݥ Q;0^QVhzw/ -f-/6UR/d// :8AoӉ/b d ?b*O?)?oG6!f\8?lJ6"OlJǃE?]4꫿TZOlJ@F^OOOOO]G_+W6!9_h/]_o______*0z(@@:F@@pJZp?W350D``uFe"b(lNoU@ߴ'8=lڜo)7oo ?oo?d6!r5"C%7I[m~~ y6!K V VB __=7_I_k# 6ʏ3EWiٟoßoo H_hBc"4FXj|į֯ 0BTfxҹC45F481A-AC68B6-B28B-B8E4F9E6C063&8J\nπώϲ֟+߁2@@9>Wߋ{3kϯ-6!YN|6!t(U(6IW6erϨ@j(6= >+= 8GV,*-?v,o>oAeJ? 16H c`?fsxfx//&->!:/\W/{/////// X,*@@>R\ @7P љ(641'ir5Ϡ-\tsŏB I6B*TA?S?e?߫f\̿ OT|d]4꫿ꃿ嚶Ӿ@ɏ 2DI\cry˫eّЯ(s9 %bX@@p8@@fl.б\..:6A@*O5[+Æ֘Ukщj!ŷ%L9%j,mr)>?~?r;OT;A`AI; 6O ّacMa9o=SJd~`U.ٕ RS  o9G0XV& wpeٕ<xCS OʏLpcAU,UBzJsυaϓϴ`0(@@2p4?F@@)#0%[mߑߣߵ;.(//a+=<=acT'6/Eu &+O(Z:AKy,4,S_waJOT_ &__ -?QK iMdoeO%~ @@h%jZu !s?L/^/p////*~/Pa?,?+PaR?TX܂GOkD*6aٕ }??O???O?wO {IOLvO__ oo0oSAOOO<5s_QxRX_j_pnï vVcLbt[qoooonv @@|>@@"x@@Vp!?+K/L^p 5"ϸ[ 6[ATIk*F?4u?ȏڏ=%T;끟O@OͿ-?׫0A?ǯٯ_N ƃƟ؟QMQotJ||oֿBD"H xBp@@u?9VhzόϞf$=J&%-JLTbEN%o߁ߓߥ߷w)/C**;p[#]?`J:?=O]rOzO(QpRNMOpSS l~YQm? -U@ph@@#@Fr ?F#?F.d5C%Pb駪8[d >k"`64dd$e?oo5AUQ/!?&e_UQ;/T`)MciAG95%=售8#\o7'2 oBp/////f?/7f?]"i2?l_?EQ8vQEXAROB@OJlY T@[[cOBQrlU2(U0S ?3OE3ROV_hWv_______ „BP($@@(@@\.?@@?k No`oroЄoo׳?9o>T{ԧDCas'ՏKLsϲl *ϏX1@RdvTGlYCڗkT)@;M_qB *@@p8o-o?ooo]op hͿTZPk%_Isb`&8J\nϒϤ϶dτo4߷X|C3 .@ß՜!kT!V?"4BL@@\*JR@KFdsv3?zN m!FXM!~TЗH@Ź=١/Eh4 /ASe/E,o9/ o*/*@fha8San_SEQ ~\CTIONS_OpjRwGROUPS_IDjXwjdT~qdw5;X8dat?2~t$@~~t.@2qWCAe+n__pd<_^UX?@(bz*qs0FuP`@4jT8_J_ ѸѸI<ϧP3Qð7s0tQр__ѿ___}?4pdUqo֞oNޝQ(1$E1m "Us08| ]omQu'9Kwhr\nQwHH$n۳Q[AI[mw? PZnVQE1"4w` ]nQ&w7p8nۜQA6HZl~{УV[n?'Q 3 //yX(>l6F/n'Qy/////xYKA/oڅ7Q;?/?A?S?e?Yk:????OO*OaOsOOOOOOOܦc__*_<_N_`_r__fBado}+ s*zQub^uk}}F_____oo%oFxqSqt[u|q"w[JϑRd*™y襑%@FU*JR?FM&d2?F.sƼFsڈ..u;cwUuCK?CS[_qϟ1T|q+M?M|qT݊0AQAQQSequences :['+}O?,ܿ z&iL^ۘۘ#| p'2U/ś#AbNonqmTjggUrQҙeꢦeom˱Uu״qcqE|ÂFw qEieuE8Mdvfu uq@@gߧ @@M@@8?E pFbۀAu.`u`,Ӆ0]ґ6ꤤ8ˎ Usa8Fb tN:Szs.Fb .Y  Q a_?cĊ]Et!@uB``u5f:e:zp.іA/2H2/К*_G28H?@rǯqFO tcט&yrs1ndߟ颩ߡaa>UuJVY >y\稨Uq,QəżUU8o7U@7t b?|@iRooUq(˄RX/cs1">%Y/i-Uu>.Ŝ/i)Ba2Lɚ2qm/ p`2B4ba aUr-i4 !ڒ/4DHr!4r142f24 34 "944954*64ױ7D~~%D]A9 ,DFa:9Da " !&2e2eǵUu6҅``&{AF355C66-E69E-4AD1-A9C9-F41A2DB6CAB1}˱׻D˱׻=@GLHڕOE21_HV_Efx_2ԷP-DT! Bɳ %_2Է@I*W_B_"oBXno7B_HDBoDа"ɚ$Fඒ'颪X1A{ ?Az$'? sriuƅ{|ufu6JJϡ? |ꥀugu9;$f?MxeNUqf\5J-ʱk2)kA۰Database Properties..шV&4LXπj|ώϠϲϽ>/%e)@@@ך!?`Zg 0BTfxߊ߮"߹*  ]ޘ?b٨9M?!t荒 ٷ"U@XU?$Ϳ@_GYk}CU1Cg߽ 1?QcugZ>śC//)/(0ǨM%мi/{+ `/?QP/?%?7?I?[?m?????????O!O3OEOWOiO{OK7EA84FE6-D6F5-47A0-90B7-F08318DB1008OoOO__+_=_4/a_X/_|/_VOސK#=_/_hdVbSho,o?0h ^o-iOREphpGACL VSchemaoDUU U U!U%+,-U./01U2345U6789U:;<=U>?@AUBCDEULMOPUQRSTU_bnoUpqrst4;"H$0@Y,b'@ SRG- S+E@ SKVH<( E 3T V\D( ۓ{nRD? T.TDL @T.TU1( UO"D&aU=QJf )h"Ty)U- & ' Q    .#1 VB^bTS*\Ʌ>- H*= M5$C- *EU//i!$?B %~# ƒ 0MOfDB Engineer/CMD=1DB Engineer/CMD=14\/ UG/$ VGGuideTheDocHairlineGesture FormatDatabase ModelDBNotationVisio Extended DataDBCrowsFootEntityDBHideDiscriminatorDBDependentDBHideAnnotationsvisDescriptionDBHidePKRowvisKeywordsDBHideTypesvisVersionDBHideVerticalLinesWidthDBHideHorizontalLineConnectorWhite lineArial centeredArial top leftArial topTimes centeredTimes topTimes top leftTracking TextDBShapeTypeDBEventTriggerOldPinXOldPinYManualEditsHideSetPinXSetPinYDBShapeVersionRefSheetIDRefControlDynamic ConnectorDBHideRelationshipsDBHideRelationshipTextRefShapeGuidIdentifyingDBCardinalityCrowsFootOptionalOverrideOverrideDBNo?tationEndAngleOverrideCrowsFootDX1DY1DX3DY3BeginAngleRITextRefGuidCardTextRefGuidHeightSubShapeIDPKCountDBHidePKSeperatorLineDBHideTabLine1DBHideTabLine2LineCountLineHeightRelationshipphpGACL Sche?ma 2.1RectangleVisio 13Title Block TextVisio 70Title block retroTheme MarginAntiScale ScaleTitle block classic12pt. textAutoWidthWidthIncrement12pt. text.7912pt. text.8012pt. text.81Border graduatedBorder AnnotationVisio 21Title block contemp.Notepad Fore?groundVisio 20Title block notepadVisio 01Title block elegant8pt. textRelationship.17Dynamic connector.18Border DarkBorder smallBorder Page ?NumberVisio 00Note box classic#Border Text Transparent LeftNote box contemp.Visio 10Note box tri?angles%Border Text Transparent C erNote box fileVisio 11Note box neon15 ruled columnphpGACL SchemaRow_1Row_2Row_3Row_4 tt3 sV E3\{nV E3,{nVE3( VG3İ VG3nVE3( VG3ܿVG3|W E3D WG3D{n?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrsUjUUFGUHUVWUYZ\aUcdfhlt4"H$0@Y,b'@ FjR}G-ԃxf7 E%t4 $ _j E-| 7EJ@l pjKV@ j6VH<( H<( JE 'l VEL 4l V{ * g"4FX ,h(~߹V@G9%8 F:[F1{ T ԯ<{G pL-{j&~ BX& oF${V$d' d!J)evV' ( nTh1 . 'VL2\ _ ?4 j*TD1 Al6%phpgacl-3.3.7/docs/manual.odt0100644025754300001440000034644010476664473015022 0ustar ipsousersPKf#5^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKf#5Configurations2/statusbar/PKf#5'Configurations2/accelerator/current.xmlPKPKf#5Configurations2/floater/PKf#5Configurations2/popupmenu/PKf#5Configurations2/progressbar/PKf#5Configurations2/menubar/PKf#5Configurations2/toolbar/PKf#5Configurations2/images/Bitmaps/PKf#5JyYO O -Pictures/100000000000020F000000A2B73D24A4.pngPNG  IHDR430PLTE"""___}}}EEE dZ IDATx]=R;N6쥄8\ (n[ ]`NoSwwfva/oӣ_F[Gn8 UiI8ĶP\6uPk;EwB*~RQvGMhc&RjB6kz4 |Ѐ+r"iWK%[Ѭ} ԺNcz lnm[h  8ilhB\vO@"m+!# RvmYQL0P&8 @s92p-$@EթxՃ`n^#;n=5S 5yMb U`h'A P =Q!hM#G>=Kцh]w;hR $R3W0 $ZݙF;8)%E@ł089Nd:Krb u !cXqRl!a+?)èHz%YҶmR`e4]+ qڞYVNÜthMǦf̃ C"II[bG@M>g+ר@ 1``veH_@@"! uPiXޢxx)[9N&# tx0#*d-+)A~SV\ մLSX]1ҡp "` TQ52̠\'n. ב1@B:2&@銀ؘte @ΙkĦ˶YQN 5"ah L(g1JR 26 IGQTy P줒J*ר@Tؖ +_eٖTl I 9*aW>bcҥhIW4t @%]1.mvJ*\+E9dҥ;b)*\SKyrڏie5޽Fԫ5*t{ SG, qMc<* K'Ië:{z sBy 5.#JtD%]u@@\>(3E^OD:8yGOpQ/xO©//Cr//D" onޘ(#v{5q,ml1@Jt' *8xk3F{FGG>::- ;x;NfyxgO( ᓓ€2c388c| AiEU.l_m?K|\rAs?|"6hm"m*y,y1'=>5M&>< I=Sbx0.5{ 3xv~ qD\ #M^(barNݴDMZ<.E*1; =% ;0L-6 F MIDL.E@\sga65P+x+G+#/E\Rh~Q pN ]lWĕ]J쀠1*g39"1VQF bO#ZcYFt4&d1%JbHwFM!3dpQ˥=^J9<(bf4}9%әQ5@̦"5IENDB`PKf#5&;;-Pictures/1000000000000277000000CC676F8C98.pngPNG  IHDRw: IDATx{|T09%r%7r!  @hQD"Е*Z\|ž]G_jkپh®K`᣶UiVTv&\ $!r\g9NOr-I~sgΜ<lC0ghU}Se.Wz?ksuI2F(&a6(E fcGL( \tUUUvݸ}5z,˶_~9++d`kb-v{JJJ__ߥK\RXXp/ݾk׮~oV!qƾ{JSRR0 pPZZ>22b23 sQƸJKVM!+"ml)E!7STDekP)dy|EUU0(ve9eɓoeL222BfճZ6y6i62 ">zQu r9s<U/]}vмy|@z%K̛OgеfvܝQZZJc [G?MQhC t"+&ƥ.2귏GORuuu1&yd;d,3\jz<:t=.7xڵk,!IΜ9Cޕeg I/^|wv-^~ȑ;w.`}4%%j$bGFFN:*,,,))"5[|;nsssbeVߚb22 CٵNYU>:&¿}L Ϙ' 6o rdZ/^@ uww;v>E wE DMww3g; /DE4m۶}7&>|?i^^(!4k֬軁~_7Μ9v,+ $IzgY .((K#{GSFx;XY5S9 +UU[29'z|~"DP2`,oz0(׻e˖3ۿk׮:u*Ƹʕ+&tر/rŊ'%%eΝ{5cww0̙3gHHel6e#>Vܹs:Bh޼ywu8;;{ҤI}}}]]]Nӧ~/\o5i)7oVEj~P93_Ju{qmnH`j:e[$(2B{ˎN4׏#ᰞV Mg។ψgJIIٽ{b+**0 rN8Ί O:Ejxs禦?NsqmxifWyeٳg{Y2|!DI:e+++X?Oδdʕp1(꥗^&m4M1v:yyy}}}()kyYOZ~TUei\)3\toJb2ꐇ:mEQ'O&1LO~p8|*v}[n%WnGc_^~O>Iˣic<44DFh{aeY~7l6ll({CkmŤ{XUUaE9s>)(NI&n]UUEI *BKL%e6&ז{oGڭTyB jF}LYV)$+aTpx1^uAe-!nVe˖Dr:$  i6w^jc1EEEdÇɋyyy˗/w\R+--UeٖK.!f͚t:NY[1Ƌ-҇XVKQ6[d[[M4V_GqBwVWW|h0%hVx Y;B(Ū]JzaFb?3B9(cFv"C&mEY3I$0n/&`U dL@͉3g]^PqqqFFB<I;lj6* B1c݈#Y PpgL4DidGAȒ!,[nڴx0sL)KB(---`}NdFQӧOkcu_^^F]^^^TTSTUU~iSظsNŋ~(/_$q][[[NYseN,ʨ ]MÒZ-_Þfi,̵yB(3O Q9O61n18ׁB>DlR4#XV=c|Oݘp4V0YAڐrrrd`]Μ9S$}jYYYY`>mєTⰨeӔ*HxFxOYd--\c'*w[X!sy6QbF? qnnnnn}nMtmd/MڎbFZ}4G˲#Yܧ9ZS6///j6Eke,Km6Q}Q}ׯ\2` v/xTUmoo'UOeϜ9C%[,ŋ Ϥ!xCJa0BeoFdJ)..$L.5ki}Xd̔[^^β?%I:~8K.%_Q?u`-6IVX1}taŋov(YYY}}}/_:u*EQZx NqźX[?иŘX("Fkz)kG e΅tXM+wպ*ц( An?QsNJKK{UU%Y߆p{A\XX8ydEQ]d%`6n8cƌ'O-ݻwm4KKKy|жmTU%m!G2eJGG(]]](VVV :::~iӦi3{(B5`z-.i4HQ ry(TpeԖO:hD\f HempVVԺBegg?EEE^a5961 amAV ["dYN??d` 33sժU/rFF(JGGB+--5)S---4Mst/33L v}ƌd>f̘%ICC~7Ea5ŢV拒lWI3E+23D},}uP(jddDYjٲpv[vvACʚ2eJOjY9}4ـe>&M*++[:絇^y~󟷵i(..A[[[[[[kjjj\.ɂ8nڵs!EAJeY~V^rfKKK!55{b?ޑNj-*+iSAߪ4aQ^?68ڦHk3S$Qbeَ-Vۿfc0%!!??hMd:'^iӴYx/׽qĿ4}"dUŠ(^\՟$I7{=BZƙ)"1 ^K f)Bמ$la)r#p](rqQv;k۵_Pq2qJ;y?Ɉ(%%%d.~R7%h &W Y}E$AQY%+ӑwg̘Ah13)zcs>&7jʲsDL_jI{]ª[.8`3K!Ӕ*+xQϟ$c4e=χ~8wիW[,m7~3bZB/^t\yyyfd:[4}|G)fN&r yg >أHUpSkFx|OEZ=r"fiT.߳MfP|F9sI8LFK!''gdd@i)=`9OF$m޼MGlvl[oE'KF0!H}W(n޼|K,!n~~>[,^r'X|OTV+MӤoCEQ,RZ$(0b!胙jN&XlOZIHM!dXAEp?.i2(,|}B/Ƹ鑑?c\[[kZ%Ijmme,Ų!r$I˻Xy^Q9sL0dp*fLav2S@Ka{GSz);"zGsuQ` eIwqs78p -IWА@i8=^yyҷO,cǎ;wEUWWϚ5^裏m۶uֳg{ ެfIDAT}_=zhqqիl2eʔ$gZ?#l߾8ꫯ}G7o/)bGoߞs͛7?siiiC?/wQSS駟]ToAB$I/BOOOffݻ_s=˗/ooo߷oot~{ӦMXf޽{;::};::ӵ Ymjj*//OMMu \|wI:wkkfۿ9s?s޼yZ9CHs8T7kgUEEFvVeq4BW1B B{|yFB" ~v*Md4I$2t[ByEҿk3e-'Ov8ѣ y6fWJt2:` `ifKBUE }l[Cڑܽx$ݡt 2_FZ׾xf8p ԿmO>]BFa&n)!K[_?hv,J̘۠8gt%xd^q\NN#쳥K<_XXNblٲ^zi_VUU{SN}Wz)..lll馛B===uuu%%% +W$cK0ٳgnwttL>W^_n>g̿hCZ( 'nkn HWc% _q˂i.^|96uY擊A@(P t"K,,ʲLsy8n̙/qw-ȲrO>{l2(6YxժU,_wu555!Qwq6I!K,AcID&(j˖-Y#뿣>M<NȉIiӲT`i aQ+_IA]WehpU%# EИh/`߱؆n"&koLoҎA̗4}^4בǙ0@tϋyUqideTE{uYQb?֯x[̧UʧL=XP`PV>4yL 60s Eob`SQqAPCfЏ2P=ߣk+ [oX,GNJF{ruww_tn[!w^(8/y?9|m`:$q\= Ff| &'yQV1(maTG-:Pq5qJ;y4IWX~YI8kmmmoo'v{iiiyyn'FJ1/yOrO `l(;<<Ν;bD9(  ;wmժU1qu"%)CPS9s̞=?kTU%R䌌9DKS'`F(ֶrJA8.Y^_Ŷ6rH1/yOrO Q*v98DL=.aO$&OG$|$)rs4| Fc%I"ݴ[ƍA\ ބ{t,+V먝3-a`%kט+>šW+ vjEYEQLlx!^1'BQ9x/=j <5@B\ &Qf|T<;A-?SsSgOzZADLfE}2opO?,Wfs1l'h$ fna|OQڠ1*.1)atWV`t1â{Pk+qNd k0YQ&K$5@%b]e=1& kO*4ZBl}-zb5`j/`dcՖ6`)$ `|3eF5'\lxF!! u+(fw3bؖ~b'i163nj_m3r4'lGi;\\E8e5`~J5$m`v h1X$jo֊+Ey^ΧY.ҥKv}ݺu{&㉄ݎ! 慱ZH@PQ6@d 0.ka>㸡[1Q@@,/!|Ijނ,/ex( DY ^ vG>n,/exe1S6d @@чFs?D2TWWf&S 80f&Ee!H4k555fmpi?fuuuff&S}3eQ,ޱm!@"HJ&&2 >6@e ` 7B]@@% N>@6v`USbc xh߲؞/I- QXΰ$ff4'tSQ:rq3Рil9.uM M 4MQ^ +,9烁c6ͩ(h}>oB,Ev=r1`|O@sefb)7ط0h_H*x sj Wgm_R9D&1a6U_!}g>`Lទq.;~?ڀqCG@ZR\s^)q\8KcH߯$j7 uL?<h1NR )jTD7n2FE0e3j&@@iLX-|& >KE[;ѣlkr SKvP 1Cqˎ/ۿ(KD)pEM4 Xq 9Y nzIgg|"4@BqRpE%p?A3dWH(!ElWP|=#&L.gap"!! 1Pp$hn \-nY,p'P ,P4~fMG(m}F|. Kl}}} `9n@ &ڷ,KR @$zh0^{5CD` Q!TWW7Y%555Sh/3ٻ]m>D'w?-Gٚw$$`!vV tc0XUI C,EI%31['ffTɒ|_\{K{*\Ąq諸O 1aF3a81$<7[X #3O %؅4UyA z!v]k4vw9eJ_OZKQaͨVq7:, B,EaAvz{$cbG. ƊgĖqQ !_8 E3!f߭p؂ 8FeOnwgb1uYndFX 8FpsI68n\T؆XY >cV=q Fܷ eاO;OhbTA|>p32OFBS.8ivY 3UHEQֲ̥\^<]1_SVRW7,xBȣvGxSQR5%?w{*v R[=OUEiVœ[>&oEEX*ۖ Uup/ڻmx{R G[ѭ*=?'U"dEK|W=T2bBx lsxf^Hj-^^dODݒ z^yO O434I | o]w2 b1C ًr/k+ t2S?d,<abo+{O,{dN,vN<;3H=[ ssiA^^}ܘUg>R\l9m "Wz]PdԳ[V5:_qF9iOu8d O:Lor]s)qQ%Rg. +I2r&9<2OS{R^<w7$gQWẴS)czeͼ>m)IuhtE,6gI|'U,di(1M{xᶉ5k孃4`6l-;Nl8Gfg.[Z>5S|e-kYZ#Нr:Xqb;Rti{pԝ'%F;gsxq3y6?&E$ܒPPIaZ#M(9B j VMm.Ivmcj},T<6YtSЛiyy2ܺ+9"{̓PWD f2f#Wքf$'E5TrGvrVH|d4'Gxic-# x96Mvdn&f>Aec- -`uf6I!Cʺ '#|PfƯdWܔ+ s^Zֲ%a]˲<2"w9ҤI <&%x8N]X)D1<%ޕ=>iɮA ]yr|LWA7#cgy|R#V$nT:~kU{:~>'o|]?u(Rv6c>/,Ld8IENDB`PKf#5q`ٻee-Pictures/1000000000000279000002454B803670.pngPNG  IHDRyE.v IDATxy?O}s b* Qdg\/nW5&&lv]&jMbDPɢr380WU#>|SO}Sϧ>)Fea.@58p`'NW/Gѣ.]:g[Do~'N|-++K&,#R7|sk֬)--U-> \uUtMQd2t"LJD,{7ƎO~e߿P~믿~رmYIDQ$ Z$I ӧo۶m„ ~?Qb/2Ih[VEb-%DҦm,FXa---FӧOWTT$ B(x<c-".!a!(F"X,6<Ds};ߡvժU_|qK$===Ǐ'̘12ƓҙanǑ !䲙1I eBC(_}Qxh!$6X3*Sg<ϟ={ĉϖP(d=sBMfc:=zaC;w. ;wٳ&L0\+ =c}}}nl<OČb,;vlpp2w\:pR&^nSU*@?Z2|݂͍F6X !Je %x٘^<<ϟ8qB>miiaYVz1kꃜ߿? \sMuuE6 /"![n9wSO=%bwww}}a,O${!5y7 ! ô(kII±$ a-U%xN'PJDf _ P|.,P(Ѩ.o6}&N844DgU `0Aba.󥥥DBX}뭷̙2ٳ=!d~BdY>uԒ%K ˦s7N;+++1 C}˒K!ue3&%B#k5]v9n ; 7JBg2J D"[nݹsgGG]1555˖-[f "黒$w}=K.C5ih߳g֭[?n6---miihLefhhw!L0^E[7n_ !uuu#Gv |>یVh%^%fwG`V JW#]Ӂ|ezTk5шXchL@SN}[R{8q _,,˞?L&:x޽{~:ΈeYx~ں{ѣ O>M={vYYYG_W_%r-f uuu>O'O0={>4Ο?_yV,!4 s̪6աBȳoŘ%I'-)qf_Y!ex[[ѣGg֬Y3ea:::z{{mnh޽W^DJKKlSOY>'mܹg0|f$X,0?#Bϟ 7 1 3f̘ѣG;w̙3ϟ///״8Y?. Ĥ~B~,gO+}tI'wMJ$%BOUM#Z4=M =^Z(\O<hnj3m4u燇z-%>}zڴiǽ;0̼y7۷O8A)//?~s{zz*++ձV껤jjj&OGeLxNH}=ái>^ rA7)At©\=6vp5ߪa" /@8qC=4vXu{a!eqG$TWW?())44COi[. cmYYOōe3=Cܞʖ!gee©\>8J.t9HL_qfBD/;v|Z0ruEE iӦH$ffҤIt\kF?+֏y^ɭ!L|G={2{rb-a}}}>dEa.bu +,7q:OXsNJꋵs5g!AVZᲙgW!'Hp# S}K9O+ HT`5 <+nڔU^Jx3grF}2g^_Bɓ+++ !4 c|$IRQ?~,}}} JKKm;uwiDQ.mj.֊X^^H󥹪ʲ$I<@4UK>yd={QOu?p6kg͚e8ZL2CɫDz"A_raC<`^>6NbYЋ/p_%fOGzt><-qyvOKMJ }۴۵,_E,\5jTcc>lBZZZ~2<|h,g>884g̘Q]]-rwwX(JVk?/B{zWɴCCݾO s7CV4;z*,W,% ٌꪪ rە0k,z=BȨQ v._BHSSdwUFTFweFbJuccI4BY5wBw}WU5 8p`˖-K.䦛n4ʓdYYYEEE__{700PUUExŹXA",ǂD*\>(J ‰^.uq ³򶃡m !Q1>QL;]Zp!}avˬX?SE1l  lf$IDBU:uرc39<ѡ}g͚%:j B={6} [ZZJJJRYk8"qx>0`ԨQ{kv@7144t v ٹX!,J$9&QU a†ӻJ_> H˱C RBԳO>Jӫɲl"O%|Az̘1ȧ6I.}9\|PHߺuɓ']t'^`0X^^nxDQ%gfѢEDh;uSNw"N&cƌy^gφaq2$D"!"Z|ydB$qq8.Nب,aq8.0ф9,ӡ7./ ]+\qD2}z }2ŋ_|w4NTfɢ( !g.))7єkt[tDr鷱y睚8-}uP(tR Lo߾7Ν;׬_z%:!ԕW^9c wOB.뮻Niܹsޔ)SXU ,[7'O< eٞ޺yL&2qdp{I0I?*Qz G_4S # %oX<e!U2A H87[lШQ>ɲLW0}"P ƍL&՗~:::ur-3g8nܸqǏ'x'fCC?yHx@e{m?'N̙3ӦM&w OL&r-Bed2I+KO M.I} ַK8JZ<W#;r@G$epuuuyyrTo̘1_W'MyWlhhзDlt>EB,X`qK0k׮hY/}K"XZjՏ~d2ӟrw444ĉGrF.]z뭷VUU! ͜9dYc̙բ( 93>/̲\ꗛjQu(1j/=voh0 dZl,;44mjjb@Vr„ ,kƌD! twwӔ'N4UQQQQQA;w$I<Ͽ40a‹/ѣNzW_tEXL )ϟ?'?I{{2LeeɓnjH$D<_xŋ`kk͊Fk֬3g z UO^{mGGGM`pԨQD_ ȩ>=8e)ɔcʓvB$%pXOK^;2V\ \Ujb-@|>_gg?Ca8 X,OOJ3 Jb<>}D"PHQ5f̘1c}@  %IRb͚5zɤ(J@+,b1;9P(544BA*dYFUUUƍK9E2b(;Vi"$IR<04 QQ2!̳LCxpB<e_;X63jݟX -#}5hPP,}%I<]ez{{Տ0F8LZ4ɤ_MylNY''f =شTJ&%N16.0bsv/"!J'VI%)V6;v#:CP BʏYԶCFNmK^pXU [__/}tLU> 33gΤ=a8wy*]@@$uV#!>v4ȟ_13n};#2r8 ǘr(!K@XYJ2O-oՎXF6H矟7o޵^KAQOW&?sϭZ vȩm=}[o^=ʕ+5:i󴱱N D{{{"?~|UU=v6$ӧkkk9{wYFWhwJ  :ujxxx񕕕vt)9۷o_UUUmm-,*30?{{{=+@dƖ'o8 C1r䬯"&s._T`|n$LH:cǎ͙3Io.ȲL?cǎ:vLvȩm=G?zW]***VZE)/z(s}M'Uw~ǂ !ꫯ'?) Qxo"жreY})}Сgy{W]uՒ%KgF/{]w]qve k4pYAۢ @ @U<~uH gΜO~B\|mFih %%%vڽ{>aYdR$ZN.!?醆H$|___[[ۗ嚚Qoۉ'^{dR)siio~B'>񉁁}i%Dpa1-Oqoߣ!:yr\`bñ$gX"&h4Z#}}} 3 j9# ow\={zkin}}=ףL}__,X0̡C:::8zWYxq Eرc$|p8ICEqzbwN&s7n\<`03wy>ѣ'Onnny~Ν===gnllTs?ógϞ`uuuww/lܸí?OtM]]]i&M7ߜ-[% @9~J'+c֙KkD~,_,jNmk6g3F6l@BRM5  H$S O^ti8ޱc`ii>Gyfڵxy[n哟sN> z!}իW]6LnٲeժU?O၁zaazuM:uxx8H[͛󾾾|^@79f~O Pe͛7/];#씇}SRyߺuy䑒oq޽'O:u=ܳk׮|+w}ܹs9r̙{w߯o8qbҤIPN&oZ:KG9rRfIMVSSjݺu]v]wݥ_e#%~Y?G>9!BA~eKdRsлru-'FVUM&6E@@~mAmʲ >MKa%zhs8'=Dx}M47lll|!VEq===K,MMM4x/g zx$Ae;vٳg#֭[Ae|3Nۺ+.\[niiiYn]YYʕ+~={\rŊNǂ O0A9j<-gʉGf+Tnidhʩ2X4 fnsĔC5ڶ+;jJUGS}d2j2WgKT*Ɋ90o$Fcǎ?O?o~'SOYfuww|>MJBȑ#G***hîRy0,+8tl}ܹ%KpWRRrp8<~x:\mb1{{SԕW^gϞ}{ӧO2e2ryz}p8|ܹ)S9rc(hiiAW~ `.ɓ'`eYI饗N:U]~t[`pppаI俛2VH3',&ɡn;]>!I&Cx/ɳQbj+ HL`S>(\5nEb# ljian6r2l4(&M7owOӄ7MMMM6]}ջvK8P^^+H4nܸ24tZ z>׿^pۯ暚_{>%駟߷oߘ1c&L_gϞMQҲwiӦ3Ҳ_~yҥXl„ 4˗/9jYYYKK˳>;eʔ~K/%D"ɓ'8p/'twwڵ~+V㐧Nj."۷o#,X9cƌJgΜH$:;;~[oH$v޽dM !I(O9"HFքNL\d|G$r![xu~K^GYei~e/;Z*VPęRysOANibBnɲ,wԴe˖'|rٲe֭8[Vgg͛׮]Dofa +s]jUIIɗoٲx X~=w뮻[nijj'N̚5_b"ؼys";w׾5e['N $!dŊzk]]?<׋xm=:^z̙ϟ_fMmmox}Zśoy2# ÷~{__/\QQcX~ːgYu79ߏbY2=CuRBd8< L.]kc~V9(#ze5$I|~$I7EYf%FW\)I3#4xժU$ ^tER,Ǖicؒ%K! q(#Xmkkw%i֮]\";nܸ&A=Jvx_=?Ϻ{'MF9[n],b7dYN$7-pyytsXl޼y/&h4O|"L>-Z/$3KgFo\);\qK&<'ϛx}ƜTzlvlJUs*Z[bL&52 9+SQ6 Iiԓ!K7If"O eYVUQ$Iɐ.^)PoN-yjʬ,kO6$|&|Q}Do)!䢆8SayRzg7e,ԇ^ؐ ~iQ*qRY~S$Rg0хk#*.HFҙdEi#x/OyѶ~2.,~l>߲^+eJdakv `-箘j u)j.j&f]y̎e CD&WK^O(I*TGT6lC ?~W' 7{YΞ= ֮]KyꩧP)3ӂdΝ{w>oВ0WT p$訫[lYKKKOrQ64)Dlf16!vK/r@t5rj PL׊H'ѡTeP1 ]7]܎W{~LF"~޼y^{rZ6 {vNTnY3{Z]9dXU?.9a| &o e iѨ6z$Ύxvb:z̖In^laS6%gƜP>#ڲ#/!Gk4;j8U2eP4\缗,|濵k7mb@4ڸq0y Bpb-kݻwh…)Ӡ] ,Zg!8 Z[[[[[n]vLo}؉h PLkȰUVUJk 2wڥ? hϰm=4g<}YE_< q^ P<&ZB E&!,:kXi-Zg!8 Y)y<X@Y)ڵuq"A"TPD5kX ,Zg!8 s49&o=e][[wE+K4Tc=M@Y'OiBNU/*E8+!!XQ~uìmB3cV%)۸\DTk#^>dXE ZQ+-X[l8NrC+? 4kGOd_0|MwaO鞈׎,o=EoiP4{t$w *?,NDHowF-6?!y 차%֗2Y@2kX kY#[Ui4˕wKԫA??eYe3SYH4RO,7]}!䦫%I.{* *E|ork cK>P)ڪgY>U_P È"g璘o.Ao_[_E'|Wb7*ѰVgɦ5n(J*<ա|o]%Zf?**E|orѮUZO#ּ<@MNL&æ.U`T.tnO7|P.B{SΏ{~W[3Ki?E"}ȰC&e/ !=9aS޺:Q)]& k7:;j¤Y>PýDaDY ! 9u "T79}\Ҙ"el fo̟x|kO&|k|B\4FHO͇ $h'eC)P OP#- zįU]TP.!o!ϯpڵ9?] "TP67?vF.TP.B!8+Ev…)GB"TPZgh޽;?(8%|]T_{fZga2`.BX6lp#ԦMPnAhӦMn!gnHEDw6#PH(]yZ^}7rѼ%.^&k`D{cߎ˖^f'+/r5|{ʷ g]mN06ӋN<|DsaH~w94U"El_Wx3|C_(%dn]obڵUP b&CrOehO*洬sv/aڢU^(i{4l[ěBMW6"BTK~ߝx(}Ws`ԍcu2MYA 'RJ "g璘o.A|?B<{ dqȆ-y;ۦ4ԧWdܨ%4toy[Vr/ K! !hfCl>EUHij^gÝXKŕ:58EaCdo\ ~Or4rnQѼ3KYv S EsS"dZez&M;4~z!ZY2;p73(:ڌ3O/jn:b:'3 Cr@xkΟNXZL$+Bn_n%k ;?wԿ) _[,: O˚ηf0 m*/״KTM^} -K !䦫s !%?lwHnR׸RцLt($[~NNRn뛯K-/b6rlu-ubGesVMiLUOɸQK Gi!_"BB2'ڴ-8'q™㨄[r!f/Xn^(o\Zbz[񵵵ASR3>uڿ4e2%HQR? ?)Mik0cԉ_C֙`Uؿ7Sz&qe'(/(p7C6;f&L B0dkvfOf8%Or;E,%(Uh?i`F(lYv Ϝ|BvRf Z_ 3YqW]~êsUc^+kE `b-M6]e…ݻwT~ީ+hmn!kMuy@/EvYԏP.B廨8*8>k][ds()@Ѯp! .oyFsYA8Z}JyЅLMn kw]@|{r3Yw5Xiq5/4fY˜v3d,k8dv2Qn@Œ߆ڵ)ۋmn9h@ҋ`54Ùa IDATi_ᶵUnSJ 9Z&c15 #i 2e8XU/;2Kc/ۇ:qv2LYim~a4G#+kFNشiEB2Rk.\r(v-Rky_>ekqЩc#b- PV"xX ,Zg!xԘ& (P>}4=w๳ k<A!xK۾J n+-p/@( 4|jٟmZO0lf Skdg!8 Y^ `j媛.ZcׯwP$Ї ZڵBpb-kX ,Zg!8 YBpb-kX ,Zg!8 Y޽{w~P.\2 ڵBpb-kX ଴cmkk# ڵBpV.cmkkE29e_ C)捲l׮]'D [$KWQ<Ń7Xjl%^꣌[$#Xk.;a"Ғ}Wڵ$q:`dTr36 \(K&R6 2d P\J;֢ #SZg!8 Y)!/\0?(Vh8+EvΌnietj8cE_u"YCY?${ sZo3m!l.O7/s~p}889cY3nOZP/Q/f\w5{]saŦTbXԧD*zB9+@˲_uk>zʰFg9&ybfg5יU*6PT ?zYZ26g}4{OIJ]sYYEYUէzI6nէŧȰt;͢NYQ컣BeS>A?=h+PDCb@M-,4WHA2;.Ba]̭~=>zJYHt"I}ɲ<ցa<k=ێ1 f)ᚚDZ3Oue}ډ'M GBw:2 y6ө{pЗԽ dY>`+k<_Htgt/Yn$.ue.Z;<ԮMN)+vJ#EԵ8R`+.jR{͎Z;U++i-1LfC|l%/"vQ0լVJ! V*yfOA!m Qm>Xڶ)9d$}f [يY lvN-Cak`24Lj3˃| Ck][я6]Lm3ZW#pV.&)LtKR&Hk+j+҂9bmfw84YhTn"}(BmF-l-,hm&{d ։I Mwt&59{pIJI̫Tiסέ6o]fcB(zȤXf_n"I^U*XͅFSSzR ֌FO"kH6I ;5lf^dU #Y1Z'IEf?&⇋̋`ds~mf=Jxj߳izntsvŦL\էҺ5MMmJ:̸ =?['Nf1 4mA鮕Wk!<ج>ZH+un/, sZȊm[mnδ?`qqDZ,|냙vS˖-ˠX`4pnYE0`\:*ZmLLfhY6;w҈f]X-̤lzEeY.lӈؾ}{Z| +9&/Мo) mڵ9" w9/@X @X ah/_IueBhTˎNm7Ĉ@rue&'5>ձ igxVlyNqZ=?E] _k^dS”? c.⻬_9Ge&o) Cxdl&nifkGmր}C2nu; /:WÚܸ`L)߱66ˑ% Fwlj:Tՙ=T|Z֪w3 j-Fqx*TJ_EðnȀ;Z7gzW1@q L+`kcԭ[u~6jڝdy([iN_椤_`eaÆM6x/{iG0yJR4ڸqc]]ݲe˚1G#kX ,Fi:X`Zg9ޮ9F6Oih8 YB%;GLI(0 p}͇l83}gM^q2=WF(v烢ķCR~,]12+Uf'1h;9Gʳ\zrd#boƬT.W/ޜ2.YXɵD;wYfߴ~Il af٪p~]MzzSzGe3+fuhpX 4UgFMѬNS9)Ba0?rzsrճIL6y:foekglNLW~C)wʬY3iz",+*oFP?uQc6˦m6Z97|~%,ķ=c@σEtCJbds r(Ъߢ/WM?c|r]}M7vZK̊gbZuY9s m6 l3s'G6>8ho |?uhn4'ٺr=(ܹVi0em(g[M0 cWEU;{*>=Ô]&^3rWx 7ڵ#uTUo۶,56wNbO k>e=,J;vֲY,6})F[d9#AWqf[~K5)\adY&lذaӦMyp"Yde/t#v5OIFCCC7n[lYsssA^-E*Z7Y@*wF)ڵn d| H;_k]o߾7o^9iѪcθ]_헝f Zg!8 Yϟ?|K/sYиw^%mi~ (,7ʔ`PU7єap޽6a s#&Cul>uYWk}z|W*gyJ^L5M܂YIg@>[Z5(^"9 AJ!(Dk qLSa_)S`ZrfCϩVJagKdP ܍ Bͬ*ZrBub{pGlSc> oUf,7*L^_p2dv6@fajdGϞ#lN5*# i-1LfC|l%/"=?9SO9t:n uDzYI9!p"VKV8%vϏ,!]uس0A1K`u"! -H_$6<-Pz-wh6Phjdl82gQETٖQЯY6 @a [џf t Z.Q`KcLS$cCi3_h,޵Nm6-Q`PA?hpڑ\ZP <> A)X@)X"$H`b~. GP &si̒!B P ] kdښ 2GdpK3Yw3789y8o}0ӎfw5ɑńMi1;g>3_XʻAW?mnZkl̆ݚMk6 Yr%e;S,-rgF5)O ـSn=U+ dI%-%AޢuS m@>c3y -.bMܥ6 b@[H։dl۬i= [%I:RUU84/W @[x6?@s 9v*[(Vցa *Չ ڻݸ=IRTR@PҕhFlȆ ^c`Hlt5H)]]TT M2$3p뜜x!xl;㱁jYoj?z5ˋf)*q *_'@e6X[-V}ȑmR7?.0lp%.6 2|޽ثX)l…-h quO> J8ؗ9E p}Us(%|]GXߠ#bAw  X myU}ji[Cd{Ƹ%oX6'j(@ՊXŭt q]cYl" SE,Z';<ꎵZmmos1_V9>\K6JejXc6Tvpstf~_Ot٨ե~ݠJ+57Jn2Q.S+VC q@;IƊ9/Z̵N+Vsz卑7@K(g4{^6`\k+z^ثЬ4`_^IF^ @\Z"J<Jh-lԠg4Z_/K t*vuy)ݯ@3xgx"8<[ڤVN7,zF2N/YpIoy X5fxěڸZ:]_ J5ֺ\2/ҙA4 L v%E"jy"f=\HPYK`r缐#ϦPMZi}Vֶ.S4'G/0hWjwZSO+K+Rɥ6_uTXOkLm"i_6k󣼃:K^ @\9y?XO;XF<-C^ @\Z"kwwwc+kX @\U]I~)&S) UeO<)\LX.iϬBDY@0▊Fx k H/(UDXI@mk*HԪ((".q-jbmJظ` @K0ij,+8֒zVSH^ @\Z"W}<ty-q_u=4 'ҭG^ @\U>7 n$mX OMX-Il26#*^IDATv WK`򓠐zVFnKy6c[CVLekJ9$K\.mFl8gV\|{)_0u/tUC6T{Y!t;°z{~.to~pBwE?1YKNO"4! upX,u9[b%ܠn Y*w'6k D"mد;%ɫg x7=Wڗj֌Mؤb-F2?tX6\r -YJ2% 1\WRnvKI%{70kPȍQ-[,I<ȇv[;*ժh-yȃ`ooogg1dtMrh@mߘ4t=>4M,F %RMZNZ,/S[E܇'fЉ|EX%zN1jG0: ՉY8j.J%FjI9,WgN:Hl ~뉝ʅ݉hsVw͉UdW;ԏX \iXt@+ ]f۔ ՚+X Nܴ9Xcᴒ|+o~5mVcs|+1)VUCEC\M< Z -/1%.Y\>mڵ85wZ0zX,t:l@!-4DU5a-htvvWyt:Mn{^s W4u2\\\\\\LSX{~l6ωov<˧*I/`ut:Fy:Nxk޽޺uk:f3ƐHMch4Jl6KHWvoooI4jahdux4M&~^ڏ>觟~_}d}} Fo]ѣGۿ˗/ӱ rϋ/NOOzwygsss}}>۷ox<(.//ONN.//޽; nܸqk766vww777/^O|`X~kkۃ?666wI֭[˗/θ!7oۃ߿sf.lvzzz|||tt4')z~ƍofݦ_zG G[4hKn@ZP݋ *IENDB`PKf#5ж88-Pictures/100000000000026C000000CF9F74C6D6.pngPNG  IHDRlIDATx}{aL '(Ve(D<ZXEʠWxQ8SJʫ\r;X)ҫX%$\P+ d8oK# /66O=MOO3<=y  :=PT~veh#=VAgF W P- @! $Bs̳.yVޥ!Ȼh ^+ߒX&]*s%)@"G]4,@"Df%W3.( LtL, -$`˙$ܛg ݥ$ @hxK#e#Z=?r; =L: X Œ\ϞJj;gF*$** 6 ъ`R`{;I˷(8+̬F)t0on"A)m@hl?6Ci^OϠ6SU(CTl=? OYEl <)ttF ϳ8¬HleBm7 gh`} `# R'euҼ;'oq6j~<I.>"r1#wh "ssQP'ɵ ?~ d;7J%eS.]sKUle΢gy3\!5J6x0Uѹc2\ p7W̑ !U(\LF綳0TTgF]B^=WOז e h !ׁG&uI Ր !h#\D l"DOz9BəH 艶NJPXx^ÚH^Cs mh[3 NeECˎ)@Dv@ ,@fFe cϞ=y̟?_AgF W P- @! |B5lnvLrET_ !-j5Pv]zG7W7-iVU;k֣|%pmB+&?*=f|ZN[j>d-@ hO QZG9Jv&ȱfڜ!ȼJ&WIXT QR3c&Xt`9fw]IdzUnmHIs|9IO:9xO}zbԹ*\|#6\4ƻ*\bR{>xXػĴ%s?)U ĭ1)w"{~ɕK {lEQP ,40kcவ9&xrp$ K%IkCq_|zJ3͉#ӽ|*||IIա'n\ֈHB2UDCoO߭MKT*('& ĭQ:{!s_ϙObo!nG=!4!5k4_K$ϾX,+:ٌڢ^J^nilhVԳI5’O`K37G}Δ)]9p ilhݹCN4F~Dcۜ3HqH^L.mhNZsneP5BBPOю-o~XUp64.0MFœLo_k(/$+ ϞZgp[#j9u`̋g?~tl-?wvƭY}_C70|!Ğ={%iTx.K@5nmBkDvnd̚k/ S?Uʌ :hp8)D:^H/e/Od٭#HIU^8%nh4wnCcc 1n45!BM?1JkR)O^{( :hp+|jfF?i]!~_N ~o9Ȇ hp Q7rщ0I;vm8GFF a!F!hz>6>>6oK06<ׄ4MdA!VhS>3)Sk)7Ma!ѡѱܽb6oyr 7]£¡Z#$C->L0";(T+ w#FԚ*hp0 {E}~hpb}fy!Л C!DEޫnsU82^SmE^S(5҇S=gIHhFhݶ~$[0m~}Tbwgdnrwf"""( P v@!Z<+[hsEڝ[zP ރ5 )Fxߡ]Lwnwμ =Q'˻8_^sl{[W?88Շ~_⫮Z:&BO.o>k@N^~_Yl]+ʕҙ+.3/ !̚t3)kZFōha=5yc<C{|g#/Wt]nх;ǽ!7)( E V/n}G{oxddtl뺦izwhf4rh=\}:{!Z0P,=3^}ytuM8_iVό)ѹjqF[v wL=^:S+~뺮iϙOb F*$oZ1l[q.9oj[~yE}PMӟ}p'ztk5eN! HQ{WW{楊^Y;<j%#[ܻE#TJU9M񵇞ZE.9g77yաt/!@ҥibi_?xڹ%DǺ<םP ]S;]|ֲʻ I"D]\k&w )hdMk͓(MMFGè˭ot翳[?޷{4 =Q/t@p0 Q{'j{=gƑm/myŽ(8!sƴ]+ z3/qmW^>^T:\k¡Z#!?E3FCFBTBSkZӜvݹyF=u@ST n^စASTnmE.ep Z#!S?EK0~CFB43hBzcd|b8)-obq/`qܧJWߞrќ83Qh1 oG YOmhfՑv&$-W yY] I!D# ic !hWl!wnm^%a]vI-ewiH_j)2@*$8ZމM]¼ 3A/yロz%! x;cl!đ无B3unUjZ{˷|7lGN',3A/WΞCr Oڧ繌Цc_|G1Qc'*X_4-'-ϰ #„h);W1m_F<%iNTӂB4j}MZ2:5<(? x@KqP>Irk*Su[(J.#̼4#3CYgip%Ru&IOT]+Ż盡I{yb/CzZW'g3!~Au5Az X{%'ڏ2n9^ޱdXs=~+{/*HJPs,nL|=f=r  *@Ҿ_Q8s*Qd~Cm |Hh1%x$+ I+fk.R!DN,"DԱ4դr*:f.#DN,3&{w S8up$H"DD @! $BIhdMk͓i'hu!DèRR¡Z#Q$H"DD @! $BI(tCoz3 !N1:' :hp DD @! $BI(Q$H"D#¡Z#Fd\}^o6ѹ3.BtNdօ+#*5oŗZ.5 TnT8Ak!͗'. ý̪8GCFXQI2 J&p W,1F~wѨ9>b&.IuaGJZ!Ěߘz9XL㹕tp !*cxM[Wg-ظiJVRT8Ak!*ڐ6h:.TXp!}qnBHu!Qizc !~有CRACFX8Ey@c_7! chx0 uaG;.0BxtQPvhd̚k/ S?Uʌ :hp Dy-߸|-|' Ͼ>"uFFs~w=T?:>6FS!>{3'wc PFCF9`hS&uIK4oj*Z:# Wz5"Ctv;F!>tL?$0@!@,hjIENDB`PKf#5_F-Pictures/100000000000026100000099B0BCDB35.pngPNG  IHDRazAyIDATxOhJ8kDJ M}H!% 9&8ć "%Pqz188=!|0iJB@IMmٖ%YvV̛7ov~f{3ȿ_Q`----//MLLLNNGB @#z$&Hȑ#|y3O1Z#oé]w@^~Jmfa>SI(zF0ȑh<9ׇ8/#aCDi@2 "2β] =? I/⡚l+aH4zU ʸm %HHoO?v{d#ldsq}$jG D}jn=,$* 3H4DXa7|ĊOu{wCSYUT!(~fǫ #:Q,mSvYS_j#1\L7|FHTI[2>NöԏsS+s?##G}=!<77711133399 u$I+WJ#MOOK#mcvl<~ k ٺCr}~c!G? u%s \kt:uGQ;> Bԑ4^^ݠ1],Ux1T0lʝk;>2}< ƅ ȑa2 f bɗ6jh#RfF U}I6#0gD҆v'F<Gl`yZBp3u*B pP/ws##wVPI=P922M'Y^SJ`Q;ް+zdx 0(+YI Ou@DslG㵣zvXmڨDW)5HC~1PwdvvyZ6k` 9=r$zN!<}Gp+f˵Q*cɅT&-1<-R%GBԎ%Ijz .د":juyԑWF.pudm̗ &v:ioOԗj{O@yxGh/ԑ$!GG@:n#+Ix?p9=qH:IHϞAAY)5q3C9QG:F y#]Go@QȑU !<< J~[yPGB/SP@!xG##G}=!<77711133399 u$I\1;6r$zfggɴNgza! ܾ&uՑIgNg:Ӕw ҩkt)R97ךZ6@jRDDC>6#"Ms.G&| ;8egthӤZF*इ0T\ny"sfI+K-J##S|ISH hS%!m3 gLcš\\翺")3lTbjm:'mpSM6Z-|+h 6?oͼ#ؒbt w*g_G3$F&Ÿiґu`4&uN:A[}+xIIc0~8b(Jݩ8s~'$bHXrjY[Yְ- s0$ [gG{ *G)M="RƤ5JMi >Ǧ-#W_8WGjG7F^VUx?g|=ٵ r 4j&3q5l9xIg5r+GқVZx>$%$]3`OE9c0_-*$bȄfD$Kmt12=i$Y?kz?i|e9H7 !<@N5RBՕ7i6yg R͛e}bߙz5 aGv H4PIv A@O3'<{G{Ն!;zlMn{{9х P>I1s˯>| Ͽxhnye#G|\⡩#BYU6\{7pUVbۊ=6hbX63tέd?q8~v+[3/6x@2>2^_{ U}n\iTVȑFΦIENDB`PKf#5 ##-Pictures/10000000000000B000000030AB2F3128.pngPNG  IHDR0>eggAMA7tEXtSoftwareAdobe ImageReadyqe<"IDATxbܽ{7( ?~3RLb !X@)cyvJVh2G~e& W-?ߌLTIb 衑cXv {Y<7_?~g/3ßo@2EC0TJ0ؘ8s~c9 }x/5?MDoH: >fffL1雃*j!2X߿?`gcJ OF/'`=$?L_jGV $66 ~>Im޽wއ"(( q*c?v0qHJJLx} *2޼ys CAAAVV>(ƓXndrm~8i_'Y6_?0hI@q h ?p~OLh[pΝ;_xA0/_Q5y_reɒ%gϞ%&' ͛7/^(ӣ÷,sL1~o`C bPg!8[OA h μvɩ>P6|ܹs$i⁛6lp=z_=050ٳXb7QJJ fDH3U]b1XXX&n[o\\<sH= o@0_vp!/+3מ p~17sGa 0 )Y,9"'^bBa!)ZX*͒",,! gwPߍ}߇9gtRZ Re>B!؎~Mp,!R?oY(Zsvqa \$'O4KR[icDpܶn<W7˪uWn`j`<8"0bCT`Ӑ@`L +W$foZ , 9&`ZYfAXS0q&^ # 'd5}=ٟe ?9ja17Ad csiasٸˉOXÇ8I@ aIA"fOP#KFuqtvPWB-i/\.|9 1H8p Cw\<ϙɗ( h@X+WU6 )0F y+E+:96/eY&B$*dx|FFPUTBbZ)8uB:$t0>:=_z;嬃P.:IӪ,r%wlx]۽8#] V}.|e>Uǿ,L O3 4!ԪLw1~YQ8A<1.T APt* &JTbܩo@!R~IJpF `Pf}Jat؁K'!]!TPNYMk-E"T DcenZOScL Xkc?Qb`~7% 褬uU=``1}񦶇jGzKg T8 r\*&Kx7=| ƍph4!0@ ''L@A`M qYC#RCZ`$x%l t$е$)3@Y>>Sb@677BfSI? xerO!mI`*D+2>% `ƪ?}e6 l@(qUNE?, >e_y؁l慍n1;u&h K U֚DHҰ=0RZEE3 uuu`5wAqiԵ؅@5 ]6TA遥˂ Vk`G.)|!jw4}j5 P@cSn:g&o?m 'DL扻2٨+VVpKUU~%hR7&\Mb>x@x̚X %<|*R) S0:X@ *.VԀ5=Hz*0AVVV#Z2&, (`A4>@\웙Q& $9|-%?O30o>g$L ?~3fu` cf0 <7aTtizŧzN"y&֦\mThll= \U&R9@H~!fPs JhY /LĶ_/vǑaYky 4 e|k1+lj@*l`hhh? `011!'Mր޲̐OF`[ A*+gM˵gl , i 3!D`m@axV 02sxD;f逗"Dez Z`LC1Ps]MHeyfǁ 0`Άdh}Xorn<݁a${~ l"?x 9bV+?0L`.Z!s(5a Sp]t7{{y#HEk ]&Lf޼˾Ml"e1?:U-T6bMuY#(E:/V6>kwˇI,Cl!VJ9se *ձx$ۑޟde:[<>/JOCPY ~=z0AN$֖ @ PV[023DS.A@9 ``U- /|)BcqmIO"9b"e y,j\?UeBU'^Oc $ƨ*O@LWcҘnH.Fj]whCzv|e3܊<0]{lQvs=97< "o5ca c.85j L'YXy VzIcBHihwT,&~NLCintm 6t>,IEDHG`֍1u c[۶ }W6r9k<'iBEQI PJu]H3_%a- j2֔9^g?8r{/TC:1L ;\| Z,Q-:~8. ¬, PJzoOF0Xv!7G) "!s@bb"0c4`54y9SP"`3qD 1G =O X ],ҀqxCE  H4vҤIW,YlP2_2Xs7XS5S2'3#?_Ȟ &4s00 b`L46690BR0¬/ Zx;Xe$ S0@`aaauhk" `1Y`d@k xHlv$$$'`L̳` b/>183h ])ȥ1jfFd^FvQ1!E-6Ƹ6cG4\p ظNB 1 fwCI?3LW]`- @`%R 䬵FKRH`}(]>fcR;'yH ē#^J?؀}|.Wicc^0s佼kZ彇mxՃ83и40^o r$21,ǡcnô*sp^.`%@vפB縸8`# w0#0D X0 -\*MYYdj̙u2w6XAF0h&d$ R "'}>-;ƀ.xO ^l ^|>a0FL_Fi? Sބ0A|0M * ڜOl@cm!cȋCyS @Dkh  %Gs&0k8/ڀ[̯gbu1~eVlD%j` И&`1#'4@,9^z} $"X!&b d۳g` {Z LN)}`49@Z-H 6-e[g杗9=dcb,?~c#f&ߘ`S_.Z?FbB^l@@ =`~GZ7 C>|)!IHBJ`E@A]t 8 ppxuANl~a2FdrP()7ꋬF`|; @ rK%R6[XYY{Ńx A"^ )$U>X3-]QU@Р cƘ$I`ICQm*?휍15lǮi<O}뺮+͏ 0%Ya*U+p-גJ+k Lw kuX;!麮{Bn UtyD<σ `"˲$Fmqu(RN4u 8q#cqN9_|̿Gykݟb|o@4 QE)rp ȒH.\ rX"g"MdmƻwͼĢ(D Ef/넒v4R 0tDnj& lpA=hjh{t &"۶PAh!m4ȵ˄62KLRopp8y.ʳ,vI/$@cN-UUi캎~7(Egy1`.Q 85Y+a n=Ku]r,qXruq:Cr=?wNc~pO.5B(LH`Vl rM`c#X* bP@4!`cϻ뽈j ޣ`GuEu]8>X (@d zW%,jB8eYQRaYu;MӾӨ3Id&HyxdȲ8Fa@8q 0ZE,NeI*7![$|!>4hEu]Sp!#۶*(1bcAy i8b80<y۶ PWc m[^ߏ$c|\뛋zp̑m:^ľs  @٫FQe5`-YvK FBlm}[|KAH'4鲰 Xح,9s\;\:Mm`u pypA4:F Feq](Ⱥ(urïi0  <0r(e8 *ƃ Kka^$mQ!+@<τ) @* 9f̄8xwtH@b0$a bjQ2řK`HT.iX:!u&LJhK ,+r~9h ډ/ s|zzVw)+?A{;q.W*]__(mda" G?WY7I YF)&fz^owp "%Y4W$k< iUht)IaD_PڶB 5.c\?UU!:8.aOA gE~=P*T, CY]3 8d18˲p/* яM7-8֊Tpo^.x񙇃b7EJy uMOynٗir<4<ƔdUbqus>n_Gj#w13h!pU:2M{! ڤ!c-[6d3fb@&u](@.JrIM9'Q#x#)c:vCJ`[׵fUUH${Wp|6a aE}8 j-)qJ Հ"^CrC4ljIcӗGn]e,<`[m[6yfVّxi}dQj.xZO״+c)"Ɏt @`I8xCt@A@B$gDԣ073o"Hn4IdEa~ nMӬRv@~0]PzHj0@dHy68l8mJ1s:1jF0iLݮHo \TcX·C<#Bɲ,0LV9*yk}̶P_snCheVmK`wu?Ta5">;!G,-FwKZ_gYN<^hBu돶8zB"q$ :\w&?~cqk{L};-Ґ򉍅Gx -=Tb=z`%D0u-6V$ _T~M|(MWIژA@AQ'Oܒ`W4ZB O^ J%d1> FK A)B 3u#IENDB`PKf#5{m-Pictures/10000000000002BA000000CE157037E7.pngPNG  IHDR^6IDATx[W=0&=%<(xH#K )9X^8XE+h6` ІHD «/,Fx"j(-XY_23ڇTu9u:Ui4Suӧ|}r ˲ \ݪ"{߈u^*\2@Ԕt;UpD \@n@?1o~jK"8-]jZ`.|'>v'>&M9AveJCP'> 1G`ߍ8C .X`Y\'ePkdtwB?'D [`@=AiϱyY="+笈%_`7c# 5ˤ 'zv.kGD Gܐ[R()\"19]oe!lGaOANܶ6PްNsI,\ht:TeH*6e.ͭ:irkPq}VAT0#eฺz^Tr\$,#ްqyS6gp78Џ F8Q9~ ൫oؘ{=O4 Z,;f@o,ojm2WRzC]55_w;^ ] UviǎYc}9COT.Ld^Z yuǫ%hYIAǻ:ViH q @p!{vZ*'AKj+:_.sEmK^MIduP^$ Ȯ6{_ѧBa#%X't#.{J M*^* -*I.UxS!тa >.v@6Mt<TH̽d) ,yDt]p9_}.4sb.m^8RhIRk O2x>@R0cvd:Teh )TGP&pD \޿2*,GMm_H'u6aMy RNRR+.uc7M|p"N`p6Lvoï=:SPvRƣ/*P U}@Dx؉SՉS;=peeal.tECFT.MW:ĩ~um |^dFCFTK¹Uصae2~VZ#j*K ؄I_[TRJ \zL& Ap "ZwxDWZOaۢU4].LiMp~':iV K0T8QPk\ܝRuxͲ4ݔ#r[*{a`z|"L?<"5wl82|5|GJM ,7&Llݛ8K<; M *57cZ-cM.,tidtGV?})B?pR 懎gTΗP(5Z#j*K-`D3k?t` kuw!nj9 p Zr  {г<ζqwh4*\)щ-VOfkX jS tS.<h9hu=N 9T@ GG.x:c0\~g`l )Oa=wk?Grp [K!<4]թs;?Ѐ"Ddqi)"5 Ӳ"b?X\Zwwώ\>x1E`$KAVr FY7Q.o1AƦqeD`I\mwMD?׊t |{ ?_ ٪ˬ]mQ@:FK~ɲ푱rc߾-2N#"\V >hMp'daO3G{$2Oy}QS4]F}rۑٙW]4{!2cug=_ąpֈʥ.$ɏ|q1_ p (N.Mp!Ng7ͽkזW-D4{PDbfsgv}˽hZN˄ >*oǜ?x:$Y4O.Mp!X~5CF"b#Dd4-,N̗_=UpC֨%L!{%\HOglngXXCs`"b~\^fl<T8Q\kjBrA7]Fnṥa\1LZ̤*57]FXxVwƠbsCCڶFŽNS0 ٛ.Խusg'p.FJݓS 9Rvel[;-"16Ols5,p$LIG뀖`@". PE\'JS9\g>FmQF@=d=m ɠ9d;dXP3h?B 15O3wqFfA!:Vgdu:f?"5*$`Ks9۱]Le,f"\PǑSVn`mq],9lH@[YC (r1~n)K$t] 0[~/rCh/b\L&<[+DbtO,a)zP'Pg.s)2!)6mڴy**hD@9/;,Yo(p`` @(V:sr"< )F!F*sC܀@1GsIxrS>sd$spN (~I| LvwSJ"1]kC0L-S:Tt_NP F"OzyEQ(hԓNbJIFByhtE꧴n[CpBF٧;!\dm%JJY*|ff&ZO:Ye)%&$VB&oY N - }45Ft8SJ&*D֩<0Z|T8Ak @I^ >mqkWv"\H} ׋|eu7@"T8AkDMeMB&A_/:0m@CڶƼ."bYEtr;<~u9Z+[?IP` }8'`08s&ll|78u\{>sWV6fZh0,j޾uM8uO߯N/_߹Vp`zCzt - [5qjo.0#_oKapaw%04GƎ= ){q Nzv&*E2šntˁ˹21eVRF¡fFv8ݳgy䆲ͻe^ 4w54~k8o{9wl8 n :KohO L4\]rI3.@Z0@̭[<]iiڡ#Ǐy-"\Zޜp(н砀gD_2(.D {] )Fm.@{):y}㏙Oeh-w|.@Y ip/;Q"¤( 9\~gDW WCq!%u17I:u2r"}eu_铖0-ޜpjdwMD^+" >@@PwwޱoW n\KXꌬNs6g5L[cP¡Z#Wg>x#HdʟzymWѬ]J8[r#3h^zC[#Cd("ꡇڛ&&l{|%x)מd~_u @Rc2b$ȟV.@6z @".طo_ETfff.$62F37Zf%k(*ȷ5;z8yyh״g Bbwl?U30ZO؇.\QȄ^Ȕ/_F Ej6*5WFbJ!==\, CCFB2F?kYs$5 }kj .Eњ{c'NW'O 5}sSt6O90;q__s,QQ )- [5qj/dP$GCƂ3r3#wj?NZ#\Hi|*bw4}*H:l9SF,RZ5"Ӭ%%pX8{"&3"筡[á54WK̹d}˷ic/pqY!&…> "?50M Lsu'WVơ¡ZcA# BK9h?~lo9tJPT8Ak?*,0s;t=O^ׇ )F#VH >hY!lsw4^D!  >hY)L^m <8`lK<#"/d0tBP-BK ~0L ?}Ҳ:Bl*5X]3^+"a&T8AkD;.$v[mquٰ dT8ok|htn% F̯}GȔ??vPf¡|[LN@ێ\y o ccݵ7ML SdFCF מ5_?6.c)F)"+<+\ְ~0ܲQ-ċ8wD0,˪ @k.(vuP:IENDB`PKf#5z//-Pictures/10000000000002A8000002957DCED6CE.pngPNG  IHDRr0PLTE%%%VVvvfgf]b IDATx;rqTĹ7t2O@*`ko`ud&|j4fa@S(P&P&P&Pd=TI-"mjwjj=ACLX X 0L_pB5(S υB |fqBr2hh i3Xg: a*$-Յ`hx}R6.>e3iŁB5PU: ZpP z-Nr瘩j)TiM-\&Dκ=H*(vZUuꖭ;fjZȲ۴xT0 O&p~j^L6n90 Ɂ~:V][S5)TP5)TP5)TP5)TP5)TP5)TP"P UjR UjR UjR UjR UjR Uj }cBPT|ͺO`7d&*b`>0AGGGb -p[CA5ÀzޛZh~PzdGXfpq9:V)5 0P`Aiܼ9CIÐ ^ę{\:e>r2`BLnR ,X.p%T އ -ygjs<P"'VR `d9C q:4 P,Y:{h mPANc4dzc(T] (jҩMP><٨KP7j?thP*Ae>Ajs TT1PTH I}S3=v{Pzj7jש0HzM}{ϝ>? ]w %ǕrøL&J MST*KEy?Ec(jO\IpA@CM܌PG 5Q{8BդPBդPBդP?3zMUkϤ:U*P5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)TP5)A5qJeʭBGiI-"mjZ% *(Ծ^8 %oE\6>P96ėp*F:H P9T A&:fjjBm PTuC訰qd^PYRiET:*WjCGVVZM; `j!tIq RP`^Wu^vBTp*.:UBU UBU UBU UBU UBU UBU U*P&P&P&P&P&PPw?Ɖ+*$Gbfk Nl<.7'7 qꚡݡښk`P{3T(rBMP=#Q|,38ʏI/ i# +h N6,>k<:Ͽ;6C纹~b;NR3zzmgq8:c Frt'V@@0 c1HA*F/˱2i O2>Lh< PJYب%lٞlYP P#ж$# *Vq (ڠF,=&-"*Tkd I@5&TŘm!+H X1#Cuְ.C]-N*JPYs2TWkA>TW$U" Tu}SSnj۠t*PCZX[S_?g ?K?Ө[{n6Sp>$r $l~!P#KTU>SO96Z3bBThwa u6I~(T.荁]W@KI@Qfj M *|6TThtї7M !0> (#G|} T#NE-w) 9?7FtOwXϘ<2h!cxٲlg|:FlN6SQ~kPm{(+Ma=L<ހN%!x)Ce{B) ƥ*:ءںe1:Rt _6H `swbE~NgPs=(P0L*-*][;q2Ւ^$B:^%맶Ϡh ;@vVmM{|qLQ}:eUeO礋CTM uxPLeAֹοMǬ@oZσuw!@B?z1_r W kY//炩ݥصZhO0$Ie#nv;?FAfs6>Qp.Y S;} |lgKKsH&րv76Cmu;4LE{+#V jwFyr/*@L.l67A_ @uYύ66CRuw[AMuj5;:Pu[w/Ct2䆪?R?զ~jѝJ R?՚oLGd})TITvT u?ɬ7TT͕*ԃ5RkToS5~6YH:NZ{ER~nlB5DBOP }x>R'> {_lCfwBq<%dOG 7w}!yn8]2T 9SFd 6y ¶  P>ՉJ,Prg%L!s8EW-$2WL,P;<0v[T[ҩnNqA,ldmj#MWnM]_cc#Tp~ZoR? Fk&bPqZʿdPB kd\p}@ uTPnMwCݫPfTGÓ=X!Ub-oSaXg/ڟ]EA9܀( N~DVe>1z"CSMPJC'T[2뗍IRӠꐅ2eIb&c*:ShPMֿ?MNu-[6s?=~*ROu)lȡ2|4z(PGF U:_1M P?~?:= IgP?:P?P}}BF/d=?_|] EJSZ=C~SX=@դP/tY=v(mnb ˗/ox;gԟ_[ #_ .3jPwO?[_cb!Ϸ|.lxPH(3?ρ3N+Bzmg~K*-l:[&i{+p3w_ϐ9THPğaBKXSڭX$ !Ew뙷2v/O/&/WVJӍ,[*9+oTjA xzP~?5l *I8,!P39mTѩ\BQA ?IR_0V_j'NFYDgʠvn#w#[$Ԡ6ьTz?'''gOuB\vZp a\>ōs>NjP7=B= UBUcz{cqZzکӇAnaCU#OP/w)_ z-x-f~}k5&oo/o/VE?NXO^N-pk*_1ǙAEI}At r8[z#|!6$ۀ^%BτaB['PJ Qy{UelARL/ʋ^¿3o 5ˆ]V nJʩ@3p8G _ga 9ٙdݾL(gw|5>~B@%@ Jp5 9bPi-_ġvԳ/m(C\0ɡI\9Y@8׳5To_"fU _U QRC畿i@evz$~ 3aCSHKOK@BQNj}a4r~P/kX #Rfs,W~ zY HP5 eZ,K:S W^?02A]{KgjZP\^^r*I,s>&PQN$y":W:dԙP$u*өY2FNRC5g;'}=%9XPPӥUN%OWP|eP/1($t犚|ihFt<שWWVDVOxۂN^&zB@{>Xgf,+6+ԡC zrPTRTPXVuީmmPWDtUa1v:B:vY StZFW+k\L-s4uWڀð m[XY.֢ő/+~+iH*Vw iy6Ye. !0I%B<+ꘗVhd@ bØ9,@?sɖ!K^@W4q4K7u쀠l(ARퟑW 5KՍPwCEfx*Dc T{%Tg@-TXͦuk:/v:5ѤSЩ̔%V0`Cz)ƭt* :D@(*kY=\Fweڰ"suR4oXPN *0iC]7Bu] j|6eK?zIPhHXRuU-RH*ô<2l S5`/f *C?\)IRcbehؠwO]S0 8ghQ&N>Do%u̒.tZ d^WQ rjI\=KT*ԤS:~Q+/p._af7tK^,AfE4^J] uECF &%B5Wz3į5wE*߀VTJu1DN]3 5TU~Qބ"p WuRK_~[?i*ܫE."kuK_QsPS f}֩uF9UeМXY&-x,ʙs6_sO]%{ f˾NU=.MYu&UUŨ?W#&:kzRw|u>o4Ң5o%w+>a,|M3#=B\'_AkA)X#H2Tn3 Е]*_H:|+B{5K1_1-ijz0.\3Z@?:!>& raFZe|>A S7\/WNnR~@͜oֆA8DIZ1?ݮķ8zQNޅHX66,1=>Kj0Vq;"J)Bv/T󿋩tT|w#Y ^ތxoRƒ=Өփeԟȃw6_=Cjp^Px7zjԿ$5 H}]:zfԟgT$:/5)(uF9>qh(<+ZGQ@&^+ sW+q?jEuNdpʙ W35F]!HA*o9(80o;ЇҬuqM?h?P㘽 ?U~5flPWS//+ɬFjM  *fzSGA=Gǰ:KM]q9zǯ4@pq~xVE?xNH}7#n:6svbqw,TNj ޏ!72ΌTR__0s@Va.hmì{6Q*n'].P3¯0L 3Fy.K&^;:s;AN4\cC"5PZO;ch,2Y΃4ҕLc /s4?/z{%O^_%+&xQBa_~/҂d 2*bd TZ^3fz{e^"% 9 ߧ_a!lSY~NqYN$|NNp8 4GO1Nb@,N¹F'{@RI/zr¼LaU TE={?-5{;Mw §*m8TY=uI8xIDAT/_wͧb[>~ȆRbԣ QPob:f]?n Ÿ@Ȏ G1m,3 Q8_v o2anlqqh9 =C@co G?"K_\`iN+?B63o6o7BuVy(x 37@(Ưх&A-d9)>Lh<՟NOK HQĜ@yT`i;-LARlq/JPT(Qu}L*jR"ld9ɵ%g8p/.p``PCCU5TtKXiToSV{u SMPIP%,9NuTj\T:I:S0zDzLB :Ʃ)TڨS:}Ub?tngDm~Im]Ko0&s?B?:JHSx&ɖC yOuaX^m Q@$9V}dE }oJ*~zFjMjzFWӟ:Spيf ŽB+1AuaTn5nJy6u5&ۀݩ qJ\t ]B6xɰlyL;p@2F4A܃,I-c BVػ+ԘM,G j́n eFMs綀JZ \T990F"uj+It2jK7v=7FukjK:mS[ntȠF6M6+f6K=WYJFw'^!Nz᩟&P9`ctdQe jIF u/}78_nL*q BդP&:SxY0~QvuFi8QN_oԟa"N_cԇ8>z~| 4@.?L{}Gs{>I|w1ڎE{p|Cp|'u>v ?HaO$|'X>?,DD"?@W7;p}GlU#Bߵ;k~(ndMi0uMjG 5{PYU"Z&f ]7Y'.,x]5fܦ %CЩ,:&mr GXc/V`<,,.Yű?9IE+VPɢ.v/All2l_,궦L@vtjg:uWT,eAwש,ˀ}N d?JBU UBU UBU UBU UBU UBU UBU UBUc W7WẀ+i?ʮor8^@Rm%(kGDd%ʳPh,fH2T?|+B-H-?cA~l$&WV"ʚ:Br PE*RS_d. u  P&.MQtPE%P "T@GAE*TM U*TMP1Cc_,HF "يu \WoyM@i@ܒaKii,uDn8Pi\['d)kcgTkv [BzF bo\_C#1bYt XQ$ m,q#TkH.>MPDleCl~n(qL:fz!R )@8"x\ c@dGu-s $5AqyPs5@FɿdTsӕNĵ Tx?9H6R0xl{%P8\1$w y| *-a*u+԰]iTcS8y w("[.eB#T BTn 5T5P9} TeX@Ud8YT5ib UNutkөW7.N 3*[D6P֩N:;Isj\6ϱY$~h65TRǞbHˆAeJ6 qqߵ[zj7Fj .4&S02O10C(PS?AH+P:u)g6U&XOOӪCzV~헽4jP]ߦZ?6Mӱi]qjIU#Z'6r۶Ľ^Ŀ?~?*zW+TWjյP&NmLP7~ޠҨx C G҃|xQX-"Irzr&^;[zxGȰmN1CX}Ӎ3y0׋)AMVs`_vQsXF6DwrYWdʥ7_Fq\v(< a2^OYq6\]DE]Pӆi얅LoтlKZ&&ű Ylf{Je)Yҳꕡ%3Yf橮)\[d̠2ZAoS -ȡB•9(G;@+f)߬0be6CP2ܔ7Ig$ӋkC \Fi2T63T@jS3ò1 P,҉ihe*1C>oܘR?3≖Åf?02njx6=QDV;4@u 63FٲM U2Kj6h͍[@u9Tȡ}ܢYҩN-Q(Bѩ77Ԃz.TV, =vjupLVQ/RJRxG!rG\u?̊w-Lj,:?bOmӏ$[֩Nnizl5#zWS+w2TYꩴ5,Y??~*Tۓָ#xa~m2C\S֡ߴ!4BP;Z- Uj/?o!IMIENDB`PKf#5 layout-cache=KQ]ggdQܑ& +lتF1E- `h0e 67Fm6xyg^s/""zl7A 7LJI&F ǡ()i\D?|)x޼ׇ]s@z V߸Ov.#\'}ݱWǝ^ %(k3 Y~X2.bD 87~sW͑deDx+޴K XŅ wrVo|y2*[2zӰno[SN___n8ŲլEVC竴k  3' @'RxXԢ KFnRaGΓαys~r1_.b[?eqF± _n>KG )_gkoWΙТ֛)~Z7 ywwvE56R6PqfMցC9er㿉:đb%b[_$f"`'DΠI$77v2;jNPmȁ-ɏg `uvCtB`~^ӕC@ZvMSz:#)fh y)0MgIv@緁t/{6϶A{vWҋ[H"HO;'}~ Nz>Xãҵ?89y9ChGE0o@M#JL`*lԳpp|؃~C+^(B~P" }ױg~|Oy[iA{:*_|?hF?8` QJzdn }1~xq8TXP%X30LHKSϺُpwwf^<.\-%.RR> KCWb FmOoN;u7۱2vNڝnm]̌MImhV j;ch=`ME F xٝ[~t}{+<,(\J7,((UAn'&-3hKp ==:euN ;_4ˆD+.4xczK;U[;U/" <.:[qV`hu:oo<ڎA+.n|lKXds,q"Wtpc+FS%_ 8zSOaMb4gF1Ov$ 8peaa;ED ɤQqɷBӧn )'8A3 5M 9$ `,T#??h" T(5XKӺv\D@.`ڽ~Zkwj%+f?];t[XKG޶J_ni). ֬^KW%wҿ*̽Aub\PÝVxhسe@SOvX l}LJӌcW=uQC8D^i̼\m(x.9l6I?doѳ=Sۊbįic3A~ڇߺTR]*-L|p`>/\#G[?(03%lV{-KOgp d'oOef*Ul,2~VҨ5^K=*ۋ]q[Mܺalnt_ /YuMYY6HMg/(Zm[>Z[=q%:TCrlƷti[EkUS /rr{nCo7h7dN7!qϑ :Ԥci2w ӺOp|%a]۠&D,&g[Qh/ySͻ ={bއw;1Awt>~n'ûɃwn} }j}ݼQ z?>l㺏bܣw~ŸG1Q{ŸG1n 1(=q>G1Q{ŸG1Q{ŸG1(=qbܣ(=qbbB1Vqxgs0_m,qknie$o (nK~2qqU]ZW\wL,s\UJWe㪺${*Wu{U|縪.1+;Uu)^qU*9K淁8ς-"Y"п;.>R8ؒbL7S'XK}ޛљbQI˵XI:hNO!7&+i J%bԳq Ov>8(d>0L=8ymwNZSd"ܔ ;c[֏t Ɏ'XxDғ3f$ghw^}ЛWe{[ZZϤ;;7hڊ%`NF3  =ؗs|wvdYn(Hq [|P v֜#1pevl ɊIQ.lŹHzsy-\/t-;_N8`Ҳ.jt lŸΩVӄYQomX Ԣ~DzMrg ϣ]yAވ0~hb-E|oO<,cJ?E wWApU̓U=k[;O/4dG l`Vsy$p,%36ή'ggZ+I{poP%=.u_NxXi0,H±!oQ$g2,cT^ȏ A`R5%͉ô7M n"Y\pXmZFa _%Ř z_ >W%2+ϔ3ra62Pck)V a=VLzԔLt~\ԔLt\ԔyL~\Ԕ9L)9qQS0Qs㢦`EMD鏋R;F;摳33Uw )SLtO;Xk`4Uh2ԧ,'u@ZP_[# @`Ѕ'mE޺]8@S  vLև KSBОr˚h^4=6. Ldt?_Kd@8^_j]ac4+'C=!˖&5C4;&Vܦjj?5ݮ }]ga*U{y'&E\rL6-6S?>*J5 x,kل1ݮ+r]잉X,j;B=$8 [F}Y0- p9A4_4B Ŀ,?2CDž "5A%Қa`Oa;S]0N~!4h9ziAa7%OԲFx  D(qhnV3\ nc -5NZ_؀ں~4eXv?~ :>sVCV@3(\}v_| {U#uHFvWf:_=19F":r~Z1a0*uk] 5Y7MI_{d5l? F@L!? BA7y1}Ύ`n,hXpYn }_{V>5CP'LgXc?TV5klE-+mM`Aq#BYDSë7>aL .bMi $l8Cxad{ 4'9O i%в XKן7qQ8J"ubY rF1#d˃qsEhzhz_؈pKh_c'diWGB8`$SxMZl&~QcX̤GG9u~l ٺ`(}avVX Fdմ0s4WJƃ%B(a?Ħ \*}kaƆ2xf-Dwmo $J9t!T>hVvS@˸q b^|3g [xX~3,RN8MS, cT Ʋ *mz1KB9}UvT#~Gc NE5%6mGpn=0]lZC Dlb2M SxLG_nOG>P 8qj]y)A`N(+Fa  {%ikxb.tIty13G Jxn[1g;6v}_6 lWXCxfzw35|^t?oY 0d\:=Qd7"p^$cgzFzͅ?:pcaF]OM4({u9wopǮN]u )?P%Fpna;v@@N4@Xd LxfG@f,ijIR}(lVFHOBH`[3?C`{cRxH16 X%kX7J;G}o~H!O6{VM2k+$(d/ ;'Rl❇63Nj.&T)CoZ[0 dz$>E"IR vEn,6VtApc FɢTxB%FE5|T v>&ة3s+Sg27(`1Agce1wѼQkTOIΐ5C *#vk ZӌQoNGل(MI1/IvƱpCf0#qO7Dց0@\8e:2BL1=k X>P8aAR1!Od?)೽czEt)I(uAT rZ Fc @Rx4&) j6OT~t#|(9A¬1m%#YP!3?t kx33ǖ0<)| w#pǓI'n?N" ˟mqXP΀`6.l|1V+PLrN5,QLSՍj(NNNNNΆ?y8uB7?y~<ۈؐz?P xy A[x( |?~!$mO<|K'Tnv9\bϏз %sټ1?AϚ*dBm9pv̾kK[:Lj=פ^qZP(@AʯVS8g12N~_ dGb /Z| ECPpoG(^zIk7N*y2?ۉH*2T9=8>"ĥp!G?iS[9'$R1SEi35v# E1j+O3UorVSڤlɱ' \ ؓ"B^,1ݡgyA(R?z\0vd pw5Uk1``C{)q4\7Ƒ3TU W sWb[Cp#I"1ڀʗ&^DحVj -%3 2PMNlf:uQ*+z F$";ÄKmgsvU,R2ŠƟ~-ʤ_rVl _,.uT&-vM[]ZxH36tP ;-MnZWT 6G?320+jSS2v-V$40Pg.X*ʞy ji\ O Z`oz#j (=/jj0"H PV?J P2LZw y0+ӲchYyC(PA\+n լz ߩ2 Rv)^euk鴖jL[j0W>CNf&_jG[zX =[xD`t}Xٮ@{k(W#W/LXsR)xfÓ]zCWm#')1R1+DpG[eQQw½ A ދeEQlHrT*%͢@DqePLZqyLkY10+_y;sSHDtZ S PP&&aol)0 :+ab7g,P\lLP)(x9(97 `f$XdVodIPL &!9Jub &Hj|hGŊDJ桻Wb2zvT$܁gz6Wj:yGKMT~r@sc)\I6:tϗ&d80I9M5d{BD`C IxX%*]_L$0S puAi#K8A)\6Ee+DY-hZʞuS'0Vq-) C[nm[: ncde"JxRr3i%Đn\SvROm-Z~™tI'!v1(XnZwҰo99' oY~̶Hً<n"5踒ߞgO qE6h-Hu0ܭMv1[zYs%^Ii~O,cW|55Ki2 yr ucr:Osy%9bӻq:.뙎ۉLx&L>+{DnT͟sP:ɺ|e:=xOMB$g"ټzY_"gAB8Z0 I"p\0)۝ǡr ~^>wb,/ HTau|f5BƠ)FUm8([}ڕ93 mpfR)I ]?eMbe\ko\(lƻ->Qh:[%3Z22!?$?j|wCnNUx`D(E@sB[6Wx=ΙBAa|s P dMd:65x3ȈEz1MnH^Yeْ3 0 o$ӊ;X$ec-[^-'l(z])8FvkpĒ.x+ )·U\42z?VV@*j)o0d+\#_9$m}w ,}CNoŸCl|(kaY$ج5rPaV4XYӺg ӺDWuZyRtyt;B@y2s+sT-l;)2~tH⧯{lMu0RG*?G~)q,bV;r<e_2|=Dgd.0NޭD p{0eD*nY؀6gʽU NwġDPakH+[SΩ5󻯇xeT0WS"c\`:5!Kd3)ֈ]G!+Ql+@{TR#kn%쭖ʟaDZ238-I: B+0_q:}K7Fz|0Xb|(nhT.SIjɮ҅ه3pߔGfH"K%wC^TGq1Jˢ]@ͭmYa#BHLxm$$dN "4{y}tC~=ze-ӈ%ېњ cZɭسΏYMy@V/g`x?TƕNʇ76k֫Xʉ2x/G<YcE4pЀij,UsdV}lQBRBO@&I#de,Ƨ0ʢHV@cK)bQa2ve}헸 mzp"J*J1>Vo"WaAoH+B  q9 uG8d ptCBաk*. Aб:$T@ؘ k)i4R#a|:]JQ '3SU)߄(ZjG\.N;`OoWx +xw}{̥ IZ*zG'?v.E0 &KaV9Aӡ &EzuomWF]Cs6tUkv'P7'.ӒLŜReZlZČL I痳̵SKӅ8Q W{oY 9E*{+d;QDA$<1.Ia9A%K]x+QMy, (!ЌR>u5:9E-Up&S'Ǎ,E2U30$̅>$*Eht$S'"NбAyXYVJaqzMjyɅ!l4N{5Bl5`eWiHRyrDItjc9/,_@ER9lRq+) óXwQߵלq2"rQ'fvjci?"8bz;O2csp7qOGEl {]$E2o0>%@~ SIzS=V Ɲ0tSP\(0祽}&`K!>mdm0WC&3C2g ȐS9J"%tHT(S2i%+lqx&8NMsP|'('{#s]Nv ƋhUU^P:^U`bLd(50n]`>Uv󫄉!iQlSnu6?S4]6=tgz[[l釺@Ÿ VPse~p %pFHPَR]8İph6=Z|4u_{[Ɠ.$l.4[VsD[In5 "m>1_ADA$&S1 OJBpDSPPUICѯ%YBԭ憤gXz.ք)@C\yАҭ?!D-+d$7iUȿtJf:4p' k"1 'c@u#Px'p[}NPN%H2REõ ZUcƬM:%4D՝e8;"#mƒ8=Uw8$(rI xWvX Ϋєُ ӄݬ %̞^68ȝX6P \8".ʾT(uXLjlu!r׉ڽK4Ӳ$hV쳢4tUS1pPro]ē#>a_/, "b9 l4NװڇAG\J.IeUFXnVU M$?ܮVK0cKZ Xxj#͍Gnz@?ܵg٠(Eǣb$#uw#>*e0vڇT>yi=OTncO`Dx *J $If`}{'R-sbղnW}>pFF+x*kCt<6EbyizXďv?# ?_Os 8\_;+8:Ajoa - VI3šqR8]K /֊u@=|jV-y}RJpX e:%Qݱ-e>hf%,0-,2L/]t$E*5uD2JQι`og2bO}Ae&po/Xʑ+M ,z(G5yNbj'9ʫ R,.hySm'(ڻy{Cw"14QMbJxTv <+w" RւM*U[#rVgj\9 kk `( +' sI{(g^"W1(tavN*MW"oe|wEOq~7' Z^5~츸J*kOo~Y8+C/CƄNm" Fb `t2pqp"Hby5Z^?]c*ZHJ=_^6rb 7 M4 mN ?|ˇ/Wbo炰 e?Wh =l $#MEWDxap5&s|NrKS]<)ӟD*V*jd04|ὕ5I 7[i^ /b3B˅ie]WC*u$bn[#ʱl&"n z"6d& Lb/AHn-Fa@c\$da0w!(Bၕ zVnΧ<[#de%v{t>^Ja%}+Vo~ TZJAh@b6ւ}U'͢^Vc| XH/2$^#t^k뗙k@0B\⹎m{,-7̃9=i9w )A79Yn m]Kैkkf.Zk`WC+zZ3M ;|;z5%/ѫǜ T.1ie=SEx 6DZ;ΈpǨiaZK>/ l(kD7Mh #߷pcd; /&LE !VUh:| r"Pt4Qz3+*IUX*G*3@BՓVG{+Wqg8 ;gT2PpFVe}!%,aYpZpcvR0,s)%>8xA"|JA5 [+I UiĄ1rJCʺD& 0r7UbI?>D1q@L7훬BuHg6݌#.N{v"@Vr*ճYTL;S˼*'gFr^7XU CA7[buUD,$_CAOulf R2Ǽx3"m)\J:Me~z=+m %F^^]m)"E R\*vRP l+ '~& `۸ M345T؆H\ayt.u^ĸq>ʩ9ڲBCEʒyRD2=ϜTVٶTV9ǔ{ǁV{EEsS+uj9$M_%,j+L#*75-Cl?~ ?W_jEHxeK}KɜǑ}`@*,?(6yW^3Wt_4^F>^}U2'] ]rp*Iʥ/lwF'LEIީ;g4c (Y!i=:#SV7:ђ﵀#&G: Т6 S^!B8 .i,M\Yׇv䍛W ]00&ƆMD _P(+JÆuFIvpp2JZT3Kg XK( P8K`>PwX$-p8kH6 0ꂤө$:'cp\ ^;c$Uᆭ x_zDޣ25ףdER|JhPS )&z&ʬV6 #|WR`#V[,4<<5¢4clS B(PV8c2ik7_ɱ R+6s= NMڱB>C71Nƭ$;U0vH*+&g@Ά9Oa@LΠUV';:ތf:G'gK:jSoMt~}sq&(}Ev~6u :/γNךzՔ!c~'I_]I6ZS<݆cmDh\`l3\QPŪ\H1B}Pr( P/iKl%`cUirA*PB7)vk;8=)k\uɵ89=9yyx~m2Loq0$.gH<k"'JJUn4Xa:+7,H$5}q^Yi%xpJ 8{`GU!;1 fxN,%ϵ+)㣣v[Gn;y {zxN l~ { 騲pG''l,N=bx ?3)dO^DhL`lNY. H=ڂA `/''saY:\ ~),('zO5](8BԱXQ(>_[6L~$M7(ڥN!} Eؽ9+4$?o5c,3ޙ^V8z63è7l.Ғ U%W}/!k(xRpvHpn )θ$Mn^m QJ$g6 Dp[>7FdjJf*K e)<2tUNCOeԄg"G=p4J-gbh"(+s")lO!"1aJYc8Jrnġj{q):6鶝{R\.cꚌIMApW >+qdMpx#ԇS-`. T5h DY ̍N7Dja !HmH‹-pFr/ʒO"L 3QU&k6k8$cV׳s+EYn Ϩ0e3Ĥ)P SJJg"3q7Uq?%J {EcԒr8o,;u)мhWZbWn۾L|dW$.SHK'?<&Xp}dd9;GQbT߷4UbynJ`J /Uʄ擝z׫)S)&ou{J%ױOUpiefOvy@NgՓ. &]) `5Lh`9V7f&Fͮ z,z/_^mZ͌z˧m}x*Fz \xS]^~|rO^||AO *YFW-rj[%hGkM>gwB?cv d[* /{.k ̢A++3.+Cm"Q)Nk F[yBnטI!U iYZ~|qy[m :cs0tjq c0ju_2y3$U oCQwn5Zd01|]*|hox;m}LMhS`O'RQ(Q-|MPI* ׶~ [XV3L֋.ފ *NyQP]2 oY c :-TqD$сu(p::IYPR'99,+.pA]( L$.ѥ`͆J'WE 2o4 #d23bhlN ǽaNjSósE /ڇO qS g$v55(T=Ե{Z8r)`8;?1Ef:L2,p:, 8g1G'֎+Yx+sOkm8J\1K=Y>X!qC|Yfy+=t>G8E}) VvTK/(8#TqXoZڳ"gBz_|yI%4'2qb4VG-)/Hq"QQX<ArӵZ%BsaR+9E&!FP|RULc,oEx >Ff-*tgB]H΍i޶ *b?2K+RhrޡxRP;xvYE^hLP5`ט_P_ bFJVzQ tIկL"5|řUsZն\?ncWTLғ O<'ݳ] fU/wFBF2 2& 8FV͙.3It. fq<wuN^"բ~eֱ"g9>ه6!ơ¿F&ZTTzýJ?եLf;5(CgkȖʑwmFO")Po}Gȇ/;, Bߕ,a̲sx#@ c "gȤa gL!ym/Q2* 8Qn2%@Sk[Ռ)*Zɤ*XOJc/ta=PKCKPKf#5 styles.xml]M۶WpiOJZiu&Nc'Mk'9z SW>{h=C/=/$~(6Kz=| 9w#j2Rc r6w>Wϟ Z-ޚvm+»Q9oq\DnE[[ 1|u+SD]3k׭Le b5[5R sRx-h{; y\.Ǭ4˹g3)Cv#Ġ8$'حWAFEo 1mL8ީY_SSx[)dy` ޮn_T6Yniri>B(* WLfc /{ n6b.4" 繁0$(FOk`@Մ?}m+~)Fw7xow+dbGβSFt\ɏi@sM6ϢA]΍@"8 khk9#|AlKxp?2AwȄSߜ>Ò+ur-*r,*_ƂN1Oupjm=@Awiu~숞Y; p|rP1E5j[~nh5Ss%9N;UzS uZV^Υ6[|[wF}/ Y-$_%ϐmc{mV0+RyYLV[0#>G;jPGjueN8606{۸GyJ}"r!*ĩ9 D6v/Z񀻵Q$'x"Hwͷ^%n>܍&W,m9rBJP]`ng} 6fet4F,\n7#jMV % *(^DU`D aqQ`xa`X%=9&Q ݻѱQ:t7=`9&[J'Cl`0V%+r`q Y[Ȼ}6a#6@F.fll!3{91p!aQG6|(h1.ͶV%ŘJ}tlUl,w#Mx5m =)&v2ob FjZ`yF8n`*YI[{-GG^BMuǪɓ}U1@IQ`_յerL|&D곁Oʸdѹ ')C aLr2@rY~P^ʟ>$˚Ck.˖fzc}eJsW  z[;}nǨ9]< E,.T9yʨR*4,1\Al)OD6h/55іCxGX,r1"0"z]en.hS؟7;xW47PN̡̻ 0sfO;̴1=uȃt\5ki$cYLi#2f7j!y "e0 g{ǼRn6Ę=IfKXZk CMN\8/%iۡbJZpdRrwK,YV"zirSɷ@\bKtmkClV`|SckHِNm%i\'r|eV "7 NX{}rdZ%H۞bW$f@ˍG[@c`m<@8*Y鰱FJZ5!M? PXgGip:M?- XgGi\}Ǫ9H^_0`/EU`97`\8څศqq8V󁹬sy`VRn|kaᛄ Y12N6;esϢ:{8*+}9 )V%Z sV?jkŞRvgwvMrzyrj-e!N^Z>ΌRVѳ5VrY_hB<>noyY]jMHD:jRQ+' {Ǩ]N6Ey_x^i zm<%aavL4iF4zIɍy͒a"o ƒJJnWۆ.VIm%'5,ycQXhȃ.RDrH- 1O׉C؀xǿSԊ T L>û- M:̗`ԗEրf8X0hr:&;2Ԧ5,hu +)q ED>äe$Ư#xSf݊7;CJ}a=Ӷ`?2g0Y?=Yo=Ͳ==DzE?`k}ё^HDkHwK$7{OûRsξX>3Q@TdR~sLoPqۆ=|7TP s&@ǯ4T)[\P"ïu{BiA3q!ͤ=@8ZU?dž&`%%-wETߍhjjO[՞}ݪUVj/[&EDžh[~G0XU>4lGriKʚٟ\ JE<*b,r7#k:x/⯖'W:;cE;zVRK s``)I 77GH;$Zi uݗk5\kޟk駵N\?ך^˵XV-NKAjG-L7 =1'kg\YIȳ.syܵL;v]ks0yصO;w-I{&si0; Ϻ.&SIK t#:$u]ks0صO;w-I{&гs>`#sysyܵL;v]ks0yصO;w-I{&-sV]BUWT{2E 7$=L+ٙZXf(pZm*B蘧c7>Ʈ{ͺ|T3wi"lK۬KN5EoYpj.msv!q?Qpu3e4ع ;8}󇝻ְ󇝻ր󇝻̟oZg?C?C?ߒC?Y? Niq! F%2ؗk >عk ;عk 8رk >عkbzق@L L L L  $@I$ $@I$ ЧEw{yN;w;%w*ߑ_#o)G^Sq磧m.yDGyDGyDGLQ.Gya]5MO,(Q#Ne4عw 9(oR#v]#v]C#T*{#M\"2(2(2\2( @mhGy2pd4x-yνkyĎkyνkyνkyĎkyνKgYEW3iyJ3iyJ ׫T0iyJ90iyJW|LZޱ"O剼<'ڦ<'D } <-(qjFk0;CfQ2=ewF%O>Un LԉJc&s!W\D iI3ǶT U*G5l?3Z}rӆkYt0]0-I'0"mZ 6xDNpgww0tAFt⒬$ŕ>_h-6[: jԗs {!b õI< &wȥ-`0b~+ =4Q S** xI4n#1̘$BOy+P0 Aգ 8.u !c[ 1=S׫;p)ddD(M&7 8~ Lh\5[QdSccszFO#]N?̕?1'+<6T~ٻN~yeJȤJ;.ƅ:,dV!^aό*UͤļVtqI1zl"#GPK$HjPKf#5gmeta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m166$Build-9021phpGACLjrussell2003-02-06T08:32:002006-09-03T16:43:032003-05-20T18:57:24en-US60PT14H31M4SPKf#5Thumbnails/thumbnail.png? WNْeVX9ls>Lʡn IR,D94r(,fc6D8 fr*~_C /Rv&l5j>4Cd(G+ Omm]FDGE}$/^BZc0&^sC%lHvmEu:ۻjc)ƥ/#8S܌蟓^~jMq--_f8%բՖ_?}+7:ܓ}`m* 53I4 l]Lc\tH< .=Uh$(uB6-цS#ڹtE0練e;HDӽYGf=Q_2D׏O"glǙ3J[g&\ؗmI[KSi]ࡨ1)"68I[OxϺ^Ľ =&7C5)O.(S+ V-W:B^i[/B{^m{>q?Ts l#XDc@}8ب+?zqJSO` lxL`o_cbMG}ߌv֫= 52L G"*R#סBl}`tqXo.cٍg̒3˽wzlr4 Jоy|fUӪ9 "yrلk|ܬ,퍤/u.\2Z8z~㙔iZ$2O|dg4ׅ O]ǻ#t_6fXugi$a}jnfE*?o1҆gҌNE=OC?;..% 3Gvj%tcC&N/Ծ LS,yk j)o v](XK~t3:IwɐPXPR,c#27^M{ Sru%쪒=bT PAː]R $6LG(2JtK@o& ԯ{sDnzꗖ8J`b'{^4z-:DLJA2CEpK $}Ʌ^Y"SpE.;L.!fIoBw'hd@YVj*n ]nO^} uæ r/zPP"$-fxJ1h0(sB1 ٚmbLN/x7!T5tbaTD$lhEi` 4Ra-5)X6ج^־ǟN$>oy|:>q[s:w\|:߹?tǏ'?I=y5ny;^׸5x߼Ck4ckx͗ޫ'ޫsb}Wo^yyr^?^ …<L+cBaƠKe0wXtibZ2m }.0p*xP⛑RQrr5 -|^_|;dQP<%\)"VeUS)}dX؜("M }v ~NO‘ʆ?lD66滇n+ʪb[LFihƍb&STjikݛx1֮޹YT|s1^ח+N,^[GŠ[ՒRMъfjDǾu/F7j57UWcC, m@lD=Rb纉wKj6 k*. P +ðH`=>`:H_t+ ƄzHN0']Z& QNBM,('L BM<`KYpuӈ H]*YDDh}v+; Hu v1vdr- 3[tU3tHKƷ)ֱpҠ{vPME.:V &])SYF:C٨VkH DEᮊ By` "niw9l冿6^]& 6xe` $H&vøMC*OfY:{;&uɤ6lMT02%6y>5"cKDӦH{ _@*PK X!PKf#5META-INF/manifest.xmlݎ )L-mR^>"* Ugtw:c )%0~kiÕ,9&p+/4uOHe`ҽ'gp"\hZ&7چIr@}\dl-+ҶS]Unܣ˽VBL[AKvR);I `͚aR&XUQu?Ůl"m@xvt`pPJ  10fPǝ ceZ9)?ZM)nEw^} J'cTUuZeXNAi 7^'h:$e<2jh,mد?2qy.j%^ezj(YVe\K)%tT1ԟWn=v;A0plÀ James Russell Karsten Dambekalns Copyright 2002,2003,2004 Mike Benoit Copyright 2003, James Russell Copyright 2003, Karsten Dambekalns Document Version: 58 Last Updated: September 3, 2005 - 06:55 PM Table of Contents Table of Contents 2 About 4 What is it? 4 Where can I get it? 4 What do I need to run it? 4 Who is responsible for it? 4 Introduction 5 Understanding Access Control 5 Who/Where 5 Who/Where 6 Defining access control with phpGACL 6 Fine-grain access control 7 Multi-level Groups 8 How does phpGACL determine permissions? 9 Adding groups 10 Adding people 11 Resolving conflicts 11 Naming Access Objects 12 Adding Sections 14 Multiple Purposes 15 Access eXtension Objects 16 Installation 18 Basic setup 18 Advanced setup 20 Reusing an already existing ADOdb installation 20 Reusing an already existing Smarty installation 20 How do I move the phpGACL files out of my website tree while leaving a link in the tree for administration? 20 Using phpGACL in your application 21 Basic usage 21 Advanced usage 21 Using the ACL admin utility 22 ACL's 22 Creating 22 Sections 23 Extended Return Value 24 Notes 24 Glossary 25 ACO 25 ARO 25 AXO 25 References 26 phpGACL API 26 phpGACL Examples and Tutorials 26 Access Control Resources 26 FAQ 27 Can phpGACL handle large sets of data? 27 About What is it? phpGACL is an set of functions that allows you to apply access control to arbitrary objects (web pages, databases, etc) by other arbitrary objects (users, remote hosts, etc). It offers fine-grained access control with simple management, and is very fast. It is written in PHP (hence phpGACL), a popular scripting language that is commonly used to dynamically create web pages. The GACL part of phpGACL stands for Generic Access Control List. Where can I get it? phpGACL is hosted by sourceforge.net at http://phpGACL.sourceforge.net/ What do I need to run it? phpGACL requires a relational database to store the access control information. It accesses this database via an abstract wrapper called ADOdb. This is compatible with databases such as PostgreSQL, MySQL and Oracle. phpGACL is written in the PHP scripting language. It requires PHP 4.2 and above. Access Control List administration is performed by a web interface, and therefore it is necessary to have a web server with PHP support, such as Apache. Who is responsible for it? Mike Benoit is the author and project manager. James Russell and Karsten Dambekalns did the documentation. Introduction Understanding Access Control The best way to explain access control is to use examples with real things rather than trying to relate to concepts. Han is captain of the Millennium Falcon and Chewie is his second officer. They've taken on board some passengers: Luke, Obi-wan, R2D2 and C3PO. Han needs to define access restrictions for various rooms of the ship: The Cockpit, Lounge, Engines and the external Guns. Han says: "Me and Chewie should have access to everywhere, but after a particularly messy hyperdrive repair, I forbid Chewie from going near the Engine Room ever again. Passengers are confined to the Passenger's Lounge." Let's assume from now on that access is Boolean. That is, the result of looking up a person's access to a room is either ALLOW or DENY. There is no middle ground. If we mapped this statement into an access matrix showing who has access to where, it would look something like this (O means ALLOW, X means DENY): Who/Where Cockpit Lounge Guns Engines Han O O O O Chewie O O O X Obi-wan X O X X Luke X O X X R2-D2 X O X X C3PO X O X X The columns list the rooms that Han wants to restrict access to, and the rows list the people that might request access to those rooms. More generally, the "rooms" are "things to control access on". We call these Access Control Objects (ACOs). The "people" are "things requesting access". We call these Access Request Objects (AROs). The people request access to the rooms, or in our terminology, AROs request access to the ACOs. There is a third type of Object, the Access eXtention Object (AXO) that we'll discuss later. These objects share many attributes and are collectively referred to as Access Objects. Managing access using an access matrix like the one above has advantages and disadvantages. Advantages: It's very fine-grained. It's possible to control access for an individual person if necessary. It's easy to see who has access to what. The answer is stored in the intersection of the person and the room. Disadvantages: It's difficult to manage on a large scale. 6 passengers and 4 places is fairly simple, but what if there were thousands of passengers and hundreds of places, and you need to restrict access to large groups of them at once, but still retain enough fine-grained control to manage access for an individual? That would mean a lot of fiddly and lengthy adjustment to the matrix, and it's a difficult task to verify that the final matrix is correct. It's hard to summarize or visualize. The above example is fairly simple to summarize in a few sentences (as Han did above), but what if the matrix looked like this? Who/Where Cockpit Lounge Guns Engines Han O O O O Chewie O X O X Obi-wan X O X X Luke O O O X R2-D2 X O X O C3PO O O X O This matrix is not so obvious to summarize, and it's not clear to the reader why those access decisions might have been made in the first place. Defining access control with phpGACL It seems that for large or complex situations, this 'access matrix' approach is clearly unsuitable. We need a better system that maintains the advantages (fine-grain control and a clear idea of who has access to what) but removes the disadvantages (difficult to summarize, and difficult to manage large groups of people at once). One solution is phpGACL. phpGACL doesn't describe access from the 'bottom-up' like the Access Matrix above. Instead, it describes it 'top-down', like the textual description of Han's access policy. This is a very flexible system that allows you to manage access in large groups, it neatly summarizes the access policy, and it's easy to see who has access to what. An ARO tree defines a hierarchy of Groups and AROs (things that request access). This is very similar to a tree view of folders and files. The 'folders' are the Groups and the 'files' are AROs. Let's make an ACL tree for the people on Han's ship. First we define some categories for the people. It's clear that Han and Chewie run the ship, and the rest of them are just passengers: Millennium Falcon Passengers Group ??Crew Group ? ??Han ARO ? ??Chewie ARO ??Passengers Group ??Obi-wan ARO ??Luke ARO ??R2D2 ARO ??C3PO ARO This tree by itself doesn't specify any access policy; it just shows how we're grouping the people who might request access (AROs). We apply access restrictions by assigning instructions about a particular room (ACO) to Groups or AROs in the tree. Han says: "By default, no-one should be allowed access to any room on the Millennium Falcon. But the Crew should have access to every room. The Passengers should only have access to the Lounge." Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie ??Passengers [ALLOW: Lounge] ??Obi-wan ??Luke ??R2D2 ??C3PO To interpret this ARO tree, we start from the top and work our way down. Firstly, the default policy is always to deny access. Permissions have been overridden for the "Crew", so they have access to everywhere ("ALL" is a synonym for all rooms: "Cockpit, Lounge, Guns, Engines"). The "Passengers" have access only to the Lounge. This way of describing the access policy is much clearer than the access matrix. You can easily see who has access to what, and it's easier to determine why they've got access (it seems obvious that Han and Chewie would have access to everything, since they're grouped under "Crew"). To summarize: Access Control Objects (ACOs) are the things we want to control access to (e.g. web pages, databases, rooms, etc). Access Request Objects (AROs) are the things that request access (e.g. people, remote computers, etc) ARO trees define a hierarchy of Groups and AROs. Groups can contain other Groups and AROs. The default 'catch-all' policy for the ARO tree is always "DENY ALL". To assign access policy, work your way down the tree, explicitly assigning permissions to Groups and AROs for each ACO as the need arises. Fine-grain access control Oops! What about Chewie? By grouping him in "Crew", Han has indirectly given him access to the Engines! He doesn't want that after what Chewie recently did to the hyperdrive, so he adds a rule to disallow this: Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ??Passengers [ALLOW: Lounge] ??Obi-wan ??Luke ??R2D2 ??C3PO This is an example of the way you can control access policy in a fine-grained manner. It is not necessary to move Chewie to another Group; we simply over-ride the access policy at a lower level. Another example of fine-grain control happens when the Empire attacks; Han needs to let Luke man the guns, and let R2D2 repair the hyperdrive in the Engine room. He can do this by over-riding the general permissions granted by their status as a "Passenger": Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ??Passengers [ALLOW: Lounge] ??Obi-wan ??Luke [ALLOW: Guns] ??R2D2 [ALLOW: Engines] ??C3PO Multi-level Groups Groups can be extended to any level in the ARO tree. For example, you could add a Group "Jedi" to "Passengers". Most passengers would be categorized under "Passengers", but Luke and Obi-wan would be under "Jedi" and therefore might be extended extra privileges (like access to the Cockpit): Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ??Passengers [ALLOW: Lounge] ??Jedi [ALLOW: Cockpit] ? ??Obi-wan ? ??Luke [ALLOW: Guns] ??R2D2 [ALLOW: Engines] ??C3PO How does phpGACL determine permissions? When the ship's computer (running phpGACL of course) checks access, the only question it can ask itself is "Does person X have access to room Y?" In phpGACL terms, this is rephrased as "Does ARO 'X' have access to ACO 'Y'?" phpGACL determines whether a specific person has access to a specific room by working from the top of the ARO tree towards the specified person, noting explicit access controls for that place along the way. When it reaches that person, it uses the last explicit access control it encountered as the result to return. In this way, you can define access controls for groups of people, but over-ride them further down the tree if you need to. Example 1: We ask: "Does Luke have access to the Lounge?". Set the default result, "DENY". Work out a path to Luke: Millennium Falcon Passengers ? Passengers ? Jedi ? Luke Start at the top of the tree and move towards Luke: The "Millennium Falcon Passengers" node doesn't say anything about any room, so do nothing here. Move on to "Passengers", which explicitly says that "Passengers" have Lounge access, so change the internal result to "ALLOW". Move to the "Jedi" node, which doesn't mention the Lounge at all. Finally move to Luke's node, and again there's nothing there about the Lounge. There's nowhere left to go, so the result returned is the current value of the internal result: "ALLOW" Example 2: We ask: "Does Chewie have access to the Engines?" Set the default result, "DENY". Work out a path to Chewie: Millennium Falcon Passengers ? Crew ? Chewie Start at the top of the tree and move towards Chewie. The "Millennium Falcon Passengers" node doesn't say anything about anywhere, so do nothing here. Move on to "Crew", which explicitly says that "Crew" have Engine access, so change the internal result to "ALLOW". Move to Chewie's node, and there's an explicit rule saying that he doesn't have access to the Engines, so change the internal result to "DENY". There's nowhere left to go, so the result returned is the current value of the internal result: "DENY" As you can see from the examples, if a Group doesn't explicitly specify a permission for a room, then that Group inherits the access restrictions of its parent for that room. If the root node ("Millennium Falcon Passengers") doesn't specify a permission, it inherits it from the default setting ("DENY ALL" in the above examples). This implies a couple of interesting points about the ARO tree: The ARO tree always shows the full list of the AROs. It would not make sense to ask "Does Jabba have access to the Cockpit?" because Jabba has not been defined in this system. However, phpGACL does not check to see if AROs or ACOs exist before performing the check, so if this question was actually asked then the result would be the default "DENY". The ARO tree may not display some defined ACOs, and relies on the default setting to define access policy. For example, say Han defined a "Bathroom" ACO. Any question like "Does Luke have access to the Bathroom?" would have the answer "DENY", because the default is "DENY" and nowhere in the ARO tree does it ever explicitly mention the Bathroom. Keep in mind when examining the ARO tree that some ACOs may not be visible. Note: When asking phpGACL questions about access to an ACO, it is not possible to use Groups as AROs (even though it might 'seem' right). For example, it is impossible to answer the question "Do Passengers have access to Guns?" The complete answer is not a Boolean "ALLOW" or "DENY", but the more complex "Luke and Obi-wan can but R2D2 and C3PO cannot." phpGACL is not designed to return that kind of answer. Adding groups Han feels this ACL is starting to look a little complicated. There are so many exceptions! Perhaps he should make another group, "Engineers", containing the people who are allowed access to the Engines and Guns. That group should contain Han and R2D2 since they're both capable of repairing the engines and guns. This means Han can remove some of those messy exceptions-to-the-rules, and that has the benefit of making the description clearer: Default: DENY ALL Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ??Passengers [ALLOW: Lounge] ? ??Jedi [ALLOW: Cockpit] ? ? ??Obi-wan ? ? ??Luke [ALLOW: Guns] ? ??R2D2 ? ??C3PO ??Engineers [ALLOW: Engines, Guns] ??Han ??R2D2 We can read this as "By default, no-one has access to anywhere. Crew have access to everywhere (except Chewie, who has no access to the Engines). Passengers only have access to the Lounge, except Jedi who also have access to the Cockpit. Luke has access to the Guns too. Engineers are allowed access to the Engines and Guns." Most importantly, we can see that Han and R2D2 are now in two places in the ACL. It is not necessary for them to be uniquely categorized at all. This defines the policy more clearly to the reader: "Ahh, Han and R2D2 have access to the Engines and Guns because they're engineers." Adding people Han goes to Cloud City to pick up Lando and get some repairs. Lando's the Millennium Falcon's previous owner, so Han reckons he qualifies as Crew. Lando also offers the services of his top engineer, Hontook, for help with repairing the ship while they're in dock. Default: DENY ALL Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ? ??Lando ??Passengers [ALLOW: Lounge] ? ??Jedi [ALLOW: Cockpit] ? ? ??Obi-wan ? ? ??Luke [ALLOW: Guns] ? ??R2D2 ? ??C3PO ??Engineers [ALLOW: Engines, Guns] ??Han ??R2D2 ??Hontook This shows how easy it is to grant new people access. If we used the original matrix scheme, we'd have to set permissions for each room for both Lando and Hontook. Instead, we simply add them to their appropriate groups and their access is implicitly and easily defined. Resolving conflicts What happens if we add Chewie to the list of Engineers? Default: DENY ALL Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??Han ? ??Chewie [DENY: Engines] ? ??Lando ??Passengers [ALLOW: Lounge] ? ??Jedi [ALLOW: Cockpit] ? ? ??Obi-wan ? ? ??Luke [ALLOW: Guns] ? ??R2D2 ? ??C3PO ??Engineers [ALLOW: Engines, Guns] ??Han ??R2D2 ??Hontook ??Chewie This makes Chewie's access to the Engines ambiguous, because now there are two paths from the root of the tree to Chewie. If the ship's computer follows one path (along the "Crew" branch), the result is "DENY access to Engines." If it follows the other path (along the "Engineers" branch) then the result is "ALLOW access to Engines". So, is he allowed or denied? phpGACL will warn you if you add or edit an multiply-grouped ARO in such a way that the ARO's access to an arbitrary ACO would be ambiguous. But it is up to you to resolve the conflict. If we now asked phpGACL the question "Does Chewie have access to Engines?" the result returned is the result given by the last ACL entry to be modified (this is phpGACL's policy). In this case the result is ALLOW, because the "ALLOW: Engines, Guns" directive assigned to the Engineers Group is more recent than the "DENY: Engines" directive assigned to Chewie's Group. When ambiguous access entries exist in the ACL, the ACL is said to be inconsistent. Inconsistent ACLs can be very dangerous, and you may unwittingly provide access to inappropriate people if you allow your ACL to remain in this state. When phpGACL warns you that the ACL is inconsistent, it is best to resolve the conflicts as soon as possible to regain consistency. To resolve the conflict in this case, we could either: Remove the "DENY: Engines" directive from Chewie's entry under the Crew Group. Add a "DENY: Engines" directive to Chewie's entry under the Engineers Group. Remove Chewie from the Engineers Group, since Han doesn't think him a worthy Engineer anyway. Han chooses option 3, and removes Chewie from the Engineers list. Naming Access Objects phpGACL uniquely identifies each Access Object (AROs, AXOs and ACOs) with a two-keyword combination and it's Access Object type. The tuple "(Access Object type, Section, Value)" uniquely identifies any Access Object. The first element of the tuple is the type of Access Object (ARO, AXO or ACO). The second element of the tuple, called the Section, is a user-defined string which names the general category of the Access Object. Multiple Access Objects can share the same Section name. The Section name should be short but descriptive. It's used in the user interface in selection boxes, so try not to make it too long. Sections are stored in a flat namespace; they are not nestable like Groups. Sections have nothing to do with Groups or the ARO/AXO trees - they are purely a mechanism for helping to maintain large numbers of Access Objects. The third element of the tuple is a user-defined name for the Access Object, and is called the Value. A Value cannot contain spaces (however, a Section can). Both Section and Values are case sensitive. Aside: It is commonly asked why strings are used to identify Access Objects, rather than integers which ostensibly seem faster. The answer is for legibility. It is much easier to understand: acl_check('system', 'login', 'users', 'john_doe'); than: acl_check(10, 21004, 15, 20304); Since it is often obvious from the context which type of Access Object we are referring to, the interface for phpGACL (and this documentation) drops the Access Object type and uses the format "Section > Value" when displaying the name of an Access Object. However, the API requires an Access Object's "Section" and "Value" to be specified in separate function arguments (the Access Object type is usually implicit in the argument description). Example ACO "Section > Values": "Floors > 1st" "Floors > 2nd" "Rooms > Engines" Example ARO "Section > Values": "People > John_Smith" "People > Cathy_Jones" "Hosts > sandbox.something.com" Example API usage: acl_check ( aco_section, aco_value, aro_section, aro_value); acl_check ( 'Floors', '2nd', 'People', 'John_Smith' ); Valid Naming Restrictions Examples: "ACO -Frob > Flerg", "ARO - Frob > Flerg" (The Section and Value are the same in both, but this is fine as namespaces are separate across Access Object types) "ACO -Frob > Flerg", "ACO - Frob > Queegle" (The Access Object type and Section are the same, but this is fine as the Values are different) "AXO - Frob Hrung > Flerg" (Sections can contain spaces) Invalid Naming Restrictions Examples: "ACO - Frob > Flerg", "ACO - Frob > Flerg" ("Access Object type - Section > Value" must be unique) "ACO - Frob > Flerg Habit" (Values cannot contain spaces) Adding Sections Before you can add a new Access Object, its Section must be defined. To add a new section, use the add_object_section() function. add_object_section ( string NAME, A short description of what this Section is for. (e.g. "Levels in building"). string VALUE, The name of the Section (e.g. "Floor"). int ORDER, An arbitrary value which affects the order this Section appears in the UI. bool HIDDEN, Whether this should appear in the UI or not (TRUE means that is will be hidden). string GROUP_TYPE) The Access Object type ("aco", "aro" or "axo") Han creates 3 Sections for the AROs. "Humans", "Aliens" and "Androids". Let's list the AROs with their full names Millennium Falcon Passengers ??Crew [ALLOW: ALL] ? ??"Humans > Han" ? ??"Aliens > Chewie" [DENY: Engines] ? ??"Humans > Lando" ??Passengers [ALLOW: Lounge] ? ??Jedi [ALLOW: Cockpit] ? ? ??"Humans > Obi-wan" ? ? ??"Humans > Luke" [ALLOW: Guns] ? ??"Androids > R2D2" ? ??"Androids > C3PO" ??Engineers [ALLOW: Engines, Guns] ??"Humans > Han" ??"Androids > R2D2" ??"Aliens > Hontook" Sections are just a way of categorizing Access Objects, to make the user interface more usable, and the code for acl_check() more readable. They do not affect the way phpGACL determines access to an object. They cannot be nested (so it would not be able to create a "Males" sub-Section under "Humans" for example; you'd have to create a Section called "Humans-Male" or similar) Multiple Purposes You may need to use phpGACL for multiple independent purposes. For example, you may need to restrict user access to web pages, and also remote host access to your server. The two tasks are not related. phpGACL can handle this in three different ways. It can use an alternative database to store the access tables. It can use the same database but with differently named access tables. (this feature is not implemented yet). You can store the Access Objects for both purposes in the same tables, and carefully manage your list so that they don't conflict. To implement Option 1 (and Option 2 when it becomes available), use the $gacl_options array when creating a new phpGACL class. This allows you to specify the database and table name prefixes to use: $gacl_options = array( 'db_table_prefix' => 'gacl_', 'db_type' => 'mysql', 'db_host' => 'host1', 'db_user' => 'user', 'db_password' => 'passwd', 'db_name' => 'gacl'); $gacl_host1 = new gacl($gacl_options); To implement Option 3, you must be careful, since phpGACL doesn't know the relationship between your different tasks, and it will be possible to make meaningless Access Policy Directives. For example, say Han wanted to restrict access to other ships contacting his ship's computer, in addition to restricting access to the different rooms. To do this, he might add "Luke's X-Wing Fighter" as a remote ship ARO (in addition to other ships and an ACO for the ship's computer). Because all AROs are in the same ARO tree, it would be possible to create an APD like "Ships > Luke's X-Wing Fighter" [ALLOW: "Rooms > Lounge"], which would be totally meaningless! To help reduce mistakes like this, good Section naming can make it clearer what Access Objects are for which tasks. It should be obvious to any administrator that it's meaningless to assign a Ship permission to use a Room. Access eXtension Objects Access eXtension Objects (AXOs) can add a 3rd dimension to the permissions that can be configured in phpGACL. We've seen how phpGACL allows you to combine an ARO and an ACO (2 dimensions) to create an Access Policy Directive. This is great for simple permission requests like: Luke (ARO) requests access to "Guns" (ACO) If that's all you need, that's fine - AXOs are totally optional. But because all ACOs are considered equal, it makes it difficult to manage if there are many ACOs. If this is the case, we can change the way we look at Access Objects to manage it more easily. AXOs are identical to AROs in many respects. There is an AXO tree (separate from the ARO tree), with it's own Groups and AXOs. When dealing with AXOs, consider an AXO to take the old role of the ACO (i.e. "things to control access on"), and change the view of ACOs from "things to control access on" to "actions that are requested". ARO and ACO-only View: AROs: Things requesting access ACOs: Things to control access on ARO, ACO and AXO View: AROs: Things requesting access ACOs: Actions that are requested AXOs: Things to control access on Example: A website manager is trying to manage access to projects on the website. The ARO tree consists of all the users: Website ??Administrators ? ??Alice ? ??Carol ??Users ??Bob ??Alan The projects are organized by Operating System into categories in the AXO tree: Projects ??Linux ? ??SpamFilter2 ? ??AutoLinusWorshipper ??Windows ??PaperclipKiller ??PopupStopper The actions that can be taken with each project are "View" and "Edit". These are the ACOs. Now we want Bob to have "View" access to all the Linux projects, so it's possible to add an ACL that links Bob's ARO to the View ACO and the Linux AXO, and thus we can ask the question: Bob (ARO) requests access to "View" (ACO) the project(s) called "Linux" (AXO) Keep in mind AXO's are optional, if you don't specify an AXO when calling acl_check() and a matching ACL exists with no AXO, it will be allowed. However if only ACLs exist with AXO's, and you call acl_check() without an AXO, it will fail. So basically as soon as you specify an AXO when calling acl_check(), acl_check() will only search ACLs containing AXO's. If no AXO is specified, only ACLs without AXOs are searched. This in theory (I haven't benchmarked) gives us a slight performance increase as well. Installation Basic setup 1.Untar the distribution .tar.gz file into the root or a subdirectory of your web site. You might want to rename it to something more suitable. 2.Edit phpgacl/gacl.class.php using your favourite editor and set the db_type, db_host, db_user, db_password, and db_name you will be using. Now edit phpgacl/admin/gacl_admin.inc.php to hold the same information. This file is used by the setup script as well as the ACL admin backend. The reason for two files holding that (same) information, is that the core ACL library gacl.class.php is much smaller than the full-fledged API class, and there is no need to include all the code when you just want to call acl_check(). 3.Create the database you specified in db_name on the server. 4.Surf to http://yoursite.net/phpgacl/setup.php. The required tables will be installed based on your choice of database. Don't be afraid of the truckload of output, if all goes well you will see only success messages. 5.Now follow the last advice shown on that screen and create the phpgacl/admin/smarty/templates_c directory. It must be writable by the user the webserver runs as. If you don't do this, you will not be able to use the CAL admin! 6.Click the link at the bottom of the successful setup page or surf to: http://yoursite.net/phpgacl/admin/acl_admin.php Advanced setup Reusing an already existing ADOdb installation If you already have ADOdb installed you can get phpGACL to use this copy of ADOdb. 1.Edit phpgacl/gacl.class.php so that ADODB_DIR reflects the location of the ADOdb library in your path. 2.Rename the phpgacl/adodb folder to something else like adodb_x and reload the phpgacl/admin/acl_admin.php page to ensure it still works. 3.Erase the adodb directory installed with phpGACL. Reusing an already existing Smarty installation If you already have ADOdb installed you can get phpGACL to use this copy of ADOdb. 1.Edit phpgacl/admin/gacl_admin.inc.php so that the variables $smarty_dir and $smarty_compile_dir reflect the location of the Smarty library in your path and the template_c directory you already use. Move the templates directory that came with phpGACL to another directory (e.g. one level up). Adjust the $smarty_template_dir so it points to the new location. If you like you can move those templates to your existing templates folder, of course. 2.Rename the phpgacl/smarty folder to something else like smarty_x and reload the phpgacl/admin/acl_admin.php page to ensure it still works. 3.Erase the smarty directory installed with phpGACL. How do I move the phpGACL files out of my website tree while leaving a link in the tree for administration? 1.Go to your website root. 2.Move the phpGACL directory to your includes directory and create a symlink to the admin directory where you want the admin tool to go. For example: mv phpgacl/ /www/includes_directory ln -s /www/includes_directory/phpgacl/admin/ gacl 3.Now surfing to http://yoursite.net/gacl/acl_admin.php will take you to the admin page. If it doesn't work, make sure your Webserver allows symbolic links in the website tree. Using phpGACL in your application Basic usage This example shows a basic example of using phpGACL in your code. It uses the ADOdb abstraction layer as well, and shows a simple way to validate a login attempt against a database. // include basic ACL api include('phpgacl/gacl.class.php'); $gacl = new gacl(); $username = $db->quote($_POST['username']); $password = $db->quote(md5($_POST['password'])); $sql = 'SELECT name FROM users WHERE name='; $sql .= $username.' AND password='.$password; $row = $db->GetRow($sql); if($gacl->acl_check('system','login','user',$row['name'])){ $_SESSION['username'] = $row['name']; return true; } else return false; As you can see there is only one call to acl_check() in this code. What does it do? Well, it checks the ARO object $row['name'] from the ARO section 'user' against the ACO object 'login' from the ACO section 'system'. Advanced usage Using the ACL admin utility If you want to get a grip on the included ACL admin utitlity, it will help you a lot if you run the example.php file. It contains some ACO, ARO and AXO objects, as well as some ACL defined using those objects. After running it, you should see some sample data in the admin interface. Play around with it, and if you get stuck, come back and read on... (yet to be written) ACL's Creating You must have a minimum of an ACO and an ARO defined to create an ACL. Select an ACO Section then select from the available items show in the Access Control Objects list. Click the [ > > ] button to add the Section-ACO to the Selected list. You may add any number of Section-ACO pairs to this list. Next select an ARO Section. At this point you may select from either the Access Request Objects list or from the ARO Groups list. Select on of the ACL Sections (usually "user" for this case), provide a brief description in the Note area and then click Submit. Click on the "ACL Admin" tab and you will see your new ACL in the list. Sections A default install provides you with two ACL sections - 'system' and 'user'. You would typically put user created ACL's (for example, those you enter via the admin interface) in the 'user' section and put ACL's generated by code in the 'system' section. However, you can use the ACL sections to provide any other logical grouping that suits your purposes. Extended Return Value Typically a call to the acl_check method will return a boolean value. However, you may specify a different value or evan a string to be returned. For example, you may negotiate for a user to login at a cost of $0.20 per time by default and another for $0.18 per time under a different scheme. You could create a separate ACL for the default login and for the special use but varying the 'return value'. If the call to acl_check is successful, you will know the cost of the login via the return value. Notes It's a good idea to add a note when creating an ACL to help remember it's purpose, for example "Basic permissions for a user in the Administrator group". Glossary ACO Access Control Object - An action that are requested to be performed. ARO Access Request Object - An entity (for example, a user) that is requesting an action to be performed. AXO Access eXtension Object - An object to perform an action on for an entity. References phpGACL API The API documentation is included in the tarball under the /docs/phpdoc/ directory. phpGACL Examples and Tutorials See example.php included in the tarball. Access Control Resources . . . FAQ Can phpGACL handle large sets of data? Not a problem at all. We've tested up to 100,000 AXO's and 100,000 ARO's on moderate hardware even. The performance issues come down to how well you can cache the ACL's, and how fast your database server is.phpgacl-3.3.7/docs/examples/0040755025754300001440000000000010476665052014635 5ustar ipsousersphpgacl-3.3.7/docs/examples/millennium_falcon/0040755025754300001440000000000010476665052020330 5ustar ipsousersphpgacl-3.3.7/docs/examples/millennium_falcon/welcome.php0100644025754300001440000000267710154675137022503 0ustar ipsousers

Pre-Requirements:

These 3 examples work hand in hand with the phpGACL documentation Millenium Falcon example, which can be found here: phpGACL documentation
Run the examples and have a look at the code, but don't forget to check out phpGACLs excellent admin suite.

By now you should have a good idea about the phpGACL API and how to:

  • Creating ACO sections and ACOs.
  • Creating ARO sections and AROs.
  • Creating ARO Groups and assigning AROs.
  • Editing Groups.
  • Deleting Groups / Objects.
The rest of the API functions are in the manual!





phpgacl-3.3.7/docs/examples/millennium_falcon/footer.php0100644025754300001440000000047010152165445022325 0ustar ipsousers
phpGACL v3.3.3 (Schema v2.1) - Generic Access Control Lists
Copyright © 2004 Mike Benoit
phpgacl-3.3.7/docs/examples/millennium_falcon/fineGrainAccessControl.php0100644025754300001440000001136410152533537025421 0ustar ipsousers array( Object Value ) pairs. $aco_array = array('access' => array('engines') ); $aro_array = array('crew' => array('chewie') ); $allow = FALSE; $enabled = TRUE; $return_value = NULL; $note = "Denying Chewie access to the engines!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, $aro_array, NULL, NULL, NULL, $allow, $enabled, $return_value, $note); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Chewie has been denied access to the Engines!
\n"; } else { echo "Error creating ACL.
\n"; } echo "
\n"; echo "=================================================================================================
\n"; echo "-- Lets test the new ACL for Chewie! --
\n"; echo "=================================================================================================
\n"; } // Lets check if Chewie has access to the engines if ( $gacl_api->acl_check('access', 'engines', 'crew', 'chewie') ) { if ($outputDebug == TRUE){ echo "Chewie still has access to the engines!
\n"; } } else { if ($outputDebug == TRUE){ echo "Chewie has been denied access to the engines! (Han saves the hyperdrive from further distress!)
\n"; } } // Lets check if Chewie still has access to the cockpit if ( $gacl_api->acl_check('access', 'cockpit', 'crew', 'chewie') ) { if ($outputDebug == TRUE){ echo "And Chewie still has access to the cockpit! (Hans plan worked!)
\n"; } } else { if ($outputDebug == TRUE){ echo "Chewie has been denied access to the cockpit! (Not good - somethings not right!)
\n"; } } if ($outputDebug == TRUE){ echo "
\n"; echo "=================================================================================================
\n"; echo "-- Under Attack - Allow Luke Access to the Guns and R2D2 to the Engines! --
\n"; echo "=================================================================================================
\n"; } /* * Allow Luke Access to the Guns! */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('access' => array('guns') ); $aro_array = array('passengers' => array('luke') ); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing Luke access to the guns!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, $aro_array, NULL, NULL, NULL, $allow, $enabled, $return_value, $note); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Luke has been granted access to the Guns!
\n"; } else { echo "Error creating ACL - Luke can't get to the Guns.
\n"; } } /* * Allow R2D2 Access to the Engines! */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('access' => array('engines') ); $aro_array = array('passengers' => array('r2d2') ); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing R2D2 access to the engines!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, $aro_array, NULL, NULL, NULL, $allow, $enabled, $return_value, $note); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "R2D2 has been granted access to the Engines!
\n"; } else { echo "Error creating ACL - R2D2 can't get to the Engines! (we're doomed says C3PO!).
\n"; } } if ($outputDebug == TRUE){ echo "
\n
\nDone! Now how easy was that?
\n"; echo "Remember to check out the Administration Interface which can do all of the above in a few simple clicks.
\n
\n"; } ?> phpgacl-3.3.7/docs/examples/millennium_falcon/millenniumFalcon.inc0100644025754300001440000000174210152165445024310 0ustar ipsousers $gacl_options['db_name'], $gacl_options = array( 'debug' => $gacl_options['debug'], 'items_per_page' => 100, 'max_select_box_items' => 100, 'max_search_return_items' => 200, 'db_type' => $gacl_options['db_type'], 'db_host' => $gacl_options['db_host'], 'db_user' => $gacl_options['db_user'], 'db_password' => $gacl_options['db_password'], 'db_name' => $gacl_options['db_name'], 'db_table_prefix' => $gacl_options['db_table_prefix'], 'caching' => FALSE, 'force_cache_expire' => TRUE, 'cache_dir' => '/tmp/phpgacl_cache', 'cache_expire_time' => 600 ); ?> phpgacl-3.3.7/docs/examples/millennium_falcon/Multi-levelGroups.php0100644025754300001440000002250310154675137024435 0ustar ipsousersadd_object_section('Jedi', 'jedi', 12, 0, 'ARO'); //Must specifiy Object Type, notice it is ARO now. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created Jedi ARO section sucessfully.
\n"; } else { echo "Error creating Jedi ARO section.
\n"; } } unset($result); /* * Add Jedi Group to Passengers * * First get the Passengers Groupid. */ $result = $gacl_api->get_group_id('passengers'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Got the groupid for Passengers!
\n"; } else { echo "Error failed getting the groupid for passengers.
\n"; } } $passengersGroupID = $result; unset($result); /* * We add the Jedi Group and use the Passengers groupid for the parent. */ $result = $gacl_api->add_group('jedi','Jedi', $passengersGroupID, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "The Jedi ARO Group has been added to the Passengers Successfully.
\n"; } else { echo "Error creating the Jedi ARO Group.
\n"; } } // Get the Jedi Group Id that has been returned from: add_group. $jediGroupID = $result; unset($result); /* * The tree now looks like: * * Millennium Falcon Passengers Group * |-Crew Group [ALLOW: ALL] * | |-Han ARO * | '-Chewie ARO [DENY: Engines] * '-Passengers Group [ALLOW: Lounge] * | '- Jedi * |-Obi-wan ARO * |-Luke ARO [ALLOW: Guns] * |-R2D2 ARO [ALLOW: Engines] * '-C3PO ARO * * So we need to reassign Obi-wan and Luke from the Passengers Group, to the * Jedi group. * * So we do this by editing Obi-wans and Lukes object which links them to the Passengers Section. * edit_object($object_id, $section_value, $name, $value=0, $order=0, $hidden=0, $object_type) */ /* * First we need the object_id's! * get_object_id($section_value, $value, $object_type) */ $result = $gacl_api->get_object_id('passengers','obi-wan','aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Got 'Passengers > Obi-wan' objectid!
\n"; } else { echo "Error getting 'Passengers > Obi-wan' objectid.
\n"; } } $obiWanObjectId = $result; unset($result); $result = $gacl_api->get_object_id('passengers','luke','aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Got 'Passengers > Luke' objectid!
\n"; } else { echo "Error getting 'Passengers > Luke' objectid.
\n"; } } $lukeObjectId = $result; unset($result); /* * Excellent, we are nearly there! * Next we need to remove Obi-wans and Lukes assigned AROs to the Passengers Group * and add the AROs to the Jedi ARO Group. * * get_group_id($value, $name, $group_type) * del_group_object($group_id, $object_section_value, $object_value, $group_type) * add_group_object($group_id, $object_section_value, $object_value, $group_type) */ /* * First lets get the Passengers ARO GroupID */ $result = $gacl_api->get_group_id('passengers', 'Passengers', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Got 'Passengers' ARO GroupID!
\n"; } else { echo "Error getting 'Passengers' ARO GroupID!.
\n"; } } $passengersGroupId = $result; unset($result); $gacl_api->_DEBUG = true; /* * Now lets delete Obi-Wans and Lukes ARO GROUP ARO connections. */ $result = $gacl_api->del_group_object($passengersGroupId, 'passengers', 'obi-wan', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Deleted 'Passengers > Obi-Wan' ARO Group > ARO Connection!
\n"; } else { echo "Error deleting 'Passengers > Obi-Wan' ARO Group > ARO Connection!
\n"; } } unset($result); $result = $gacl_api->del_group_object($passengersGroupId, 'passengers', 'luke', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Deleted 'Passengers > Luke' ARO Group > ARO Connection!
\n"; } else { echo "Error deleting 'Passengers > Luke' ARO Group > ARO Connection!
\n"; } } unset($result); /* * OK - we now have the id's to edit - lets change the Section_Value to 'jedi' * before we add the new AROs to the ARO Group. */ $result = $gacl_api->edit_object($obiWanObjectId, 'jedi', 'Obi-wan', 'obi-wan', 10, 0, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Edited 'Passengers > Obi-wan' Successfully - now its: 'Jedi > Obi-wan'!
\n"; } else { echo "Error editing 'Passengers > Obi-wan' Object.
\n"; } } unset($result); $result = $gacl_api->edit_object($lukeObjectId, 'jedi', 'Luke', 'luke', 11, 0, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Edited 'Passengers > Luke' Successfully - now its: 'Jedi > Luke'!
\n"; } else { echo "Error editing 'Passengers > Luke' Object.
\n"; } } unset($result); /* * Ok lets get the Jedi ARO GroupID */ $result = $gacl_api->get_group_id('jedi', 'Jedi', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Got 'Jedi' ARO GroupID!
\n"; } else { echo "Error getting 'Jedi' ARO GroupID!.
\n"; } } $jediGroupId = $result; unset($result); /* * Now lets add Obi-Wans and Lukes ARO GROUP ARO connections to Jedi. */ $result = $gacl_api->add_group_object($jediGroupId, 'jedi', 'obi-wan', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Jedi > Obi-Wan' ARO Group > ARO Connection!
\n"; } else { echo "Error creating 'Jedi > Obi-Wan' ARO Group > ARO Connection!
\n"; } } unset($result); $result = $gacl_api->add_group_object($jediGroupId, 'jedi', 'luke', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Jedi > Luke' ARO Group > ARO Connection!
\n"; } else { echo "Error creating 'Jedi > Luke' ARO Group > ARO Connection!
\n"; } } unset($result); /* * Allow the Jedi ARO Group Access to the Cockpit: */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('access' => array('cockpit') ); $aro_array_GroupID =array($gacl_api->get_group_id('jedi') ); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing the Jedi to have Access to the cockpit!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, NULL, $aro_array_GroupID, NULL, NULL, $allow, $enabled, $return_value, $note, 'user'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our Jedi Cockpit Access ACL sucessfully!
\n"; } else { echo "Error creating ACL.
\n"; } } unset($result); if ($outputDebug == TRUE){ echo "
\n"; echo "=================================================================================================
\n"; echo "-- Lets test the Jedi ACL for Obi-wan! --
\n"; echo "=================================================================================================
\n"; } // Lets check if Obi-wan has access to the Lounge if ( $gacl_api->acl_check('access', 'lounge', 'jedi', 'obi-wan') ) { if ($outputDebug == TRUE){ echo "Obi-Wan still has access to the lounge!
\n"; } } else { if ($outputDebug == TRUE){ echo "Obi-Wan no longer has access to the lounge! (Not good!)
\n"; } } // Lets check if Obi-wan has access to the Cockpit if ( $gacl_api->acl_check('access', 'cockpit', 'jedi', 'obi-wan') ) { if ($outputDebug == TRUE){ echo "Obi-Wan has access to the Cockpit! (the Jedi ACL Worked!)
\n"; } } else { if ($outputDebug == TRUE){ echo "Obi-Wan can't access to the Cockpit! (Something went wrong!)
\n"; } } // Lets check if Obi-wan has access to the Engines - should fail! if ( $gacl_api->acl_check('access', 'engines', 'jedi', 'obi-wan') ) { if ($outputDebug == TRUE){ echo "Obi-Wan has access to the Engines! (How'd he get in there?)
\n"; } } else { if ($outputDebug == TRUE){ echo "Obi-Wan can't access to the Engines! (Good hes not allowed there!)
\n"; } } if ($outputDebug == TRUE){ echo "
\n
\nDone! Not difficult really!
\n"; echo "Remember to check out the Administration Interface which can do all of the above in a few simple clicks.
\n
\n"; } ?> phpgacl-3.3.7/docs/examples/millennium_falcon/index.php0100644025754300001440000000236110154675137022145 0ustar ipsousers

Millenium Falcon API Example by Ross Lawley.

clear_database(); include('definingAccessControl.php'); break; case 'example2': $outputDebug = false; $gacl_api = new gacl_api($gacl_options); $gacl_api->clear_database(); include('definingAccessControl.php'); $outputDebug = true; include('fineGrainAccessControl.php'); break; case 'example3': $outputDebug = false; $gacl_api = new gacl_api($gacl_options); $gacl_api->clear_database(); include('definingAccessControl.php'); include('fineGrainAccessControl.php'); $outputDebug = true; include('Multi-levelGroups.php'); break; default: include('welcome.php'); break; } ?>
phpgacl-3.3.7/docs/examples/millennium_falcon/header.php0100644025754300001440000000173310152533537022263 0ustar ipsousers phpGACL Millennium Falcon API

phpGACL

phpGACL Millennium Falcon API demo.

phpgacl-3.3.7/docs/examples/millennium_falcon/definingAccessControl.php0100644025754300001440000003277410152533537025312 0ustar ipsousersadd_object_section('Access', 'access', 10, 0, 'ACO'); //Must specifiy Object Type. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created ACO section sucessfully.
\n"; } else { echo "Error creating ACO section.
\n"; } } unset($result); /* * Now that we have our ACO Section created, lets put a Access Control Object (ACO) in it. * You can think of ACO's as "Actions". * In this case the Action is the rooms the passengers have Access to. * The ACOs required for the Millennium Falcon are Access to: * - Lounge * - Engines * - Guns * - Cockpit * * add_object($section_value, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ $result = $gacl_api->add_object('access', 'Lounge', 'lounge', 10, 0, 'ACO'); //Must specifiy Object Type. if ($outputDebug == TRUE) { if ($result !== FALSE) { echo "Created Lounge ACO sucessfully.
\n"; } else { echo "Error creating Lounge ACO.
\n"; } } unset($result); // Add the Engines ACO $result = $gacl_api->add_object('access', 'Engines', 'engines', 10, 0, 'ACO'); //Must specifiy Object Type. if ($outputDebug == TRUE) { if ($result !== FALSE) { echo "Created Engines ACO sucessfully.
\n"; } else { echo "Error creating Engines ACO.
\n"; } } unset($result); // Add the Guns ACO $result = $gacl_api->add_object('access', 'Guns', 'guns', 10, 0, 'ACO'); //Must specifiy Object Type. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created Guns ACO sucessfully.
\n"; } else { echo "Error creating Guns ACO.
\n"; } } unset($result); // Add the Cockpit ACO $result = $gacl_api->add_object('access', 'Cockpit', 'cockpit', 10, 0, 'ACO'); //Must specifiy Object Type. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created Cockpit ACO sucessfully.
\n"; } else { echo "Error creating Cockpit ACO.
\n"; } } unset($result); /* * So we've created our ACOs that will be used to control who has access to where. * Now we create Access Request Objects (ARO) Sections to assign to the passengers. * The Sections are in this example are: * - Crew * - Passengers * * This is an almost identical process as for the ACOs. * * add_object_section($name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ $result = $gacl_api->add_object_section('Crew', 'crew', 10, 0, 'ARO'); //Must specifiy Object Type, notice it is ARO now. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created Crew ARO section sucessfully.
\n"; } else { echo "Error creating Crew ARO section.
\n"; } } unset($result); // Add Passengers Section $result = $gacl_api->add_object_section('Passengers', 'passengers', 11, 0, 'ARO'); //Must specifiy Object Type, notice it is ARO now. if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created Passengers ARO section sucessfully.
\n"; } else { echo "Error creating Passengers ARO section.
\n"; } } unset($result); /* * Now we have our sections, now we create Access Request Objects (ARO). * The passengers of the Millenium Falcon: * * -Han * -Chewie * -Obi-wan * -Luke * -R2D2 * -C3PO * * So, we will create AROs for the Two Sections. * add_object_section($name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ // Add Han to the Crew $result = $gacl_api->add_object('crew', 'Han', 'han', 10, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Han' ARO sucessfully.
\n"; } else { echo "Error creating 'Han' ARO.
\n"; } } unset($result); // Add Chewie to the Crew $result = $gacl_api->add_object('crew', 'Chewie', 'chewie', 11, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Chewie' ARO sucessfully.
\n"; } else { echo "Error creating 'Chewie' ARO.
\n"; } } unset($result); // Add Obi-wan to the Passengers $result = $gacl_api->add_object('passengers', 'Obi-wan', 'obi-wan', 10, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Obi-wan' ARO sucessfully.
\n"; } else { echo "Error creating 'Obi-wan' ARO.
\n"; } } unset($result); // Add Luke to the Passengers $result = $gacl_api->add_object('passengers', 'Luke', 'luke', 11, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'Luke' ARO sucessfully.
\n"; } else { echo "Error creating 'Luke' ARO.
\n"; } } unset($result); // Add R2D2 to the Passengers $result = $gacl_api->add_object('passengers', 'R2D2', 'r2d2', 12, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'R2D2' ARO sucessfully.
\n"; } else { echo "Error creating 'R2D2' ARO.
\n"; } } unset($result); // Add C3PO to the Passengers $result = $gacl_api->add_object('passengers', 'C3PO', 'c3po', 13, 0, 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created 'C3PO' ARO sucessfully.
\n"; } else { echo "Error creating 'C3PO' ARO.
\n"; } } unset($result); /* * The Millennium Falcon has now got all its passengers. * Now we need to add the groups: * * Millennium Falcon Passengers Group * |-Crew Group * '-Passengers Group * * add_group($value, $name, $parent_id, $group_type); */ /* * So working from the Top lets add the Millennium Falcon Passengers Group */ $result = $gacl_api->add_group('millennium_falcon_passengers','Millennium Falcon Passengers', 0, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our Millennium Falcon Passengers ARO Group Successfully.
\n"; } else { echo "Error Millennium Falcon Passengers ARO Group.
\n"; } } $millenniumFalconPassengersGroupID = $result; unset($result); /* * Next its the Crew Group */ $result = $gacl_api->add_group('crew','Crew', $millenniumFalconPassengersGroupID, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our Crew ARO Group Successfully.
\n"; } else { echo "Error Crew ARO Group.
\n"; } } $crewGroupID = $result; unset($result); /* * Next its the Passengers Group */ $result = $gacl_api->add_group('passengers','Passengers', $millenniumFalconPassengersGroupID, 'aro'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our Passengers ARO Group Successfully.
\n"; } else { echo "Error Passengers ARO Group.
\n"; } } $passengersGroupID = $result; unset($result); /* * The Millennium Falcon has now got all its passengers & groups. * But we need to assign the passengers to the groups, like so: * * Millennium Falcon Passengers Group * |-Crew Group * | |-Han ARO * | '-Chewie ARO * '-Passengers Group * |-Obi-wan ARO * |-Luke ARO * |-R2D2 ARO * '-C3PO ARO * * add_group_object($group_id, $object_section_value, $object_value, $group_type='ARO') */ /* * Assign Han to the Crew Group. */ $result = $gacl_api->add_group_object($crewGroupID, 'crew', 'han', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'Han' to the Crew ARO Group.
\n"; } else { echo "Error assigning 'Han' to the Crew ARO Group.
\n"; } } unset($result); /* * Assign Chewie to the Crew Group. */ $result = $gacl_api->add_group_object($crewGroupID, 'crew', 'chewie', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'Chewie' to the Crew ARO Group.
\n"; } else { echo "Error assigning 'Chewie' to the Crew ARO Group.
\n"; } } unset($result); /* * Assign Obi-wan to the Passengers Group. */ $result = $gacl_api->add_group_object($passengersGroupID, 'passengers', 'obi-wan', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'Obi-wan' to the Passengers ARO Group.
\n"; } else { echo "Error assigning 'Obi-wan' to the Passengers ARO Group.
\n"; } } unset($result); /* * Assign Luke to the Passengers Group. */ $result = $gacl_api->add_group_object($passengersGroupID, 'passengers', 'luke', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'Luke' to the Passengers ARO Group.
\n"; } else { echo "Error assigning 'Luke' to the Passengers ARO Group.
\n"; } } unset($result); /* * Assign R2D2 to the Passengers Group. */ $result = $gacl_api->add_group_object($passengersGroupID, 'passengers', 'r2d2', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'R2D2' to the Passengers ARO Group.
\n"; } else { echo "Error assigning 'R2D2' to the Passengers ARO Group.
\n"; } } unset($result); /* * Assign C3PO to the Passengers Group. */ $result = $gacl_api->add_group_object($passengersGroupID, 'passengers', 'c3po', 'ARO'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Assigned 'C3PO' to the Passengers ARO Group.
\n"; } else { echo "Error assigning 'C3PO' to the Passengers ARO Group.
\n"; } } unset($result); /* * The Millennium Falcon has now got all its passengers & groups. * The passengers are assigned to their groups, but as yet no one has permission to * to go anywhere - we need to create ACLs, shown in the tree by the ALLOW notation. * * Millennium Falcon Passengers Group * |-Crew Group [ALLOW: ALL] * | |-Han ARO * | '-Chewie ARO * '-Passengers Group [ALLOW: Lounge] * |-Obi-wan ARO * |-Luke ARO * |-R2D2 ARO * '-C3PO ARO * * add_acl($aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $return_value=NULL, $note=NULL, $section_value=NULL ) */ /* * First The Crew: */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('access' => array('cockpit','engines','guns','lounge') ); $aro_array_GroupID =array($gacl_api->get_group_id('crew') ); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing the Crew to have Access to: cockpit, engines, guns and lounge!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, NULL, $aro_array_GroupID, NULL, NULL, $allow, $enabled, $return_value, $note, 'user'); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our first ACL sucessfully!
\n"; } else { echo "Error creating ACL.
\n"; } } unset($result); /* * Now The Passengers: */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('access' => array('lounge') ); $aro_array_GroupID = array($gacl_api->get_group_id('Passengers')); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing the Passengers to have Access to the lounge!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, NULL, $aro_array_GroupID, NULL, NULL, $allow, $enabled, $return_value, $note); if ($outputDebug == TRUE){ if ($result !== FALSE) { echo "Created our second ACL sucessfully! Click here to see it in action!
\n"; } else { echo "Error creating ACL.
\n"; } } unset($result); if ($outputDebug == TRUE){ echo "
\n"; echo "=================================================================================================
\n"; echo "-- Good stuff thats it all done as of the top of Page 8! - so lets test a couple of scenarios --
\n"; echo "=================================================================================================
\n"; } /* * Awesome, we've setup our ACL system just the way we want it. Now for the easy part, * the code to check ACLs. * * Keep in the mind the API class does not need to be included in scripts that just * check ACLs. This is for performance reasons of course. * * I'm including gacl.class.php again here just to give you the full picture of what you * need in each script to check ACLs. */ require_once(dirname(__FILE__).'/../../../gacl.class.php'); $gacl = new gacl($gacl_options); //Use the same options as above. // Lets check Han has access to the cockpit if ( $gacl->acl_check('access', 'cockpit', 'crew', 'han') ) { if ($outputDebug == TRUE){ echo "Han has been granted access to the cockpit!
\n"; } } else { if ($outputDebug == TRUE){ echo "Han has been denied access to the cockpit!
\n"; } } // Lets check Luke has access to the cockpit *should fail! if ( $gacl->acl_check('access', 'cockpit', 'crew', 'Luke') ) { if ($outputDebug == TRUE){ echo "Luke has been granted access to the cockpit!
\n"; } } else { if ($outputDebug == TRUE){ echo "Luke has been denied access to the cockpit! (good he's not allowed there!)
\n"; } } if ($outputDebug == TRUE){ echo "
\n
\nDone! Easy - lots of setting up done in this example - but next we'll extend this setup
\n"; echo "Remember to check out the Administration Interface which can do all of the above in a few simple clicks.
\n
\n"; } ?> phpgacl-3.3.7/docs/examples/example.php0100644025754300001440000001302010152165445016762 0ustar ipsousers $gacl_options['debug'], 'items_per_page' => 100, 'max_select_box_items' => 100, 'max_search_return_items' => 200, 'db_type' => $gacl_options['db_type'], 'db_host' => $gacl_options['db_host'], 'db_user' => $gacl_options['db_user'], 'db_password' => $gacl_options['db_password'], 'db_name' => $gacl_options['db_name'], 'db_table_prefix' => $gacl_options['db_table_prefix'], 'caching' => FALSE, 'force_cache_expire' => TRUE, 'cache_dir' => '/tmp/phpgacl_cache', 'cache_expire_time' => 600 ); /* * Let's get ready to RUMBLE!!! */ $gacl_api = new gacl_api($gacl_options); /* * Keep in mind, all of this can be done through the Administration Interface via your browser. */ /* * Create an Access Control Object (ACO) section. * Sections serve no other purpose than to categorize ACOs. * * add_object_section($name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ $result = $gacl_api->add_object_section('System', 'system', 10, 0, 'ACO'); //Must specifiy Object Type. if ($result !== FALSE) { echo "Created ACO section sucessfully.
\n"; } else { echo "Error creating ACO section.
\n"; } unset($result); /* * Now that we have our ACO Section created, lets put a Access Control Object (ACO) in it. * You can think of ACO's as "Actions". * * add_object($section_value, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ $result = $gacl_api->add_object('system', 'Enable - Login', 'login', 10, 0, 'ACO'); //Must specifiy Object Type. if ($result !== FALSE) { echo "Created ACO sucessfully.
\n"; } else { echo "Error creating ACO.
\n"; } unset($result); /* * So we've created our ACO that will enable login access. Now we have create Access Request Objects (ARO) * that will eventually "request" access to login. This is an almost identical process. * * add_object_section($name, $value=0, $order=0, $hidden=0, $object_type=NULL) * add_object($section_value, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) */ $result = $gacl_api->add_object_section('Users', 'users', 10, 0, 'ARO'); //Must specifiy Object Type, notice it is ARO now. if ($result !== FALSE) { echo "Created ARO section sucessfully.
\n"; } else { echo "Error creating ARO section.
\n"; } unset($result); //Notice the Object Type. In most cases you'll want to make the ARO value for users a unique User ID, //or user name of some sort. $result = $gacl_api->add_object('users', 'John Doe', 'john_doe', 10, 0, 'ARO'); if ($result !== FALSE) { echo "Created 'John Doe' ARO sucessfully.
\n"; } else { echo "Error creating 'John Doe' ARO.
\n"; } unset($result); //Lets create two users, just for fun. $result = $gacl_api->add_object('users', 'Jane Doe', 'jane_doe', 11, 0, 'ARO'); if ($result !== FALSE) { echo "Created 'Jane Doe' ARO sucessfully.
\n"; } else { echo "Error creating 'Jane Doe' ARO.
\n"; } unset($result); /* * There, we now have the building blocks to start creating our ACL matrix from. * Lets give John Doe access to login. * * add_acl($aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $return_value=NULL, $note=NULL, $acl_id=FALSE ) */ //Associative array, with Object Section Value => array( Object Value ) pairs. $aco_array = array('system' => array('login') ); $aro_array = array('users' => array('john_doe', 'jane_doe') ); $allow = TRUE; $enabled = TRUE; $return_value = NULL; $note = "Allowing John and Jane Doe access to login!"; //The NULL values are for the more advanced options such as groups, and AXOs. Refer to the manual for more info. $result = $gacl_api->add_acl($aco_array, $aro_array, NULL, NULL, NULL, $allow, $enabled, $return_value, $note); if ($result !== FALSE) { echo "Created our first ACL sucessfully. Click here to see it in action!
\n"; } else { echo "Error creating ACL.
\n"; } unset($result); echo "
\n
\n"; echo "-- Lets test our work --
\n"; /* * Awesome, we've setup our ACL system just the way we want it. Now for the easy part, * the code to check ACLs. * * Keep in the mind the API class does not need to be included in scripts that just * check ACLs. This is for performance reasons of course. * * I'm including gacl.class.php again here just to give you the full picture of what you * need in each script to check ACLs. */ require_once(dirname(__FILE__).'/gacl.class.php'); $gacl = new gacl($gacl_options); //Use the same options as above. if ( $gacl->acl_check('system','login','users','john_doe') ) { echo "John Doe has been granted access to login!
\n"; } else { echo "John Doe has been denied access to login!
\n"; } if ( $gacl->acl_check('system','login','users','jane_doe') ) { echo "Jane Doe has been granted access to login!
\n"; } else { echo "Jane Doe has been denied access to login!
\n"; } echo "
\n
\nDone! Now how easy was that?
\n"; echo "Remember to check out the Administration Interface which can do all of the above in a few simple clicks.
\n
\n"; echo "If you run this script more then once, you may get some errors, as duplicate object entries can not be created.
\n"; ?> phpgacl-3.3.7/docs/manual_html_m608b392a.png0100644025754300001440000001507010143544544017332 0ustar ipsousersPNG  IHDRlIDATx}{aL '(Ve(D<ZXEʠWxQ8SJʫ\r;X)ҫX%$\P+ d8oK# /66O=MOO3<=y  :=PT~veh#=VAgF W P- @! $Bs̳.yVޥ!Ȼh ^+ߒX&]*s%)@"G]4,@"Df%W3.( LtL, -$`˙$ܛg ݥ$ @hxK#e#Z=?r; =L: X Œ\ϞJj;gF*$** 6 ъ`R`{;I˷(8+̬F)t0on"A)m@hl?6Ci^OϠ6SU(CTl=? OYEl <)ttF ϳ8¬HleBm7 gh`} `# R'euҼ;'oq6j~<I.>"r1#wh "ssQP'ɵ ?~ d;7J%eS.]sKUle΢gy3\!5J6x0Uѹc2\ p7W̑ !U(\LF綳0TTgF]B^=WOז e h !ׁG&uI Ր !h#\D l"DOz9BəH 艶NJPXx^ÚH^Cs mh[3 NeECˎ)@Dv@ ,@fFe cϞ=y̟?_AgF W P- @! |B5lnvLrET_ !-j5Pv]zG7W7-iVU;k֣|%pmB+&?*=f|ZN[j>d-@ hO QZG9Jv&ȱfڜ!ȼJ&WIXT QR3c&Xt`9fw]IdzUnmHIs|9IO:9xO}zbԹ*\|#6\4ƻ*\bR{>xXػĴ%s?)U ĭ1)w"{~ɕK {lEQP ,40kcவ9&xrp$ K%IkCq_|zJ3͉#ӽ|*||IIա'n\ֈHB2UDCoO߭MKT*('& ĭQ:{!s_ϙObo!nG=!4!5k4_K$ϾX,+:ٌڢ^J^nilhVԳI5’O`K37G}Δ)]9p ilhݹCN4F~Dcۜ3HqH^L.mhNZsneP5BBPOю-o~XUp64.0MFœLo_k(/$+ ϞZgp[#j9u`̋g?~tl-?wvƭY}_C70|!Ğ={%iTx.K@5nmBkDvnd̚k/ S?Uʌ :hp8)D:^H/e/Od٭#HIU^8%nh4wnCcc 1n45!BM?1JkR)O^{( :hp+|jfF?i]!~_N ~o9Ȇ hp Q7rщ0I;vm8GFF a!F!hz>6>>6oK06<ׄ4MdA!VhS>3)Sk)7Ma!ѡѱܽb6oyr 7]£¡Z#$C->L0";(T+ w#FԚ*hp0 {E}~hpb}fy!Л C!DEޫnsU82^SmE^S(5҇S=gIHhFhݶ~$[0m~}Tbwgdnrwf"""( P v@!Z<+[hsEڝ[zP ރ5 )Fxߡ]Lwnwμ =Q'˻8_^sl{[W?88Շ~_⫮Z:&BO.o>k@N^~_Yl]+ʕҙ+.3/ !̚t3)kZFōha=5yc<C{|g#/Wt]nх;ǽ!7)( E V/n}G{oxddtl뺦izwhf4rh=\}:{!Z0P,=3^}ytuM8_iVό)ѹjqF[v wL=^:S+~뺮iϙOb F*$oZ1l[q.9oj[~yE}PMӟ}p'ztk5eN! HQ{WW{楊^Y;<j%#[ܻE#TJU9M񵇞ZE.9g77yաt/!@ҥibi_?xڹ%DǺ<םP ]S;]|ֲʻ I"D]\k&w )hdMk͓(MMFGè˭ot翳[?޷{4 =Q/t@p0 Q{'j{=gƑm/myŽ(8!sƴ]+ z3/qmW^>^T:\k¡Z#!?E3FCFBTBSkZӜvݹyF=u@ST n^စASTnmE.ep Z#!S?EK0~CFB43hBzcd|b8)-obq/`qܧJWߞrќ83Qh1 oG YOmhfՑv&$-W yY] I!D# ic !hWl!wnm^%a]vI-ewiH_j)2@*$8ZމM]¼ 3A/yロz%! x;cl!đ无B3unUjZ{˷|7lGN',3A/WΞCr Oڧ繌Цc_|G1Qc'*X_4-'-ϰ #„h);W1m_F<%iNTӂB4j}MZ2:5<(? x@KqP>Irk*Su[(J.#̼4#3CYgip%Ru&IOT]+Ż盡I{yb/CzZW'g3!~Au5Az X{%'ڏ2n9^ޱdXs=~+{/*HJPs,nL|=f=r  *@Ҿ_Q8s*Qd~Cm |Hh1%x$+ I+fk.R!DN,"DԱ4դr*:f.#DN,3&{w S8up$H"DD @! $BIhdMk͓i'hu!DèRR¡Z#Q$H"DD @! $BI(tCoz3 !N1:' :hp DD @! $BI(Q$H"D#¡Z#Fd\}^o6ѹ3.BtNdօ+#*5oŗZ.5 TnT8Ak!͗'. ý̪8GCFXQI2 J&p W,1F~wѨ9>b&.IuaGJZ!Ěߘz9XL㹕tp !*cxM[Wg-ظiJVRT8Ak!*ڐ6h:.TXp!}qnBHu!Qizc !~有CRACFX8Ey@c_7! chx0 uaG;.0BxtQPvhd̚k/ S?Uʌ :hp Dy-߸|-|' Ͼ>"uFFs~w=T?:>6FS!>{3'wc PFCF9`hS&uIK4oj*Z:# Wz5"Ctv;F!>tL?$0@!@,hjIENDB`phpgacl-3.3.7/docs/manual_html_m48c2db5c.png0100644025754300001440000000511710005475146017471 0ustar ipsousersPNG  IHDR430PLTE"""___}}}EEE dZ IDATx]=R;N6쥄8\ (n[ ]`NoSwwfva/oӣ_F[Gn8 UiI8ĶP\6uPk;EwB*~RQvGMhc&RjB6kz4 |Ѐ+r"iWK%[Ѭ} ԺNcz lnm[h  8ilhB\vO@"m+!# RvmYQL0P&8 @s92p-$@EթxՃ`n^#;n=5S 5yMb U`h'A P =Q!hM#G>=Kцh]w;hR $R3W0 $ZݙF;8)%E@ł089Nd:Krb u !cXqRl!a+?)èHz%YҶmR`e4]+ qڞYVNÜthMǦf̃ C"II[bG@M>g+ר@ 1``veH_@@"! uPiXޢxx)[9N&# tx0#*d-+)A~SV\ մLSX]1ҡp "` TQ52̠\'n. ב1@B:2&@銀ؘte @ΙkĦ˶YQN 5"ah L(g1JR 26 IGQTy P줒J*ר@Tؖ +_eٖTl I 9*aW>bcҥhIW4t @%]1.mvJ*\+E9dҥ;b)*\SKyrڏie5޽Fԫ5*t{ SG, qMc<* K'Ië:{z sBy 5.#JtD%]u@@\>(3E^OD:8yGOpQ/xO©//Cr//D" onޘ(#v{5q,ml1@Jt' *8xk3F{FGG>::- ;x;NfyxgO( ᓓ€2c388c| AiEU.l_m?K|\rAs?|"6hm"m*y,y1'=>5M&>< I=Sbx0.5{ 3xv~ qD\ #M^(barNݴDMZ<.E*1; =% ;0L-6 F MIDL.E@\sga65P+x+G+#/E\Rh~Q pN ]lWĕ]J쀠1*g39"1VQF bO#ZcYFt4&d1%JbHwFM!3dpQ˥=^J9<(bf4}9%әQ5@̦"5IENDB`phpgacl-3.3.7/docs/manual_html_m54d0ced8.png0100644025754300001440000002142010143544544017470 0ustar ipsousersPNG  IHDR0>eggAMA7tEXtSoftwareAdobe ImageReadyqe<"IDATxbܽ{7( ?~3RLb !X@)cyvJVh2G~e& W-?ߌLTIb 衑cXv {Y<7_?~g/3ßo@2EC0TJ0ؘ8s~c9 }x/5?MDoH: >fffL1雃*j!2X߿?`gcJ OF/'`=$?L_jGV $66 ~>Im޽wއ"(( q*c?v0qHJJLx} *2޼ys CAAAVV>(ƓXndrm~8i_'Y6_?0hI@q h ?p~OLh[pΝ;_xA0/_Q5y_reɒ%gϞ%&' ͛7/^(ӣ÷,sL1~o`C bPg!8[OA h μvɩ>P6|ܹs$i⁛6lp=z_=050ٳXb7QJJ fDH3U]b1XXX&n[o\\<sH= o@0_vp!/+3מ p~17sGa 0 )Y,9"'^bBa!)ZX*͒",,! gwPߍ}߇9gtRZ Re>B!؎~Mp,!R?oY(Zsvqa \$'O4KR[icDpܶn<W7˪uWn`j`<8"0bCT`Ӑ@`L +W$foZ , 9&`ZYfAXS0q&^ # 'd5}=ٟe ?9ja17Ad csiasٸˉOXÇ8I@ aIA"fOP#KFuqtvPWB-i/\.|9 1H8p Cw\<ϙɗ( h@X+WU6 )0F y+E+:96/eY&B$*dx|FFPUTBbZ)8uB:$t0>:=_z;嬃P.:IӪ,r%wlx]۽8#] V}.|e>Uǿ,L O3 4!ԪLw1~YQ8A<1.T APt* &JTbܩo@!R~IJpF `Pf}Jat؁K'!]!TPNYMk-E"T DcenZOScL Xkc?Qb`~7% 褬uU=``1}񦶇jGzKg T8 r\*&Kx7=| ƍph4!0@ ''L@A`M qYC#RCZ`$x%l t$е$)3@Y>>Sb@677BfSI? xerO!mI`*D+2>% `ƪ?}e6 l@(qUNE?, >e_y؁l慍n1;u&h K U֚DHҰ=0RZEE3 uuu`5wAqiԵ؅@5 ]6TA遥˂ Vk`G.)|!jw4}j5 P@cSn:g&o?m 'DL扻2٨+VVpKUU~%hR7&\Mb>x@x̚X %<|*R) S0:X@ *.VԀ5=Hz*0AVVV#Z2&, (`A4>@\웙Q& $9|-%?O30o>g$L ?~3fu` cf0 <7aTtizŧzN"y&֦\mThll= \U&R9@H~!fPs JhY /LĶ_/vǑaYky 4 e|k1+lj@*l`hhh? `011!'Mր޲̐OF`[ A*+gM˵gl , i 3!D`m@axV 02sxD;f逗"Dez Z`LC1Ps]MHeyfǁ 0`Άdh}Xorn<݁a${~ l"?x 9bV+?0L`.Z!s(5a Sp]t7{{y#HEk ]&Lf޼˾Ml"e1?:U-T6bMuY#(E:/V6>kwˇI,Cl!VJ9se *ձx$ۑޟde:[<>/JOCPY ~=z0AN$֖ @ PV[023DS.A@9 ``U- /|)BcqmIO"9b"e y,j\?UeBU'^Oc $ƨ*O@LWcҘnH.Fj]whCzv|e3܊<0]{lQvs=97< "o5ca c.85j L'YXy VzIcBHihwT,&~NLCintm 6t>,IEDHG`֍1u c[۶ }W6r9k<'iBEQI PJu]H3_%a- j2֔9^g?8r{/TC:1L ;\| Z,Q-:~8. ¬, PJzoOF0Xv!7G) "!s@bb"0c4`54y9SP"`3qD 1G =O X ],ҀqxCE  H4vҤIW,YlP2_2Xs7XS5S2'3#?_Ȟ &4s00 b`L46690BR0¬/ Zx;Xe$ S0@`aaauhk" `1Y`d@k xHlv$$$'`L̳` b/>183h ])ȥ1jfFd^FvQ1!E-6Ƹ6cG4\p ظNB 1 fwCI?3LW]`- @`%R 䬵FKRH`}(]>fcR;'yH ē#^J?؀}|.Wicc^0s佼kZ彇mxՃ83и40^o r$21,ǡcnô*sp^.`%@vפB縸8`# w0#0D X0 -\*MYYdj̙u2w6XAF0h&d$ R "'}>-;ƀ.xO ^l ^|>a0FL_Fi? Sބ0A|0M * ڜOl@cm!cȋCyS @Dkh  %Gs&0k8/ڀ[̯gbu1~eVlD%j` И&`1#'4@,9^z} $"X!&b d۳g` {Z LN)}`49@Z-H 6-e[g杗9=dcb,?~c#f&ߘ`S_.Z?FbB^l@@ =`~GZ7 C>|)!IHBJ`E@A]t 8 ppxuANl~a2FdrP()7ꋬF`|; @ rK%R6[XYY{Ńx A"^ )$U>X3-]QU@Р cƘ$I`ICQm*?휍15lǮi<O}뺮+͏ 0%Ya*U+p-גJ+k Lw kuX;!麮{Bn UtyD<σ `"˲$Fmqu(RN4u 8q#cqN9_|̿Gykݟb|o@4 QE)rp ȒH.\ rX"g"MdmƻwͼĢ(D Ef/넒v4R 0tDnj& lpA=hjh{t &"۶PAh!m4ȵ˄62KLRopp8y.ʳ,vI/$@cN-UUi캎~7(Egy1`.Q 85Y+a n=Ku]r,qXruq:Cr=?wNc~pO.5B(LH`Vl rM`c#X* bP@4!`cϻ뽈j ޣ`GuEu]8>X (@d zW%,jB8eYQRaYu;MӾӨ3Id&HyxdȲ8Fa@8q 0ZE,NeI*7![$|!>4hEu]Sp!#۶*(1bcAy i8b80<y۶ PWc m[^ߏ$c|\뛋zp̑m:^ľs  @٫FQe5`-YvK FBlm}[|KAH'4鲰 Xح,9s\;\:Mm`u pypA4:F Feq](Ⱥ(urïi0  <0r(e8 *ƃ Kka^$mQ!+@<τ) @* 9f̄8xwtH@b0$a bjQ2řK`HT.iX:!u&LJhK ,+r~9h ډ/ s|zzVw)+?A{;q.W*]__(mda" G?WY7I YF)&fz^owp "%Y4W$k< iUht)IaD_PڶB 5.c\?UU!:8.aOA gE~=P*T, CY]3 8d18˲p/* яM7-8֊Tpo^.x񙇃b7EJy uMOynٗir<4<ƔdUbqus>n_Gj#w13h!pU:2M{! ڤ!c-[6d3fb@&u](@.JrIM9'Q#x#)c:vCJ`[׵fUUH${Wp|6a aE}8 j-)qJ Հ"^CrC4ljIcӗGn]e,<`[m[6yfVّxi}dQj.xZO״+c)"Ɏt @`I8xCt@A@B$gDԣ073o"Hn4IdEa~ nMӬRv@~0]PzHj0@dHy68l8mJ1s:1jF0iLݮHo \TcX·C<#Bɲ,0LV9*yk}̶P_snCheVmK`wu?Ta5">;!G,-FwKZ_gYN<^hBu돶8zB"q$ :\w&?~cqk{L};-Ґ򉍅Gx -=Tb=z`%D0u-6V$ _T~M|(MWIژA@AQ'Oܒ`W4ZB O^ J%d1> FK A)B 3u#IENDB`phpgacl-3.3.7/docs/manual_html_4b803670.png0100644025754300001440000006267310143544544017107 0ustar ipsousersPNG  IHDRyE.v IDATxy?O}s b* Qdg\/nW5&&lv]&jMbDPɢr380WU#>|SO}Sϧ>)Fea.@58p`'NW/Gѣ.]:g[Do~'N|-++K&,#R7|sk֬)--U-> \uUtMQd2t"LJD,{7ƎO~e߿P~믿~رmYIDQ$ Z$I ӧo۶m„ ~?Qb/2Ih[VEb-%DҦm,FXa---FӧOWTT$ B(x<c-".!a!(F"X,6<Ds};ߡvժU_|qK$===Ǐ'̘12ƓҙanǑ !䲙1I eBC(_}Qxh!$6X3*Sg<ϟ={ĉϖP(d=sBMfc:=zaC;w. ;wٳ&L0\+ =c}}}nl<OČb,;vlpp2w\:pR&^nSU*@?Z2|݂͍F6X !Je %x٘^<<ϟ8qB>miiaYVz1kꃜ߿? \sMuuE6 /"![n9wSO=%bwww}}a,O${!5y7 ! ô(kII±$ a-U%xN'PJDf _ P|.,P(Ѩ.o6}&N844DgU `0Aba.󥥥DBX}뭷̙2ٳ=!d~BdY>uԒ%K ˦s7N;+++1 C}˒K!ue3&%B#k5]v9n ; 7JBg2J D"[nݹsgGG]1555˖-[f "黒$w}=K.C5ih߳g֭[?n6---miihLefhhw!L0^E[7n_ !uuu#Gv |>یVh%^%fwG`V JW#]Ӂ|ezTk5шXchL@SN}[R{8q _,,˞?L&:x޽{~:ΈeYx~ں{ѣ O>M={vYYYG_W_%r-f uuu>O'O0={>4Ο?_yV,!4 s̪6աBȳoŘ%I'-)qf_Y!ex[[ѣGg֬Y3ea:::z{{mnh޽W^DJKKlSOY>'mܹg0|f$X,0?#Bϟ 7 1 3f̘ѣG;w̙3ϟ///״8Y?. Ĥ~B~,gO+}tI'wMJ$%BOUM#Z4=M =^Z(\O<hnj3m4u燇z-%>}zڴiǽ;0̼y7۷O8A)//?~s{zz*++ձV껤jjj&OGeLxNH}=ái>^ rA7)At©\=6vp5ߪa" /@8qC=4vXu{a!eqG$TWW?())44COi[. cmYYOōe3=Cܞʖ!gee©\>8J.t9HL_qfBD/;v|Z0ruEE iӦH$ffҤIt\kF?+֏y^ɭ!L|G={2{rb-a}}}>dEa.bu +,7q:OXsNJꋵs5g!AVZᲙgW!'Hp# S}K9O+ HT`5 <+nڔU^Jx3grF}2g^_Bɓ+++ !4 c|$IRQ?~,}}} JKKm;uwiDQ.mj.֊X^^H󥹪ʲ$I<@4UK>yd={QOu?p6kg͚e8ZL2CɫDz"A_raC<`^>6NbYЋ/p_%fOGzt><-qyvOKMJ }۴۵,_E,\5jTcc>lBZZZ~2<|h,g>884g̘Q]]-rwwX(JVk?/B{zWɴCCݾO s7CV4;z*,W,% ٌꪪ rە0k,z=BȨQ v._BHSSdwUFTFweFbJuccI4BY5wBw}WU5 8p`˖-K.䦛n4ʓdYYYEEE__{700PUUExŹXA",ǂD*\>(J ‰^.uq ³򶃡m !Q1>QL;]Zp!}avˬX?SE1l  lf$IDBU:uرc39<ѡ}g͚%:j B={6} [ZZJJJRYk8"qx>0`ԨQ{kv@7144t v ٹX!,J$9&QU a†ӻJ_> H˱C RBԳO>Jӫɲl"O%|Az̘1ȧ6I.}9\|PHߺuɓ']t'^`0X^^nxDQ%gfѢEDh;uSNw"N&cƌy^gφaq2$D"!"Z|ydB$qq8.Nب,aq8.0ф9,ӡ7./ ]+\qD2}z }2ŋ_|w4NTfɢ( !g.))7єkt[tDr鷱y睚8-}uP(tR Lo߾7Ν;׬_z%:!ԕW^9c wOB.뮻Niܹsޔ)SXU ,[7'O< eٞ޺yL&2qdp{I0I?*Qz G_4S # %oX<e!U2A H87[lШQ>ɲLW0}"P ƍL&՗~:::ur-3g8nܸqǏ'x'fCC?yHx@e{m?'N̙3ӦM&w OL&r-Bed2I+KO M.I} ַK8JZ<W#;r@G$epuuuyyrTo̘1_W'MyWlhhзDlt>EB,X`qK0k׮hY/}K"XZjՏ~d2ӟrw444ĉGrF.]z뭷VUU! ͜9dYc̙բ( 93>/̲\ꗛjQu(1j/=voh0 dZl,;44mjjb@Vr„ ,kƌD! twwӔ'N4UQQQQQA;w$I<Ͽ40a‹/ѣNzW_tEXL )ϟ?'?I{{2LeeɓnjH$D<_xŋ`kk͊Fk֬3g z UO^{mGGGM`pԨQD_ ȩ>=8e)ɔcʓvB$%pXOK^;2V\ \Ujb-@|>_gg?Ca8 X,OOJ3 Jb<>}D"PHQ5f̘1c}@  %IRb͚5zɤ(J@+,b1;9P(544BA*dYFUUUƍK9E2b(;Vi"$IR<04 QQ2!̳LCxpB<e_;X63jݟX -#}5hPP,}%I<]ez{{Տ0F8LZ4ɤ_MylNY''f =شTJ&%N16.0bsv/"!J'VI%)V6;v#:CP BʏYԶCFNmK^pXU [__/}tLU> 33gΤ=a8wy*]@@$uV#!>v4ȟ_13n};#2r8 ǘr(!K@XYJ2O-oՎXF6H矟7o޵^KAQOW&?sϭZ vȩm=}[o^=ʕ+5:i󴱱N D{{{"?~|UU=v6$ӧkkk9{wYFWhwJ  :ujxxx񕕕vt)9۷o_UUUmm-,*30?{{{=+@dƖ'o8 C1r䬯"&s._T`|n$LH:cǎ͙3Io.ȲL?cǎ:vLvȩm=G?zW]***VZE)/z(s}M'Uw~ǂ !ꫯ'?) Qxo"жreY})}Сgy{W]uՒ%KgF/{]w]qve k4pYAۢ @ @U<~uH gΜO~B\|mFih %%%vڽ{>aYdR$ZN.!?醆H$|___[[ۗ嚚Qoۉ'^{dR)siio~B'>񉁁}i%Dpa1-Oqoߣ!:yr\`bñ$gX"&h4Z#}}} 3 j9# ow\={zkin}}=ףL}__,X0̡C:::8zWYxq Eرc$|p8ICEqzbwN&s7n\<`03wy>ѣ'Onnny~Ν===gnllTs?ógϞ`uuuww/lܸí?OtM]]]i&M7ߜ-[% @9~J'+c֙KkD~,_,jNmk6g3F6l@BRM5  H$S O^ti8ޱc`ii>Gyfڵxy[n哟sN> z!}իW]6LnٲeժU?O၁zaazuM:uxx8H[͛󾾾|^@79f~O Pe͛7/];#씇}SRyߺuy䑒oq޽'O:u=ܳk׮|+w}ܹs9r̙{w߯o8qbҤIPN&oZ:KG9rRfIMVSSjݺu]v]wݥ_e#%~Y?G>9!BA~eKdRsлru-'FVUM&6E@@~mAmʲ >MKa%zhs8'=Dx}M47lll|!VEq===K,MMM4x/g zx$Ae;vٳg#֭[Ae|3Nۺ+.\[niiiYn]YYʕ+~={\rŊNǂ O0A9j<-gʉGf+Tnidhʩ2X4 fnsĔC5ڶ+;jJUGS}d2j2WgKT*Ɋ90o$Fcǎ?O?o~'SOYfuww|>MJBȑ#G***hîRy0,+8tl}ܹ%KpWRRrp8<~x:\mb1{{SԕW^gϞ}{ӧO2e2ryz}p8|ܹ)S9rc(hiiAW~ `.ɓ'`eYI饗N:U]~t[`pppаI俛2VH3',&ɡn;]>!I&Cx/ɳQbj+ HL`S>(\5nEb# ljian6r2l4(&M7owOӄ7MMMM6]}ջvK8P^^+H4nܸ24tZ z>׿^pۯ暚_{>%駟߷oߘ1c&L_gϞMQҲwiӦ3Ҳ_~yҥXl„ 4˗/9jYYYKK˳>;eʔ~K/%D"ɓ'8p/'twwڵ~+V㐧Nj."۷o#,X9cƌJgΜH$:;;~[oH$v޽dM !I(O9"HFքNL\d|G$r![xu~K^GYei~e/;Z*VPęRysOANibBnɲ,wԴe˖'|rٲe֭8[Vgg͛׮]Dofa +s]jUIIɗoٲx X~=w뮻[nijj'N̚5_b"ؼys";w׾5e['N $!dŊzk]]?<׋xm=:^z̙ϟ_fMmmox}Zśoy2# ÷~{__/\QQcX~ːgYu79ߏbY2=CuRBd8< L.]kc~V9(#ze5$I|~$I7EYf%FW\)I3#4xժU$ ^tER,Ǖicؒ%K! q(#Xmkkw%i֮]\";nܸ&A=Jvx_=?Ϻ{'MF9[n],b7dYN$7-pyytsXl޼y/&h4O|"L>-Z/$3KgFo\);\qK&<'ϛx}ƜTzlvlJUs*Z[bL&52 9+SQ6 Iiԓ!K7If"O eYVUQ$Iɐ.^)PoN-yjʬ,kO6$|&|Q}Do)!䢆8SayRzg7e,ԇ^ؐ ~iQ*qRY~S$Rg0хk#*.HFҙdEi#x/OyѶ~2.,~l>߲^+eJdakv `-箘j u)j.j&f]y̎e CD&WK^O(I*TGT6lC ?~W' 7{YΞ= ֮]KyꩧP)3ӂdΝ{w>oВ0WT p$訫[lYKKKOrQ64)Dlf16!vK/r@t5rj PL׊H'ѡTeP1 ]7]܎W{~LF"~޼y^{rZ6 {vNTnY3{Z]9dXU?.9a| &o e iѨ6z$Ύxvb:z̖In^laS6%gƜP>#ڲ#/!Gk4;j8U2eP4\缗,|濵k7mb@4ڸq0y Bpb-kݻwh…)Ӡ] ,Zg!8 Z[[[[[n]vLo}؉h PLkȰUVUJk 2wڥ? hϰm=4g<}YE_< q^ P<&ZB E&!,:kXi-Zg!8 Y)y<X@Y)ڵuq"A"TPD5kX ,Zg!8 s49&o=e][[wE+K4Tc=M@Y'OiBNU/*E8+!!XQ~uìmB3cV%)۸\DTk#^>dXE ZQ+-X[l8NrC+? 4kGOd_0|MwaO鞈׎,o=EoiP4{t$w *?,NDHowF-6?!y 차%֗2Y@2kX kY#[Ui4˕wKԫA??eYe3SYH4RO,7]}!䦫%I.{* *E|ork cK>P)ڪgY>U_P È"g璘o.Ao_[_E'|Wb7*ѰVgɦ5n(J*<ա|o]%Zf?**E|orѮUZO#ּ<@MNL&æ.U`T.tnO7|P.B{SΏ{~W[3Ki?E"}ȰC&e/ !=9aS޺:Q)]& k7:;j¤Y>PýDaDY ! 9u "T79}\Ҙ"el fo̟x|kO&|k|B\4FHO͇ $h'eC)P OP#- zįU]TP.!o!ϯpڵ9?] "TP67?vF.TP.B!8+Ev…)GB"TPZgh޽;?(8%|]T_{fZga2`.BX6lp#ԦMPnAhӦMn!gnHEDw6#PH(]yZ^}7rѼ%.^&k`D{cߎ˖^f'+/r5|{ʷ g]mN06ӋN<|DsaH~w94U"El_Wx3|C_(%dn]obڵUP b&CrOehO*洬sv/aڢU^(i{4l[ěBMW6"BTK~ߝx(}Ws`ԍcu2MYA 'RJ "g璘o.A|?B<{ dqȆ-y;ۦ4ԧWdܨ%4toy[Vr/ K! !hfCl>EUHij^gÝXKŕ:58EaCdo\ ~Or4rnQѼ3KYv S EsS"dZez&M;4~z!ZY2;p73(:ڌ3O/jn:b:'3 Cr@xkΟNXZL$+Bn_n%k ;?wԿ) _[,: O˚ηf0 m*/״KTM^} -K !䦫s !%?lwHnR׸RцLt($[~NNRn뛯K-/b6rlu-ubGesVMiLUOɸQK Gi!_"BB2'ڴ-8'q™㨄[r!f/Xn^(o\Zbz[񵵵ASR3>uڿ4e2%HQR? ?)Mik0cԉ_C֙`Uؿ7Sz&qe'(/(p7C6;f&L B0dkvfOf8%Or;E,%(Uh?i`F(lYv Ϝ|BvRf Z_ 3YqW]~êsUc^+kE `b-M6]e…ݻwT~ީ+hmn!kMuy@/EvYԏP.B廨8*8>k][ds()@Ѯp! .oyFsYA8Z}JyЅLMn kw]@|{r3Yw5Xiq5/4fY˜v3d,k8dv2Qn@Œ߆ڵ)ۋmn9h@ҋ`54Ùa IDATi_ᶵUnSJ 9Z&c15 #i 2e8XU/;2Kc/ۇ:qv2LYim~a4G#+kFNشiEB2Rk.\r(v-Rky_>ekqЩc#b- PV"xX ,Zg!xԘ& (P>}4=w๳ k<A!xK۾J n+-p/@( 4|jٟmZO0lf Skdg!8 Y^ `j媛.ZcׯwP$Ї ZڵBpb-kX ,Zg!8 YBpb-kX ,Zg!8 Y޽{w~P.\2 ڵBpb-kX ଴cmkk# ڵBpV.cmkkE29e_ C)捲l׮]'D [$KWQ<Ń7Xjl%^꣌[$#Xk.;a"Ғ}Wڵ$q:`dTr36 \(K&R6 2d P\J;֢ #SZg!8 Y)!/\0?(Vh8+EvΌnietj8cE_u"YCY?${ sZo3m!l.O7/s~p}889cY3nOZP/Q/f\w5{]saŦTbXԧD*zB9+@˲_uk>zʰFg9&ybfg5יU*6PT ?zYZ26g}4{OIJ]sYYEYUէzI6nէŧȰt;͢NYQ컣BeS>A?=h+PDCb@M-,4WHA2;.Ba]̭~=>zJYHt"I}ɲ<ցa<k=ێ1 f)ᚚDZ3Oue}ډ'M GBw:2 y6ө{pЗԽ dY>`+k<_Htgt/Yn$.ue.Z;<ԮMN)+vJ#EԵ8R`+.jR{͎Z;U++i-1LfC|l%/"vQ0լVJ! V*yfOA!m Qm>Xڶ)9d$}f [يY lvN-Cak`24Lj3˃| Ck][я6]Lm3ZW#pV.&)LtKR&Hk+j+҂9bmfw84YhTn"}(BmF-l-,hm&{d ։I Mwt&59{pIJI̫Tiסέ6o]fcB(zȤXf_n"I^U*XͅFSSzR ֌FO"kH6I ;5lf^dU #Y1Z'IEf?&⇋̋`ds~mf=Jxj߳izntsvŦL\էҺ5MMmJ:̸ =?['Nf1 4mA鮕Wk!<ج>ZH+un/, sZȊm[mnδ?`qqDZ,|냙vS˖-ˠX`4pnYE0`\:*ZmLLfhY6;w҈f]X-̤lzEeY.lӈؾ}{Z| +9&/Мo) mڵ9" w9/@X @X ah/_IueBhTˎNm7Ĉ@rue&'5>ձ igxVlyNqZ=?E] _k^dS”? c.⻬_9Ge&o) Cxdl&nifkGmր}C2nu; /:WÚܸ`L)߱66ˑ% Fwlj:Tՙ=T|Z֪w3 j-Fqx*TJ_EðnȀ;Z7gzW1@q L+`kcԭ[u~6jڝdy([iN_椤_`eaÆM6x/{iG0yJR4ڸqc]]ݲe˚1G#kX ,Fi:X`Zg9ޮ9F6Oih8 YB%;GLI(0 p}͇l83}gM^q2=WF(v烢ķCR~,]12+Uf'1h;9Gʳ\zrd#boƬT.W/ޜ2.YXɵD;wYfߴ~Il af٪p~]MzzSzGe3+fuhpX 4UgFMѬNS9)Ba0?rzsrճIL6y:foekglNLW~C)wʬY3iz",+*oFP?uQc6˦m6Z97|~%,ķ=c@σEtCJbds r(Ъߢ/WM?c|r]}M7vZK̊gbZuY9s m6 l3s'G6>8ho |?uhn4'ٺr=(ܹVi0em(g[M0 cWEU;{*>=Ô]&^3rWx 7ڵ#uTUo۶,56wNbO k>e=,J;vֲY,6})F[d9#AWqf[~K5)\adY&lذaӦMyp"Yde/t#v5OIFCCC7n[lYsssA^-E*Z7Y@*wF)ڵn d| H;_k]o߾7o^9iѪcθ]_헝f Zg!8 Yϟ?|K/sYиw^%mi~ (,7ʔ`PU7єap޽6a s#&Cul>uYWk}z|W*gyJ^L5M܂YIg@>[Z5(^"9 AJ!(Dk qLSa_)S`ZrfCϩVJagKdP ܍ Bͬ*ZrBub{pGlSc> oUf,7*L^_p2dv6@fajdGϞ#lN5*# i-1LfC|l%/"=?9SO9t:n uDzYI9!p"VKV8%vϏ,!]uس0A1K`u"! -H_$6<-Pz-wh6Phjdl82gQETٖQЯY6 @a [џf t Z.Q`KcLS$cCi3_h,޵Nm6-Q`PA?hpڑ\ZP <> A)X@)X"$H`b~. GP &si̒!B P ] kdښ 2GdpK3Yw3789y8o}0ӎfw5ɑńMi1;g>3_XʻAW?mnZkl̆ݚMk6 Yr%e;S,-rgF5)O ـSn=U+ dI%-%AޢuS m@>c3y -.bMܥ6 b@[H։dl۬i= [%I:RUU84/W @[x6?@s 9v*[(Vցa *Չ ڻݸ=IRTR@PҕhFlȆ ^c`Hlt5H)]]TT M2$3p뜜x!xl;㱁jYoj?z5ˋf)*q *_'@e6X[-V}ȑmR7?.0lp%.6 2|޽ثX)l…-h quO> J8ؗ9E p}Us(%|]GXߠ#bAw  X myU}ji[Cd{Ƹ%oX6'j(@ՊXŭt q]cYl" SE,Z';<ꎵZmmos1_V9>\K6JejXc6Tvpstf~_Ot٨ե~ݠJ+57Jn2Q.S+VC q@;IƊ9/Z̵N+Vsz卑7@K(g4{^6`\k+z^ثЬ4`_^IF^ @\Z"J<Jh-lԠg4Z_/K t*vuy)ݯ@3xgx"8<[ڤVN7,zF2N/YpIoy X5fxěڸZ:]_ J5ֺ\2/ҙA4 L v%E"jy"f=\HPYK`r缐#ϦPMZi}Vֶ.S4'G/0hWjwZSO+K+Rɥ6_uTXOkLm"i_6k󣼃:K^ @\9y?XO;XF<-C^ @\Z"kwwwc+kX @\U]I~)&S) UeO<)\LX.iϬBDY@0▊Fx k H/(UDXI@mk*HԪ((".q-jbmJظ` @K0ij,+8֒zVSH^ @\Z"W}<ty-q_u=4 'ҭG^ @\U>7 n$mX OMX-Il26#*^IDATv WK`򓠐zVFnKy6c[CVLekJ9$K\.mFl8gV\|{)_0u/tUC6T{Y!t;°z{~.to~pBwE?1YKNO"4! upX,u9[b%ܠn Y*w'6k D"mد;%ɫg x7=Wڗj֌Mؤb-F2?tX6\r -YJ2% 1\WRnvKI%{70kPȍQ-[,I<ȇv[;*ժh-yȃ`ooogg1dtMrh@mߘ4t=>4M,F %RMZNZ,/S[E܇'fЉ|EX%zN1jG0: ՉY8j.J%FjI9,WgN:Hl ~뉝ʅ݉hsVw͉UdW;ԏX \iXt@+ ]f۔ ՚+X Nܴ9Xcᴒ|+o~5mVcs|+1)VUCEC\M< Z -/1%.Y\>mڵ85wZ0zX,t:l@!-4DU5a-htvvWyt:Mn{^s W4u2\\\\\\LSX{~l6ωov<˧*I/`ut:Fy:Nxk޽޺uk:f3ƐHMch4Jl6KHWvoooI4jahdux4M&~^ڏ>觟~_}d}} Fo]ѣGۿ˗/ӱ rϋ/NOOzwygsss}}>۷ox<(.//ONN.//޽; nܸqk766vww777/^O|`X~kkۃ?666wI֭[˗/θ!7oۃ߿sf.lvzzz|||tt4')z~ƍofݦ_zG G[4hKn@ZP݋ *IENDB`phpgacl-3.3.7/docs/manual_html_m6a630ca2.png0100644025754300001440000001205510005475146017377 0ustar ipsousersPNG  IHDRxPLTET+B bIDATxKv(3#34|NVa=a-!Y'Y$_( UoFھ sMY < x+~>> s? `̞3GcxW5W>?~4ÃY|oq@[mG5nhjz#Gl PU  uOxJ >%o:?C9jv>`mb7tíh{fxmi xW;Bv«0x5'ۚH_0E<.-"<ɳ d mm h[{8m޼$O^]0 )1$Xmvv-#^Vѕ>OjgGn>ںtY_ۚhk >Ømâ Zݟyn˲նmeFֶx6@Gʉ|~}>x}s7*|@5KUYdy)jEKWQU\Oobvʉ\YZyە| ܎#vr7=gQUU"fkk,=vD+s=v_XtU%V£X>*>" [5CT^._~`[6=pЎRUVҪʐ}T&w2x2 /S^Ux\U1^8=wҝ M-G(fvzvgdu/oNUrJ  oNUq5"!7˥ ,Ki'MXn*{sW]Zh4gWUYZۮU%\Uq8rv:xJaŢF*2)n9yU/|^»8=TU.,1׿TުG9% 阮Us1]Cf[U֬Ul: mIZ j*vp_ nr?T0`S"uvSAWIUenu ޹y ^{*,we PU&sPU֩*'PU ֪PUʌPU6Uc T9UeU)2BU0hj^JUUi@UyU^##*VfRYWuJFZ*Ж>2#-Tʌ2{kC!y;]o#/}w[X=Hq L(m]iJhkTzUH"炕.w1ȋ$ ҚRgʧɇYô0xwTH5'/|?rn?;V#) 7-S돃w:-4w6 ^!%K2 (TƇQ2w#;;᜷DUMEQBpqfF(6hy^!%ϡT& ga N+QU䥮lad&~"u2Z/*N⎞+< <3[L'gyQ^CަVfE$Ԅgu[N,o*mӍD@T%<OIYRIq^6)j2!U> >^xIs&T]_q\>,QsЇ-A8FB=;<>À.oGx^cx;k ZVz_>gj݄g}3/XuxD&jZʶ8AZ /՞_;4PM.sUϫ2 '@qB<V=%Z m;WD(ۮw#d7H>!kݍ2@h}0TXq=/ZΡt}=vMU Ê𪪊g <qZE?4⁻Ś0p&!T!iDZҋǵkiy(lRU܀g:pRKg:Hw?Qx>xLhYx~~ax0B0-&_kyUU%WD5hzJUG[iRKmK&/VV5>~~>ʭ/@x`x$o XN xM/R{,)v?\UYO_Od|ao,.ੇ7Vep_'oZ0rg_.nƃK{vi.sf-<*֧Bq(mPUeB@iI9_\ Wמ5$tM!,+G*j@x9f,m|N-9xl?&Y*eżWIRU (qm9^g%7:9\,C>U覀Gls/Z pxY?oi3dny! _*J[g4me(8g)n&BXUXBnu:v'>&M9AveJCP'> 1G`ߍ8C .X`Y\'ePkdtwB?'D [`@=AiϱyY="+笈%_`7c# 5ˤ 'zv.kGD Gܐ[R()\"19]oe!lGaOANܶ6PްNsI,\ht:TeH*6e.ͭ:irkPq}VAT0#eฺz^Tr\$,#ްqyS6gp78Џ F8Q9~ ൫oؘ{=O4 Z,;f@o,ojm2WRzC]55_w;^ ] UviǎYc}9COT.Ld^Z yuǫ%hYIAǻ:ViH q @p!{vZ*'AKj+:_.sEmK^MIduP^$ Ȯ6{_ѧBa#%X't#.{J M*^* -*I.UxS!тa >.v@6Mt<TH̽d) ,yDt]p9_}.4sb.m^8RhIRk O2x>@R0cvd:Teh )TGP&pD \޿2*,GMm_H'u6aMy RNRR+.uc7M|p"N`p6Lvoï=:SPvRƣ/*P U}@Dx؉SՉS;=peeal.tECFT.MW:ĩ~um |^dFCFTK¹Uصae2~VZ#j*K ؄I_[TRJ \zL& Ap "ZwxDWZOaۢU4].LiMp~':iV K0T8QPk\ܝRuxͲ4ݔ#r[*{a`z|"L?<"5wl82|5|GJM ,7&Llݛ8K<; M *57cZ-cM.,tidtGV?})B?pR 懎gTΗP(5Z#j*K-`D3k?t` kuw!nj9 p Zr  {г<ζqwh4*\)щ-VOfkX jS tS.<h9hu=N 9T@ GG.x:c0\~g`l )Oa=wk?Grp [K!<4]թs;?Ѐ"Ddqi)"5 Ӳ"b?X\Zwwώ\>x1E`$KAVr FY7Q.o1AƦqeD`I\mwMD?׊t |{ ?_ ٪ˬ]mQ@:FK~ɲ푱rc߾-2N#"\V >hMp'daO3G{$2Oy}QS4]F}rۑٙW]4{!2cug=_ąpֈʥ.$ɏ|q1_ p (N.Mp!Ng7ͽkזW-D4{PDbfsgv}˽hZN˄ >*oǜ?x:$Y4O.Mp!X~5CF"b#Dd4-,N̗_=UpC֨%L!{%\HOglngXXCs`"b~\^fl<T8Q\kjBrA7]Fnṥa\1LZ̤*57]FXxVwƠbsCCڶFŽNS0 ٛ.Խusg'p.FJݓS 9Rvel[;-"16Ols5,p$LIG뀖`@". PE\'JS9\g>FmQF@=d=m ɠ9d;dXP3h?B 15O3wqFfA!:Vgdu:f?"5*$`Ks9۱]Le,f"\PǑSVn`mq],9lH@[YC (r1~n)K$t] 0[~/rCh/b\L&<[+DbtO,a)zP'Pg.s)2!)6mڴy**hD@9/;,Yo(p`` @(V:sr"< )F!F*sC܀@1GsIxrS>sd$spN (~I| LvwSJ"1]kC0L-S:Tt_NP F"OzyEQ(hԓNbJIFByhtE꧴n[CpBF٧;!\dm%JJY*|ff&ZO:Ye)%&$VB&oY N - }45Ft8SJ&*D֩<0Z|T8Ak @I^ >mqkWv"\H} ׋|eu7@"T8AkDMeMB&A_/:0m@CڶƼ."bYEtr;<~u9Z+[?IP` }8'`08s&ll|78u\{>sWV6fZh0,j޾uM8uO߯N/_߹Vp`zCzt - [5qjo.0#_oKapaw%04GƎ= ){q Nzv&*E2šntˁ˹21eVRF¡fFv8ݳgy䆲ͻe^ 4w54~k8o{9wl8 n :KohO L4\]rI3.@Z0@̭[<]iiڡ#Ǐy-"\Zޜp(н砀gD_2(.D {] )Fm.@{):y}㏙Oeh-w|.@Y ip/;Q"¤( 9\~gDW WCq!%u17I:u2r"}eu_铖0-ޜpjdwMD^+" >@@PwwޱoW n\KXꌬNs6g5L[cP¡Z#Wg>x#HdʟzymWѬ]J8[r#3h^zC[#Cd("ꡇڛ&&l{|%x)מd~_u @Rc2b$ȟV.@6z @".طo_ETfff.$62F37Zf%k(*ȷ5;z8yyh״g Bbwl?U30ZO؇.\QȄ^Ȕ/_F Ej6*5WFbJ!==\, CCFB2F?kYs$5 }kj .Eњ{c'NW'O 5}sSt6O90;q__s,QQ )- [5qj/dP$GCƂ3r3#wj?NZ#\Hi|*bw4}*H:l9SF,RZ5"Ӭ%%pX8{"&3"筡[á54WK̹d}˷ic/pqY!&…> "?50M Lsu'WVơ¡ZcA# BK9h?~lo9tJPT8Ak?*,0s;t=O^ׇ )F#VH >hY!lsw4^D!  >hY)L^m <8`lK<#"/d0tBP-BK ~0L ?}Ҳ:Bl*5X]3^+"a&T8AkD;.$v[mquٰ dT8ok|htn% F̯}GȔ??vPf¡|[LN@ێ\y o ccݵ7ML SdFCF מ5_?6.c)F)"+<+\ְ~0ܲQ-ċ8wD0,˪ @k.(vuP:IENDB`phpgacl-3.3.7/docs/manual_html_676f8c98.png0100644025754300001440000003565310143544544017214 0ustar ipsousersPNG  IHDRw: IDATx{|T09%r%7r!  @hQD"Е*Z\|ž]G_jkپh®K`᣶UiVTv&\ $!r\g9NOr-I~sgΜ<lC0ghU}Se.Wz?ksuI2F(&a6(E fcGL( \tUUUvݸ}5z,˶_~9++d`kb-v{JJJ__ߥK\RXXp/ݾk׮~oV!qƾ{JSRR0 pPZZ>22b23 sQƸJKVM!+"ml)E!7STDekP)dy|EUU0(ve9eɓoeL222BfճZ6y6i62 ">zQu r9s<U/]}vмy|@z%K̛OgеfvܝQZZJc [G?MQhC t"+&ƥ.2귏GORuuu1&yd;d,3\jz<:t=.7xڵk,!IΜ9Cޕeg I/^|wv-^~ȑ;w.`}4%%j$bGFFN:*,,,))"5[|;nsssbeVߚb22 CٵNYU>:&¿}L Ϙ' 6o rdZ/^@ uww;v>E wE DMww3g; /DE4m۶}7&>|?i^^(!4k֬軁~_7Μ9v,+ $IzgY .((K#{GSFx;XY5S9 +UU[29'z|~"DP2`,oz0(׻e˖3ۿk׮:u*Ƹʕ+&tر/rŊ'%%eΝ{5cww0̙3gHHel6e#>Vܹs:Bh޼ywu8;;{ҤI}}}]]]Nӧ~/\o5i)7oVEj~P93_Ju{qmnH`j:e[$(2B{ˎN4׏#ᰞV Mg។ψgJIIٽ{b+**0 rN8Ί O:Ejxs禦?NsqmxifWyeٳg{Y2|!DI:e+++X?Oδdʕp1(꥗^&m4M1v:yyy}}}()kyYOZ~TUei\)3\toJb2ꐇ:mEQ'O&1LO~p8|*v}[n%WnGc_^~O>Iˣic<44DFh{aeY~7l6ll({CkmŤ{XUUaE9s>)(NI&n]UUEI *BKL%e6&ז{oGڭTyB jF}LYV)$+aTpx1^uAe-!nVe˖Dr:$  i6w^jc1EEEdÇɋyyy˗/w\R+--UeٖK.!f͚t:NY[1Ƌ-҇XVKQ6[d[[M4V_GqBwVWW|h0%hVx Y;B(Ū]JzaFb?3B9(cFv"C&mEY3I$0n/&`U dL@͉3g]^PqqqFFB<I;lj6* B1c݈#Y PpgL4DidGAȒ!,[nڴx0sL)KB(---`}NdFQӧOkcu_^^F]^^^TTSTUU~iSظsNŋ~(/_$q][[[NYseN,ʨ ]MÒZ-_Þfi,̵yB(3O Q9O61n18ׁB>DlR4#XV=c|Oݘp4V0YAڐrrrd`]Μ9S$}jYYYY`>mєTⰨeӔ*HxFxOYd--\c'*w[X!sy6QbF? qnnnnn}nMtmd/MڎbFZ}4G˲#Yܧ9ZS6///j6Eke,Km6Q}Q}ׯ\2` v/xTUmoo'UOeϜ9C%[,ŋ Ϥ!xCJa0BeoFdJ)..$L.5ki}Xd̔[^^β?%I:~8K.%_Q?u`-6IVX1}taŋov(YYY}}}/_:u*EQZx NqźX[?иŘX("Fkz)kG e΅tXM+wպ*ц( An?QsNJKK{UU%Y߆p{A\XX8ydEQ]d%`6n8cƌ'O-ݻwm4KKKy|жmTU%m!G2eJGG(]]](VVV :::~iӦi3{(B5`z-.i4HQ ry(TpeԖO:hD\f HempVVԺBegg?EEE^a5961 amAV ["dYN??d` 33sժU/rFF(JGGB+--5)S---4Mst/33L v}ƌd>f̘%ICC~7Ea5ŢV拒lWI3E+23D},}uP(jddDYjٲpv[vvACʚ2eJOjY9}4ـe>&M*++[:絇^y~󟷵i(..A[[[[[[kjjj\.ɂ8nڵs!EAJeY~V^rfKKK!55{b?ޑNj-*+iSAߪ4aQ^?68ڦHk3S$Qbeَ-Vۿfc0%!!??hMd:'^iӴYx/׽qĿ4}"dUŠ(^\՟$I7{=BZƙ)"1 ^K f)Bמ$la)r#p](rqQv;k۵_Pq2qJ;y?Ɉ(%%%d.~R7%h &W Y}E$AQY%+ӑwg̘Ah13)zcs>&7jʲsDL_jI{]ª[.8`3K!Ӕ*+xQϟ$c4e=χ~8wիW[,m7~3bZB/^t\yyyfd:[4}|G)fN&r yg >أHUpSkFx|OEZ=r"fiT.߳MfP|F9sI8LFK!''gdd@i)=`9OF$m޼MGlvl[oE'KF0!H}W(n޼|K,!n~~>[,^r'X|OTV+MӤoCEQ,RZ$(0b!胙jN&XlOZIHM!dXAEp?.i2(,|}B/Ƹ鑑?c\[[kZ%Ijmme,Ų!r$I˻Xy^Q9sL0dp*fLav2S@Ka{GSz);"zGsuQ` eIwqs78p -IWА@i8=^yyҷO,cǎ;wEUWWϚ5^裏m۶uֳg{ ެfIDAT}_=zhqqիl2eʔ$gZ?#l߾8ꫯ}G7o/)bGoߞs͛7?siiiC?/wQSS駟]ToAB$I/BOOOffݻ_s=˗/ooo߷oot~{ӦMXf޽{;::};::ӵ Ymjj*//OMMu \|wI:wkkfۿ9s?s޼yZ9CHs8T7kgUEEFvVeq4BW1B B{|yFB" ~v*Md4I$2t[ByEҿk3e-'Ov8ѣ y6fWJt2:` `ifKBUE }l[Cڑܽx$ݡt 2_FZ׾xf8p ԿmO>]BFa&n)!K[_?hv,J̘۠8gt%xd^q\NN#쳥K<_XXNblٲ^zi_VUU{SN}Wz)..lll馛B===uuu%%% +W$cK0ٳgnwttL>W^_n>g̿hCZ( 'nkn HWc% _q˂i.^|96uY擊A@(P t"K,,ʲLsy8n̙/qw-ȲrO>{l2(6YxժU,_wu555!Qwq6I!K,AcID&(j˖-Y#뿣>M<NȉIiӲT`i aQ+_IA]WehpU%# EИh/`߱؆n"&koLoҎA̗4}^4בǙ0@tϋyUqideTE{uYQb?֯x[̧UʧL=XP`PV>4yL 60s Eob`SQqAPCfЏ2P=ߣk+ [oX,GNJF{ruww_tn[!w^(8/y?9|m`:$q\= Ff| &'yQV1(maTG-:Pq5qJ;y4IWX~YI8kmmmoo'v{iiiyyn'FJ1/yOrO `l(;<<Ν;bD9(  ;wmժU1qu"%)CPS9s̞=?kTU%R䌌9DKS'`F(ֶrJA8.Y^_Ŷ6rH1/yOrO Q*v98DL=.aO$&OG$|$)rs4| Fc%I"ݴ[ƍA\ ބ{t,+V먝3-a`%kט+>šW+ vjEYEQLlx!^1'BQ9x/=j <5@B\ &Qf|T<;A-?SsSgOzZADLfE}2opO?,Wfs1l'h$ fna|OQڠ1*.1)atWV`t1â{Pk+qNd k0YQ&K$5@%b]e=1& kO*4ZBl}-zb5`j/`dcՖ6`)$ `|3eF5'\lxF!! u+(fw3bؖ~b'i163nj_m3r4'lGi;\\E8e5`~J5$m`v h1X$jo֊+Ey^ΧY.ҥKv}ݺu{&㉄ݎ! 慱ZH@PQ6@d 0.ka>㸡[1Q@@,/!|Ijނ,/ex( DY ^ vG>n,/exe1S6d @@чFs?D2TWWf&S 80f&Ee!H4k555fmpi?fuuuff&S}3eQ,ޱm!@"HJ&&2 >6@e ` 7B]@@% N>@6v`USbc xh߲؞/I- QXΰ$ff4'tSQ:rq3Рil9.uM M 4MQ^ +,9烁c6ͩ(h}>oB,Ev=r1`|O@sefb)7ط0h_H*x sj Wgm_R9D&1a6U_!}g>`Lទq.;~?ڀqCG@ZR\s^)q\8KcH߯$j7 uL?<h1NR )jTD7n2FE0e3j&@@iLX-|& >KE[;ѣlkr SKvP 1Cqˎ/ۿ(KD)pEM4 Xq 9Y nzIgg|"4@BqRpE%p?A3dWH(!ElWP|=#&L.gap"!! 1Pp$hn \-nY,p'P ,P4~fMG(m}F|. Kl}}} `9n@ &ڷ,KR @$zh0^{5CD` Q!TWW7Y%555Sh/3ٻ]m>D'w?-Gٚw$$`!vV tc0XUI C,EI%31['ffTɒ|_\{K{*\Ąq諸O 1aF3a81$<7[X #3O %؅4UyA z!v]k4vw9eJ_OZKQaͨVq7:, B,EaAvz{$cbG. ƊgĖqQ !_8 E3!f߭p؂ 8FeOnwgb1uYndFX 8FpsI68n\T؆XY >cV=q Fܷ eاO;OhbTA|>p32TӘ5 /fW;}MM ;ٍy'z7'i6rÒ#MFkQ1a\јq]YyGyfg= sw~4mIX4b8|fBQR Qs}no1@:7ͽWC;\vGSrkoFn4I~ޏGFg߸r:'{-i7Oͽ oBu='ǰϻ8>=6:Ɇ_ uPSJ5q7掷Y|WݬvO)O>M ȅ) )&G]ry†}0f$뛮=karM!gn{J.Ȑk"O} Y=&z4nq'gaK7bS&GLN"KǖQli9!.8o:>4>%ep㡍\`JE LI•dJMs6Q{hl Ȏ9Nnfצiܹ3dscK5z[Vޡm?ғwOsoNFYb4=r7<N{FMs/ ` wzr_0a"@Nff@zAPR $'&C W9;.O WU=CiY2 rۗ j$' W9;` 9 `0#s`pIeN$Ö}_V/@O`AZS99-Ž̿Y'@=3. 4K9TxtN?^{92%H:c2Ӛ8 JK%Gn s8$aW1I~}KN/Q w zr2t6 e vLdlnȮt&P?=6DO5m$N)Sؿtȑ]V!-5gb)9LJP# \kK+~ӵ KFv]Z~KN[B֔z_Iu%~_v&q깈_茸|&gKB$R;$GɅŽf#uE䈜9㥎fY>G9;3bKvq,SZr#GI1B|kE.ʊrRrLr@\ϕsK%|]W6-ir}hl$|9W"$YQ[zRQlQQ=g48jTd='g~WC%T=Q6\ WԞB{N},soBfUXl@r:Ɩ cHʱ!4h;N/9C3 @@K3r0 __P@ZrKʋ2rOCŒ|ߚ{/ }cm ȁr<ΐ'\ r'#w3DzɽU߽ wLr?}[F޷*]/z,#3rAur nrG \ ]M]rǛ 4#7Or-\Ks&-Y;+;TZ0r&V<ܔy7]r~=nrA=wzj][M[[q^އuGCzɽ9[> Focx2VKj|gڛܙg3ܦ݊ [Ou:f**"rՑSA6 ]\x˸ W9l- wtrf}( rA8Us Fn9΋3pA@8ö2™,Z߰? rlrrr r rYrD Hb6].t ]@*@:rUILC{@@@ngr W9EN2̔g>~apuC_aru/D(-ax~69T@r 9ȁRQI9xgʭ (f!p5ː*Ns窭`h@r%@` 9lurxzُy͑>D}?MINS^Br^'` )9}r6tGN~K>喓SIyGsXn0{:ꔖc'ș#>O^<9ײCNLсQ7l髷~2 'frf28 3UJ$#' Q=WYu&͹z9Dlz.qfk(s68MNOQ9˓#uL2Ln#r9 |09sYt47u7"'>%eJ?svfY7swH<ʐH=@r33YM,br}κssfUF[IrFl\FH'ǖs'llNOn&dsE9>,m3=g)m씴Ȗ\ sBmKbѓϐKs60u%٢PR..R4--NtcsDH܇B998c@.L_ή̈?.#lڪ\jRdq%3v ȕ` |Kw&3-)Z=*u ߁{|wg\:ɥ=kv<9'q>AN)+#܆|o!TKhҍgR&g=QFC髖P MĖ{_-t(-K z# +Pȭևn>999sxN8ߠ_y{+D( r r{sB@sTA3#Ϸ:ܣQ0g\OO."&' ȅ<9aw!Id֟{rڵlo듋ϝ9_ 00m•ɹⳔ.9ΐQKs R]V&U7Wٽ$9{k&׆ἢbrf銵qi#Bi Us\L}Nr{Fi~sʫ@."sd|9ʒNҪJ*#L{Nl$ƖcK"NӗŖ~s ▍ٴF!:>irʕ/+ /iFO[Ozk@!'z!rQar/soo6M.mҴ(-u#iSݣޭҒ xn&.hNEx!rKNŷE}(:7 r ãcD=NF NMIr[y!rg3Mti%з"wrc҅B_89m"r dHϽ'g\Cξ>s &빗"g9G OKPA'$ 勐kKC#eecKv#+e]X=:V0ڰCJV٨"GNs:T,rr17$gfyrY19X8MsB%}6\ zw 'qW>rOD9rE= ?9r'go&B-)mρ81UJ'Gάr#R΂ȁȁȁȁȁȁFkʾâG%O j%5eaEkGU.ȁ `kcόV2*&URD* W+9dcM#GruZA*kYtĖCȁ@r/BSv'8ġ'Xmc9KtLU6__Z+)BO|S=,9!v"=Uan:_ Gh9G. |n\#5O!VV뫒@M%챓EhJ\a=79"YX ܘFP0oaiǎGt2D9T@nA=q82>;e9sJĭҢҒzN%dyJ}MecK5;$sŖ*3i {ЉbKWLz' =ɹzx>8q3Y,K8: W TG%w챂 ĹCHI3 X8|szVuk ɱYXAzsx~" 9VPrr j_^υ?Vo=UO3" ㏎r+XWO\AO}(j@O4U]zYrYߕ\OO4U]z}r?h=+qv"t&ی\^O* 7TJA~\}&%"ɭ r r r r r r r'=qġ'8l@Drk&=*KK艟&rU*x9艣r r 9Ƚ9Lف8 zc1ȁȁܑ{y(JO|)g)鉛4;RrOS,7H[խ'(Fru뉯CnE",F\z+sr`4G?Jn==A9p_R,7θ;F&Gֿm9[[CO\k?-?=O9cP%CXAnUr*i\4?D(*yicmr+L9Ŗma܀r G+*\܈rxr ȭ܇R])ȭv$mɭ(s{\9PW U%" 8I@n ܘ|&AJrC!FH!9<{8 ;99hN#LU@@KA#`r@#|R#9h&rUF!FH` 99r4B*99+!^[\Cq r=?V|HqCȹtre!~/LνܸFu.|,I$rg8 sA'#Gck{qJK#/ٙKHAr!9}B_1Q4B(~Lqi)O\95Bv&'rb7.$4Bvh. :!2D)9ݞcFbV 1@(zyrjg8Ƚ<9^䞥'~>rR0e;rO?%9ۂVחb{ȉf*"r{[QOvw%JOGwF zg#ʖV?)brsVQIoVOI.ĖzhLz(27dQOއ9%=V1@N{QO¬uȵG(?h #={ BOθ_=Sz q^'W'Kns_?SmA- IǏ%W'.'#-Ygɽe$x;ӛ;A.ϖI #'cT(/ݽs>W'.Ns*'+r{@伞x\Q)-LŖJ;x$23B뉓U0ݼ9zYA~"GZچܤ$8WH̽_ r r r r r r r;{J69999 GaG0{%rO֟AgR9<YX9ɑJKARdd䐍5הUFܪk07sd4[r r 9Ƚ 9L٩@vra׳~=z6C;}3ՌcsA r rg#gW6ê~1dI HrG6HGwA}z!h0g,"%:3K7;9KGR '^%'JJL ONFJ"HH.h|+䵲M-IaM:IԈG*g"g// ML.o BA B% *BI$K=P\ M(TrXú1JKQoC.-U[rbK"ONRP)ePU#ΘͫCĖNu*&HO<碋͵C1Lz  na1Nz═x=\Bz' 7SO X/\An}r2P뉓 L_D{ ;ԡ?<9  9 ,i=q{rۑSj\8KNȕ8 7BYJΖ e=ga.8q=q7 rG%s깃2ОP@mRHNiNN!9F=SF(09;:b=qh\nh4Bj$SC6D!ՓA#^r ` r r0=@SvK#V~="9 @r0x "X &A`BG fɐe\s'9ss0 ` Ud r0@:9SI(zF0ȑh<9ׇ8/#aCDi@2 "2β] =? I/⡚l+aH4zU ʸm %HHoO?v{d#ldsq}$jG D}jn=,$* 3H4DXa7|ĊOu{wCSYUT!(~fǫ #:Q,mSvYS_j#1\L7|FHTI[2>NöԏsS+s?##G}=!<77711133399 u$I+WJ#MOOK#mcvl<~ k ٺCr}~c!G? u%s \kt:uGQ;> Bԑ4^^ݠ1],Ux1T0lʝk;>2}< ƅ ȑa2 f bɗ6jh#RfF U}I6#0gD҆v'F<Gl`yZBp3u*B pP/ws##wVPI=P922M'Y^SJ`Q;ް+zdx 0(+YI Ou@DslG㵣zvXmڨDW)5HC~1PwdvvyZ6k` 9=r$zN!<}Gp+f˵Q*cɅT&-1<-R%GBԎ%Ijz .د":juyԑWF.pudm̗ &v:ioOԗj{O@yxGh/ԑ$!GG@:n#+Ix?p9=qH:IHϞAAY)5q3C9QG:F y#]Go@QȑU !<< J~[yPGB/SP@!xG##G}=!<77711133399 u$I\1;6r$zfggɴNgza! ܾ&uՑIgNg:Ӕw ҩkt)R97ךZ6@jRDDC>6#"Ms.G&| ;8egthӤZF*इ0T\ny"sfI+K-J##S|ISH hS%!m3 gLcš\\翺")3lTbjm:'mpSM6Z-|+h 6?oͼ#ؒbt w*g_G3$F&Ÿiґu`4&uN:A[}+xIIc0~8b(Jݩ8s~'$bHXrjY[Yְ- s0$ [gG{ *G)M="RƤ5JMi >Ǧ-#W_8WGjG7F^VUx?g|=ٵ r 4j&3q5l9xIg5r+GқVZx>$%$]3`OE9c0_-*$bȄfD$Kmt12=i$Y?kz?i|e9H7 !<@N5RBՕ7i6yg R͛e}bߙz5 aGv H4PIv A@O3'<{G{Ն!;zlMn{{9х P>I1s˯>| Ͽxhnye#G|\⡩#BYU6\{7pUVbۊ=6hbX63tέd?q8~v+[3/6x@2>2^_{ U}n\iTVȑFΦIENDB`phpgacl-3.3.7/soap/0040755025754300001440000000000010476665053013032 5ustar ipsousersphpgacl-3.3.7/soap/clients/0040755025754300001440000000000010476665053014473 5ustar ipsousersphpgacl-3.3.7/soap/clients/python_client.py0100644025754300001440000000151007722515274017715 0ustar ipsousers#!/usr/bin/python # # This uses the Python ZSI SOAP Infrastructure from: http://pywebsvcs.sourceforge.net/ # import sys; from ZSI.client import Binding; #soapclient = Binding(url='/phpgacl/soap/server.php', host='hubcap.netnation.com', tracefile=sys.stdout); soapclient = Binding(url='/phpgacl/soap/server.php', host='hubcap.netnation.com'); def acl_check(aco_section_value, aco_value, aro_section_value, aro_value, axo_section_value=0, axo_value=0, root_aro_group_id=0, root_axo_group_id=0): return soapclient.acl_check(aco_section_value, aco_value, aro_section_value, aro_value, axo_section_value, axo_value, root_aro_group_id, root_axo_group_id); if ( acl_check('system','login','users','john_doe') ): print "John Doe has been granted access to login!
\n"; else: print "John Doe has been denied access to login!
\n"; phpgacl-3.3.7/soap/clients/perl_client.pl0100644025754300001440000000165207722515274017330 0ustar ipsousers#!/usr/bin/perl # # SOAP Lite needs to be installed for this to work correctly. # use SOAP::Lite; # # EDIT THE BELOW URL TO MATCH YOUR SERVER. # sub soap_init { my $soap = new SOAP::Lite uri => 'http://localhost/phpgacl/soap/server.php', proxy => 'http://localhost/phpgacl/soap/server.php' or die "Failed SOAP connection: $! \n"; } $soap = soap_init(); sub acl_check { my ($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value, $root_aro_group_id, $root_axo_group_id) = @_; return $soap->call('acl_check',$aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value,$axo_value, $root_aro_group_id, $root_axo_group_id)->result } if ( acl_check('system','login','users','john_doe') ) { print "John Doe has been granted access to login!
\n"; } else { print "John Doe has been denied access to login!
\n"; } phpgacl-3.3.7/soap/clients/php_client.php0100644025754300001440000000142207722503714017320 0ustar ipsouserscall('acl_check',$parameters); } if ( acl_check('system','login','users','john_doe') ) { echo "John Doe has been granted access to login!
\n"; } else { echo "John Doe has been denied access to login!
\n"; } ?> phpgacl-3.3.7/soap/nusoap.php0100644025754300001440000040340607722503660015047 0ustar ipsousers * @version v 0.6.3 * @access public */ class nusoap_base { var $title = 'NuSOAP'; var $version = '0.6.3'; var $error_str = false; var $debug_str = ''; // toggles automatic encoding of special characters var $charencoding = true; /** * set schema version * * @var XMLSchemaVersion * @access public */ var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema'; /** * set default encoding * * @var soap_defencoding * @access public */ //var $soap_defencoding = 'UTF-8'; var $soap_defencoding = 'ISO-8859-1'; /** * load namespace uris into an array of uri => prefix * * @var namespaces * @access public */ var $namespaces = array( 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/', 'xsd' => 'http://www.w3.org/2001/XMLSchema', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/', 'si' => 'http://soapinterop.org/xsd'); /** * load types into typemap array * is this legacy yet? * no, this is used by the xmlschema class to verify type => namespace mappings. * @var typemap * @access public */ var $typemap = array( 'http://www.w3.org/2001/XMLSchema' => array( 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double', 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'', 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string', // derived datatypes 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'', 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer', 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer', 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''), 'http://www.w3.org/1999/XMLSchema' => array( 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', 'float'=>'double','dateTime'=>'string', 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'), 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'), 'http://xml.apache.org/xml-soap' => array('Map') ); /** * entities to convert * * @var xmlEntities * @access public */ var $xmlEntities = array('quot' => '"','amp' => '&', 'lt' => '<','gt' => '>','apos' => "'"); /** * adds debug data to the class level debug string * * @param string $string debug data * @access private */ function debug($string){ $this->debug_str .= get_class($this).": $string\n"; } /** * returns error string if present * * @return boolean $string error string * @access public */ function getError(){ if($this->error_str != ''){ return $this->error_str; } return false; } /** * sets error string * * @return boolean $string error string * @access private */ function setError($str){ $this->error_str = $str; } /** * serializes PHP values in accordance w/ section 5. Type information is * not serialized if $use == 'literal'. * * @return string * @access public */ function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){ if(is_object($val) && get_class($val) == 'soapval'){ return $val->serialize($use); } $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use"); // if no name, use item $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name; // if name has ns, add ns prefix to name $xmlns = ''; if($name_ns){ $prefix = 'nu'.rand(1000,9999); $name = $prefix.':'.$name; $xmlns .= " xmlns:$prefix=\"$name_ns\""; } // if type is prefixed, create type prefix if($type_ns != '' && $type_ns == $this->namespaces['xsd']){ // need to fix this. shouldn't default to xsd if no ns specified // w/o checking against typemap $type_prefix = 'xsd'; } elseif($type_ns){ $type_prefix = 'ns'.rand(1000,9999); $xmlns .= " xmlns:$type_prefix=\"$type_ns\""; } // serialize attributes if present if($attributes){ foreach($attributes as $k => $v){ $atts .= " $k=\"$v\""; } } // serialize if an xsd built-in primitive type if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){ if ($use == 'literal') { return "<$name$xmlns>$val"; } else { return "<$name$xmlns xsi:type=\"xsd:$type\">$val"; } } // detect type and serialize $xml = ''; $atts = ''; switch(true) { case ($type == '' && is_null($val)): if ($use == 'literal') { // TODO: depends on nillable $xml .= "<$name$xmlns/>"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>"; } break; case (is_bool($val) || $type == 'boolean'): if(!$val){ $val = 0; } if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val"; } break; case (is_int($val) || is_long($val) || $type == 'int'): if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val"; } break; case (is_float($val)|| is_double($val) || $type == 'float'): if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val"; } break; case (is_string($val) || $type == 'string'): if($this->charencoding){ $val = htmlspecialchars($val, ENT_QUOTES); } if ($use == 'literal') { $xml .= "<$name$xmlns $atts>$val"; } else { $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val"; } break; case is_object($val): $name = get_class($val); foreach(get_object_vars($val) as $k => $v){ $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use); } $xml .= '<'.$name.'>'.$pXml.''; break; break; case (is_array($val) || $type): // detect if struct or array $keyList = array_keys($val); $valueType = 'arraySimple'; foreach($keyList as $keyListValue){ if(!is_int($keyListValue)){ $valueType = 'arrayStruct'; break; } } if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){ $i = 0; if(is_array($val) && count($val)> 0){ foreach($val as $v){ if(is_object($v) && get_class($v) == 'soapval'){ $tt = $v->type; } else { $tt = gettype($v); } $array_types[$tt] = 1; $xml .= $this->serialize_val($v,'item',false,false,false,false,$use); if(is_array($v) && is_numeric(key($v))){ $i += sizeof($v); } else { ++$i; } } if(count($array_types) > 1){ $array_typename = 'xsd:ur-type'; } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) { $array_typename = 'xsd:'.$tt; } elseif($tt == 'array' || $tt == 'Array'){ $array_typename = 'SOAP-ENC:Array'; } else { $array_typename = $tt; } if(isset($array_types['array'])){ $array_type = $i.",".$i; } else { $array_type = $i; } if ($use == 'literal') { $xml = "<$name $atts>".$xml.""; } else { $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"$atts>".$xml.""; } // empty array } else { if ($use == 'literal') { $xml = "<$name $atts>".$xml."";; } else { $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>".$xml."";; } } } else { // got a struct if(isset($type) && isset($type_prefix)){ $type_str = " xsi:type=\"$type_prefix:$type\""; } else { $type_str = ''; } if ($use == 'literal') { $xml .= "<$name$xmlns $atts>"; } else { $xml .= "<$name$xmlns$type_str$atts>"; } foreach($val as $k => $v){ $xml .= $this->serialize_val($v,$k,false,false,false,false,$use); } $xml .= ""; } break; default: $xml .= 'not detected, got '.gettype($val).' for '.$val; break; } return $xml; } /** * serialize message * * @param string body * @param string headers * @param array namespaces * @param string style * @return string message * @access public */ function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc'){ // serialize namespaces $ns_string = ''; foreach(array_merge($this->namespaces,$namespaces) as $k => $v){ $ns_string .= " xmlns:$k=\"$v\""; } if($style == 'rpc') { $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string; } // serialize headers if($headers){ $headers = "".$headers.""; } // serialize envelope return 'soap_defencoding .'"?'.">". '". $headers. "". $body. "". ""; } function formatDump($str){ $str = htmlspecialchars($str); return nl2br($str); } /** * returns the local part of a prefixed string * returns the original string, if not prefixed * * @param string * @return string * @access public */ function getLocalPart($str){ if($sstr = strrchr($str,':')){ // get unqualified name return substr( $sstr, 1 ); } else { return $str; } } /** * returns the prefix part of a prefixed string * returns false, if not prefixed * * @param string * @return mixed * @access public */ function getPrefix($str){ if($pos = strrpos($str,':')){ // get prefix return substr($str,0,$pos); } return false; } function varDump($data) { ob_start(); var_dump($data); $ret_val = ob_get_contents(); ob_end_clean(); return $ret_val; } } // XML Schema Datatype Helper Functions //xsd:dateTime helpers /** * convert unix timestamp to ISO 8601 compliant date string * * @param string $timestamp Unix time stamp * @access public */ function timestamp_to_iso8601($timestamp,$utc=true){ $datestr = date('Y-m-d\TH:i:sO',$timestamp); if($utc){ $eregStr = '([0-9]{4})-'. // centuries & years CCYY- '([0-9]{2})-'. // months MM- '([0-9]{2})'. // days DD 'T'. // separator T '([0-9]{2}):'. // hours hh: '([0-9]{2}):'. // minutes mm: '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss... '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's if(ereg($eregStr,$datestr,$regs)){ return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]); } return false; } else { return $datestr; } } /** * convert ISO 8601 compliant date string to unix timestamp * * @param string $datestr ISO 8601 compliant date string * @access public */ function iso8601_to_timestamp($datestr){ $eregStr = '([0-9]{4})-'. // centuries & years CCYY- '([0-9]{2})-'. // months MM- '([0-9]{2})'. // days DD 'T'. // separator T '([0-9]{2}):'. // hours hh: '([0-9]{2}):'. // minutes mm: '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss... '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's if(ereg($eregStr,$datestr,$regs)){ // not utc if($regs[8] != 'Z'){ $op = substr($regs[8],0,1); $h = substr($regs[8],1,2); $m = substr($regs[8],strlen($regs[8])-2,2); if($op == '-'){ $regs[4] = $regs[4] + $h; $regs[5] = $regs[5] + $m; } elseif($op == '+'){ $regs[4] = $regs[4] - $h; $regs[5] = $regs[5] - $m; } } return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z"); } else { return false; } } ?> * @version v 0.6.3 * @access public */ class soap_fault extends nusoap_base { var $faultcode; var $faultactor; var $faultstring; var $faultdetail; /** * constructor * * @param string $faultcode (client | server) * @param string $faultactor only used when msg routed between multiple actors * @param string $faultstring human readable error message * @param string $faultdetail */ function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){ $this->faultcode = $faultcode; $this->faultactor = $faultactor; $this->faultstring = $faultstring; $this->faultdetail = $faultdetail; } /** * serialize a fault * * @access public */ function serialize(){ $ns_string = ''; foreach($this->namespaces as $k => $v){ $ns_string .= "\n xmlns:$k=\"$v\""; } $return_msg = '\n". '\n". ''. ''. ''.$this->faultcode.''. ''.$this->faultactor.''. ''.$this->faultstring.''. ''.$this->serialize_val($this->faultdetail).''. ''. ''. ''; return $return_msg; } } ?> * @version v 0.6.3 * @access public */ class XMLSchema extends nusoap_base { // files var $schema = ''; var $xml = ''; // define internal arrays of bindings, ports, operations, messages, etc. var $complexTypes = array(); // target namespace var $schemaTargetNamespace = ''; // parser vars var $parser; var $position; var $depth = 0; var $depth_array = array(); /** * constructor * * @param string $schema schema document URI * @param string $xml xml document URI * @access public */ function XMLSchema($schema='',$xml=''){ $this->debug('xmlschema class instantiated, inside constructor'); // files $this->schema = $schema; $this->xml = $xml; // parse schema file if($schema != ''){ $this->debug('initial schema file: '.$schema); $this->parseFile($schema); } // parse xml file if($xml != ''){ $this->debug('initial xml file: '.$xml); $this->parseFile($xml); } } /** * parse an XML file * * @param string $xml, path/URL to XML file * @param string $type, (schema | xml) * @return boolean * @access public */ function parseFile($xml,$type){ // parse xml file if($xml != ""){ $this->debug('parsing $xml'); $xmlStr = @join("",@file($xml)); if($xmlStr == ""){ $this->setError('No file at the specified URL: '.$xml); return false; } else { $this->parseString($xmlStr,$type); return true; } } return false; } /** * parse an XML string * * @param string $xml path or URL * @param string $type, (schema|xml) * @access private */ function parseString($xml,$type){ // parse xml string if($xml != ""){ // Create an XML parser. $this->parser = xml_parser_create(); // Set the options for parsing the XML data. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); // Set the object for the parser. xml_set_object($this->parser, $this); // Set the element handlers for the parser. if($type == "schema"){ xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); xml_set_character_data_handler($this->parser,'schemaCharacterData'); } elseif($type == "xml"){ xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); xml_set_character_data_handler($this->parser,'xmlCharacterData'); } // Parse the XML file. if(!xml_parse($this->parser,$xml,true)){ // Display an error message. $errstr = sprintf('XML error on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)) ); $this->debug('XML parse error: '.$errstr); $this->setError('Parser error: '.$errstr); } xml_parser_free($this->parser); } else{ $this->debug('no xml passed to parseString()!!'); $this->setError('no xml passed to parseString()!!'); } } /** * start-element handler * * @param string $parser XML parser object * @param string $name element name * @param string $attrs associative array of attributes * @access private */ function schemaStartElement($parser, $name, $attrs) { // position in the total number of elements, starting from 0 $pos = $this->position++; $depth = $this->depth++; // set self as current value for this depth $this->depth_array[$depth] = $pos; // get element prefix if($prefix = $this->getPrefix($name)){ // get unqualified name $name = $this->getLocalPart($name); } else { $prefix = ''; } // loop thru attributes, expanding, and registering namespace declarations if(count($attrs) > 0){ foreach($attrs as $k => $v){ // if ns declarations, add to class level array of valid namespaces if(ereg("^xmlns",$k)){ //$this->xdebug("$k: $v"); //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); if($ns_prefix = substr(strrchr($k,':'),1)){ $this->namespaces[$ns_prefix] = $v; } else { $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; } if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){ $this->XMLSchemaVersion = $v; $this->namespaces['xsi'] = $v.'-instance'; } } } foreach($attrs as $k => $v){ // expand each attribute $k = strpos($k,':') ? $this->expandQname($k) : $k; $v = strpos($v,':') ? $this->expandQname($v) : $v; $eAttrs[$k] = $v; } $attrs = $eAttrs; } else { $attrs = array(); } // find status, register data switch($name){ case ('all'|'choice'|'sequence'): //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all'; $this->complexTypes[$this->currentComplexType]['compositor'] = $name; if($name == 'all'){ $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; } break; case 'attribute': //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); if(isset($attrs['name'])){ $this->attributes[$attrs['name']] = $attrs; $aname = $attrs['name']; } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; } elseif(isset($attrs['ref'])){ $aname = $attrs['ref']; $this->attributes[$attrs['ref']] = $attrs; } if(isset($this->currentComplexType)){ $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; } elseif(isset($this->currentElement)){ $this->elements[$this->currentElement]['attrs'][$aname] = $attrs; } // arrayType attribute if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; $prefix = $this->getPrefix($aname); if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; } else { $v = ''; } if(strpos($v,'[,]')){ $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; } $v = substr($v,0,strpos($v,'[')); // clip the [] if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ $v = $this->XMLSchemaVersion.':'.$v; } $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; } break; case 'complexType': if(isset($attrs['name'])){ $this->currentElement = false; $this->currentComplexType = $attrs['name']; $this->complexTypes[$this->currentComplexType] = $attrs; $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; } else { $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; } $this->xdebug('processing complexType '.$attrs['name']); } break; case 'element': if(isset($attrs['type'])){ $this->xdebug("processing element ".$attrs['name']); $this->currentElement = $attrs['name']; $this->elements[ $attrs['name'] ] = $attrs; $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; $ename = $attrs['name']; } elseif(isset($attrs['ref'])){ $ename = $attrs['ref']; } else { $this->xdebug('adding complexType '.$attrs['name']); $this->currentComplexType = $attrs['name']; $this->complexTypes[ $attrs['name'] ] = $attrs; $this->complexTypes[ $attrs['name'] ]['element'] = 1; $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; } if(isset($ename) && $this->currentComplexType){ $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; } break; case 'restriction': $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement"); if($this->currentElement){ $this->elements[$this->currentElement]['type'] = $attrs['base']; } elseif($this->currentComplexType){ $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; if(strstr($attrs['base'],':') == ':Array'){ $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; } } break; case 'schema': $this->schema = $attrs; $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); break; case 'simpleType': $this->currentElement = $attrs['name']; $this->elements[ $attrs['name'] ] = $attrs; $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; break; } } /** * end-element handler * * @param string $parser XML parser object * @param string $name element name * @access private */ function schemaEndElement($parser, $name) { // position of current element is equal to the last value left in depth_array for my depth if(isset($this->depth_array[$this->depth])){ $pos = $this->depth_array[$this->depth]; } // bring depth down a notch $this->depth--; // move on... if($name == 'complexType'){ $this->currentComplexType = false; $this->currentElement = false; } if($name == 'element'){ $this->currentElement = false; } } /** * element content handler * * @param string $parser XML parser object * @param string $data element content * @access private */ function schemaCharacterData($parser, $data){ $pos = $this->depth_array[$this->depth]; $this->message[$pos]['cdata'] .= $data; } /** * serialize the schema * * @access public */ function serializeSchema(){ $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); $xml = ''; // complex types foreach($this->complexTypes as $typeName => $attrs){ $contentStr = ''; // serialize child elements if(count($attrs['elements']) > 0){ foreach($attrs['elements'] as $element => $eParts){ if(isset($eParts['ref'])){ $contentStr .= ""; } else { $contentStr .= ""; } } } // attributes if(count($attrs['attrs']) >= 1){ foreach($attrs['attrs'] as $attr => $aParts){ $contentStr .= '".$contentStr.""; } // "all" compositor obviates complex/simple content if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){ $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr.""; } // complex or simple content elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){ $contentStr = "<$schemaPrefix:complexContent>".$contentStr.""; } // compositors if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){ $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr.""; } // finalize complex type if($contentStr != ''){ $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr.""; } else { $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>"; } $xml .= $contentStr; } // elements if(isset($this->elements) && count($this->elements) > 0){ foreach($this->elements as $element => $eParts){ $xml .= "<$schemaPrefix:element name=\"$element\" type=\"".$eParts['type']."\"/>"; } } // attributes if(isset($this->attributes) && count($this->attributes) > 0){ foreach($this->attributes as $attr => $aParts){ $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"".$aParts['type']."\"/>"; } } // finish 'er up $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml.""; return $xml; } /** * expands a qualified name * * @param string $string qname * @return string expanded qname * @access private */ function expandQname($qname){ // get element prefix if(strpos($qname,':') && !ereg('^http://',$qname)){ // get unqualified name $name = substr(strstr($qname,':'),1); // get ns prefix $prefix = substr($qname,0,strpos($qname,':')); if(isset($this->namespaces[$prefix])){ return $this->namespaces[$prefix].':'.$name; } else { return $qname; } } else { return $qname; } } /** * adds debug data to the clas level debug string * * @param string $string debug data * @access private */ function xdebug($string){ $this->debug(' xmlschema: '.$string); } /** * get the PHP type of a user defined type in the schema * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays * returns false if no type exists, or not w/ the given namespace * else returns a string that is either a native php type, or 'struct' * * @param string $type, name of defined type * @param string $ns, namespace of type * @return mixed * @access public */ function getPHPType($type,$ns){ global $typemap; if(isset($typemap[$ns][$type])){ //print "found type '$type' and ns $ns in typemap
"; return $typemap[$ns][$type]; } elseif(isset($this->complexTypes[$type])){ //print "getting type '$type' and ns $ns from complexTypes array
"; return $this->complexTypes[$type]['phpType']; } return false; } /** * returns the local part of a prefixed string * returns the original string, if not prefixed * * @param string * @return string * @access public */ function getLocalPart($str){ if($sstr = strrchr($str,':')){ // get unqualified name return substr( $sstr, 1 ); } else { return $str; } } /** * returns the prefix part of a prefixed string * returns false, if not prefixed * * @param string * @return mixed * @access public */ function getPrefix($str){ if($pos = strrpos($str,':')){ // get prefix return substr($str,0,$pos); } return false; } /** * pass it a prefix, it returns a namespace * returns false if no namespace registered with the given prefix * * @param string * @return mixed * @access public */ function getNamespaceFromPrefix($prefix){ if(isset($this->namespaces[$prefix])){ return $this->namespaces[$prefix]; } //$this->setError("No namespace registered for prefix '$prefix'"); return false; } /** * returns the prefix for a given namespace (or prefix) * or false if no prefixes registered for the given namespace * * @param string * @return mixed * @access public */ function getPrefixFromNamespace($ns){ foreach($this->namespaces as $p => $n){ if($ns == $n || $ns == $p){ $this->usedNamespaces[$p] = $n; return $p; } } return false; } /** * returns an array of information about a given type * returns false if no type exists by the given name * * typeDef = array( * 'elements' => array(), // refs to elements array * 'restrictionBase' => '', * 'phpType' => '', * 'order' => '(sequence|all)', * 'attrs' => array() // refs to attributes array * ) * * @param string * @return mixed * @access public */ function getTypeDef($type){ if(isset($this->complexTypes[$type])){ return $this->complexTypes[$type]; } elseif(isset($this->elements[$type])){ return $this->elements[$type]; } elseif(isset($this->attributes[$type])){ return $this->attributes[$type]; } return false; } /** * returns a sample serialization of a given type, or false if no type by the given name * * @param string $type, name of type * @return mixed * @access public */ function serializeTypeDef($type){ //print "in sTD() for type $type
"; if($typeDef = $this->getTypeDef($type)){ $str .= '<'.$type; if(is_array($typeDef['attrs'])){ foreach($attrs as $attName => $data){ $str .= " $attName=\"{type = ".$data['type']."}\""; } } $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; if(count($typeDef['elements']) > 0){ $str .= ">"; foreach($typeDef['elements'] as $element => $eData){ $str .= $this->serializeTypeDef($element); } $str .= ""; } elseif($typeDef['typeClass'] == 'element') { $str .= ">"; } else { $str .= "/>"; } return $str; } return false; } /** * returns HTML form elements that allow a user * to enter values for creating an instance of the given type. * * @param string $name, name for type instance * @param string $type, name of type * @return string * @access public */ function typeToForm($name,$type){ // get typedef if($typeDef = $this->getTypeDef($type)){ // if struct if($typeDef['phpType'] == 'struct'){ $buffer .= ''; foreach($typeDef['elements'] as $child => $childDef){ $buffer .= " "; } $buffer .= '
$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):
'; // if array } elseif($typeDef['phpType'] == 'array'){ $buffer .= ''; for($i=0;$i < 3; $i++){ $buffer .= " "; } $buffer .= '
array item (type: $typeDef[arrayType]):
'; // if scalar } else { $buffer .= ""; } } else { $buffer .= ""; } return $buffer; } /** * adds an XML Schema complex type to the WSDL types * * example: array * * addType( * 'ArrayOfstring', * 'complexType', * 'array', * '', * 'SOAP-ENC:Array', * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), * 'xsd:string' * ); * * example: PHP associative array ( SOAP Struct ) * * addType( * 'SOAPStruct', * 'complexType', * 'struct', * 'all', * array('myVar'=> array('name'=>'myVar','type'=>'string') * ); * * @param name * @param typeClass (complexType|simpleType|attribute) * @param phpType: currently supported are array and struct (php assoc array) * @param compositor (all|sequence|choice) * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) * @param elements = array ( name = array(name=>'',type=>'') ) * @param attrs = array( * array( * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" * ) * ) * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) * */ function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ $this->complexTypes[$name] = array( 'name' => $name, 'typeClass' => $typeClass, 'phpType' => $phpType, 'compositor'=> $compositor, 'restrictionBase' => $restrictionBase, 'elements' => $elements, 'attrs' => $attrs, 'arrayType' => $arrayType ); } } ?> * @version v 0.6.3 * @access public */ class soapval extends nusoap_base { /** * constructor * * @param string $name optional name * @param string $type optional type name * @param mixed $value optional value * @param string $namespace optional namespace of value * @param string $type_namespace optional namespace of type * @param array $attributes associative array of attributes to add to element serialization * @access public */ function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) { $this->name = $name; $this->value = $value; $this->type = $type; $this->element_ns = $element_ns; $this->type_ns = $type_ns; $this->attributes = $attributes; } /** * return serialized value * * @return string XML data * @access private */ function serialize($use='encoded') { return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use); } /** * decodes a soapval object into a PHP native type * * @param object $soapval optional SOAPx4 soapval object, else uses self * @return mixed * @access public */ function decode(){ return $this->value; } } ?> * @version v 0.6.3 * @access public */ class soap_transport_http extends nusoap_base { var $username = ''; var $password = ''; var $url = ''; var $proxyhost = ''; var $proxyport = ''; var $scheme = ''; var $request_method = 'POST'; var $protocol_version = '1.0'; var $encoding = ''; var $outgoing_headers = array(); var $incoming_headers = array(); var $outgoing_payload = ''; var $incoming_payload = ''; var $useSOAPAction = true; /** * constructor */ function soap_transport_http($url){ $this->url = $url; $u = parse_url($url); foreach($u as $k => $v){ $this->debug("$k = $v"); $this->$k = $v; } if(isset($u['query']) && $u['query'] != ''){ $this->path .= '?' . $u['query']; } if(!isset($u['port']) && $u['scheme'] == 'http'){ $this->port = 80; } } function connect($timeout){ // proxy if($this->proxyhost != '' && $this->proxyport != ''){ $host = $this->proxyhost; $port = $this->proxyport; $this->debug("using http proxy: $host, $port"); } else { $host = $this->host; $port = $this->port; } // ssl if($this->scheme == 'https'){ $host = 'ssl://'.$host; $port = 443; } $this->debug("connection params: $host, $port"); // timeout if($timeout > 0){ $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout); } else { $fp = fsockopen($host, $port, $this->errno, $this->error_str); } // test pointer if(!$fp) { $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error: '.$this->error_str); $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error: '.$this->error_str); return false; } return $fp; } /** * send the SOAP message via HTTP * * @param string $data message data * @param integer $timeout set timeout in seconds * @return string data * @access public */ function send($data, $timeout=0) { $this->debug('entered send() with data of length: '.strlen($data)); // get connnection if(!$fp = $this->connect($timeout)){ return false; } $this->debug('socket connected'); // start building outgoing payload: // swap url for path if going through a proxy if($this->proxyhost != '' && $this->proxyport != ''){ $this->outgoing_payload = "$this->request_method $this->url ".strtoupper($this->scheme)."/$this->protocol_version\r\n"; } else { $this->outgoing_payload = "$this->request_method $this->path ".strtoupper($this->scheme)."/$this->protocol_version\r\n"; } // make payload $this->outgoing_payload .= "User-Agent: $this->title/$this->version\r\n". "Host: ".$this->host."\r\n"; // http auth $credentials = ''; if($this->username != '') { $this->debug('setting http auth credentials'); $this->outgoing_payload .= 'Authorization: Basic '.base64_encode("$this->username:$this->password")."\r\n"; } // set content type $this->outgoing_payload .= 'Content-Type: text/xml; charset='.$this->soap_defencoding."\r\nContent-Length: ".strlen($data)."\r\n"; // http encoding if($this->encoding != '' && function_exists('gzdeflate')){ $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n". "Connection: close\r\n"; set_magic_quotes_runtime(0); } // set soapaction if($this->useSOAPAction){ $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n"; } $this->outgoing_payload .= "\r\n"; // add data $this->outgoing_payload .= $data; // send payload if(!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { $this->setError('couldn\'t write message data to socket'); $this->debug('Write error'); } $this->debug('wrote data to socket'); // get response $this->incoming_payload = ''; //$strlen = 0; while( $data = fread($fp, 32768) ){ $this->incoming_payload .= $data; //$strlen += strlen($data); } $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server'); // close filepointer fclose($fp); $this->debug('closed socket'); // connection was closed unexpectedly if($this->incoming_payload == ''){ $this->setError('no response from server'); return false; } $this->debug('received incoming payload: '.strlen($this->incoming_payload)); $data = $this->incoming_payload."\r\n\r\n\r\n\r\n"; // remove 100 header if(ereg('^HTTP/1.1 100',$data)){ if($pos = strpos($data,"\r\n\r\n") ){ $data = ltrim(substr($data,$pos)); } elseif($pos = strpos($data,"\n\n") ){ $data = ltrim(substr($data,$pos)); } }// // separate content from HTTP headers if( $pos = strpos($data,"\r\n\r\n") ){ $lb = "\r\n"; } elseif( $pos = strpos($data,"\n\n") ){ $lb = "\n"; } else { $this->setError('no proper separation of headers and document'); return false; } $header_data = trim(substr($data,0,$pos)); $header_array = explode($lb,$header_data); $data = ltrim(substr($data,$pos)); $this->debug('found proper separation of headers and document'); $this->debug('cleaned data, stringlen: '.strlen($data)); // clean headers foreach($header_array as $header_line){ $arr = explode(':',$header_line); if(count($arr) >= 2){ $headers[trim($arr[0])] = trim($arr[1]); } } //print "headers:
$header_data

"; //print "data:
$data

"; // decode transfer-encoding if(isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked'){ //$timer->setMarker('starting to decode chunked content'); if(!$data = $this->decodeChunked($data)){ $this->setError('Decoding of chunked data failed'); return false; } //$timer->setMarker('finished decoding of chunked content'); //print "
\nde-chunked:\n---------------\n$data\n\n---------------\n
"; } // decode content-encoding if(isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != ''){ if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){ // if decoding works, use it. else assume data wasn't gzencoded if(function_exists('gzinflate')){ //$timer->setMarker('starting decoding of gzip/deflated content'); if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){ $data = $degzdata; } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){ $data = $degzdata; } else { $this->setError('Errors occurred when trying to decode the data'); } //$timer->setMarker('finished decoding of gzip/deflated content'); //print "\nde-inflated:\n---------------\n$data\n-------------\n"; } else { $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); } } } if(strlen($data) == 0){ $this->debug('no data after headers!'); $this->setError('no data present after HTTP headers'); return false; } $this->debug('end of send()'); return $data; } /** * send the SOAP message via HTTPS 1.0 using CURL * * @param string $msg message data * @param integer $timeout set timeout in seconds * @return string data * @access public */ function sendHTTPS($data, $timeout=0) { //global $t; //$t->setMarker('inside sendHTTPS()'); $this->debug('entered sendHTTPS() with data of length: '.strlen($data)); // init CURL $ch = curl_init(); //$t->setMarker('got curl handle'); // set proxy if($this->proxyhost && $this->proxyport){ $host = $this->proxyhost; $port = $this->proxyport; } else { $host = $this->host; $port = $this->port; } // set url $hostURL = ($port != '') ? "https://$host:$port" : "https://$host"; // add path $hostURL .= $this->path; curl_setopt($ch, CURLOPT_URL, $hostURL); // set other options curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // encode if(function_exists('gzinflate')){ curl_setopt($ch, CURLOPT_ENCODING, 'deflate'); } // persistent connection //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true); // set timeout if($timeout != 0){ curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); } $credentials = ''; if($this->username != '') { $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n'; } if($this->encoding != ''){ if(function_exists('gzdeflate')){ $encoding_headers = "Accept-Encoding: $this->encoding\r\n". "Connection: close\r\n"; set_magic_quotes_runtime(0); } } if($this->proxyhost && $this->proxyport){ $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n"; } else { $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n"; } $this->outgoing_payload .= "User-Agent: $this->title v$this->version\r\n". "Host: ".$this->host."\r\n". $encoding_headers. $credentials. "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n". "Content-Length: ".strlen($data)."\r\n". "SOAPAction: \"$this->soapaction\""."\r\n\r\n". $data; // set payload curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); //$t->setMarker('set curl options, executing...'); // send and receive $this->incoming_payload = curl_exec($ch); //$t->setMarker('executed transfer'); $data = $this->incoming_payload; $cErr = curl_error($ch); if($cErr != ''){ $err = 'cURL ERROR: '.curl_errno($ch).': '.$cErr.'
'; foreach(curl_getinfo($ch) as $k => $v){ $err .= "$k: $v
"; } $this->setError($err); curl_close($ch); return false; } else { //echo '
';
			//var_dump(curl_getinfo($ch));
			//echo '
'; } // close curl curl_close($ch); //$t->setMarker('closed curl'); // remove 100 header if(ereg('^HTTP/1.1 100',$data)){ if($pos = strpos($data,"\r\n\r\n") ){ $data = ltrim(substr($data,$pos)); } elseif($pos = strpos($data,"\n\n") ){ $data = ltrim(substr($data,$pos)); } }// // separate content from HTTP headers if( $pos = strpos($data,"\r\n\r\n") ){ $lb = "\r\n"; } elseif( $pos = strpos($data,"\n\n") ){ $lb = "\n"; } else { $this->setError('no proper separation of headers and document'); return false; } $header_data = trim(substr($data,0,$pos)); $header_array = explode($lb,$header_data); $data = ltrim(substr($data,$pos)); $this->debug('found proper separation of headers and document'); $this->debug('cleaned data, stringlen: '.strlen($data)); // clean headers foreach($header_array as $header_line){ $arr = explode(':',$header_line); $headers[trim($arr[0])] = trim($arr[1]); } if(strlen($data) == 0){ $this->debug('no data after headers!'); $this->setError('no data present after HTTP headers.'); return false; } // decode transfer-encoding if($headers['Transfer-Encoding'] == 'chunked'){ if(!$data = $this->decodeChunked($data)){ $this->setError('Decoding of chunked data failed'); return false; } } // decode content-encoding if($headers['Content-Encoding'] != ''){ if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){ // if decoding works, use it. else assume data wasn't gzencoded if(function_exists('gzinflate')){ if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){ $data = $degzdata; } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){ $data = $degzdata; } else { $this->setError('Errors occurred when trying to decode the data'); } } else { $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); } } } // set decoded payload $this->incoming_payload = $header_data."\r\n\r\n".$data; return $data; } /** * if authenticating, set user credentials here * * @param string $user * @param string $pass * @access public */ function setCredentials($username, $password) { $this->username = $username; $this->password = $password; } /** * set the soapaction value * * @param string $soapaction * @access public */ function setSOAPAction($soapaction) { $this->soapaction = $soapaction; } /** * use http encoding * * @param string $enc encoding style. supported values: gzip, deflate, or both * @access public */ function setEncoding($enc='gzip, deflate'){ $this->encoding = $enc; $this->protocol_version = '1.1'; } /** * set proxy info here * * @param string $proxyhost * @param string $proxyport * @access public */ function setProxy($proxyhost, $proxyport) { $this->proxyhost = $proxyhost; $this->proxyport = $proxyport; } /** * decode a string that is encoded w/ "chunked' transfer encoding * as defined in RFC2068 19.4.6 * * @param string $buffer * @returns string * @access public */ function decodeChunked($buffer){ // length := 0 $length = 0; $new = ''; // read chunk-size, chunk-extension (if any) and CRLF // get the position of the linebreak $chunkend = strpos($buffer,"\r\n") + 2; $temp = substr($buffer,0,$chunkend); $chunk_size = hexdec( trim($temp) ); $chunkstart = $chunkend; // while (chunk-size > 0) { while ($chunk_size > 0) { $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size); // Just in case we got a broken connection if ($chunkend == FALSE) { $chunk = substr($buffer,$chunkstart); // append chunk-data to entity-body $new .= $chunk; $length += strlen($chunk); break; } // read chunk-data and CRLF $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); // append chunk-data to entity-body $new .= $chunk; // length := length + chunk-size $length += strlen($chunk); // read chunk-size and CRLF $chunkstart = $chunkend + 2; $chunkend = strpos($buffer,"\r\n",$chunkstart)+2; if ($chunkend == FALSE) { break; //Just in case we got a broken connection } $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); $chunk_size = hexdec( trim($temp) ); $chunkstart = $chunkend; } // Update headers //$this->Header['content-length'] = $length; //unset($this->Header['transfer-encoding']); return $new; } } ?> * @version v 0.6.3 * @access public */ class soap_server extends nusoap_base { var $service = ''; // service name var $operations = array(); // assoc array of operations => opData var $responseHeaders = false; var $headers = ''; var $request = ''; var $charset_encoding = 'UTF-8'; var $fault = false; var $result = 'successful'; var $wsdl = false; var $externalWSDLURL = false; var $debug_flag = true; /** * constructor * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. * * @param string $wsdl path or URL to a WSDL file * @access public */ function soap_server($wsdl=false){ // turn on debugging? global $debug; if(isset($debug)){ $this->debug_flag = 1; } // wsdl if($wsdl){ $this->wsdl = new wsdl($wsdl); $this->externalWSDLURL = $wsdl; if($err = $this->wsdl->getError()){ die('WSDL ERROR: '.$err); } } } /** * processes request and returns response * * @param string $data usually is the value of $HTTP_RAW_POST_DATA * @access public */ function service($data){ // print wsdl global $QUERY_STRING; if(isset($_SERVER['QUERY_STRING'])){ $qs = $_SERVER['QUERY_STRING']; } elseif(isset($GLOBALS['QUERY_STRING'])){ $qs = $GLOBALS['QUERY_STRING']; } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){ $qs = $QUERY_STRING; } // gen wsdl if(isset($qs) && ereg('wsdl', $qs) ){ if($this->externalWSDLURL){ header('Location: '.$this->externalWSDLURL); exit(); } else { header("Content-Type: text/xml\r\n"); print $this->wsdl->serialize(); exit(); } } // print web interface if($data == '' && $this->wsdl){ print $this->webDescription(); } else { // $response is the serialized response message $response = $this->parse_request($data); $this->debug('server sending...'); $payload = $response; // add debug data if in debug mode if(isset($this->debug_flag) && $this->debug_flag == 1){ $payload .= ""; } // print headers if($this->fault){ $header[] = "HTTP/1.0 500 Internal Server Error\r\n"; $header[] = "Status: 500 Internal Server Error\r\n"; } else { $header[] = "Status: 200 OK\r\n"; } $header[] = "Server: $this->title Server v$this->version\r\n"; $header[] = "Connection: Close\r\n"; $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n"; $header[] = "Content-Length: ".strlen($payload)."\r\n\r\n"; reset($header); foreach($header as $hdr){ header($hdr); } $this->response = join("\r\n",$header).$payload; print $payload; } } /** * parses request and posts response * * @param string $data XML string * @return string XML response msg * @access private */ function parse_request($data='') { $this->debug('entering parseRequest() on '.date('H:i Y-m-d')); $dump = ''; // get headers if(function_exists('getallheaders')){ $this->headers = getallheaders(); foreach($this->headers as $k=>$v){ $dump .= "$k: $v\r\n"; $this->debug("$k: $v"); } // get SOAPAction header if(isset($this->headers['SOAPAction'])){ $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']); } // get the character encoding of the incoming request if(strpos($this->headers['Content-Type'],'=')){ $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1)); if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){ $this->xml_encoding = $enc; } else { $this->xml_encoding = 'us-ascii'; } } $this->debug('got encoding: '.$this->charset_encoding); } elseif(is_array($_SERVER)){ $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT']; $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : ''; } $this->request = $dump."\r\n\r\n".$data; // parse response, get soap parser obj $parser = new soap_parser($data,$this->charset_encoding); // if fault occurred during message parsing if($err = $parser->getError()){ // parser debug $this->debug("parser debug: \n".$parser->debug_str); $this->result = 'fault: error in msg parsing: '.$err; $this->fault('Server',"error in msg parsing:\n".$err); // return soapresp return $this->fault->serialize(); // else successfully parsed request into soapval object } else { // get/set methodname $this->methodname = $parser->root_struct_name; $this->debug('method name: '.$this->methodname); // does method exist? if(!function_exists($this->methodname)){ // "method not found" fault here $this->debug("method '$this->methodname' not found!"); $this->debug("parser debug: \n".$parser->debug_str); $this->result = 'fault: method not found'; $this->fault('Server',"method '$this->methodname' not defined in service '$this->service'"); return $this->fault->serialize(); } if($this->wsdl){ if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){ //if( $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service"); return $this->fault->serialize(); } } $this->debug("method '$this->methodname' exists"); // evaluate message, getting back parameters $this->debug('calling parser->get_response()'); $request_data = $parser->get_response(); // parser debug $this->debug("parser debug: \n".$parser->debug_str); // verify that request parameters match the method's signature if($this->verify_method($this->methodname,$request_data)){ // if there are parameters to pass $this->debug('params var dump '.$this->varDump($request_data)); if($request_data){ $this->debug("calling '$this->methodname' with params"); if (! function_exists('call_user_func_array')) { $this->debug('calling method using eval()'); $funcCall = $this->methodname.'('; foreach($request_data as $param) { $funcCall .= "\"$param\","; } $funcCall = substr($funcCall, 0, -1).')'; $this->debug('function call:
'.$funcCall); @eval("\$method_response = $funcCall;"); } else { $this->debug('calling method using call_user_func_array()'); $method_response = call_user_func_array("$this->methodname",$request_data); } $this->debug('response var dump'.$this->varDump($method_response)); } else { // call method w/ no parameters $this->debug("calling $this->methodname w/ no params"); $m = $this->methodname; $method_response = @$m(); } $this->debug("done calling method: $this->methodname, received $method_response of type".gettype($method_response)); // if we got nothing back. this might be ok (echoVoid) if(isset($method_response) && $method_response != '' || is_bool($method_response)) { // if fault if(get_class($method_response) == 'soap_fault'){ $this->debug('got a fault object from method'); $this->fault = $method_response; return $method_response->serialize(); // if return val is soapval object } elseif(get_class($method_response) == 'soapval'){ $this->debug('got a soapval object from method'); $return_val = $method_response->serialize(); // returned other } else { $this->debug('got a(n) '.gettype($method_response).' from method'); $this->debug('serializing return value'); if($this->wsdl){ // weak attempt at supporting multiple output params if(sizeof($this->opData['output']['parts']) > 1){ $opParams = $method_response; } else { $opParams = array($method_response); } $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams); } else { $return_val = $this->serialize_val($method_response); } } $this->debug('return val:'.$this->varDump($return_val)); } else { $return_val = ''; $this->debug('got no response from method'); } $this->debug('serializing response'); $payload = '<'.$this->methodname."Response>".$return_val.'methodname."Response>"; $this->result = 'successful'; if($this->wsdl){ //if($this->debug_flag){ $this->debug("WSDL debug data:\n".$this->wsdl->debug_str); // } // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. return $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']); } else { return $this->serializeEnvelope($payload,$this->responseHeaders); } } else { // debug $this->debug('ERROR: request not verified against method signature'); $this->result = 'fault: request failed validation against method signature'; // return fault $this->fault('Server',"Operation '$this->methodname' not defined in service."); return $this->fault->serialize(); } } } /** * takes the value that was created by parsing the request * and compares to the method's signature, if available. * * @param mixed * @return boolean * @access private */ function verify_method($operation,$request){ if(isset($this->wsdl) && is_object($this->wsdl)){ if($this->wsdl->getOperationData($operation)){ return true; } } elseif(isset($this->operations[$operation])){ return true; } return false; } /** * add a method to the dispatch map * * @param string $methodname * @param string $in array of input values * @param string $out array of output values * @access public */ function add_to_map($methodname,$in,$out){ $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out); } /** * register a service with the server * * @param string $methodname * @param string $in assoc array of input values: key = param name, value = param type * @param string $out assoc array of output values: key = param name, value = param type * @param string $namespace * @param string $soapaction * @param string $style (rpc|literal) * @access public */ function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false){ if($this->externalWSDLURL){ die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); } if(false == $in) { } if(false == $out) { } if(false == $namespace) { } if(false == $soapaction) { global $SERVER_NAME, $SCRIPT_NAME; $soapaction = "http://$SERVER_NAME$SCRIPT_NAME"; } if(false == $style) { $style = "rpc"; } if(false == $use) { $use = "encoded"; } $this->operations[$name] = array( 'name' => $name, 'in' => $in, 'out' => $out, 'namespace' => $namespace, 'soapaction' => $soapaction, 'style' => $style); if($this->wsdl){ $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use); } return true; } /** * create a fault. this also acts as a flag to the server that a fault has occured. * * @param string faultcode * @param string faultactor * @param string faultstring * @param string faultdetail * @access public */ function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){ $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail); } /** * prints html description of services * * @access private */ function webDescription(){ $b = ' NuSOAP: '.$this->wsdl->serviceName.'


'.$this->wsdl->serviceName.'
'; return $b; } /** * sets up wsdl object * this acts as a flag to enable internal WSDL generation * NOTE: NOT FUNCTIONAL * * @param string $serviceName, name of the service * @param string $namespace, tns namespace */ function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http') { $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME']; $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME']; if(false == $namespace) { $namespace = "http://$SERVER_NAME/soap/$serviceName"; } if(false == $endpoint) { $endpoint = "http://$SERVER_NAME$SCRIPT_NAME"; } $this->wsdl = new wsdl; $this->wsdl->serviceName = $serviceName; $this->wsdl->endpoint = $endpoint; $this->wsdl->namespaces['tns'] = $namespace; $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; $this->wsdl->bindings[$serviceName.'Binding'] = array( 'name'=>$serviceName.'Binding', 'style'=>$style, 'transport'=>$transport, 'portType'=>$serviceName.'PortType'); $this->wsdl->ports[$serviceName.'Port'] = array( 'binding'=>$serviceName.'Binding', 'location'=>$endpoint, 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/'); } } ?> * @version v 0.6.3 * @access public */ class wsdl extends XMLSchema { var $wsdl; // define internal arrays of bindings, ports, operations, messages, etc. var $message = array(); var $complexTypes = array(); var $messages = array(); var $currentMessage; var $currentOperation; var $portTypes = array(); var $currentPortType; var $bindings = array(); var $currentBinding; var $ports = array(); var $currentPort; var $opData = array(); var $status = ''; var $documentation = false; var $endpoint = ''; // array of wsdl docs to import var $import = array(); // parser vars var $parser; var $position = 0; var $depth = 0; var $depth_array = array(); var $usedNamespaces = array(); // for getting wsdl var $proxyhost = ''; var $proxyport = ''; /** * constructor * * @param string $wsdl WSDL document URL * @access public */ function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){ $this->wsdl = $wsdl; $this->proxyhost = $proxyhost; $this->proxyport = $proxyport; // parse wsdl file if ($wsdl != "") { $this->debug('initial wsdl file: ' . $wsdl); $this->parseWSDL($wsdl); } // imports if (sizeof($this->import) > 0) { foreach($this->import as $ns => $url) { $this->debug('importing wsdl from ' . $url); $this->parseWSDL($url); } } } /** * parses the wsdl document * * @param string $wsdl path or URL * @access private */ function parseWSDL($wsdl = '') { if ($wsdl == '') { $this->debug('no wsdl passed to parseWSDL()!!'); $this->setError('no wsdl passed to parseWSDL()!!'); return false; } $this->debug('getting ' . $wsdl); // parse $wsdl for url format $wsdl_props = parse_url($wsdl); if (isset($wsdl_props['host'])) { // get wsdl $tr = new soap_transport_http($wsdl); $tr->request_method = 'GET'; $tr->useSOAPAction = false; if($this->proxyhost && $this->proxyport){ $tr->setProxy($this->proxyhost,$this->proxyport); } if (isset($wsdl_props['user'])) { $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']); } $wsdl_string = $tr->send(''); // catch errors if($err = $tr->getError() ){ $this->debug('HTTP ERROR: '.$err); $this->setError('HTTP ERROR: '.$err); return false; } unset($tr); /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET $fsockopen_timeout = 30; // check if a port value is supplied in url if (isset($wsdl_props['port'])) { // yes $wsdl_url_port = $wsdl_props['port']; } else { // no, assign port number, based on url protocol (scheme) switch ($wsdl_props['scheme']) { case ('https') : case ('ssl') : case ('tls') : $wsdl_url_port = 443; break; case ('http') : default : $wsdl_url_port = 80; } } // FIXME: should implement SSL/TLS support here if CURL is available if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) { // perform HTTP GET for WSDL file // 10.9.02 - added poulter fix for doing this properly $sHeader = "GET " . $wsdl_props['path']; if (isset($wsdl_props['query'])) { $sHeader .= "?" . $wsdl_props['query']; } $sHeader .= " HTTP/1.0\r\n"; if (isset($wsdl_props['user'])) { $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']); $sHeader .= "Authorization: Basic $base64auth\r\n"; } $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n"; fputs($fp, $sHeader); while (fgets($fp, 1024) != "\r\n") { // do nothing, just read/skip past HTTP headers // FIXME: should actually detect HTTP response code, and act accordingly if error // HTTP headers end with extra CRLF before content body } // read in WSDL just like regular fopen() $wsdl_string = ''; while ($data = fread($fp, 32768)) { $wsdl_string .= $data; } fclose($fp); } else { $this->setError('bad path to WSDL file.'); return false; } */ } else { // $wsdl seems to be a non-url file path, do the regular fopen if ($fp = @fopen($wsdl, 'r')) { $wsdl_string = ''; while ($data = fread($fp, 32768)) { $wsdl_string .= $data; } fclose($fp); } else { $this->setError('bad path to WSDL file.'); return false; } } // end new code added // Create an XML parser. $this->parser = xml_parser_create(); // Set the options for parsing the XML data. // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); // Set the object for the parser. xml_set_object($this->parser, $this); // Set the element handlers for the parser. xml_set_element_handler($this->parser, 'start_element', 'end_element'); xml_set_character_data_handler($this->parser, 'character_data'); // Parse the XML file. if (!xml_parse($this->parser, $wsdl_string, true)) { // Display an error message. $errstr = sprintf( 'XML error on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)) ); $this->debug('XML parse error: ' . $errstr); $this->setError('Parser error: ' . $errstr); return false; } // free the parser xml_parser_free($this->parser); // catch wsdl parse errors if($this->getError()){ return false; } // add new data to operation data foreach($this->bindings as $binding => $bindingData) { if (isset($bindingData['operations']) && is_array($bindingData['operations'])) { foreach($bindingData['operations'] as $operation => $data) { $this->debug('post-parse data gathering for ' . $operation); $this->bindings[$binding]['operations'][$operation]['input'] = isset($this->bindings[$binding]['operations'][$operation]['input']) ? array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) : $this->portTypes[ $bindingData['portType'] ][$operation]['input']; $this->bindings[$binding]['operations'][$operation]['output'] = isset($this->bindings[$binding]['operations'][$operation]['output']) ? array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) : $this->portTypes[ $bindingData['portType'] ][$operation]['output']; if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){ $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ]; } if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){ $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ]; } if (isset($bindingData['style'])) { $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style']; } $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : ''; $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : ''; $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : ''; } } } return true; } /** * start-element handler * * @param string $parser XML parser object * @param string $name element name * @param string $attrs associative array of attributes * @access private */ function start_element($parser, $name, $attrs) { if ($this->status == 'schema' || ereg('schema$', $name)) { // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")"); $this->status = 'schema'; $this->schemaStartElement($parser, $name, $attrs); } else { // position in the total number of elements, starting from 0 $pos = $this->position++; $depth = $this->depth++; // set self as current value for this depth $this->depth_array[$depth] = $pos; $this->message[$pos] = array('cdata' => ''); // get element prefix if (ereg(':', $name)) { // get ns prefix $prefix = substr($name, 0, strpos($name, ':')); // get ns $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; // get unqualified name $name = substr(strstr($name, ':'), 1); } if (count($attrs) > 0) { foreach($attrs as $k => $v) { // if ns declarations, add to class level array of valid namespaces if (ereg("^xmlns", $k)) { if ($ns_prefix = substr(strrchr($k, ':'), 1)) { $this->namespaces[$ns_prefix] = $v; } else { $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v; } if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') { $this->XMLSchemaVersion = $v; $this->namespaces['xsi'] = $v . '-instance'; } } // // expand each attribute $k = strpos($k, ':') ? $this->expandQname($k) : $k; if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') { $v = strpos($v, ':') ? $this->expandQname($v) : $v; } $eAttrs[$k] = $v; } $attrs = $eAttrs; } else { $attrs = array(); } // find status, register data switch ($this->status) { case 'message': if ($name == 'part') { if (isset($attrs['type'])) { $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs)); $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type']; } if (isset($attrs['element'])) { $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element']; } } break; case 'portType': switch ($name) { case 'operation': $this->currentPortOperation = $attrs['name']; $this->debug("portType $this->currentPortType operation: $this->currentPortOperation"); if (isset($attrs['parameterOrder'])) { $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder']; } break; case 'documentation': $this->documentation = true; break; // merge input/output data default: $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : ''; $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m; break; } break; case 'binding': switch ($name) { case 'binding': // get ns prefix if (isset($attrs['style'])) { $this->bindings[$this->currentBinding]['prefix'] = $prefix; } $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs); break; case 'header': $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; break; case 'operation': if (isset($attrs['soapAction'])) { $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction']; } if (isset($attrs['style'])) { $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style']; } if (isset($attrs['name'])) { $this->currentOperation = $attrs['name']; $this->debug("current binding operation: $this->currentOperation"); $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name']; $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding; $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : ''; } break; case 'input': $this->opStatus = 'input'; break; case 'output': $this->opStatus = 'output'; break; case 'body': if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) { $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs); } else { $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs; } break; } break; case 'service': switch ($name) { case 'port': $this->currentPort = $attrs['name']; $this->debug('current port: ' . $this->currentPort); $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']); break; case 'address': $this->ports[$this->currentPort]['location'] = $attrs['location']; $this->ports[$this->currentPort]['bindingType'] = $namespace; $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace; $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location']; break; } break; } // set status switch ($name) { case "import": if (isset($attrs['location'])) { $this->import[$attrs['namespace']] = $attrs['location']; } break; case 'types': $this->status = 'schema'; break; case 'message': $this->status = 'message'; $this->messages[$attrs['name']] = array(); $this->currentMessage = $attrs['name']; break; case 'portType': $this->status = 'portType'; $this->portTypes[$attrs['name']] = array(); $this->currentPortType = $attrs['name']; break; case "binding": if (isset($attrs['name'])) { // get binding name if (strpos($attrs['name'], ':')) { $this->currentBinding = $this->getLocalPart($attrs['name']); } else { $this->currentBinding = $attrs['name']; } $this->status = 'binding'; $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']); $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']); } break; case 'service': $this->serviceName = $attrs['name']; $this->status = 'service'; $this->debug('current service: ' . $this->serviceName); break; case 'definitions': foreach ($attrs as $name => $value) { $this->wsdl_info[$name] = $value; } break; } } } /** * end-element handler * * @param string $parser XML parser object * @param string $name element name * @access private */ function end_element($parser, $name){ // unset schema status if (ereg('types$', $name) || ereg('schema$', $name)) { $this->status = ""; } if ($this->status == 'schema') { $this->schemaEndElement($parser, $name); } else { // bring depth down a notch $this->depth--; } // end documentation if ($this->documentation) { $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation; $this->documentation = false; } } /** * element content handler * * @param string $parser XML parser object * @param string $data element content * @access private */ function character_data($parser, $data) { $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0; if (isset($this->message[$pos]['cdata'])) { $this->message[$pos]['cdata'] .= $data; } if ($this->documentation) { $this->documentation .= $data; } } function getBindingData($binding) { if (is_array($this->bindings[$binding])) { return $this->bindings[$binding]; } } /** * returns an assoc array of operation names => operation data * NOTE: currently only supports multiple services of differing binding types * This method needs some work * * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported) * @return array * @access public */ function getOperations($bindingType = 'soap') { if ($bindingType == 'soap') { $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; } // loop thru ports foreach($this->ports as $port => $portData) { // binding type of port matches parameter if ($portData['bindingType'] == $bindingType) { // get binding return $this->bindings[ $portData['binding'] ]['operations']; } } return array(); } /** * returns an associative array of data necessary for calling an operation * * @param string $operation , name of operation * @param string $bindingType , type of binding eg: soap * @return array * @access public */ function getOperationData($operation, $bindingType = 'soap') { if ($bindingType == 'soap') { $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; } // loop thru ports foreach($this->ports as $port => $portData) { // binding type of port matches parameter if ($portData['bindingType'] == $bindingType) { // get binding //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) { if ($operation == $bOperation) { $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation]; return $opData; } } } } } /** * serialize the parsed wsdl * * @return string , serialization of WSDL * @access public */ function serialize() { $xml = 'namespaces as $k => $v) { $xml .= " xmlns:$k=\"$v\""; } // 10.9.02 - add poulter fix for wsdl and tns declarations if (isset($this->namespaces['wsdl'])) { $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\""; } if (isset($this->namespaces['tns'])) { $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\""; } $xml .= '>'; // imports if (sizeof($this->import) > 0) { foreach($this->import as $ns => $url) { $xml .= ''; } } // types if (count($this->complexTypes)>=1) { $xml .= ''; $xml .= $this->serializeSchema(); $xml .= ''; } // messages if (count($this->messages) >= 1) { foreach($this->messages as $msgName => $msgParts) { $xml .= ''; foreach($msgParts as $partName => $partType) { // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'
'; if (strpos($partType, ':')) { $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType)); } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) { // print 'checking typemap: '.$this->XMLSchemaVersion.'
'; $typePrefix = 'xsd'; } else { foreach($this->typemap as $ns => $types) { if (isset($types[$partType])) { $typePrefix = $this->getPrefixFromNamespace($ns); } } if (!isset($typePrefix)) { die("$partType has no namespace!"); } } $xml .= ''; } $xml .= '
'; } } // bindings & porttypes if (count($this->bindings) >= 1) { $binding_xml = ''; $portType_xml = ''; foreach($this->bindings as $bindingName => $attrs) { $binding_xml .= ''; $binding_xml .= ''; $portType_xml .= ''; foreach($attrs['operations'] as $opName => $opParts) { $binding_xml .= ''; $binding_xml .= ''; $binding_xml .= ''; $binding_xml .= ''; $binding_xml .= ''; $portType_xml .= ''; $portType_xml .= ''; $portType_xml .= ''; } $portType_xml .= ''; $binding_xml .= ''; } $xml .= $portType_xml . $binding_xml; } // services $xml .= ''; if (count($this->ports) >= 1) { foreach($this->ports as $pName => $attrs) { $xml .= ''; $xml .= ''; $xml .= ''; } } $xml .= ''; return $xml . ''; } /** * serialize a PHP value according to a WSDL message definition * * TODO * - multi-ref serialization * - validate PHP values against type definitions, return errors if invalid * * @param string $ type name * @param mixed $ param value * @return mixed new param or false if initial value didn't validate */ function serializeRPCParameters($operation, $direction, $parameters) { $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); if ($direction != 'input' && $direction != 'output') { $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); return false; } if (!$opData = $this->getOperationData($operation)) { $this->debug('Unable to retrieve WSDL data for operation: ' . $operation); $this->setError('Unable to retrieve WSDL data for operation: ' . $operation); return false; } $this->debug($this->varDump($opData)); // set input params $xml = ''; if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { $use = $opData[$direction]['use']; $this->debug("use=$use"); $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)'); foreach($opData[$direction]['parts'] as $name => $type) { $this->debug('serializing part "'.$name.'" of type "'.$type.'"'); // NOTE: add error handling here // if serializeType returns false, then catch global error and fault if (isset($parameters[$name])) { $this->debug('calling serializeType w/ named param'); $xml .= $this->serializeType($name, $type, $parameters[$name], $use); } elseif(is_array($parameters)) { $this->debug('calling serializeType w/ unnamed param'); $xml .= $this->serializeType($name, $type, array_shift($parameters), $use); } else { $this->debug('no parameters passed.'); } } } return $xml; } /** * serializes a PHP value according a given type definition * * @param string $name , name of type (part) * @param string $type , type of type, heh (type or element) * @param mixed $value , a native PHP value (parameter value) * @param string $use , use for part (encoded|literal) * @return string serialization * @access public */ function serializeType($name, $type, $value, $use='encoded') { $this->debug("in serializeType: $name, $type, $value, $use"); $xml = ''; if (strpos($type, ':')) { $uqType = substr($type, strrpos($type, ':') + 1); $ns = substr($type, 0, strrpos($type, ':')); $this->debug("got a prefixed type: $uqType, $ns"); if($ns == $this->XMLSchemaVersion || ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){ if ($uqType == 'boolean' && !$value) { $value = 0; } elseif ($uqType == 'boolean') { $value = 1; } if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') { $value = htmlspecialchars($value); } // it's a scalar if ($use == 'literal') { return "<$name>$value"; } else { return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value"; } } } else { $uqType = $type; } if(!$typeDef = $this->getTypeDef($uqType)){ $this->setError("$uqType is not a supported type."); return false; } else { //foreach($typeDef as $k => $v) { //$this->debug("typedef, $k: $v"); //} } $phpType = $typeDef['phpType']; $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); // if php type == struct, map value to the element names if ($phpType == 'struct') { if (isset($typeDef['element']) && $typeDef['element']) { $elementName = $uqType; // TODO: use elementFormDefault="qualified|unqualified" to determine // how to scope the namespace $elementNS = " xmlns=\"$ns\""; } else { $elementName = $name; $elementNS = ''; } if ($use == 'literal') { $xml = "<$elementName$elementNS>"; } else { $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">"; } if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) { //if (is_array($this->complexTypes[$uqType]['elements'])) { // toggle whether all elements are present - ideally should validate against schema if(count($this->complexTypes[$uqType]['elements']) != count($value)){ $optionals = true; } foreach($this->complexTypes[$uqType]['elements'] as $eName => $attrs) { // if user took advantage of a minOccurs=0, then only serialize named parameters if(isset($optionals) && !isset($value[$eName])){ // do nothing } else { // get value if (isset($value[$eName])) { $v = $value[$eName]; } elseif (is_array($value)) { $v = array_shift($value); } // serialize schema-defined type if (!isset($attrs['type'])) { $xml .= $this->serializeType($eName, $attrs['name'], $v, $use); // serialize generic type } else { $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use); $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use); } } } } $xml .= ""; } elseif ($phpType == 'array') { $rows = sizeof($value); if (isset($typeDef['multidimensional'])) { $nv = array(); foreach($value as $v) { $cols = ',' . sizeof($v); $nv = array_merge($nv, $v); } $value = $nv; } else { $cols = ''; } if (is_array($value) && sizeof($value) >= 1) { $contents = ''; foreach($value as $k => $v) { $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]"); //if (strpos($typeDef['arrayType'], ':') ) { if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) { $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use); } else { $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use); } } $this->debug('contents: '.$this->varDump($contents)); } else { $contents = null; } if ($use == 'literal') { $xml = "<$name>" .$contents .""; } else { $xml = "<$name xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '. $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .':arrayType="' .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">" .$contents .""; } } $this->debug('returning: '.$this->varDump($xml)); return $xml; } /** * register a service with the server * * @param string $methodname * @param string $in assoc array of input values: key = param name, value = param type * @param string $out assoc array of output values: key = param name, value = param type * @param string $namespace * @param string $soapaction * @param string $style (rpc|literal) * @access public */ function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '') { if ($style == 'rpc' && $use == 'encoded') { $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; } else { $encodingStyle = ''; } // get binding $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] = array( 'name' => $name, 'binding' => $this->serviceName . 'Binding', 'endpoint' => $this->endpoint, 'soapAction' => $soapaction, 'style' => $style, 'input' => array( 'use' => $use, 'namespace' => $namespace, 'encodingStyle' => $encodingStyle, 'message' => $name . 'Request', 'parts' => $in), 'output' => array( 'use' => $use, 'namespace' => $namespace, 'encodingStyle' => $encodingStyle, 'message' => $name . 'Response', 'parts' => $out), 'namespace' => $namespace, 'transport' => 'http://schemas.xmlsoap.org/soap/http', 'documentation' => $documentation); // add portTypes // add messages if($in) { foreach($in as $pName => $pType) { if(strpos($pType,':')) { $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); } $this->messages[$name.'Request'][$pName] = $pType; } } if($out) { foreach($out as $pName => $pType) { if(strpos($pType,':')) { $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); } $this->messages[$name.'Response'][$pName] = $pType; } } return true; } } ?> * @version v 0.6.3 * @access public */ class soap_parser extends nusoap_base { var $xml = ''; var $xml_encoding = ''; var $method = ''; var $root_struct = ''; var $root_struct_name = ''; var $root_header = ''; var $document = ''; // determines where in the message we are (envelope,header,body,method) var $status = ''; var $position = 0; var $depth = 0; var $default_namespace = ''; var $namespaces = array(); var $message = array(); var $parent = ''; var $fault = false; var $fault_code = ''; var $fault_str = ''; var $fault_detail = ''; var $depth_array = array(); var $debug_flag = true; var $soapresponse = NULL; var $responseHeaders = ''; var $body_position = 0; // for multiref parsing: // array of id => pos var $ids = array(); // array of id => hrefs => pos var $multirefs = array(); /** * constructor * * @param string $xml SOAP message * @param string $encoding character encoding scheme of message * @access public */ function soap_parser($xml,$encoding='UTF-8',$method=''){ $this->xml = $xml; $this->xml_encoding = $encoding; $this->method = $method; // Check whether content has been read. if(!empty($xml)){ $this->debug('Entering soap_parser()'); // Create an XML parser. $this->parser = xml_parser_create($this->xml_encoding); // Set the options for parsing the XML data. //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); // Set the object for the parser. xml_set_object($this->parser, $this); // Set the element handlers for the parser. xml_set_element_handler($this->parser, 'start_element','end_element'); xml_set_character_data_handler($this->parser,'character_data'); // Parse the XML file. if(!xml_parse($this->parser,$xml,true)){ // Display an error message. $err = sprintf('XML error on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser))); $this->debug('parse error: '.$err); $this->errstr = $err; } else { $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); // get final value $this->soapresponse = $this->message[$this->root_struct]['result']; // get header value if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){ $this->responseHeaders = $this->message[$this->root_header]['result']; } // resolve hrefs/ids if(sizeof($this->multirefs) > 0){ foreach($this->multirefs as $id => $hrefs){ $this->debug('resolving multirefs for id: '.$id); $idVal = $this->buildVal($this->ids[$id]); foreach($hrefs as $refPos => $ref){ $this->debug('resolving href at pos '.$refPos); $this->multirefs[$id][$refPos] = $idVal; } } } } xml_parser_free($this->parser); } else { $this->debug('xml was empty, didn\'t parse!'); $this->errstr = 'xml was empty, didn\'t parse!'; } } /** * start-element handler * * @param string $parser XML parser object * @param string $name element name * @param string $attrs associative array of attributes * @access private */ function start_element($parser, $name, $attrs) { // position in a total number of elements, starting from 0 // update class level pos $pos = $this->position++; // and set mine $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>''); // depth = how many levels removed from root? // set mine as current global depth and increment global depth value $this->message[$pos]['depth'] = $this->depth++; // else add self as child to whoever the current parent is if($pos != 0){ $this->message[$this->parent]['children'] .= '|'.$pos; } // set my parent $this->message[$pos]['parent'] = $this->parent; // set self as current parent $this->parent = $pos; // set self as current value for this depth $this->depth_array[$this->depth] = $pos; // get element prefix if(strpos($name,':')){ // get ns prefix $prefix = substr($name,0,strpos($name,':')); // get unqualified name $name = substr(strstr($name,':'),1); } // set status if($name == 'Envelope'){ $this->status = 'envelope'; } elseif($name == 'Header'){ $this->root_header = $pos; $this->status = 'header'; } elseif($name == 'Body'){ $this->status = 'body'; $this->body_position = $pos; // set method } elseif($this->status == 'body' && $pos == ($this->body_position+1)){ $this->status = 'method'; $this->root_struct_name = $name; $this->root_struct = $pos; $this->message[$pos]['type'] = 'struct'; $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); } // set my status $this->message[$pos]['status'] = $this->status; // set name $this->message[$pos]['name'] = htmlspecialchars($name); // set attrs $this->message[$pos]['attrs'] = $attrs; // loop through atts, logging ns and type declarations $attstr = ''; foreach($attrs as $key => $value){ $key_prefix = $this->getPrefix($key); $key_localpart = $this->getLocalPart($key); // if ns declarations, add to class level array of valid namespaces if($key_prefix == 'xmlns'){ if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){ $this->XMLSchemaVersion = $value; $this->namespaces['xsd'] = $this->XMLSchemaVersion; $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; } $this->namespaces[$key_localpart] = $value; // set method namespace if($name == $this->root_struct_name){ $this->methodNamespace = $value; } // if it's a type declaration, set type } elseif($key_localpart == 'type'){ $value_prefix = $this->getPrefix($value); $value_localpart = $this->getLocalPart($value); $this->message[$pos]['type'] = $value_localpart; $this->message[$pos]['typePrefix'] = $value_prefix; if(isset($this->namespaces[$value_prefix])){ $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; } else if(isset($attrs['xmlns:'.$value_prefix])) { $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; } // should do something here with the namespace of specified type? } elseif($key_localpart == 'arrayType'){ $this->message[$pos]['type'] = 'array'; /* do arrayType ereg here [1] arrayTypeValue ::= atype asize [2] atype ::= QName rank* [3] rank ::= '[' (',')* ']' [4] asize ::= '[' length~ ']' [5] length ::= nextDimension* Digit+ [6] nextDimension ::= Digit+ ',' */ $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]'; if(ereg($expr,$value,$regs)){ $this->message[$pos]['typePrefix'] = $regs[1]; $this->message[$pos]['arraySize'] = $regs[3]; $this->message[$pos]['arrayCols'] = $regs[4]; } } // log id if($key == 'id'){ $this->ids[$value] = $pos; } // root if($key_localpart == 'root' && $value == 1){ $this->status = 'method'; $this->root_struct_name = $name; $this->root_struct = $pos; $this->debug("found root struct $this->root_struct_name, pos $pos"); } // for doclit $attstr .= " $key=\"$value\""; } // get namespace - must be done after namespace atts are processed if(isset($prefix)){ $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; $this->default_namespace = $this->namespaces[$prefix]; } else { $this->message[$pos]['namespace'] = $this->default_namespace; } if($this->status == 'header'){ $this->responseHeaders .= "<$name$attstr>"; } elseif($this->root_struct_name != ''){ $this->document .= "<$name$attstr>"; } } /** * end-element handler * * @param string $parser XML parser object * @param string $name element name * @access private */ function end_element($parser, $name) { // position of current element is equal to the last value left in depth_array for my depth $pos = $this->depth_array[$this->depth--]; // get element prefix if(strpos($name,':')){ // get ns prefix $prefix = substr($name,0,strpos($name,':')); // get unqualified name $name = substr(strstr($name,':'),1); } // build to native type if(isset($this->body_position) && $pos > $this->body_position){ // deal w/ multirefs if(isset($this->message[$pos]['attrs']['href'])){ // get id $id = substr($this->message[$pos]['attrs']['href'],1); // add placeholder to href array $this->multirefs[$id][$pos] = "placeholder"; // add set a reference to it as the result value $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; // build complex values } elseif($this->message[$pos]['children'] != ""){ $this->message[$pos]['result'] = $this->buildVal($pos); } else { $this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); if(is_numeric($this->message[$pos]['cdata']) ){ if( strpos($this->message[$pos]['cdata'],'.') ){ $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']); } else { $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']); } } else { $this->message[$pos]['result'] = $this->message[$pos]['cdata']; } } } // switch status if($pos == $this->root_struct){ $this->status = 'body'; } elseif($name == 'Body'){ $this->status = 'header'; } elseif($name == 'Header'){ $this->status = 'envelope'; } elseif($name == 'Envelope'){ // } // set parent back to my parent $this->parent = $this->message[$pos]['parent']; // for doclit if($this->status == 'header'){ $this->responseHeaders .= ""; } elseif($pos >= $this->root_struct){ $this->document .= ""; } } /** * element content handler * * @param string $parser XML parser object * @param string $data element content * @access private */ function character_data($parser, $data){ $pos = $this->depth_array[$this->depth]; if ($this->xml_encoding=='UTF-8'){ $data = utf8_decode($data); } $this->message[$pos]['cdata'] .= $data; // for doclit if($this->status == 'header'){ $this->responseHeaders .= $data; } else { $this->document .= $data; } } /** * get the parsed message * * @return mixed * @access public */ function get_response(){ return $this->soapresponse; } /** * get the parsed headers * * @return string XML or empty if no headers * @access public */ function getHeaders(){ return $this->responseHeaders; } /** * decodes entities * * @param string $text string to translate * @access private */ function decode_entities($text){ foreach($this->entities as $entity => $encoded){ $text = str_replace($encoded,$entity,$text); } return $text; } /** * builds response structures for compound values (arrays/structs) * * @param string $pos position in node tree * @access private */ function buildVal($pos){ if(!isset($this->message[$pos]['type'])){ $this->message[$pos]['type'] = ''; } $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); // if there are children... if($this->message[$pos]['children'] != ''){ $children = explode('|',$this->message[$pos]['children']); array_shift($children); // knock off empty // md array if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){ $r=0; // rowcount $c=0; // colcount foreach($children as $child_pos){ $this->debug("got an MD array element: $r, $c"); $params[$r][] = $this->message[$child_pos]['result']; $c++; if($c == $this->message[$pos]['arrayCols']){ $c = 0; $r++; } } // array } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){ $this->debug('adding array '.$this->message[$pos]['name']); foreach($children as $child_pos){ $params[] = &$this->message[$child_pos]['result']; } // apache Map type: java hashtable } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){ foreach($children as $child_pos){ $kv = explode("|",$this->message[$child_pos]['children']); $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; } // generic compound type //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { } else { // is array or struct? better way to do this probably foreach($children as $child_pos){ if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){ $struct = 1; break; } $keys[$this->message[$child_pos]['name']] = 1; } // foreach($children as $child_pos){ if(isset($struct)){ $params[] = &$this->message[$child_pos]['result']; } else { $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; } } } return is_array($params) ? $params : array(); } else { $this->debug('no children'); if(strpos($this->message[$pos]['cdata'],'&')){ return strtr($this->message[$pos]['cdata'],array_flip($this->entities)); } else { return $this->message[$pos]['cdata']; } } } } ?>call( string methodname [ ,array parameters] ); * * // bye bye client * unset($soapclient); * * @author Dietrich Ayala * @version v 0.6.3 * @access public */ class soapclient extends nusoap_base { var $username = ''; var $password = ''; var $requestHeaders = false; var $responseHeaders; var $endpoint; var $error_str = false; var $proxyhost = ''; var $proxyport = ''; var $xml_encoding = ''; var $http_encoding = false; var $timeout = 0; var $endpointType = ''; var $persistentConnection = false; var $defaultRpcParams = false; /** * fault related variables * * @var fault * @var faultcode * @var faultstring * @var faultdetail * @access public */ var $fault, $faultcode, $faultstring, $faultdetail; /** * constructor * * @param string $endpoint SOAP server or WSDL URL * @param bool $wsdl optional, set to true if using WSDL * @param int $portName optional portName in WSDL document * @access public */ function soapclient($endpoint,$wsdl = false){ $this->endpoint = $endpoint; // make values if($wsdl){ $this->endpointType = 'wsdl'; $this->wsdlFile = $this->endpoint; // instantiate wsdl object and parse wsdl file $this->debug('instantiating wsdl class with doc: '.$endpoint); $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport); $this->debug("wsdl debug: \n".$this->wsdl->debug_str); $this->wsdl->debug_str = ''; // catch errors if($errstr = $this->wsdl->getError()){ $this->debug('got wsdl error: '.$errstr); $this->setError('wsdl error: '.$errstr); } elseif($this->operations = $this->wsdl->getOperations()){ $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile); } else { $this->debug( 'getOperations returned false'); $this->setError('no operations defined in the WSDL document!'); } } } /** * calls method, returns PHP native type * * @param string $method SOAP server URL or path * @param array $params array of parameters, can be associative or not * @param string $namespace optional method namespace * @param string $soapAction optional SOAPAction value * @param boolean $headers optional array of soapval objects for headers * @param boolean $rpcParams optional treat params as RPC for use="literal" * This can be used on a per-call basis to overrider defaultRpcParams. * @return mixed * @access public */ function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null){ $this->operation = $operation; $this->fault = false; $this->error_str = ''; $this->request = ''; $this->response = ''; $this->faultstring = ''; $this->faultcode = ''; $this->opData = array(); $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams"); $this->debug("endpointType: $this->endpointType"); // if wsdl, get operation data and process parameters if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){ $this->opData = $opData; foreach($opData as $key => $value){ $this->debug("$key -> $value"); } $soapAction = $opData['soapAction']; $this->endpoint = $opData['endpoint']; $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org'; $style = $opData['style']; // add ns to ns array if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){ $this->wsdl->namespaces['nu'] = $namespace; } // serialize payload if($opData['input']['use'] == 'literal') { if (is_null($rpcParams)) { $rpcParams = $this->defaultRpcParams; } if ($rpcParams) { $this->debug("serializing literal params for operation $operation"); $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params); $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace']; } else { $this->debug("serializing literal document for operation $operation"); $payload = is_array($params) ? array_shift($params) : $params; } } else { $this->debug("serializing encoded params for operation $operation"); $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation>". $this->wsdl->serializeRPCParameters($operation,'input',$params). 'wsdl->getPrefixFromNamespace($namespace).":$operation>"; } $this->debug('payload size: '.strlen($payload)); // serialize envelope $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style); $this->debug("wsdl debug: \n".$this->wsdl->debug_str); $this->wsdl->debug_str = ''; } elseif($this->endpointType == 'wsdl') { $this->setError( 'operation '.$operation.' not present.'); $this->debug("operation '$operation' not present."); $this->debug("wsdl debug: \n".$this->wsdl->debug_str); return false; // no wsdl } else { // make message if(!isset($style)){ $style = 'rpc'; } if($namespace == ''){ $namespace = 'http://testuri.org'; $this->wsdl->namespaces['ns1'] = $namespace; } // serialize envelope $payload = ''; foreach($params as $k => $v){ $payload .= $this->serialize_val($v,$k); } $payload = "\n".$payload."\n"; $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders); } $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace"); // send $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'..."); $return = $this->send($soapmsg,$soapAction,$this->timeout); if($errstr = $this->getError()){ $this->debug('Error: '.$errstr); return false; } else { $this->return = $return; $this->debug('sent message successfully and got a(n) '.gettype($return).' back'); // fault? if(is_array($return) && isset($return['faultcode'])){ $this->debug('got fault'); $this->setError($return['faultcode'].': '.$return['faultstring']); $this->fault = true; foreach($return as $k => $v){ $this->$k = $v; $this->debug("$k = $v
"); } return $return; } else { // array of return values if(is_array($return)){ // multiple 'out' parameters if(sizeof($return) > 1){ return $return; } // single 'out' parameter return array_shift($return); // nothing returned (ie, echoVoid) } else { return ""; } } } } /** * get available data pertaining to an operation * * @param string $operation operation name * @return array array of data pertaining to the operation * @access public */ function getOperationData($operation){ if(isset($this->operations[$operation])){ return $this->operations[$operation]; } $this->debug("No data for operation: $operation"); } /** * send the SOAP message * * Note: if the operation has multiple return values * the return value of this method will be an array * of those values. * * @param string $msg a SOAPx4 soapmsg object * @param string $soapaction SOAPAction value * @param integer $timeout set timeout in seconds * @return mixed native PHP types. * @access private */ function send($msg, $soapaction = '', $timeout=0) { // detect transport switch(true){ // http(s) case ereg('^http',$this->endpoint): $this->debug('transporting via HTTP'); if($this->persistentConnection && is_object($this->persistentConnection)){ $http =& $this->persistentConnection; } else { $http = new soap_transport_http($this->endpoint); // pass encoding into transport layer, so appropriate http headers are sent $http->soap_defencoding = $this->soap_defencoding; } $http->setSOAPAction($soapaction); if($this->proxyhost && $this->proxyport){ $http->setProxy($this->proxyhost,$this->proxyport); } if($this->username != '' && $this->password != '') { $http->setCredentials($this->username,$this->password); } if($this->http_encoding != ''){ $http->setEncoding($this->http_encoding); } $this->debug('sending message, length: '.strlen($msg)); if(ereg('^http:',$this->endpoint)){ //if(strpos($this->endpoint,'http:')){ $response = $http->send($msg,$timeout); } elseif(ereg('^https',$this->endpoint)){ //} elseif(strpos($this->endpoint,'https:')){ //if(phpversion() == '4.3.0-dev'){ //$response = $http->send($msg,$timeout); //$this->request = $http->outgoing_payload; //$this->response = $http->incoming_payload; //} else if (extension_loaded('curl')) { $response = $http->sendHTTPS($msg,$timeout); } else { $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); } } else { $this->setError('no http/s in endpoint url'); } $this->request = $http->outgoing_payload; $this->response = $http->incoming_payload; $this->debug("transport debug data...\n".$http->debug_str); // save transport object if using persistent connections if($this->persistentConnection && !is_object($this->persistentConnection)){ $this->persistentConnection = $http; } if($err = $http->getError()){ $this->setError('HTTP Error: '.$err); return false; } elseif($this->getError()){ return false; } else { $this->debug('got response, length: '.strlen($response)); return $this->parseResponse($response); } break; default: $this->setError('no transport found, or selected transport is not yet supported!'); return false; break; } } /** * processes SOAP message returned from server * * @param string unprocessed response data from server * @return mixed value of the message, decoded into a PHP type * @access private */ function parseResponse($data) { $this->debug('Entering parseResponse(), about to create soap_parser instance'); $parser = new soap_parser($data,$this->xml_encoding,$this->operation); // if parse errors if($errstr = $parser->getError()){ $this->setError( $errstr); // destroy the parser object unset($parser); return false; } else { // get SOAP headers $this->responseHeaders = $parser->getHeaders(); // get decoded message $return = $parser->get_response(); // add parser debug data to our debug $this->debug($parser->debug_str); // add document for doclit support $this->document = $parser->document; // destroy the parser object unset($parser); // return decode message return $return; } } /** * set the SOAP headers * * @param $headers string XML * @access public */ function setHeaders($headers){ $this->requestHeaders = $headers; } /** * get the response headers * * @return mixed object SOAPx4 soapval object or empty if no headers * @access public */ function getHeaders(){ if($this->responseHeaders != '') { return $this->responseHeaders; } } /** * set proxy info here * * @param string $proxyhost * @param string $proxyport * @access public */ function setHTTPProxy($proxyhost, $proxyport) { $this->proxyhost = $proxyhost; $this->proxyport = $proxyport; } /** * if authenticating, set user credentials here * * @param string $username * @param string $password * @access public */ function setCredentials($username, $password) { $this->username = $username; $this->password = $password; } /** * use HTTP encoding * * @param string $enc * @access public */ function setHTTPEncoding($enc='gzip, deflate'){ $this->http_encoding = $enc; } /** * use HTTP persistent connections if possible * * @access public */ function useHTTPPersistentConnection(){ $this->persistentConnection = true; } /** * gets the default RPC parameter setting. * If true, default is that call params are like RPC even for document style. * Each call() can override this value. * * @access public */ function getDefaultRpcParams() { return $this->defaultRpcParams; } /** * sets the default RPC parameter setting. * If true, default is that call params are like RPC even for document style * Each call() can override this value. * * @param boolean $rpcParams * @access public */ function setDefaultRpcParams($rpcParams) { $this->defaultRpcParams = $rpcParams; } /** * dynamically creates proxy class, allowing user to directly call methods from wsdl * * @return object soap_proxy object * @access public */ function getProxy(){ $evalStr = ''; foreach($this->operations as $operation => $opData){ if($operation != ''){ // create param string $paramStr = ''; if(sizeof($opData['input']['parts']) > 0){ foreach($opData['input']['parts'] as $name => $type){ $paramStr .= "\$$name,"; } $paramStr = substr($paramStr,0,strlen($paramStr)-1); } $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace']; $evalStr .= "function $operation ($paramStr){ // load params into array \$params = array($paramStr); return \$this->call('$operation',\$params,'".$opData['namespace']."','".$opData['soapAction']."'); }"; unset($paramStr); } } $r = rand(); $evalStr = 'class soap_proxy_'.$r.' extends soapclient { '.$evalStr.' }'; //print "proxy class:
$evalStr
"; // eval the class eval($evalStr); // instantiate proxy object eval("\$proxy = new soap_proxy_$r('');"); // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice $proxy->endpointType = 'wsdl'; $proxy->wsdlFile = $this->wsdlFile; $proxy->wsdl = $this->wsdl; $proxy->operations = $this->operations; $proxy->defaultRpcParams = $this->defaultRpcParams; return $proxy; } } ?>phpgacl-3.3.7/soap/server.php0100644025754300001440000000165507722517550015053 0ustar ipsousersregister('acl_check'); $s->register('test'); function acl_check($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value=NULL, $axo_value=NULL, $root_aro_group_id=NULL, $root_axo_group_id=NULL) { global $gacl; return $gacl->acl_check($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value, $root_aro_group_id, $root_axo_group_id); } function test($text) { return $text; } $s->service($HTTP_RAW_POST_DATA); ?> phpgacl-3.3.7/setup.php0100644025754300001440000001515010476661234013735 0ustar ipsousers_db_table_prefix; $db_type = $gacl->_db_type; $db_name = $gacl->_db_name; $db_host = $gacl->_db_host; $db_user = $gacl->_db_user; $db_password = $gacl->_db_password; $db_name = $gacl->_db_name; $failed = 0; echo '

phpGACL Database Setup

Configuration:
driver = '.$db_type.',
host = '.$db_host.',
user = '.$db_user.',
database = '.$db_name.',
table prefix = '.$db_table_prefix.'

'; function echo_success($text) { echo 'Success! '.$text."
\n"; } function echo_failed($text) { global $failed; echo 'Failed! '.$text."
\n"; $failed++; } function echo_normal($text) { echo $text."
\n"; } /* * Test database connection */ echo '

Testing database connection...

'."\n"; if (is_resource($db->_connectionID)) { echo_success('Connected to "'.$db_type.'" database on "'.$db_host.'".'); } else { echo_failed('ERROR connecting to database,
are you sure you specified the proper host, user name, password, and database in admin/gacl_admin.inc.php?
Did you create the database, and give read/write permissions to "'.$db_user.'" already?'); exit; } /* * Do database specific stuff. */ echo '

Testing database type...

'."\n"; switch ( $db_type ) { case ($db_type == "mysql" OR $db_type == "mysqlt" OR $db_type == "maxsql" ): echo_success("Compatible database type \"$db_type\" detected!"); echo_normal("Making sure database \"$db_name\" exists..."); $databases = $db->GetCol("show databases"); if (in_array($db_name, $databases) ) { echo_success("Good, database \"$db_name\" already exists!"); } else { echo_normal("Database \"$db_name\" does not exist!"); echo_normal("Lets try to create it..."); if (!$db->Execute("create database $db_name") ) { echo_failed("Database \"$db_name\" could not be created, please do so manually."); } else { echo_success("Good, database \"$db_name\" has been created!!"); //Reconnect. Hrmm, this is kinda weird. $db->Connect($db_host, $db_user, $db_password, $db_name); } } break; case ( $db_type == "postgres8" OR $db_type == "postgres7" ): echo_success("Compatible database type \"$db_type\" detected!"); echo_normal("Making sure database \"$db_name\" exists..."); $databases = $db->GetCol("select datname from pg_database"); if (in_array($db_name, $databases) ) { echo_success("Good, database \"$db_name\" already exists!"); } else { echo_normal("Database \"$db_name\" does not exist!"); echo_normal("Lets try to create it..."); if (!$db->Execute("create database $db_name") ) { echo_failed("Database \"$db_name\" could not be created, please do so manually."); } else { echo_success("Good, database \"$db_name\" has been created!!"); //Reconnect. Hrmm, this is kinda weird. $db->Connect($db_host, $db_user, $db_password, $db_name); } } break; case "oci8-po": echo_success("Compatible database type \"$db_type\" detected!"); echo_normal("Making sure database \"$db_name\" exists..."); $databases = $db->GetCol("select '$db_name' from dual"); if (in_array($db_name, $databases) ) { echo_success("Good, database \"$db_name\" already exists!"); } else { echo_normal("Database \"$db_name\" does not exist!"); echo_normal("Lets try to create it..."); if (!$db->Execute("create database $db_name") ) { echo_failed("Database \"$db_name\" could not be created, please do so manually."); } else { echo_success("Good, database \"$db_name\" has been created!!"); //Reconnect. Hrmm, this is kinda weird. $db->Connect($db_host, $db_user, $db_password, $db_name); } } break; case "mssql": echo_success("Compatible database type \"$db_type\" detected!"); echo_normal("Making sure database \"$db_name\" exists..."); $databases = $db->GetCol("select CATALOG_NAME from INFORMATION_SCHEMA.SCHEMATA"); if (in_array($db_name, $databases) ) { echo_success("Good, database \"$db_name\" already exists!"); } else { echo_normal("Database \"$db_name\" does not exist!"); echo_normal("Lets try to create it..."); if (!$db->Execute("create database $db_name") ) { echo_failed("Database \"$db_name\" could not be created, please do so manually."); } else { echo_success("Good, database \"$db_name\" has been created!!"); //Reconnect. Hrmm, this is kinda weird. $db->Connect($db_host, $db_user, $db_password, $db_name); } } break; default: echo_normal("Sorry, setup.php currently does not fully support \"$db_type\" databases.
I'm assuming you've already created the database \"$db_name\", attempting to create tables.
Please email $author_email code to detect if a database is created or not so full support for \"$db_type\" can be added."); } /* * Attempt to create tables */ // Create the schema object and build the query array. $schema = new adoSchema($db); $schema->SetPrefix($db_table_prefix, FALSE); //set $underscore == FALSE // Build the SQL array $schema->ParseSchema('schema.xml'); // maybe display this if $gacl->debug is true? if ($gacl->_debug) { print "Here's the SQL to do the build:
\n"; print $schema->getSQL('html'); print "\n"; // exit; } // Execute the SQL on the database #ADODB's xmlschema is being lame, continue on error. $schema->ContinueOnError(TRUE); $result = $schema->ExecuteSchema(); if ($result != 2) { echo_failed('Failed creating tables. Please enable DEBUG mode (set it to TRUE in $gacl_options near top of admin/gacl_admin.inc.php) to see the error and try again. You will most likely need to delete any tables already created.'); } if ( $failed <= 0 ) { echo_success(' Installation Successful!!!
*IMPORTANT*

Please make sure you create the <phpGACL root>/admin/templates_c directory, and give it write permissions for the user your web server runs as.

Please read the manual, and docs/examples/* to familiarize yourself with phpGACL.

Let\'s get started!
'); } else { echo_failed('Please fix the above errors and try again.'); } ?> phpgacl-3.3.7/AUTHORS0100644025754300001440000000033707721247374013141 0ustar ipsousersMike Benoit - Initial idea of completely configurable ACLs. Though I haven't seen anything similar available, I'm sure its out there. - Initial implementation - Initial Documentation phpgacl-3.3.7/admin/0040755025754300001440000000000010476665053013160 5ustar ipsousersphpgacl-3.3.7/admin/smarty/0040755025754300001440000000000010476665052014476 5ustar ipsousersphpgacl-3.3.7/admin/smarty/libs/0040755025754300001440000000000010476665052015427 5ustar ipsousersphpgacl-3.3.7/admin/smarty/libs/internals/0040755025754300001440000000000010476665052017426 5ustar ipsousersphpgacl-3.3.7/admin/smarty/libs/internals/core.assemble_plugin_filepath.php0100644025754300001440000000363510476656640026122 0ustar ipsousersplugins_dir as $_plugin_dir) { $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename; // see if path is relative if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $_plugin_dir)) { $_relative_paths[] = $_plugin_dir; // relative path, see if it is in the SMARTY_DIR if (@is_readable(SMARTY_DIR . $_plugin_filepath)) { $_return = SMARTY_DIR . $_plugin_filepath; break; } } // try relative to cwd (or absolute) if (@is_readable($_plugin_filepath)) { $_return = $_plugin_filepath; break; } } if($_return === false) { // still not found, try PHP include_path if(isset($_relative_paths)) { foreach ((array)$_relative_paths as $_plugin_dir) { $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename; $_params = array('file_path' => $_plugin_filepath); require_once(SMARTY_CORE_DIR . 'core.get_include_path.php'); if(smarty_core_get_include_path($_params, $smarty)) { $_return = $_params['new_file_path']; break; } } } } $_filepaths_cache[$_plugin_filename] = $_return; return $_return; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.assign_smarty_interface.php0100644025754300001440000000235210476656640025773 0ustar ipsousers * Name: assign_smarty_interface
* Purpose: assign the $smarty interface variable * @param array Format: null * @param Smarty */ function smarty_core_assign_smarty_interface($params, &$smarty) { if (isset($smarty->_smarty_vars) && isset($smarty->_smarty_vars['request'])) { return; } $_globals_map = array('g' => 'HTTP_GET_VARS', 'p' => 'HTTP_POST_VARS', 'c' => 'HTTP_COOKIE_VARS', 's' => 'HTTP_SERVER_VARS', 'e' => 'HTTP_ENV_VARS'); $_smarty_vars_request = array(); foreach (preg_split('!!', strtolower($smarty->request_vars_order)) as $_c) { if (isset($_globals_map[$_c])) { $_smarty_vars_request = array_merge($_smarty_vars_request, $GLOBALS[$_globals_map[$_c]]); } } $_smarty_vars_request = @array_merge($_smarty_vars_request, $GLOBALS['HTTP_SESSION_VARS']); $smarty->_smarty_vars['request'] = $_smarty_vars_request; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.load_resource_plugin.php0100644025754300001440000000414310476656640025274 0ustar ipsousers_plugins['resource'][$params['type']]; if (isset($_plugin)) { if (!$_plugin[1] && count($_plugin[0])) { $_plugin[1] = true; foreach ($_plugin[0] as $_plugin_func) { if (!is_callable($_plugin_func)) { $_plugin[1] = false; break; } } } if (!$_plugin[1]) { $smarty->_trigger_fatal_error("[plugin] resource '" . $params['type'] . "' is not implemented", null, null, __FILE__, __LINE__); } return; } $_plugin_file = $smarty->_get_plugin_filepath('resource', $params['type']); $_found = ($_plugin_file != false); if ($_found) { /* * If the plugin file is found, it -must- provide the properly named * plugin functions. */ include_once($_plugin_file); /* * Locate functions that we require the plugin to provide. */ $_resource_ops = array('source', 'timestamp', 'secure', 'trusted'); $_resource_funcs = array(); foreach ($_resource_ops as $_op) { $_plugin_func = 'smarty_resource_' . $params['type'] . '_' . $_op; if (!function_exists($_plugin_func)) { $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", null, null, __FILE__, __LINE__); return; } else { $_resource_funcs[] = $_plugin_func; } } $smarty->_plugins['resource'][$params['type']] = array($_resource_funcs, true); } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.rm_auto.php0100644025754300001440000000435610476656640022544 0ustar ipsousers $params['auto_base'], 'level' => 0, 'exp_time' => $params['exp_time'] ); require_once(SMARTY_CORE_DIR . 'core.rmdir.php'); $_res = smarty_core_rmdir($_params, $smarty); } else { $_tname = $smarty->_get_auto_filename($params['auto_base'], $params['auto_source'], $params['auto_id']); if(isset($params['auto_source'])) { if (isset($params['extensions'])) { $_res = false; foreach ((array)$params['extensions'] as $_extension) $_res |= $smarty->_unlink($_tname.$_extension, $params['exp_time']); } else { $_res = $smarty->_unlink($_tname, $params['exp_time']); } } elseif ($smarty->use_sub_dirs) { $_params = array( 'dirname' => $_tname, 'level' => 1, 'exp_time' => $params['exp_time'] ); require_once(SMARTY_CORE_DIR . 'core.rmdir.php'); $_res = smarty_core_rmdir($_params, $smarty); } else { // remove matching file names $_handle = opendir($params['auto_base']); $_res = true; while (false !== ($_filename = readdir($_handle))) { if($_filename == '.' || $_filename == '..') { continue; } elseif (substr($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, 0, strlen($_tname)) == $_tname) { $_res &= (bool)$smarty->_unlink($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, $params['exp_time']); } } } } return $_res; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.write_compiled_include.php0100644025754300001440000000606210476656640025603 0ustar ipsouserscaching && \!\$this->_cache_including\) \{ echo \'\{nocache\:('.$params['cache_serial'].')#(\d+)\}\'; \};'; $_tag_end = 'if \(\$this->caching && \!\$this->_cache_including\) \{ echo \'\{/nocache\:(\\2)#(\\3)\}\'; \};'; preg_match_all('!('.$_tag_start.'(.*)'.$_tag_end.')!Us', $params['compiled_content'], $_match_source, PREG_SET_ORDER); // no nocache-parts found: done if (count($_match_source)==0) return; // convert the matched php-code to functions $_include_compiled = "_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; $_include_compiled .= " compiled from " . strtr(urlencode($params['resource_name']), array('%2F'=>'/', '%3A'=>':')) . " */\n\n"; $_compile_path = $params['include_file_path']; $smarty->_cache_serials[$_compile_path] = $params['cache_serial']; $_include_compiled .= "\$this->_cache_serials['".$_compile_path."'] = '".$params['cache_serial']."';\n\n?>"; $_include_compiled .= $params['plugins_code']; $_include_compiled .= "= 5.0) ? '_smarty' : 'this'; for ($_i = 0, $_for_max = count($_match_source); $_i < $_for_max; $_i++) { $_match =& $_match_source[$_i]; $source = $_match[4]; if ($this_varname == '_smarty') { /* rename $this to $_smarty in the sourcecode */ $tokens = token_get_all('\n"; $_params = array('filename' => $_compile_path, 'contents' => $_include_compiled, 'create_dirs' => true); require_once(SMARTY_CORE_DIR . 'core.write_file.php'); smarty_core_write_file($_params, $smarty); return true; } ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.process_cached_inserts.php0100644025754300001440000000464010476656640025606 0ustar ipsousers_smarty_md5.'{insert_cache (.*)}'.$smarty->_smarty_md5.'!Uis', $params['results'], $match); list($cached_inserts, $insert_args) = $match; for ($i = 0, $for_max = count($cached_inserts); $i < $for_max; $i++) { if ($smarty->debugging) { $_params = array(); require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); $debug_start_time = smarty_core_get_microtime($_params, $smarty); } $args = unserialize($insert_args[$i]); $name = $args['name']; if (isset($args['script'])) { $_params = array('resource_name' => $smarty->_dequote($args['script'])); require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); if(!smarty_core_get_php_resource($_params, $smarty)) { return false; } $resource_type = $_params['resource_type']; $php_resource = $_params['php_resource']; if ($resource_type == 'file') { $smarty->_include($php_resource, true); } else { $smarty->_eval($php_resource); } } $function_name = $smarty->_plugins['insert'][$name][0]; if (empty($args['assign'])) { $replace = $function_name($args, $smarty); } else { $smarty->assign($args['assign'], $function_name($args, $smarty)); $replace = ''; } $params['results'] = substr_replace($params['results'], $replace, strpos($params['results'], $cached_inserts[$i]), strlen($cached_inserts[$i])); if ($smarty->debugging) { $_params = array(); require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); $smarty->_smarty_debug_info[] = array('type' => 'insert', 'filename' => 'insert_'.$name, 'depth' => $smarty->_inclusion_depth, 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $debug_start_time); } } return $params['results']; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.write_cache_file.php0100644025754300001440000000704410476656640024347 0ustar ipsousers_cache_info['timestamp'] = time(); if ($smarty->cache_lifetime > -1){ // expiration set $smarty->_cache_info['expires'] = $smarty->_cache_info['timestamp'] + $smarty->cache_lifetime; } else { // cache will never expire $smarty->_cache_info['expires'] = -1; } // collapse nocache.../nocache-tags if (preg_match_all('!\{(/?)nocache\:[0-9a-f]{32}#\d+\}!', $params['results'], $match, PREG_PATTERN_ORDER)) { // remove everything between every pair of outermost noache.../nocache-tags // and replace it by a single nocache-tag // this new nocache-tag will be replaced by dynamic contents in // smarty_core_process_compiled_includes() on a cache-read $match_count = count($match[0]); $results = preg_split('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!', $params['results'], -1, PREG_SPLIT_DELIM_CAPTURE); $level = 0; $j = 0; for ($i=0, $results_count = count($results); $i < $results_count && $j < $match_count; $i++) { if ($results[$i] == $match[0][$j]) { // nocache tag if ($match[1][$j]) { // closing tag $level--; unset($results[$i]); } else { // opening tag if ($level++ > 0) unset($results[$i]); } $j++; } elseif ($level > 0) { unset($results[$i]); } } $params['results'] = implode('', $results); } $smarty->_cache_info['cache_serials'] = $smarty->_cache_serials; // prepend the cache header info into cache file $_cache_info = serialize($smarty->_cache_info); $params['results'] = strlen($_cache_info) . "\n" . $_cache_info . $params['results']; if (!empty($smarty->cache_handler_func)) { // use cache_handler function call_user_func_array($smarty->cache_handler_func, array('write', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null)); } else { // use local cache file if(!@is_writable($smarty->cache_dir)) { // cache_dir not writable, see if it exists if(!@is_dir($smarty->cache_dir)) { $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR); return false; } $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR); return false; } $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']); $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id); $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true); require_once(SMARTY_CORE_DIR . 'core.write_file.php'); smarty_core_write_file($_params, $smarty); return true; } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.rmdir.php0100644025754300001440000000264410476656640022211 0ustar ipsousers keep root) * WARNING: no tests, it will try to remove what you tell it! * * @param string $dirname * @param integer $level * @param integer $exp_time * @return boolean */ // $dirname, $level = 1, $exp_time = null function smarty_core_rmdir($params, &$smarty) { if(!isset($params['level'])) { $params['level'] = 1; } if(!isset($params['exp_time'])) { $params['exp_time'] = null; } if($_handle = @opendir($params['dirname'])) { while (false !== ($_entry = readdir($_handle))) { if ($_entry != '.' && $_entry != '..') { if (@is_dir($params['dirname'] . DIRECTORY_SEPARATOR . $_entry)) { $_params = array( 'dirname' => $params['dirname'] . DIRECTORY_SEPARATOR . $_entry, 'level' => $params['level'] + 1, 'exp_time' => $params['exp_time'] ); smarty_core_rmdir($_params, $smarty); } else { $smarty->_unlink($params['dirname'] . DIRECTORY_SEPARATOR . $_entry, $params['exp_time']); } } } closedir($_handle); } if ($params['level']) { return @rmdir($params['dirname']); } return (bool)$_handle; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.write_compiled_resource.php0100644025754300001440000000207110476656640026003 0ustar ipsouserscompile_dir)) { // compile_dir not writable, see if it exists if(!@is_dir($smarty->compile_dir)) { $smarty->trigger_error('the $compile_dir \'' . $smarty->compile_dir . '\' does not exist, or is not a directory.', E_USER_ERROR); return false; } $smarty->trigger_error('unable to write to $compile_dir \'' . realpath($smarty->compile_dir) . '\'. Be sure $compile_dir is writable by the web server user.', E_USER_ERROR); return false; } $_params = array('filename' => $params['compile_path'], 'contents' => $params['compiled_content'], 'create_dirs' => true); require_once(SMARTY_CORE_DIR . 'core.write_file.php'); smarty_core_write_file($_params, $smarty); return true; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.load_plugins.php0100644025754300001440000001051510476656640023550 0ustar ipsousers_plugins[$_type][$_name]; /* * We do not load plugin more than once for each instance of Smarty. * The following code checks for that. The plugin can also be * registered dynamically at runtime, in which case template file * and line number will be unknown, so we fill them in. * * The final element of the info array is a flag that indicates * whether the dynamically registered plugin function has been * checked for existence yet or not. */ if (isset($_plugin)) { if (empty($_plugin[3])) { if (!is_callable($_plugin[0])) { $smarty->_trigger_fatal_error("[plugin] $_type '$_name' is not implemented", $_tpl_file, $_tpl_line, __FILE__, __LINE__); } else { $_plugin[1] = $_tpl_file; $_plugin[2] = $_tpl_line; $_plugin[3] = true; if (!isset($_plugin[4])) $_plugin[4] = true; /* cacheable */ } } continue; } else if ($_type == 'insert') { /* * For backwards compatibility, we check for insert functions in * the symbol table before trying to load them as a plugin. */ $_plugin_func = 'insert_' . $_name; if (function_exists($_plugin_func)) { $_plugin = array($_plugin_func, $_tpl_file, $_tpl_line, true, false); continue; } } $_plugin_file = $smarty->_get_plugin_filepath($_type, $_name); if (! $_found = ($_plugin_file != false)) { $_message = "could not load plugin file '$_type.$_name.php'\n"; } /* * If plugin file is found, it -must- provide the properly named * plugin function. In case it doesn't, simply output the error and * do not fall back on any other method. */ if ($_found) { include_once $_plugin_file; $_plugin_func = 'smarty_' . $_type . '_' . $_name; if (!function_exists($_plugin_func)) { $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", $_tpl_file, $_tpl_line, __FILE__, __LINE__); continue; } } /* * In case of insert plugins, their code may be loaded later via * 'script' attribute. */ else if ($_type == 'insert' && $_delayed_loading) { $_plugin_func = 'smarty_' . $_type . '_' . $_name; $_found = true; } /* * Plugin specific processing and error checking. */ if (!$_found) { if ($_type == 'modifier') { /* * In case modifier falls back on using PHP functions * directly, we only allow those specified in the security * context. */ if ($smarty->security && !in_array($_name, $smarty->security_settings['MODIFIER_FUNCS'])) { $_message = "(secure mode) modifier '$_name' is not allowed"; } else { if (!function_exists($_name)) { $_message = "modifier '$_name' is not implemented"; } else { $_plugin_func = $_name; $_found = true; } } } else if ($_type == 'function') { /* * This is a catch-all situation. */ $_message = "unknown tag - '$_name'"; } } if ($_found) { $smarty->_plugins[$_type][$_name] = array($_plugin_func, $_tpl_file, $_tpl_line, true, true); } else { // output error $smarty->_trigger_fatal_error('[plugin] ' . $_message, $_tpl_file, $_tpl_line, __FILE__, __LINE__); } } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.is_secure.php0100644025754300001440000000323610476656640023053 0ustar ipsouserssecurity || $smarty->security_settings['INCLUDE_ANY']) { return true; } if ($params['resource_type'] == 'file') { $_rp = realpath($params['resource_name']); if (isset($params['resource_base_path'])) { foreach ((array)$params['resource_base_path'] as $curr_dir) { if ( ($_cd = realpath($curr_dir)) !== false && strncmp($_rp, $_cd, strlen($_cd)) == 0 && substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) { return true; } } } if (!empty($smarty->secure_dir)) { foreach ((array)$smarty->secure_dir as $curr_dir) { if ( ($_cd = realpath($curr_dir)) !== false) { if($_cd == $_rp) { return true; } elseif (strncmp($_rp, $_cd, strlen($_cd)) == 0 && substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR) { return true; } } } } } else { // resource is not on local file system return call_user_func_array( $smarty->_plugins['resource'][$params['resource_type']][0][2], array($params['resource_name'], &$smarty)); } return false; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.create_dir_structure.php0100644025754300001440000000471310476656640025314 0ustar ipsousers_dir_perms) && !is_dir($_new_dir)) { $smarty->trigger_error("problem creating directory '" . $_new_dir . "'"); return false; } $_new_dir .= '/'; } } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.is_trusted.php0100644025754300001440000000240410476656640023253 0ustar ipsouserstrusted_dir)) { $_rp = realpath($params['resource_name']); foreach ((array)$smarty->trusted_dir as $curr_dir) { if (!empty($curr_dir) && is_readable ($curr_dir)) { $_cd = realpath($curr_dir); if (strncmp($_rp, $_cd, strlen($_cd)) == 0 && substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) { $_smarty_trusted = true; break; } } } } } else { // resource is not on local file system $_smarty_trusted = call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][3], array($params['resource_name'], $smarty)); } return $_smarty_trusted; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.display_debug_console.php0100644025754300001440000000305710476656640025430 0ustar ipsousers * Name: display_debug_console
* Purpose: display the javascript debug console window * @param array Format: null * @param Smarty */ function smarty_core_display_debug_console($params, &$smarty) { // we must force compile the debug template in case the environment // changed between separate applications. if(empty($smarty->debug_tpl)) { // set path to debug template from SMARTY_DIR $smarty->debug_tpl = SMARTY_DIR . 'debug.tpl'; if($smarty->security && is_file($smarty->debug_tpl)) { $smarty->secure_dir[] = realpath($smarty->debug_tpl); } $smarty->debug_tpl = 'file:' . SMARTY_DIR . 'debug.tpl'; } $_ldelim_orig = $smarty->left_delimiter; $_rdelim_orig = $smarty->right_delimiter; $smarty->left_delimiter = '{'; $smarty->right_delimiter = '}'; $_compile_id_orig = $smarty->_compile_id; $smarty->_compile_id = null; $_compile_path = $smarty->_get_compile_path($smarty->debug_tpl); if ($smarty->_compile_resource($smarty->debug_tpl, $_compile_path)) { ob_start(); $smarty->_include($_compile_path); $_results = ob_get_contents(); ob_end_clean(); } else { $_results = ''; } $smarty->_compile_id = $_compile_id_orig; $smarty->left_delimiter = $_ldelim_orig; $smarty->right_delimiter = $_rdelim_orig; return $_results; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.run_insert_handler.php0100644025754300001440000000514010476656640024753 0ustar ipsousersdebugging) { $_params = array(); $_debug_start_time = smarty_core_get_microtime($_params, $smarty); } if ($smarty->caching) { $_arg_string = serialize($params['args']); $_name = $params['args']['name']; if (!isset($smarty->_cache_info['insert_tags'][$_name])) { $smarty->_cache_info['insert_tags'][$_name] = array('insert', $_name, $smarty->_plugins['insert'][$_name][1], $smarty->_plugins['insert'][$_name][2], !empty($params['args']['script']) ? true : false); } return $smarty->_smarty_md5."{insert_cache $_arg_string}".$smarty->_smarty_md5; } else { if (isset($params['args']['script'])) { $_params = array('resource_name' => $smarty->_dequote($params['args']['script'])); require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); if(!smarty_core_get_php_resource($_params, $smarty)) { return false; } if ($_params['resource_type'] == 'file') { $smarty->_include($_params['php_resource'], true); } else { $smarty->_eval($_params['php_resource']); } unset($params['args']['script']); } $_funcname = $smarty->_plugins['insert'][$params['args']['name']][0]; $_content = $_funcname($params['args'], $smarty); if ($smarty->debugging) { $_params = array(); require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); $smarty->_smarty_debug_info[] = array('type' => 'insert', 'filename' => 'insert_'.$params['args']['name'], 'depth' => $smarty->_inclusion_depth, 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time); } if (!empty($params['args']["assign"])) { $smarty->assign($params['args']["assign"], $_content); } else { return $_content; } } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.get_microtime.php0100644025754300001440000000055010476656640023715 0ustar ipsousers phpgacl-3.3.7/admin/smarty/libs/internals/core.smarty_include_php.php0100644025754300001440000000310210476656640024753 0ustar ipsousers $params['smarty_file']); require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); smarty_core_get_php_resource($_params, $smarty); $_smarty_resource_type = $_params['resource_type']; $_smarty_php_resource = $_params['php_resource']; if (!empty($params['smarty_assign'])) { ob_start(); if ($_smarty_resource_type == 'file') { $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']); } else { $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']); } $smarty->assign($params['smarty_assign'], ob_get_contents()); ob_end_clean(); } else { if ($_smarty_resource_type == 'file') { $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']); } else { $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']); } } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.write_file.php0100644025754300001440000000254210476656640023222 0ustar ipsousers $_dirname); require_once(SMARTY_CORE_DIR . 'core.create_dir_structure.php'); smarty_core_create_dir_structure($_params, $smarty); } // write to tmp file, then rename it to avoid // file locking race condition $_tmp_file = tempnam($_dirname, 'wrt'); if (!($fd = @fopen($_tmp_file, 'wb'))) { $_tmp_file = $_dirname . DIRECTORY_SEPARATOR . uniqid('wrt'); if (!($fd = @fopen($_tmp_file, 'wb'))) { $smarty->trigger_error("problem writing temporary file '$_tmp_file'"); return false; } } fwrite($fd, $params['contents']); fclose($fd); // Delete the file if it allready exists (this is needed on Win, // because it cannot overwrite files with rename() if (file_exists($params['filename'])) { @unlink($params['filename']); } @rename($_tmp_file, $params['filename']); @chmod($params['filename'], $smarty->_file_perms); return true; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.read_cache_file.php0100644025754300001440000000702410476656640024126 0ustar ipsousersforce_compile) { // force compile enabled, always regenerate return false; } if (isset($content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']])) { list($params['results'], $smarty->_cache_info) = $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']]; return true; } if (!empty($smarty->cache_handler_func)) { // use cache_handler function call_user_func_array($smarty->cache_handler_func, array('read', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null)); } else { // use local cache file $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']); $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id); $params['results'] = $smarty->_read_file($_cache_file); } if (empty($params['results'])) { // nothing to parse (error?), regenerate cache return false; } $_contents = $params['results']; $_info_start = strpos($_contents, "\n") + 1; $_info_len = (int)substr($_contents, 0, $_info_start - 1); $_cache_info = unserialize(substr($_contents, $_info_start, $_info_len)); $params['results'] = substr($_contents, $_info_start + $_info_len); if ($smarty->caching == 2 && isset ($_cache_info['expires'])){ // caching by expiration time if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) { // cache expired, regenerate return false; } } else { // caching by lifetime if ($smarty->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $smarty->cache_lifetime)) { // cache expired, regenerate return false; } } if ($smarty->compile_check) { $_params = array('get_source' => false, 'quiet'=>true); foreach (array_keys($_cache_info['template']) as $_template_dep) { $_params['resource_name'] = $_template_dep; if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) { // template file has changed, regenerate cache return false; } } if (isset($_cache_info['config'])) { $_params = array('resource_base_path' => $smarty->config_dir, 'get_source' => false, 'quiet'=>true); foreach (array_keys($_cache_info['config']) as $_config_dep) { $_params['resource_name'] = $_config_dep; if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) { // config file has changed, regenerate cache return false; } } } } $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']] = array($params['results'], $_cache_info); $smarty->_cache_info = $_cache_info; return true; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.get_php_resource.php0100644025754300001440000000464310476656640024432 0ustar ipsouserstrusted_dir; $smarty->_parse_resource_name($params, $smarty); /* * Find out if the resource exists. */ if ($params['resource_type'] == 'file') { $_readable = false; if(file_exists($params['resource_name']) && is_readable($params['resource_name'])) { $_readable = true; } else { // test for file in include_path $_params = array('file_path' => $params['resource_name']); require_once(SMARTY_CORE_DIR . 'core.get_include_path.php'); if(smarty_core_get_include_path($_params, $smarty)) { $_include_path = $_params['new_file_path']; $_readable = true; } } } else if ($params['resource_type'] != 'file') { $_template_source = null; $_readable = is_callable($smarty->_plugins['resource'][$params['resource_type']][0][0]) && call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][0], array($params['resource_name'], &$_template_source, &$smarty)); } /* * Set the error function, depending on which class calls us. */ if (method_exists($smarty, '_syntax_error')) { $_error_funcc = '_syntax_error'; } else { $_error_funcc = 'trigger_error'; } if ($_readable) { if ($smarty->security) { require_once(SMARTY_CORE_DIR . 'core.is_trusted.php'); if (!smarty_core_is_trusted($params, $smarty)) { $smarty->$_error_funcc('(secure mode) ' . $params['resource_type'] . ':' . $params['resource_name'] . ' is not trusted'); return false; } } } else { $smarty->$_error_funcc($params['resource_type'] . ':' . $params['resource_name'] . ' is not readable'); return false; } if ($params['resource_type'] == 'file') { $params['php_resource'] = $params['resource_name']; } else { $params['php_resource'] = $_template_source; } return true; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.process_compiled_include.php0100644025754300001440000000176310476656640026132 0ustar ipsousers_cache_including; $smarty->_cache_including = true; $_return = $params['results']; foreach ($smarty->_cache_info['cache_serials'] as $_include_file_path=>$_cache_serial) { $smarty->_include($_include_file_path, true); } foreach ($smarty->_cache_serials as $_include_file_path=>$_cache_serial) { $_return = preg_replace_callback('!(\{nocache\:('.$_cache_serial.')#(\d+)\})!s', array(&$smarty, '_process_compiled_include_callback'), $_return); } $smarty->_cache_including = $_cache_including; return $_return; } ?> phpgacl-3.3.7/admin/smarty/libs/internals/core.get_include_path.php0100644025754300001440000000175210476656640024371 0ustar ipsousers phpgacl-3.3.7/admin/smarty/libs/plugins/0040755025754300001440000000000010476665052017110 5ustar ipsousersphpgacl-3.3.7/admin/smarty/libs/plugins/modifier.count_sentences.php0100644025754300001440000000121510476656640024614 0ustar ipsousers * Name: count_sentences * Purpose: count the number of sentences in a text * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php * count_sentences (Smarty online manual) * @author Monte Ohrt * @param string * @return integer */ function smarty_modifier_count_sentences($string) { // find periods with a word before but not after. return preg_match_all('/[^\s]\.(?!\w)/', $string, $match); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/function.html_image.php0100644025754300001440000001125710476656640023561 0ustar ipsousers * Name: html_image
* Date: Feb 24, 2003
* Purpose: format HTML tags for the image
* Input:
* - file = file (and path) of image (required) * - height = image height (optional, default actual height) * - width = image width (optional, default actual width) * - basedir = base directory for absolute paths, default * is environment variable DOCUMENT_ROOT * - path_prefix = prefix for path output (optional, default empty) * * Examples: {html_image file="/images/masthead.gif"} * Output: * @link http://smarty.php.net/manual/en/language.function.html.image.php {html_image} * (Smarty online manual) * @author Monte Ohrt * @author credits to Duda - wrote first image function * in repository, helped with lots of functionality * @version 1.0 * @param array * @param Smarty * @return string * @uses smarty_function_escape_special_chars() */ function smarty_function_html_image($params, &$smarty) { require_once $smarty->_get_plugin_filepath('shared','escape_special_chars'); $alt = ''; $file = ''; $height = ''; $width = ''; $extra = ''; $prefix = ''; $suffix = ''; $path_prefix = ''; $server_vars = ($smarty->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS']; $basedir = isset($server_vars['DOCUMENT_ROOT']) ? $server_vars['DOCUMENT_ROOT'] : ''; foreach($params as $_key => $_val) { switch($_key) { case 'file': case 'height': case 'width': case 'dpi': case 'path_prefix': case 'basedir': $$_key = $_val; break; case 'alt': if(!is_array($_val)) { $$_key = smarty_function_escape_special_chars($_val); } else { $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); } break; case 'link': case 'href': $prefix = ''; $suffix = ''; break; default: if(!is_array($_val)) { $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; } else { $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); } break; } } if (empty($file)) { $smarty->trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE); return; } if (substr($file,0,1) == '/') { $_image_path = $basedir . $file; } else { $_image_path = $file; } if(!isset($params['width']) || !isset($params['height'])) { if(!$_image_data = @getimagesize($_image_path)) { if(!file_exists($_image_path)) { $smarty->trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE); return; } else if(!is_readable($_image_path)) { $smarty->trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE); return; } else { $smarty->trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE); return; } } if ($smarty->security && ($_params = array('resource_type' => 'file', 'resource_name' => $_image_path)) && (require_once(SMARTY_CORE_DIR . 'core.is_secure.php')) && (!smarty_core_is_secure($_params, $smarty)) ) { $smarty->trigger_error("html_image: (secure) '$_image_path' not in secure directory", E_USER_NOTICE); } if(!isset($params['width'])) { $width = $_image_data[0]; } if(!isset($params['height'])) { $height = $_image_data[1]; } } if(isset($params['dpi'])) { if(strstr($server_vars['HTTP_USER_AGENT'], 'Mac')) { $dpi_default = 72; } else { $dpi_default = 96; } $_resize = $dpi_default/$params['dpi']; $width = round($width * $_resize); $height = round($height * $_resize); } return $prefix . ''.$alt.'' . $suffix; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.spacify.php0100644025754300001440000000120410476656640023051 0ustar ipsousers * Name: spacify
* Purpose: add spaces between characters in a string * @link http://smarty.php.net/manual/en/language.modifier.spacify.php * spacify (Smarty online manual) * @author Monte Ohrt * @param string * @param string * @return string */ function smarty_modifier_spacify($string, $spacify_char = ' ') { return implode($spacify_char, preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY)); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.count_words.php0100644025754300001440000000135710476656640023772 0ustar ipsousers * Name: count_words
* Purpose: count the number of words in a text * @link http://smarty.php.net/manual/en/language.modifier.count.words.php * count_words (Smarty online manual) * @author Monte Ohrt * @param string * @return integer */ function smarty_modifier_count_words($string) { // split text by ' ',\r,\n,\f,\t $split_array = preg_split('/\s+/',$string); // count matches that contain alphanumerics $word_count = preg_grep('/[a-zA-Z0-9\\x80-\\xff]/', $split_array); return count($word_count); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.escape.php0100644025754300001440000000527710476656640022671 0ustar ipsousers * Name: escape
* Purpose: Escape the string according to escapement type * @link http://smarty.php.net/manual/en/language.modifier.escape.php * escape (Smarty online manual) * @author Monte Ohrt * @param string * @param html|htmlall|url|quotes|hex|hexentity|javascript * @return string */ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1') { switch ($esc_type) { case 'html': return htmlspecialchars($string, ENT_QUOTES, $char_set); case 'htmlall': return htmlentities($string, ENT_QUOTES, $char_set); case 'url': return rawurlencode($string); case 'urlpathinfo': return str_replace('%2F','/',rawurlencode($string)); case 'quotes': // escape unescaped single quotes return preg_replace("%(?'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n',''<\/')); case 'mail': // safe way to display e-mail address on a web page return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string); case 'nonstd': // escape non-standard chars, such as ms document quotes $_res = ''; for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) { $_ord = ord(substr($string, $_i, 1)); // non-standard char, escape it if($_ord >= 126){ $_res .= '&#' . $_ord . ';'; } else { $_res .= substr($string, $_i, 1); } } return $_res; default: return $string; } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/compiler.assign.php0100644025754300001440000000214310476656640022716 0ustar ipsousers * Name: assign
* Purpose: assign a value to a template variable * @link http://smarty.php.net/manual/en/language.custom.functions.php#LANGUAGE.FUNCTION.ASSIGN {assign} * (Smarty online manual) * @author Monte Ohrt (initial author) * @auther messju mohr (conversion to compiler function) * @param string containing var-attribute and value-attribute * @param Smarty_Compiler */ function smarty_compiler_assign($tag_attrs, &$compiler) { $_params = $compiler->_parse_attrs($tag_attrs); if (!isset($_params['var'])) { $compiler->_syntax_error("assign: missing 'var' parameter", E_USER_WARNING); return; } if (!isset($_params['value'])) { $compiler->_syntax_error("assign: missing 'value' parameter", E_USER_WARNING); return; } return "\$this->assign({$_params['var']}, {$_params['value']});"; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.count_paragraphs.php0100644025754300001440000000116610476656640024762 0ustar ipsousers * Name: count_paragraphs
* Purpose: count the number of paragraphs in a text * @link http://smarty.php.net/manual/en/language.modifier.count.paragraphs.php * count_paragraphs (Smarty online manual) * @author Monte Ohrt * @param string * @return integer */ function smarty_modifier_count_paragraphs($string) { // count \r or \n characters return count(preg_split('/[\r\n]+/', $string)); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.strip.php0100644025754300001440000000134610476656640022563 0ustar ipsousers * Name: strip
* Purpose: Replace all repeated spaces, newlines, tabs * with a single space or supplied replacement string.
* Example: {$var|strip} {$var|strip:" "} * Date: September 25th, 2002 * @link http://smarty.php.net/manual/en/language.modifier.strip.php * strip (Smarty online manual) * @author Monte Ohrt * @version 1.0 * @param string * @param string * @return string */ function smarty_modifier_strip($text, $replace = ' ') { return preg_replace('!\s+!', $replace, $text); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.replace.php0100644025754300001440000000111110476656640023023 0ustar ipsousers * Name: replace
* Purpose: simple search/replace * @link http://smarty.php.net/manual/en/language.modifier.replace.php * replace (Smarty online manual) * @author Monte Ohrt * @param string * @param string * @param string * @return string */ function smarty_modifier_replace($string, $search, $replace) { return str_replace($search, $replace, $string); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.cat.php0100644025754300001440000000115710476656640022171 0ustar ipsousers * Name: cat
* Date: Feb 24, 2003 * Purpose: catenate a value to a variable * Input: string to catenate * Example: {$var|cat:"foo"} * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat * (Smarty online manual) * @author Monte Ohrt * @version 1.0 * @param string * @param string * @return string */ function smarty_modifier_cat($string, $cat) { return $string . $cat; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.count_characters.php0100644025754300001440000000134710476656640024752 0ustar ipsousers * Name: count_characteres
* Purpose: count the number of characters in a text * @link http://smarty.php.net/manual/en/language.modifier.count.characters.php * count_characters (Smarty online manual) * @author Monte Ohrt * @param string * @param boolean include whitespace in the character count * @return integer */ function smarty_modifier_count_characters($string, $include_spaces = false) { if ($include_spaces) return(strlen($string)); return preg_match_all("/[^\s]/",$string, $match); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.debug_print_var.php0100644025754300001440000000356710476656640024603 0ustar ipsousers * Name: debug_print_var
* Purpose: formats variable contents for display in the console * @link http://smarty.php.net/manual/en/language.modifier.debug.print.var.php * debug_print_var (Smarty online manual) * @author Monte Ohrt * @param array|object * @param integer * @param integer * @return string */ function smarty_modifier_debug_print_var($var, $depth = 0, $length = 40) { $_replace = array("\n"=>'\n', "\r"=>'\r', "\t"=>'\t'); if (is_array($var)) { $results = "Array (".count($var).")"; foreach ($var as $curr_key => $curr_val) { $return = smarty_modifier_debug_print_var($curr_val, $depth+1, $length); $results .= "
".str_repeat(' ', $depth*2)."".strtr($curr_key, $_replace)." => $return"; } } else if (is_object($var)) { $object_vars = get_object_vars($var); $results = "".get_class($var)." Object (".count($object_vars).")"; foreach ($object_vars as $curr_key => $curr_val) { $return = smarty_modifier_debug_print_var($curr_val, $depth+1, $length); $results .= "
".str_repeat(' ', $depth*2)."$curr_key => $return"; } } else if (is_resource($var)) { $results = ''.(string)$var.''; } else if (empty($var) && $var != "0") { $results = 'empty'; } else { if (strlen($var) > $length ) { $results = substr($var, 0, $length-3).'...'; } else { $results = $var; } $results = htmlspecialchars($results); $results = strtr($results, $_replace); } return $results; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/function.eval.php0100644025754300001440000000176610476656640022406 0ustar ipsousers * Name: eval
* Purpose: evaluate a template variable as a template
* @link http://smarty.php.net/manual/en/language.function.eval.php {eval} * (Smarty online manual) * @author Monte Ohrt * @param array * @param Smarty */ function smarty_function_eval($params, &$smarty) { if (!isset($params['var'])) { $smarty->trigger_error("eval: missing 'var' parameter"); return; } if($params['var'] == '') { return; } $smarty->_compile_source('evaluated template', $params['var'], $_var_compiled); ob_start(); $smarty->_eval('?>' . $_var_compiled); $_contents = ob_get_contents(); ob_end_clean(); if (!empty($params['assign'])) { $smarty->assign($params['assign'], $_contents); } else { return $_contents; } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.string_format.php0100644025754300001440000000110310476656640024267 0ustar ipsousers * Name: string_format
* Purpose: format strings via sprintf * @link http://smarty.php.net/manual/en/language.modifier.string.format.php * string_format (Smarty online manual) * @author Monte Ohrt * @param string * @param string * @return string */ function smarty_modifier_string_format($string, $format) { return sprintf($format, $string); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.nl2br.php0100644025754300001440000000131510476656640022435 0ustar ipsousers * Name: nl2br
* Date: Feb 26, 2003 * Purpose: convert \r\n, \r or \n to <
> * Input:
* - contents = contents to replace * - preceed_test = if true, includes preceeding break tags * in replacement * Example: {$text|nl2br} * @link http://smarty.php.net/manual/en/language.modifier.nl2br.php * nl2br (Smarty online manual) * @version 1.0 * @author Monte Ohrt * @param string * @return string */ function smarty_modifier_nl2br($string) { return nl2br($string); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.strip_tags.php0100644025754300001440000000124410476656640023576 0ustar ipsousers * Name: strip_tags
* Purpose: strip html tags from text * @link http://smarty.php.net/manual/en/language.modifier.strip.tags.php * strip_tags (Smarty online manual) * @author Monte Ohrt * @param string * @param boolean * @return string */ function smarty_modifier_strip_tags($string, $replace_with_space = true) { if ($replace_with_space) return preg_replace('!<[^>]*?>!', ' ', $string); else return strip_tags($string); } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.default.php0100644025754300001440000000117310476656640023044 0ustar ipsousers * Name: default
* Purpose: designate default value for empty variables * @link http://smarty.php.net/manual/en/language.modifier.default.php * default (Smarty online manual) * @author Monte Ohrt * @param string * @param string * @return string */ function smarty_modifier_default($string, $default = '') { if (!isset($string) || $string === '') return $default; else return $string; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.date_format.php0100644025754300001440000000260110476656640023702 0ustar ipsousers_get_plugin_filepath('shared','make_timestamp'); /** * Smarty date_format modifier plugin * * Type: modifier
* Name: date_format
* Purpose: format datestamps via strftime
* Input:
* - string: input date string * - format: strftime format for output * - default_date: default date if $string is empty * @link http://smarty.php.net/manual/en/language.modifier.date.format.php * date_format (Smarty online manual) * @author Monte Ohrt * @param string * @param string * @param string * @return string|void * @uses smarty_make_timestamp() */ function smarty_modifier_date_format($string, $format="%b %e, %Y", $default_date=null) { if (substr(PHP_OS,0,3) == 'WIN') { $_win_from = array ('%e', '%T', '%D'); $_win_to = array ('%#d', '%H:%M:%S', '%m/%d/%y'); $format = str_replace($_win_from, $_win_to, $format); } if($string != '') { return strftime($format, smarty_make_timestamp($string)); } elseif (isset($default_date) && $default_date != '') { return strftime($format, smarty_make_timestamp($default_date)); } else { return; } } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/modifier.capitalize.php0100644025754300001440000000174210476656640023547 0ustar ipsousers * Name: capitalize
* Purpose: capitalize words in the string * @link http://smarty.php.net/manual/en/language.modifiers.php#LANGUAGE.MODIFIER.CAPITALIZE * capitalize (Smarty online manual) * @author Monte Ohrt * @param string * @return string */ function smarty_modifier_capitalize($string, $uc_digits = false) { smarty_modifier_capitalize_ucfirst(null, $uc_digits); return preg_replace_callback('!\b\w+\b!', 'smarty_modifier_capitalize_ucfirst', $string); } function smarty_modifier_capitalize_ucfirst($string, $uc_digits = null) { static $_uc_digits = false; if(isset($uc_digits)) { $_uc_digits = $uc_digits; return; } if(!preg_match('!\d!',$string[0]) || $_uc_digits) return ucfirst($string[0]); else return $string[0]; } ?> phpgacl-3.3.7/admin/smarty/libs/plugins/function.cycle.php0100644025754300001440000000617010476656640022550 0ustar ipsousers * Name: cycle
* Date: May 3, 2002
* Purpose: cycle through given values
* Input: * - name = name of cycle (optional) * - values = comma separated list of values to cycle, * or an array of values to cycle * (this can be left out for subsequent calls) * - reset = boolean - resets given var to true * - print = boolean - print var or not. default is true * - advance = boolean - whether or not to advance the cycle * - delimiter = the value delimiter, default is "," * - assign = boolean, assigns to template var instead of * printed. * * Examples:
*
 * {cycle values="#eeeeee,#d0d0d0d"}
 * {cycle name=row values="one,two,three" reset=true}
 * {cycle name=row}
 * 
* @link http://smarty.php.net/manual/en/language.function.cycle.php {cycle} * (Smarty online manual) * @author Monte Ohrt * @author credit to Mark Priatel * @author credit to Gerard * @author credit to Jason Sweat * @version 1.3 * @param array * @param Smarty * @return string|null */ function smarty_function_cycle($params, &$smarty) { static $cycle_vars; $name = (empty($params['name'])) ? 'default' : $params['name']; $print = (isset($params['print'])) ? (bool)$params['print'] : true; $advance = (isset($params['advance'])) ? (bool)$params['advance'] : true; $reset = (isset($params['reset'])) ? (bool)$params['reset'] : false; if (!in_array('values', array_keys($params))) { if(!isset($cycle_vars[$name]['values'])) { $smarty->trigger_error("cycle: missing 'values' parameter"); return; } } else { if(isset($cycle_vars[$name]['values']) && $cycle_vars[$name]['values'] != $params['values'] ) { $cycle_vars[$name]['index'] = 0; } $cycle_vars[$name]['values'] = $params['values']; } $cycle_vars[$name]['delimiter'] = (isset($params['delimiter'])) ? $params['delimiter'] : ','; if(is_array($cycle_vars[$name]['values'])) { $cycle_array = $cycle_vars[$name]['values']; } else { $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']); } if(!isset($cycle_vars[$name]['index']) || $reset ) { $cycle_vars[$name]['index'] = 0; } if (isset($params['assign'])) { $print = false; $smarty->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); } if($print) { $retval = $cycle_array[$cycle_vars[$name]['index']]; } else { $retval = null; } if($advance) { if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) { $cycle_vars[$name]['index'] = 0; } else { $cycle_vars[$name]['index']++; } } return $retval; } /* vim: set expandtab: */ ?> phpgacl-3.3.7/admin/smarty/libs/plugins/function.html_radios.php0100644025754300001440000001135110476656640023753 0ustar ipsousers * Type: function
* Name: html_radios
* Date: 24.Feb.2003
* Purpose: Prints out a list of radio input types
* Input:
* - name (optional) - string default "radio" * - values (required) - array * - options (optional) - associative array * - checked (optional) - array default not set * - separator (optional) - ie
or   * - output (optional) - the output next to each radio button * - assign (optional) - assign the output as an array to this variable * Examples: *
 * {html_radios values=$ids output=$names}
 * {html_radios values=$ids name='box' separator='
' output=$names} * {html_radios values=$ids checked=$checked separator='
' output=$names} *
* @link http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios} * (Smarty online manual) * @author Christopher Kvarme * @author credits to Monte Ohrt * @version 1.0 * @param array * @param Smarty * @return string * @uses smarty_function_escape_special_chars() */ function smarty_function_html_radios($params, &$smarty) { require_once $smarty->_get_plugin_filepath('shared','escape_special_chars'); $name = 'radio'; $values = null; $options = null; $selected = null; $separator = ''; $labels = true; $label_ids = false; $output = null; $extra = ''; foreach($params as $_key => $_val) { switch($_key) { case 'name': case 'separator': $$_key = (string)$_val; break; case 'checked': case 'selected': if(is_array($_val)) { $smarty->trigger_error('html_radios: the "' . $_key . '" attribute cannot be an array', E_USER_WARNING); } else { $selected = (string)$_val; } break; case 'labels': case 'label_ids': $$_key = (bool)$_val; break; case 'options': $$_key = (array)$_val; break; case 'values': case 'output': $$_key = array_values((array)$_val); break; case 'radios': $smarty->trigger_error('html_radios: the use of the "radios" attribute is deprecated, use "options" instead', E_USER_WARNING); $options = (array)$_val; break; case 'assign': break; default: if(!is_array($_val)) { $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; } else { $smarty->trigger_error("html_radios: extra attribute '$_key' cannot be an array", E_USER_NOTICE); } break; } } if (!isset($options) && !isset($values)) return ''; /* raise error here? */ $_html_result = array(); if (isset($options)) { foreach ($options as $_key=>$_val) $_html_result[] = smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids); } else { foreach ($values as $_i=>$_key) { $_val = isset($output[$_i]) ? $output[$_i] : ''; $_html_result[] = smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids); } } if(!empty($params['assign'])) { $smarty->assign($params['assign'], $_html_result); } else { return implode("\n",$_html_result); } } function smarty_function_html_radios_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids) { $_output = ''; if ($labels) { if($label_ids) { $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!', '_', $name . '_' . $value)); $_output .= '
phpgacl-3.3.7/admin/templates/phpgacl/acl_admin_js.tpl0100644025754300001440000000012610001413673021673 0ustar ipsousers{literal} {/literal}phpgacl-3.3.7/admin/templates/phpgacl/edit_group.tpl0100644025754300001440000000323310021466113021432 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/navigation.tpl"}
ID Parent Name Value
{$id|default:"N/A"}
{include file="phpgacl/footer.tpl"}phpgacl-3.3.7/admin/templates/phpgacl/group_admin.tpl0100644025754300001440000000414510064374642021614 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/acl_admin_js.tpl"} {include file="phpgacl/navigation.tpl"}
{foreach from=$groups item=group} {/foreach}
ID Name Value Objects Functions
{$group.id} {$group.name} {$group.value} {$group.object_count} Assign {$group_type|upper} ] [ Add Child ] [ Edit ] [ ACLs ]
 
{include file="phpgacl/footer.tpl"} phpgacl-3.3.7/admin/templates/phpgacl/acl_debug.tpl0100644025754300001440000000613110006771676021216 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/navigation.tpl"}
  ACO ARO AXO Root ARO
Group ID
Root AXO
Group ID
 
Section Value Section Value Section Value
acl_query( )
{if count($acls) gt 0}
{foreach from=$acls item=acl} {/foreach}
ACL ID ACO ARO AXO ACL
Section Value Section Value Section Value Access Updated Date
{$acl.id} {$acl.aco_section_value} {$acl.aco_value} {$acl.aro_section_value}
{$acl.aro_value}
{$acl.axo_section_value}
{$acl.axo_value}
{if $acl.allow} ALLOW {else} DENY {/if} {$acl.updated_date}
Return Value: {$acl.return_value}
Note: {$acl.note}
{/if}
{include file="phpgacl/footer.tpl"}phpgacl-3.3.7/admin/templates/phpgacl/acl_admin.tpl0100644025754300001440000002400510034347573021214 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/acl_admin_js.tpl"} {include file="phpgacl/navigation.tpl"}
Sections Access Control Objects   Selected Access
[ Edit ]
[ Edit ]



Sections Access Request Objects   Selected Groups
[ Edit ]
[ Edit ] [ Search ]



[ Edit ]

[ Show / Hide ] Access eXtension Objects (Optional)
Sections Access eXtension Objects   Selected Groups
[ Edit ]
[ Edit ] [ Search ]



[ Edit ]

Miscellaneous Attributes
ACL Section Extended Return Value:
[ Edit ]
Note:
{include file="phpgacl/footer.tpl"}phpgacl-3.3.7/admin/templates/phpgacl/about.tpl0100644025754300001440000000573410011530256020412 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/navigation.tpl"}
{if $first_run != 1} {/if} {if $first_run != 1} {/if}
Help
Please join the Mailing Lists if you have any questions, comments, or support questions.

TIP: Searching the Mailing List archives may be a good idea prior to emailing the list,
as well the below "Report" information may be helpful in any support questions.

PLEASE DO NOT EMAIL ME DIRECTLY REGARDING SUPPORT QUESTIONS
You will receive answers faster on the mailing list, and any answers given may benefit others.

But if you must email me (Mike Benoit) directly, click here.
Donate
Time working on phpGACL means less time that I can work to get paid.
Therefore any donations I receive will help me to devote more time to developing phpGACL.

However, I'd much rather donations in the form of code and/or documentation.

{if $first_run != 1} Report {else} * Report * {/if}
Report some basic information back to the phpGACL project so we know where to spend our time.
All information will be kept private, will not be sold, and will only be used for informational purposes regarding phpGACL.


Credits
{$credits}
{include file="phpgacl/footer.tpl"} phpgacl-3.3.7/admin/templates/phpgacl/acl_test2.tpl0100644025754300001440000000454710152167553021175 0ustar ipsousers{include file="phpgacl/header.tpl"} {include file="phpgacl/navigation.tpl"}
{section name=x loop=$acls} {/section}
{include file="phpgacl/pager.tpl" pager_data=$paging_data link="?"}
# Section > ACO Section > ARO Return Value ACL_CHECK() Code Debug Time (ms) Access
{$smarty.section.x.iteration} {$acls[x].display_aco_name} {$acls[x].aro_section_name} > {$acls[x].aro_name} {$acls[x].return_value}
acl_check('{$acls[x].aco_section_value}', '{$acls[x].aco_value}', '{$acls[x].aro_section_value}', '{$acls[x].aro_value}') [ debug ] {$acls[x].acl_check_time} {if $acls[x].access} ALLOW {else} DENY {/if}
{include file="phpgacl/pager.tpl" pager_data=$paging_data link="?"}

Summary
Total ACL Check(s) {$total_acl_checks}
Average Time / Check {$avg_acl_check_time}ms

Do you want to test 3-dimensional ACLs?
[ 3-dimensional ACLs ]

{include file="phpgacl/footer.tpl"}phpgacl-3.3.7/admin/admin.css0100644025754300001440000000730410034347573014755 0ustar ipsousersbody { background-color:#ffffff; margin: 0px; padding: 0px; } div { position: relative; } div#top-tr { margin: 0px; padding: 0px; background: #cccccc url("images/top-r.png") no-repeat top right; } div#top-tl { margin: 0px; padding: 0px; background: url("images/top-l.png") no-repeat top left; } div#top-br { margin: 0px; padding: 0px; background: url("images/div-r.png") no-repeat bottom right; } div#top-bl { margin: 0px; padding: 10px 180px 0px 10px; background: url("images/div-l.png") no-repeat bottom left; } div#top-bl h1 { position: absolute; visibility: hidden; } div#top-bl h2 { color: #eeeeee; font-size: 150%; margin: 6px 4px; padding: 0px; } ul#menu { margin: 0px; padding: 0px; display: table; width: 100%; list-style-type: none; } ul#menu li { float: left; margin: 0px 2px 0px 0px; padding: 0px; background: #999999 url("images/tab-off-r.png") no-repeat top right; border-bottom: 1px solid #666666; } ul#menu li a { display: block; margin: 0px; padding: 2px 5px 1px 5px; background: url("images/tab-off-l.png") no-repeat top left; color: #d8d8d8; text-decoration: none; text-align: center; font-weight: normal; width: .1em; white-space: nowrap; min-width: 50px; } ul#menu li>a { width: auto; } ul#menu li a:hover { color: #ffffff; } ul#menu li.current { background-color: #ffffff; background-image: url("images/tab-on-r.png"); border-bottom-width: 0px; } ul#menu li.current a { background-image: url("images/tab-on-l.png"); padding-bottom: 2px; color: #666666; text-decoration: none; } ul#menu li.current a:hover { color: #666666; } div#mid-r { margin: 0px; padding: 0px; background: #ffffff url("images/mid-r.png") repeat-y top right; } div#mid-l { margin: 0px; padding: 4px 10px; background: url("images/mid-l.png") repeat-y top left; } div#bot-tr { margin: 0px; padding: 0px; background: url("images/div-r.png") no-repeat top right; } div#bot-tl { margin: 0px; padding: 2px 10px 10px 10px; background: url("images/div-l.png") no-repeat top left; font-size: 8pt; text-align: center; } div#bot-tl a { text-decoration: none; } div#bot-br { margin: 0px; padding: 0px; background: #cccccc url("images/bot-r.png") no-repeat bottom right; } div#bot-bl { margin: 0px; padding: 0px; background: url("images/bot-l.png") no-repeat bottom left; } body, td, th { font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 9pt; } table, td, th { border: 0px; } tr { background-color: #cccccc; } th { background-color: #d3dce3; font-weight: bold; text-align: center; } tr.controls { background-color: #999999; } tr.odd { background-color: #c0c0c0; } tr.even { background-color: #d0d0d0; } tr.spacer { background-color: transparent; height: 10px; } td.green { background-color: green; } td.red { background-color: red; } input, select, textarea { font-family: verdana, sans-serif; font-size: 9pt; border: 1px solid #333333; } input { padding: 1px 2px; } input.checkbox, input.radio { border: 0px; margin: 1px 2px 0px; padding: 0px; } input.button { width: 100px; } input.paypal { border-width: 0px; } .navigation { font-family: verdana, sans-serif; font-size: 8pt; padding-bottom: 5px; } .navigation a { color: #0000ff; } .navigation a:hover { color: #aa0000; background-color: #f0f0f0; } .footer { font-size: 8pt; text-align: center; } form { margin: 0px; } tr.hide { display: none; } tr.show { } td.tabon { background: #438EC5; } td.taboff { background: #ABC3D4; } select { width: 99%; margin-top: 5px; } textarea, input#return_value { width: 99%; } input.un-select { width: 99%; margin-top: 5px; } input.select { width: 99%; margin-top: 5px; } input.deselect { width: 99%; margin-top: 8px; }phpgacl-3.3.7/admin/object_search.php0100644025754300001440000000566410011530256016451 0ustar ipsousersdebug_text('Submit!!'); //Function to pass array_walk to trim all entries in an array. function array_walk_trim(&$array_field) { $array_field = $db->qstr(strtolower(trim($array_field))); } $value_search_str = trim($_GET['value_search_str']); $name_search_str = trim($_GET['name_search_str']); $exploded_value_search_str = explode("\n", $value_search_str); $exploded_name_search_str = explode("\n", $name_search_str); if (count($exploded_value_search_str) > 1 OR count($exploded_name_search_str) > 1) { //Given a list, lets try to match all lines in it. array_walk($exploded_value_search_str, 'array_walk_trim'); array_walk($exploded_name_search_str, 'array_walk_trim'); } else { if ($value_search_str != '') { $value_search_str .= '%'; } if ($name_search_str != '') { $name_search_str .= '%'; } } //Search $query = ' SELECT section_value,value,name FROM '. $gacl_api->_db_table_prefix . $object_type .' WHERE section_value='. $db->qstr($_GET['section_value']) .' AND ('; if (count($exploded_value_search_str) > 1) { $query .= 'lower(value) IN ('. implode(',', $exploded_value_search_str) .')'; } else { $query .= 'lower(value) LIKE ' . $db->qstr($value_search_str); } $query .= ' OR '; if (count($exploded_name_search_str) > 1) { $query .= 'lower(name) IN ('. implode(',', $exploded_name_search_str) .')'; } else { $query .= 'lower(name) LIKE ' . $db->qstr($name_search_str); } $query .= ') ORDER BY section_value,order_value,name'; $rs = $db->SelectLimit($query, $gacl_api->_max_search_return_items); $options_objects = array(); $total_rows = 0; if (is_object($rs)) { $total_rows = $rs->RecordCount(); while ($row = $rs->FetchRow()) { list($section_value, $value, $name) = $row; $options_objects[$value] = $name; } } $smarty->assign('options_objects', $options_objects); $smarty->assign('total_rows', $total_rows); $smarty->assign('value_search_str', $_GET['value_search_str']); $smarty->assign('name_search_str', $_GET['name_search_str']); //break; default: $smarty->assign('src_form', $_GET['src_form']); $smarty->assign('section_value', $_GET['section_value']); $smarty->assign('section_value_name', ucfirst($_GET['section_value'])); $smarty->assign('object_type', $object_type); $smarty->assign('object_type_name', strtoupper($object_type)); break; } $smarty->assign('current', $object_type .'_search'); $smarty->assign('page_title', strtoupper($object_type) .' Search'); $smarty->assign('phpgacl_version', $gacl_api->get_version()); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version()); $smarty->display('phpgacl/object_search.tpl'); ?>phpgacl-3.3.7/admin/acl_list.php0100644025754300001440000002242710013210147015440 0ustar ipsousersdebug_text('Delete!'); if (is_array ($_GET['delete_acl']) AND !empty($_GET['delete_acl'])) { foreach($_GET['delete_acl'] as $id) { $gacl_api->del_acl($id); } } //Return page. $gacl_api->return_page($_GET['return_page']); break; case 'Submit': $gacl_api->debug_text('Submit!!'); break; default: /* * When the user requests to filter the list, run the filter and get just the matching IDs. * Use these IDs to get the entire ACL information in the second query. * * If we just put the LIKE statements in the second query, it will match the correct ACLs * but will only return the matching rows, so it won't show the entire ACL information. * */ if (isset($_GET['action']) AND $_GET['action'] == 'Filter') { $gacl_api->debug_text('Filtering...'); $query = ' SELECT DISTINCT a.id FROM '. $gacl_api->_db_table_prefix .'acl a LEFT JOIN '. $gacl_api->_db_table_prefix .'aco_map ac ON ac.acl_id=a.id LEFT JOIN '. $gacl_api->_db_table_prefix .'aro_map ar ON ar.acl_id=a.id LEFT JOIN '. $gacl_api->_db_table_prefix .'axo_map ax ON ax.acl_id=a.id'; if ( isset($_GET['filter_aco_section']) AND $_GET['filter_aco_section'] != '-1') { $filter_query[] = 'ac.section_value='. $db->qstr(strtolower($_GET['filter_aco_section'])); } if ( isset($_GET['filter_aco']) AND $_GET['filter_aco'] != '') { $query .= ' LEFT JOIN '. $gacl_api->_db_table_prefix .'aco c ON (c.section_value=ac.section_value AND c.value=ac.value)'; $name = $db->qstr(strtolower($_GET['filter_aco'])); $filter_query[] = '(lower(c.value) LIKE '. $name .' OR lower(c.name) LIKE '. $name .')'; } if ( isset($_GET['filter_aro_section']) AND $_GET['filter_aro_section'] != '-1') { $filter_query[] = 'ar.section_value='. $db->qstr(strtolower($_GET['filter_aro_section'])); } if ( isset($_GET['filter_aro']) AND $_GET['filter_aro'] != '') { $query .= ' LEFT JOIN '. $gacl_api->_db_table_prefix .'aro r ON (r.section_value=ar.section_value AND r.value=ar.value)'; $name = $db->qstr(strtolower($_GET['filter_aro'])); $filter_query[] = '(lower(r.value) LIKE '. $name .' OR lower(r.name) LIKE '. $name .')'; } if ( isset($_GET['filter_aro_group']) AND $_GET['filter_aro_group'] != '') { $query .= ' LEFT JOIN '. $gacl_api->_db_table_prefix .'aro_groups_map arg ON arg.acl_id=a.id LEFT JOIN '. $gacl_api->_db_table_prefix .'aro_groups rg ON rg.id=arg.group_id'; $filter_query[] = '(lower(rg.name) LIKE '. $db->qstr(strtolower($_GET['filter_aro_group'])) .')'; } if ( isset($_GET['filter_axo_section']) AND $_GET['filter_axo_section'] != '-1') { $filter_query[] = 'ax.section_value='. $db->qstr(strtolower($_GET['filter_axo_section'])); } if ( isset($_GET['filter_axo']) AND $_GET['filter_axo'] != '') { $query .= ' LEFT JOIN '. $gacl_api->_db_table_prefix .'axo x ON (x.section_value=ax.section_value AND x.value=ax.value)'; $name = $db->qstr(strtolower($_GET['filter_axo'])); $filter_query[] = '(lower(x.value) LIKE '. $name .' OR lower(x.name) LIKE '. $name .')'; } if ( isset($_GET['filter_axo_group']) AND $_GET['filter_axo_group'] != '') { $query .= ' LEFT JOIN '. $gacl_api->_db_table_prefix .'axo_groups_map axg ON axg.acl_id=a.id LEFT JOIN '. $gacl_api->_db_table_prefix .'axo_groups xg ON xg.id=axg.group_id'; $filter_query[] = '(lower(xg.name) LIKE '. $db->qstr(strtolower($_GET['filter_axo_group'])) .')'; } if ( isset($_GET['filter_acl_section']) AND $_GET['filter_acl_section'] != '-1') { $filter_query[] = 'a.section_value='. $db->qstr(strtolower($_GET['filter_acl_section'])); } if ( isset($_GET['filter_return_value']) AND $_GET['filter_return_value'] != '') { $filter_query[] = '(lower(a.return_value) LIKE '. $db->qstr(strtolower($_GET['filter_return_value'])) .')'; } if ( isset($_GET['filter_allow']) AND $_GET['filter_allow'] != '-1') { $filter_query[] = '(a.allow LIKE '. $db->qstr($_GET['filter_allow']) .')'; } if ( isset($_GET['filter_enabled']) AND $_GET['filter_enabled'] != '-1') { $filter_query[] = '(a.enabled LIKE '. $db->qstr($_GET['filter_enabled']) .')'; } if (isset($filter_query) AND is_array($filter_query)) { $query .= ' WHERE '. implode(' AND ', $filter_query); } } else { $query = ' SELECT a.id FROM ' . $gacl_api->_db_table_prefix . 'acl a'; } $query .= ' ORDER BY a.id ASC'; $acl_ids = array(); $rs = $db->PageExecute($query, $gacl_api->_items_per_page, $_GET['page']); if ( is_object($rs) ) { $smarty->assign('paging_data', $gacl_api->get_paging_data($rs)); while ( $row = $rs->FetchRow() ) { $acl_ids[] = $row[0]; } $rs->Close(); } if ( !empty($acl_ids) ) { $acl_ids_sql = implode(',', $acl_ids); } else { //This shouldn't match any ACLs, returning 0 rows. $acl_ids_sql = -1; } $acls = array(); //If the user is searching, and there are no results, don't run the query at all if ( !($_GET['action'] == 'Filter' AND $acl_ids_sql == -1) ) { // grab acl details $query = ' SELECT a.id,x.name,a.allow,a.enabled,a.return_value,a.note,a.updated_date FROM '. $gacl_api->_db_table_prefix .'acl a INNER JOIN '. $gacl_api->_db_table_prefix .'acl_sections x ON x.value=a.section_value WHERE a.id IN ('. $acl_ids_sql . ')'; $rs = $db->Execute($query); if ( is_object($rs) ) { while ( $row = $rs->FetchRow() ) { $acls[$row[0]] = array( 'id' => $row[0], // 'section_id' => $section_id, 'section_name' => $row[1], 'allow' => (bool)$row[2], 'enabled' => (bool)$row[3], 'return_value' => $row[4], 'note' => $row[5], 'updated_date' => $row[6], 'aco' => array(), 'aro' => array(), 'aro_groups' => array(), 'axo' => array(), 'axo_groups' => array() ); } } // grab ACO, ARO and AXOs foreach ( array('aco', 'aro', 'axo') as $type ) { $query = ' SELECT a.acl_id,o.name,s.name FROM '. $gacl_api->_db_table_prefix . $type .'_map a INNER JOIN '. $gacl_api->_db_table_prefix . $type .' o ON (o.section_value=a.section_value AND o.value=a.value) INNER JOIN '. $gacl_api->_db_table_prefix . $type . '_sections s ON s.value=a.section_value WHERE a.acl_id IN ('. $acl_ids_sql . ')'; $rs = $db->Execute($query); if ( is_object($rs) ) { while ( $row = $rs->FetchRow() ) { list($acl_id, $name, $section_name) = $row; if ( isset($acls[$acl_id]) ) { $acls[$acl_id][$type][$section_name][] = $name; } } } } // grab ARO and AXO groups foreach ( array('aro', 'axo') as $type ) { $query = ' SELECT a.acl_id,g.name FROM '. $gacl_api->_db_table_prefix . $type .'_groups_map a INNER JOIN '. $gacl_api->_db_table_prefix . $type .'_groups g ON g.id=a.group_id WHERE a.acl_id IN ('. $acl_ids_sql . ')'; $rs = $db->Execute($query); if ( is_object($rs) ) { while ( $row = $rs->FetchRow () ) { list($acl_id, $name) = $row; if ( isset($acls[$acl_id]) ) { $acls[$acl_id][$type .'_groups'][] = $name; } } } } } $smarty->assign('acls', $acls); $smarty->assign('filter_aco', $_GET['filter_aco']); $smarty->assign('filter_aro', $_GET['filter_aro']); $smarty->assign('filter_aro_group', $_GET['filter_aro_group']); $smarty->assign('filter_axo', $_GET['filter_axo']); $smarty->assign('filter_axo_group', $_GET['filter_axo_group']); $smarty->assign('filter_return_value', $_GET['filter_return_value']); foreach(array('aco','aro','axo','acl') as $type) { // //Grab all sections for select box // $options = array ( -1 => 'Any' ); $query = ' SELECT value,name FROM '. $gacl_api->_db_table_prefix .$type .'_sections WHERE hidden=0 ORDER BY order_value,name'; $rs = $db->Execute($query); if ( is_object($rs) ) { while ($row = $rs->FetchRow()) { $options[$row[0]] = $row[1]; } } $smarty->assign('options_filter_'. $type . '_sections', $options); if (!isset($_GET['filter_' . $type . '_section']) OR $_GET['filter_' . $type . '_section'] == '') { $_GET['filter_' . $type . '_section'] = '-1'; } $smarty->assign('filter_' . $type . '_section', $_GET['filter_' . $type .'_section']); } $smarty->assign('options_filter_allow', array('-1' => 'Any', 1 => 'Allow', 0 => 'Deny')); $smarty->assign('options_filter_enabled', array('-1' => 'Any', 1 => 'Yes', 0 => 'No')); if (!isset($_GET['filter_allow']) OR $_GET['filter_allow'] == '') { $_GET['filter_allow'] = '-1'; } if (!isset($_GET['filter_enabled']) OR $_GET['filter_enabled'] == '') { $_GET['filter_enabled'] = '-1'; } $smarty->assign('filter_allow', $_GET['filter_allow']); $smarty->assign('filter_enabled', $_GET['filter_enabled']); } $smarty->assign('action', $_GET['action']); $smarty->assign('return_page', $_SERVER['PHP_SELF']); $smarty->assign('current','acl_list'); $smarty->assign('page_title', 'ACL List'); $smarty->assign('phpgacl_version', $gacl_api->get_version()); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version()); $smarty->display('phpgacl/acl_list.tpl'); ?>phpgacl-3.3.7/admin/gacl_admin_api.class.php0100644025754300001440000000637710143550555017705 0ustar ipsousers * */ class gacl_admin_api extends gacl_api { /* * Administration interface settings */ /** @var int Number of items to display per page in the phpGACL interface. */ var $_items_per_page = 100; /** @var int Maximum number of items to display in a select box. Override to manage large collections via ACL Admin */ var $_max_select_box_items = 100; /** @var int Maximum number of items to return in an ACL Search. */ var $_max_search_return_items = 100; /* * * Misc admin functions. * */ /** * return_page() * * Sends the user back to a passed URL, unless debug is enabled, then we don't redirect. * If no URL is passed, try the REFERER * @param string URL to return to. */ function return_page($url="") { global $_SERVER, $debug; if (empty($url) AND !empty($_SERVER[HTTP_REFERER])) { $this->debug_text("return_page(): URL not set, using referer!"); $url = $_SERVER[HTTP_REFERER]; } if (!$debug OR $debug==0) { header("Location: $url\n\n"); } else { $this->debug_text("return_page(): URL: $url -- Referer: $_SERVER[HTTP_REFERRER]"); } } /** * get_paging_data() * * Creates a basic array for Smarty to deal with paging large recordsets. * * @param ADORecordSet ADODB recordset. */ function get_paging_data($rs) { return array( 'prevpage' => $rs->absolutepage() - 1, 'currentpage' => $rs->absolutepage(), 'nextpage' => $rs->absolutepage() + 1, 'atfirstpage' => $rs->atfirstpage(), 'atlastpage' => $rs->atlastpage(), 'lastpageno' => $rs->lastpageno() ); } } ?> phpgacl-3.3.7/admin/edit_objects.php0100644025754300001440000001120510011530256016300 0ustar ipsousers_db_table_prefix . 'aco'; $object_sections_table = $gacl_api->_db_table_prefix . 'aco_sections'; break; case 'aro': $object_type = 'aro'; $object_table = $gacl_api->_db_table_prefix . 'aro'; $object_sections_table = $gacl_api->_db_table_prefix . 'aro_sections'; break; case 'axo': $object_type = 'axo'; $object_table = $gacl_api->_db_table_prefix . 'axo'; $object_sections_table = $gacl_api->_db_table_prefix . 'axo_sections'; break; default: echo "ERROR: Must select an object type
\n"; exit(); break; } switch ($_POST['action']) { case 'Delete': if (count($_POST['delete_object']) > 0) { foreach($_POST['delete_object'] as $id) { $gacl_api->del_object($id, $object_type, TRUE); } } //Return page. $gacl_api->return_page($_POST['return_page']); break; case 'Submit': $gacl_api->debug_text("Submit!!"); //Update objects while (list(,$row) = @each($_POST['objects'])) { list($id, $value, $order, $name) = $row; $gacl_api->edit_object($id, $_POST['section_value'], $name, $value, $order, 0, $object_type); } unset($id); unset($section_value); unset($value); unset($order); unset($name); //Insert new sections while (list(,$row) = @each($_POST['new_objects'])) { list($value, $order, $name) = $row; if (!empty($value) AND !empty($name)) { $object_id= $gacl_api->add_object($_POST['section_value'], $name, $value, $order, 0, $object_type); } } $gacl_api->debug_text("return_page: ". $_POST['return_page']); $gacl_api->return_page($_POST['return_page']); break; default: //Grab section name $query = "select name from $object_sections_table where value = '". $_GET['section_value'] ."'"; $section_name = $db->GetOne($query); $query = "select id, section_value, value, order_value, name from $object_table where section_value='". $_GET['section_value'] ."' order by order_value"; $rs = $db->pageexecute($query, $gacl_api->_items_per_page, $_GET['page']); $rows = $rs->GetRows(); while (list(,$row) = @each($rows)) { list($id, $section_value, $value, $order_value, $name) = $row; $objects[] = array( 'id' => $id, 'section_value' => $section_value, 'value' => $value, 'order' => $order_value, 'name' => $name ); } for($i=0; $i < 5; $i++) { $new_objects[] = array( 'id' => $i, 'section_value' => NULL, 'value' => NULL, 'order' => NULL, 'name' => NULL ); } $smarty->assign('objects', $objects); $smarty->assign('new_objects', $new_objects); $smarty->assign("paging_data", $gacl_api->get_paging_data($rs)); break; } $smarty->assign('section_value', stripslashes($_GET['section_value'])); $smarty->assign('section_name', $section_name); $smarty->assign('object_type', $object_type); $smarty->assign('return_page', $_SERVER['REQUEST_URI']); $smarty->assign('current','edit_'. $object_type .'s'); $smarty->assign('page_title', 'Edit '. strtoupper($object_type) .' Objects'); $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/edit_objects.tpl'); ?> phpgacl-3.3.7/admin/acl_test.php0100644025754300001440000000113410152167553015454 0ustar ipsousersassign("return_page", $_SERVER['PHP_SELF'] ); $smarty->assign('current','acl_test'); $smarty->assign('page_title', 'ACL Test'); $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/acl_test.tpl'); ?> phpgacl-3.3.7/admin/assign_group.php0100644025754300001440000001373410314302310016345 0ustar ipsousers_db_table_prefix . 'axo'; $group_table = $gacl_api->_db_table_prefix . 'axo_groups'; $group_sections_table = $gacl_api->_db_table_prefix . 'axo_sections'; $group_map_table = $gacl_api->_db_table_prefix . 'groups_axo_map'; $object_type = 'Access eXtension Object'; break; default: $group_type = 'aro'; $table = $gacl_api->_db_table_prefix . 'aro'; $group_table = $gacl_api->_db_table_prefix . 'aro_groups'; $group_sections_table = $gacl_api->_db_table_prefix . 'aro_sections'; $group_map_table = $gacl_api->_db_table_prefix . 'groups_aro_map'; $object_type = 'Access Request Object'; break; } switch ($_POST['action']) { case 'Remove': $gacl_api->debug_text('Delete!!'); //Parse the form values //foreach ($_POST['delete_assigned_aro'] as $aro_value) { while (list(,$object_value) = @each($_POST['delete_assigned_object'])) { $split_object_value = explode('^', $object_value); $selected_object_array[$split_object_value[0]][] = $split_object_value[1]; } //Insert Object -> GROUP mappings while (list($object_section_value,$object_array) = @each($selected_object_array)) { $gacl_api->debug_text('Assign: Object ID: '. $object_section_value .' to Group: '. $_POST['group_id']); foreach ($object_array as $object_value) { $gacl_api->del_group_object($_POST['group_id'], $object_section_value, $object_value, $group_type); } } //Return page. $gacl_api->return_page($_SERVER['PHP_SELF'] .'?group_type='. $_POST['group_type'] .'&group_id='. $_POST['group_id']); break; case 'Submit': $gacl_api->debug_text('Submit!!'); //showarray($_POST['selected_'.$_POST['group_type']]); //Parse the form values //foreach ($_POST['selected_aro'] as $aro_value) { while (list(,$object_value) = @each($_POST['selected_'.$_POST['group_type']])) { $split_object_value = explode('^', $object_value); $selected_object_array[$split_object_value[0]][] = $split_object_value[1]; } //Insert ARO -> GROUP mappings while (list($object_section_value,$object_array) = @each($selected_object_array)) { $gacl_api->debug_text('Assign: Object ID: '. $object_section_value .' to Group: '. $_POST['group_id']); foreach ($object_array as $object_value) { $gacl_api->add_group_object($_POST['group_id'], $object_section_value, $object_value, $group_type); } } $gacl_api->return_page($_SERVER['PHP_SELF'] .'?group_type='. $_POST['group_type'] .'&group_id='. $_POST['group_id']); break; default: // //Grab all sections for select box // $query = 'SELECT value,name FROM '. $group_sections_table .' ORDER BY order_value,name'; $rs = $db->Execute($query); $options_sections = array(); if (is_object($rs)) { while ($row = $rs->FetchRow()) { $options_sections[$row[0]] = $row[1]; } } //showarray($options_sections); $smarty->assign('options_sections', $options_sections); $smarty->assign('section_value', reset($options_sections)); // //Grab all objects for select box // $query = 'SELECT section_value,value,name FROM '. $table .' ORDER BY section_value,order_value,name'; $rs = $db->SelectLimit($query, $gacl_api->_max_select_box_items); $js_array_name = 'options[\''. $group_type .'\']'; //Init the main aro js array. $js_array = 'var options = new Array();' . "\n"; $js_array .= $js_array_name .' = new Array();' . "\n"; unset($tmp_section_value); if (is_object($rs)) { while ($row = $rs->FetchRow()) { //list($section_value, $value, $name) = $row; $section_value = addslashes($row[0]); $value = addslashes($row[1]); $name = addslashes($row[2]); //Prepare javascript code for dynamic select box. //Init the javascript sub-array. if (!isset($tmp_section_value) OR $section_value != $tmp_section_value) { $i = 0; $js_array .= $js_array_name .'[\''. $section_value .'\'] = new Array();' . "\n"; } //Add each select option for the section $js_array .= $js_array_name .'[\''. $section_value .'\']['. $i .'] = new Array(\''. $value .'\', \''. $name ."');\n"; $tmp_section_value = $section_value; $i++; } } $smarty->assign('js_array', $js_array); $smarty->assign('js_array_name', $group_type); //Grab list of assigned Objects $query = ' SELECT b.section_value,b.value,b.name AS b_name,c.name AS c_name FROM '. $group_map_table .' a INNER JOIN '. $table .' b ON b.id=a.'. $group_type .'_id INNER JOIN '. $group_sections_table .' c ON c.value=b.section_value WHERE a.group_id='. $db->qstr($_GET['group_id']) .' ORDER BY c.name, b.name'; //$rs = $db->Execute($query); $rs = $db->PageExecute($query, $gacl_api->_items_per_page, $_GET['page']); $object_rows = array(); if (is_object($rs)) { while ($row = $rs->FetchRow()) { list($section_value, $value, $name, $section) = $row; $object_rows[] = array( 'section_value' => $row[0], 'value' => $row[1], 'name' => $row[2], 'section' => $row[3] ); } $smarty->assign('total_objects', $rs->_maxRecordCount); $smarty->assign('paging_data', $gacl_api->get_paging_data($rs)); } //showarray($aros); $smarty->assign('rows', $object_rows); //Get group name. $group_data = $gacl_api->get_group_data($_GET['group_id'], $group_type); $smarty->assign('group_name', $group_data[2]); $smarty->assign('group_id', $_GET['group_id']); break; } $smarty->assign('group_type', $group_type); $smarty->assign('object_type', $object_type); $smarty->assign('return_page', $_SERVER['REQUEST_URI'] ); $smarty->assign('current','assign_group_'. $group_type); $smarty->assign('page_title', 'Assign Group - '. strtoupper($group_type)); $smarty->assign('phpgacl_version', $gacl_api->get_version() ); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version() ); $smarty->display('phpgacl/assign_group.tpl'); ?> phpgacl-3.3.7/admin/edit_group.php0100644025754300001440000000573410153012552016016 0ustar ipsousers_db_table_prefix . 'axo_groups'; break; default: $group_type = 'aro'; $group_table = $gacl_api->_db_table_prefix . 'aro_groups'; break; } switch ($_POST['action']) { case 'Delete': $gacl_api->debug_text('Delete'); if (count($_POST['delete_group']) > 0) { //Always reparent children when deleting a group. foreach ($_POST['delete_group'] as $group_id) { $gacl_api->debug_text('Deleting group_id: '. $group_id); $result = $gacl_api->del_group($group_id, TRUE, $group_type); if ($result == FALSE) { $retry[] = $group_id; } } if (count($retry) > 0) { foreach($retry as $group_id) { $gacl_api->del_group($group_id, TRUE, $group_type); } } } //Return page. $gacl_api->return_page($return_page); break; case 'Submit': $gacl_api->debug_text('Submit'); if (empty($_POST['parent_id'])) { $parent_id = 0; } else { $parent_id = $_POST['parent_id']; } //Make sure we're not reparenting to ourself. if (!empty($_POST['group_id']) AND $parent_id == $_POST['group_id']) { echo "Sorry, can't reparent to self!
\n"; exit; } //No parent, assume a "root" group, generate a new parent id. if (empty($_POST['group_id'])) { $gacl_api->debug_text('Insert'); $insert_id = $gacl_api->add_group($_POST['value'], $_POST['name'], $parent_id, $group_type); } else { $gacl_api->debug_text('Update'); $gacl_api->edit_group($_POST['group_id'], $_POST['value'], $_POST['name'], $parent_id, $group_type); } $gacl_api->return_page($return_page); break; default: //Grab specific group data if (!empty($_GET['group_id'])) { $query = ' SELECT id,parent_id,value,name FROM '. $group_table .' WHERE id='. (int)$_GET['group_id']; list($id, $parent_id, $value, $name) = $db->GetRow($query); //showarray($row); } else { $parent_id = $_GET['parent_id']; $value = ''; $name = ''; } $smarty->assign('id', $id); $smarty->assign('parent_id', $parent_id); $smarty->assign('value', $value); $smarty->assign('name', $name); $smarty->assign('options_groups', $gacl_api->format_groups($gacl_api->sort_groups($group_type))); break; } $smarty->assign('group_type', $group_type); $smarty->assign('return_page', $return_page); $smarty->assign('current','edit_'. $group_type .'_group'); $smarty->assign('page_title', 'Edit '. strtoupper($group_type) .' Group'); $smarty->assign('phpgacl_version', $gacl_api->get_version()); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version()); $smarty->display('phpgacl/edit_group.tpl'); ?>phpgacl-3.3.7/admin/group_admin.php0100644025754300001440000000405310076265645016173 0ustar ipsousers_db_table_prefix . 'axo_groups'; $group_map_table = $gacl_api->_db_table_prefix . 'groups_axo_map'; $smarty->assign('current','axo_group'); break; default: $group_type = 'aro'; $group_table = $gacl_api->_db_table_prefix . 'aro_groups'; $group_map_table = $gacl_api->_db_table_prefix . 'groups_aro_map'; $smarty->assign('current','aro_group'); break; } switch ($_POST['action']) { case 'Delete': //See edit_group.php break; default: $formatted_groups = $gacl_api->format_groups($gacl_api->sort_groups($group_type), HTML); $query = ' SELECT a.id, a.name, a.value, count(b.'. $group_type .'_id) FROM '. $group_table .' a LEFT JOIN '. $group_map_table .' b ON b.group_id=a.id GROUP BY a.id,a.name,a.value'; $rs = $db->Execute($query); $group_data = array(); if(is_object($rs)) { while($row = $rs->FetchRow()) { $group_data[$row[0]] = array( 'name' => $row[1], 'value' => $row[2], 'count' => $row[3] ); } } $groups = array(); foreach($formatted_groups as $id => $name) { $groups[] = array( 'id' => $id, // 'parent_id' => $parent_id, // 'family_id' => $family_id, 'name' => $name, 'raw_name' => $group_data[$id]['name'], 'value' => $group_data[$id]['value'], 'object_count' => $group_data[$id]['count'] ); } $smarty->assign('groups', $groups); break; } $smarty->assign('group_type', $group_type); $smarty->assign('return_page', $_SERVER['REQUEST_URI']); $smarty->assign('current', $group_type .'_group'); $smarty->assign('page_title', strtoupper($group_type) .' Group Admin'); $smarty->assign('phpgacl_version', $gacl_api->get_version()); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version()); $smarty->display('phpgacl/group_admin.tpl'); ?> phpgacl-3.3.7/admin/admin_functions.js0100644025754300001440000001562710045262773016700 0ustar ipsousers/* * phpGACL - Generic Access Control List * Copyright (C) 2002,2003 Mike Benoit * * 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 * * For questions, help, comments, discussion, etc., please join the * phpGACL mailing list. http://sourceforge.net/mail/?group_id=57103 * * You may contact the author of phpGACL by e-mail at: * ipso@snappymail.ca * * The latest version of phpGACL can be obtained from: * http://phpgacl.sourceforge.net/ * */ var selectedTab = null; //Function to totally clear a select box. function depopulate(form_element) { if (form_element.options.length > 0) { form_element.innerHTML = ''; } } //Populates a select box based off the value of "parent" select box. function populate(parent_form_element, child_form_element, src_array) { //alert('Parent: ' + parent_form_element); //alert('Child: ' + child_form_element); if (parent_form_element.selectedIndex >= 0) { //Grab the current selected value from the parent parent_id = parent_form_element.options[parent_form_element.selectedIndex].value; //Clear the child form element depopulate(child_form_element); //Populate child form element if (options[src_array][parent_id]) { for (i=0; i < options[src_array][parent_id].length; i++) { child_form_element.options[i] = new Option(options[src_array][parent_id][i][1], options[src_array][parent_id][i][0]); } } } } //Select an item by "copying" it from one select box to another function select_item(parent_form_element, src_form_element, dst_form_element) { //alert('Src: ' + src_form_element); //alert('Dst: ' + dst_form_element); found_dup=false; //Copy it over to the dst element for (i=0; i < src_form_element.options.length; i++) { if (src_form_element.options[i].selected) { //Check to see if duplicate entries exist. for (n=0; n < dst_form_element.options.length; n++) { if ( parent_form_element.options[parent_form_element.selectedIndex].value + '^' + src_form_element.options[i].value == dst_form_element.options[n].value) { found_dup=true; } } //Only add if its not a duplicate entry. if (!found_dup) { //Grab the current selected value from the parent src_id = src_form_element.options[i].value; src_text = src_form_element.options[i].text; src_section_id = parent_form_element.options[parent_form_element.selectedIndex].value; src_section_text = parent_form_element.options[parent_form_element.selectedIndex].text; options_length = dst_form_element.options.length; dst_form_element.options[options_length] = new Option(src_section_text + ' > ' + src_text, src_section_id + '^' + src_id); dst_form_element.options[options_length].selected = true; } } found_dup=false; } } //Used for moving items to and from the selected combo box. function deselect_item(form_element) { //alert('Src: ' + src_form_element); //alert('Dst: ' + dst_form_element); //Copy it over to the dst element for (i=0; i < form_element.options.length; i++) { if (form_element.options[i].selected) { form_element.options[i] = null; i=i - 1; } } } //Used to unselect all items in a combo box function unselect_all(form_element) { for (i=0; i < form_element.options.length; i++) { form_element.options[i].selected = false; } } function select_all(select_box) { for (i=0; i < select_box.options.length; i++) { select_box.options[i].selected = true; } } function edit_link(link, parent_id) { alert('edit_aco.php?section_id=' + parent_id + '&return_page={$return_page}') } function toggleObject(objectID) { if(document.getElementById) { if(document.getElementById(objectID).className == 'hide') { showObject(objectID); } else { hideObject(objectID); } } } function showObject(objectID) { if(document.getElementById) { document.getElementById(objectID).className = 'show'; } } function hideObject(objectID) { if(document.getElementById) { document.getElementById(objectID).className = 'hide'; } } function showTab(objectID) { if(document.getElementById) { if(selectedObject != objectID) { document.getElementById(objectID).className = 'tabon'; selectedTab = objectID; } } } function hideTab() { if(document.getElementById) { if(selectedTab) { document.getElementById(selectedTab).className = 'taboff'; } } } function checkAll(checkbox) { for (i=0; idebug_text('Submit!!'); //$result = $gacl_api->acl_query('system', 'email_pw', 'users', '1', NULL, NULL, NULL, NULL, TRUE); $result = $gacl_api->acl_query( $_GET['aco_section_value'], $_GET['aco_value'], $_GET['aro_section_value'], $_GET['aro_value'], $_GET['axo_section_value'], $_GET['axo_value'], $_GET['root_aro_group_id'], $_GET['root_axo_group_id'], TRUE); //Grab all relavent columns $result['query'] = str_replace( 'a.id,a.allow,a.return_value', ' a.id, a.allow, a.return_value, a.note, a.updated_date, ac.section_value as aco_section_value, ac.value as aco_value, ar.section_value as aro_section_value, ar.value as aro_value, ax.section_value as axo_section_value, ax.value as axo_value', $result['query']); $rs = $gacl_api->db->Execute($result['query']); if (is_object($rs)) { while ($row = $rs->FetchRow()) { list( $id, $allow, $return_value, $note, $updated_date, $aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value ) = $row; $acls[] = array( 'id' => $id, 'allow' => $allow, 'return_value' => $return_value, 'note' => $note, 'updated_date' => date('d-M-y H:m:i',$updated_date), 'aco_section_value' => $aco_section_value, 'aco_value' => $aco_value, 'aro_section_value' => $aro_section_value, 'aro_value' => $aro_value, 'axo_section_value' => $axo_section_value, 'axo_value' => $axo_value, ); } } //echo "

$x ACL_CHECK()'s
\n"; $smarty->assign('acls', $acls); $smarty->assign('aco_section_value', $_GET['aco_section_value']); $smarty->assign('aco_value', $_GET['aco_value']); $smarty->assign('aro_section_value', $_GET['aro_section_value']); $smarty->assign('aro_value', $_GET['aro_value']); $smarty->assign('axo_section_value', $_GET['axo_section_value']); $smarty->assign('axo_value', $_GET['axo_value']); $smarty->assign('root_aro_group_id', $_GET['root_aro_group_id']); $smarty->assign('root_axo_group_id', $_GET['root_axo_group_id']); break; default: break; } $smarty->assign('return_page', $_SERVER['PHP_SELF']); $smarty->assign('current','acl_debug'); $smarty->assign('page_title', 'ACL Debug'); $smarty->assign('phpgacl_version', $gacl_api->get_version()); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version()); $smarty->display('phpgacl/acl_debug.tpl'); ?>phpgacl-3.3.7/admin/acl_admin.php0100644025754300001440000002064110016761203015560 0ustar ipsousersdebug_text('Submit!!'); //showarray($_POST['selected_aco']); //showarray($_POST['selected_aro']); //Parse the form values foreach (array('aco','aro','axo') as $type) { $type_array = 'selected_'. $type .'_array'; $$type_array = array(); if (is_array($_POST['selected_'. $type])) { foreach ($_POST['selected_'. $type] as $value) { $split_value = explode('^', $value); ${$type_array}[$split_value[0]][] = $split_value[1]; } } //showarray($$type_array); } //Some sanity checks. if (empty($selected_aco_array)) { echo 'Must select at least one Access Control Object
' . "\n"; exit; } if (empty($selected_aro_array) AND empty($_POST['aro_groups'])) { echo 'Must select at least one Access Request Object or Group
' . "\n"; exit; } $enabled = $_POST['enabled']; if (empty($enabled)) { $enabled = 0; } //function add_acl($aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $acl_id=FALSE ) { if (!empty($_POST['acl_id'])) { //Update existing ACL $acl_id = $_POST['acl_id']; if ($gacl_api->edit_acl($acl_id, $selected_aco_array, $selected_aro_array, $_POST['aro_groups'], $selected_axo_array, $_POST['axo_groups'], $_POST['allow'], $enabled, $_POST['return_value'], $_POST['note'], $_POST['acl_section']) == FALSE) { echo 'ERROR editing ACL, possible conflict or error found...
' . "\n"; exit; } } else { //Insert new ACL. if ($gacl_api->add_acl($selected_aco_array, $selected_aro_array, $_POST['aro_groups'], $selected_axo_array, $_POST['axo_groups'], $_POST['allow'], $enabled, $_POST['return_value'], $_POST['note'], $_POST['acl_section']) == FALSE) { echo 'ERROR adding ACL, possible conflict or error found...
' . "\n"; exit; } } $gacl_api->return_page($_POST['return_page']); break; default: //showarray($_GET); if ($_GET['action'] == 'edit' AND !empty($_GET['acl_id'])) { $gacl_api->debug_text('EDITING ACL'); //Grab ACL information $query = ' SELECT id,section_value,allow,enabled,return_value,note FROM '. $gacl_api->_db_table_prefix .'acl WHERE id='. $db->qstr($_GET['acl_id']); $acl_row = $db->GetRow($query); list($acl_id, $acl_section_value, $allow, $enabled, $return_value, $note) = $acl_row; //Grab selected objects foreach (array('aco','aro','axo') as $type) { $type_array = 'options_selected_'. $type; $$type_array = array(); $query = ' SELECT a.section_value,a.value,c.name,b.name FROM '. $gacl_api->_db_table_prefix . $type .'_map a INNER JOIN '. $gacl_api->_db_table_prefix . $type .' b ON b.section_value=a.section_value AND b.value=a.value INNER JOIN '. $gacl_api->_db_table_prefix . $type .'_sections c ON c.value=a.section_value WHERE a.acl_id='. $db->qstr($acl_id); $rs = $db->Execute($query); if (is_object($rs)) { while ($row = $rs->FetchRow()) { list($section_value, $value, $section, $obj) = $row; $gacl_api->debug_text("Section Value: $section_value Value: $value Section: $section ACO: $aco"); ${$type_array}[$section_value.'^'.$value] = $section.' > '.$obj; } } //showarray($$type_array); } //Grab selected groups. foreach (array('aro','axo') as $type) { $type_array = 'selected_'. $type .'_groups'; $query = ' SELECT group_id FROM '. $gacl_api->_db_table_prefix . $type .'_groups_map WHERE acl_id='. $db->qstr($acl_id); $$type_array = $db->GetCol($query); //showarray($$type_array); } $show_axo = (!empty($selected_axo_groups) OR !empty($options_selected_axo)); } else { $gacl_api->debug_text('NOT EDITING ACL'); $allow=1; $enabled=1; $acl_section_value='user'; $show_axo = isset($_COOKIE['show_axo']) && $_COOKIE['show_axo'] == '1'; } //Grab sections for select boxes foreach (array('acl','aco','aro','axo') as $type) { $type_array = 'options_'. $type .'_sections'; $$type_array = array(); $query = ' SELECT value,name FROM '. $gacl_api->_db_table_prefix . $type .'_sections WHERE hidden=0 ORDER BY order_value,name'; $rs = $db->Execute($query); if (is_object($rs)) { while ($row = $rs->FetchRow()) { ${$type_array}[$row[0]] = $row[1]; } } ${$type .'_section_id'} = reset($$type_array); } //Init the main js array $js_array = 'var options = new Array();' . "\n"; //Grab objects for select boxes foreach (array('aco','aro','axo') as $type) { //Init the main object js array. $js_array .= 'options[\''. $type .'\'] = new Array();' . "\n"; unset($tmp_section_value); $query = ' SELECT section_value,value,name FROM '. $gacl_api->_db_table_prefix . $type .' WHERE hidden=0 ORDER BY section_value,order_value,name'; $rs = $db->SelectLimit($query,$gacl_api->_max_select_box_items); if (is_object($rs)) { while ($row = $rs->FetchRow()) { $section_value = addslashes($row[0]); $value = addslashes($row[1]); $name = addslashes($row[2]); //Prepare javascript code for dynamic select box. //Init the javascript sub-array. if (!isset($tmp_section_value) OR $section_value != $tmp_section_value) { $i = 0; $js_array .= 'options[\''. $type .'\'][\''. $section_value . '\'] = new Array();' . "\n"; $tmp_section_value = $section_value; } //Add each select option for the section $js_array .= 'options[\''. $type .'\'][\''. $section_value .'\']['. $i .'] = new Array(\''. $value . '\', \''. $name . "');\n"; $i++; } } } //echo "Section ID: $section_id
\n"; //echo "Section Value: ". $acl_section_value ."
\n"; $smarty->assign('options_acl_sections', $options_acl_sections); $smarty->assign('acl_section_value', $acl_section_value); $smarty->assign('options_axo_sections', $options_axo_sections); $smarty->assign('axo_section_value', $axo_section_value); $smarty->assign('options_aro_sections', $options_aro_sections); $smarty->assign('aro_section_value', $aro_section_value); $smarty->assign('options_aco_sections', $options_aco_sections); $smarty->assign('aco_section_value', $aco_section_value); $smarty->assign('js_array', $js_array); $smarty->assign('js_aco_array_name', 'aco'); $smarty->assign('js_aro_array_name', 'aro'); $smarty->assign('js_axo_array_name', 'axo'); //Grab formatted ARO Groups for select box $smarty->assign('options_aro_groups', $gacl_api->format_groups($gacl_api->sort_groups('ARO')) ); $smarty->assign('selected_aro_groups', $selected_aro_groups); //Grab formatted AXO Groups for select box $smarty->assign('options_axo_groups', $gacl_api->format_groups($gacl_api->sort_groups('AXO')) ); $smarty->assign('selected_axo_groups', $selected_axo_groups); $smarty->assign('allow', $allow); $smarty->assign('enabled', $enabled); $smarty->assign('return_value', $return_value); $smarty->assign('note', $note); if (isset($options_selected_aco)) { $smarty->assign('options_selected_aco', $options_selected_aco); } $smarty->assign('selected_aco', @array_keys($options_selected_aco)); if (isset($options_selected_aro)) { $smarty->assign('options_selected_aro', $options_selected_aro); } $smarty->assign('selected_aro', @array_keys($options_selected_aro)); if (isset($options_selected_axo)) { $smarty->assign('options_selected_axo', $options_selected_axo); } $selected_axo = @array_keys($options_selected_axo); $smarty->assign('selected_axo', $selected_axo); //Show AXO layer if AXO's are selected. $smarty->assign('show_axo', $show_axo); if (isset($_GET['acl_id'])) { $smarty->assign('acl_id', $_GET['acl_id'] ); } break; } //$smarty->assign('return_page', urlencode($_SERVER[REQUEST_URI]) ); if (isset($_GET['return_page'])) { $smarty->assign('return_page', $_GET['return_page']); } if (isset($_GET['action'])) { $smarty->assign('action', $_GET['action']); } $smarty->assign('current','acl_admin'); $smarty->assign('page_title', 'ACL Admin'); $smarty->assign('phpgacl_version', $gacl_api->get_version() ); $smarty->assign('phpgacl_schema_version', $gacl_api->get_schema_version() ); $smarty->display('phpgacl/acl_admin.tpl'); ?>phpgacl-3.3.7/admin/index.php0100644025754300001440000000005310034251201014743 0ustar ipsousersphpgacl-3.3.7/admin/images/0040755025754300001440000000000010476665053014425 5ustar ipsousersphpgacl-3.3.7/admin/images/tab-off-r.png0100644025754300001440000000236210011246262016666 0ustar ipsousersPNG  IHDR"d$gAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbIKKc`Pprr*(((++ĸo߾ý{߿@,?~ Q0 d~ݻpϟ޽7B /_(߿?~i4DF(Qׯ_N@fQ0 h l6 Fm3l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lVkVIENDB`phpgacl-3.3.7/admin/images/bot-l.png0100644025754300001440000000154310011246262016126 0ustar ipsousersPNG  IHDRdևgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb? ϯ_$ &R@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ TȽp4@LX@,h|`O?@hEfddjNNί_211AH" ү^T $ PTۯ[N[[ׯ_?x PT]~Ν;BBB?~{=DDD߿` F4>/^ؿ }E  dee711u?}t0p|@u@60p={t!@,XH.$WJHd H8(IENDB`phpgacl-3.3.7/admin/images/tab-off-l.png0100644025754300001440000000125710011246262016662 0ustar ipsousersPNG  IHDR d06gAMA7tEXtSoftwareAdobe ImageReadyqe<AIDATxbܽ{޽ ˷o߶o߮߿åOWWW[[(ׯ? K]\\$4@ׯ߿v uUI49  & f@1YX/ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ HTe[IENDB`phpgacl-3.3.7/admin/images/spacer.gif0100644025754300001440000000005310011246262016342 0ustar ipsousersGIF89a!,D;phpgacl-3.3.7/admin/images/tab-on-r.png0100644025754300001440000000234310011246262016527 0ustar ipsousersPNG  IHDR"d$gAMA7tEXtSoftwareAdobe ImageReadyqe<uIDATxbIKKc`Pprr*(((++Çǎۺu;@(^^^߿{.@1(4/_>@fQ0 h>}ӷo F(4ϟlf`߿h4Q@$h4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 F(44F(9l6 F@fQ0 hh4Q@s@lf`h6 h9IENDB`phpgacl-3.3.7/admin/images/logo.png0100644025754300001440000002142010011246262016045 0ustar ipsousersPNG  IHDR0>eggAMA7tEXtSoftwareAdobe ImageReadyqe<"IDATxbܽ{7( ?~3RLb !X@)cyvJVh2G~e& W-?ߌLTIb 衑cXv {Y<7_?~g/3ßo@2EC0TJ0ؘ8s~c9 }x/5?MDoH: >fffL1雃*j!2X߿?`gcJ OF/'`=$?L_jGV $66 ~>Im޽wއ"(( q*c?v0qHJJLx} *2޼ys CAAAVV>(ƓXndrm~8i_'Y6_?0hI@q h ?p~OLh[pΝ;_xA0/_Q5y_reɒ%gϞ%&' ͛7/^(ӣ÷,sL1~o`C bPg!8[OA h μvɩ>P6|ܹs$i⁛6lp=z_=050ٳXb7QJJ fDH3U]b1XXX&n[o\\<sH= o@0_vp!/+3מ p~17sGa 0 )Y,9"'^bBa!)ZX*͒",,! gwPߍ}߇9gtRZ Re>B!؎~Mp,!R?oY(Zsvqa \$'O4KR[icDpܶn<W7˪uWn`j`<8"0bCT`Ӑ@`L +W$foZ , 9&`ZYfAXS0q&^ # 'd5}=ٟe ?9ja17Ad csiasٸˉOXÇ8I@ aIA"fOP#KFuqtvPWB-i/\.|9 1H8p Cw\<ϙɗ( h@X+WU6 )0F y+E+:96/eY&B$*dx|FFPUTBbZ)8uB:$t0>:=_z;嬃P.:IӪ,r%wlx]۽8#] V}.|e>Uǿ,L O3 4!ԪLw1~YQ8A<1.T APt* &JTbܩo@!R~IJpF `Pf}Jat؁K'!]!TPNYMk-E"T DcenZOScL Xkc?Qb`~7% 褬uU=``1}񦶇jGzKg T8 r\*&Kx7=| ƍph4!0@ ''L@A`M qYC#RCZ`$x%l t$е$)3@Y>>Sb@677BfSI? xerO!mI`*D+2>% `ƪ?}e6 l@(qUNE?, >e_y؁l慍n1;u&h K U֚DHҰ=0RZEE3 uuu`5wAqiԵ؅@5 ]6TA遥˂ Vk`G.)|!jw4}j5 P@cSn:g&o?m 'DL扻2٨+VVpKUU~%hR7&\Mb>x@x̚X %<|*R) S0:X@ *.VԀ5=Hz*0AVVV#Z2&, (`A4>@\웙Q& $9|-%?O30o>g$L ?~3fu` cf0 <7aTtizŧzN"y&֦\mThll= \U&R9@H~!fPs JhY /LĶ_/vǑaYky 4 e|k1+lj@*l`hhh? `011!'Mր޲̐OF`[ A*+gM˵gl , i 3!D`m@axV 02sxD;f逗"Dez Z`LC1Ps]MHeyfǁ 0`Άdh}Xorn<݁a${~ l"?x 9bV+?0L`.Z!s(5a Sp]t7{{y#HEk ]&Lf޼˾Ml"e1?:U-T6bMuY#(E:/V6>kwˇI,Cl!VJ9se *ձx$ۑޟde:[<>/JOCPY ~=z0AN$֖ @ PV[023DS.A@9 ``U- /|)BcqmIO"9b"e y,j\?UeBU'^Oc $ƨ*O@LWcҘnH.Fj]whCzv|e3܊<0]{lQvs=97< "o5ca c.85j L'YXy VzIcBHihwT,&~NLCintm 6t>,IEDHG`֍1u c[۶ }W6r9k<'iBEQI PJu]H3_%a- j2֔9^g?8r{/TC:1L ;\| Z,Q-:~8. ¬, PJzoOF0Xv!7G) "!s@bb"0c4`54y9SP"`3qD 1G =O X ],ҀqxCE  H4vҤIW,YlP2_2Xs7XS5S2'3#?_Ȟ &4s00 b`L46690BR0¬/ Zx;Xe$ S0@`aaauhk" `1Y`d@k xHlv$$$'`L̳` b/>183h ])ȥ1jfFd^FvQ1!E-6Ƹ6cG4\p ظNB 1 fwCI?3LW]`- @`%R 䬵FKRH`}(]>fcR;'yH ē#^J?؀}|.Wicc^0s佼kZ彇mxՃ83и40^o r$21,ǡcnô*sp^.`%@vפB縸8`# w0#0D X0 -\*MYYdj̙u2w6XAF0h&d$ R "'}>-;ƀ.xO ^l ^|>a0FL_Fi? Sބ0A|0M * ڜOl@cm!cȋCyS @Dkh  %Gs&0k8/ڀ[̯gbu1~eVlD%j` И&`1#'4@,9^z} $"X!&b d۳g` {Z LN)}`49@Z-H 6-e[g杗9=dcb,?~c#f&ߘ`S_.Z?FbB^l@@ =`~GZ7 C>|)!IHBJ`E@A]t 8 ppxuANl~a2FdrP()7ꋬF`|; @ rK%R6[XYY{Ńx A"^ )$U>X3-]QU@Р cƘ$I`ICQm*?휍15lǮi<O}뺮+͏ 0%Ya*U+p-גJ+k Lw kuX;!麮{Bn UtyD<σ `"˲$Fmqu(RN4u 8q#cqN9_|̿Gykݟb|o@4 QE)rp ȒH.\ rX"g"MdmƻwͼĢ(D Ef/넒v4R 0tDnj& lpA=hjh{t &"۶PAh!m4ȵ˄62KLRopp8y.ʳ,vI/$@cN-UUi캎~7(Egy1`.Q 85Y+a n=Ku]r,qXruq:Cr=?wNc~pO.5B(LH`Vl rM`c#X* bP@4!`cϻ뽈j ޣ`GuEu]8>X (@d zW%,jB8eYQRaYu;MӾӨ3Id&HyxdȲ8Fa@8q 0ZE,NeI*7![$|!>4hEu]Sp!#۶*(1bcAy i8b80<y۶ PWc m[^ߏ$c|\뛋zp̑m:^ľs  @٫FQe5`-YvK FBlm}[|KAH'4鲰 Xح,9s\;\:Mm`u pypA4:F Feq](Ⱥ(urïi0  <0r(e8 *ƃ Kka^$mQ!+@<τ) @* 9f̄8xwtH@b0$a bjQ2řK`HT.iX:!u&LJhK ,+r~9h ډ/ s|zzVw)+?A{;q.W*]__(mda" G?WY7I YF)&fz^owp "%Y4W$k< iUht)IaD_PڶB 5.c\?UU!:8.aOA gE~=P*T, CY]3 8d18˲p/* яM7-8֊Tpo^.x񙇃b7EJy uMOynٗir<4<ƔdUbqus>n_Gj#w13h!pU:2M{! ڤ!c-[6d3fb@&u](@.JrIM9'Q#x#)c:vCJ`[׵fUUH${Wp|6a aE}8 j-)qJ Հ"^CrC4ljIcӗGn]e,<`[m[6yfVّxi}dQj.xZO״+c)"Ɏt @`I8xCt@A@B$gDԣ073o"Hn4IdEa~ nMӬRv@~0]PzHj0@dHy68l8mJ1s:1jF0iLݮHo \TcX·C<#Bɲ,0LV9*yk}̶P_snCheVmK`wu?Ta5">;!G,-FwKZ_gYN<^hBu돶8zB"q$ :\w&?~cqk{L};-Ґ򉍅Gx -=Tb=z`%D0u-6V$ _T~M|(MWIژA@AQ'Oܒ`W4ZB O^ J%d1> FK A)B 3u#IENDB`phpgacl-3.3.7/admin/images/div-r.png0100644025754300001440000000023210011246262016124 0ustar ipsousersPNG  IHDR11gAMA7tEXtSoftwareAdobe ImageReadyqe<,IDATx1 0TDMK@_IvwfmbIENDB`phpgacl-3.3.7/admin/images/top-r.png0100644025754300001440000001701110034347573016163 0ustar ipsousersPNG  IHDR1j3gAMA7tEXtSoftwareAdobe ImageReadyqe<PLTEfffũKKKItǡTz===-bͼ999)))]]]UUU3f{ã;k˙|||pppiiicƍ%] BBBuuummm%%%111*ayyym!!!---555YYY0dEEEaaaQQQ̛rļ/d\dz1ȇcccggg7i5gBo˺hkIDATxb``Q0 0`Q0 4ZcQ0 F( h1 F(8@ L`Q0 F 1uQ0 F(jʢUT A5@1Q0 F(8AYf4j  Q0 F(7d6w+gd Q0 F((**W /g Q0 F('W ̯ Q0 F(PV1`-Ѱ`Q *S?6.ȒW FiQ0 F60fP(3cHUcpg1YJhXQ0 Fz٢fF8k ތ<&qz Q0 F(FQ COL FCkQ0 Pu&V1&k NAQ0 F(L@!XY(\ȀD5VLU!D((M,q)Sch쌂Q0 F 2JJ6\Da I_\\`fNARah-..ɐX,_meb"l .@3 F(qpp18P4BV* e,焬6PWJN(SO$AAT! o@3 F(Hksp8qa{pKIDs9f0O3s 5DX8*1h4zF(`N  NB글w䁦d~ +V0 psp#)VaV U)•f{Xvek Q0 F(<@ u< 2U{we)oxa HgrpdF"ZcRYn]chQ0 F@ (t+@%9G)pu88p(aabQAg`VIB1R4[`Q0pd2q*i-, .t!T.bn&+GJvU8B8P S{0Dm=E4a`Q0P9ET <ŀI?HFZ Ҁ Gq{ >> XTЗpe[lPĒ3^E|(d4m%o?@(` T)JL0eMGvR=)` R ST"fj$ճR FlQ0 H0ya@bAҒOcx{0B8<$ ]9U Z(VKksD3;$¢V:f& ¸j Q0 F( m ,]A@#r`1uLi,=T($ǥ+a/`ׂ :>/G$/x1A & "" F& j Q0 F(`a+ \RB^X($]Ճ.^EJ(+q0(J~-V* d*s:ˡP!":0FQ VDH`-Yi`o" 0v8 J.Ƒi {0T8Ƥ@PH%uJS; (ČBTSQ$۬RH`OqeM\rJ_U̠xC_ T!ke$l!-2Mc.իVmΖu55v-4i`Q0 tT'RC\@7I@, `P`4L hcxL$;h.l!-@Lcy&& &f "¬:EG' Xod4i`Q0)<\x%b WMc@$]AߠDPa8` Ƞf1 y 1@عԑyyyjox@6 F(PNXĬAt98\JJ$GD==P#$- R @AJXaz{* O U &l5@(`  ŧ`NBD 1tPIQ\L /s.h#P Ջq0( БII5PLk Q0 F(>A 22!$ @ `%j Z:#H Bg*$9ԣ 9981FU"& F: C6OTLS'DQX`c!r+ .XHB ^Jg9JƕZxH2X 7IOOOp0F4m`Q@3␓ncRb`RR̅냶UxqIT9C/X \nSSTDtTų8$T[2pr^Nyov0 Б7J4o`Q@Y5+ 5Y;Ȅ cAa`FUT@ SY5! `f5s Ia@1A8$EGG1^chQ0 FdG7@Kj@$l9UQ,B4o`Q@ -Ơ lC1,[" NF{5n F2DWHI/B0 5HDx`C {{_C}8c}Jeu#@IᄌdfV5VAY,ʼn3 S $b!]1qҒеH@T3E^chčQ0 FkpX ?99f IIpCCfT(* jA\9IN]'T ВV3dq1p&q2Hjyq꺸HX K8@vhk Q0 F( lk̩fUTeT@4%$] -]☁j]$U]; 8%yt\be,bdc%bܮb vJ&.X3MC[_BSGG@6 F($ܢtU䢅ͭ`lg !~(gm})A \ZrBB*rrBBrvZ~.\墁}(u!S sm? Ic' !_ m4.'3P/H0\)``b,!Xx& +@Q6 F(8Mc%}5!lkIf !-I~@;eS~; )I&u)if%S)pI*)~q %5l]M㢓@b<9us$y4,@zŲL5M @Gή519QϮ8`Ʀn2JrrrZ*|` i9w $XMv>Sr6R֚A017]`0fi-!^)k.}~a`Q, Zmce|!@(` S5WeQfpW1΂,|L $ =]3L9N)XJMW3P+tVtyOy J7j Q0 F(//£eKK j%qJ8Ii[Y' d)hdx20jkh{Ȩk+"N%( ` 7]]!!WkHEP1T?L u(8`rrrZƺ.bP6Wu's^^\jH \`,Dqke1zi[~x7n1^h4F(`@ JVoo\ x@a4 F(ReMb 7 #vk  Q0 F(Xie&1h4tF(` W&&j  Q0 F(xѠ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j  Q0 F(4`Q0 1h4(F(`WchPQ0 F( Ѡ`Q@\@A1 F( FbQ0 Fq5@(`j nӧfycIENDB`phpgacl-3.3.7/admin/images/mid-l.png0100644025754300001440000000113410011246262016107 0ustar ipsousersPNG  IHDRbgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb@  Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @0E+0IENDB`phpgacl-3.3.7/admin/images/bot-r.png0100644025754300001440000000540310011246262016133 0ustar ipsousersPNG  IHDR1dHLgAMA7tEXtSoftwareAdobe ImageReadyqe< IDATx10b @8fԊ"qv=3nfWvaP2bH`v8KرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcAֳU9)n  ŮIpKرcaU3{%'X~c6 ښttw3pR|I2urj`ǎi`=צiaO Q~ر G&PU_xlftwU;C(B1@x_X[&~t$U3s};DaHaieeѕv&~j/?jowS};4  8)I%ܻݪ̈8C4D! ^4M\7S:nftkU}sF W"Qx 9ݙz{3С 0Q`1(97v{fݙ""*3̮С@&ǿk4޳sνwf|`iB &  _ *ۑ0owU%if;3`ma)I{aІQ$U3RLGv '\-+%y-)"ϟGU={ 0&[d.IENDB`phpgacl-3.3.7/admin/images/mid-r.png0100644025754300001440000000467610011246262016133 0ustar ipsousersPNG  IHDR1bQfQgAMA7tEXtSoftwareAdobe ImageReadyqe< PIDATx1@A10~UtK{sf#2:`̿@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@vX`,vFN [cg0rR@SIENDB`phpgacl-3.3.7/admin/images/tab-on-l.png0100644025754300001440000000117610011246262016524 0ustar ipsousersPNG  IHDR d06gAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxbܽ{޽ ƍn X|}}=<<b5@L?% @L x@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" @4@ D@ Hi" `2rIENDB`phpgacl-3.3.7/admin/images/div-l.png0100644025754300001440000000020510011246262016116 0ustar ipsousersPNG  IHDRcmgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb? 3x49`IENDB`phpgacl-3.3.7/admin/images/top-l.png0100644025754300001440000000236110011246262016143 0ustar ipsousersPNG  IHDRY,gAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb? HDj"M5@`z>|dqbDׯ_Tgee%))߿` t+V+ ?0O:u]߿_ $n4ȅ PT?}T@@* Ȫ͋@@† Cb'!@HC wz\"LpE5d@"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj"M5@ HS @ Ti4Dj*׽~IENDB`phpgacl-3.3.7/admin/about.php0100644025754300001440000000566210011530256014766 0ustar ipsousersget_version()."\n"; $system_info .= ' phpGACL Schema Version: '.$gacl_api->get_schema_version()."\n"; if($gacl_api->_caching == TRUE) { $caching = 'True'; } else { $caching = 'False'; } $system_info .= ' Caching Enabled: '. $caching ."\n"; if($gacl_api->_force_cache_expire == TRUE) { $force_cache_expire = 'True'; } else { $force_cache_expire = 'False'; } $system_info .= ' Force Cache Expire: '.$force_cache_expire."\n"; $system_info .= ' Database Prefix: \''.$gacl_api->_db_table_prefix."'\n"; $system_info .= ' Database Type: '.$gacl_api->_db_type."\n"; $database_server_info = $gacl_api->db->ServerInfo(); $system_info .= ' Database Version: '.$database_server_info['version']."\n"; $system_info .= ' Database Description: '.$database_server_info['description']."\n\n"; $system_info .= 'Server Name: '. $_SERVER["SERVER_NAME"] ."\n"; $system_info .= ' OS: '. PHP_OS ."\n"; $system_info .= ' IP Address: '. $_SERVER["REMOTE_ADDR"] ."\n"; $system_info .= ' Browser: '. $_SERVER["HTTP_USER_AGENT"] ."\n\n"; $system_info .= 'System Information: '. php_uname() ."\n"; return trim($system_info); } function submit_system_info($system_information, $system_info_md5) { $md5sum = md5(trim($system_information)); if (trim($system_info_md5) == $md5sum) { $tainted = 'FALSE'; } else { $tainted = 'TRUE'; } mail('phpgacl@snappymail.ca', 'phpGACL Report... ', "". $system_information ."\n\nTainted: $tainted"); return $tainted; } switch ($_POST['action']) { case 'Submit': $gacl_api->debug_text("Submit!!"); submit_system_info($_POST['system_information'], $_POST['system_info_md5']); echo "
Thanks for contributing to phpGACL.
Click here to proceed to the Administration Interface.

\n"; exit; break; default: $system_info = get_system_info(); //Read credits. $smarty->assign("credits", htmlentities( implode('',file('../CREDITS')) )); $smarty->assign("system_info", $system_info); $smarty->assign("system_info_md5", md5($system_info) ); break; } $smarty->assign("first_run", $_GET['first_run'] ); $smarty->assign("return_page", $_SERVER['PHP_SELF'] ); $smarty->assign('current','about'); if ($_GET['first_run']) { $smarty->assign('page_title', 'Installation Report'); $smarty->assign('hidemenu', 1); } else { $smarty->assign('page_title', 'About phpGACL'); } $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/about.tpl'); ?> phpgacl-3.3.7/admin/s.gif0100644025754300001440000000006307523334171014076 0ustar ipsousersGIF89a!, T;phpgacl-3.3.7/admin/test.php0100644025754300001440000002327710154675302014647 0ustar ipsousersadd_group_object(10, 'user','10'); $gacl->showarray($test); $test = $gacl->add_group_object(10, 'user','10'); $gacl->showarray($test); $test = $gacl->del_group_object(10, 'user','10'); $gacl->showarray($test); $test = $gacl->del_group_object(10, 'user','10'); $gacl->showarray($test); */ //$test = $gacl->acl_query('system','login','users','john_doe',NULL, NULL, NULL, NULL, TRUE); //showarray($test); /* $test = $gacl_api->get_group_objects(14,'ARO','RECURSE'); showarray($test); $test = $gacl_api->get_group_objects(14,'ARO'); showarray($test); */ /* $group_ids = array(14); $test = $gacl_api->acl_get_group_path($group_ids); showarray($test); $test = $gacl_api->get_group_objects(14,'ARO'); showarray($test); */ //$gacl_api->clean_path_to_root(ARO); //$gacl_api->clean_path_to_root(AXO); /* $gacl_api->add_acl( array('system' => array('login', 'enabled', 'login')), array('users' => array(1)), array(10,12,10), NULL, NULL, TRUE, TRUE, 666, 'NOTE'); */ /* $gacl_api->is_conflicting_acl( array('system' => array('login')), array('accounts' => array(1)), array(99), array('projects' => array(99)), array(99)); */ //$gacl_api->consolidated_edit_acl('system', 'add_pop','accounts',1, 99); //$gacl_api->search_acl('system','add_pop','accounts',1, 'Browsers','projects',5599,'Projects',99); /* $gacl_api->shift_acl( 18, array('accounts' => array(1)), array(14), array('projects' => array(5599)), array(23), array('system' => array('add_pop')) ); */ /* $gacl_api->append_acl( 18, array('accounts' => array(1,2,3,4)), array(14), array('projects' => array(5599)), array(23), array('system' => array('add_pop')) ); */ /* $gacl_api->add_acl( array('system' => array(99)), array('accounts' => array(99)), array(99), array('projects' => array(99)), array(99), TRUE, TRUE, 666, 'NOTE'); */ //$rows = $rs->GetRows(); //showarray($rows); /* $query = ' SELECT a.value AS a_value, a.name AS a_name, b.value AS b_value, b.name AS b_name, c.value AS c_value, c.name AS c_name, d.value AS d_value, d.name AS d_name FROM aco_sections a, aco b, aro_sections c, aro d WHERE a.value=b.section_value AND c.value=d.section_value ORDER BY a.value, b.value, c.value, d.value'; //$rs = $db->Execute($query); //$rows = $rs->GetRows(); $rs = $db->pageexecute($query, 100, 2); showarray($rows); $rows = $rs->GetRows(); showarray($rows); */ //$test=$gacl-> acl_query('system', 'email_pw', 'users', '1'); //showarray($test); //Test object deleting. //$gacl_api->del_object(10,'ARO', TRUE); //$gacl_api->del_object_section(10,'ACO',TRUE); /* //Test AXO's //function acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value=NULL, $axo_value=NULL, $root_aro_group_id=NULL, $root_axo_group_id=NULL) { $test1= acl_query('system','login','users', '1'); showarray($test1); $test2=acl_query('system','login','users', '1','projects','1'); showarray($test2); */ //Test subtree'ing /* $test=acl_get_groups('test_section2','user1',0); showarray($test); $test=acl_get_groups('test_section2','user1'); showarray($test); */ /* //require_once('../Cache_Lite.php'); require_once('./profiler.inc'); $profiler = new Profiler(true,true); $options = array( 'caching' => true, 'cacheDir' => '/tmp/phpgacl_cache', 'lifeTime' => 100 ); //$Cache_Lite = new Hashed_Cache_Lite($options); $Cache_Lite = new Cache_Lite($options); $data = '0123456789'; $Cache_Lite->save($data,'123'); $profiler->startTimer( "acl_query()"); //Test memory caching. for ($i=0; $i < 2; $i++) { //$Cache_Lite->save($data,'123'.$i); $data = $Cache_Lite->get('123'); //echo "$data
\n"; } $profiler->stopTimer( "acl_query()"); $profiler->printTimers(); */ /* //Test multi-layer ACOs $test = acl_query(array(21,19), 10); showarray($test); */ //Stress test. /* //Cleanup $aco_section_id = $gacl_api->get_aco_section_section_id("Stress Test"); $del_aco_ids = $gacl_api->get_aco($aco_section_id); foreach ($del_aco_ids as $del_aco_id) { $gacl_api->del_aco($del_aco_id); } $gacl_api->del_aco_section($aco_section_id); $aro_section_id = $gacl_api->get_aro_section_section_id("Stress Test"); $del_aro_ids = $gacl_api->get_aro($aro_section_id); foreach ($del_aro_ids as $del_aro_id) { $gacl_api->del_aro($del_aro_id); } $gacl_api->del_aro_section($aro_section_id); //Get all ACLs $query = "select id from acl"; $rs = $db->GetCol($query); foreach($rs as $del_acl_id) { $gacl_api->del_acl($del_acl_id); } */ /* $max_aco=10; $max_aro=50; $max_acl=100; $min_rand_aco=1; $max_rand_aco=9; $min_rand_aro=1; $max_rand_aro=9; //Seed random. srand ((float) microtime() * 10000000); //Grab ACO Section_id $aco_section_id = $gacl_api->get_aco_section_section_id("Stress Test"); if (!$aco_section_id) { //Add an ACO section. $aco_section_id = $gacl_api->add_aco_section("Stress Test", 999,999); $gacl_api->debug_text("Stress Test: ACO Section ID: $aco_section_id"); } //Add 100 random ACO's if ($aco_section_id) { for ($i=0; $i < $max_aco; $i++) { $aco_id = $gacl_api->get_aco_id("Stress Test ACO #$i"); if (!$aco_id) { //Add ACO. $aco_id = $gacl_api->add_aco($aco_section_id, "Stress Test ACO #$i",$i, $i); } } } $aco_ids = $gacl_api->get_aco($aco_section_id); //showarray($aco_ids); //Grab ARO section id $aro_section_id = $gacl_api->get_aro_section_section_id("Stress Test"); if (!$aro_section_id) { //Add an ACO section. $aro_section_id = $gacl_api->add_aro_section("Stress Test", 999,999); $gacl_api->debug_text("Stress Test: ARO Section ID: $aro_section_id"); } //Add 10,000 random ARO's if ($aro_section_id) { for ($i=0; $i < $max_aro; $i++) { $aro_id = $gacl_api->get_aro_id("Stress Test ARO #$i"); if (!$aro_id) { //Add ARO. $aro_id = $gacl_api->add_aro($aro_section_id, "Stress Test ARO #$i",$i, $i); } } } $aro_ids = $gacl_api->get_aro($aro_section_id); //showarray($aro_ids); //Create random ACL's using the above stress test ACO/AROs if (count($aco_ids) > 1 AND count($aro_ids) > 1) { for ($i=0; $i < $max_acl; $i++) { //Get random ACO IDS $rand_aco_keys = array_rand($aco_ids, mt_rand($min_rand_aco, $max_rand_aco) ); unset($rand_aco_ids); foreach ($rand_aco_keys as $rand_aco_key) { $rand_aco_ids[] = $aco_ids[$rand_aco_key]; } //Get random ARO IDS $rand_aro_keys = array_rand($aro_ids, mt_rand($min_rand_aro, $max_rand_aro)); unset($rand_aro_ids); foreach ($rand_aro_keys as $rand_aro_key) { $rand_aro_ids[] = $aro_ids[$rand_aro_key]; } //Random ALLOW $allow = mt_rand(0,1); $gacl_api->debug_text("Inserting ACL with ". count($rand_aco_ids) ." ACOs and ". count($rand_aro_ids) ." AROs - Allow: $allow"); $gacl_api->add_acl($rand_aco_ids, $rand_aro_ids, NULL, $allow, 1); } } //Create much more Decoy data $max_aco=100; $max_aro=4000; $max_acl=1000; $min_rand_aco=1; $max_rand_aco=10; $min_rand_aro=1; $max_rand_aro=10; //Seed random. srand ((float) microtime() * 10000000); //Grab ACO Section_id $aco_section_id = $gacl_api->get_aco_section_section_id("Stress Test Decoy"); if (!$aco_section_id) { //Add an ACO section. $aco_section_id = $gacl_api->add_aco_section("Stress Test Decoy", 1000,1000); $gacl_api->debug_text("Stress Test: ACO Section ID: $aco_section_id"); } //Add 100 random ACO's if ($aco_section_id) { for ($i=0; $i < $max_aco; $i++) { $aco_id = $gacl_api->get_aco_id("Stress Test Decoy ACO #$i"); if (!$aco_id) { //Add ACO. $aco_id = $gacl_api->add_aco($aco_section_id, "Stress Test ACO Decoy #$i",$i, $i); } } } $aco_ids = $gacl_api->get_aco($aco_section_id); //showarray($aco_ids); //Grab ARO section id $aro_section_id = $gacl_api->get_aro_section_section_id("Stress Test Decoy"); if (!$aro_section_id) { //Add an ACO section. $aro_section_id = $gacl_api->add_aro_section("Stress Test Decoy", 1000,1000); $gacl_api->debug_text("Stress Test: ARO Section ID: $aro_section_id"); } //Add 10,000 random ARO's if ($aro_section_id) { for ($i=0; $i < $max_aro; $i++) { $aro_id = $gacl_api->get_aro_id("Stress Test Decoy ARO #$i"); if (!$aro_id) { //Add ARO. $aro_id = $gacl_api->add_aro($aro_section_id, "Stress Test Decoy ARO #$i",$i, $i); } } } $aro_ids = $gacl_api->get_aro($aro_section_id); //showarray($aro_ids); //Create random ACL's using the above stress test ACO/AROs if (count($aco_ids) > 1 AND count($aro_ids) > 1) { for ($i=0; $i < $max_acl; $i++) { //Get random ACO IDS $rand_aco_keys = array_rand($aco_ids, mt_rand($min_rand_aco, $max_rand_aco) ); unset($rand_aco_ids); foreach ($rand_aco_keys as $rand_aco_key) { $rand_aco_ids[] = $aco_ids[$rand_aco_key]; } //Get random ARO IDS $rand_aro_keys = array_rand($aro_ids, mt_rand($min_rand_aro, $max_rand_aro)); unset($rand_aro_ids); foreach ($rand_aro_keys as $rand_aro_key) { $rand_aro_ids[] = $aro_ids[$rand_aro_key]; } //Random ALLOW $allow = mt_rand(0,1); $gacl_api->debug_text("Inserting ACL with ". count($rand_aco_ids) ." ACOs and ". count($rand_aro_ids) ." AROs - Allow: $allow"); $gacl_api->add_acl($rand_aco_ids, $rand_aro_ids, NULL, $allow, 1); } } */ /* //Test subtree'ing $aco_id=10; $aro_id=22; $root_group_id=30; $test=acl_query($aco_id,$aro_id,$root_group_id); showarray($test); $aco_id=10; $aro_id=22; $root_group_id=33; $test=acl_query($aco_id,$aro_id,$root_group_id); showarray($test); */ /* //Populate the ARO's $max_aros = 100; for ($i=0; $i < $max_aros; $i++) { $aro_id = $gacl_api->add_aro(41,"$i First $i Last", $i, $i); if ($aro_id) { $gacl_api->debug_text("ARO ID: $aro_id"); } else { $gacl_api->debug_text("Insert ARO ID FAILED!"); } } */ ?> phpgacl-3.3.7/admin/gacl_admin.inc.php0100644025754300001440000000474210323535444016512 0ustar ipsousersdb; //Setup the Smarty Class. require_once($gacl_options['smarty_dir'].'/Smarty.class.php'); $smarty = new Smarty; $smarty->compile_check = TRUE; $smarty->template_dir = $gacl_options['smarty_template_dir']; $smarty->compile_dir = $gacl_options['smarty_compile_dir']; /* * Email address used in setup.php, please do not change. */ $author_email = 'ipso@snappymail.ca'; /* * Don't need to show notices, some of them are pretty lame and people get overly worried when they see them. * Mean while I will try to fix most of these. ;) Please submit patches if you find any I may have missed. */ error_reporting (E_ALL ^ E_NOTICE); ?> phpgacl-3.3.7/admin/acl_test2.php0100644025754300001440000000600210152167553015535 0ustar ipsousers_db_table_prefix .'aco_sections a LEFT JOIN '. $gacl_api->_db_table_prefix .'aco b ON a.value=b.section_value, '. $gacl_api->_db_table_prefix .'aro_sections c LEFT JOIN '. $gacl_api->_db_table_prefix .'aro d ON c.value=d.section_value ORDER BY a.value, b.value, c.value, d.value'; //$rs = $db->Execute($query); $rs = $db->pageexecute($query, $gacl_api->_items_per_page, $_GET['page']); $rows = $rs->GetRows(); /* echo("
");
print_r($rows);
echo("
"); */ $total_rows = count($rows); while (list(,$row) = @each(&$rows)) { list( $aco_section_value, $aco_section_name, $aco_value, $aco_name, $aro_section_value, $aro_section_name, $aro_value, $aro_name ) = $row; $acl_check_begin_time = $profiler->getMicroTime(); $acl_result = $gacl->acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value); $acl_check_end_time = $profiler->getMicroTime(); $access = &$acl_result['allow']; $return_value = &$acl_result['return_value']; $acl_check_time = ($acl_check_end_time - $acl_check_begin_time) * 1000; $total_acl_check_time += $acl_check_time; if ($aco_section_name != $tmp_aco_section_name OR $aco_name != $tmp_aco_name) { $display_aco_name = "$aco_section_name > $aco_name"; } else { $display_aco_name = "
"; } $acls[] = array( 'aco_section_value' => $aco_section_value, 'aco_section_name' => $aco_section_name, 'aco_value' => $aco_value, 'aco_name' => $aco_name, 'aro_section_value' => $aro_section_value, 'aro_section_name' => $aro_section_name, 'aro_value' => $aro_value, 'aro_name' => $aro_name, 'access' => $access, 'return_value' => $return_value, 'acl_check_time' => number_format($acl_check_time, 2), 'display_aco_name' => $display_aco_name, ); $tmp_aco_section_name = $aco_section_name; $tmp_aco_name = $aco_name; } //echo "

$x ACL_CHECK()'s
\n"; $smarty->assign("acls", $acls); $smarty->assign("total_acl_checks", $total_rows); $smarty->assign("total_acl_check_time", $total_acl_check_time); if ($total_rows > 0) { $avg_acl_check_time = $total_acl_check_time / $total_rows; } $smarty->assign("avg_acl_check_time", number_format( ($avg_acl_check_time + 0) ,2)); $smarty->assign("paging_data", $gacl_api->get_paging_data($rs)); $smarty->assign("return_page", $_SERVER['PHP_SELF'] ); $smarty->assign('current','acl_test'); $smarty->assign('page_title', '2-dim. ACL Test'); $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/acl_test2.tpl'); ?> phpgacl-3.3.7/admin/acl_test3.php0100644025754300001440000001004510152167553015540 0ustar ipsousers_db_table_prefix .'aco_sections a LEFT JOIN '. $gacl_api->_db_table_prefix .'aco b ON a.value=b.section_value, '. $gacl_api->_db_table_prefix .'aro_sections c LEFT JOIN '. $gacl_api->_db_table_prefix .'aro d ON c.value=d.section_value ORDER BY a.value, b.value, c.value, d.value'; */ $query = ' SELECT a.value AS a_value, a.name AS a_name, b.value AS b_value, b.name AS b_name, c.value AS c_value, c.name AS c_name, d.value AS d_value, d.name AS d_name, e.value AS e_value, e.name AS e_name, f.value AS f_value, f.name AS f_name FROM '. $gacl_api->_db_table_prefix .'aco_sections a LEFT JOIN '. $gacl_api->_db_table_prefix .'aco b ON a.value=b.section_value, '. $gacl_api->_db_table_prefix .'aro_sections c LEFT JOIN '. $gacl_api->_db_table_prefix .'aro d ON c.value=d.section_value, '. $gacl_api->_db_table_prefix .'axo_sections e LEFT JOIN '. $gacl_api->_db_table_prefix .'axo f ON e.value=f.section_value ORDER BY a.value, b.value, c.value, d.value, e.value, f.value'; //$rs = $db->Execute($query); $rs = $db->pageexecute($query, $gacl_api->_items_per_page, $_GET['page']); $rows = $rs->GetRows(); /* echo("
");
print_r($rows);
echo("
"); */ $total_rows = count($rows); while (list(,$row) = @each(&$rows)) { list( $aco_section_value, $aco_section_name, $aco_value, $aco_name, $aro_section_value, $aro_section_name, $aro_value, $aro_name, $axo_section_value, $axo_section_name, $axo_value, $axo_name ) = $row; $acl_check_begin_time = $profiler->getMicroTime(); $acl_result = $gacl->acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value); $acl_check_end_time = $profiler->getMicroTime(); $access = &$acl_result['allow']; $return_value = &$acl_result['return_value']; $acl_check_time = ($acl_check_end_time - $acl_check_begin_time) * 1000; $total_acl_check_time += $acl_check_time; if ($aco_section_name != $tmp_aco_section_name OR $aco_name != $tmp_aco_name) { $display_aco_name = "$aco_section_name > $aco_name"; } else { $display_aco_name = "
"; } $acls[] = array( 'aco_section_value' => $aco_section_value, 'aco_section_name' => $aco_section_name, 'aco_value' => $aco_value, 'aco_name' => $aco_name, 'aro_section_value' => $aro_section_value, 'aro_section_name' => $aro_section_name, 'aro_value' => $aro_value, 'aro_name' => $aro_name, 'axo_section_value' => $axo_section_value, 'axo_section_name' => $axo_section_name, 'axo_value' => $axo_value, 'axo_name' => $axo_name, 'access' => $access, 'return_value' => $return_value, 'acl_check_time' => number_format($acl_check_time, 2), 'display_aco_name' => $display_aco_name, ); $tmp_aco_section_name = $aco_section_name; $tmp_aco_name = $aco_name; } //echo "

$x ACL_CHECK()'s
\n"; $smarty->assign("acls", $acls); $smarty->assign("total_acl_checks", $total_rows); $smarty->assign("total_acl_check_time", $total_acl_check_time); if ($total_rows > 0) { $avg_acl_check_time = $total_acl_check_time / $total_rows; } $smarty->assign("avg_acl_check_time", number_format( ($avg_acl_check_time + 0) ,2)); $smarty->assign("paging_data", $gacl_api->get_paging_data($rs)); $smarty->assign("return_page", $_SERVER['PHP_SELF'] ); $smarty->assign('current','acl_test'); $smarty->assign('page_title', '3-dim. ACL Test'); $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/acl_test3.tpl'); ?> phpgacl-3.3.7/admin/edit_object_sections.php0100644025754300001440000000775110011530256020037 0ustar ipsousers_db_table_prefix . 'aco_sections'; break; case 'aro': $object_type = 'aro'; $object_sections_table = $gacl_api->_db_table_prefix . 'aro_sections'; break; case 'axo': $object_type = 'axo'; $object_sections_table = $gacl_api->_db_table_prefix . 'axo_sections'; break; case 'acl': $object_type = 'acl'; $object_sections_table = $gacl_api->_db_table_prefix . 'acl_sections'; break; default: echo "ERROR: Must select an object type
\n"; exit(); break; } switch ($_POST['action']) { case 'Delete': if (count($_POST['delete_sections']) > 0) { foreach($_POST['delete_sections'] as $id) { $gacl_api->del_object_section($id, $object_type, TRUE); } } //Return page. $gacl_api->return_page($_POST['return_page']); break; case 'Submit': $gacl_api->debug_text("Submit!!"); //Update sections while (list(,$row) = @each($_POST['sections'])) { list($id, $value, $order, $name) = $row; $gacl_api->edit_object_section($id, $name, $value, $order,0,$object_type ); } unset($id); unset($value); unset($order); unset($name); //Insert new sections while (list(,$row) = @each($_POST['new_sections'])) { list($value, $order, $name) = $row; if (!empty($value) AND !empty($order) AND !empty($name)) { $object_section_id = $gacl_api->add_object_section($name, $value, $order, 0, $object_type); $gacl_api->debug_text("Section ID: $object_section_id"); } } $gacl_api->debug_text("return_page: ". $_POST['return_page']); $gacl_api->return_page($_POST['return_page']); break; default: $query = "select id,value,order_value,name from $object_sections_table order by order_value"; $rs = $db->pageexecute($query, $gacl_api->_items_per_page, $_GET['page']); $rows = $rs->GetRows(); $sections = array(); while (list(,$row) = @each($rows)) { list($id, $value, $order_value, $name) = $row; $sections[] = array( 'id' => $id, 'value' => $value, 'order' => $order_value, 'name' => $name ); } $new_sections = array(); for($i=0; $i < 5; $i++) { $new_sections[] = array( 'id' => $i, 'value' => NULL, 'order' => NULL, 'name' => NULL ); } $smarty->assign('sections', $sections); $smarty->assign('new_sections', $new_sections); $smarty->assign("paging_data", $gacl_api->get_paging_data($rs)); break; } $smarty->assign('object_type', $object_type); $smarty->assign('return_page', $_SERVER['REQUEST_URI']); $smarty->assign('current','edit_'. $object_type .'_sections'); $smarty->assign('page_title', 'Edit '. strtoupper($object_type) .' Sections'); $smarty->assign("phpgacl_version", $gacl_api->get_version() ); $smarty->assign("phpgacl_schema_version", $gacl_api->get_schema_version() ); $smarty->display('phpgacl/edit_object_sections.tpl'); ?> phpgacl-3.3.7/CHANGELOG0100644025754300001440000006365510476664743013323 0ustar ipsousersTue Sep 05 2006 mikeb - Release version 3.3.7 Sun Sep 03 2006 mikeb - Update documentation to reflect using gacl.ini.php mikeb - Allow ADODB_DIR to be set prior to GACL class being loaded mikeb - Remove ADODB_FETCH_MODE global variable, use just setFetchMode() so we don't conflict with other application code. mikeb - Upgrade Smarty v2.6.14 mikeb - Upgrade ADODB v4.92 mikeb - Switch from CVS to SVN on sourceforge.net. Sat Dec 03 2005 mikeb - Release version 3.3.6 Wed Oct 26 2005 mikeb - Added function get_section_data() thanks to Rodrigo Moraes Mon Oct 17 2005 mikeb - Fixed a bunch of warnings/notices thanks to Charles M Hall Thu Oct 13 2005 mikeb - Fixed a few PHP NOTICEs Sun Oct 9 2005 mikeb - Fixed PHP5 warning/notice of reference in gacl_api.class to do with end() call. Thu Sep 22 2005 mikeb - added dirname() to the config_file variable in admin/gacl_admin.inc.php Wed Sep 21 2005 mikeb - Addslashes to JS creation code in assign_group.php. Thu Sep 08 2005 mikeb - Added support for MS-SQL in setup.php, thanks to Franois Tissandier Sat Sep 03 2005 mikeb - Fixed typo in manual mikeb - Fixed comments in gacl.class.php mikeb - released v3.3.5 Mon Aug 22 2005 mikeb - Fixed bug with edit_acl() for cases where a non-existant acl_id is specified. Thanks to Caleb Simonyi-Gindele Thu Aug 04 2005 mikeb - Fixed bug with config ini file path in setup.php Thu Jul 28 2005 mikeb - Fixed bug with double/single quotes on line 3526. Jrmy Cohen Solal Sun Jul 24 2005 mikeb - Removed table prefix comment regarding not using "_". Apparently it works now mikeb - Fixed comments in gacl.class.php regarding group IDs, it now correctly says group values. mikeb - Hashed_Cache_Lite class now checks to see if Cache_Lite class already exists before including it. mikeb - Added Russian manual translation thanks to mikeb - Added gacl.conf config file for both admin interface and gacl.class.php. Thanks to Richard Plevin mikeb - Fixed a couple notices with E_ALL enabled in the API. John Mortlock mikeb - Added a SQL quoting to insert. Eder mikeb - Added support for custom ACL sections to add_acl(), for instance if someone deletes the 'system' section. Vincent Woo mikeb - Upgraded ADODB to v4.65 mikeb - Upgraded Smarty to v2.6.9 mikeb - Release v3.3.5b1 Wed Dec 8 2004 mikeb - Release v3.3.4 Sun Dec 5 2004 mikeb - Added clear_database() API function that truncates all phpGACL tables. Thanks to Ross Lawley mikeb - Modified millennium falcon example. dcech - Updated get_group_objects function with much more efficient SQL. Tue Nov 30 2004 mikeb - modified query in get_group_objects() to speed it up. It could still be optimized further though. Mon Nov 29 2004 mikeb - Applied patch to admin/edit_group.php enabling the deletion of all groups. John Mortlock Sun Nov 28 2004 mikeb - Removed default table prefix from gacl.class.php. mikeb - Corrected minor mistake in the manual with regards to incorrect argument ordering in acl_check() mikeb - Modified API comments for search_acl() to include return value. mikeb - Fixed admin link at the bottom of several pages in the Millennium Falcon examples. Sat Nov 27 2004 mikeb - Added Millennium Falcon example written by Ross Lawley mikeb - Added 3-dimensional ACL Test page from Meinhard mikeb - Upgraded ADODB to v4.54 mikeb - Upgraded Smarty to v2.6.6 Sun Nov 7 2004 mikeb - Updated Cache Lite class to latest version, including improved Hashed_Cache_Lite class thanks to Dan. mikeb - If caching is not enabled, improved the bypassing of the caching library/overhead thanks to Andrew Eddie mikeb - Updated manual thanks to Andrew Eddie mikeb - add_group API function now assigns the value to the insert ID if the value is not specified. thanks to Andrew Eddie mikeb - Moved admin interface specific functions/variables from gacl_api.class.php to admin/gacl_admin_api.class.php. thanks to Andrew Eddie Mon Oct 4 2004 mikeb - Added phpDoc style comments thanks to Gordon Luk Sat Oct 2 2004 mikeb - Added get_ungrouped_objects() function, thanks to Gordon Luk Sun Sep 19 2004 mikeb - Added a "isset()" call in Hashed_Cache_Lite mikeb - Applied patch from Gordon Luk to allow the search_acl() function to proceed without a where clause. Thu Sep 09 2004 mikeb - gacl.class.php line 532, missing quotes around $root_group. Thanks to Erwan le Gall Sat Aug 28 2004 mikeb - Released v3.3.3 Mon Aug 16 2004 mikeb - Quoted group value in acl_get_groups(). Thanks to Christer Lindh Wed Aug 11 2004 mikeb - Add comment to admin/gacl_admin.inc.php to tell people not to use "_" for table prefixes. Thu Aug 05 2004 mikeb - Fixed acl_test.php ACL Check timings. Thanks to Gordon Luk Tue Jul 27 2004 dcech - Fixed missing table prefix in edit_object function. Thanks to Martin Salo . dcech - Updated to adodb-xmlschema CVS for prefixing. Mon Jul 26 2004 mikeb - Fixed bug in is_conflicting_acl() where it would detect an incorrect conflict when using certain AXO combinations. Thanks to a patch from Jeff McDonald Sat Jul 17 2004 mikeb - Fixed group_admin SQL query to be standard complaint so it works on PostgreSQL. mikeb - Made aro_groups/axo_groups value column unique. Sat Jul 10 2004 mikeb - Released v3.3.2 mikeb - upgraded to ADOdb v4.50 mikeb - upgraded to Smarty v2.6.3 mikeb - Fixed group_admin.php bug with hardcoded group_type column name. Sat Jun 19 2004 dcech - cleaned up setup script a little dcech - upgraded to adodb-xmlschema CVS to get fixes for prefix-related problems. Fri Jun 18 2004 dcech - upgraded to ADOdb v4.23 Thu Jun 17 2004 mikeb - Removed admin/mysql_db_demo_data.sql as it was the old 3.2.x format and useless now. mikeb - Fixed issue with links and group names with spaces in them. Thanks to Gordon Luk dcech - fixed object count in group admin for groups without objects Tue May 24 2004 mikeb - Fixed bug in API that stopped you from changing a group name to the same thing as the group value. Hakan Kuecuekyilmaz Sat May 08 2004 mikeb - Released v3.3.1 Fri May 07 2004 dcech - Fixed setup issues with new schema format, and moved to AXMLS prefixing. Wed May 05 2004 mikeb - Fixed bug in acl_get_groups() where $root_group = '' causes a SQL error. Thanks to Hakan Kuecuekyilmaz mikeb - Fixed Unit Tests to work with group_value's. Thanks to the help from Hakan Kuecuekyilmaz Mon May 03 2004 mikeb - Updated comments for add_group() to explain the Root group limitation of our MPTT format. Sun May 02 2004 mikeb - Added Perl ACL_Check module written by Gordon Luk mikeb - Upgraded to ADODB v4.22 mikeb - Upgraded to Smarty v2.6.2 mikeb - Re-enabled JS selected = true when selecting objects to assign to groups. mikeb - Released v3.3.0-final ==> mikeb - Read the CHANGELOG line with "==>" infront of it. Mar 03 2004. Wed Apr 21 2004 mikeb - Fixed index name for value column in AXO group table. Falk mikeb - Updated schema diagrams. Falk Tue Apr 06 2004 mikeb - applied code documentation patch thanks to Andrew Eddie Mon Apr 05 2004 dcech - added index.php to admin directory, fixed bug in format_groups. Thanks to Andrew Eddie dcech - Fixed object_search javascript bug, cleaned up admin navigation html. Sat Apr 03 2004 dcech - Fixed JavaScript bug in IE, minor cleanup of admin_functions.js Fri Mar 19 2004 dcech - Fixed minor typo in gacl_api.class.php. Thanks to Andrew Eddie Wed Mar 03 2004 dcech - Updated admin interface and gacl class for value patch. acl_* functions now all accept group values rather than IDs. dcech - Minor fixes in gacl_api for group_value patch. ==> mikeb - *BEWARE* add_group(), edit_group() both require a group_value now! This breaks backwards compatibility. Sat Feb 28 2004 mikeb - Upgraded ADODB to 4.20. mikeb - Applied group value patch from Christer Lindh . Admin interface still needs updating. As well as the gacl.class. Tue Feb 24 2004 dcech - minor fix to display AXO controls when editing ACLs Sat Feb 14 2004 mikeb - Added DB schema diagram to docs/ directory. Thanks to Falk John Fri Feb 13 2004 dcech - Changed all JOIN queries to INNER JOIN for MySQL 3.23.x compatibility Thu Feb 12 2004 dcech - removed final trailing semicolon from schema.xml Tue Feb 10 2004 mikeb - Fixed get_object() to support ACLs. Mon Feb 09 2004 mikeb - Fixed del_object_section() to properly delete ACLs if the section is deleted. Thanks to Falk John mikeb - Fixed del_object_section() when using ACL objects. mikeb - Took ";" out of schema.xml, and changed version to b2. mikeb - Added some missing transaction rollbacks to gacl_api. Thanks to Falk John Sun Feb 08 2004 dcech - Added titles to admin interface mikeb - Removed the last of "is_string()" DB errro checks, replaced with "is_object($rs)" mikeb - Oracle SQL syntax fixes for gacl.class.php and gacl_api.class.php. Thanks to Falk John dcech - SQL syntax fixes for admin system Sat Feb 07 2004 dcech - New style for admin interface Wed Feb 04 2004 mikeb - Removed old, out dated examples from gacl_api.class.php Sun Feb 01 2004 dcech - cleanup of assign_group.php, template updates for assign_group and group_admin Sat Jan 31 2004 dcech - more interface cleanups Fri Jan 30 2004 dcech - complete overhaul of acl_admin.php dcech - fix for LIMIT in sql, complete overhaul of object search Thu Jan 29 2004 dcech - major interface overhaul dcech - acl_list interface improvements, minor css modification mikeb - Upgraded to ADODB 4.11, removed trailing whitespace from adodb-pager.inc.php dcech - major overhaul of acl_list filtering Wed Jan 28 2004 dcech - major update to acl_list dcech - fix for stress_test to handle existing groups dcech - fixed trailing whitespace in adodb-pager.inc.php Sun Jan 25 2004 mikeb - Changed acl_list layout. mikeb - Changed setup.php to point to about.php and modified about.php to only display the report section on first run. mikeb - Unleashed v3.3.0b1 upon the world! Wahahahah! Sat Jan 24 2004 mikeb - Minor updates to CREDITS file, README, and footer.tpl mikeb - Upgraded ADODB to v4.10 mikeb - Upgraded Smarty to v2.6.1 dcech - del_object: fix for new GetCol behaviour in ADOdb 4.10 Wed Jan 14 2004 mikeb - modified TODO list slightly. mikeb - added Navigation bar submitted by: on Oct 13/2003. mikeb - Moved the "select all" checkboxes... mikeb - modified include syntax in acl_admin_js.tpl slightly. mikeb - add_object(), edit_object(), edit_object_section(), add_object_section(), fixed issue with hidden being set to a non-int value. Thu Jan 08 2004 dcech - Added Select/Deselect All to Admin Interface Thu Jan 08 2004 dcech - applied _group_ patch Mon Nov 24 2003 mikeb - Fixed PostgreSQL LEFT JOIN issue with is_conflicting_acl(), Can't reference multiple tables, then a LEFT JOIN. ie: select * from table1, table2 LEFT JOIN table3 LEFT JOIN table4. Instead: select * from table1 LEFT JOIN table2 LEFT JOIN table3 LEFT JOIN table4. mikeb - Fixed PosgresSQL LEFT JOIN issue with acl_check() mikeb - Fixed PostgreSQL transaction issue, where generating sequences causes the transaction to abort. Simply move the transaction start _after_ the GenID() call. dcech - cleaned up get_object_section_section_id - fixed bug if invalid object type specified, and bug if no value specified. Wed Nov 19 2003 mikeb - Added transactions to edit_object_section() mikeb - Added more API test_suite functions. mikeb - Fixed minor display issue with pager.tpl. dcech - numerous fixes in gacl_api. edit_group, add_group_object, rebuild_tree & _rebuild_tree. dcech - new functions in gacl_api. get_root_group_id & get_object_groups. Tue Nov 18 2003 mikeb - Added more tests to the test suite. Fixed up a couple SQL syntax errors in del_group(). Fri Nov 14 2003 mikeb - Fixed minor bug in get_group_objects() to do with the SQL query, and table prefixes. dcech - improved count_all function and documentation dcech - updated Stress Test & added script to do random ACL checks dcech - fixed bug where debug_db was called after RollBackTrans Wed Nov 12 2003 mikeb - More MPTT fixes from Dan mikeb - is_conflicting_acl() patch from Dan also. Tue Nov 11 2003 mikeb - Applied MPTT (different group tree format) patch from Dan Cech mikeb - Minor cleanup of the above patch. mikeb - FIxed a couple minor bugs with add_group_object() and del_object() relating to the MPTT patch. Wed Nov 05 2003 - Fixed issue where ARO and AXO groups use the same sequence. Not really a bug, but shouldn't be the case. Thanks to Dan Cech Wed Oct 29 2003 - Fixed bug in schema.xml where group_id was missing from a unique index. Tue Oct 21 2003 - Group select boxes represent the tree structure much better. Purely cosmetic change. Thanks to Dan Cech Wed Oct 08 2003 - Released 3.2.2 - Fixed bug in acl_list.php where the filter wasn't being passed between pages. Thanks to Dan Cech - Greatly improved the speed of acl_list.php. Thanks to Dan Cech Sun Oct 05 2003 - Fix for major bug in AXOs, if you don't specify AXO values in acl_check(), it could still match ACLs that have AXO groups assigned to them. Discovered by Dan Cech . Sun Oct 03 2003 - Released 3.2.1 Mon Sep 29 2003 - Fixed possible bug with re-using database connection objects. - Fixed bug in assign_group.tpl where it was hard coded to reference AROs. - Fixed major bug with table prefixes and del_object() function, as well as sequences. Thanks to Ian Eure for reporting this. - Fixed minor bug in generating javascript arrays with values containing quotes. Bug found by: Martino Piccinato Sat Sep 20 2003 - Released 3.2.0-final. - Upgraded ADODB to 3.90. Sat Sep 06 2003 - Fixed minor bug when assigning objects to groups. Thanks Jos Vanlanduyt for pointing this out. Mon Aug 25 2003 - Added SOAP server and clients for PHP, Perl, and Python. - Updated README regarding upgrading and PostgreSQL. - Released phpGACL v3.2.0b2 Thu Aug 21 2003 - Added Help / About page. - Fixed up the ACL Sections, to use values instead of IDs for consistency sake - Upgraded to Smarty 2.5.0 - Released phpGACL v3.2.0b Mon Aug 18 2003 - Merged table prefix patch from Ben Margolin (ben@prince.org), did some minor clean-ups/fixes. - Upgraded to ADODB 3.72 Wed Aug 13 2003 - Decreased column lengths to varchar(240) so MySQL can index multiple columns. Thanks to Ben Margolin (ben@prince.org) - Fixed bug in add_object(), it was selecting from a non-existent table. Thanks to Ben Margolin (ben@prince.org) Tue July 08 2003 - Create a new setup.php from scratch using ADODB XML-Schema. This should greatly ease table creation on many different databases. - Modified ADODB XML-Schema to support all types of indexes. Submitted patch to Richard Tango-Lowy Mon July 07 2003 - add_object() now returns TRUE if the object is already in the database. Fri July 04 2003 - Updated table schema for the acl table. Added the acl_section table, and the phpgacl table. - Added ACL sections. The purpose of these is like all other sections, to simply classify objects. For example, you may want to differentiate between automatically created ACLs (System ACLs) and manually created ACLs (User ACLs). As some installations may have several thousand automatically created ACLs, its nice to filter these out. - add_group_object() now returns TRUE if the object is already assigned to the group. Tue July 01 2003 - Fixed security bug that could allow someone to see your database password. Mon June 16 2003 - Added acl_query_array() to aid in running an acl_check() over a large amount of AROs, returning section_value, values of those that return true. Especially useful with get_group_objects(); Thu June 12 2003 - Added "ACLs" link to the group page, so you easily show all ACLs a specific group is assigned too. - Added "ACLs" link to the group assign page, so you easily show all ACLs a specific ARO/AXO is assigned too. - modified return array format for get_group_objects() and added functionality to return objects in child groups as well. Wed June 11 2003 - Object searching now supports a newline delimited list of names/values. Sun June 8 2003 - Fixed more warnings. Wed May 28 2003 - More minor warning fixes. - Moved the manual to the documentation directory. - Case insensitive object searching when assigning to groups. Mon May 26 2003 - Fixed many more PHP warnings all over. - Added example.php - Upgraded Smarty to v2.5 - Upgraded ADODB to v3.5. Added line to set FETCH_MODE on a per connection basis. Wed May 21 2003 - Fixed add_acl() for AXOs, as it was previously completely broken. Mon May 19 2003 - Fixed ACL List filtering so it returns all ACL rows, not just the ones matching the LIKE query. Fri May 16 2003 - Fixed a few PHP warnings gacl.class.php - Removed an else condition from an if statement that didn't do anything anyways. Sun Mar 09 2003 - Fixed small bug in API function get_object_id() where it referenced the $name variable, but $name was never used. If the function was used properly, this shouldn't have affected anything. Thanks to: Harald Fielker for pointing this out. Sat Mar 01 2003 - Fixed up the layers with AXO show/hide functionality. Usable, but still needs improvement. Thu Feb 27 2003 - Attempted to add javascript show/hide to the AXO section of the acl_admin. Tue Feb 25 2003 - Fixed invalid DB reference in acl_get_groups(); Thanks to Gianluca Faieta for the patch. Thu Feb 20 2003 - Fixed bug in get_group_objects() that caused it to return the query record set, not the data itself. Thanks to Thomas for submitting a patch. - Fixed bug in del_group(), as it neglected to remove the object mapping. Thanks to Thomas for pointing this out. Tue Feb 18 2003 - Added 'force_cache_expire' option, so the cache is automaticalled expired when ACL/Group modifications take place. This should allow the cache_expire_time to be greatly increased (86400?) and still maintain instance updates. - Removed un-needed table joins in search_acl() resulting in a large performance increase. Mon Feb 17 2003 - Added "limit" to ARO select query in group_assign.php Fri Feb 14 2003 - Tweeked consolidate_edit_acl() to better consolidate ACLs in the expected manor. - Fixed cache ID in acl_get_group_path() so it doesn't contain non-alphanumeric characters. Wed Feb 12 2003 - Fixed bug caused by overzealous array_unique in add_acl() - Switched acl_query() to return TRUE/FALSE for the 'allow' return value instead of 1/0 - API group functions didn't manipulate the path_to_root mapping when reparenting or deleting occured. Tue Feb 04 2003 - Fixed bug in edit_group(), added more error checking. - Added object searching to the assign group pages. - Moved all templates to $smarty_template_dir/phpgacl/ to better integrate with existing smarty installations. - Fixed recursion in format_groups() so it doesn't use globals anymore. - Greatly improved performance in sort_groups() (50% improvement) and format_groups() (20%) functions. Thu Jan 31 2003 - Added conflict detection to add_acl(); Thu Jan 30 2003 - Created search_acl() to search for ACLs mapped to specific objects. This should also work for detecting conflicting ACLs. - Created shift_acl() that basically does the opposite of append_acl(). Remove just the specific objects from the ACL mapping. Wed Jan 29 2003 - Created append_acl() function for simply appending ACO/ARO/ARO Groups/AXO/AXO Groups on to an already existing ACL. Mon Jan 28 2003 - Added ACL searching. - Added ACL debug page. - Added sanity checks to add_acl()/edit_acl() to make sure the objects mapped to ACLs actually exist. - Transactionalized most API functions. Fri Jan 24 2003 - Fixed JS UnSelect button bug for AXO groups. - Added Object searching to help the admin interface handle an unlimited number of objects. - Added "max_select_box_items" config option to set the maximum number of items shown in select boxes. Tue Jan 14 2003 - Upgraded ADODB to v3.00 - Added user definable ACL return values, along with the acl_return_value() function. - Added ACL notes which serve no other purpose other than to help the administrator keep track of ACL's - Added paging for large record sets. Sun Dec 15 2002 - Moved to a completely OO structure. - Config options can now be passed during object creation. $gacl = new gacl($options); Sun Dec 01 2002 - Fixed bug that broke allow and enabled from being changed when editing ACLs. Sat Nov 30 2002 - Merged ERASE feature in to API functions, del_object() and del_object_section() thanks to Martino. - Changed the acl list page layout - Changed the acl test page layout - Added assigned object count to the group admin page. - Fixed a JS bug that wouldn't allow you to "select" (>>) any objects at all, if one was already selected. Sun Nov 17 2002 - format_groups() function wasn't recursing as efficiently as it could be. Fixed. - Re-wrote API aro/aco/group specific functions to be generic to help support AXO's. - Re-wrote aro/aco/group specific admin scripts to be generic to help support AXO's. - Added AXO support to acl_admin.php. - Updated gacl.inc.php to support AXO's. Mon Nov 11 2002 - Changed the ordering of the main acl_query() SQL query. It wasn't properly preferring the deepest groups ACLs. - Thanks to Mauro for spotting this one and helping me track it down. - Fixed bug that prevented ACO/ARO's with the same 'value' in different sections from being assigned to an ACL. - Thanks to Martino - Fixed bug that made it seem like assigning a single ARO to a group really added all AROs with the same value to that group. - Thanks to Martino - Fixed bug causing virtual subtree'ing to only traverse 2 levels of the tree. - Thanks to Martino - Used dirname(__FILE__) to remove the need for users to manually set the root path in config.inc.php. Wed Oct 23 2002 - Switched away from ID's in favor of VALUES for most functions, including acl_check(). Broke all backwards compatibility. - Modified DB Schema to reflect above. - When ARO/ACO values are changed, the API will modify said values in all tables. - Added a variable for phpGACL's current path. Wed Oct 09 2002 - Added "hidden" flags to relavent API functions - Added Oracle 8i schema to setup.php - Changed group "level" table column name to "tree_level", as the old name "level" was a reserved word in some db's. MySQL: ALTER TABLE `groups_path` CHANGE `level` `tree_level` INT(12) DEFAULT '0' NOT NULL - Modified a few of the queries to be more portable, specifically for Oracle's sake. - Added a FAQ entry regarding individualized item permissions. Wed Sep 25 2002 - Added multi-layer ACO checking to ACL_QUERY() - Removed multi-layer ACO checking in favor of "dual" acl checking. ie: If you want to set ACL's on a contact list. Create ACO's for each Contact in the list. and create more generic ACO's for things like "View - Contacts", "Edit - Contacts". Then simply create an ACL with ACO's "View - Contacts" AND "Contact ID: 15" Then if ( acl_check("View - Contacts", "John Doe") AND acl_check("Contact ID: 15", "John Doe")) Very simple, and should be just as fast, or faster then adding more bloat to the database. - Added "hidden" flag to ACO, ACO Sections, ARO, and ARO sections. phpgacl-3.3.7/test_suite/0040755025754300001440000000000010476665053014260 5ustar ipsousersphpgacl-3.3.7/test_suite/phpunit/0040755025754300001440000000000010476665053015747 5ustar ipsousersphpgacl-3.3.7/test_suite/phpunit/phpunit_test.php0100644025754300001440000001704007740420450021173 0ustar ipsousersTextTestResult(); echo ''; echo ''; } function _startTest($test) { print(''); flush(); } function _endTest($test) { /* Report both the test result and, for this special situation where some tests are expected to fail, a "meta" test result which indicates whether the test result matches the expected result. */ $expect_failure = preg_match('/fail/i', $test->name()); $test_passed = ($test->failed() == 0); if ($test->errored()) $outcome = "ERROR"; else if ($test->failed()) $outcome = "FAIL"; else $outcome = "OK"; if ($test->errored()) $meta_outcome = 'unknown'; else $meta_outcome = ($expect_failure xor $test_passed) ? 'as expected' : 'UNEXPECTED'; printf("\n"); flush(); } } class TestFixture extends TestCase { function TestFixture($name) { $this->TestCase($name); } function setUp() { /* put any common setup here */ $this->intVal = 1; $this->strVal = 'foo'; } function testFail1() { $this->assert($this->intVal == 0, "1 == 0"); } function testFail2() { $this->assert($this->strVal == 'bar'); } function testPass1() { $this->assert($this->intVal == 1); } } $suite = new TestSuite; $suite->addTest(new TestFixture("testFail1")); $suite->addTest(new TestFixture("testFail2")); $suite->addTest(new TestFixture("testPass1")); //$suite->addTest(new TestFixture("testNotExistFail")); class Fixture2 extends TestCase { function Fixture2($name) { $this->TestCase($name); } function setUp() { $this->str1 = 'foo'; $this->str2 = 'bar'; } function runTest() { $this->testStrNotEqual(); $this->testStrAppend(); } function testStrNotEqual() { $this->assert($this->str1 == $this->str2, 'str equal'); } function testStrAppend() { $this->assertEquals($this->str1 . 'bar', 'foobars', 'str append'); } } $suite->addTest(new Fixture2("Fail3")); class TestPass2 extends TestFixture { function TestPass2($name) { $this->TestFixture($name); } function runTest() { $this->assertEquals($this->strVal . 'x', $this->strVal . 'x'); $this->assertEquals($this->strVal . 'x', $this->strVal . 'y'); $this->assertEquals(1, 0); $this->assertEquals(1, "1", 'equals int and str'); } } $suite->addTest(new TestPass2("Fail4")); class MoreTesterTests extends TestCase { function MoreTesterTests($name) { $this->TestCase($name); } function testRegexpPass() { $this->assertRegexp('/fo+ba[^a-m]/', 'foobar'); } function testRegexpFail() { $this->assertRegexp('/fo+ba[^m-z]/', 'foobar'); } function testRegexpFailWithMessage() { $this->assertRegexp('/fo+ba[^m-z]/', 'foobar', "This is the message"); } } $suite->addTest(new TestSuite("MoreTesterTests")); class ManyFailingTests extends TestCase { function ManyFailingTests($name) { $this->TestCase($name); } function testPass1() { $this->assertEquals(0, 0); } function testPass2() { $this->assertEquals(0, 0); } function testFail1() { $this->assertEquals(1, 0); } function testFail2() { $this->assertEquals(1, 0); } function testFail3() { $this->assertEquals(1, 0); } function testFail4() { $this->assertEquals(1, 0); } function testFail5() { $this->assertEquals(1, 0); } function testFail6() { $this->assertEquals(1, 0); } function testPass3() { $this->assertEquals(0, 0); } function testFail7() { $this->assertEquals(1, 0); } function testPass4() { $this->assertEquals(0, 0); } function testFail8() { $this->assertEquals(1, 0); } function testPass5() { $this->assertEquals(0, 0); } function testPass6() { $this->assertEquals(0, 0); } function testFail9() { $this->assertEquals(1, 0); } function testPass7() { $this->assertEquals(0, 0); } function testPass8() { $this->assertEquals(0, 0); } } $suite->addTest(new TestSuite("ManyFailingTests")); class DummyClass1 { var $fX; } class DummyClass2 { var $fX; var $fY; function DummyClass2($x="", $y="") { $this->fX = $x; $this->fY = $y; } function equals($another) { return $another->fX == $this->fX; } function toString() { return sprintf("DummyClass2(%s, %s)", $this->fX, $this->fY); } } class AssertEqualsTests extends TestCase { function AssertEqualsTests($name) { $this->TestCase($name); } function testDiffTypesFail() { $this->assertEquals(0, ""); } function testMultiLinePass() { $str1 = "line1\nline2\nline3"; $str2 = "line1\nline2\nline3"; $this->assertEqualsMultilineStrings($str1, $str2); } function testMultiLineFail() { $str1 = "line1\nline2\nline3"; $str2 = "line1\nline2 modified\nline3"; $this->assertEqualsMultilineStrings($str1, $str2); } function testMultiLineFail2() { $str1 = "line1\nline2\nline3"; $str2 = "line1\nline2\nline3\nline4"; $this->assertEqualsMultilineStrings($str1, $str2); } } $suite->addTest(new TestSuite("AssertEqualsTests")); class AssertEqualsPhp3ErrorTests extends TestCase { /* These tests create an ERROR in PHP3 and work as expected in PHP4. */ function AssertEqualsPhp3ErrorTests($name) { $this->TestCase($name); } function testDiffClassFail() { $this->assertEquals(new DummyClass1, new DummyClass2); } function testSameClassPass() { $this->assertEquals(new DummyClass1, new DummyClass1); } function testSameClassFail() { $dummy1 = new DummyClass1; $dummy2 = new DummyClass1; $dummy1->fX = 1; $dummy2->fX = 2; $this->assertEquals($dummy1, $dummy2); } function testSameClassEqualsFail() { $dummy1 = new DummyClass2(3); $dummy2 = new DummyClass2(4); $this->assertEquals($dummy1, $dummy2); } function testSameClassEqualsPass() { $dummy1 = new DummyClass2(5, 6); $dummy2 = new DummyClass2(5, 7); $this->assertEquals($dummy1, $dummy2); } } $suite->addTest(new TestSuite("AssertEqualsPhp3ErrorTests")); if (phpversion() >= '4') { class AssertEqualsTests4 extends TestCase { /* these tests only make sense starting with PHP4 */ function AssertEqualsTests($name) { $this->TestCase($name); } function testNullFail() { $this->assertEquals(0, NULL); } function testNullPass() { $this->assertEquals(NULL, NULL); } function testArrayValuesPass1() { $a1 = array('first' => 10, 'second' => 20); $a2 = array('first' => 10, 'second' => 20); $this->assertEquals($a1, $a2); } function testArrayValuesFail1() { $a1 = array('first' => 10, 'second' => 20); $a2 = array('first' => 10, 'second' => 22); $this->assertEquals($a1, $a2); } } $suite->addTest(new TestSuite("AssertEqualsTests4")); class TestClassNameStartingWithTest extends TestCase { function TestClassNameStartingWithTest($name) { $this->TestCase($name); } function testWhateverPass() { $this->assert(true); } } $suite->addTest(new TestSuite("TestClassNameStartingWithTest")); } // $suite now consists of phpUnit self-test suite ?> phpgacl-3.3.7/test_suite/phpunit/runtests2.php0100644025754300001440000000156307740420450020421 0ustar ipsousers<?php echo $title; ?>

This page runs all the phpUnit self-tests, and uses the PrettyTestResult subclass of TestResult to produce nice HTML output.

Unlike typical test run, expect many test cases to fail. Exactly those with pass in their name should succeed.

run($result); $result->report(); ?> phpgacl-3.3.7/test_suite/phpunit/stylesheet.css0100644025754300001440000000226007740420450020635 0ustar ipsousers body { font:normal 68% verdana,arial,helvetica; color:#000000; } table tr td, table tr th { font-size: 68%; } table.details tr th{ font-weight: bold; text-align:left; background:#a6caf0; } table.details tr{ background:#eeeee0; } p { line-height:1.5em; margin-top:0.5em; margin-bottom:1.0em; } h1 { margin: 0px 0px 5px; font: 165% verdana,arial,helvetica } h2 { margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica } h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica } h4 { margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica } h5 { margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica } h6 { margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica } .Error { font-weight:bold; color:red; } .Failure, .Unexpected { background:#ff0000; font-weight:bold; color:black; } .Unknown { background:#ffff00; font-weight:bold; color:black; } .Pass, .Expected { background:#00ff00; font-weight:bold; color:black; } .Properties { text-align:right; } CODE.expected { color: green; background: none; font-weight: normal; } CODE.actual { color: red; background: none; font-weight: normal; } .typeinfo { color: gray; } phpgacl-3.3.7/test_suite/phpunit/ChangeLog0100644025754300001440000000422207740420450017504 0ustar ipsousers2002-10-31 Fred Yankowski * stylesheet.css: Add classes for "meta-results" types. * runtests.php: Explain expected results. Accomodate tabular format of results. * phpunit_test.php: Use CSS formatting of results. Use tabular layout similar to PrettyTestResult. 2002-10-30 Fred Yankowski * phpunit_test.php: (SelfTestResult): New class for reporting self-tests. * phpunit.php (Exception): Record type, error or failure. (Assert::assertEquals): Generate new error exception. (Assert::_formatValue): Display serialized value of arrays and objects. (TestCase::error): Record error as Exception. (TestCase::failed): Check for failure exceptions. (TestFailure): Record only single exception per instance. (TestResult): Record errors separately from failures. (TestResult::errorCount): New function. (TestResult::failureCount): New function. (TextTestResult::report): List failures and errors separately. 2002-10-22 Fred Yankowski * phpunit.php (TextTestUnit::_startTest): Print class name too. (per Ryan King). 2002-05-08 Fred Yankowski * phpunit_test.php: New tests for assertEqualsMultilineStrings method. * phpunit.php: New assertEqualsMultilineStrings method. 2002-04-12 Fred Yankowski * phpunit_test.php: New TestClassNameStartingWithTest class. * phpunit.php (runTest): Add defensive check on test method name. (TestSuite): Smarter filtering of test method names. 2002-04-10 Fred Yankowski * runtests.php: New file -- example driver script. * runtests2.php: New file. 2002-03-27 Fred Yankowski * phpunit_test.php (AssertEqualsTests): test bug 535772 * phpunit.php (assertEquals): use equals() method if avail (_formatValue): new method for bug 535772 (failNotEquals): use _formatValue() * phpunit_test.php (AssertEqualsTests): tests for bug 498937 * phpunit.php (assertEquals): finer-grained tests per bug 498937 * stylesheet.css: new file, used along with PrettyTestResult * phpunit_test.php: remove HTML page context * phpunit.php: new PrettyTestResult class * README: new file phpgacl-3.3.7/test_suite/phpunit/phpunit.php0100644025754300001440000004344307740420450020142 0ustar ipsousers // OntoSys, Inc // // $Id: phpunit.php 212 2003-10-07 02:12:56Z ipso $ // Copyright (c) 2000 Fred Yankowski // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING); /* interface Test { function run(&$aTestResult); function countTestCases(); } */ function trace($msg) { return; print($msg); flush(); } if (phpversion() >= '4') { function PHPUnit_error_handler($errno, $errstr, $errfile, $errline) { global $PHPUnit_testRunning; $PHPUnit_testRunning[0]->fail("PHP ERROR: ".$errstr." in ".$errfile." at line ".$errline); } } class Exception { /* Emulate a Java exception, sort of... */ var $message; var $type; function Exception($message, $type = 'FAILURE') { $this->message = $message; $this->type = $type; } function getMessage() { return $this->message; } function getType() { return $this->type; } } class Assert { function assert($boolean, $message=0) { if (! $boolean) $this->fail($message); } function assertEquals($expected, $actual, $message=0) { if (gettype($expected) != gettype($actual)) { $this->failNotEquals($expected, $actual, "expected", $message); return; } if (phpversion() < '4') { if (is_object($expected) or is_object($actual) or is_array($expected) or is_array($actual)) { $this->error("INVALID TEST: cannot compare arrays or objects in PHP3"); return; } } if (phpversion() >= '4' && is_object($expected)) { if (get_class($expected) != get_class($actual)) { $this->failNotEquals($expected, $actual, "expected", $message); return; } if (method_exists($expected, "equals")) { if (! $expected->equals($actual)) { $this->failNotEquals($expected, $actual, "expected", $message); } return; // no further tests after equals() } } if (phpversion() >= '4.0.4') { if (is_null($expected) != is_null($actual)) { $this->failNotEquals($expected, $actual, "expected", $message); return; } } if ($expected != $actual) { $this->failNotEquals($expected, $actual, "expected", $message); } } function assertRegexp($regexp, $actual, $message=false) { if (! preg_match($regexp, $actual)) { $this->failNotEquals($regexp, $actual, "pattern", $message); } } function assertEqualsMultilineStrings($string0, $string1, $message="") { $lines0 = split("\n",$string0); $lines1 = split("\n",$string1); if (sizeof($lines0) != sizeof($lines1)) { $this->failNotEquals(sizeof($lines0)." line(s)", sizeof($lines1)." line(s)", "expected", $message); } for($i=0; $i< sizeof($lines0); $i++) { $this->assertEquals(trim($lines0[$i]), trim($lines1[$i]), "line ".($i+1)." of multiline strings differ. ".$message); } } function _formatValue($value, $class="") { $translateValue = $value; if (phpversion() >= '4.0.0') { if (is_object($value)) { if (method_exists($value, "toString") ) { $translateValue = $value->toString(); } else { $translateValue = serialize($value); } } else if (is_array($value)) { $translateValue = serialize($value); } } $htmlValue = "" . htmlspecialchars($translateValue) . ""; if (phpversion() >= '4.0.0') { if (is_bool($value)) { $htmlValue = $value ? "true" : "false"; } elseif (phpversion() >= '4.0.4' && is_null($value)) { $htmlValue = "null"; } $htmlValue .= "   "; $htmlValue .= "type:" . gettype($value); $htmlValue .= is_object($value) ? ", class:" . get_class($value) : ""; $htmlValue .= ""; } return $htmlValue; } function failNotEquals($expected, $actual, $expected_label, $message=0) { // Private function for reporting failure to match. $str = $message ? ($message . ' ') : ''; //$str .= "($expected_label/actual)
"; $str .= "
"; $str .= sprintf("%s
%s", $this->_formatValue($expected, "expected"), $this->_formatValue($actual, "actual")); $this->fail($str); } } class TestCase extends Assert /* implements Test */ { /* Defines context for running tests. Specific context -- such as instance variables, global variables, global state -- is defined by creating a subclass that specializes the setUp() and tearDown() methods. A specific test is defined by a subclass that specializes the runTest() method. */ var $fName; var $fClassName; var $fResult; var $fExceptions = array(); function TestCase($name) { $this->fName = $name; } function run($testResult=0) { /* Run this single test, by calling the run() method of the TestResult object which will in turn call the runBare() method of this object. That complication allows the TestResult object to do various kinds of progress reporting as it invokes each test. Create/obtain a TestResult object if none was passed in. Note that if a TestResult object was passed in, it must be by reference. */ if (! $testResult) $testResult = $this->_createResult(); $this->fResult = $testResult; $testResult->run(&$this); $this->fResult = 0; return $testResult; } function classname() { if (isset($this->fClassName)) { return $this->fClassName; } else { return get_class($this); } } function countTestCases() { return 1; } function runTest() { if (phpversion() >= '4') { global $PHPUnit_testRunning; eval('$PHPUnit_testRunning[0] = & $this;'); // Saved ref to current TestCase, so that the error handler // can access it. This code won't even parse in PHP3, so we // hide it in an eval. $old_handler = set_error_handler("PHPUnit_error_handler"); // errors will now be handled by our error handler } $name = $this->name(); if (phpversion() >= '4' && ! method_exists($this, $name)) { $this->error("Method '$name' does not exist"); } else $this->$name(); /* if (phpversion() >= '4') { set_error_handler($old_handler); // revert to prior error handler $PHPUnit_testRunning = null; } */ } function setUp() /* expect override */ { //print("TestCase::setUp()
\n"); } function tearDown() /* possible override */ { //print("TestCase::tearDown()
\n"); } //////////////////////////////////////////////////////////////// function _createResult() /* protected */ { /* override this to use specialized subclass of TestResult */ return new TestResult; } function fail($message=0) { //printf("TestCase::fail(%s)
\n", ($message) ? $message : ''); /* JUnit throws AssertionFailedError here. We just record the failure and carry on */ $this->fExceptions[] = new Exception(&$message, 'FAILURE'); } function error($message) { /* report error that requires correction in the test script itself, or (heaven forbid) in this testing infrastructure */ $this->fExceptions[] = new Exception(&$message, 'ERROR'); $this->fResult->stop(); // [does not work] } function failed() { reset($this->fExceptions); while (list($key, $exception) = each($this->fExceptions)) { if ($exception->type == 'FAILURE') return true; } return false; } function errored() { reset($this->fExceptions); while (list($key, $exception) = each($this->fExceptions)) { if ($exception->type == 'ERROR') return true; } return false; } function getExceptions() { return $this->fExceptions; } function name() { return $this->fName; } function runBare() { $this->setup(); $this->runTest(); $this->tearDown(); } } class TestSuite /* implements Test */ { /* Compose a set of Tests (instances of TestCase or TestSuite), and run them all. */ var $fTests = array(); var $fClassname; function TestSuite($classname=false) { // Find all methods of the given class whose name starts with // "test" and add them to the test suite. // PHP3: We are just _barely_ able to do this with PHP's limited // introspection... Note that PHP seems to store method names in // lower case, and we have to avoid the constructor function for // the TestCase class superclass. Names of subclasses of TestCase // must not start with "Test" since such a class will have a // constructor method name also starting with "test" and we can't // distinquish such a construtor from the real test method names. // So don't name any TestCase subclasses as "Test..."! // PHP4: Never mind all that. We can now ignore constructor // methods, so a test class may be named "Test...". if (empty($classname)) return; $this->fClassname = $classname; if (floor(phpversion()) >= 4) { // PHP4 introspection, submitted by Dylan Kuhn $names = get_class_methods($classname); while (list($key, $method) = @each($names)) { if (preg_match('/^test/', $method)) { $test = new $classname($method); if (strcasecmp($method, $classname) == 0 || is_subclass_of($test, $method)) { // Ignore the given method name since it is a constructor: // it's the name of our test class or it is the name of a // superclass of our test class. (This code smells funny. // Anyone got a better way?) //print "skipping $method
"; } else { $this->addTest($test); } } } } else { // PHP3 $dummy = new $classname("dummy"); $names = (array) $dummy; while (list($key, $value) = each($names)) { $type = gettype($value); if ($type == "user function" && preg_match('/^test/', $key) && $key != "testcase") { $this->addTest(new $classname($key)); } } } } function addTest($test) { /* Add TestCase or TestSuite to this TestSuite */ $this->fTests[] = $test; } function run(&$testResult) { /* Run all TestCases and TestSuites comprising this TestSuite, accumulating results in the given TestResult object. */ reset($this->fTests); while (list($na, $test) = each($this->fTests)) { if ($testResult->shouldStop()) break; $test->run(&$testResult); } } function countTestCases() { /* Number of TestCases comprising this TestSuite (including those in any constituent TestSuites) */ $count = 0; reset($fTests); while (list($na, $test_case) = each($this->fTests)) { $count += $test_case->countTestCases(); } return $count; } } class TestFailure { /* Record failure of a single TestCase, associating it with the exception that occurred */ var $fFailedTestName; var $fException; function TestFailure(&$test, &$exception) { $this->fFailedTestName = $test->name(); $this->fException = $exception; } function getExceptions() { // deprecated return array($this->fException); } function getException() { return $this->fException; } function getTestName() { return $this->fFailedTestName; } } class TestResult { /* Collect the results of running a set of TestCases. */ var $fFailures = array(); var $fErrors = array(); var $fRunTests = 0; var $fStop = false; function TestResult() { } function _endTest($test) /* protected */ { /* specialize this for end-of-test action, such as progress reports */ } function addError($test, $exception) { $this->fErrors[] = new TestFailure(&$test, &$exception); } function addFailure($test, $exception) { $this->fFailures[] = new TestFailure(&$test, &$exception); } function getFailures() { return $this->fFailures; } function run($test) { /* Run a single TestCase in the context of this TestResult */ $this->_startTest($test); $this->fRunTests++; $test->runBare(); /* this is where JUnit would catch AssertionFailedError */ $exceptions = $test->getExceptions(); reset($exceptions); while (list($key, $exception) = each($exceptions)) { if ($exception->type == 'ERROR') $this->addError($test, $exception); else if ($exception->type == 'FAILURE') $this->addFailure($test, $exception); } // if ($exceptions) // $this->fFailures[] = new TestFailure(&$test, &$exceptions); $this->_endTest($test); } function countTests() { return $this->fRunTests; } function shouldStop() { return $this->fStop; } function _startTest($test) /* protected */ { /* specialize this for start-of-test actions */ } function stop() { /* set indication that the test sequence should halt */ $fStop = true; } function errorCount() { return count($this->fErrors); } function failureCount() { return count($this->fFailures); } function countFailures() { // deprecated return $this->failureCount(); } } class TextTestResult extends TestResult { /* Specialize TestResult to produce text/html report */ function TextTestResult() { $this->TestResult(); // call superclass constructor } function report() { /* report result of test run */ $nRun = $this->countTests(); $nFailures = $this->failureCount(); $nErrors = $this->errorCount(); printf("

%s test%s run
", $nRun, ($nRun == 1) ? '' : 's'); printf("%s failure%s
\n", $nFailures, ($nFailures == 1) ? '' : 's'); printf("%s error%s.
\n", $nErrors, ($nErrors == 1) ? '' : 's'); if ($nFailures > 0) { print("

Failures

"); print("
    \n"); $failures = $this->getFailures(); while (list($i, $failure) = each($failures)) { $failedTestName = $failure->getTestName(); printf("
  1. %s\n", $failedTestName); $exceptions = $failure->getExceptions(); print("
      "); while (list($na, $exception) = each($exceptions)) printf("
    • %s\n", $exception->getMessage()); print("
    "); } print("
\n"); } if ($nErrors > 0) { print("

Errors

"); print("
    \n"); reset($this->fErrors); while (list($i, $error) = each($this->fErrors)) { $erroredTestName = $error->getTestName(); printf("
  1. %s\n", $failedTestName); $exception = $error->getException(); print("
      "); printf("
    • %s\n", $exception->getMessage()); print("
    "); } print("
\n"); } } function _startTest($test) { if (phpversion() > '4') { printf("%s - %s ", get_class($test), $test->name()); } else { printf("%s ", $test->name()); } flush(); } function _endTest($test) { $outcome = $test->failed() ? "FAIL" : "ok"; printf("$outcome
\n"); flush(); } } // PrettyTestResult created by BJG 17/11/01 // beacuse the standard test result provided looks // rubbish. class PrettyTestResult extends TestResult { /* Specialize TestResult to produce text/html report */ function PrettyTestResult() { $this->TestResult(); // call superclass constructor echo "

Tests

"; echo "
Test nameResultMeta-result
'); if (phpversion() > '4') { printf("%s - %s ", get_class($test), $test->name()); } else { printf("%s ", $test->name()); } print('$outcome$meta_outcome
"; echo ""; } function report() { echo "
ClassFunctionSuccess?
"; /* report result of test run */ $nRun = $this->countTests(); $nFailures = $this->countFailures(); echo "

Summary

"; printf("

%s test%s run
", $nRun, ($nRun == 1) ? '' : 's'); printf("%s failure%s.
\n", $nFailures, ($nFailures == 1) ? '' : 's'); if ($nFailures == 0) return; echo "

Failure Details

"; print("
    \n"); $failures = $this->getFailures(); while (list($i, $failure) = each($failures)) { $failedTestName = $failure->getTestName(); printf("
  1. %s\n", $failedTestName); $exceptions = $failure->getExceptions(); print("
      "); while (list($na, $exception) = each($exceptions)) printf("
    • %s\n", $exception->getMessage()); print("
    "); } print("
\n"); } function _startTest($test) { printf("%s %s ", $test->classname(),$test->name()); flush(); } function _endTest($test) { $outcome = $test->failed() ? " class=\"Failure\">FAIL" : " class=\"Pass\">OK"; printf(""); flush(); } } class TestRunner { /* Run a suite of tests and report results. */ function run($suite) { $result = new TextTestResult; $suite->run($result); $result->report(); } } ?>phpgacl-3.3.7/test_suite/phpunit/README0100644025754300001440000000126407740420450016615 0ustar ipsousersDEPENDENCIES phpUnit runs on PHP3 or PHP4. phpUnit uses PCRE (Perl Compatible Regular Expressions). PCRE is normally compiled into PHP, but it might be missing from custom builds of PHP. INSTALLATION Place the phpunit.php file so that it is in the PHP include_path for the PHP code to be tested. Installing it in the same directory as the code-under-test should work. The same phpunit.php file can be shared by any number of test suites. You may wish to install phpunit.php in just one place and adjust the PHP include_path to include that place. EXAMPLES The self-test suite for phpUnit itself is currently the best (only) example distributed with phpUnit. See phpunit_test.php. phpgacl-3.3.7/test_suite/phpunit/runtests.php0100644025754300001440000000247607740420450020343 0ustar ipsousers <?php echo $title; ?>

Unlike a typical test run, expect many test cases to fail. Exactly those with pass in their name should succeed.

For each test we display both the test result -- ok, FAIL, or ERROR -- and also a meta-result -- as expected, UNEXPECTED, or unknown -- that indicates whether the expected test result occurred. Although many test results will be 'FAIL' here, all meta-results should be 'as expected', except for a few 'unknown' meta-results (because of errors) when running in PHP3.

Test Results

run($result); print(''); $result->report(); ?> phpgacl-3.3.7/test_suite/unit_tests.php0100644025754300001440000004662010046256337017171 0ustar ipsousersgacl_api->get_version(); //$expected = '/^[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}$/i'; $expected = '/^[0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2}[a-zA-Z]{0,1}[0-9]{0,1}$/i'; $this->assertRegexp($expected, $result, 'Version incorrect.'); } function get_schema_version() { $result = $this->gacl_api->get_schema_version(); $expected = '/^[0-9]{1,2}.[0-9]{1,2}$/i'; $this->assertRegexp($expected, $result, 'Schema Version incorrect.'); } /** GENERAL **/ function count_all() { //Create array $arr = array( 'Level1a' => array( 'Level2a' => array( 'Level3a' => 1, 'Level3b' => 2 ), 'Level2b' => 3, ), 'Level1b' => 4, 'Level1c' => array( 'Level2c' => array( 'Level3c' => 5, 'Level3d' => 6 ), 'Level2d' => 7, ), 'Level1d' => 8 ); //Keep in mind count_all only counts actual values. So array()'s don't count as +1 $result = $this->gacl_api->count_all($arr); $this->assertEquals(8, $result, 'Incorrect array count, Should be 8.'); } /** ACO SECTION **/ function get_object_section_section_id_aco() { $result = $this->gacl_api->get_object_section_section_id('unit_test', 'unit_test', 'ACO'); $message = 'get_object_section_section_id failed'; $this->assert($result, $message); return $result; } function add_object_section_aco() { $result = $this->gacl_api->add_object_section('unit_test', 'unit_test', 999, 0, 'ACO'); $message = 'add_object_section failed'; $this->assert($result, $message); } function del_object_section_aco() { $result = $this->gacl_api->del_object_section($this->get_object_section_section_id_aco(), 'ACO'); $message = 'del_object_section failed'; $this->assert($result, $message); } /** ACO **/ function get_object_id_aco() { $result = $this->gacl_api->get_object_id('unit_test','enable_tests', 'ACO'); $message = 'get_object_id failed'; $this->assert($result, $message); return $result; } function add_object_aco() { $result = $this->gacl_api->add_object('unit_test', 'Enable - Tests', 'enable_tests', 999, 0, 'ACO'); $message = 'add_object failed'; $this->assert($result, $message); } function del_object_aco() { $result = $this->gacl_api->del_object($this->get_object_id_aco(), 'ACO'); $message = 'del_object failed'; $this->assert($result, $message); } /** ARO SECTION **/ function get_object_section_section_id_aro() { $result = $this->gacl_api->get_object_section_section_id('unit_test', 'unit_test', 'ARO'); $this->_aco_section_id = $result; $message = 'get_object_section_section_id failed'; $this->assert($result >= 0, $message); return $result; } function add_object_section_aro() { $result = $this->gacl_api->add_object_section('unit_test', 'unit_test', 999, 0, 'ARO'); $message = 'add_object_section failed'; $this->assert($result, $message); } function edit_object_section_aro() { $object_id = $this->get_object_section_section_id_aro(); $rename_result = $this->gacl_api->edit_object_section($object_id, 'unit_test_tmp', 'unit_test_tmp', 999, 0, 'ARO'); $rename2_result = $this->gacl_api->edit_object_section($object_id, 'unit_test', 'unit_test', 999, 0, 'ARO'); if ($rename_result === TRUE AND $rename2_result === TRUE) { $result = TRUE; } else { $result = FALSE; } $message = 'edit_object_section failed'; $this->assert($result, $message); } function del_object_section_aro() { $result = $this->gacl_api->del_object_section($this->get_object_section_section_id_aro(), 'ARO'); $message = 'del_object_section failed'; $this->assert($result, $message); } /** ARO **/ function get_object_id_aro() { $result = $this->gacl_api->get_object_id('unit_test','john_doe', 'ARO'); $message = 'get_object_id failed'; $this->assert($result, $message); return $result; } function get_object2_id_aro() { $result = $this->gacl_api->get_object_id('unit_test','jane_doe', 'ARO'); $message = 'get_object2_id failed'; $this->assert($result, $message); return $result; } function add_object_aro() { $result = $this->gacl_api->add_object('unit_test', 'John Doe', 'john_doe', 999, 0, 'ARO'); $message = 'add_object failed'; $this->assert($result, $message); } function del_object_aro() { $result = $this->gacl_api->del_object($this->get_object_id_aro(), 'ARO'); $message = 'del_object failed'; $this->assert($result, $message); } function add_object2_aro() { $result = $this->gacl_api->add_object('unit_test', 'Jane Doe', 'jane_doe', 998, 0, 'ARO'); $message = 'add_object2 failed'; $this->assert($result, $message); } function del_object2_aro() { $result = $this->gacl_api->del_object($this->get_object2_id_aro(), 'ARO'); $message = 'del_object2 failed'; $this->assert($result, $message); } /** AXO SECTION **/ function get_object_section_section_id_axo() { $result = $this->gacl_api->get_object_section_section_id('unit_test', 'unit_test', 'AXO'); $message = 'get_object_section_section_id failed'; $this->assert($result, $message); return $result; } function add_object_section_axo() { $result = $this->gacl_api->add_object_section('unit_test', 'unit_test', 999, 0, 'AXO'); $this->_aco_section_id = $result; $message = 'add_object_section failed'; $this->assert($result, $message); } function del_object_section_axo() { $result = $this->gacl_api->del_object_section($this->get_object_section_section_id_axo(), 'AXO'); $message = 'del_object_section failed'; $this->assert($result, $message); } /** AXO **/ function get_object_id_axo() { $result = $this->gacl_api->get_object_id('unit_test','object_1', 'AXO'); $message = 'get_object_id failed'; $this->assert($result, $message); return $result; } function add_object_axo() { $result = $this->gacl_api->add_object('unit_test', 'Object 1', 'object_1', 999, 0, 'AXO'); $message = 'add_object failed'; $this->assert($result, $message); } function del_object_axo() { $result = $this->gacl_api->del_object($this->get_object_id_axo(), 'AXO'); $message = 'del_object failed'; $this->assert($result, $message); } /** ARO GROUP **/ function get_group_id_parent_aro() { $result = $this->gacl_api->get_group_id(NULL, 'ARO Group 1', 'ARO'); $message = 'get_group_id_parent_aro failed'; $this->assert($result, $message); return $result; } function get_group_id_child_aro() { $result = $this->gacl_api->get_group_id(NULL, 'ARO Group 2', 'ARO'); $message = 'get_group_id_child_aro failed'; $this->assert($result, $message); return $result; } function get_group_parent_id_aro() { $parent_id = $this->gacl_api->get_group_parent_id($this->get_group_id_child_aro(), 'ARO'); //Make sure it matches with the actual parent. if ($parent_id === $this->get_group_id_parent_aro() ) { $result = TRUE; } else { $result = FALSE; } $message = 'get_group_parent_id_aro failed'; $this->assert($result, $message); return $result; } function get_group_data_aro() { list($id, $parent_id, $value, $name, $lft, $rgt) = $this->gacl_api->get_group_data($this->get_group_id_parent_aro(), 'ARO'); //Check all values in the resulting array. if ( $id > 0 AND $parent_id >= 0 AND strlen($name) > 0 AND $lft >= 1 AND $rgt > 1) { $result = TRUE; } else { $result = FALSE; } $message = 'get_group_data_aro failed'; $this->assert($result, $message); return $result; } function get_parent_group_objects_aro() { $group_objects = $this->gacl_api->get_group_objects($this->get_group_id_parent_aro(), 'ARO'); if (count($group_objects, COUNT_RECURSIVE) == 2 AND $group_objects['unit_test'][0] == 'john_doe') { $result = TRUE; } else { $result = FALSE; } $message = 'get_parent_group_objects_aro failed'; $this->assert($result, $message); return $result; } function get_parent_group_objects_recurse_aro() { $group_objects = $this->gacl_api->get_group_objects($this->get_group_id_parent_aro(), 'ARO', 'RECURSE'); switch (TRUE) { case count($group_objects) != 1: case !isset($group_objects['unit_test']): case count($group_objects['unit_test']) != 2: case !in_array('john_doe', $group_objects['unit_test']): case !in_array('jane_doe', $group_objects['unit_test']): $result = FALSE; break; default: $result = TRUE; } $message = 'get_parent_group_objects_recurse_aro failed'; $this->assert($result, $message); return $result; } function add_group_parent_aro() { $result = $this->gacl_api->add_group('group_1', 'ARO Group 1', 0, 'ARO'); $message = 'add_group_parent_aro failed'; $this->assert($result, $message); } function edit_group_parent_aro() { $group_id = $this->get_group_id_parent_aro(); $first_rename = $this->gacl_api->edit_group($group_id, 'group_1_tmp', 'ARO Group 1 - tmp', 0, 'ARO'); $second_rename = $this->gacl_api->edit_group($group_id,'group_1', 'ARO Group 1', 0, 'ARO'); $reparent_to_self = $this->gacl_api->edit_group($group_id,'group_1', 'ARO Group 1', $group_id, 'ARO'); if ($first_rename === TRUE AND $second_rename === TRUE AND $reparent_to_self === FALSE) { $result = TRUE; } else { $result = FALSE; } $message = 'edit_group_parent_aro failed'; $this->assert($result, $message); } function del_group_parent_reparent_aro() { $this->add_group_parent_aro(); $this->add_group_child_aro(); $this->add_parent_group_object_aro(); $this->add_child_group_object_aro(); $result = $this->gacl_api->del_group($this->get_group_id_parent_aro(), TRUE, 'ARO'); $this->del_child_group_object_aro(); $this->del_group_child_aro(); $message = 'del_group_parent_no_reparent_aro failed'; $this->assert($result, $message); } function del_group_parent_no_reparent_aro() { $this->add_group_parent_aro(); $this->add_group_child_aro(); $this->add_parent_group_object_aro(); $this->add_child_group_object_aro(); $result = $this->gacl_api->del_group($this->get_group_id_parent_aro(), FALSE, 'ARO'); $message = 'del_group_parent_reparent_aro failed'; $this->assert($result, $message); } function del_group_parent_aro() { $result = $this->gacl_api->del_group($this->get_group_id_parent_aro(), TRUE, 'ARO'); $message = 'del_group_parent_aro failed'; $this->assert($result, $message); } function add_group_child_aro() { $result = $this->gacl_api->add_group('group_2', 'ARO Group 2', $this->get_group_id_parent_aro(), 'ARO'); $message = 'add_group_child failed'; $this->assert($result, $message); } function del_group_child_aro() { $result = $this->gacl_api->del_group($this->get_group_id_child_aro(), TRUE, 'ARO'); $message = 'del_group failed'; $this->assert($result, $message); } function add_parent_group_object_aro() { $result = $this->gacl_api->add_group_object($this->get_group_id_parent_aro(), 'unit_test', 'john_doe', 'ARO'); $message = 'add_parent_group_object failed'; $this->assert($result, $message); } function del_parent_group_object_aro() { $result = $this->gacl_api->del_group_object($this->get_group_id_parent_aro(), 'unit_test', 'john_doe', 'ARO'); $message = 'del_group_object failed'; $this->assert($result, $message); } function add_child_group_object_aro() { $result = $this->gacl_api->add_group_object($this->get_group_id_child_aro(), 'unit_test', 'jane_doe', 'ARO'); $message = 'add_child_group_object failed'; $this->assert($result, $message); } function del_child_group_object_aro() { $result = $this->gacl_api->del_group_object($this->get_group_id_child_aro(), 'unit_test', 'jane_doe', 'ARO'); $message = 'del_child_group_object failed'; $this->assert($result, $message); } /** AXO GROUP **/ function get_group_id_parent_axo() { $result = $this->gacl_api->get_group_id(NULL, 'AXO Group 1', 'AXO'); $message = 'get_group_id_parent_aro failed'; $this->assert($result, $message); return $result; } function get_group_id_child_axo() { $result = $this->gacl_api->get_group_id(NULL, 'AXO Group 2', 'AXO'); $message = 'get_group_id_child_axo failed'; $this->assert($result, $message); return $result; } function get_group_parent_id_axo() { $parent_id = $this->gacl_api->get_group_parent_id($this->get_group_id_child_axo(), 'AXO'); //Make sure it matches with the actual parent. if ($parent_id === $this->get_group_id_parent_axo() ) { $result = TRUE; } else { $result = FALSE; } $message = 'get_group_parent_id_aro failed'; $this->assert($result, $message); return $result; } function get_group_data_axo() { list($id, $parent_id, $value, $name, $lft, $rgt) = $this->gacl_api->get_group_data($this->get_group_id_parent_axo(), 'AXO'); //Check all values in the resulting array. if ( $id > 0 AND $parent_id >= 0 AND strlen($name) > 0 AND $lft >= 1 AND $rgt > 1) { $result = TRUE; } else { $result = FALSE; } $message = 'get_group_data_axo failed'; $this->assert($result, $message); return $result; } function add_group_parent_axo() { $result = $this->gacl_api->add_group('group_1', 'AXO Group 1', 0, 'AXO'); $message = 'add_group failed'; $this->assert($result, $message); } function del_group_parent_axo() { $result = $this->gacl_api->del_group($this->get_group_id_parent_axo(), TRUE, 'AXO'); $message = 'del_group failed'; $this->assert($result, $message); } function add_group_child_axo() { $result = $this->gacl_api->add_group('group_2', 'AXO Group 2', $this->get_group_id_parent_axo(), 'AXO'); $message = 'add_group failed'; $this->assert($result, $message); } function del_group_child_axo() { $result = $this->gacl_api->del_group($this->get_group_id_child_axo(), TRUE, 'AXO'); $message = 'del_group failed'; $this->assert($result, $message); } function add_group_object_axo() { $result = $this->gacl_api->add_group_object($this->get_group_id_parent_axo(), 'unit_test', 'object_1', 'AXO'); $message = 'add_group_object failed'; $this->assert($result, $message); } function del_group_object_axo() { $result = $this->gacl_api->del_group_object($this->get_group_id_parent_axo(), 'unit_test', 'object_1', 'AXO'); $message = 'del_group_object failed'; $this->assert($result, $message); } } // initialise test suite $suite = new gacl_test_suite; //This comes in handy. //$suite->gacl_api->db->debug=TRUE; // general $suite->addTest(new phpgacl_api_test('get_version')); $suite->addTest(new phpgacl_api_test('get_schema_version')); $suite->addTest(new phpgacl_api_test('count_all')); // build structure $suite->addTest(new phpgacl_api_test('add_object_section_aco')); $suite->addTest(new phpgacl_api_test('get_object_section_section_id_aco')); $suite->addTest(new phpgacl_api_test('add_object_aco')); $suite->addTest(new phpgacl_api_test('get_object_id_aco')); $suite->addTest(new phpgacl_api_test('add_object_section_aro')); $suite->addTest(new phpgacl_api_test('get_object_section_section_id_aco')); $suite->addTest(new phpgacl_api_test('add_object_aro')); //Test the below with ACLs as well... I haven't gotten around to that just yet. $suite->addTest(new phpgacl_api_test('edit_object_section_aro')); $suite->addTest(new phpgacl_api_test('get_object_id_aro')); $suite->addTest(new phpgacl_api_test('add_object2_aro')); $suite->addTest(new phpgacl_api_test('get_object2_id_aro')); $suite->addTest(new phpgacl_api_test('add_object_section_axo')); $suite->addTest(new phpgacl_api_test('get_object_section_section_id_axo')); $suite->addTest(new phpgacl_api_test('add_object_axo')); $suite->addTest(new phpgacl_api_test('get_object_id_axo')); $suite->addTest(new phpgacl_api_test('add_group_parent_aro')); $suite->addTest(new phpgacl_api_test('edit_group_parent_aro')); $suite->addTest(new phpgacl_api_test('get_group_id_parent_aro')); $suite->addTest(new phpgacl_api_test('get_group_data_aro')); $suite->addTest(new phpgacl_api_test('add_group_child_aro')); $suite->addTest(new phpgacl_api_test('get_group_id_child_aro')); $suite->addTest(new phpgacl_api_test('get_group_parent_id_aro')); $suite->addTest(new phpgacl_api_test('add_parent_group_object_aro')); //Try adding twice. Both times should return true. $suite->addTest(new phpgacl_api_test('add_parent_group_object_aro')); $suite->addTest(new phpgacl_api_test('add_child_group_object_aro')); $suite->addTest(new phpgacl_api_test('get_parent_group_objects_aro')); $suite->addTest(new phpgacl_api_test('get_parent_group_objects_recurse_aro')); $suite->addTest(new phpgacl_api_test('add_group_parent_axo')); $suite->addTest(new phpgacl_api_test('get_group_id_parent_axo')); $suite->addTest(new phpgacl_api_test('get_group_data_axo')); $suite->addTest(new phpgacl_api_test('add_group_child_axo')); $suite->addTest(new phpgacl_api_test('get_group_id_child_axo')); $suite->addTest(new phpgacl_api_test('add_group_object_axo')); $suite->addTest(new phpgacl_api_test('get_group_parent_id_axo')); // clean up... $suite->addTest(new phpgacl_api_test('del_parent_group_object_aro')); $suite->addTest(new phpgacl_api_test('del_child_group_object_aro')); $suite->addTest(new phpgacl_api_test('del_group_child_aro')); $suite->addTest(new phpgacl_api_test('del_group_parent_aro')); $suite->addTest(new phpgacl_api_test('del_group_object_axo')); $suite->addTest(new phpgacl_api_test('del_group_child_axo')); $suite->addTest(new phpgacl_api_test('del_group_parent_axo')); $suite->addTest(new phpgacl_api_test('del_object_aco')); $suite->addTest(new phpgacl_api_test('del_object_section_aco')); //Test group reparenting - Order of this test is important. $suite->addTest(new phpgacl_api_test('del_group_parent_no_reparent_aro')); $suite->addTest(new phpgacl_api_test('del_group_parent_reparent_aro')); $suite->addTest(new phpgacl_api_test('del_object_aro')); $suite->addTest(new phpgacl_api_test('del_object2_aro')); $suite->addTest(new phpgacl_api_test('del_object_section_aro')); $suite->addTest(new phpgacl_api_test('del_object_axo')); $suite->addTest(new phpgacl_api_test('del_object_section_axo')); // run tests echo '

Running API tests... '; $suite->run($result); echo 'Done

'; unset ($suite); // done. ?>phpgacl-3.3.7/test_suite/random_acl_check.php0100644025754300001440000001012307755247743020227 0ustar ipsousers Random ACL Check
_db_table_prefix . $type . ' ORDER BY RAND()';
	
	if ( is_scalar ($limit) )
	{
		$rs = $GLOBALS['gacl_api']->db->SelectLimit ($sql,$limit);
	}
	else
	{
		$rs = $GLOBALS['gacl_api']->db->Execute ($sql);
	}
	
	if ( !is_object ($rs) )
	{
		return FALSE;
	}
	
	$retarr = array ();
	
	while ( $row = $rs->FetchRow () )
	{
		$retarr[$row[0]] = array (
			$row[1],
			$row[2]
		);
	}
	
	return $retarr;
}

// require gacl
require_once (dirname (__FILE__) . '/../admin/gacl_admin.inc.php');

/*
 * Let's get ready to RUMBLE!!!
 */
$scale = 100;

echo 'Random ACL Check' . "\n";
echo '    Scale: ' . $scale . "\n\n";

$overall_start = getmicrotime ();

mt_srand ((double)microtime () *10000);

echo "Generating Test Data Set\n";
flush ();

$start_time = getmicrotime ();

$start = 1;
$max = 5 * $scale;
// $max = 1;

$check = array ();

$aco = random_objects ('aco', $max);
$aro = random_objects ('aro', $max);
$axo = random_objects ('axo', $max);

for ( $i = $start; $i <= $max; $i++ )
{
	$rand_aco_id = array_mt_rand ($aco, 1);
	$rand_aro_id = array_mt_rand ($aro, 1);
	$rand_axo_id = array_mt_rand ($axo, 1);
	
	// echo '    Rand ACO: '. $rand_aco_id .' ARO: '. $rand_aro_id . ' AXO: ' . $rand_axo_id . "\n";
	
	$check[$i] = array (
		'aco' => $aco[$rand_aco_id],
		'aro' => $aro[$rand_aro_id],
		'axo' => $axo[$rand_axo_id]
	);
}

$elapsed = getmicrotime () - $start_time;

echo "Done\n\n";
echo '    Count:   ' . $max . "\n";
echo '    Time:    ' . $elapsed . " s\n";
echo '    Average: ' . $elapsed/$max . " s\n\n";

echo "Testing...\n";
flush ();

$best = 99999;
$worst = 0;
$total = 0;

$allowed = 0;
$denied = 0;

$allowed_time = 0;
$denied_time = 0;

foreach ( $check as $i => $data )
{
	echo '    Trying: ACO Section: '. $data['aco'][0] .' Value: '. $data['aco'][1] .' ARO Section: '. $data['aro'][0] .' Value: '. $data['aro'][1] . ' ARO Section: '. $data['axo'][0] .' Value: '. $data['axo'][1] . "\n";
	
	$check_start = getmicrotime ();
	
	$allow = $gacl_api->acl_check ($data['aco'][0],$data['aco'][1],$data['aro'][0],$data['aro'][1],$data['axo'][0],$data['axo'][1]);
	
	$check_time = getmicrotime () - $check_start;
	
	if ( $allow ) {
		echo '    ' . $i . ". Access Granted";
		$allowed++;
		$allowed_time += $check_time;
	} else {
		echo '    ' . $i . ". Access Denied";
		$denied++;
		$denied_time += $check_time;
	}
	
	echo ' - ' . $check_time . " s\n";
	
	$best = min ($best, $check_time);
	$worst = max ($worst, $check_time);
	$total = $total + $check_time;
}

echo "Done\n";
echo '    Count:   ' . $max . "\n";
echo '    Total:   ' . $total . " s\n";
echo '    Average: ' . $total/$max . " s\n\n";

echo '    Allowed: ' . $allowed . "\n";
echo '    Total:   ' . $allowed_time . " s\n";
echo '    Average: ' . $allowed_time/$allowed . " s\n\n";

echo '    Denied:  ' . $denied . "\n";
echo '    Total:   ' . $denied_time . " s\n";
echo '    Average: ' . $denied_time/$denied . " s\n\n";

echo '    Best:    ' . $best . " s\n";
echo '    Worst:   ' . $worst . " s\n\n";

// print_r ($gacl_api->db);


$elapsed = getmicrotime () - $overall_start;

echo 'All Finished' . "\n";
echo '    Total Time: ' . $elapsed . " s\n";

/*
 * end of script
 */

?>
phpgacl-3.3.7/test_suite/acl_tests.php0100644025754300001440000002101610046256337016741 0ustar ipsousersgacl_api = &$GLOBALS['gacl_api']; } function setup() { // ACO $this->aco_section[] = $this->gacl_api->add_object_section ('Test','test_aco',0,0,'ACO'); $this->aco[] = $this->gacl_api->add_object ('test_aco','Access','access',0,0,'ACO'); // ARO $this->aro_section[] = $this->gacl_api->add_object_section ('Human', 'test_human',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_human','Han','han',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_human','Lando','lando',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_human','Obi-wan','obiwan',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_human','Luke','luke',0,0,'ARO'); $this->aro_section[] = $this->gacl_api->add_object_section ('Android', 'test_android',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_android','R2D2','r2d2',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_android','C3PO','c3po',0,0,'ARO'); $this->aro_section[] = $this->gacl_api->add_object_section ('Alien', 'test_alien',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_alien','Chewie','chewie',0,0,'ARO'); $this->aro[] = $this->gacl_api->add_object ('test_alien','Hontook','hontook',0,0,'ARO'); // ARO groups $this->aro_group['root'] = $this->gacl_api->add_group('millennium_falcon_passengers', 'Millennium Falcon Passengers',0,'ARO'); $this->aro_group['crew'] = $this->gacl_api->add_group('crew', 'Crew',$this->aro_group['root'],'ARO'); $this->aro_group['passengers'] = $this->gacl_api->add_group('passengers','Passengers',$this->aro_group['root'],'ARO'); $this->aro_group['jedi'] = $this->gacl_api->add_group('jedi', 'Jedi',$this->aro_group['passengers'],'ARO'); $this->aro_group['engineers'] = $this->gacl_api->add_group('engineers', 'Engineers',$this->aro_group['root'],'ARO'); // add AROs to groups $this->gacl_api->add_group_object($this->aro_group['crew'],'test_alien','chewie','ARO'); $this->gacl_api->add_group_object($this->aro_group['crew'],'test_human','han','ARO'); $this->gacl_api->add_group_object($this->aro_group['crew'],'test_human','lando','ARO'); $this->gacl_api->add_group_object($this->aro_group['passengers'],'test_android','c3po','ARO'); $this->gacl_api->add_group_object($this->aro_group['passengers'],'test_android','r2d2','ARO'); $this->gacl_api->add_group_object($this->aro_group['jedi'],'test_human','luke','ARO'); $this->gacl_api->add_group_object($this->aro_group['jedi'],'test_human','obiwan','ARO'); $this->gacl_api->add_group_object($this->aro_group['engineers'],'test_alien','hontook','ARO'); $this->gacl_api->add_group_object($this->aro_group['engineers'],'test_android','r2d2','ARO'); $this->gacl_api->add_group_object($this->aro_group['engineers'],'test_human','han','ARO'); // AXO $this->axo_section[] = $this->gacl_api->add_object_section ('Location', 'test_location',0,0,'AXO'); $this->axo[] = $this->gacl_api->add_object ('test_location','Engines','engines',0,0,'AXO'); $this->axo[] = $this->gacl_api->add_object ('test_location','Lounge','lounge',0,0,'AXO'); $this->axo[] = $this->gacl_api->add_object ('test_location','Cockpit','cockpit',0,0,'AXO'); $this->axo[] = $this->gacl_api->add_object ('test_location','Guns','guns',0,0,'AXO'); // AXO Groups $this->axo_group['locations'] = $this->gacl_api->add_group('locations', 'Locations',0,'AXO'); // add AXOs to groups $this->gacl_api->add_group_object($this->axo_group['locations'],'test_location','engines','AXO'); $this->gacl_api->add_group_object($this->axo_group['locations'],'test_location','lounge','AXO'); $this->gacl_api->add_group_object($this->axo_group['locations'],'test_location','cockpit','AXO'); $this->gacl_api->add_group_object($this->axo_group['locations'],'test_location','guns','AXO'); // create ACLs $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),NULL,array($this->aro_group['crew']),NULL,array($this->axo_group['locations']),1,1,NULL,'Crew can go anywhere'); $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),array('test_alien'=>array('chewie')),NULL,array('test_location'=>array('engines')),NULL,0,1,NULL,'Chewie can\'t access the engines'); $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),NULL,array($this->aro_group['passengers']),array('test_location'=>array('lounge')),NULL,1,1,NULL,'Passengers are allowed in the lounge'); $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),NULL,array($this->aro_group['jedi']),array('test_location'=>array('cockpit')),NULL,1,1,NULL,'Jedi are allowed in the cockpit'); $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),array('test_human'=>array('luke')),NULL,array('test_location'=>array('guns')),NULL,1,1,NULL,'Luke can access the guns'); $this->acl[] = $this->gacl_api->add_acl(array('test_aco'=>array('access')),NULL,array($this->aro_group['engineers']),array('test_location'=>array('engines','guns')),NULL,1,1,NULL,'Engineers can access the engines and guns'); } function teardown() { // delete ACLs foreach ($this->acl as $id) { $this->gacl_api->del_acl($id); } // delete AXO groups foreach (array_reverse($this->axo_group) as $id) { $this->gacl_api->del_group($id,TRUE,'AXO'); } // delete AXOs foreach ($this->axo as $id) { $this->gacl_api->del_object($id,'AXO'); } // delete AXO sections foreach ($this->axo_section as $id) { $this->gacl_api->del_object_section($id,'AXO'); } // delete ARO groups foreach (array_reverse($this->aro_group) as $id) { $this->gacl_api->del_group($id,TRUE,'ARO'); } // delete AROs foreach ($this->aro as $id) { $this->gacl_api->del_object($id,'ARO'); } // delete ARO sections foreach ($this->aro_section as $id) { $this->gacl_api->del_object_section($id,'ARO'); } // delete ACOs foreach ($this->aco as $id) { $this->gacl_api->del_object($id,'ACO'); } // delete ACO sections foreach ($this->aco_section as $id) { $this->gacl_api->del_object_section($id,'ACO'); } } } class acl_test extends gacl_test_case { var $acl_setup; function acl_test($name) { $this->gacl_test_case($name); $this->acl_setup = &$GLOBALS['acl_setup']; } function test_check_luke_lounge() { $result = $this->gacl_api->acl_check('test_aco','access','test_human','luke','test_location','lounge'); $message = 'Luke should have access to the Lounge'; $this->assertEquals(TRUE, $result, $message); } function test_check_luke_engines() { $result = $this->gacl_api->acl_check('test_aco','access','test_human','luke','test_location','engines'); $message = 'Luke shouldn\'t have access to the Engines'; $this->assertEquals(FALSE, $result, $message); } function test_check_chewie_guns() { $result = $this->gacl_api->acl_check('test_aco','access','test_alien','chewie','test_location','guns'); $message = 'Chewie should have access to the Guns'; $this->assertEquals(TRUE, $result, $message); } function test_check_chewie_engines() { $result = $this->gacl_api->acl_check('test_aco','access','test_alien','chewie','test_location','engines'); $message = 'Chewie shouldn\'t have access to the Engines'; $this->assertEquals(FALSE, $result, $message); } function test_query_luke_lounge() { $result = $this->gacl_api->acl_query('test_aco','access','test_human','luke','test_location','lounge'); $expected = array( 'acl_id' => $this->acl_setup->acl[2], 'return_value' => '', 'allow' => TRUE ); $message = 'Luke should have access to the Lounge'; $this->assertEquals($expected, $result, $message); } } // check no previous tests failed if ( $result->failureCount() > 0 ) { echo '

Previous test failed, not running ACL check tests.

'; return; } echo '

Running ACL tests...
'; // set up test environment $acl_setup = new acl_setup; echo '  Setting Up... '; $acl_setup->setup(); echo 'Done
'; // run tests & destroy suite $suite = new gacl_test_suite('acl_test'); echo '  Running Tests... '; $suite->run($result); echo 'Done
'; unset ($suite); // tear down test environment; echo '  Cleaning Up... '; $acl_setup->teardown(); unset ($acl_setup); echo 'Done
'; echo 'Done

'; // done. ?>phpgacl-3.3.7/test_suite/stress_test.php0100644025754300001440000002223310050202642017325 0ustar ipsousers Stress Test
Stress Test' . "\n";
echo '    Scale: ' . $scale . "\n\n";

$overall_start = getmicrotime ();

mt_srand ((double)microtime () *10000);

$gacl_api->add_object_section ('System', 'system', 0, 0, 'ACO');

echo "Create ACOs\n";
flush ();


$start_time = getmicrotime ();

$start = 1;
$max = 10 * $scale;
for ( $i = $start; $i <= $max; $i++ )
{
	if ( $gacl_api->add_object ('system', 'ACO: ' . $i, $i, 10, 0, 'ACO') == FALSE )
	{
		echo "    Error creating ACO: $i.\n";
		echo '    ' . $gacl_api->_debug_msg . "\n";
	}
}

$elapsed = getmicrotime () - $start_time;

echo "Done\n";
echo '    Count:   ' . $max . "\n";
echo '    Time:    ' . $elapsed . " s\n";
echo '    Average: ' . $elapsed/$max . " s\n\n";



$gacl_api->add_object_section ('Users', 'users', 0, 0, 'ARO');

echo "Create many ARO Groups.\n";
flush ();

$start_time = getmicrotime ();

$query = 'SELECT id FROM '.$gacl_api->_db_table_prefix.'aro_groups';
$ids = $gacl_api->db->GetCol($query);

// print_r ($ids);

$start = 1;
$max = 10 * $scale;

// function add_group ($name, $parent_id=0, $group_type='ARO') {
for ( $i = $start; $i <= $max; $i++ )
{
	// Find a random parent
	if ( !empty ($ids) ) {
		$parent_id = $ids[array_mt_rand ($ids, 1)];
	} else {
		$parent_id = 0;
	}
	
	$result = $gacl_api->add_group ('aro_group'.$i,'ARO Group: '. $i, $parent_id, 'ARO');
	
	if ( $result == FALSE )
	{
		echo "    Error creating ARO Group: $i.\n";
		echo '    ' . $gacl_api->_debug_msg . "\n";
	}
	else
	{
		$ids[] = $result;
	}
}

$elapsed = getmicrotime () - $start_time;

echo "Done\n";
echo '    Count:   ' . $max . "\n";
echo '    Time:    ' . $elapsed . " s\n";
echo '    Average: ' . $elapsed/$max . " s\n\n";



echo "Create AROs & assign to ARO Groups\n";
flush ();

$start_time = getmicrotime ();

$start = 1;
$max = 1000 * $scale;

$groups = array_keys ($gacl_api->format_groups ($gacl_api->sort_groups ('ARO'), 'ARRAY'));
$randmax = count ($groups) - 1;

for ( $i = $start; $i <= $max; $i++ )
{
	if ( $gacl_api->add_object ('users', 'ARO: '. $i, $i, 10, 0, 'ARO') == FALSE )
	{
		echo "    Error creating ARO: $i.
\n"; echo ' ' . $gacl_api->_debug_msg . "\n"; } else { // Assign to random groups. $rand_key = $groups[mt_rand (0, $randmax)]; $gacl_api->add_group_object ($rand_key, 'users', $i, 'ARO'); } } $elapsed = getmicrotime () - $start_time; echo "Done\n"; echo ' Count: ' . $max . "\n"; echo ' Time: ' . $elapsed . " s\n"; echo ' Average: ' . $elapsed/$max . " s\n\n"; $gacl_api->add_object_section ('Users', 'users', 0, 0, 'AXO'); echo "Create many AXO Groups.\n"; flush (); $start_time = getmicrotime (); $query = 'SELECT id FROM '.$gacl_api->_db_table_prefix.'axo_groups'; $ids = $gacl_api->db->GetCol($query); $start = 1; $max = 10 * $scale; // function add_group ($name, $parent_id=0, $group_type='ARO') { for ( $i = $start; $i <= $max; $i++ ) { // Find a random parent if ( !empty ($ids) ) { $parent_id = $ids[array_mt_rand ($ids, 1)]; } else { $parent_id = 0; } $result = $gacl_api->add_group ('axo_group'.$i,'AXO Group: '. $i, $parent_id, 'AXO'); if ( $result == FALSE ) { echo " Error creating AXO Group: $i.\n"; echo ' ' . $gacl_api->_debug_msg . "\n"; } else { $ids[] = $result; } } $elapsed = getmicrotime () - $start_time; echo "Done\n"; echo ' Count: ' . $max . "\n"; echo ' Time: ' . $elapsed . " s\n"; echo ' Average: ' . $elapsed/$max . " s\n\n"; echo "Create AXOs & assign to AXO Groups\n"; flush (); $start_time = getmicrotime (); $start = 1; $max = 1000 * $scale; // $groups = array_keys ($gacl_api->format_groups ($gacl_api->sort_groups ('AXO'), 'ARRAY')); $rand_max = count ($groups) - 1; for ( $i = $start; $i <= $max; $i++ ) { if ( $gacl_api->add_object ('users', 'AXO: ' . $i, $i, 10, 0, 'AXO') == FALSE ) { echo " Error creating ARO: $i.
\n"; echo ' ' . $gacl_api->_debug_msg . "\n"; } else { // Assign to random groups. $rand_key = $groups[mt_rand (0, $rand_max)]; $gacl_api->add_group_object ($rand_key, 'users', $i, 'AXO'); } } $elapsed = getmicrotime () - $start_time; echo "Done\n"; echo ' Count: ' . $max . "\n"; echo ' Time: ' . $elapsed . " s\n"; echo ' Average: ' . $elapsed/$max . " s\n\n"; echo "Generate random ACLs now.\n"; flush (); $start_time = getmicrotime (); $start = 1; $max = 10 * $scale; $aco_list = $gacl_api->get_object ('system', 1, 'ACO'); $query = 'SELECT id, name FROM '.$gacl_api->_db_table_prefix.''. $gacl_.'aro_groups ORDER BY parent_id DESC LIMIT 100'; $rs = $gacl_api->db->Execute($query); $aro_groups = $rs->GetAssoc(); $query = 'SELECT id, name FROM '.$gacl_api->_db_table_prefix.'axo_groups ORDER BY parent_id DESC LIMIT 100'; $rs = $gacl_api->db->Execute($query); $axo_groups = $rs->GetAssoc(); // $aro_groups = $gacl_api->format_groups ($gacl_api->sort_groups ('ARO'), 'ARRAY'); print_r ($aro_groups); // $axo_groups = $gacl_api->format_groups ($gacl_api->sort_groups ('AXO'), 'ARRAY'); print_r ($axo_groups); for ( $i = $start; $i <= $max; $i++ ) { $rand_aco_key = array_mt_rand ($aco_list, mt_rand (2, 10)); $rand_aro_key = array_mt_rand ($aro_groups, mt_rand(2,10)); $rand_axo_key = array_mt_rand ($axo_groups, mt_rand(2,10)); $aco_array = array (); foreach ( $rand_aco_key as $aco_key ) { $aco_data = $gacl_api->get_object_data ($aco_list[$aco_key], 'ACO'); $aco_array[$aco_data[0][0]][] = $aco_data[0][1]; } // Randomly create ACLs with AXOs assigned to them. // if ($i % 2 == 0) { $axo_array = $rand_axo_key; // } if ( $gacl_api->add_acl ($aco_array, NULL, $rand_aro_key, NULL, $axo_array) == FALSE ) { echo " Error creating ACL: $i.\n"; echo ' ' . $gacl_api->_debug_msg . "\n"; // print_r (array_slice ($gacl_api->_debug_msg, -2)); } unset ($axo_array); } $elapsed = getmicrotime () - $start_time; echo "Done\n"; echo ' Count: ' . $max . "\n"; echo ' Time: ' . $elapsed . " s\n"; echo ' Average: ' . $elapsed/$max . " s\n\n"; echo "Generating Test Data Set\n"; flush (); $start_time = getmicrotime (); $start = 1; $max = 5 * $scale; // $max = 1; $check = array (); for ( $i = $start; $i <= $max; $i++ ) { $rand_aco_key = mt_rand (10,10 * $scale); $rand_aro_key = mt_rand (10,1000 * $scale); $rand_axo_key = mt_rand (10,1000 * $scale); // echo ' Rand ACO: '. $rand_aco_key .' ARO: '. $rand_aro_key . ' AXO: ' . $rand_axo_key . "\n"; $aco_data = &$gacl_api->get_object_data ($rand_aco_key, 'ACO'); $aro_data = &$gacl_api->get_object_data ($rand_aro_key, 'ARO'); $axo_data = &$gacl_api->get_object_data ($rand_axo_key, 'AXO'); $check[$i] = array ( 'aco' => $aco_data[0], 'aro' => $aro_data[0], 'axo' => $axo_data[0] ); } $elapsed = getmicrotime () - $start_time; echo "Done\n\n"; echo ' Count: ' . $max . "\n"; echo ' Time: ' . $elapsed . " s\n"; echo ' Average: ' . $elapsed/$max . " s\n\n"; echo "Testing...\n"; flush (); $best = 99999; $worst = 0; $total = 0; foreach ( $check as $i => $data ) { echo ' Trying: ACO Section: '. $data['aco'][0] .' Value: '. $data['aco'][1] .' ARO Section: '. $data['aro'][0] .' Value: '. $data['aro'][1] . ' ARO Section: '. $data['axo'][0] .' Value: '. $data['axo'][1] . "\n"; $check_start = getmicrotime (); $allow = $gacl_api->acl_check ($data['aco'][0],$data['aco'][1],$data['aro'][0],$data['aro'][1],$data['axo'][0],$data['axo'][1]); $check_time = getmicrotime () - $check_start; if ( $allow ) { echo ' ' . $i . ". Access Granted!"; } else { echo ' ' . $i . ". Access Denied!"; } echo ' - ' . $check_time . " s\n"; $best = min ($best, $check_time); $worst = max ($worst, $check_time); $total = $total + $check_time; } echo "Done\n"; echo ' Count: ' . $max . "\n\n"; echo ' Total: ' . $total . " s\n"; echo ' Average: ' . $total/$max . " s\n\n"; echo ' Best: ' . $best . " s\n"; echo ' Worst: ' . $worst . " s\n\n"; // print_r ($gacl_api->db); $elapsed = getmicrotime () - $overall_start; echo 'All Finished' . "\n"; echo ' Total Time: ' . $elapsed . " s\n"; /* * end of script */ ?>
phpgacl-3.3.7/test_suite/run.php0100644025754300001440000000767007756773036015614 0ustar ipsousersTest Results' . "\n"; echo ''."\n"; echo ''."\n"; echo $this->output; echo '
FunctionSuccess?
'."\n"; /* summary */ $nRun = $this->countTests(); $nFailures = $this->countFailures(); echo '

Summary

'."\n"; printf('

%s test%s run
', $nRun, ($nRun == 1) ? '' : 's'); printf("%s failure%s.

\n", $nFailures, ($nFailures == 1) ? '' : 's'); } function _startTest($test) { } function _endTest($test) { if ( $test->classname() != $this->class_name ) { $this->class_name = $test->classname(); $this->output .= ''. $test->classname() .''."\n"; } $this->output .= ''. $test->name(); if ($test->failed()) { $this->output .= "
    \n"; foreach ($test->getExceptions() as $exception) { $this->output .= '
  • '. $exception->getMessage() ."
  • \n"; } $this->output .= "
\n"; $outcome = ' class="fail">FAIL'; } else { $outcome = ' class="pass">OK'; } $this->output .= ''."\n"; } } /*! class custom TestCase class to allow control of error formatting can also be used for custom assert functions !*/ class gacl_test_case extends TestCase { var $gacl_api; function gacl_test_case($name) { $this->TestCase($name); $this->gacl_api = &$GLOBALS['gacl_api']; } function setUp() { } function tearDown() { } function _formatValue($value, $class='') { if (phpversion() < '4.0.0') { return ''. htmlentities((string)$value) .''; } switch (TRUE) { case is_object($value): if (method_exists($value, 'toString')) { $translateValue = $value->toString(); } else { $translateValue = serialize($value); } $htmlValue = htmlentities($translateValue); break; case is_array($value): ob_start(); print_r($value); $translateValue = ob_get_contents(); ob_end_clean(); $htmlValue = nl2br(str_replace(' ', '    ', htmlentities(rtrim($translateValue)))); break; case is_bool($value): $htmlValue = $value ? 'true' : 'false'; break; case phpversion() >= '4.0.4' && is_null($value): $htmlValue = 'null'; break; default: $htmlValue = htmlentities(strval($value)); } $htmlValue = '' . $htmlValue . ''; $htmlValue .= '  '; $htmlValue .= 'type:' . gettype($value); if (is_object($value)) { $htmlValue .= ', class:' . get_class($value); } $htmlValue .= ''; return $htmlValue; } } /*! class custom TestSuite class for future expansion !*/ class gacl_test_suite extends TestSuite { } $title = 'phpGACL Test Suite'; ?> <?php echo $title; ?>

Running Tests

'; // show report $result->report(); ?> phpgacl-3.3.7/test_suite/styles.css0100644025754300001440000000231307756773036016321 0ustar ipsousersbody { font-size: x-small; font-family: Verdana,Arial,Helvetica,sans-serif; background-color: #ffffff; color: #000000; } h1 { margin: 4px 0px; text-align: center; font-weight: bold; } h2 { margin: 4px 0px 0px; } p { margin: 4px 0px; } ul { margin: 2px 0px; padding-left: 20px; } div.indent { margin-left: 3%; margin-right: 3%; } table.details { margin-top: 4px; width: 94%; border-width: 0px; margin-left: 3%; margin-right: 3%; } table.details th { font-weight: bold; text-align: left; background: #a6caf0; border-width: 0px; } table.details tr { background: #eeeee0; } table.details td { border-width: 0px; } table.details td.class_name { font-weight: bold; background-color: #eeeec0; padding-left: 2px; } table.details td.function { vertical-align: top; padding-left: 10px; } table.details td.fail { background-color: #ff0000; font-weight: bold; vertical-align: middle; text-align: center; } table.details td.pass { background-color: #00ff00; font-weight: bold; vertical-align: middle; text-align: center; } .error { color: #ff0000; font-weight: bold; } code.expected { color: #00ff00; } code.actual { color: #ff0000; } span.typeinfo { color: #999999; } phpgacl-3.3.7/schema.xml0100644025754300001440000002413310476660057014051 0ustar ipsousers
SQL to be executed only to insert default rows DELETE FROM phpgacl WHERE name='version' INSERT INTO phpgacl (name,value) VALUES ('version','3.3.7') DELETE FROM phpgacl WHERE name='schema_version' INSERT INTO phpgacl (name,value) VALUES ('schema_version','2.1') enabled section_value updated_date
value hidden
SQL to be executed only to insert default rows DELETE FROM acl_sections WHERE id=1 AND value='system' INSERT INTO acl_sections (id,value,order_value,name) VALUES (1,'system',1,'System') DELETE FROM acl_sections WHERE id=2 AND value='user' INSERT INTO acl_sections (id,value,order_value,name) VALUES (2,'user',2,'User') section_valuevalue hidden
value hidden
section_valuevalue hidden
value hidden
section_valuevalue hidden
value hidden
parent_id value lftrgt
Index on aro_id aro_id
parent_id value lftrgt
Index on axo_id axo_id
phpgacl-3.3.7/CREDITS0100644025754300001440000000264610130375647013107 0ustar ipsousersMike Benoit Wrote initial "proof of concept" implementation, and maintains documentation & code base. Dan Cech Implemented the MPTT code, '_group_' feature, and ACL checks in the test suite. Gordon Luk Native ACL_Check module for Perl, and wrote comments for phpDocumentor. Ben Margolin Caught and fixed several bugs. R. Kunoth Caught and fixed several bugs. Karsten Dambekalns Sent in several patches to fix PHP warnings, and helped with the documentation. Martino Piccinato Modified several API functions to help keep data in sync across tables. Paul Munn Sent in Postgres7 table schema, plus some very good suggestions. As well helped with the documentation. Fred Cohen Sent in Oracle 8i table schema. Eric Batchelor Always there to bounce ideas off of, he has consistently helped me out in many areas, including testing. Monte Ohrt Andrei Zmievski Together they made the best template engine available for PHP. Awesome job. John Lim Author of ADODB, also the best database abstraction layer for PHP. Rasmus Lerdorf For starting what eventually became the coolest programming language ever. phpgacl-3.3.7/README0100644025754300001440000000303710130377062012733 0ustar ipsousersNAME: phpGACL - PHP Generic Access Control List. AUTHORS: Mike Benoit MAILING LISTS: See here: http://sourceforge.net/mail/?group_id=57103 INSTALL: Please refer to the docs/MANUAL or the "Manual" / API Guide link at http://phpgacl.sourceforge.net/ Consult the example.php file in this directory. UPGRADING: MAKE A BACKUP FIRST! MAKE A BACKUP FIRST! MAKE A BACKUP FIRST! Then run setup.php. It will most likely NOT work, as ADODB's XML Schema still needs some work. Upgrading from ANY version to v3.3.x WILL NOT WORK YET! The schema format has changed way to much. Unless your running PostgreSQL, then upgrading from 3.1.x or older to 3.2.x or newer will require a complete dump (including column names), dropping the database, running setup.php, then importing the dump. SYNOPSIS: require_once("gacl.class.php"); $gacl_options = array( 'db_type' => 'mysql', 'db_host' => 'localhost', 'db_user' => 'root', 'db_password' => '', 'db_name' => 'gacl', ); $gacl = new gacl($gacl_options); if ( $gacl->acl_check( , , , ) ) { echo "Access Granted!"; } else { echo "Access Denied!"; } DESCRIPTION: See here: http://phpgacl.sourceforge.net/ COPYRIGHT: Copyright (c) 2004 Mike Benoit, All rights reserved. This software is released under the GNU Lesser General Public License. Please read the disclaimer at the top of the gacl.inc.php file. phpgacl-3.3.7/gacl_api.class.php0100644025754300001440000035350110330010700015414 0ustar ipsousers * */ class gacl_api extends gacl { /* * * Misc helper functions. * */ /** * showarray() * * Dump all contents of an array in HTML (kinda) * * @param array * */ function showarray($array) { echo "
\n";
		var_dump($array);
		echo "

\n"; } /** * count_all() * * Recursively counts elements in an array and sub-arrays. * * This is different from count($arg, COUNT_RECURSIVE) * in PHP >= 4.2.0, which includes sub-arrays in the count. * * @return int The returned count is a count of all scalar elements found. * * @param array Array to count */ function count_all($arg = NULL) { switch (TRUE) { case is_scalar($arg): case is_object($arg): // single object return 1; case is_array($arg): // call recursively for all elements of $arg $count = 0; foreach ($arg as $val) { $count += $this->count_all($val); } return $count; } return FALSE; } /** * get_version() * * Grabs phpGACL version from the database. * * @return string Version of phpGACL */ function get_version() { $query = "select value from ".$this->_db_table_prefix."phpgacl where name = 'version'"; $version = $this->db->GetOne($query); return $version; } /** * get_schema_version() * * Grabs phpGACL schema version from the database. * * @return string Schema Version */ function get_schema_version() { $query = "select value from ".$this->_db_table_prefix."phpgacl where name = 'schema_version'"; $version = $this->db->GetOne($query); return $version; } /* * * ACL * */ /** * consolidated_edit_acl() * * Add's an ACL but checks to see if it can consolidate it with another one first. * * This ONLY works with ACO's and ARO's. Groups, and AXO are excluded. * As well this function is designed for handling ACLs with return values, * and consolidating on the return_value, in hopes of keeping the ACL count to a minimum. * * A return value of false must _always_ be handled outside this function. * As this function will remove AROs from ACLs and return false, in most cases * you will need to a create a completely new ACL on a false return. * * @return bool Special boolean return value. See note. * * @param string ACO Section Value * @param string ACO Value * @param string ARO Section Value * @param string ARO Value * @param string Return Value of ACL */ function consolidated_edit_acl($aco_section_value, $aco_value, $aro_section_value, $aro_value, $return_value) { $this->debug_text("consolidated_edit_acl(): ACO Section Value: $aco_section_value ACO Value: $aco_value ARO Section Value: $aro_section_value ARO Value: $aro_value Return Value: $return_value"); $acl_ids = array(); if (empty($aco_section_value) ) { $this->debug_text("consolidated_edit_acl(): ACO Section Value ($aco_section_value) is empty, this is required!"); return false; } if (empty($aco_value) ) { $this->debug_text("consolidated_edit_acl(): ACO Value ($aco_value) is empty, this is required!"); return false; } if (empty($aro_section_value) ) { $this->debug_text("consolidated_edit_acl(): ARO Section Value ($aro_section_value) is empty, this is required!"); return false; } if (empty($aro_value) ) { $this->debug_text("consolidated_edit_acl(): ARO Value ($aro_value) is empty, this is required!"); return false; } if (empty($return_value) ) { $this->debug_text("consolidated_edit_acl(): Return Value ($return_value) is empty, this is required!"); return false; } //See if a current ACL exists with the current objects, excluding return value $current_acl_ids = $this->search_acl($aco_section_value, $aco_value, $aro_section_value, $aro_value, FALSE, FALSE, FALSE, FALSE, FALSE); //showarray($current_acl_ids); if (is_array($current_acl_ids)) { $this->debug_text("add_consolidated_acl(): Found current ACL_IDs, counting ACOs"); foreach ($current_acl_ids as $current_acl_id) { //Check to make sure these ACLs only have a single ACO mapped to them. $current_acl_array = &$this->get_acl($current_acl_id); //showarray($current_acl_array); $this->debug_text("add_consolidated_acl(): Current Count: ".$this->count_all($current_acl_array['aco']).""); if ( $this->count_all($current_acl_array['aco']) == 1) { $this->debug_text("add_consolidated_acl(): ACL ID: $current_acl_id has 1 ACO."); //Test to see if the return values match, if they do, no need removing or appending ARO. Just return true. if ($current_acl_array['return_value'] == $return_value) { $this->debug_text("add_consolidated_acl(): ACL ID: $current_acl_id has 1 ACO, and the same return value. No need to modify."); return true; } $acl_ids[] = $current_acl_id; } } } //showarray($acl_ids); $acl_ids_count = count($acl_ids); //If acl_id's turns up more then one ACL, lets remove the ARO from all of them in hopes to //eliminate any conflicts. if (is_array($acl_ids) AND $acl_ids_count > 0) { $this->debug_text("add_consolidated_acl(): Removing specified ARO from existing ACL."); foreach ($acl_ids as $acl_id) { //Remove ARO from current ACLs, so we don't create conflicting ACLs later on. if (!$this->shift_acl($acl_id, array($aro_section_value => array($aro_value)) ) ) { $this->debug_text("add_consolidated_acl(): Error removing specified ARO from ACL ID: $acl_id"); return false; } } } else { $this->debug_text("add_consolidated_acl(): Didn't find any current ACLs with a single ACO. "); } //unset($acl_ids); $acl_ids = array(); unset($acl_ids_count); //At this point there should be no conflicting ACLs, searching for an existing ACL with the new values. $new_acl_ids = $this->search_acl($aco_section_value, $aco_value, FALSE, FALSE, NULL, NULL, NULL, NULL, $return_value); $new_acl_count = count($new_acl_ids); //showarray($new_acl_ids); if (is_array($new_acl_ids)) { $this->debug_text("add_consolidated_acl(): Found new ACL_IDs, counting ACOs"); foreach ($new_acl_ids as $new_acl_id) { //Check to make sure these ACLs only have a single ACO mapped to them. $new_acl_array = &$this->get_acl($new_acl_id); //showarray($new_acl_array); $this->debug_text("add_consolidated_acl(): New Count: ".$this->count_all($new_acl_array['aco']).""); if ( $this->count_all($new_acl_array['aco']) == 1) { $this->debug_text("add_consolidated_acl(): ACL ID: $new_acl_id has 1 ACO, append should be able to take place."); $acl_ids[] = $new_acl_id; } } } //showarray($acl_ids); $acl_ids_count = count($acl_ids); if (is_array($acl_ids) AND $acl_ids_count == 1) { $this->debug_text("add_consolidated_acl(): Appending specified ARO to existing ACL."); $acl_id=$acl_ids[0]; if (!$this->append_acl($acl_id, array($aro_section_value => array($aro_value)) ) ) { $this->debug_text("add_consolidated_acl(): Error appending specified ARO to ACL ID: $acl_id"); return false; } $this->debug_text("add_consolidated_acl(): Hot damn, ACL consolidated!"); return true; } elseif($acl_ids_count > 1) { $this->debug_text("add_consolidated_acl(): Found more then one ACL with a single ACO. Possible conflicting ACLs."); return false; } elseif ($acl_ids_count == 0) { $this->debug_text("add_consolidated_acl(): No existing ACLs found, create a new one."); if (!$this->add_acl( array( $aco_section_value => array($aco_value) ), array( $aro_section_value => array($aro_value) ), NULL, NULL, NULL, TRUE, TRUE, $return_value, NULL) ) { $this->debug_text("add_consolidated_acl(): Error adding new ACL for ACO Section: $aco_section_value ACO Value: $aco_value Return Value: $return_value"); return false; } $this->debug_text("add_consolidated_acl(): ADD_ACL() successfull, returning True."); return true; } $this->debug_text("add_consolidated_acl(): Returning false."); return false; } /** * search_acl() * * Searches for ACL's with specified objects mapped to them. * * NULL values are included in the search, if you want to ignore * for instance aro_groups use FALSE instead of NULL. * * @return array containing ACL IDs if search is successful * * @param string ACO Section Value * @param string ACO Value * @param string ARO Section Value * @param string ARO Value * @param string ARO Group Name * @param string AXO Section Value * @param string AXO Value * @param string AXO Group Name * @param string Return Value */ function search_acl($aco_section_value=NULL, $aco_value=NULL, $aro_section_value=NULL, $aro_value=NULL, $aro_group_name=NULL, $axo_section_value=NULL, $axo_value=NULL, $axo_group_name=NULL, $return_value=NULL) { $this->debug_text("search_acl(): aco_section_value: $aco_section_value aco_value: $aco_value, aro_section_value: $aro_section_value, aro_value: $aro_value, aro_group_name: $aro_group_name, axo_section_value: $axo_section_value, axo_value: $axo_value, axo_group_name: $axo_group_name, return_value: $return_value"); $query = ' SELECT a.id FROM '. $this->_db_table_prefix .'acl a'; $where_query = array(); // ACO if ($aco_section_value !== FALSE AND $aco_value !== FALSE) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'aco_map ac ON a.id=ac.acl_id'; if ($aco_section_value == NULL AND $aco_value == NULL) { $where_query[] = '(ac.section_value IS NULL AND ac.value IS NULL)'; } else { $where_query[] = '(ac.section_value='. $this->db->quote($aco_section_value) .' AND ac.value='. $this->db->quote($aco_value) .')'; } } // ARO if ($aro_section_value !== FALSE AND $aro_value !== FALSE) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'aro_map ar ON a.id=ar.acl_id'; if ($aro_section_value == NULL AND $aro_value == NULL) { $where_query[] = '(ar.section_value IS NULL AND ar.value IS NULL)'; } else { $where_query[] = '(ar.section_value='. $this->db->quote($aro_section_value) .' AND ar.value='. $this->db->quote($aro_value) .')'; } } // AXO if ($axo_section_value !== FALSE AND $axo_value !== FALSE) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'axo_map ax ON a.id=ax.acl_id'; if ($axo_section_value == NULL AND $axo_value == NULL) { $where_query[] = '(ax.section_value IS NULL AND ax.value IS NULL)'; } else { $where_query[] = '(ax.section_value='. $this->db->quote($axo_section_value) .' AND ax.value='. $this->db->quote($axo_value) .')'; } } // ARO Group if ($aro_group_name !== FALSE) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'aro_groups_map arg ON a.id=arg.acl_id LEFT JOIN '. $this->_db_table_prefix .'aro_groups rg ON arg.group_id=rg.id'; if ($aro_group_name == NULL) { $where_query[] = '(rg.name IS NULL)'; } else { $where_query[] = '(rg.name='. $this->db->quote($aro_group_name) .')'; } } // AXO Group if ($axo_group_name !== FALSE) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'axo_groups_map axg ON a.id=axg.acl_id LEFT JOIN '. $this->_db_table_prefix .'axo_groups xg ON axg.group_id=xg.id'; if ($axo_group_name == NULL) { $where_query[] = '(xg.name IS NULL)'; } else { $where_query[] = '(xg.name='. $this->db->quote($axo_group_name) .')'; } } if ($return_value != FALSE) { if ($return_value == NULL) { $where_query[] = '(a.return_value IS NULL)'; } else { $where_query[] = '(a.return_value='. $this->db->quote($return_value) .')'; } } if (count($where_query) > 0) { $query .= ' WHERE '. implode (' AND ', $where_query); } return $this->db->GetCol($query); } /** * append_acl() * * Appends objects on to a specific ACL. * * @return bool TRUE if successful, FALSE otherwise. * * @param int ACL ID # * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] */ function append_acl($acl_id, $aro_array=NULL, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $aco_array=NULL) { $this->debug_text("append_acl(): ACL_ID: $acl_id"); $update = 0; if (empty($acl_id)) { $this->debug_text("append_acl(): No ACL_ID specified! ACL_ID: $acl_id"); return false; } //Grab ACL data. $acl_array = &$this->get_acl($acl_id); //Append each object type seperately. if (is_array($aro_array) AND count($aro_array) > 0) { $this->debug_text("append_acl(): Appending ARO's"); while (list($aro_section_value,$aro_value_array) = @each($aro_array)) { foreach ($aro_value_array as $aro_value) { if ( count($acl_array['aro'][$aro_section_value]) != 0 ) { if (!in_array($aro_value, $acl_array['aro'][$aro_section_value])) { $this->debug_text("append_acl(): ARO Section Value: $aro_section_value ARO VALUE: $aro_value"); $acl_array['aro'][$aro_section_value][] = $aro_value; $update=1; } else { $this->debug_text("append_acl(): Duplicate ARO, ignoring... "); } } else { //Array is empty so add this aro value. $acl_array['aro'][$aro_section_value][] = $aro_value; $update = 1; } } } } if (is_array($aro_group_ids) AND count($aro_group_ids) > 0) { $this->debug_text("append_acl(): Appending ARO_GROUP_ID's"); while (list(,$aro_group_id) = @each($aro_group_ids)) { if (!is_array($acl_array['aro_groups']) OR !in_array($aro_group_id, $acl_array['aro_groups'])) { $this->debug_text("append_acl(): ARO Group ID: $aro_group_id"); $acl_array['aro_groups'][] = $aro_group_id; $update = 1; } else { $this->debug_text("append_acl(): Duplicate ARO_Group_ID, ignoring... "); } } } if (is_array($axo_array) AND count($axo_array) > 0) { $this->debug_text("append_acl(): Appending AXO's"); while (list($axo_section_value,$axo_value_array) = @each($axo_array)) { foreach ($axo_value_array as $axo_value) { if (!in_array($axo_value, $acl_array['axo'][$axo_section_value])) { $this->debug_text("append_acl(): AXO Section Value: $axo_section_value AXO VALUE: $axo_value"); $acl_array['axo'][$axo_section_value][] = $axo_value; $update = 1; } else { $this->debug_text("append_acl(): Duplicate AXO, ignoring... "); } } } } if (is_array($axo_group_ids) AND count($axo_group_ids) > 0) { $this->debug_text("append_acl(): Appending AXO_GROUP_ID's"); while (list(,$axo_group_id) = @each($axo_group_ids)) { if (!is_array($acl_array['axo_groups']) OR !in_array($axo_group_id, $acl_array['axo_groups'])) { $this->debug_text("append_acl(): AXO Group ID: $axo_group_id"); $acl_array['axo_groups'][] = $axo_group_id; $update = 1; } else { $this->debug_text("append_acl(): Duplicate ARO_Group_ID, ignoring... "); } } } if (is_array($aco_array) AND count($aco_array) > 0) { $this->debug_text("append_acl(): Appending ACO's"); while (list($aco_section_value,$aco_value_array) = @each($aco_array)) { foreach ($aco_value_array as $aco_value) { if (!in_array($aco_value, $acl_array['aco'][$aco_section_value])) { $this->debug_text("append_acl(): ACO Section Value: $aco_section_value ACO VALUE: $aco_value"); $acl_array['aco'][$aco_section_value][] = $aco_value; $update = 1; } else { $this->debug_text("append_acl(): Duplicate ACO, ignoring... "); } } } } if ($update == 1) { $this->debug_text("append_acl(): Update flag set, updating ACL."); //function edit_acl($acl_id, $aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $return_value=NULL, $note=NULL) { return $this->edit_acl($acl_id, $acl_array['aco'], $acl_array['aro'], $acl_array['aro_groups'], $acl_array['axo'], $acl_array['axo_groups'], $acl_array['allow'], $acl_array['enabled'], $acl_array['return_value'], $acl_array['note']); } //Return true if everything is duplicate and no ACL id updated. $this->debug_text("append_acl(): Update flag not set, NOT updating ACL."); return true; } /** * shift_acl() * * Opposite of append_acl(). Removes objects from a specific ACL. (named after PHP's array_shift()) * * @return bool TRUE if successful, FALSE otherwise. * * @param int ACL ID # * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] */ function shift_acl($acl_id, $aro_array=NULL, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $aco_array=NULL) { $this->debug_text("shift_acl(): ACL_ID: $acl_id"); $update = 0; if (empty($acl_id)) { $this->debug_text("shift_acl(): No ACL_ID specified! ACL_ID: $acl_id"); return false; } //Grab ACL data. $acl_array = &$this->get_acl($acl_id); //showarray($acl_array); //Remove each object type seperately. if (is_array($aro_array) AND count($aro_array) > 0) { $this->debug_text("shift_acl(): Removing ARO's"); while (list($aro_section_value,$aro_value_array) = @each($aro_array)) { foreach ($aro_value_array as $aro_value) { $this->debug_text("shift_acl(): ARO Section Value: $aro_section_value ARO VALUE: $aro_value"); //Only search if aro array contains data. if ( count($acl_array['aro'][$aro_section_value]) != 0 ) { $aro_key = array_search($aro_value, $acl_array['aro'][$aro_section_value]); if ($aro_key !== FALSE) { $this->debug_text("shift_acl(): Removing ARO. ($aro_key)"); unset($acl_array['aro'][$aro_section_value][$aro_key]); $update = 1; } else { $this->debug_text("shift_acl(): ARO doesn't exist, can't remove it."); } } } } } if (is_array($aro_group_ids) AND count($aro_group_ids) > 0) { $this->debug_text("shift_acl(): Removing ARO_GROUP_ID's"); while (list(,$aro_group_id) = @each($aro_group_ids)) { $this->debug_text("shift_acl(): ARO Group ID: $aro_group_id"); $aro_group_key = array_search($aro_group_id, $acl_array['aro_groups']); if ($aro_group_key !== FALSE) { $this->debug_text("shift_acl(): Removing ARO Group. ($aro_group_key)"); unset($acl_array['aro_groups'][$aro_group_key]); $update = 1; } else { $this->debug_text("shift_acl(): ARO Group doesn't exist, can't remove it."); } } } if (is_array($axo_array) AND count($axo_array) > 0) { $this->debug_text("shift_acl(): Removing AXO's"); while (list($axo_section_value,$axo_value_array) = @each($axo_array)) { foreach ($axo_value_array as $axo_value) { $this->debug_text("shift_acl(): AXO Section Value: $axo_section_value AXO VALUE: $axo_value"); $axo_key = array_search($axo_value, $acl_array['axo'][$axo_section_value]); if ($axo_key !== FALSE) { $this->debug_text("shift_acl(): Removing AXO. ($axo_key)"); unset($acl_array['axo'][$axo_section_value][$axo_key]); $update = 1; } else { $this->debug_text("shift_acl(): AXO doesn't exist, can't remove it."); } } } } if (is_array($axo_group_ids) AND count($axo_group_ids) > 0) { $this->debug_text("shift_acl(): Removing AXO_GROUP_ID's"); while (list(,$axo_group_id) = @each($axo_group_ids)) { $this->debug_text("shift_acl(): AXO Group ID: $axo_group_id"); $axo_group_key = array_search($axo_group_id, $acl_array['axo_groups']); if ($axo_group_key !== FALSE) { $this->debug_text("shift_acl(): Removing AXO Group. ($axo_group_key)"); unset($acl_array['axo_groups'][$axo_group_key]); $update = 1; } else { $this->debug_text("shift_acl(): AXO Group doesn't exist, can't remove it."); } } } if (is_array($aco_array) AND count($aco_array) > 0) { $this->debug_text("shift_acl(): Removing ACO's"); while (list($aco_section_value,$aco_value_array) = @each($aco_array)) { foreach ($aco_value_array as $aco_value) { $this->debug_text("shift_acl(): ACO Section Value: $aco_section_value ACO VALUE: $aco_value"); $aco_key = array_search($aco_value, $acl_array['aco'][$aco_section_value]); if ($aco_key !== FALSE) { $this->debug_text("shift_acl(): Removing ACO. ($aco_key)"); unset($acl_array['aco'][$aco_section_value][$aco_key]); $update = 1; } else { $this->debug_text("shift_acl(): ACO doesn't exist, can't remove it."); } } } } if ($update == 1) { //We know something was changed, so lets see if no ACO's or no ARO's are left assigned to this ACL, if so, delete the ACL completely. //$this->showarray($acl_array); $this->debug_text("shift_acl(): ACOs: ". $this->count_all($acl_array['aco']) ." AROs: ".$this->count_all($acl_array['aro']).""); if ( $this->count_all($acl_array['aco']) == 0 OR ( $this->count_all($acl_array['aro']) == 0 AND ( $this->count_all($acl_array['axo']) == 0 OR $acl_array['axo'] == FALSE) AND (count($acl_array['aro_groups']) == 0 OR $acl_array['aro_groups'] == FALSE) AND (count($acl_array['axo_groups']) == 0 OR $acl_array['axo_groups'] == FALSE) ) ) { $this->debug_text("shift_acl(): No ACOs or ( AROs AND AXOs AND ARO Groups AND AXO Groups) left assigned to this ACL (ID: $acl_id), deleting ACL."); return $this->del_acl($acl_id); } $this->debug_text("shift_acl(): Update flag set, updating ACL."); return $this->edit_acl($acl_id, $acl_array['aco'], $acl_array['aro'], $acl_array['aro_groups'], $acl_array['axo'], $acl_array['axo_groups'], $acl_array['allow'], $acl_array['enabled'], $acl_array['return_value'], $acl_array['note']); } //Return true if everything is duplicate and no ACL id updated. $this->debug_text("shift_acl(): Update flag not set, NOT updating ACL."); return true; } /** * get_acl() * * Grabs ACL data. * * @return bool FALSE if not found, or Associative Array with the following items: * * - 'aco' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * - 'aro' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * - 'axo' => Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * - 'aro_groups' => Array of Group IDs * - 'axo_groups' => Array of Group IDs * - 'acl_id' => int ACL ID # * - 'allow' => int Allow flag * - 'enabled' => int Enabled flag * - 'return_value' => string Return Value * - 'note' => string Note * * @param int ACL ID # */ function get_acl($acl_id) { $this->debug_text("get_acl(): ACL_ID: $acl_id"); if (empty($acl_id)) { $this->debug_text("get_acl(): No ACL_ID specified! ACL_ID: $acl_id"); return false; } //Grab ACL information $query = "select id, allow, enabled, return_value, note from ".$this->_db_table_prefix."acl where id = ".$acl_id.""; $acl_row = $this->db->GetRow($query); // return false if not found if (!$acl_row) { $this->debug_text("get_acl(): No ACL found for that ID! ACL_ID: $acl_id"); return false; } list($retarr['acl_id'], $retarr['allow'], $retarr['enabled'], $retarr['return_value'], $retarr['note']) = $acl_row; //Grab selected ACO's $query = "select distinct a.section_value, a.value, c.name, b.name from ".$this->_db_table_prefix."aco_map a, ".$this->_db_table_prefix."aco b, ".$this->_db_table_prefix."aco_sections c where ( a.section_value=b.section_value AND a.value = b.value) AND b.section_value=c.value AND a.acl_id = $acl_id"; $rs = $this->db->Execute($query); $rows = $rs->GetRows(); $retarr['aco'] = array(); while (list(,$row) = @each($rows)) { list($section_value, $value, $section, $aco) = $row; $this->debug_text("Section Value: $section_value Value: $value Section: $section ACO: $aco"); $retarr['aco'][$section_value][] = $value; } //showarray($aco); //Grab selected ARO's $query = "select distinct a.section_value, a.value, c.name, b.name from ".$this->_db_table_prefix."aro_map a, ".$this->_db_table_prefix."aro b, ".$this->_db_table_prefix."aro_sections c where ( a.section_value=b.section_value AND a.value = b.value) AND b.section_value=c.value AND a.acl_id = $acl_id"; $rs = $this->db->Execute($query); $rows = $rs->GetRows(); $retarr['aro'] = array(); while (list(,$row) = @each($rows)) { list($section_value, $value, $section, $aro) = $row; $this->debug_text("Section Value: $section_value Value: $value Section: $section ARO: $aro"); $retarr['aro'][$section_value][] = $value; } //showarray($options_aro); //Grab selected AXO's $query = "select distinct a.section_value, a.value, c.name, b.name from ".$this->_db_table_prefix."axo_map a, ".$this->_db_table_prefix."axo b, ".$this->_db_table_prefix."axo_sections c where ( a.section_value=b.section_value AND a.value = b.value) AND b.section_value=c.value AND a.acl_id = $acl_id"; $rs = $this->db->Execute($query); $rows = $rs->GetRows(); $retarr['axo'] = array(); while (list(,$row) = @each($rows)) { list($section_value, $value, $section, $axo) = $row; $this->debug_text("Section Value: $section_value Value: $value Section: $section AXO: $axo"); $retarr['axo'][$section_value][] = $value; } //showarray($options_aro); //Grab selected ARO groups. $retarr['aro_groups'] = array(); $query = "select distinct group_id from ".$this->_db_table_prefix."aro_groups_map where acl_id = $acl_id"; $retarr['aro_groups'] = $this->db->GetCol($query); //showarray($selected_groups); //Grab selected AXO groups. $retarr['axo_groups'] = array(); $query = "select distinct group_id from ".$this->_db_table_prefix."axo_groups_map where acl_id = $acl_id"; $retarr['axo_groups'] = $this->db->GetCol($query); //showarray($selected_groups); return $retarr; } /** * is_conflicting_acl() * * Checks for conflicts when adding a specific ACL. * * @return bool Returns true if conflict is found. * * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Array of ACL IDs to ignore from the result set. * */ function is_conflicting_acl($aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $ignore_acl_ids=NULL) { //Check for potential conflicts. Ignore groups, as groups will almost always have "conflicting" ACLs. //Thats part of inheritance. if (!is_array($aco_array)) { $this->debug_text('is_conflicting_acl(): Invalid ACO Array.'); return FALSE; } if (!is_array($aro_array)) { $this->debug_text('is_conflicting_acl(): Invalid ARO Array.'); return FALSE; } $query = ' SELECT a.id FROM '. $this->_db_table_prefix .'acl a LEFT JOIN '. $this->_db_table_prefix .'aco_map ac ON ac.acl_id=a.id LEFT JOIN '. $this->_db_table_prefix .'aro_map ar ON ar.acl_id=a.id LEFT JOIN '. $this->_db_table_prefix .'axo_map ax ON ax.acl_id=a.id LEFT JOIN '. $this->_db_table_prefix .'axo_groups_map axg ON axg.acl_id=a.id LEFT JOIN '. $this->_db_table_prefix .'axo_groups xg ON xg.id=axg.group_id '; //ACO foreach ($aco_array as $aco_section_value => $aco_value_array) { $this->debug_text("is_conflicting_acl(): ACO Section Value: $aco_section_value ACO VALUE: $aco_value_array"); //showarray($aco_array); if (!is_array($aco_value_array)) { $this->debug_text('is_conflicting_acl(): Invalid Format for ACO Array item. Skipping...'); continue; // return TRUE; } //Move the below line in to the LEFT JOIN above for PostgreSQL sake. //'ac1' => 'ac.acl_id=a.id', $where_query = array( 'ac2' => '(ac.section_value='. $this->db->quote($aco_section_value) .' AND ac.value IN (\''. implode ('\',\'', $aco_value_array) .'\'))' ); //ARO foreach ($aro_array as $aro_section_value => $aro_value_array) { $this->debug_text("is_conflicting_acl(): ARO Section Value: $aro_section_value ARO VALUE: $aro_value_array"); if (!is_array($aro_value_array)) { $this->debug_text('is_conflicting_acl(): Invalid Format for ARO Array item. Skipping...'); continue; // return TRUE; } $this->debug_text("is_conflicting_acl(): Search: ACO Section: $aco_section_value ACO Value: $aco_value_array ARO Section: $aro_section_value ARO Value: $aro_value_array"); //Move the below line in to the LEFT JOIN above for PostgreSQL sake. //$where_query['ar1'] = 'ar.acl_id=a.id'; $where_query['ar2'] = '(ar.section_value='. $this->db->quote($aro_section_value) .' AND ar.value IN (\''. implode ('\',\'', $aro_value_array) .'\'))'; if (is_array($axo_array) AND count($axo_array) > 0) { foreach ($axo_array as $axo_section_value => $axo_value_array) { $this->debug_text("is_conflicting_acl(): AXO Section Value: $axo_section_value AXO VALUE: $axo_value_array"); if (!is_array($axo_value_array)) { $this->debug_text('is_conflicting_acl(): Invalid Format for AXO Array item. Skipping...'); continue; // return TRUE; } $this->debug_text("is_conflicting_acl(): Search: ACO Section: $aco_section_value ACO Value: $aco_value_array ARO Section: $aro_section_value ARO Value: $aro_value_array AXO Section: $axo_section_value AXO Value: $axo_value_array"); //$where_query['ax1'] = 'ax.acl_id=x.id'; $where_query['ax1'] = 'ax.acl_id=a.id'; $where_query['ax2'] = '(ax.section_value='. $this->db->quote($axo_section_value) .' AND ax.value IN (\''. implode ('\',\'', $axo_value_array) .'\'))'; $where = 'WHERE ' . implode(' AND ', $where_query); $conflict_result = $this->db->GetCol($query . $where); if (is_array($conflict_result) AND !empty($conflict_result)) { // showarray($conflict_result); if (is_array($ignore_acl_ids)) { $conflict_result = array_diff($conflict_result, $ignore_acl_ids); } if (count($conflict_result) > 0) { $conflicting_acls_str = implode(',', $conflict_result); $this->debug_text("is_conflicting_acl(): Conflict FOUND!!! ACL_IDS: ($conflicting_acls_str)"); return TRUE; } } } } else { $where_query['ax1'] = '(ax.section_value IS NULL AND ax.value IS NULL)'; $where_query['ax2'] = 'xg.name IS NULL'; $where = 'WHERE ' . implode(' AND ', $where_query); $conflict_result = $this->db->GetCol($query . $where); if (is_array($conflict_result) AND !empty($conflict_result)) { // showarray($conflict_result); if (is_array($ignore_acl_ids)) { $conflict_result = array_diff($conflict_result, $ignore_acl_ids); } if (count($conflict_result) > 0) { $conflicting_acls_str = implode(',', $conflict_result); $this->debug_text("is_conflicting_acl(): Conflict FOUND!!! ACL_IDS: ($conflicting_acls_str)"); return TRUE; } } } } } $this->debug_text('is_conflicting_acl(): No conflicting ACL found.'); return FALSE; } /** * add_acl() * * Add's an ACL. ACO_IDS, ARO_IDS, GROUP_IDS must all be arrays. * * @return bool Return ACL ID of new ACL if successful, FALSE otherewise. * * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param int Allow flag * @param int Enabled flag * @param string Return Value * @param string Note * @param string ACL Section Value * @param int ACL ID # Specific Request */ function add_acl($aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $return_value=NULL, $note=NULL, $section_value=NULL, $acl_id=FALSE ) { $this->debug_text("add_acl():"); if (count($aco_array) == 0) { $this->debug_text("Must select at least one Access Control Object"); return false; } if (count($aro_array) == 0 AND count($aro_group_ids) == 0) { $this->debug_text("Must select at least one Access Request Object or Group"); return false; } if (empty($allow)) { $allow=0; } if (empty($enabled)) { $enabled=0; } if (!empty($section_value) AND !$this->get_object_section_section_id(NULL, $section_value, 'ACL')) { $this->debug_text("add_acl(): Section Value: $section_value DOES NOT exist in the database."); return false; } //Unique the group arrays. Later one we unique ACO/ARO/AXO arrays. if (is_array($aro_group_ids)) { $aro_group_ids = array_unique($aro_group_ids); } if (is_array($axo_group_ids)) { $axo_group_ids = array_unique($axo_group_ids); } //Check for conflicting ACLs. if ($this->is_conflicting_acl($aco_array,$aro_array,$aro_group_ids,$axo_array,$axo_group_ids,array($acl_id))) { $this->debug_text("add_acl(): Detected possible ACL conflict, not adding ACL!"); return false; } //Edit ACL if acl_id is set. This is simply if we're being called by edit_acl(). if ($this->get_acl($acl_id) == FALSE) { if ( empty($section_value) ) { $section_value='system'; if( !$this->get_object_section_section_id(NULL, $section_value, 'ACL') ) { // Use the acl section with the lowest order value. $acl_sections_table = $this->_db_table_prefix .'acl_sections'; $acl_section_order_value = $this->db->GetOne("SELECT min(order_value) from $acl_sections_table"); $query = " SELECT value FROM $acl_sections_table WHERE order_value = $acl_section_order_value "; $section_value = $this->db->GetOne($query); if ( empty($section_value) ) { $this->debug_text("add_acl(): No valid acl section found."); return false; } else { $this->debug_text("add_acl(): Using default section value: $section_value."); } } } //ACL not specified, so create acl_id if (empty($acl_id)) { //Create ACL row first, so we have the acl_id $acl_id = $this->db->GenID($this->_db_table_prefix.'acl_seq',10); //Double check the ACL ID was generated. if (empty($acl_id)) { $this->debug_text("add_acl(): ACL_ID generation failed!"); return false; } } //Begin transaction _after_ GenID. Because on the first run, if GenID has to create the sequence, //the transaction will fail. $this->db->BeginTrans(); $query = 'INSERT INTO '.$this->_db_table_prefix.'acl (id,section_value,allow,enabled,return_value,note,updated_date) VALUES('. $acl_id .','. $this->db->quote($section_value) .','. $allow .','. $enabled .','. $this->db->quote($return_value) .', '. $this->db->quote($note) .','. time() .')'; $result = $this->db->Execute($query); } else { $section_sql = ''; if ( !empty($section_value) ) { $section_sql = 'section_value='. $this->db->quote ($section_value) .','; } $this->db->BeginTrans(); //Update ACL row, and remove all mappings so they can be re-inserted. $query = ' UPDATE '. $this->_db_table_prefix .'acl SET ' . $section_sql . ' allow='. $allow .', enabled='. $enabled .', return_value='. $this->db->quote($return_value) .', note='. $this->db->quote($note) .', updated_date='. time() .' WHERE id='. $acl_id; $result = $this->db->Execute($query); if ($result) { $this->debug_text("Update completed without error, delete mappings..."); //Delete all mappings so they can be re-inserted. foreach (array('aco_map', 'aro_map', 'axo_map', 'aro_groups_map', 'axo_groups_map') as $map) { $query = 'DELETE FROM '. $this->_db_table_prefix . $map .' WHERE acl_id='. $acl_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_acl'); $this->db->RollBackTrans(); return FALSE; } } } } if (!is_object($result)) { $this->debug_db('add_acl'); $this->db->RollBackTrans(); return false; } $this->debug_text("Insert or Update completed without error, insert new mappings."); // Insert ACO/ARO/AXO mappings foreach (array('aco', 'aro', 'axo') as $map) { $map_array = ${$map .'_array'}; if (!is_array ($map_array)) { continue; } foreach ($map_array as $section_value => $value_array) { $this->debug_text ('Insert: '. strtoupper($map) .' Section Value: '. $section_value .' '. strtoupper($map) .' VALUE: '. $value_array); // $this->showarray ($aco_value_array); if (!is_array($value_array)) { $this->debug_text ('add_acl (): Invalid Format for '. strtoupper ($map) .' Array item. Skipping...'); continue; // return true; } $value_array = array_unique($value_array); foreach ($value_array as $value) { $object_id = &$this->get_object_id($section_value, $value, $map); if (empty($object_id)) { $this->debug_text('add_acl(): '. strtoupper($map) . " Object Section Value: $section_value Value: $value DOES NOT exist in the database. Skipping..."); $this->db->RollBackTrans(); return false; } $query = 'INSERT INTO '. $this->_db_table_prefix . $map .'_map (acl_id,section_value,value) VALUES ('. $acl_id .', '. $this->db->quote($section_value) .', '. $this->db->quote($value) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_acl'); $this->db->RollBackTrans(); return false; } } } } // Insert ARO/AXO GROUP mappings foreach (array('aro', 'axo') as $map) { $map_group_ids = ${$map .'_group_ids'}; if (!is_array($map_group_ids)) { continue; } foreach ($map_group_ids as $group_id) { $this->debug_text ('Insert: '. strtoupper($map) .' GROUP ID: '. $group_id); $group_data = &$this->get_group_data($group_id, $map); if (empty($group_data)) { $this->debug_text('add_acl(): '. strtoupper($map) . " Group: $group_id DOES NOT exist in the database. Skipping..."); $this->db->RollBackTrans(); return false; } $query = 'INSERT INTO '. $this->_db_table_prefix . $map .'_groups_map (acl_id,group_id) VALUES ('. $acl_id .', '. $group_id .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_acl'); $this->db->RollBackTrans(); return false; } } } $this->db->CommitTrans(); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { //Expire all cache. $this->Cache_Lite->clean('default'); } //Return only the ID in the first row. return $acl_id; } /** * edit_acl() * * Edit's an ACL, ACO_IDS, ARO_IDS, GROUP_IDS must all be arrays. * * @return bool Return TRUE if successful, FALSE otherewise. * * @param int ACL ID # to edit * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * @param array Array of Group IDs * @param int Allow flag * @param int Enabled flag * @param string Return Value * @param string Note * @param string ACL Section Value */ function edit_acl($acl_id, $aco_array, $aro_array, $aro_group_ids=NULL, $axo_array=NULL, $axo_group_ids=NULL, $allow=1, $enabled=1, $return_value=NULL, $note=NULL, $section_value=NULL) { $this->debug_text("edit_acl():"); if (empty($acl_id) ) { $this->debug_text("edit_acl(): Must specify a single ACL_ID to edit"); return false; } if (count($aco_array) == 0) { $this->debug_text("edit_acl(): Must select at least one Access Control Object"); return false; } if (count($aro_array) == 0 AND count($aro_group_ids) == 0) { $this->debug_text("edit_acl(): Must select at least one Access Request Object or Group"); return false; } if (empty($allow)) { $allow=0; } if (empty($enabled)) { $enabled=0; } //if ($this->add_acl($aco_array, $aro_array, $group_ids, $allow, $enabled, $acl_id)) { if ($this->add_acl($aco_array, $aro_array, $aro_group_ids, $axo_array, $axo_group_ids, $allow, $enabled, $return_value, $note, $section_value, $acl_id)) { return true; } else { $this->debug_text("edit_acl(): error in add_acl()"); return false; } } /** * del_acl() * * Deletes a given ACL * * @return bool Returns TRUE if successful, FALSE otherwise. * * @param int ACL ID # to delete */ function del_acl($acl_id) { $this->debug_text("del_acl(): ID: $acl_id"); if (empty($acl_id) ) { $this->debug_text("del_acl(): ACL_ID ($acl_id) is empty, this is required"); return false; } $this->db->BeginTrans(); // Delete all mappings to the ACL first foreach (array('aco_map', 'aro_map', 'axo_map', 'aro_groups_map', 'axo_groups_map') as $map) { $query = 'DELETE FROM '. $this->_db_table_prefix . $map .' WHERE acl_id='. $acl_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('del_acl'); $this->db->RollBackTrans(); return false; } } // Delete the ACL $query = 'DELETE FROM '. $this->_db_table_prefix .'acl WHERE id='. $acl_id; $this->debug_text('delete query: '. $query); $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('del_acl'); $this->db->RollBackTrans(); return false; } $this->debug_text("del_acl(): deleted ACL ID: $acl_id"); $this->db->CommitTrans(); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { //Expire all cache. $this->Cache_Lite->clean('default'); } return TRUE; } /* * * Groups * */ /** * sort_groups() * * Grabs all the groups from the database doing preliminary grouping by parent * * @return array Returns 2-Dimensional array: $array[][] = * * @param string Group Type, either 'ARO' or 'AXO' */ function sort_groups($group_type='ARO') { switch(strtolower(trim($group_type))) { case 'axo': $table = $this->_db_table_prefix .'axo_groups'; break; default: $table = $this->_db_table_prefix .'aro_groups'; break; } //Grab all groups from the database. $query = 'SELECT id, parent_id, name FROM '. $table .' ORDER BY parent_id, name'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('sort_groups'); return false; } /* * Save groups in an array sorted by parent. Should be make it easier for later on. */ $sorted_groups = array(); while ($row = $rs->FetchRow()) { $id = &$row[0]; $parent_id = &$row[1]; $name = &$row[2]; $sorted_groups[$parent_id][$id] = $name; } return $sorted_groups; } /** * format_groups() * * Takes the array returned by sort_groups() and formats for human * consumption. Recursively calls itself to produce the desired output. * * @return array Array of formatted text, ordered by group id, formatted according to $type * * @param array Output from gacl_api->sorted_groups($group_type) * @param array Output type desired, either 'TEXT', 'HTML', or 'ARRAY' * @param int Root of tree to produce * @param int Current level of depth * @param array Pass the current formatted groups object for appending via recursion. */ function format_groups($sorted_groups, $type='TEXT', $root_id=0, $level=0, $formatted_groups=NULL) { if ( !is_array ($sorted_groups) ) { return FALSE; } if ( !is_array ($formatted_groups) ) { $formatted_groups = array (); } //$this->showarray($formatted_groups); //while (list($id,$name) = @each($sorted_groups[$root_id])) { if (isset($sorted_groups[$root_id])) { //$last_id = end( array_keys($sorted_groups[$root_id])); //PHP5 compatibility $keys = array_keys($sorted_groups[$root_id]); $last_id = end($keys); unset($keys); foreach ($sorted_groups[$root_id] as $id => $name) { switch (strtoupper($type)) { case 'TEXT': /* * Formatting optimized for TEXT (combo box) output. */ if ( is_numeric($level) ) { $level = str_repeat('   ', $level); } if ( strlen($level) >= 8 ) { if ( $id == $last_id ) { $spacing = substr($level, 0, -8) .'\'- '; $level = substr($level, 0, -8) .'   '; } else { $spacing = substr($level, 0, -8) .'|- '; } } else { $spacing = $level; } $next = $level .'|  '; $text = $spacing.$name; break; case 'HTML': /* * Formatting optimized for HTML (tables) output. */ $width= $level * 20; $spacing = ""; $next = $level + 1; $text = $spacing." ".$name; break; case 'ARRAY': $next = $level; $text = $name; break; default: return FALSE; } $formatted_groups[$id] = $text; /* * Recurse if we can. */ //if (isset($sorted_groups[$id]) AND count($sorted_groups[$id]) > 0) { if (isset($sorted_groups[$id]) ) { //$this->debug_text("format_groups(): Recursing! Level: $level"); $formatted_groups = $this->format_groups($sorted_groups, $type, $id, $next, $formatted_groups); } else { //$this->debug_text("format_groups(): Found last branch!"); } } } //$this->debug_text("format_groups(): Returning final array."); return $formatted_groups; } /** * get_group_id() * * Gets the group_id given the name or value. * * Will only return one group id, so if there are duplicate names, it will return false. * * @return int Returns Group ID if found and Group ID is unique in database, otherwise, returns FALSE * * @param string Group Value * @param string Group Name * @param string Group Type, either 'ARO' or 'AXO' */ function get_group_id($value = NULL, $name = NULL, $group_type = 'ARO') { $this->debug_text("get_group_id(): Value: $value, Name: $name, Type: $group_type" ); switch(strtolower(trim($group_type))) { case 'axo': $table = $this->_db_table_prefix .'axo_groups'; break; default: $table = $this->_db_table_prefix .'aro_groups'; break; } $name = trim($name); $value = trim($value); if (empty($name) AND empty($value) ) { $this->debug_text("get_group_id(): name and value, at least one is required"); return false; } $query = 'SELECT id FROM '. $table .' WHERE '; if ( !empty($value) ) { $query .= ' value='. $this->db->quote($value); } else { $query .= ' name='. $this->db->quote($name); } $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_group_id'); return false; } $row_count = $rs->RecordCount(); if ($row_count > 1) { $this->debug_text("get_group_id(): Returned $row_count rows, can only return one. Please make your names unique."); return false; } if ($row_count == 0) { $this->debug_text("get_group_id(): Returned $row_count rows"); return false; } $row = $rs->FetchRow(); //Return the ID. return $row[0]; } /** * get_group_children() * * Gets a groups child IDs * * @return array Array of Child ID's of the referenced group * * @param int Group ID # * @param int Group Type, either 'ARO' or 'AXO' * @param string Either 'RECURSE' or 'NO_RECURSE', to recurse while fetching group children. */ function get_group_children($group_id, $group_type = 'ARO', $recurse = 'NO_RECURSE') { $this->debug_text("get_group_children(): Group_ID: $group_id Group Type: $group_type Recurse: $recurse"); switch (strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; } if (empty($group_id)) { $this->debug_text("get_group_children(): ID ($group_id) is empty, this is required"); return FALSE; } $query = ' SELECT g1.id FROM '. $table .' g1'; //FIXME-mikeb: Why is group_id in quotes? switch (strtoupper($recurse)) { case 'RECURSE': $query .= ' LEFT JOIN '. $table .' g2 ON g2.lftg1.rgt WHERE g2.id='. $group_id; break; default: $query .= ' WHERE g1.parent_id='. $group_id; } $query .= ' ORDER BY g1.value'; return $this->db->GetCol($query); } /** * get_group_data() * * Gets the group data given the GROUP_ID. * * @return array Returns numerically indexed array with the following columns: * - array[0] = (int) Group ID # * - array[1] = (int) Parent Group ID # * - array[2] = (string) Group Value * - array[3] = (string) Group Name * - array[4] = (int) lft MPTT Value * - array[5] = (int) rgt MPTT Value * * @param int Group ID # * @param string Group Type, either 'ARO' or 'AXO' */ function get_group_data($group_id, $group_type = 'ARO') { $this->debug_text("get_group_data(): Group_ID: $group_id Group Type: $group_type"); switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; break; } if (empty($group_id) ) { $this->debug_text("get_group_data(): ID ($group_id) is empty, this is required"); return false; } $query = 'SELECT id, parent_id, value, name, lft, rgt FROM '. $table .' WHERE id='. $group_id; //$rs = $this->db->Execute($query); $row = $this->db->GetRow($query); if ($row) { return $row; } $this->debug_text("get_object_data(): Group does not exist."); return false; } /** * get_group_parent_id() * * Grabs the parent_id of a given group * * @return int Parent ID of the Group * * @param int Group ID # * @param string Group Type, either 'ARO' or 'AXO' */ function get_group_parent_id($id, $group_type='ARO') { $this->debug_text("get_group_parent_id(): ID: $id Group Type: $group_type"); switch(strtolower(trim($group_type))) { case 'axo': $table = $this->_db_table_prefix .'axo_groups'; break; default: $table = $this->_db_table_prefix .'aro_groups'; break; } if (empty($id) ) { $this->debug_text("get_group_parent_id(): ID ($id) is empty, this is required"); return false; } $query = 'SELECT parent_id FROM '. $table .' WHERE id='. $id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_group_parent_id'); return false; } $row_count = $rs->RecordCount(); if ($row_count > 1) { $this->debug_text("get_group_parent_id(): Returned $row_count rows, can only return one. Please make your names unique."); return false; } if ($row_count == 0) { $this->debug_text("get_group_parent_id(): Returned $row_count rows"); return false; } $row = $rs->FetchRow(); //Return the ID. return $row[0]; } /** * get_root_group_id () * * Grabs the id of the root group for the specified tree * * @return int Root Group ID # * * @param string Group Type, either 'ARO' or 'AXO' */ function get_root_group_id($group_type='ARO') { $this->debug_text('get_root_group_id(): Group Type: '. $group_type); switch (strtolower($group_type)) { case 'axo': $table = $this->_db_table_prefix .'axo_groups'; break; case 'aro': $table = $this->_db_table_prefix .'aro_groups'; break; default: $this->debug_text('get_root_group_id(): Invalid Group Type: '. $group_type); return FALSE; } $query = 'SELECT id FROM '. $table .' WHERE parent_id=0'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_root_group_id'); return FALSE; } $row_count = $rs->RecordCount(); switch ($row_count) { case 1: $row = $rs->FetchRow(); // Return the ID. return $row[0]; case 0: $this->debug_text('get_root_group_id(): Returned 0 rows, you do not have a root group defined yet.'); return FALSE; } $this->debug_text('get_root_group_id(): Returned '. $row_count .' rows, can only return one. Your tree is very broken.'); return FALSE; } /*======================================================================*\ Function: map_path_to_root() Purpose: Maps a unique path to root to a specific group. Each group can only have one path to root. \*======================================================================*/ /** REMOVED **/ /*======================================================================*\ Function: put_path_to_root() Purpose: Writes the unique path to root to the database. There should really only be one path to root for each level "deep" the groups go. If the groups are branched 10 levels deep, there should only be 10 unique path to roots. These of course overlap each other more and more the closer to the root/trunk they get. \*======================================================================*/ /** REMOVED **/ /*======================================================================*\ Function: clean_path_to_root() Purpose: Cleans up any paths that are not being used. \*======================================================================*/ /** REMOVED **/ /*======================================================================*\ Function: get_path_to_root() Purpose: Generates the path to root for a given group. \*======================================================================*/ /** REMOVED **/ /** * add_group() * * Inserts a group, defaults to be on the "root" branch. * * Since v3.3.x you can only create one group with Parent_ID=0 * So, its a good idea to create a "Virtual Root" group with Parent_ID=0 * Then assign other groups to that. * * @return int New Group ID # if successful, FALSE if otherwise. * * @param string Group Value * @param string Group Name * @param int Parent Group ID # * @param string Group Type, either 'ARO' or 'AXO' */ function add_group($value, $name, $parent_id=0, $group_type='ARO') { switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; break; } $this->debug_text("add_group(): Name: $name Value: $value Parent ID: $parent_id Group Type: $group_type"); $name = trim($name); $value = trim($value); if ( $name == '' ) { $this->debug_text("add_group(): name ($name) OR parent id ($parent_id) is empty, this is required"); return false; } //This has to be outside the transaction, because the first time it is run, it will say the sequence //doesn't exist. Then try to create it, but the transaction will already by aborted by then. $insert_id = $this->db->GenID($this->_db_table_prefix.$group_type.'_groups_id_seq',10); if ( $value === '' ) { $value = $insert_id; } $this->db->BeginTrans(); // special case for root group if ($parent_id == 0) { // check a root group is not already defined $query = 'SELECT id FROM '. $table .' WHERE parent_id=0'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group'); $this->db->RollBackTrans(); return FALSE; } if ($rs->RowCount() > 0) { $this->debug_text('add_group (): A root group already exists.'); $this->db->RollBackTrans(); return FALSE; } $parent_lft = 0; $parent_rgt = 1; } else { if (empty($parent_id)) { $this->debug_text("add_group (): parent id ($parent_id) is empty, this is required"); $this->db->RollbackTrans(); return FALSE; } // grab parent details from database $query = 'SELECT id, lft, rgt FROM '. $table .' WHERE id='. $parent_id; $row = $this->db->GetRow($query); if (!is_array($row)) { $this->debug_db('add_group'); $this->db->RollBackTrans(); return FALSE; } if (empty($row)) { $this->debug_text('add_group (): Parent ID: '. $parent_id .' not found.'); $this->db->RollBackTrans(); return FALSE; } $parent_lft = &$row[1]; $parent_rgt = &$row[2]; // make room for the new group $query = 'UPDATE '. $table .' SET rgt=rgt+2 WHERE rgt>='. $parent_rgt; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group'); $this->db->RollBackTrans(); return FALSE; } $query = 'UPDATE '. $table .' SET lft=lft+2 WHERE lft>'. $parent_rgt; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group'); $this->db->RollBackTrans(); return FALSE; } } $query = 'INSERT INTO '. $table .' (id,parent_id,name,value,lft,rgt) VALUES ('. $insert_id .','. $parent_id .','. $this->db->quote($name) .','. $this->db->quote($value) .','. $parent_rgt .','. ($parent_rgt + 1) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group'); $this->db->RollBackTrans(); return FALSE; } $this->db->CommitTrans(); $this->debug_text('add_group (): Added group as ID: '. $insert_id); return $insert_id; } /** * get_group_objects() * * Gets all objects assigned to a group. * * If $option == 'RECURSE' it will get all objects in child groups as well. * defaults to omit child groups. * * @return array Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * * @param int Group ID # * @param string Group Type, either 'ARO' or 'AXO' * @param string Option, either 'RECURSE' or 'NO_RECURSE' */ function get_group_objects($group_id, $group_type='ARO', $option='NO_RECURSE') { switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $object_table = $this->_db_table_prefix .'axo'; $group_table = $this->_db_table_prefix .'axo_groups'; $map_table = $this->_db_table_prefix .'groups_axo_map'; break; default: $group_type = 'aro'; $object_table = $this->_db_table_prefix .'aro'; $group_table = $this->_db_table_prefix .'aro_groups'; $map_table = $this->_db_table_prefix .'groups_aro_map'; break; } $this->debug_text("get_group_objects(): Group ID: $group_id"); if (empty($group_id)) { $this->debug_text("get_group_objects(): Group ID: ($group_id) is empty, this is required"); return false; } $query = ' SELECT o.section_value,o.value'; if ($option == 'RECURSE') { $query .= ' FROM '. $group_table .' g2 JOIN '. $group_table .' g1 ON g1.lft>=g2.lft AND g1.rgt<=g2.rgt JOIN '. $map_table .' gm ON gm.group_id=g1.id JOIN '. $object_table .' o ON o.id=gm.'. $group_type .'_id WHERE g2.id='. $group_id; } else { $query .= ' FROM '. $map_table .' gm JOIN '. $object_table .' o ON o.id=gm.'. $group_type .'_id WHERE gm.group_id='. $group_id; } $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_group_objects'); return false; } $this->debug_text("get_group_objects(): Got group objects, formatting array."); $retarr = array(); //format return array. while ($row = $rs->FetchRow()) { $section = &$row[0]; $value = &$row[1]; $retarr[$section][] = $value; } return $retarr; } /** * add_group_object() * * Assigns an Object to a group * * @return bool Returns TRUE if successful, FALSE otherwise. * * @param int Group ID # * @param string Object Section Value * @param string Object Value * @param string Group Type, either 'ARO' or 'AXO' */ function add_group_object($group_id, $object_section_value, $object_value, $group_type='ARO') { switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'groups_axo_map'; $object_table = $this->_db_table_prefix .'axo'; $group_table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'groups_aro_map'; $object_table = $this->_db_table_prefix .'aro'; $group_table = $this->_db_table_prefix .'aro_groups'; break; } $this->debug_text("add_group_object(): Group ID: $group_id Section Value: $object_section_value Value: $object_value Group Type: $group_type"); $object_section_value = trim($object_section_value); $object_value = trim($object_value); if (empty($group_id) OR empty($object_value) OR empty($object_section_value)) { $this->debug_text("add_group_object(): Group ID: ($group_id) OR Value ($object_value) OR Section value ($object_section_value) is empty, this is required"); return false; } // test to see if object & group exist and if object is already a member $query = ' SELECT o.id AS id,g.id AS group_id,gm.group_id AS member FROM '. $object_table .' o LEFT JOIN '. $group_table .' g ON g.id='. $group_id .' LEFT JOIN '. $table .' gm ON (gm.'. $group_type .'_id=o.id AND gm.group_id=g.id) WHERE (o.section_value='. $this->db->quote($object_section_value) .' AND o.value='. $this->db->quote($object_value) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group_object'); return FALSE; } if ($rs->RecordCount() != 1) { $this->debug_text('add_group_object(): Value ('. $object_value .') OR Section value ('. $object_section_value .') is invalid. Does this object exist?'); return FALSE; } $row = $rs->FetchRow(); if ($row[1] != $group_id) { $this->debug_text('add_group_object(): Group ID ('. $group_id .') is invalid. Does this group exist?'); return FALSE; } //Group_ID == Member if ($row[1] == $row[2]) { $this->debug_text('add_group_object(): Object: ('. $object_section_value .' -> '. $object_value .') is already a member of Group: ('. $group_id .')'); //Object is already assigned to group. Return true. return TRUE; } $object_id = $row[0]; $query = 'INSERT INTO '. $table .' (group_id,'. $group_type .'_id) VALUES ('. $group_id .','. $object_id .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_group_object'); return FALSE; } $this->debug_text('add_group_object(): Added Object: '. $object_id .' to Group ID: '. $group_id); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { //Expire all cache. $this->Cache_Lite->clean('default'); } return TRUE; } /** * del_group_object() * * Removes an Object from a group. * * @return bool Returns TRUE if successful, FALSE otherwise * * @param int Group ID # * @param string Object Section Value * @param string Object Value * @param string Group Type, either 'ARO' or 'AXO' */ function del_group_object($group_id, $object_section_value, $object_value, $group_type='ARO') { switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'groups_axo_map'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'groups_aro_map'; break; } $this->debug_text("del_group_object(): Group ID: $group_id Section value: $object_section_value Value: $object_value"); $object_section_value = trim($object_section_value); $object_value = trim($object_value); if (empty($group_id) OR empty($object_value) OR empty($object_section_value)) { $this->debug_text("del_group_object(): Group ID: ($group_id) OR Section value: $object_section_value OR Value ($object_value) is empty, this is required"); return false; } if (!$object_id = $this->get_object_id($object_section_value, $object_value, $group_type)) { $this->debug_text ("del_group_object (): Group ID ($group_id) OR Value ($object_value) OR Section value ($object_section_value) is invalid. Does this object exist?"); return FALSE; } $query = 'DELETE FROM '. $table .' WHERE group_id='. $group_id .' AND '. $group_type .'_id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('del_group_object'); return false; } $this->debug_text("del_group_object(): Deleted Value: $object_value to Group ID: $group_id assignment"); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { //Expire all cache. $this->Cache_Lite->clean('default'); } return true; } /** * edit_group() * * Edits a group * * @returns bool Returns TRUE if successful, FALSE otherwise * * @param int Group ID # * @param string Group Value * @param string Group Name * @param int Parent ID # * @param string Group Type, either 'ARO' or 'AXO' */ function edit_group($group_id, $value=NULL, $name=NULL, $parent_id=NULL, $group_type='ARO') { $this->debug_text("edit_group(): ID: $group_id Name: $name Value: $value Parent ID: $parent_id Group Type: $group_type"); switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; break; } if (empty($group_id) ) { $this->debug_text('edit_group(): Group ID ('. $group_id .') is empty, this is required'); return FALSE; } if ( !is_array($curr = $this->get_group_data($group_id, $group_type)) ) { $this->debug_text('edit_group(): Invalid Group ID: '. $group_id); return FALSE; } $name = trim($name); // don't set name if it is unchanged if ($name == $curr[3]) { unset($name); } // don't set parent_id if it is unchanged if ($parent_id == $curr[1]) { unset($parent_id); } if (!empty($parent_id)) { if ($group_id == $parent_id) { $this->debug_text('edit_group(): Groups can\'t be a parent to themselves. Incest is bad. ;)'); return FALSE; } //Make sure we don't re-parent to our own children. //Grab all children of this group_id. $children_ids = $this->get_group_children($group_id, $group_type, 'RECURSE'); if (is_array($children_ids)) { if (@in_array($parent_id, $children_ids) ) { $this->debug_text('edit_group(): Groups can\'t be re-parented to their own children, this would be incestuous!'); return FALSE; } } unset($children_ids); // make sure parent exists if (!$this->get_group_data($parent_id, $group_type)) { $this->debug_text('edit_group(): Parent Group ('. $parent_id .') doesn\'t exist'); return FALSE; } } $set = array(); // update name if it is specified. if (!empty($name)) { $set[] = 'name='. $this->db->quote($name); } // update parent_id if it is specified. if (!empty($parent_id)) { $set[] = 'parent_id='. $parent_id; } // update value if it is specified. if (!empty($value)) { $set[] = 'value='. $this->db->quote($value); } if (empty($set)) { $this->debug_text('edit_group(): Nothing to update.'); return FALSE; } $this->db->BeginTrans(); $query = 'UPDATE '. $table .' SET '. implode(',', $set) .' WHERE id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_group'); $this->db->RollbackTrans(); return FALSE; } $this->debug_text('edit_group(): Modified group ID: '. $group_id); // rebuild group tree if parent_id has changed if (!empty($parent_id)) { if (!$this->_rebuild_tree($table, $this->get_root_group_id($group_type))) { $this->db->RollbackTrans(); return FALSE; } } $this->db->CommitTrans(); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { // Expire all cache. $this->Cache_Lite->clean('default'); } return TRUE; } /** * rebuild_tree () * * rebuilds the group tree for the given type * * @return bool Returns TRUE if successful, FALSE otherwise * * @param string Group Type, either 'ARO' or 'AXO' * @param int Group ID # * @param int Left value of Group */ function rebuild_tree($group_type = 'ARO', $group_id = NULL, $left = 1) { $this->debug_text("rebuild_tree(): Group Type: $group_type Group ID: $group_id Left: $left"); switch (strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; break; } if (!isset($group_id)) { if ($group_id = $this->get_root_group_id($group_type)) { $left = 1; $this->debug_text('rebuild_tree(): No Group ID Specified, using Root Group ID: '. $group_id); } else { $this->debug_text('rebuild_tree(): A Root group could not be found, are there any groups defined?'); return FALSE; } } $this->db->BeginTrans(); $rebuilt = $this->_rebuild_tree($table, $group_id, $left); if ($rebuilt === FALSE) { $this->debug_text('rebuild_tree(): Error rebuilding tree!'); $this->db->RollBackTrans(); return FALSE; } $this->db->CommitTrans(); $this->debug_text('rebuild_tree(): Tree rebuilt.'); return TRUE; } /** * _rebuild_tree () * * Utility recursive function called by rebuild_tree() * * @return int Returns right value of this node + 1 * * @param string Table name of group type * @param int Group ID # * @param int Left value of Group */ function _rebuild_tree($table, $group_id, $left = 1) { $this->debug_text("_rebuild_tree(): Table: $table Group ID: $group_id Left: $left"); // get all children of this node $query = 'SELECT id FROM '. $table .' WHERE parent_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('_rebuild_tree'); return FALSE; } // the right value of this node is the left value + 1 $right = $left + 1; while ($row = $rs->FetchRow()) { // recursive execution of this function for each // child of this node // $right is the current right value, which is // incremented by the rebuild_tree function $right = $this->_rebuild_tree($table, $row[0], $right); if ($right === FALSE) { return FALSE; } } // we've got the left value, and now that we've processed // the children of this node we also know the right value $query = 'UPDATE '. $table .' SET lft='. $left .', rgt='. $right .' WHERE id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('_rebuild_tree'); return FALSE; } // return the right value of this node + 1 return $right + 1; } /** * del_group() * * deletes a given group * * @return bool Returns TRUE if successful, FALSE otherwise. * * @param int Group ID # * @param bool If TRUE, child groups of this group will be reparented to the current group's parent. * @param string Group Type, either 'ARO' or 'AXO' */ function del_group($group_id, $reparent_children=TRUE, $group_type='ARO') { switch(strtolower(trim($group_type))) { case 'axo': $group_type = 'axo'; $table = $this->_db_table_prefix .'axo_groups'; $groups_map_table = $this->_db_table_prefix .'axo_groups_map'; $groups_object_map_table = $this->_db_table_prefix .'groups_axo_map'; break; default: $group_type = 'aro'; $table = $this->_db_table_prefix .'aro_groups'; $groups_map_table = $this->_db_table_prefix .'aro_groups_map'; $groups_object_map_table = $this->_db_table_prefix .'groups_aro_map'; break; } $this->debug_text("del_group(): ID: $group_id Reparent Children: $reparent_children Group Type: $group_type"); if (empty($group_id) ) { $this->debug_text("del_group(): Group ID ($group_id) is empty, this is required"); return false; } // Get details of this group $query = 'SELECT id, parent_id, name, lft, rgt FROM '. $table .' WHERE id='. $group_id; $group_details = $this->db->GetRow($query); if (!is_array($group_details)) { $this->debug_db('del_group'); return false; } $parent_id = $group_details[1]; $left = $group_details[3]; $right = $group_details[4]; $this->db->BeginTrans(); // grab list of all children $children_ids = $this->get_group_children($group_id, $group_type, 'RECURSE'); // prevent deletion of root group & reparent of children if it has more than one immediate child if ($parent_id == 0) { $query = 'SELECT count(*) FROM '. $table .' WHERE parent_id='. $group_id; $child_count = $this->db->GetOne($query); if (($child_count > 1) AND $reparent_children) { $this->debug_text ('del_group (): You cannot delete the root group and reparent children, this would create multiple root groups.'); $this->db->RollbackTrans(); return FALSE; } } $success = FALSE; /* * Handle children here. */ switch (TRUE) { // there are no child groups, just delete group case !is_array($children_ids): case count($children_ids) == 0: // remove acl maps $query = 'DELETE FROM '. $groups_map_table .' WHERE group_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove group object maps $query = 'DELETE FROM '. $groups_object_map_table .' WHERE group_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove group $query = 'DELETE FROM '. $table .' WHERE id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // move all groups right of deleted group left by width of deleted group $query = 'UPDATE '. $table .' SET lft=lft-'. ($right-$left+1) .' WHERE lft>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $query = 'UPDATE '. $table .' SET rgt=rgt-'. ($right-$left+1) .' WHERE rgt>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $success = TRUE; break; case $reparent_children == TRUE: // remove acl maps $query = 'DELETE FROM '. $groups_map_table .' WHERE group_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove group object maps $query = 'DELETE FROM '. $groups_object_map_table .' WHERE group_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove group $query = 'DELETE FROM '. $table .' WHERE id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // set parent of immediate children to parent group $query = 'UPDATE '. $table .' SET parent_id='. $parent_id .' WHERE parent_id='. $group_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // move all children left by 1 $query = 'UPDATE '. $table .' SET lft=lft-1, rgt=rgt-1 WHERE lft>'. $left .' AND rgt<'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // move all groups right of deleted group left by 2 $query = 'UPDATE '. $table .' SET lft=lft-2 WHERE lft>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $query = 'UPDATE '. $table .' SET rgt=rgt-2 WHERE rgt>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $success = TRUE; break; default: // make list of group and all children $group_ids = $children_ids; $group_ids[] = $group_id; // remove acl maps $query = 'DELETE FROM '. $groups_map_table .' WHERE group_id IN ('. implode (',', $group_ids) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove group object maps $query = 'DELETE FROM '. $groups_object_map_table .' WHERE group_id IN ('. implode (',', $group_ids) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // remove groups $query = 'DELETE FROM '. $table .' WHERE id IN ('. implode (',', $group_ids) .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } // move all groups right of deleted group left by width of deleted group $query = 'UPDATE '. $table .' SET lft=lft-'. ($right - $left + 1) .' WHERE lft>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $query = 'UPDATE '. $table .' SET rgt=rgt-'. ($right - $left + 1) .' WHERE rgt>'. $right; $rs = $this->db->Execute($query); if (!is_object($rs)) { break; } $success = TRUE; } // if the delete failed, rollback the trans and return false if (!$success) { $this->debug_db('del_group'); $this->db->RollBackTrans(); return false; } $this->debug_text("del_group(): deleted group ID: $group_id"); $this->db->CommitTrans(); if ($this->_caching == TRUE AND $this->_force_cache_expire == TRUE) { //Expire all cache. $this->Cache_Lite->clean('default'); } return true; } /* * * Objects (ACO/ARO/AXO) * */ /** * get_object() * * Grabs all Objects's in the database, or specific to a section_value * * @return ADORecordSet Returns recordset directly, with object ID only selected: * * @param string Filter to this section value * @param int Returns hidden objects if 1, leaves them out otherwise. * @param string Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL' */ function get_object($section_value = null, $return_hidden=1, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; case 'acl': $object_type = 'acl'; $table = $this->_db_table_prefix .'acl'; break; default: $this->debug_text('get_object(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_object(): Section Value: $section_value Object Type: $object_type"); $query = 'SELECT id FROM '. $table; $where = array(); if (!empty($section_value)) { $where[] = 'section_value='. $this->db->quote($section_value); } if ($return_hidden==0 AND $object_type != 'acl') { $where[] = 'hidden=0'; } if (!empty($where)) { $query .= ' WHERE '. implode(' AND ', $where); } $rs = $this->db->GetCol($query); if (!is_array($rs)) { $this->debug_db('get_object'); return false; } // Return Object IDs return $rs; } /** * get_ungrouped_objects() * * Grabs ID's of all Objects (ARO's and AXO's only) in the database not assigned to a Group. * * This function is useful for applications that synchronize user databases with an outside source. * If syncrhonization doesn't automatically place users in an appropriate group, this function can * quickly identify them so that they can be assigned to the correct group. * * @return array Returns an array of object ID's * * @param int Returns hidden objects if 1, does not if 0. * @param string Object Type, either 'ARO' or 'AXO' (groupable types) */ function get_ungrouped_objects($return_hidden=1, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; default: $this->debug_text('get_ungrouped_objects(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_ungrouped_objects(): Object Type: $object_type"); $query = 'SELECT id FROM '. $table. ' a LEFT JOIN ' . $this->_db_table_prefix. 'groups_'.$object_type.'_map b ON a.id = b.'. $object_type .'_id'; $where = array(); $where[] = 'b.group_id IS NULL'; if ($return_hidden==0) { $where[] = 'a.hidden=0'; } if (!empty($where)) { $query .= ' WHERE '. implode(' AND ', $where); } $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_ungrouped_objects'); return false; } while(!$rs->EOF) { $retarr[] = $rs->fields[0]; $rs->MoveNext(); } // Return Array of object IDS return $retarr; } /** * get_objects () * * Grabs all Objects in the database, or specific to a section_value * * @return array Returns objects in format suitable for add_acl and is_conflicting_acl * - i.e. Associative array, item={Section Value}, key={Array of Object Values} i.e. ["
" => ["", "", ""], ...] * * @param string Filter for section value * @param int Returns hidden objects if 1, does not if 0 * @param string Object Type, either 'ACO', 'ARO', 'AXO' */ function get_objects($section_value = NULL, $return_hidden = 1, $object_type = NULL) { switch (strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; default: $this->debug_text('get_objects(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_objects(): Section Value: $section_value Object Type: $object_type"); $query = 'SELECT section_value,value FROM '. $table; $where = array(); if (!empty($section_value)) { $where[] = 'section_value='. $this->db->quote($section_value); } if ($return_hidden==0) { $where[] = 'hidden=0'; } if (!empty($where)) { $query .= ' WHERE '. implode(' AND ', $where); } $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_objects'); return FALSE; } $retarr = array(); while ($row = $rs->FetchRow()) { $retarr[$row[0]][] = $row[1]; } // Return objects return $retarr; } /** * get_object_data() * * Gets all data pertaining to a specific Object. * * @return array Returns 2-Dimensional array of rows with columns = ( section_value, value, order_value, name, hidden ) * * @param int Object ID # * @param string Object Type, either 'ACO', 'ARO', 'AXO' */ function get_object_data($object_id, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; default: $this->debug_text('get_object_data(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_object_data(): Object ID: $object_id Object Type: $object_type"); if (empty($object_id) ) { $this->debug_text("get_object_data(): Object ID ($object_id) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("get_object_data(): Object Type ($object_type) is empty, this is required"); return false; } $query = 'SELECT section_value,value,order_value,name,hidden FROM '. $table .' WHERE id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_object_data'); return false; } if ($rs->RecordCount() < 1) { $this->debug_text('get_object_data(): Returned '. $row_count .' rows'); return FALSE; } // Return all objects return $rs->GetRows(); } /** * get_object_id() * * Gets the object_id given the section_value AND value of the object. * * @return int Object ID # * * @param string Object Section Value * @param string Object Value * @param string Object Type, either 'ACO', 'ARO', 'AXO' */ function get_object_id($section_value, $value, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; default: $this->debug_text('get_object_id(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_object_id(): Section Value: $section_value Value: $value Object Type: $object_type"); $section_value = trim($section_value); $value = trim($value); if (empty($section_value) AND empty($value) ) { $this->debug_text("get_object_id(): Section Value ($value) AND value ($value) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("get_object_id(): Object Type ($object_type) is empty, this is required"); return false; } $query = 'SELECT id FROM '. $table .' WHERE section_value='. $this->db->quote($section_value) .' AND value='. $this->db->quote($value); $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_object_id'); return false; } $row_count = $rs->RecordCount(); if ($row_count > 1) { $this->debug_text("get_object_id(): Returned $row_count rows, can only return one. This should never happen, the database may be missing a unique key."); return false; } if ($row_count == 0) { $this->debug_text("get_object_id(): Returned $row_count rows"); return false; } $row = $rs->FetchRow(); //Return the ID. return $row[0]; } /** * get_object_section_value() * * Gets the object_section_value given object id * * @return string Object Section Value * * @param int Object ID # * @param string Object Type, either 'ACO', 'ARO', or 'AXO' */ function get_object_section_value($object_id, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; break; default: $this->debug_text('get_object_section_value(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_object_section_value(): Object ID: $object_id Object Type: $object_type"); if (empty($object_id) ) { $this->debug_text("get_object_section_value(): Object ID ($object_id) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("get_object_section_value(): Object Type ($object_type) is empty, this is required"); return false; } $query = 'SELECT section_value FROM '. $table .' WHERE id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_object_section_value'); return false; } $row_count = $rs->RecordCount(); if ($row_count > 1) { $this->debug_text("get_object_section_value(): Returned $row_count rows, can only return one."); return false; } if ($row_count == 0) { $this->debug_text("get_object_section_value(): Returned $row_count rows"); return false; } $row = $rs->FetchRow(); //Return the ID. return $row[0]; } /** * get_object_groups() * * Gets all groups an object is a member of. * * If $option == 'RECURSE' it will get all ancestor groups. * defaults to only get direct parents. * * @return array Array of Group ID #'s, or FALSE if Failed * * @param int Object ID # * @param string Object Type, either 'ARO' or 'AXO' * @param string Option, either 'RECURSE', or 'NO_RECURSE' */ function get_object_groups($object_id, $object_type = 'ARO', $option = 'NO_RECURSE') { $this->debug_text('get_object_groups(): Object ID: '. $object_id .' Object Type: '. $object_type .' Option: '. $option); switch(strtolower(trim($object_type))) { case 'axo': $object_type = 'axo'; $group_table = $this->_db_table_prefix .'axo_groups'; $map_table = $this->_db_table_prefix .'groups_axo_map'; break; case 'aro': $object_type = 'aro'; $group_table = $this->_db_table_prefix .'aro_groups'; $map_table = $this->_db_table_prefix .'groups_aro_map'; break; default: $this->debug_text('get_object_groups(): Invalid Object Type: '. $object_type); return FALSE; } if (empty($object_id)) { $this->debug_text('get_object_groups(): Object ID: ('. $object_id .') is empty, this is required'); return FALSE; } if (strtoupper($option) == 'RECURSE') { $query = ' SELECT DISTINCT g.id AS group_id FROM '. $map_table .' gm LEFT JOIN '. $group_table .' g1 ON g1.id=gm.group_id LEFT JOIN '. $group_table .' g ON g.lft<=g1.lft AND g.rgt>=g1.rgt'; } else { $query = ' SELECT gm.group_id FROM '. $map_table .' gm'; } $query .= ' WHERE gm.'. $object_type .'_id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_object_groups'); return FALSE; } $retarr = array(); while ($row = $rs->FetchRow()) { $retarr[] = $row[0]; } return $retarr; } /** * add_object() * * Inserts a new object * * @return int Returns the ID # of the new object if successful, FALSE otherwise * * @param string Object Section Value * @param string Object Name * @param string Object Value * @param int Display Order * @param int Hidden Flag, either 1 to hide, or 0 to show. * @param string Object Type, either 'ACO', 'ARO', or 'AXO' */ function add_object($section_value, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; $object_sections_table = $this->_db_table_prefix .'aco_sections'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; $object_sections_table = $this->_db_table_prefix .'aro_sections'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; $object_sections_table = $this->_db_table_prefix .'axo_sections'; break; default: $this->debug_text('add_object(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("add_object(): Section Value: $section_value Value: $value Order: $order Name: $name Object Type: $object_type"); $section_value = trim($section_value); $name = trim($name); $value = trim($value); $order = trim($order); $hidden = intval($hidden); if ($order == NULL OR $order == '') { $order = 0; } if (empty($name) OR empty($section_value) ) { $this->debug_text("add_object(): name ($name) OR section value ($section_value) is empty, this is required"); return false; } if (strlen($name) >= 255 OR strlen($value) >= 230 ) { $this->debug_text("add_object(): name ($name) OR value ($value) is too long."); return false; } if (empty($object_type) ) { $this->debug_text("add_object(): Object Type ($object_type) is empty, this is required"); return false; } // Test to see if the section is invalid or object already exists. $query = ' SELECT CASE WHEN o.id IS NULL THEN 0 ELSE 1 END AS object_exists FROM '. $object_sections_table .' s LEFT JOIN '. $table .' o ON (s.value=o.section_value AND o.value='. $this->db->quote($value) .') WHERE s.value='. $this->db->quote($section_value); $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_object'); return FALSE; } if ($rs->RecordCount() != 1) { // Section is invalid $this->debug_text("add_object(): Section Value: $section_value Object Type ($object_type) does not exist, this is required"); return false; } $row = $rs->FetchRow(); if ($row[0] == 1) { //Object is already created. return true; } $insert_id = $this->db->GenID($this->_db_table_prefix.$object_type.'_seq',10); $query = 'INSERT INTO '. $table .' (id,section_value,value,order_value,name,hidden) VALUES('. $insert_id .','. $this->db->quote($section_value) .','. $this->db->quote($value) .','. $order .','. $this->db->quote($name) .','. $hidden .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_object'); return false; } $this->debug_text("add_object(): Added object as ID: $insert_id"); return $insert_id; } /** * edit_object() * * Edits a given Object * * @return bool Returns TRUE if successful, FALSE otherwise * * @param int Object ID # * @param string Object Section Value * @param string Object Name * @param string Object Value * @param int Display Order * @param int Hidden Flag, either 1 to hide, or 0 to show * @param string Object Type, either 'ACO', 'ARO', or 'AXO' */ function edit_object($object_id, $section_value, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; $object_map_table = $this->_db_table_prefix .'aco_map'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; $object_map_table = $this->_db_table_prefix .'aro_map'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; $object_map_table = $this->_db_table_prefix .'axo_map'; break; } $this->debug_text("edit_object(): ID: $object_id Section Value: $section_value Value: $value Order: $order Name: $name Object Type: $object_type"); $section_value = trim($section_value); $name = trim($name); $value = trim($value); $order = trim($order); $hidden = intval($hidden); if (empty($object_id) OR empty($section_value) ) { $this->debug_text("edit_object(): Object ID ($object_id) OR Section Value ($section_value) is empty, this is required"); return false; } if (empty($name) ) { $this->debug_text("edit_object(): name ($name) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("edit_object(): Object Type ($object_type) is empty, this is required"); return false; } $this->db->BeginTrans(); //Get old value incase it changed, before we do the update. $query = 'SELECT value, section_value FROM '. $table .' WHERE id='. $object_id; $old = $this->db->GetRow($query); $query = ' UPDATE '. $table .' SET section_value='. $this->db->quote($section_value) .', value='. $this->db->quote($value) .', order_value='. $this->db->quote($order) .', name='. $this->db->quote($name) .', hidden='. $hidden .' WHERE id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object'); $this->db->RollbackTrans(); return false; } $this->debug_text('edit_object(): Modified '. strtoupper($object_type) .' ID: '. $object_id); if ($old[0] != $value OR $old[1] != $section_value) { $this->debug_text("edit_object(): Value OR Section Value Changed, update other tables."); $query = ' UPDATE '. $object_map_table .' SET value='. $this->db->quote($value) .', section_value='. $this->db->quote($section_value) .' WHERE section_value='. $this->db->quote($old[1]) .' AND value='. $this->db->quote($old[0]); $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object'); $this->db->RollbackTrans(); return FALSE; } $this->debug_text ('edit_object(): Modified Map Value: '. $value .' Section Value: '. $section_value); } $this->db->CommitTrans(); return TRUE; } /** * del_object() * * Deletes a given Object and, if instructed to do so, erase all referencing objects * * ERASE feature by: Martino Piccinato * * @return bool Returns TRUE if successful, FALSE otherwise. * * @param int Object ID # * @param string Object Type, either 'ACO', 'ARO', or 'AXO' * @param bool Erases all referencing objects if TRUE, leaves them alone otherwise. */ function del_object($object_id, $object_type=NULL, $erase=FALSE) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; $object_map_table = $this->_db_table_prefix .'aco_map'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; $object_map_table = $this->_db_table_prefix .'aro_map'; $groups_map_table = $this->_db_table_prefix .'aro_groups_map'; $object_group_table = $this->_db_table_prefix .'groups_aro_map'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; $object_map_table = $this->_db_table_prefix .'axo_map'; $groups_map_table = $this->_db_table_prefix .'axo_groups_map'; $object_group_table = $this->_db_table_prefix .'groups_axo_map'; break; default: $this->debug_text('del_object(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("del_object(): ID: $object_id Object Type: $object_type, Erase all referencing objects: $erase"); if (empty($object_id) ) { $this->debug_text("del_object(): Object ID ($object_id) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("del_object(): Object Type ($object_type) is empty, this is required"); return false; } $this->db->BeginTrans(); // Get Object section_value/value (needed to look for referencing objects) $query = 'SELECT section_value,value FROM '. $table .' WHERE id='. $object_id; $object = $this->db->GetRow($query); if (empty($object)) { $this->debug_text('del_object(): The specified object ('. strtoupper($object_type) .' ID: '. $object_id .') could not be found.'); $this->db->RollbackTrans(); return FALSE; } $section_value = $object[0]; $value = $object[1]; // Get ids of acl referencing the Object (if any) $query = "SELECT acl_id FROM $object_map_table WHERE value='$value' AND section_value='$section_value'"; $acl_ids = $this->db->GetCol($query); if ($erase) { // We were asked to erase all acl referencing it $this->debug_text("del_object(): Erase was set to TRUE, delete all referencing objects"); if ($object_type == "aro" OR $object_type == "axo") { // The object can be referenced in groups_X_map tables // in the future this branching may become useless because // ACO might me "groupable" too // Get rid of groups_map referencing the Object $query = 'DELETE FROM '. $object_group_table .' WHERE '. $object_type .'_id='. $object_id; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object'); $this->db->RollBackTrans(); return false; } } if (!empty($acl_ids)) { //There are acls actually referencing the object if ($object_type == 'aco') { // I know it's extremely dangerous but // if asked to really erase an ACO // we should delete all acl referencing it // (and relative maps) // Do this below this branching // where it uses $orphan_acl_ids as // the array of the "orphaned" acl // in this case all referenced acl are // orhpaned acl $orphan_acl_ids = $acl_ids; } else { // The object is not an ACO and might be referenced // in still valid acls regarding also other object. // In these cases the acl MUST NOT be deleted // Get rid of $object_id map referencing erased objects $query = "DELETE FROM $object_map_table WHERE section_value='$section_value' AND value='$value'"; $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object'); $this->db->RollBackTrans(); return false; } // Find the "orphaned" acl. I mean acl referencing the erased Object (map) // not referenced anymore by other objects $sql_acl_ids = implode(",", $acl_ids); $query = ' SELECT a.id FROM '. $this->_db_table_prefix .'acl a LEFT JOIN '. $object_map_table .' b ON a.id=b.acl_id LEFT JOIN '. $groups_map_table .' c ON a.id=c.acl_id WHERE b.value IS NULL AND b.section_value IS NULL AND c.group_id IS NULL AND a.id in ('. $sql_acl_ids .')'; $orphan_acl_ids = $this->db->GetCol($query); } // End of else section of "if ($object_type == "aco")" if ($orphan_acl_ids) { // If there are orphaned acls get rid of them foreach ($orphan_acl_ids as $acl) { $this->del_acl($acl); } } } // End of if ($acl_ids) // Finally delete the Object itself $query = "DELETE FROM $table WHERE id='$object_id'"; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object'); $this->db->RollBackTrans(); return false; } $this->db->CommitTrans(); return true; } // End of "if ($erase)" $groups_ids = FALSE; if ($object_type == 'axo' OR $object_type == 'aro') { // If the object is "groupable" (may become unnecessary, // see above // Get id of groups where the object is assigned: // you must explicitly remove the object from its groups before // deleting it (don't know if this is really needed, anyway it's safer ;-) $query = 'SELECT group_id FROM '. $object_group_table .' WHERE '. $object_type .'_id='. $object_id; $groups_ids = $this->db->GetCol($query); } if ( ( isset($acl_ids) AND !empty($acl_ids) ) OR ( isset($groups_ids) AND !empty($groups_ids) ) ) { // The Object is referenced somewhere (group or acl), can't delete it $this->debug_text("del_object(): Can't delete the object as it is being referenced by GROUPs (".@implode($groups_ids).") or ACLs (".@implode($acl_ids,",").")"); $this->db->RollBackTrans(); return false; } else { // The Object is NOT referenced anywhere, delete it $query = "DELETE FROM $table WHERE id='$object_id'"; $rs = $this->db->Execute($query); if ( !is_object($rs) ) { $this->debug_db('edit_object'); $this->db->RollBackTrans(); return false; } $this->db->CommitTrans(); return true; } $this->db->RollbackTrans(); return false; } /* * * Object Sections * */ /** * get_object_section_section_id() * * Gets the object_section_id given the name AND/OR value of the section. * * Will only return one section id, so if there are duplicate names it will return false. * * @return int Object Section ID if the object section is found AND is unique, or FALSE otherwise. * * @param string Object Name * @param string Object Value * @param string Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL' * */ function get_object_section_section_id($name = NULL, $value = NULL, $object_type = NULL) { $this->debug_text("get_object_section_section_id(): Value: $value Name: $name Object Type: $object_type"); switch(strtolower(trim($object_type))) { case 'aco': case 'aro': case 'axo': case 'acl': $object_type = strtolower(trim($object_type)); $table = $this->_db_table_prefix . $object_type; $object_sections_table = $this->_db_table_prefix . $object_type .'_sections'; break; default: $this->debug_text('get_object_section_section_id(): Invalid Object Type ('. $object_type . ')'); return FALSE; } $name = trim($name); $value = trim($value); if (empty($name) AND empty($value) ) { $this->debug_text('get_object_section_section_id(): Both Name ('. $name .') and Value ('. $value .') are empty, you must specify at least one.'); return FALSE; } $query = 'SELECT id FROM '. $object_sections_table; $where = ' WHERE '; // limit by value if specified if (!empty($value)) { $query .= $where .'value='. $this->db->quote($value); $where = ' AND '; } // only use name if asked, this is SLOW if (!empty($name)) { $query .= $where .'name='. $this->db->quote($name); } $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('get_object_section_section_id'); return FALSE; } $row_count = $rs->RecordCount(); // If only one row is returned if ($row_count == 1) { // Return only the ID in the first row. $row = $rs->FetchRow(); return $row[0]; } // If more than one row is returned // should only ever occur when using name as values are unique. if ($row_count > 1) { $this->debug_text('get_object_section_section_id(): Returned '. $row_count .' rows, can only return one. Please search by value not name, or make your names unique.'); return FALSE; } // No rows returned, no matching section found $this->debug_text('get_object_section_section_id(): Returned '. $row_count .' rows, no matching section found.'); return FALSE; } /** * add_object_section() * * Inserts an object Section * * @return int Object Section ID of new section * * @param string Object Name * @param string Object Value * @param int Display Order * @param int Hidden flag, hides section if 1, shows section if 0 * @param string Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL' */ function add_object_section($name, $value=0, $order=0, $hidden=0, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $object_sections_table = $this->_db_table_prefix .'aco_sections'; break; case 'aro': $object_type = 'aro'; $object_sections_table = $this->_db_table_prefix .'aro_sections'; break; case 'axo': $object_type = 'axo'; $object_sections_table = $this->_db_table_prefix .'axo_sections'; break; case 'acl': $object_type = 'acl'; $object_sections_table = $this->_db_table_prefix .'acl_sections'; break; } $this->debug_text("add_object_section(): Value: $value Order: $order Name: $name Object Type: $object_type"); $name = trim($name); $value = trim($value); $order = trim($order); $hidden = intval($hidden); if ($order == NULL OR $order == '') { $order = 0; } if (empty($name) ) { $this->debug_text("add_object_section(): name ($name) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("add_object_section(): Object Type ($object_type) is empty, this is required"); return false; } $insert_id = $this->db->GenID($this->_db_table_prefix.$object_type.'_sections_seq',10); $query = 'insert into '. $object_sections_table .' (id,value,order_value,name,hidden) VALUES( '. $insert_id .', '. $this->db->quote($value) .', '. $order .', '. $this->db->quote($name) .', '. $hidden .')'; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('add_object_section'); return false; } else { $this->debug_text("add_object_section(): Added object_section as ID: $insert_id"); return $insert_id; } } /** * edit_object_section() * * Edits a given Object Section * * @return bool Returns TRUE if successful, FALSE otherwise * * @param int Object Section ID # * @param string Object Section Name * @param string Object Section Value * @param int Display Order * @param int Hidden Flag, hide object section if 1, show if 0 * @param string Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL' */ function edit_object_section($object_section_id, $name, $value=0, $order=0, $hidden=0, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco'; $object_sections_table = $this->_db_table_prefix .'aco_sections'; $object_map_table = $this->_db_table_prefix .'aco_map'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro'; $object_sections_table = $this->_db_table_prefix .'aro_sections'; $object_map_table = $this->_db_table_prefix .'aro_map'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo'; $object_sections_table = $this->_db_table_prefix .'axo_sections'; $object_map_table = $this->_db_table_prefix .'axo_map'; break; case 'acl': $object_type = 'acl'; $table = $this->_db_table_prefix .'acl'; $object_sections_table = $this->_db_table_prefix .'acl_sections'; break; default: $this->debug_text('edit_object_section(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("edit_object_section(): ID: $object_section_id Value: $value Order: $order Name: $name Object Type: $object_type"); $name = trim($name); $value = trim($value); $order = trim($order); $hidden = intval($hidden); if (empty($object_section_id) ) { $this->debug_text("edit_object_section(): Section ID ($object_section_id) is empty, this is required"); return false; } if (empty($name) ) { $this->debug_text("edit_object_section(): name ($name) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("edit_object_section(): Object Type ($object_type) is empty, this is required"); return false; } $this->db->BeginTrans(); //Get old value incase it changed, before we do the update. $query = "select value from $object_sections_table where id=$object_section_id"; $old_value = $this->db->GetOne($query); $query = "update $object_sections_table set value='$value', order_value='$order', name='$name', hidden=$hidden where id=$object_section_id"; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object_section'); $this->db->RollbackTrans(); return false; } else { $this->debug_text("edit_object_section(): Modified aco_section ID: $object_section_id"); if ($old_value != $value) { $this->debug_text("edit_object_section(): Value Changed, update other tables."); $query = "update $table set section_value='$value' where section_value = '$old_value'"; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('edit_object_section'); $this->db->RollbackTrans(); return false; } else { if (!empty($object_map_table)) { $query = "update $object_map_table set section_value='$value' where section_value = '$old_value'"; $rs = $this->db->Execute($query); if ( !is_object($rs) ) { $this->debug_db('edit_object_section'); $this->db->RollbackTrans(); return false; } else { $this->debug_text("edit_object_section(): Modified ojbect_map value: $value"); $this->db->CommitTrans(); return true; } } else { //ACL sections, have no mapping table. Return true. $this->db->CommitTrans(); return true; } } } $this->db->CommitTrans(); return true; } } /** * del_object_section() * * Deletes a given Object Section and, if explicitly asked, all the section objects * * ERASE feature by: Martino Piccinato * * @return bool Returns TRUE if successful, FALSE otherwise * * @param int Object Section ID # to delete * @param string Object Type, either 'ACO', 'ARO', 'AXO', or 'ACL' * @param bool Erases all section objects assigned to the section */ function del_object_section($object_section_id, $object_type=NULL, $erase=FALSE) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $object_sections_table = $this->_db_table_prefix .'aco_sections'; break; case 'aro': $object_type = 'aro'; $object_sections_table = $this->_db_table_prefix .'aro_sections'; break; case 'axo': $object_type = 'axo'; $object_sections_table = $this->_db_table_prefix .'axo_sections'; break; case 'acl': $object_type = 'acl'; $object_sections_table = $this->_db_table_prefix .'acl_sections'; break; } $this->debug_text("del_object_section(): ID: $object_section_id Object Type: $object_type, Erase all: $erase"); if (empty($object_section_id) ) { $this->debug_text("del_object_section(): Section ID ($object_section_id) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("del_object_section(): Object Type ($object_type) is empty, this is required"); return false; } // Get the value of the section $query="SELECT value FROM $object_sections_table WHERE id='$object_section_id'"; $section_value = $this->db->GetOne($query); // Get all objects ids in the section $object_ids = $this->get_object($section_value, 1, $object_type); if($erase) { // Delete all objects in the section and for // each object delete the referencing object // (see del_object method) if (is_array($object_ids)) { foreach ($object_ids as $id) { if ( $object_type === 'acl' ) { $this->del_acl($id); } else { $this->del_object($id, $object_type, TRUE); } } } } if($object_ids AND !$erase) { // There are objects in the section and we // were not asked to erase them: don't delete it $this->debug_text("del_object_section(): Could not delete the section ($section_value) as it is not empty."); return false; } else { // The section is empty (or emptied by this method) $query = "DELETE FROM $object_sections_table where id='$object_section_id'"; $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('del_object_section'); return false; } else { $this->debug_text("del_object_section(): deleted section ID: $object_section_id Value: $section_value"); return true; } } return false; } /** * get_section_data() * * Gets the section data given the Section Value * * @return array Returns numerically indexed array with the following columns: * - array[0] = (int) Section ID # * - array[1] = (string) Section Value * - array[2] = (int) Section Order * - array[3] = (string) Section Name * - array[4] = (int) Section Hidden? * @param string Section Value * @param string Object Type, either 'ACO', 'ARO', or 'AXO' */ function get_section_data($section_value, $object_type=NULL) { switch(strtolower(trim($object_type))) { case 'aco': $object_type = 'aco'; $table = $this->_db_table_prefix .'aco_sections'; break; case 'aro': $object_type = 'aro'; $table = $this->_db_table_prefix .'aro_sections'; break; case 'axo': $object_type = 'axo'; $table = $this->_db_table_prefix .'axo_sections'; break; default: $this->debug_text('get_section_data(): Invalid Object Type: '. $object_type); return FALSE; } $this->debug_text("get_section_data(): Section Value: $section_value Object Type: $object_type"); if (empty($section_value) ) { $this->debug_text("get_section_data(): Section Value ($section_value) is empty, this is required"); return false; } if (empty($object_type) ) { $this->debug_text("get_section_data(): Object Type ($object_type) is empty, this is required"); return false; } $query = "SELECT id, value, order_value, name, hidden FROM '. $table .' WHERE value='$section_value'"; $row = $this->db->GetRow($query); if ($row) { return $row; } $this->debug_text("get_section_data(): Section does not exist."); return false; } /** * clear_database() * * Deletes all data from the phpGACL tables. USE WITH CAUTION. * * @return bool Returns TRUE if successful, FALSE otherwise * */ function clear_database(){ $tablesToClear = array( $this->_db_table_prefix.'acl', $this->_db_table_prefix.'aco', $this->_db_table_prefix.'aco_map', $this->_db_table_prefix.'aco_sections', $this->_db_table_prefix.'aro', $this->_db_table_prefix.'aro_groups', $this->_db_table_prefix.'aro_groups_map', $this->_db_table_prefix.'aro_map', $this->_db_table_prefix.'aro_sections', $this->_db_table_prefix.'axo', $this->_db_table_prefix.'axo_groups', $this->_db_table_prefix.'axo_groups_map', $this->_db_table_prefix.'axo_map', $this->_db_table_prefix.'axo_sections', $this->_db_table_prefix.'groups_aro_map', $this->_db_table_prefix.'groups_axo_map' ); // Get all the table names and loop $tableNames = $this->db->MetaTables('TABLES'); $query = array(); foreach ($tableNames as $key => $value){ if (in_array($value, $tablesToClear) ) { $query[] = 'TRUNCATE TABLE '.$value.';'; } } // Loop the queries and return. foreach ($query as $key => $value){ $result = $this->db->Execute($value); } return TRUE; } } ?> phpgacl-3.3.7/profiler.inc0100644025754300001440000001703607523334171014402 0ustar ipsousersdescription = array(); $this->startTime = array(); $this->endTime = array(); $this->initTime = 0; $this->cur_timer = ""; $this->stack = array(); $this->trail = ""; $this->trace = ""; $this->count = array(); $this->running = array(); $this->initTime = $this->getMicroTime(); $this->output_enabled = $output_enabled; $this->trace_enabled = $trace_enabled; $this->startTimer('unprofiled'); } // Public Methods /** * Start an individual timer * This will pause the running timer and place it on a stack. * @param string $name name of the timer * @param string optional $desc description of the timer */ function startTimer($name, $desc="" ){ $this->trace.="start $name\n"; $n=array_push( $this->stack, $this->cur_timer ); $this->__suspendTimer( $this->stack[$n-1] ); $this->startTime[$name] = $this->getMicroTime(); $this->cur_timer=$name; $this->description[$name] = $desc; if (!array_key_exists($name,$this->count)) $this->count[$name] = 1; else $this->count[$name]++; } /** * Stop an individual timer * Restart the timer that was running before this one * @param string $name name of the timer */ function stopTimer($name){ $this->trace.="stop $name\n"; $this->endTime[$name] = $this->getMicroTime(); if (!array_key_exists($name, $this->running)) $this->running[$name] = $this->elapsedTime($name); else $this->running[$name] += $this->elapsedTime($name); $this->cur_timer=array_pop($this->stack); $this->__resumeTimer($this->cur_timer); } /** * measure the elapsed time of a timer without stoping the timer if * it is still running */ function elapsedTime($name){ // This shouldn't happen, but it does once. if (!array_key_exists($name,$this->startTime)) return 0; if(array_key_exists($name,$this->endTime)){ return ($this->endTime[$name] - $this->startTime[$name]); } else { $now=$this->getMicroTime(); return ($now - $this->startTime[$name]); } }//end start_time /** * Measure the elapsed time since the profile class was initialised * */ function elapsedOverall(){ $oaTime = $this->getMicroTime() - $this->initTime; return($oaTime); }//end start_time /** * print out a log of all the timers that were registered * */ function printTimers($enabled=false) { if($this->output_enabled||$enabled){ $TimedTotal = 0; $tot_perc = 0; ksort($this->description); print("
\n");
            $oaTime = $this->getMicroTime() - $this->initTime;
            echo"============================================================================\n";
            echo "                              PROFILER OUTPUT\n";
            echo"============================================================================\n";
            print( "Calls                    Time  Routine\n");
            echo"-----------------------------------------------------------------------------\n";
            while (list ($key, $val) = each ($this->description)) {
                $t = $this->elapsedTime($key);
                $total = $this->running[$key];
                $count = $this->count[$key];
                $TimedTotal += $total;
                $perc = ($total/$oaTime)*100;
                $tot_perc+=$perc;
                // $perc=sprintf("%3.2f", $perc );
                printf( "%3d    %3.4f ms (%3.2f %%)  %s\n", $count, $total*1000, $perc, $key);
            }

            echo "\n";

            $missed=$oaTime-$TimedTotal;
            $perc = ($missed/$oaTime)*100;
            $tot_perc+=$perc;
            // $perc=sprintf("%3.2f", $perc );
            printf( "       %3.4f ms (%3.2f %%)  %s\n", $missed*1000,$perc, "Missed");

            echo"============================================================================\n";

            printf( "       %3.4f ms (%3.2f %%)  %s\n", $oaTime*1000,$tot_perc, "OVERALL TIME");

            echo"============================================================================\n";

            print("
"); } } function printTrace( $enabled=false ) { if($this->trace_enabled||$enabled){ print("
");
            print("Trace\n$this->trace\n\n");
            print("
"); } } /// Internal Use Only Functions /** * Get the current time as accuratly as possible * */ function getMicroTime(){ $tmp=split(" ",microtime()); $rt=$tmp[0]+$tmp[1]; return $rt; } /** * resume an individual timer * */ function __resumeTimer($name){ $this->trace.="resume $name\n"; $this->startTime[$name] = $this->getMicroTime(); } /** * suspend an individual timer * */ function __suspendTimer($name){ $this->trace.="suspend $name\n"; $this->endTime[$name] = $this->getMicroTime(); if (!array_key_exists($name, $this->running)) $this->running[$name] = $this->elapsedTime($name); else $this->running[$name] += $this->elapsedTime($name); } } function profiler_start($name) { if (array_key_exists("midcom_profiler",$GLOBALS)) $GLOBALS["midcom_profiler"]->startTimer ($name); } function profiler_stop($name) { if (array_key_exists("midcom_profiler",$GLOBALS)) $GLOBALS["midcom_profiler"]->stopTimer ($name); } ?> phpgacl-3.3.7/other_languages/0040755025754300001440000000000010476665053015237 5ustar ipsousersphpgacl-3.3.7/other_languages/perl/0040755025754300001440000000000010476665053016201 5ustar ipsousersphpgacl-3.3.7/other_languages/perl/perlGACL-check-1.0.tar.gz0100644025754300001440000001543210045251652022256 0ustar ipsousers@@\{wGϿ̧EY4IJb,azP L3xl6ŌlQvϞ9qGuw[_ivi6vz8hx Fw=_5` =j.=TO'WS>V_\?ONƋg? -O1u|o χϥrA®Ecϛ/Y⇟_Z[k*!0B_gfWP舿G}oyL}4hHEn艗^KըcKtE v{dc-9I1\X<0ťC1vqT;qo37 %S OZnt}1@ZcPT^,hNX*pn$tF:Bb;G`9oҚ3ۧNٌgQ*YQ9D!bBcDrpb 1; 07FKzOmLٷXȅM&Ÿu~MĨ .O߉2FɨE-"Y &FgDFTc\]Ҩ(R_{rZc,L 43I Xx8Kn?/ED\*NJK/_|-~oXù,=9Wˁͨx P?<ӟ^q+/jUjin0j"8]JStKH2Efz4Snla8Xyjk-(3OF5סt}yXzY˥Vr3If-gbYb&!>$2JgQ0PM1Tqh$}4f,;U5 aJ*~[ROG7)ƨpy=QA3|?jWH]>݂1֫b{uz5kO@sz^UdODuU޴S'sD(&ܧ"uǭ jl5<ܐ0H!0e;Wl:vC0dhm4&٥crDm]&~g2Ҡ0G#w.{ړ&t㊨jah55-nƊJ;+"U`VH0zL}旂fWMx*eO_lW-FuX6# lŒ-o=cqK i/E I /8-R/l0=!Bzp!]7Qy >& V9ȑUd35JCN!bk.`]H7|`?Ҡk@9:v.R'- Orf7߈75dG$`LQսmt[^YYS@11Ib.YG T_(P̦6 >:}C AiK?`qJsL\Uf'YSf.O vzՑ̕ʃ~Q|(X~@MQS5)`_GB_j¶T7SazhZi,O+;g 46є<D14}9S?GɎ/)"1h0 G.B<\I%u %p%׷Z@*GQ_4ħCAFrRqDxjM r?*pZ.)$TSt%u#,"_cNs ._g`zZi࿥a5~7Ä Oh ̮VUpMtУ>oK%X&x]zݽ(*f>3ɵڜ_^u.Ń t[ FriJ$%GjW02Ӄ{<1x,+U4~Fc2Z;j_T1T|7vUBC蚉gcO 8UX8@BW446v0H ]!<{JsIl'_j?TŮbW~`G,u hR坜[H꼚 FR _) Ѕ{PBu qaCD# 2C+2ZSO~8·nY)6vMJ:4qԭTEǬϯ69y*Й]⦵޻D{Ee-M0K|8c]= =*mm+-T$~20w(hݱצSsWJQWZ!'Ut^IP)+ŐEh]7Q{^ Bsjz+>&&:=qy*}ʲswwz$& bU*lbb)dFH"9DVIjC4% P,WL"Z=ZCHcʇ1:lF4](GhlO&p7%7,Ÿ|,!!}1C_ʑ=A3G𬙴8yi9JM`:"/W#TI2\<,SGLMo{WȍᘵRԓ@Rv[k=l'|5&xs)5qI!5Vֵ懇g:z9ޤyGDR~1#eпG9XHr<ʐLfjn$fR®b18VBr: @@^0NulF &Q\ے/p=uL#QJ/oP;dz*k3^HX 8tV o8QM8IN)S k-|2iZEn$ )cA%%Uż$R_1F C3uCol`nC!71,m6ӍU(9,vm} Il S)!f4լɟl~ mӊX('r=&=dmruF*$ >63ũczN31FgrkQh}?9U>t` R:ض%8#Bp.QOdFKژ-ؘ)PSVm0!;J(_{.8ba(9a0{Y}9߲8F ڗg%^ hw~:I̧BH.1qtGi3aS6).[mwTa0lɗj +~ ̂  cA$獫N U^F)-o4;->ݵ*Q_xbMBg#we,~꒗w߾Zf:TEh 㯤*_/8Ѧ ]XŽW-_xr΀#Mzcx&؀Qp lO'IL:꘶2m!S#JǤ<^)4>PUSpO\+ v6#ݫ?kiS^F,> /64~s㌕ZlRK-N|@/6 ag;?SG^4zz , GBtfàNh, OU,k7IHYd6]J uh(uֹn4o; !xv[wG,[2AJ=?'ӝhwOG֫yfӿlzUWuޜ{}kP%؍K\ )gV{۞I2*ІcH7w㴮IPyDYTp=;djZgdų}IZU\ϭ ~.^~@-/ZB4NsB^ X>kLΗu,fAꙎԇ6A]`cu s3bNCB R^4$kp%/σO/2(qc^?|CA|O^/'ͷ nCmURt5<ލkB߯cuJrFi .AWqFI HD4b5mL|֍haoIIFU DTsMm墒E+u~|!\w0HoZS(K^-4ܱ3Ąg;%Z}t=@pW#^zw[}gKU|kN] Wufcmα:ġ@TZi^/w؟̍'xR'x$<:_w6unߊ.-#otv:|) Sm39ʃ%#*ڪ]ҟn7W ؛Ђ&mYmbl`v "tذ $e`@i v`lv_=çAL*US/O"zĆu 9)dad*`0h@gh_m{/D ok\W8X-}himk.=ˀ r^23 nWW f߮H1$4.͋nTc=S}3fe6qǍh /`l\X +mp0A0XH4߅W_(vh_C8??X!T 0WuϪH 2N|"ʜew|t@<,}p39/0·W F45M+ aƐ26bkȓ)B^@L#a$ kMu12"~.|=e]5Z~0eIdy&#ôP?! P4N Xr܉_Y!bUk{HwZIWfk<|"('SPfxP?L,3n'0;.PR  9}k-)Ot0j)t:ONAxoTl!U5RWj'6F}t-ި!9xR3|t gDiwzGѼ boơihxqq^sL=Kʵw8b>ՇQ8CK&jiz@'B"{2*Q _;J1_oѐ ho.pIW SM;̻'2dȐ!C^xphpgacl-3.3.7/FAQ0100644025754300001440000000007207670767537012432 0ustar ipsousersPlease see docs/MANUAL or http://phpgacl.sourceforge.net/ phpgacl-3.3.7/TODO0100644025754300001440000000524110206666215012547 0ustar ipsousers* Create a function that a SQL command with variables inserted can be passed to, phpGACL will then replace the variables with ACL specific SQL and return the entire query to be executed. IE: $sql = "select * from table #phggacl_join# where #phpgacl_allow# limit 100 offset 10"; $retval = get_acl_sql( , , (ARO|AXO), <(ARX|AXO) Section>, $sql ); phpGACL would insert the proper SQL to join ACL tables at #phpgacl_join# and proper SQL to limit the query to allowed entries only at #phpgacl_allow#. I think this is the best solution to the huge amount of rows for acl_check() problem. * Have the Admin interface "show code" as it does operations. * Create code that takes an ACL_ID as input arguments and returns example acl_check() code that will hit the given ACL, regardless if it is ALLOW or DENY. This should really help people get started with phpGACL. * Create enviroment tests in test suite to check if the database has data in it or not. * Create regression tests. - Need more of these. * Write function to find which groups an object is in. * Add ability to find all groups objects are assigned too in admin interface. * Value is a reserved word on SapDB/MaxDB. Quote it. * Make sure all input values in gacl.class.php are quoted. * Create upgrade.php script, to handle as much of the upgrade process as possible. Have it display the changelog as well? Have setup.php handle this? * Array ACL Checks for select boxes. - Half done currently. * Add visual notification when a section has hit the $max_select_box_items. * Add filters/AXO's to ACL Test page. * Oracle compatibility. XML Schema should fix the majority of the Oracle issues. * FUTURE: Build framework for the plugin system. Create a "plugin" directory. Create a "plugin" section on the acl_admin page where plugin form fields can be displayed? Plugins must be enabled in the config file, for both the admin interface and the calling interface? * FUTURE: Possibly support tree'd ACOs. * FUTURE: Allow phpGACL to set permissions on its own administration interface. Having said that, I think going 99.9% of the way would be inside said scope and would be something to seriously consider. We could add all the ACO's, ARO's, Groups, and mark them "system" so they can't be deleted, as well as all the acl_check()'s. Then in an include file or something we could place a hook to plugin with any existing login/authentication mechanism and disable all this by default. Once the user enables it and plugs in a simple ARO value, it would all come to life and allow them the ability to use phpGACL to set permissions on itself. * Write "mock" implementations to show off some of phpGACL's benefits. phpgacl-3.3.7/gacl.class.php0100644025754300001440000004661110476656244014622 0ustar ipsousers */ class gacl { /* --- phpGACL Configuration path/file --- */ var $config_file = './gacl.ini.php'; /* --- Private properties --- */ /** @var boolean Enables Debug output if true */ var $_debug = FALSE; /* --- Database configuration. --- */ /** @var string Prefix for all the phpgacl tables in the database */ var $_db_table_prefix = ''; /** @var string The database type, based on available ADODB connectors - mysql, postgres7, sybase, oci8po See here for more: http://php.weblogs.com/adodb_manual#driverguide */ var $_db_type = 'mysql'; /** @var string The database server */ var $_db_host = 'localhost'; /** @var string The database user name */ var $_db_user = 'root'; /** @var string The database user password */ var $_db_password = ''; /** @var string The database name */ var $_db_name = 'gacl'; /** @var object An ADODB database connector object */ var $_db = ''; /* * NOTE: This cache must be manually cleaned each time ACL's are modified. * Alternatively you could wait for the cache to expire. */ /** @var boolean Caches queries if true */ var $_caching = FALSE; /** @var boolean Force cache to expire */ var $_force_cache_expire = TRUE; /** @var string The directory for cache file to eb written (ensure write permission are set) */ var $_cache_dir = '/tmp/phpgacl_cache'; // NO trailing slash /** @var int The time for the cache to expire in seconds - 600 == Ten Minutes */ var $_cache_expire_time=600; /** @var string A switch to put acl_check into '_group_' mode */ var $_group_switch = '_group_'; /** * Constructor * @param array An arry of options to oeverride the class defaults */ function gacl($options = NULL) { $available_options = array('db','debug','items_per_page','max_select_box_items','max_search_return_items','db_table_prefix','db_type','db_host','db_user','db_password','db_name','caching','force_cache_expire','cache_dir','cache_expire_time'); //Values supplied in $options array overwrite those in the config file. if ( file_exists($this->config_file) ) { $config = parse_ini_file($this->config_file); if ( is_array($config) ) { $gacl_options = array_merge($config, $options); } unset($config); } if (is_array($options)) { foreach ($options as $key => $value) { $this->debug_text("Option: $key"); if (in_array($key, $available_options) ) { $this->debug_text("Valid Config options: $key"); $property = '_'.$key; $this->$property = $value; } else { $this->debug_text("ERROR: Config option: $key is not a valid option"); } } } require_once( ADODB_DIR .'/adodb.inc.php'); require_once( ADODB_DIR .'/adodb-pager.inc.php'); if (is_object($this->_db)) { $this->db = &$this->_db; } else { $this->db = ADONewConnection($this->_db_type); //Use NUM for slight performance/memory reasons. $this->db->SetFetchMode(ADODB_FETCH_NUM); $this->db->PConnect($this->_db_host, $this->_db_user, $this->_db_password, $this->_db_name); } $this->db->debug = $this->_debug; if ( $this->_caching == TRUE ) { if (!class_exists('Hashed_Cache_Lite')) { require_once(dirname(__FILE__) .'/Cache_Lite/Hashed_Cache_Lite.php'); } /* * Cache options. We default to the highest performance. If you run in to cache corruption problems, * Change all the 'false' to 'true', this will slow things down slightly however. */ $cache_options = array( 'caching' => $this->_caching, 'cacheDir' => $this->_cache_dir.'/', 'lifeTime' => $this->_cache_expire_time, 'fileLocking' => TRUE, 'writeControl' => FALSE, 'readControl' => FALSE, 'memoryCaching' => TRUE, 'automaticSerialization' => FALSE ); $this->Cache_Lite = new Hashed_Cache_Lite($cache_options); } return true; } /** * Prints debug text if debug is enabled. * @param string THe text to output * @return boolean Always returns true */ function debug_text($text) { if ($this->_debug) { echo "$text
\n"; } return true; } /** * Prints database debug text if debug is enabled. * @param string The name of the function calling this method * @return string Returns an error message */ function debug_db($function_name = '') { if ($function_name != '') { $function_name .= ' (): '; } return $this->debug_text ($function_name .'database error: '. $this->db->ErrorMsg() .' ('. $this->db->ErrorNo() .')'); } /** * Wraps the actual acl_query() function. * * It is simply here to return TRUE/FALSE accordingly. * @param string The ACO section value * @param string The ACO value * @param string The ARO section value * @param string The ARO section * @param string The AXO section value (optional) * @param string The AXO section value (optional) * @param integer The group id of the ARO ??Mike?? (optional) * @param integer The group id of the AXO ??Mike?? (optional) * @return boolean TRUE if the check succeeds, false if not. */ function acl_check($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value=NULL, $axo_value=NULL, $root_aro_group=NULL, $root_axo_group=NULL) { $acl_result = $this->acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value, $root_aro_group, $root_axo_group); return $acl_result['allow']; } /** * Wraps the actual acl_query() function. * * Quick access to the return value of an ACL. * @param string The ACO section value * @param string The ACO value * @param string The ARO section value * @param string The ARO section * @param string The AXO section value (optional) * @param string The AXO section value (optional) * @param integer The group id of the ARO (optional) * @param integer The group id of the AXO (optional) * @return string The return value of the ACL */ function acl_return_value($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value=NULL, $axo_value=NULL, $root_aro_group=NULL, $root_axo_group=NULL) { $acl_result = $this->acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value, $axo_value, $root_aro_group, $root_axo_group); return $acl_result['return_value']; } /** * Handles ACL lookups over arrays of AROs * @param string The ACO section value * @param string The ACO value * @param array An named array of arrays, each element in the format aro_section_value=>array(aro_value1,aro_value1,...) * @return mixed The same data format as inputted. \*======================================================================*/ function acl_check_array($aco_section_value, $aco_value, $aro_array) { /* Input Array: Section => array(Value, Value, Value), Section => array(Value, Value, Value) */ if (!is_array($aro_array)) { $this->debug_text("acl_query_array(): ARO Array must be passed"); return false; } foreach($aro_array as $aro_section_value => $aro_value_array) { foreach ($aro_value_array as $aro_value) { $this->debug_text("acl_query_array(): ARO Section Value: $aro_section_value ARO VALUE: $aro_value"); if( $this->acl_check($aco_section_value, $aco_value, $aro_section_value, $aro_value) ) { $this->debug_text("acl_query_array(): ACL_CHECK True"); $retarr[$aro_section_value][] = $aro_value; } else { $this->debug_text("acl_query_array(): ACL_CHECK False"); } } } return $retarr; } /** * The Main function that does the actual ACL lookup. * @param string The ACO section value * @param string The ACO value * @param string The ARO section value * @param string The ARO section * @param string The AXO section value (optional) * @param string The AXO section value (optional) * @param string The value of the ARO group (optional) * @param string The value of the AXO group (optional) * @param boolean Debug the operation if true (optional) * @return array Returns as much information as possible about the ACL so other functions can trim it down and omit unwanted data. */ function acl_query($aco_section_value, $aco_value, $aro_section_value, $aro_value, $axo_section_value=NULL, $axo_value=NULL, $root_aro_group=NULL, $root_axo_group=NULL, $debug=NULL) { $cache_id = 'acl_query_'.$aco_section_value.'-'.$aco_value.'-'.$aro_section_value.'-'.$aro_value.'-'.$axo_section_value.'-'.$axo_value.'-'.$root_aro_group.'-'.$root_axo_group.'-'.$debug; $retarr = $this->get_cache($cache_id); if (!$retarr) { /* * Grab all groups mapped to this ARO/AXO */ $aro_group_ids = $this->acl_get_groups($aro_section_value, $aro_value, $root_aro_group, 'ARO'); if (is_array($aro_group_ids) AND !empty($aro_group_ids)) { $sql_aro_group_ids = implode(',', $aro_group_ids); } if ($axo_section_value != '' AND $axo_value != '') { $axo_group_ids = $this->acl_get_groups($axo_section_value, $axo_value, $root_axo_group, 'AXO'); if (is_array($axo_group_ids) AND !empty($axo_group_ids)) { $sql_axo_group_ids = implode(',', $axo_group_ids); } } /* * This query is where all the magic happens. * The ordering is very important here, as well very tricky to get correct. * Currently there can be duplicate ACLs, or ones that step on each other toes. In this case, the ACL that was last updated/created * is used. * * This is probably where the most optimizations can be made. */ $order_by = array(); $query = ' SELECT a.id,a.allow,a.return_value FROM '. $this->_db_table_prefix .'acl a LEFT JOIN '. $this->_db_table_prefix .'aco_map ac ON ac.acl_id=a.id'; if ($aro_section_value != $this->_group_switch) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'aro_map ar ON ar.acl_id=a.id'; } if ($axo_section_value != $this->_group_switch) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'axo_map ax ON ax.acl_id=a.id'; } /* * if there are no aro groups, don't bother doing the join. */ if (isset($sql_aro_group_ids)) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'aro_groups_map arg ON arg.acl_id=a.id LEFT JOIN '. $this->_db_table_prefix .'aro_groups rg ON rg.id=arg.group_id'; } // this join is necessary to weed out rules associated with axo groups $query .= ' LEFT JOIN '. $this->_db_table_prefix .'axo_groups_map axg ON axg.acl_id=a.id'; /* * if there are no axo groups, don't bother doing the join. * it is only used to rank by the level of the group. */ if (isset($sql_axo_group_ids)) { $query .= ' LEFT JOIN '. $this->_db_table_prefix .'axo_groups xg ON xg.id=axg.group_id'; } //Move the below line to the LEFT JOIN above for PostgreSQL's sake. //AND ac.acl_id=a.id $query .= ' WHERE a.enabled=1 AND (ac.section_value='. $this->db->quote($aco_section_value) .' AND ac.value='. $this->db->quote($aco_value) .')'; // if we are querying an aro group if ($aro_section_value == $this->_group_switch) { // if acl_get_groups did not return an array if ( !isset ($sql_aro_group_ids) ) { $this->debug_text ('acl_query(): Invalid ARO Group: '. $aro_value); return FALSE; } $query .= ' AND rg.id IN ('. $sql_aro_group_ids .')'; $order_by[] = '(rg.rgt-rg.lft) ASC'; } else { $query .= ' AND ((ar.section_value='. $this->db->quote($aro_section_value) .' AND ar.value='. $this->db->quote($aro_value) .')'; if ( isset ($sql_aro_group_ids) ) { $query .= ' OR rg.id IN ('. $sql_aro_group_ids .')'; $order_by[] = '(CASE WHEN ar.value IS NULL THEN 0 ELSE 1 END) DESC'; $order_by[] = '(rg.rgt-rg.lft) ASC'; } $query .= ')'; } // if we are querying an axo group if ($axo_section_value == $this->_group_switch) { // if acl_get_groups did not return an array if ( !isset ($sql_axo_group_ids) ) { $this->debug_text ('acl_query(): Invalid AXO Group: '. $axo_value); return FALSE; } $query .= ' AND xg.id IN ('. $sql_axo_group_ids .')'; $order_by[] = '(xg.rgt-xg.lft) ASC'; } else { $query .= ' AND ('; if ($axo_section_value == '' AND $axo_value == '') { $query .= '(ax.section_value IS NULL AND ax.value IS NULL)'; } else { $query .= '(ax.section_value='. $this->db->quote($axo_section_value) .' AND ax.value='. $this->db->quote($axo_value) .')'; } if (isset($sql_axo_group_ids)) { $query .= ' OR xg.id IN ('. $sql_axo_group_ids .')'; $order_by[] = '(CASE WHEN ax.value IS NULL THEN 0 ELSE 1 END) DESC'; $order_by[] = '(xg.rgt-xg.lft) ASC'; } else { $query .= ' AND axg.group_id IS NULL'; } $query .= ')'; } /* * The ordering is always very tricky and makes all the difference in the world. * Order (ar.value IS NOT NULL) DESC should put ACLs given to specific AROs * ahead of any ACLs given to groups. This works well for exceptions to groups. */ $order_by[] = 'a.updated_date DESC'; $query .= ' ORDER BY '. implode (',', $order_by) . ' '; // we are only interested in the first row $rs = $this->db->SelectLimit($query, 1); if (!is_object($rs)) { $this->debug_db('acl_query'); return FALSE; } $row =& $rs->FetchRow(); /* * Return ACL ID. This is the key to "hooking" extras like pricing assigned to ACLs etc... Very useful. */ if (is_array($row)) { // Permission granted? // This below oneliner is very confusing. //$allow = (isset($row[1]) AND $row[1] == 1); //Prefer this. if ( isset($row[1]) AND $row[1] == 1 ) { $allow = TRUE; } else { $allow = FALSE; } $retarr = array('acl_id' => &$row[0], 'return_value' => &$row[2], 'allow' => $allow); } else { // Permission denied. $retarr = array('acl_id' => NULL, 'return_value' => NULL, 'allow' => FALSE); } /* * Return the query that we ran if in debug mode. */ if ($debug == TRUE) { $retarr['query'] = &$query; } //Cache data. $this->put_cache($retarr, $cache_id); } $this->debug_text("acl_query(): ACO Section: $aco_section_value ACO Value: $aco_value ARO Section: $aro_section_value ARO Value $aro_value ACL ID: ". $retarr['acl_id'] .' Result: '. $retarr['allow']); return $retarr; } /** * Grabs all groups mapped to an ARO. You can also specify a root_group for subtree'ing. * @param string The section value or the ARO or ACO * @param string The value of the ARO or ACO * @param integer The group id of the group to start at (optional) * @param string The type of group, either ARO or AXO (optional) */ function acl_get_groups($section_value, $value, $root_group=NULL, $group_type='ARO') { switch(strtolower($group_type)) { case 'axo': $group_type = 'axo'; $object_table = $this->_db_table_prefix .'axo'; $group_table = $this->_db_table_prefix .'axo_groups'; $group_map_table = $this->_db_table_prefix .'groups_axo_map'; break; default: $group_type = 'aro'; $object_table = $this->_db_table_prefix .'aro'; $group_table = $this->_db_table_prefix .'aro_groups'; $group_map_table = $this->_db_table_prefix .'groups_aro_map'; break; } //$profiler->startTimer( "acl_get_groups()"); //Generate unique cache id. $cache_id = 'acl_get_groups_'.$section_value.'-'.$value.'-'.$root_group.'-'.$group_type; $retarr = $this->get_cache($cache_id); if (!$retarr) { // Make sure we get the groups $query = ' SELECT DISTINCT g2.id'; if ($section_value == $this->_group_switch) { $query .= ' FROM ' . $group_table . ' g1,' . $group_table . ' g2'; $where = ' WHERE g1.value=' . $this->db->quote( $value ); } else { $query .= ' FROM '. $object_table .' o,'. $group_map_table .' gm,'. $group_table .' g1,'. $group_table .' g2'; $where = ' WHERE (o.section_value='. $this->db->quote($section_value) .' AND o.value='. $this->db->quote($value) .') AND gm.'. $group_type .'_id=o.id AND g1.id=gm.group_id'; } /* * If root_group_id is specified, we have to narrow this query down * to just groups deeper in the tree then what is specified. * This essentially creates a virtual "subtree" and ignores all outside groups. * Useful for sites like sourceforge where you may seperate groups by "project". */ if ( $root_group != '') { //It is important to note the below line modifies the tables being selected. //This is the reason for the WHERE variable. $query .= ','. $group_table .' g3'; $where .= ' AND g3.value='. $this->db->quote( $root_group ) .' AND ((g2.lft BETWEEN g3.lft AND g1.lft) AND (g2.rgt BETWEEN g1.rgt AND g3.rgt))'; } else { $where .= ' AND (g2.lft <= g1.lft AND g2.rgt >= g1.rgt)'; } $query .= $where; // $this->debug_text($query); $rs = $this->db->Execute($query); if (!is_object($rs)) { $this->debug_db('acl_get_groups'); return FALSE; } $retarr = array(); //Unbuffered query? while (!$rs->EOF) { $retarr[] = reset($rs->fields); $rs->MoveNext(); } //Cache data. $this->put_cache($retarr, $cache_id); } return $retarr; } /** * Uses PEAR's Cache_Lite package to grab cached arrays, objects, variables etc... * using unserialize() so it can handle more then just text string. * @param string The id of the cached object * @return mixed The cached object, otherwise FALSE if the object identifier was not found */ function get_cache($cache_id) { if ( $this->_caching == TRUE ) { $this->debug_text("get_cache(): on ID: $cache_id"); if ( is_string($this->Cache_Lite->get($cache_id) ) ) { return unserialize($this->Cache_Lite->get($cache_id) ); } } return false; } /** * Uses PEAR's Cache_Lite package to write cached arrays, objects, variables etc... * using serialize() so it can handle more then just text string. * @param mixed A variable to cache * @param string The id of the cached variable */ function put_cache($data, $cache_id) { if ( $this->_caching == TRUE ) { $this->debug_text("put_cache(): Cache MISS on ID: $cache_id"); return $this->Cache_Lite->save(serialize($data), $cache_id); } return false; } } ?> phpgacl-3.3.7/adodb/0040755025754300001440000000000010476665054013142 5ustar ipsousersphpgacl-3.3.7/adodb/datadict/0040755025754300001440000000000010476665054014717 5ustar ipsousersphpgacl-3.3.7/adodb/datadict/datadict-firebird.inc.php0100644025754300001440000000734510476657245021552 0ustar ipsousersconnection) ) { return $name; } $quote = $this->connection->nameQuote; // if name is of the form `name`, quote it if ( preg_match('/^`(.+)`$/', $name, $matches) ) { return $quote . $matches[1] . $quote; } // if name contains special characters, quote it if ( !preg_match('/^[' . $this->nameRegex . ']+$/', $name) ) { return $quote . $name . $quote; } return $quote . $name . $quote; } function CreateDatabase($dbname, $options=false) { $options = $this->_Options($options); $sql = array(); $sql[] = "DECLARE EXTERNAL FUNCTION LOWER CSTRING(80) RETURNS CSTRING(80) FREE_IT ENTRY_POINT 'IB_UDF_lower' MODULE_NAME 'ib_udf'"; return $sql; } function _DropAutoIncrement($t) { if (strpos($t,'.') !== false) { $tarr = explode('.',$t); return 'DROP GENERATOR '.$tarr[0].'."gen_'.$tarr[1].'"'; } return 'DROP GENERATOR "GEN_'.$t; } function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fautoinc) $this->seqField = $fname; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } /* CREATE or replace TRIGGER jaddress_insert before insert on jaddress for each row begin IF ( NEW."seqField" IS NULL OR NEW."seqField" = 0 ) THEN NEW."seqField" = GEN_ID("GEN_tabname", 1); end; */ function _Triggers($tabname,$tableoptions) { if (!$this->seqField) return array(); $tab1 = preg_replace( '/"/', '', $tabname ); if ($this->schema) { $t = strpos($tab1,'.'); if ($t !== false) $tab = substr($tab1,$t+1); else $tab = $tab1; $seqField = $this->seqField; $seqname = $this->schema.'.'.$this->seqPrefix.$tab; $trigname = $this->schema.'.trig_'.$this->seqPrefix.$tab; } else { $seqField = $this->seqField; $seqname = $this->seqPrefix.$tab1; $trigname = 'trig_'.$seqname; } if (isset($tableoptions['REPLACE'])) { $sql[] = "DROP GENERATOR \"$seqname\""; $sql[] = "CREATE GENERATOR \"$seqname\""; $sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END"; } else { $sql[] = "CREATE GENERATOR \"$seqname\""; $sql[] = "CREATE TRIGGER \"$trigname\" FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END"; } $this->seqField = false; return $sql; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-postgres.inc.php0100644025754300001440000002743710476657245021636 0ustar ipsouserstype; $len = $fieldobj->max_length; } $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique && $fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval('; switch (strtoupper($t)) { case 'INTERVAL': case 'CHAR': case 'CHARACTER': case 'VARCHAR': case 'NAME': case 'BPCHAR': if ($len <= $this->blobSize) return 'C'; case 'TEXT': return 'X'; case 'IMAGE': // user defined type case 'BLOB': // user defined type case 'BIT': // This is a bit string, not a single bit, so don't return 'L' case 'VARBIT': case 'BYTEA': return 'B'; case 'BOOL': case 'BOOLEAN': return 'L'; case 'DATE': return 'D'; case 'TIME': case 'DATETIME': case 'TIMESTAMP': case 'TIMESTAMPTZ': return 'T'; case 'INTEGER': return !$is_serial ? 'I' : 'R'; case 'SMALLINT': case 'INT2': return !$is_serial ? 'I2' : 'R'; case 'INT4': return !$is_serial ? 'I4' : 'R'; case 'BIGINT': case 'INT8': return !$is_serial ? 'I8' : 'R'; case 'OID': case 'SERIAL': return 'R'; case 'FLOAT4': case 'FLOAT8': case 'DOUBLE PRECISION': case 'REAL': return 'F'; default: return 'N'; } } function ActualType($meta) { switch($meta) { case 'C': return 'VARCHAR'; case 'XL': case 'X': return 'TEXT'; case 'C2': return 'VARCHAR'; case 'X2': return 'TEXT'; case 'B': return 'BYTEA'; case 'D': return 'DATE'; case 'T': return 'TIMESTAMP'; case 'L': return 'BOOLEAN'; case 'I': return 'INTEGER'; case 'I1': return 'SMALLINT'; case 'I2': return 'INT2'; case 'I4': return 'INT4'; case 'I8': return 'INT8'; case 'F': return 'FLOAT8'; case 'N': return 'NUMERIC'; default: return $meta; } } /** * Adding a new Column * * reimplementation of the default function as postgres does NOT allow to set the default in the same statement * * @param string $tabname table-name * @param string $flds column-names and types for the changed columns * @return array with SQL strings */ function AddColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; foreach($lines as $v) { if (($not_null = preg_match('/NOT NULL/i',$v))) { $v = preg_replace('/NOT NULL/i','',$v); } if (preg_match('/^([^ ]+) .*DEFAULT ([^ ]+)/',$v,$matches)) { list(,$colname,$default) = $matches; $sql[] = $alter . str_replace('DEFAULT '.$default,'',$v); $sql[] = 'UPDATE '.$tabname.' SET '.$colname.'='.$default; $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET DEFAULT ' . $default; } else { $sql[] = $alter . $v; } if ($not_null) { list($colname) = explode(' ',$v); $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; } } return $sql; } /** * Change the definition of one column * * Postgres can't do that on it's own, you need to supply the complete defintion of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { if (!$tableflds) { if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); return array(); } return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); } /** * Drop one column * * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { $has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; if (!$has_drop_column && !$tableflds) { if ($this->debug) ADOConnection::outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); return array(); } if ($has_drop_column) { return ADODB_DataDict::DropColumnSQL($tabname, $flds); } return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); } /** * Save the content into a temp. table, drop and recreate the original table and copy the content back in * * We also take care to set the values of the sequenz and recreate the indexes. * All this is done in a transaction, to not loose the content of the table, if something went wrong! * @internal * @param string $tabname table-name * @param string $dropflds column-names to drop * @param string $tableflds complete defintion of the new table, eg. for postgres * @param array/string $tableoptions options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') { if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); $copyflds = array(); foreach($this->MetaColumns($tabname) as $fld) { if (!$dropflds || !in_array($fld->name,$dropflds)) { // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && in_array($fld->type,array('varchar','char','text','bytea'))) { $copyflds[] = "to_number($fld->name,'S9999999999999D99')"; } else { $copyflds[] = $fld->name; } // identify the sequence name and the fld its on if ($fld->primary_key && $fld->has_default && preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { $seq_name = $matches[1]; $seq_fld = $fld->name; } } } $copyflds = implode(', ',$copyflds); $tempname = $tabname.'_tmp'; $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again $seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; } $aSql[] = "DROP TABLE $tempname"; // recreate the indexes, if they not contain one of the droped columns foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) { if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], $idx_data['unique'] ? array('UNIQUE') : False)); } } $aSql[] = 'COMMIT'; return $aSql; } function DropTableSQL($tabname) { $sql = ADODB_DataDict::DropTableSQL($tabname); $drop_seq = $this->_DropAutoIncrement($tabname); if ($drop_seq) $sql[] = $drop_seq; return $sql; } // return string must begin with space function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) { if ($fautoinc) { $ftype = 'SERIAL'; return ''; } $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } // search for a sequece for the given table (asumes the seqence-name contains the table-name!) // if yes return sql to drop it // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!! function _DropAutoIncrement($tabname) { $tabname = $this->connection->quote('%'.$tabname.'%'); $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) { return False; } return "DROP SEQUENCE ".$seq; } /* CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( { column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ] | table_constraint } [, ... ] ) [ INHERITS ( parent_table [, ... ] ) ] [ WITH OIDS | WITHOUT OIDS ] where column_constraint is: [ CONSTRAINT constraint_name ] { NOT NULL | NULL | UNIQUE | PRIMARY KEY | CHECK (expression) | REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] and table_constraint is: [ CONSTRAINT constraint_name ] { UNIQUE ( column_name [, ... ] ) | PRIMARY KEY ( column_name [, ... ] ) | CHECK ( expression ) | FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] [ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] */ /* CREATE [ UNIQUE ] INDEX index_name ON table [ USING acc_method ] ( column [ ops_name ] [, ...] ) [ WHERE predicate ] CREATE [ UNIQUE ] INDEX index_name ON table [ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] ) [ WHERE predicate ] */ function _IndexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; if (isset($idxoptions['HASH'])) $s .= 'USING HASH '; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; if ( is_array($flds) ) $flds = implode(', ',$flds); $s .= '(' . $flds . ')'; $sql[] = $s; return $sql; } function _GetSize($ftype, $ty, $fsize, $fprec) { if (strlen($fsize) && $ty != 'X' && $ty != 'B' && $ty != 'I' && strpos($ftype,'(') === false) { $ftype .= "(".$fsize; if (strlen($fprec)) $ftype .= ",".$fprec; $ftype .= ')'; } return $ftype; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-sybase.inc.php0100644025754300001440000001324210476657245021243 0ustar ipsouserstype; $len = $fieldobj->max_length; } $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { case 'INT': case 'INTEGER': return 'I'; case 'BIT': case 'TINYINT': return 'I1'; case 'SMALLINT': return 'I2'; case 'BIGINT': return 'I8'; case 'REAL': case 'FLOAT': return 'F'; default: return parent::MetaType($t,$len,$fieldobj); } } function ActualType($meta) { switch(strtoupper($meta)) { case 'C': return 'VARCHAR'; case 'XL': case 'X': return 'TEXT'; case 'C2': return 'NVARCHAR'; case 'X2': return 'NTEXT'; case 'B': return 'IMAGE'; case 'D': return 'DATETIME'; case 'T': return 'DATETIME'; case 'L': return 'BIT'; case 'I': return 'INT'; case 'I1': return 'TINYINT'; case 'I2': return 'SMALLINT'; case 'I4': return 'INT'; case 'I8': return 'BIGINT'; case 'F': return 'REAL'; case 'N': return 'NUMERIC'; default: return $meta; } } function AddColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $f = array(); list($lines,$pkey) = $this->_GenFields($flds); $s = "ALTER TABLE $tabname $this->addCol"; foreach($lines as $v) { $f[] = "\n $v"; } $s .= implode(', ',$f); $sql[] = $s; return $sql; } function AlterColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); foreach($lines as $v) { $sql[] = "ALTER TABLE $tabname $this->alterCol $v"; } return $sql; } function DropColumnSQL($tabname, $flds) { $tabname = $this->TableName($tabname); if (!is_array($flds)) $flds = explode(',',$flds); $f = array(); $s = "ALTER TABLE $tabname"; foreach($flds as $v) { $f[] = "\n$this->dropCol ".$this->NameQuote($v); } $s .= implode(', ',$f); $sql[] = $s; return $sql; } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) { $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fautoinc) $suffix .= ' DEFAULT AUTOINCREMENT'; if ($fnotnull) $suffix .= ' NOT NULL'; else if ($suffix == '') $suffix .= ' NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } /* CREATE TABLE [ database_name.[ owner ] . | owner. ] table_name ( { < column_definition > | column_name AS computed_column_expression | < table_constraint > ::= [ CONSTRAINT constraint_name ] } | [ { PRIMARY KEY | UNIQUE } [ ,...n ] ) [ ON { filegroup | DEFAULT } ] [ TEXTIMAGE_ON { filegroup | DEFAULT } ] < column_definition > ::= { column_name data_type } [ COLLATE < collation_name > ] [ [ DEFAULT constant_expression ] | [ IDENTITY [ ( seed , increment ) [ NOT FOR REPLICATION ] ] ] ] [ ROWGUIDCOL] [ < column_constraint > ] [ ...n ] < column_constraint > ::= [ CONSTRAINT constraint_name ] { [ NULL | NOT NULL ] | [ { PRIMARY KEY | UNIQUE } [ CLUSTERED | NONCLUSTERED ] [ WITH FILLFACTOR = fillfactor ] [ON {filegroup | DEFAULT} ] ] ] | [ [ FOREIGN KEY ] REFERENCES ref_table [ ( ref_column ) ] [ ON DELETE { CASCADE | NO ACTION } ] [ ON UPDATE { CASCADE | NO ACTION } ] [ NOT FOR REPLICATION ] ] | CHECK [ NOT FOR REPLICATION ] ( logical_expression ) } < table_constraint > ::= [ CONSTRAINT constraint_name ] { [ { PRIMARY KEY | UNIQUE } [ CLUSTERED | NONCLUSTERED ] { ( column [ ASC | DESC ] [ ,...n ] ) } [ WITH FILLFACTOR = fillfactor ] [ ON { filegroup | DEFAULT } ] ] | FOREIGN KEY [ ( column [ ,...n ] ) ] REFERENCES ref_table [ ( ref_column [ ,...n ] ) ] [ ON DELETE { CASCADE | NO ACTION } ] [ ON UPDATE { CASCADE | NO ACTION } ] [ NOT FOR REPLICATION ] | CHECK [ NOT FOR REPLICATION ] ( search_conditions ) } */ /* CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name ON { table | view } ( column [ ASC | DESC ] [ ,...n ] ) [ WITH < index_option > [ ,...n] ] [ ON filegroup ] < index_option > :: = { PAD_INDEX | FILLFACTOR = fillfactor | IGNORE_DUP_KEY | DROP_EXISTING | STATISTICS_NORECOMPUTE | SORT_IN_TEMPDB } */ function _IndexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; $clustered = isset($idxoptions['CLUSTERED']) ? ' CLUSTERED' : ''; if ( is_array($flds) ) $flds = implode(', ',$flds); $s = 'CREATE' . $unique . $clustered . ' INDEX ' . $idxname . ' ON ' . $tabname . ' (' . $flds . ')'; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; $sql[] = $s; return $sql; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-db2.inc.php0100644025754300001440000000754110476657245020431 0ustar ipsousersdebug) ADOConnection::outp("AlterColumnSQL not supported"); return array(); } function DropColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); return array(); } function ChangeTableSQL($tablename, $flds, $tableoptions = false) { /** Allow basic table changes to DB2 databases DB2 will fatally reject changes to non character columns */ $validTypes = array("CHAR","VARC"); $invalidTypes = array("BIGI","BLOB","CLOB","DATE", "DECI","DOUB", "INTE", "REAL","SMAL", "TIME"); // check table exists $cols = &$this->MetaColumns($tablename); if ( empty($cols)) { return $this->CreateTableSQL($tablename, $flds, $tableoptions); } // already exists, alter table instead list($lines,$pkey) = $this->_GenFields($flds); $alter = 'ALTER TABLE ' . $this->TableName($tablename); $sql = array(); foreach ( $lines as $id => $v ) { if ( isset($cols[$id]) && is_object($cols[$id]) ) { /** If the first field of $v is the fieldname, and the second is the field type/size, we assume its an attempt to modify the column size, so check that it is allowed $v can have an indeterminate number of blanks between the fields, so account for that too */ $vargs = explode(' ' , $v); // assume that $vargs[0] is the field name. $i=0; // Find the next non-blank value; for ($i=1;$ialterCol . ' ' . $v; } else { $sql[] = $alter . $this->addCol . ' ' . $v; } } return $sql; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-sapdb.inc.php0100644025754300001440000000614410476657245021051 0ustar ipsouserstype; $len = $fieldobj->max_length; } static $maxdb_type2adodb = array( 'VARCHAR' => 'C', 'CHARACTER' => 'C', 'LONG' => 'X', // no way to differ between 'X' and 'B' :-( 'DATE' => 'D', 'TIMESTAMP' => 'T', 'BOOLEAN' => 'L', 'INTEGER' => 'I4', 'SMALLINT' => 'I2', 'FLOAT' => 'F', 'FIXED' => 'N', ); $type = isset($maxdb_type2adodb[$t]) ? $maxdb_type2adodb[$t] : 'C'; // convert integer-types simulated with fixed back to integer if ($t == 'FIXED' && !$fieldobj->scale && ($len == 20 || $len == 3)) { $type = $len == 20 ? 'I8' : 'I1'; } if ($fieldobj->auto_increment) $type = 'R'; return $type; } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if ($funsigned) $suffix .= ' UNSIGNED'; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fautoinc) $suffix .= ' DEFAULT SERIAL'; elseif (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } function AddColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); return array( 'ALTER TABLE ' . $tabname . ' ADD (' . implode(', ',$lines) . ')' ); } function AlterColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); return array( 'ALTER TABLE ' . $tabname . ' MODIFY (' . implode(', ',$lines) . ')' ); } function DropColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); if (!is_array($flds)) $flds = explode(',',$flds); foreach($flds as $k => $v) { $flds[$k] = $this->NameQuote($v); } return array( 'ALTER TABLE ' . $tabname . ' DROP (' . implode(', ',$flds) . ')' ); } } ?>phpgacl-3.3.7/adodb/datadict/datadict-informix.inc.php0100644025754300001440000000330310476657245021605 0ustar ipsousersdebug) ADOConnection::outp("AlterColumnSQL not supported"); return array(); } function DropColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); return array(); } // return string must begin with space function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) { if ($fautoinc) { $ftype = 'SERIAL'; return ''; } $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-generic.inc.php0100644025754300001440000000472410476657245021376 0ustar ipsousersdebug) ADOConnection::outp("AlterColumnSQL not supported"); return array(); } function DropColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); return array(); } } /* //db2 function ActualType($meta) { switch($meta) { case 'C': return 'VARCHAR'; case 'X': return 'VARCHAR'; case 'C2': return 'VARCHAR'; // up to 32K case 'X2': return 'VARCHAR'; case 'B': return 'BLOB'; case 'D': return 'DATE'; case 'T': return 'TIMESTAMP'; case 'L': return 'SMALLINT'; case 'I': return 'INTEGER'; case 'I1': return 'SMALLINT'; case 'I2': return 'SMALLINT'; case 'I4': return 'INTEGER'; case 'I8': return 'BIGINT'; case 'F': return 'DOUBLE'; case 'N': return 'DECIMAL'; default: return $meta; } } // ifx function ActualType($meta) { switch($meta) { case 'C': return 'VARCHAR';// 255 case 'X': return 'TEXT'; case 'C2': return 'NVARCHAR'; case 'X2': return 'TEXT'; case 'B': return 'BLOB'; case 'D': return 'DATE'; case 'T': return 'DATETIME'; case 'L': return 'SMALLINT'; case 'I': return 'INTEGER'; case 'I1': return 'SMALLINT'; case 'I2': return 'SMALLINT'; case 'I4': return 'INTEGER'; case 'I8': return 'DECIMAL(20)'; case 'F': return 'FLOAT'; case 'N': return 'DECIMAL'; default: return $meta; } } */ ?>phpgacl-3.3.7/adodb/datadict/datadict-mssql.inc.php0100644025754300001440000001634210476657245021120 0ustar ipsouserstype; $len = $fieldobj->max_length; } $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { case 'R': case 'INT': case 'INTEGER': return 'I'; case 'BIT': case 'TINYINT': return 'I1'; case 'SMALLINT': return 'I2'; case 'BIGINT': return 'I8'; case 'REAL': case 'FLOAT': return 'F'; default: return parent::MetaType($t,$len,$fieldobj); } } function ActualType($meta) { switch(strtoupper($meta)) { case 'C': return 'VARCHAR'; case 'XL': return (isset($this)) ? $this->typeXL : 'TEXT'; case 'X': return (isset($this)) ? $this->typeX : 'TEXT'; ## could be varchar(8000), but we want compat with oracle case 'C2': return 'NVARCHAR'; case 'X2': return 'NTEXT'; case 'B': return 'IMAGE'; case 'D': return 'DATETIME'; case 'T': return 'DATETIME'; case 'L': return 'BIT'; case 'R': case 'I': return 'INT'; case 'I1': return 'TINYINT'; case 'I2': return 'SMALLINT'; case 'I4': return 'INT'; case 'I8': return 'BIGINT'; case 'F': return 'REAL'; case 'N': return 'NUMERIC'; default: return $meta; } } function AddColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $f = array(); list($lines,$pkey) = $this->_GenFields($flds); $s = "ALTER TABLE $tabname $this->addCol"; foreach($lines as $v) { $f[] = "\n $v"; } $s .= implode(', ',$f); $sql[] = $s; return $sql; } /* function AlterColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); foreach($lines as $v) { $sql[] = "ALTER TABLE $tabname $this->alterCol $v"; } return $sql; } */ function DropColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); if (!is_array($flds)) $flds = explode(',',$flds); $f = array(); $s = 'ALTER TABLE ' . $tabname; foreach($flds as $v) { $f[] = "\n$this->dropCol ".$this->NameQuote($v); } $s .= implode(', ',$f); $sql[] = $s; return $sql; } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) { $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fautoinc) $suffix .= ' IDENTITY(1,1)'; if ($fnotnull) $suffix .= ' NOT NULL'; else if ($suffix == '') $suffix .= ' NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } /* CREATE TABLE [ database_name.[ owner ] . | owner. ] table_name ( { < column_definition > | column_name AS computed_column_expression | < table_constraint > ::= [ CONSTRAINT constraint_name ] } | [ { PRIMARY KEY | UNIQUE } [ ,...n ] ) [ ON { filegroup | DEFAULT } ] [ TEXTIMAGE_ON { filegroup | DEFAULT } ] < column_definition > ::= { column_name data_type } [ COLLATE < collation_name > ] [ [ DEFAULT constant_expression ] | [ IDENTITY [ ( seed , increment ) [ NOT FOR REPLICATION ] ] ] ] [ ROWGUIDCOL] [ < column_constraint > ] [ ...n ] < column_constraint > ::= [ CONSTRAINT constraint_name ] { [ NULL | NOT NULL ] | [ { PRIMARY KEY | UNIQUE } [ CLUSTERED | NONCLUSTERED ] [ WITH FILLFACTOR = fillfactor ] [ON {filegroup | DEFAULT} ] ] ] | [ [ FOREIGN KEY ] REFERENCES ref_table [ ( ref_column ) ] [ ON DELETE { CASCADE | NO ACTION } ] [ ON UPDATE { CASCADE | NO ACTION } ] [ NOT FOR REPLICATION ] ] | CHECK [ NOT FOR REPLICATION ] ( logical_expression ) } < table_constraint > ::= [ CONSTRAINT constraint_name ] { [ { PRIMARY KEY | UNIQUE } [ CLUSTERED | NONCLUSTERED ] { ( column [ ASC | DESC ] [ ,...n ] ) } [ WITH FILLFACTOR = fillfactor ] [ ON { filegroup | DEFAULT } ] ] | FOREIGN KEY [ ( column [ ,...n ] ) ] REFERENCES ref_table [ ( ref_column [ ,...n ] ) ] [ ON DELETE { CASCADE | NO ACTION } ] [ ON UPDATE { CASCADE | NO ACTION } ] [ NOT FOR REPLICATION ] | CHECK [ NOT FOR REPLICATION ] ( search_conditions ) } */ /* CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name ON { table | view } ( column [ ASC | DESC ] [ ,...n ] ) [ WITH < index_option > [ ,...n] ] [ ON filegroup ] < index_option > :: = { PAD_INDEX | FILLFACTOR = fillfactor | IGNORE_DUP_KEY | DROP_EXISTING | STATISTICS_NORECOMPUTE | SORT_IN_TEMPDB } */ function _IndexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; $clustered = isset($idxoptions['CLUSTERED']) ? ' CLUSTERED' : ''; if ( is_array($flds) ) $flds = implode(', ',$flds); $s = 'CREATE' . $unique . $clustered . ' INDEX ' . $idxname . ' ON ' . $tabname . ' (' . $flds . ')'; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; $sql[] = $s; return $sql; } function _GetSize($ftype, $ty, $fsize, $fprec) { switch ($ftype) { case 'INT': case 'SMALLINT': case 'TINYINT': case 'BIGINT': return $ftype; } if ($ty == 'T') return $ftype; return parent::_GetSize($ftype, $ty, $fsize, $fprec); } } ?>phpgacl-3.3.7/adodb/datadict/datadict-access.inc.php0100644025754300001440000000372410476657245021222 0ustar ipsousersdebug) ADOConnection::outp("Warning: Access does not supported DEFAULT values (field $fname)"); } if ($fnotnull) $suffix .= ' NOT NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } function CreateDatabase($dbname,$options=false) { return array(); } function SetSchema($schema) { } function AlterColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported"); return array(); } function DropColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); return array(); } } ?>phpgacl-3.3.7/adodb/datadict/datadict-oci8.inc.php0100644025754300001440000001607110476657245020622 0ustar ipsouserstype; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'VARCHAR': case 'VARCHAR2': case 'CHAR': case 'VARBINARY': case 'BINARY': if (isset($this) && $len <= $this->blobSize) return 'C'; return 'X'; case 'NCHAR': case 'NVARCHAR2': case 'NVARCHAR': if (isset($this) && $len <= $this->blobSize) return 'C2'; return 'X2'; case 'NCLOB': case 'CLOB': return 'XL'; case 'LONG RAW': case 'LONG VARBINARY': case 'BLOB': return 'B'; case 'DATE': return 'T'; case 'INT': case 'SMALLINT': case 'INTEGER': return 'I'; default: return 'N'; } } function ActualType($meta) { switch($meta) { case 'C': return 'VARCHAR'; case 'X': return $this->typeX; case 'XL': return $this->typeXL; case 'C2': return 'NVARCHAR2'; case 'X2': return 'NVARCHAR2(4000)'; case 'B': return 'BLOB'; case 'D': case 'T': return 'DATE'; case 'L': return 'DECIMAL(1)'; case 'I1': return 'DECIMAL(3)'; case 'I2': return 'DECIMAL(5)'; case 'I': case 'I4': return 'DECIMAL(10)'; case 'I8': return 'DECIMAL(20)'; case 'F': return 'DECIMAL'; case 'N': return 'DECIMAL'; default: return $meta; } } function CreateDatabase($dbname, $options=false) { $options = $this->_Options($options); $password = isset($options['PASSWORD']) ? $options['PASSWORD'] : 'tiger'; $tablespace = isset($options["TABLESPACE"]) ? " DEFAULT TABLESPACE ".$options["TABLESPACE"] : ''; $sql[] = "CREATE USER ".$dbname." IDENTIFIED BY ".$password.$tablespace; $sql[] = "GRANT CREATE SESSION, CREATE TABLE,UNLIMITED TABLESPACE,CREATE SEQUENCE TO $dbname"; return $sql; } function AddColumnSQL($tabname, $flds) { $f = array(); list($lines,$pkey) = $this->_GenFields($flds); $s = "ALTER TABLE $tabname ADD ("; foreach($lines as $v) { $f[] = "\n $v"; } $s .= implode(', ',$f).')'; $sql[] = $s; return $sql; } function AlterColumnSQL($tabname, $flds) { $f = array(); list($lines,$pkey) = $this->_GenFields($flds); $s = "ALTER TABLE $tabname MODIFY("; foreach($lines as $v) { $f[] = "\n $v"; } $s .= implode(', ',$f).')'; $sql[] = $s; return $sql; } function DropColumnSQL($tabname, $flds) { if (!is_array($flds)) $flds = explode(',',$flds); foreach ($flds as $k => $v) $flds[$k] = $this->NameQuote($v); $sql = array(); $s = "ALTER TABLE $tabname DROP("; $s .= implode(', ',$flds).') CASCADE CONSTRAINTS'; $sql[] = $s; return $sql; } function _DropAutoIncrement($t) { if (strpos($t,'.') !== false) { $tarr = explode('.',$t); return "drop sequence ".$tarr[0].".seq_".$tarr[1]; } return "drop sequence seq_".$t; } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if ($fdefault == "''" && $fnotnull) {// this is null in oracle $fnotnull = false; if ($this->debug) ADOConnection::outp("NOT NULL and DEFAULT='' illegal in Oracle"); } if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fautoinc) $this->seqField = $fname; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } /* CREATE or replace TRIGGER jaddress_insert before insert on jaddress for each row begin select seqaddress.nextval into :new.A_ID from dual; end; */ function _Triggers($tabname,$tableoptions) { if (!$this->seqField) return array(); if ($this->schema) { $t = strpos($tabname,'.'); if ($t !== false) $tab = substr($tabname,$t+1); else $tab = $tabname; $seqname = $this->schema.'.'.$this->seqPrefix.$tab; $trigname = $this->schema.'.'.$this->trigPrefix.$this->seqPrefix.$tab; } else { $seqname = $this->seqPrefix.$tabname; $trigname = $this->trigPrefix.$seqname; } if (isset($tableoptions['REPLACE'])) $sql[] = "DROP SEQUENCE $seqname"; $seqCache = ''; if (isset($tableoptions['SEQUENCE_CACHE'])){$seqCache = $tableoptions['SEQUENCE_CACHE'];} $seqIncr = ''; if (isset($tableoptions['SEQUENCE_INCREMENT'])){$seqIncr = ' INCREMENT BY '.$tableoptions['SEQUENCE_INCREMENT'];} $seqStart = ''; if (isset($tableoptions['SEQUENCE_START'])){$seqIncr = ' START WITH '.$tableoptions['SEQUENCE_START'];} $sql[] = "CREATE SEQUENCE $seqname $seqStart $seqIncr $seqCache"; $sql[] = "CREATE OR REPLACE TRIGGER $trigname BEFORE insert ON $tabname FOR EACH ROW WHEN (NEW.$this->seqField IS NULL OR NEW.$this->seqField = 0) BEGIN select $seqname.nextval into :new.$this->seqField from dual; END;"; $this->seqField = false; return $sql; } /* CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [select_statement] create_definition: col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [PRIMARY KEY] [reference_definition] or PRIMARY KEY (index_col_name,...) or KEY [index_name] (index_col_name,...) or INDEX [index_name] (index_col_name,...) or UNIQUE [INDEX] [index_name] (index_col_name,...) or FULLTEXT [INDEX] [index_name] (index_col_name,...) or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...) [reference_definition] or CHECK (expr) */ function _IndexSQL($idxname, $tabname, $flds,$idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } if (isset($idxoptions['BITMAP'])) { $unique = ' BITMAP'; } elseif (isset($idxoptions['UNIQUE'])) { $unique = ' UNIQUE'; } else { $unique = ''; } if ( is_array($flds) ) $flds = implode(', ',$flds); $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' (' . $flds . ')'; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; if (isset($idxoptions['oci8'])) $s .= $idxoptions['oci8']; $sql[] = $s; return $sql; } function GetCommentSQL($table,$col) { $table = $this->connection->qstr($table); $col = $this->connection->qstr($col); return "select comments from USER_COL_COMMENTS where TABLE_NAME=$table and COLUMN_NAME=$col"; } function SetCommentSQL($table,$col,$cmt) { $cmt = $this->connection->qstr($cmt); return "COMMENT ON COLUMN $table.$col IS $cmt"; } } ?>phpgacl-3.3.7/adodb/datadict/datadict-ibase.inc.php0100644025754300001440000000253210476657245021040 0ustar ipsousersdebug) ADOConnection::outp("AlterColumnSQL not supported"); return array(); } function DropColumnSQL($tabname, $flds) { if ($this->debug) ADOConnection::outp("DropColumnSQL not supported"); return array(); } } ?>phpgacl-3.3.7/adodb/datadict/datadict-mysql.inc.php0100644025754300001440000001107310476657245021122 0ustar ipsouserstype; $len = $fieldobj->max_length; } $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->auto_increment; $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { case 'STRING': case 'CHAR': case 'VARCHAR': case 'TINYBLOB': case 'TINYTEXT': case 'ENUM': case 'SET': if ($len <= $this->blobSize) return 'C'; case 'TEXT': case 'LONGTEXT': case 'MEDIUMTEXT': return 'X'; // php_mysql extension always returns 'blob' even if 'text' // so we have to check whether binary... case 'IMAGE': case 'LONGBLOB': case 'BLOB': case 'MEDIUMBLOB': return !empty($fieldobj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': return 'D'; case 'TIME': case 'DATETIME': case 'TIMESTAMP': return 'T'; case 'FLOAT': case 'DOUBLE': return 'F'; case 'INT': case 'INTEGER': return $is_serial ? 'R' : 'I'; case 'TINYINT': return $is_serial ? 'R' : 'I1'; case 'SMALLINT': return $is_serial ? 'R' : 'I2'; case 'MEDIUMINT': return $is_serial ? 'R' : 'I4'; case 'BIGINT': return $is_serial ? 'R' : 'I8'; default: return 'N'; } } function ActualType($meta) { switch(strtoupper($meta)) { case 'C': return 'VARCHAR'; case 'XL':return 'LONGTEXT'; case 'X': return 'TEXT'; case 'C2': return 'VARCHAR'; case 'X2': return 'LONGTEXT'; case 'B': return 'LONGBLOB'; case 'D': return 'DATE'; case 'T': return 'DATETIME'; case 'L': return 'TINYINT'; case 'R': case 'I4': case 'I': return 'INTEGER'; case 'I1': return 'TINYINT'; case 'I2': return 'SMALLINT'; case 'I8': return 'BIGINT'; case 'F': return 'DOUBLE'; case 'N': return 'NUMERIC'; default: return $meta; } } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) { $suffix = ''; if ($funsigned) $suffix .= ' UNSIGNED'; if ($fnotnull) $suffix .= ' NOT NULL'; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fautoinc) $suffix .= ' AUTO_INCREMENT'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } /* CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [select_statement] create_definition: col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [PRIMARY KEY] [reference_definition] or PRIMARY KEY (index_col_name,...) or KEY [index_name] (index_col_name,...) or INDEX [index_name] (index_col_name,...) or UNIQUE [INDEX] [index_name] (index_col_name,...) or FULLTEXT [INDEX] [index_name] (index_col_name,...) or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...) [reference_definition] or CHECK (expr) */ /* CREATE [UNIQUE|FULLTEXT] INDEX index_name ON tbl_name (col_name[(length)],... ) */ function _IndexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { if ($this->alterTableAddIndex) $sql[] = "ALTER TABLE $tabname DROP INDEX $idxname"; else $sql[] = sprintf($this->dropIndex, $idxname, $tabname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } if (isset($idxoptions['FULLTEXT'])) { $unique = ' FULLTEXT'; } elseif (isset($idxoptions['UNIQUE'])) { $unique = ' UNIQUE'; } else { $unique = ''; } if ( is_array($flds) ) $flds = implode(', ',$flds); if ($this->alterTableAddIndex) $s = "ALTER TABLE $tabname ADD $unique INDEX $idxname "; else $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname; $s .= ' (' . $flds . ')'; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; $sql[] = $s; return $sql; } } ?>phpgacl-3.3.7/adodb/lang/0040755025754300001440000000000010476665054014063 5ustar ipsousersphpgacl-3.3.7/adodb/lang/adodb-esperanto.inc.php0100644025754300001440000000356310270772002020377 0ustar ipsousers 'eo', DB_ERROR => 'nekonata eraro', DB_ERROR_ALREADY_EXISTS => 'jam ekzistas', DB_ERROR_CANNOT_CREATE => 'maleblas krei', DB_ERROR_CANNOT_DELETE => 'maleblas elimini', DB_ERROR_CANNOT_DROP => 'maleblas elimini (drop)', DB_ERROR_CONSTRAINT => 'rompo de kondicxoj de provo', DB_ERROR_DIVZERO => 'divido per 0 (nul)', DB_ERROR_INVALID => 'malregule', DB_ERROR_INVALID_DATE => 'malregula dato kaj tempo', DB_ERROR_INVALID_NUMBER => 'malregula nombro', DB_ERROR_MISMATCH => 'eraro', DB_ERROR_NODBSELECTED => 'datumbazo ne elektita', DB_ERROR_NOSUCHFIELD => 'ne ekzistas kampo', DB_ERROR_NOSUCHTABLE => 'ne ekzistas tabelo', DB_ERROR_NOT_CAPABLE => 'DBMS ne povas', DB_ERROR_NOT_FOUND => 'ne trovita', DB_ERROR_NOT_LOCKED => 'ne blokita', DB_ERROR_SYNTAX => 'sintaksa eraro', DB_ERROR_UNSUPPORTED => 'ne apogata', DB_ERROR_VALUE_COUNT_ON_ROW => 'nombrilo de valoroj en linio', DB_ERROR_INVALID_DSN => 'malregula DSN-o', DB_ERROR_CONNECT_FAILED => 'konekto malsukcesa', 0 => 'cxio bone', // DB_OK DB_ERROR_NEED_MORE_DATA => 'ne suficxe da datumo', DB_ERROR_EXTENSION_NOT_FOUND=> 'etendo ne trovita', DB_ERROR_NOSUCHDB => 'datumbazo ne ekzistas', DB_ERROR_ACCESS_VIOLATION => 'ne suficxe da rajto por atingo' ); ?>phpgacl-3.3.7/adodb/lang/adodb-pt-br.inc.php0100644025754300001440000000361710074023170017420 0ustar ipsousers 'pt-br', DB_ERROR => 'erro desconhecido', DB_ERROR_ALREADY_EXISTS => 'j existe', DB_ERROR_CANNOT_CREATE => 'impossvel criar', DB_ERROR_CANNOT_DELETE => 'impossvel exclur', DB_ERROR_CANNOT_DROP => 'impossvel remover', DB_ERROR_CONSTRAINT => 'violao do confinamente', DB_ERROR_DIVZERO => 'diviso por zero', DB_ERROR_INVALID => 'invlido', DB_ERROR_INVALID_DATE => 'data ou hora invlida', DB_ERROR_INVALID_NUMBER => 'nmero invlido', DB_ERROR_MISMATCH => 'erro', DB_ERROR_NODBSELECTED => 'nenhum banco de dados selecionado', DB_ERROR_NOSUCHFIELD => 'campo invlido', DB_ERROR_NOSUCHTABLE => 'tabela inexistente', DB_ERROR_NOT_CAPABLE => 'capacidade invlida para este BD', DB_ERROR_NOT_FOUND => 'no encontrado', DB_ERROR_NOT_LOCKED => 'no bloqueado', DB_ERROR_SYNTAX => 'erro de sintaxe', DB_ERROR_UNSUPPORTED => 'no suportado', DB_ERROR_VALUE_COUNT_ON_ROW => 'a quantidade de colunas no corresponde ao de valores', DB_ERROR_INVALID_DSN => 'DSN invlido', DB_ERROR_CONNECT_FAILED => 'falha na conexo', 0 => 'sem erro', // DB_OK DB_ERROR_NEED_MORE_DATA => 'dados insuficientes', DB_ERROR_EXTENSION_NOT_FOUND=> 'extenso no encontrada', DB_ERROR_NOSUCHDB => 'banco de dados no encontrado', DB_ERROR_ACCESS_VIOLATION => 'permisso insuficiente' ); ?> phpgacl-3.3.7/adodb/lang/adodb-pl.inc.php0100644025754300001440000000356110074023170017005 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'pl', DB_ERROR => 'niezidentyfikowany bd', DB_ERROR_ALREADY_EXISTS => 'ju istniej', DB_ERROR_CANNOT_CREATE => 'nie mona stworzy', DB_ERROR_CANNOT_DELETE => 'nie mona usun', DB_ERROR_CANNOT_DROP => 'nie mona porzuci', DB_ERROR_CONSTRAINT => 'pogwacenie uprawnie', DB_ERROR_DIVZERO => 'dzielenie przez zero', DB_ERROR_INVALID => 'bdny', DB_ERROR_INVALID_DATE => 'bdna godzina lub data', DB_ERROR_INVALID_NUMBER => 'bdny numer', DB_ERROR_MISMATCH => 'niedopasowanie', DB_ERROR_NODBSELECTED => 'baza danych nie zostaa wybrana', DB_ERROR_NOSUCHFIELD => 'nie znaleziono pola', DB_ERROR_NOSUCHTABLE => 'nie znaleziono tabeli', DB_ERROR_NOT_CAPABLE => 'nie zdolny', DB_ERROR_NOT_FOUND => 'nie znaleziono', DB_ERROR_NOT_LOCKED => 'nie zakmnity', DB_ERROR_SYNTAX => 'bd skadni', DB_ERROR_UNSUPPORTED => 'nie obsuguje', DB_ERROR_VALUE_COUNT_ON_ROW => 'warto liczona w szeregu', DB_ERROR_INVALID_DSN => 'bdny DSN', DB_ERROR_CONNECT_FAILED => 'poczenie nie zostao zrealizowane', 0 => 'brak bdw', // DB_OK DB_ERROR_NEED_MORE_DATA => 'niedostateczna ilo informacji', DB_ERROR_EXTENSION_NOT_FOUND=> 'nie znaleziono rozszerzenia', DB_ERROR_NOSUCHDB => 'nie znaleziono bazy', DB_ERROR_ACCESS_VIOLATION => 'niedostateczne uprawnienia' ); ?> phpgacl-3.3.7/adodb/lang/adodb-it.inc.php0100644025754300001440000000371110074023170017003 0ustar ipsousers 'it', DB_ERROR => 'errore sconosciuto', DB_ERROR_ALREADY_EXISTS => 'esiste già', DB_ERROR_CANNOT_CREATE => 'non posso creare', DB_ERROR_CANNOT_DELETE => 'non posso cancellare', DB_ERROR_CANNOT_DROP => 'non posso eliminare', DB_ERROR_CONSTRAINT => 'violazione constraint', DB_ERROR_DIVZERO => 'divisione per zero', DB_ERROR_INVALID => 'non valido', DB_ERROR_INVALID_DATE => 'data od ora non valida', DB_ERROR_INVALID_NUMBER => 'numero non valido', DB_ERROR_MISMATCH => 'diversi', DB_ERROR_NODBSELECTED => 'nessun database selezionato', DB_ERROR_NOSUCHFIELD => 'nessun campo trovato', DB_ERROR_NOSUCHTABLE => 'nessuna tabella trovata', DB_ERROR_NOT_CAPABLE => 'DB backend non abilitato', DB_ERROR_NOT_FOUND => 'non trovato', DB_ERROR_NOT_LOCKED => 'non bloccato', DB_ERROR_SYNTAX => 'errore di sintassi', DB_ERROR_UNSUPPORTED => 'non supportato', DB_ERROR_VALUE_COUNT_ON_ROW => 'valore inserito troppo grande per una colonna', DB_ERROR_INVALID_DSN => 'DSN non valido', DB_ERROR_CONNECT_FAILED => 'connessione fallita', 0 => 'nessun errore', // DB_OK DB_ERROR_NEED_MORE_DATA => 'dati inseriti insufficienti', DB_ERROR_EXTENSION_NOT_FOUND=> 'estensione non trovata', DB_ERROR_NOSUCHDB => 'database non trovato', DB_ERROR_ACCESS_VIOLATION => 'permessi insufficienti' ); ?>phpgacl-3.3.7/adodb/lang/adodb-hu.inc.php0100644025754300001440000000363110152170437017012 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'hu', DB_ERROR => 'ismeretlen hiba', DB_ERROR_ALREADY_EXISTS => 'mr ltezik', DB_ERROR_CANNOT_CREATE => 'nem sikerlt ltrehozni', DB_ERROR_CANNOT_DELETE => 'nem sikerlt trlni', DB_ERROR_CANNOT_DROP => 'nem sikerlt eldobni', DB_ERROR_CONSTRAINT => 'szablyok megszegse', DB_ERROR_DIVZERO => 'oszts nullval', DB_ERROR_INVALID => 'rvnytelen', DB_ERROR_INVALID_DATE => 'rvnytelen dtum vagy id', DB_ERROR_INVALID_NUMBER => 'rvnytelen szm', DB_ERROR_MISMATCH => 'nem megfelel', DB_ERROR_NODBSELECTED => 'nincs kivlasztott adatbzis', DB_ERROR_NOSUCHFIELD => 'nincs ilyen mez', DB_ERROR_NOSUCHTABLE => 'nincs ilyen tbla', DB_ERROR_NOT_CAPABLE => 'DB backend nem tmogatja', DB_ERROR_NOT_FOUND => 'nem tallhat', DB_ERROR_NOT_LOCKED => 'nincs lezrva', DB_ERROR_SYNTAX => 'szintaktikai hiba', DB_ERROR_UNSUPPORTED => 'nem tmogatott', DB_ERROR_VALUE_COUNT_ON_ROW => 'soron vgzett rtk szmlls', DB_ERROR_INVALID_DSN => 'hibs DSN', DB_ERROR_CONNECT_FAILED => 'sikertelen csatlakozs', 0 => 'nincs hiba', // DB_OK DB_ERROR_NEED_MORE_DATA => 'tl kevs az adat', DB_ERROR_EXTENSION_NOT_FOUND=> 'bvtmny nem tallhat', DB_ERROR_NOSUCHDB => 'nincs ilyen adatbzis', DB_ERROR_ACCESS_VIOLATION => 'nincs jogosultsg' ); ?>phpgacl-3.3.7/adodb/lang/adodb-cz.inc.php0100644025754300001440000000401410074023170017000 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'cz', DB_ERROR => 'neznm chyba', DB_ERROR_ALREADY_EXISTS => 'ji? existuje', DB_ERROR_CANNOT_CREATE => 'nelze vytvo?it', DB_ERROR_CANNOT_DELETE => 'nelze smazat', DB_ERROR_CANNOT_DROP => 'nelze odstranit', DB_ERROR_CONSTRAINT => 'poru?en omezujc podmnky', DB_ERROR_DIVZERO => 'd?len nulou', DB_ERROR_INVALID => 'neplatn', DB_ERROR_INVALID_DATE => 'neplatn datum nebo ?as', DB_ERROR_INVALID_NUMBER => 'neplatn ?slo', DB_ERROR_MISMATCH => 'nesouhlas', DB_ERROR_NODBSELECTED => '?dn databze nen vybrna', DB_ERROR_NOSUCHFIELD => 'pole nenalezeno', DB_ERROR_NOSUCHTABLE => 'tabulka nenalezena', DB_ERROR_NOT_CAPABLE => 'nepodporovno', DB_ERROR_NOT_FOUND => 'nenalezeno', DB_ERROR_NOT_LOCKED => 'nezam?eno', DB_ERROR_SYNTAX => 'syntaktick chyba', DB_ERROR_UNSUPPORTED => 'nepodporovno', DB_ERROR_VALUE_COUNT_ON_ROW => '', DB_ERROR_INVALID_DSN => 'neplatn DSN', DB_ERROR_CONNECT_FAILED => 'p?ipojen selhalo', 0 => 'bez chyb', // DB_OK DB_ERROR_NEED_MORE_DATA => 'mlo zdrojovch dat', DB_ERROR_EXTENSION_NOT_FOUND=> 'roz??en nenalezeno', DB_ERROR_NOSUCHDB => 'databze neexistuje', DB_ERROR_ACCESS_VIOLATION => 'nedostate?n prva' ); ?>phpgacl-3.3.7/adodb/lang/adodb-ro.inc.php0100644025754300001440000000354710074023170017016 0ustar ipsousers */ $ADODB_LANG_ARRAY = array ( 'LANG' => 'ro', DB_ERROR => 'eroare necunoscuta', DB_ERROR_ALREADY_EXISTS => 'deja exista', DB_ERROR_CANNOT_CREATE => 'nu se poate creea', DB_ERROR_CANNOT_DELETE => 'nu se poate sterge', DB_ERROR_CANNOT_DROP => 'nu se poate executa drop', DB_ERROR_CONSTRAINT => 'violare de constrain', DB_ERROR_DIVZERO => 'se divide la zero', DB_ERROR_INVALID => 'invalid', DB_ERROR_INVALID_DATE => 'data sau timp invalide', DB_ERROR_INVALID_NUMBER => 'numar invalid', DB_ERROR_MISMATCH => 'nepotrivire-mismatch', DB_ERROR_NODBSELECTED => 'nu exista baza de date selectata', DB_ERROR_NOSUCHFIELD => 'camp inexistent', DB_ERROR_NOSUCHTABLE => 'tabela inexistenta', DB_ERROR_NOT_CAPABLE => 'functie optionala neinstalata', DB_ERROR_NOT_FOUND => 'negasit', DB_ERROR_NOT_LOCKED => 'neblocat', DB_ERROR_SYNTAX => 'eroare de sintaxa', DB_ERROR_UNSUPPORTED => 'nu e suportat', DB_ERROR_VALUE_COUNT_ON_ROW => 'valoare prea mare pentru coloana', DB_ERROR_INVALID_DSN => 'DSN invalid', DB_ERROR_CONNECT_FAILED => 'conectare esuata', 0 => 'fara eroare', // DB_OK DB_ERROR_NEED_MORE_DATA => 'data introduse insuficiente', DB_ERROR_EXTENSION_NOT_FOUND=> 'extensie negasita', DB_ERROR_NOSUCHDB => 'nu exista baza de date', DB_ERROR_ACCESS_VIOLATION => 'permisiuni insuficiente' ); ?> phpgacl-3.3.7/adodb/lang/adodb-ca.inc.php0100644025754300001440000000371110074023170016752 0ustar ipsousers 'ca', DB_ERROR => 'error desconegut', DB_ERROR_ALREADY_EXISTS => 'ja existeix', DB_ERROR_CANNOT_CREATE => 'no es pot crear', DB_ERROR_CANNOT_DELETE => 'no es pot esborrar', DB_ERROR_CANNOT_DROP => 'no es pot eliminar', DB_ERROR_CONSTRAINT => 'violaci de constraint', DB_ERROR_DIVZERO => 'divisi per zero', DB_ERROR_INVALID => 'no s vlid', DB_ERROR_INVALID_DATE => 'la data o l\'hora no sn vlides', DB_ERROR_INVALID_NUMBER => 'el nombre no s vlid', DB_ERROR_MISMATCH => 'no hi ha coincidncia', DB_ERROR_NODBSELECTED => 'cap base de dades seleccionada', DB_ERROR_NOSUCHFIELD => 'camp inexistent', DB_ERROR_NOSUCHTABLE => 'taula inexistent', DB_ERROR_NOT_CAPABLE => 'l\'execuci secundria de DB no pot', DB_ERROR_NOT_FOUND => 'no trobat', DB_ERROR_NOT_LOCKED => 'no blocat', DB_ERROR_SYNTAX => 'error de sintaxi', DB_ERROR_UNSUPPORTED => 'no suportat', DB_ERROR_VALUE_COUNT_ON_ROW => 'el nombre de columnes no coincideix amb el nombre de valors en la fila', DB_ERROR_INVALID_DSN => 'el DSN no s vlid', DB_ERROR_CONNECT_FAILED => 'connexi fallida', 0 => 'cap error', // DB_OK DB_ERROR_NEED_MORE_DATA => 'les dades subministrades sn insuficients', DB_ERROR_EXTENSION_NOT_FOUND=> 'extensi no trobada', DB_ERROR_NOSUCHDB => 'base de dades inexistent', DB_ERROR_ACCESS_VIOLATION => 'permisos insuficients' ); ?> phpgacl-3.3.7/adodb/lang/adodb-da.inc.php0100644025754300001440000000362710270772002016764 0ustar ipsousers 'da', DB_ERROR => 'ukendt fejl', DB_ERROR_ALREADY_EXISTS => 'eksisterer allerede', DB_ERROR_CANNOT_CREATE => 'kan ikke oprette', DB_ERROR_CANNOT_DELETE => 'kan ikke slette', DB_ERROR_CANNOT_DROP => 'kan ikke droppe', DB_ERROR_CONSTRAINT => 'begrænsning krænket', DB_ERROR_DIVZERO => 'division med nul', DB_ERROR_INVALID => 'ugyldig', DB_ERROR_INVALID_DATE => 'ugyldig dato eller klokkeslet', DB_ERROR_INVALID_NUMBER => 'ugyldigt tal', DB_ERROR_MISMATCH => 'mismatch', DB_ERROR_NODBSELECTED => 'ingen database valgt', DB_ERROR_NOSUCHFIELD => 'felt findes ikke', DB_ERROR_NOSUCHTABLE => 'tabel findes ikke', DB_ERROR_NOT_CAPABLE => 'DB backend opgav', DB_ERROR_NOT_FOUND => 'ikke fundet', DB_ERROR_NOT_LOCKED => 'ikke låst', DB_ERROR_SYNTAX => 'syntaksfejl', DB_ERROR_UNSUPPORTED => 'ikke understøttet', DB_ERROR_VALUE_COUNT_ON_ROW => 'resulterende antal felter svarer ikke til forespørgslens antal felter', DB_ERROR_INVALID_DSN => 'ugyldig DSN', DB_ERROR_CONNECT_FAILED => 'tilslutning mislykkedes', 0 => 'ingen fejl', // DB_OK DB_ERROR_NEED_MORE_DATA => 'utilstrækkelige data angivet', DB_ERROR_EXTENSION_NOT_FOUND=> 'udvidelse ikke fundet', DB_ERROR_NOSUCHDB => 'database ikke fundet', DB_ERROR_ACCESS_VIOLATION => 'utilstrækkelige rettigheder' ); ?>phpgacl-3.3.7/adodb/lang/adodb-uk1251.inc.php0100644025754300001440000000355710270772002017332 0ustar ipsousers 'uk1251', DB_ERROR => ' ', DB_ERROR_ALREADY_EXISTS => ' ', DB_ERROR_CANNOT_CREATE => ' ', DB_ERROR_CANNOT_DELETE => ' ', DB_ERROR_CANNOT_DROP => ' (drop)', DB_ERROR_CONSTRAINT => ' ', DB_ERROR_DIVZERO => ' 0', DB_ERROR_INVALID => '', DB_ERROR_INVALID_DATE => ' ', DB_ERROR_INVALID_NUMBER => ' ', DB_ERROR_MISMATCH => '', DB_ERROR_NODBSELECTED => ' ', DB_ERROR_NOSUCHFIELD => ' ', DB_ERROR_NOSUCHTABLE => ' ', DB_ERROR_NOT_CAPABLE => ' ', DB_ERROR_NOT_FOUND => ' ', DB_ERROR_NOT_LOCKED => ' ', DB_ERROR_SYNTAX => ' ', DB_ERROR_UNSUPPORTED => ' ', DB_ERROR_VALUE_COUNT_ON_ROW => ' ', DB_ERROR_INVALID_DSN => ' DSN', DB_ERROR_CONNECT_FAILED => '\' ', 0 => ' ', // DB_OK DB_ERROR_NEED_MORE_DATA => ' ', DB_ERROR_EXTENSION_NOT_FOUND=> ' ', DB_ERROR_NOSUCHDB => ' ', DB_ERROR_ACCESS_VIOLATION => ' ' ); ?> phpgacl-3.3.7/adodb/lang/adodb-de.inc.php0100644025754300001440000000403010074023170016752 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'de', DB_ERROR => 'Unbekannter Fehler', DB_ERROR_ALREADY_EXISTS => 'existiert bereits', DB_ERROR_CANNOT_CREATE => 'kann nicht erstellen', DB_ERROR_CANNOT_DELETE => 'kann nicht löschen', DB_ERROR_CANNOT_DROP => 'Tabelle oder Index konnte nicht gelöscht werden', DB_ERROR_CONSTRAINT => 'Constraint Verletzung', DB_ERROR_DIVZERO => 'Division durch Null', DB_ERROR_INVALID => 'ung¨ltig', DB_ERROR_INVALID_DATE => 'ung¨ltiges Datum oder Zeit', DB_ERROR_INVALID_NUMBER => 'ung¨ltige Zahl', DB_ERROR_MISMATCH => 'Unverträglichkeit', DB_ERROR_NODBSELECTED => 'keine Dantebank ausgewählt', DB_ERROR_NOSUCHFIELD => 'Feld nicht vorhanden', DB_ERROR_NOSUCHTABLE => 'Tabelle nicht vorhanden', DB_ERROR_NOT_CAPABLE => 'Funktion nicht installiert', DB_ERROR_NOT_FOUND => 'nicht gefunden', DB_ERROR_NOT_LOCKED => 'nicht gesperrt', DB_ERROR_SYNTAX => 'Syntaxfehler', DB_ERROR_UNSUPPORTED => 'nicht Unterst¨tzt', DB_ERROR_VALUE_COUNT_ON_ROW => 'Anzahl der zur¨ckgelieferten Felder entspricht nicht der Anzahl der Felder in der Abfrage', DB_ERROR_INVALID_DSN => 'ung¨ltiger DSN', DB_ERROR_CONNECT_FAILED => 'Verbindung konnte nicht hergestellt werden', 0 => 'kein Fehler', // DB_OK DB_ERROR_NEED_MORE_DATA => 'Nicht gen¨gend Daten geliefert', DB_ERROR_EXTENSION_NOT_FOUND=> 'erweiterung nicht gefunden', DB_ERROR_NOSUCHDB => 'keine Datenbank', DB_ERROR_ACCESS_VIOLATION => 'ungen¨gende Rechte' ); ?>phpgacl-3.3.7/adodb/lang/adodb-bg.inc.php0100644025754300001440000000376410045251652016775 0ustar ipsousers */ $ADODB_LANG_ARRAY = array ( 'LANG' => 'bg', DB_ERROR => ' ', DB_ERROR_ALREADY_EXISTS => ' ', DB_ERROR_CANNOT_CREATE => ' ', DB_ERROR_CANNOT_DELETE => ' ', DB_ERROR_CANNOT_DROP => ' ', DB_ERROR_CONSTRAINT => ' ', DB_ERROR_DIVZERO => ' ', DB_ERROR_INVALID => '', DB_ERROR_INVALID_DATE => ' ', DB_ERROR_INVALID_NUMBER => ' ', DB_ERROR_MISMATCH => ' ', DB_ERROR_NODBSELECTED => ' ', DB_ERROR_NOSUCHFIELD => ' ', DB_ERROR_NOSUCHTABLE => ' ', DB_ERROR_NOT_CAPABLE => 'DB backend not capable', DB_ERROR_NOT_FOUND => ' ', DB_ERROR_NOT_LOCKED => ' ', DB_ERROR_SYNTAX => ' ', DB_ERROR_UNSUPPORTED => ' ', DB_ERROR_VALUE_COUNT_ON_ROW => ' ', DB_ERROR_INVALID_DSN => ' DSN', DB_ERROR_CONNECT_FAILED => ' ', 0 => ' ', // DB_OK DB_ERROR_NEED_MORE_DATA => ' ', DB_ERROR_EXTENSION_NOT_FOUND=> ' ', DB_ERROR_NOSUCHDB => ' ', DB_ERROR_ACCESS_VIOLATION => ' ' ); ?> phpgacl-3.3.7/adodb/lang/adodb-sv.inc.php0100644025754300001440000000344410074023170017022 0ustar ipsousers 'en', DB_ERROR => 'Oknt fel', DB_ERROR_ALREADY_EXISTS => 'finns redan', DB_ERROR_CANNOT_CREATE => 'kan inte skapa', DB_ERROR_CANNOT_DELETE => 'kan inte ta bort', DB_ERROR_CANNOT_DROP => 'kan inte slppa', DB_ERROR_CONSTRAINT => 'begrnsning krnkt', DB_ERROR_DIVZERO => 'division med noll', DB_ERROR_INVALID => 'ogiltig', DB_ERROR_INVALID_DATE => 'ogiltigt datum eller tid', DB_ERROR_INVALID_NUMBER => 'ogiltigt tal', DB_ERROR_MISMATCH => 'felaktig matchning', DB_ERROR_NODBSELECTED => 'ingen databas vald', DB_ERROR_NOSUCHFIELD => 'inget sdant flt', DB_ERROR_NOSUCHTABLE => 'ingen sdan tabell', DB_ERROR_NOT_CAPABLE => 'DB backend klarar det inte', DB_ERROR_NOT_FOUND => 'finns inte', DB_ERROR_NOT_LOCKED => 'inte lst', DB_ERROR_SYNTAX => 'syntaxfel', DB_ERROR_UNSUPPORTED => 'stds ej', DB_ERROR_VALUE_COUNT_ON_ROW => 'vrde rknat p rad', DB_ERROR_INVALID_DSN => 'ogiltig DSN', DB_ERROR_CONNECT_FAILED => 'anslutning misslyckades', 0 => 'inget fel', // DB_OK DB_ERROR_NEED_MORE_DATA => 'otillrckligt med data angivet', DB_ERROR_EXTENSION_NOT_FOUND=> 'utkning hittades ej', DB_ERROR_NOSUCHDB => 'ingen sdan databas', DB_ERROR_ACCESS_VIOLATION => 'otillrckliga rttigheter' ); ?>phpgacl-3.3.7/adodb/lang/adodb-bgutf8.inc.php0100644025754300001440000000471310045251652017577 0ustar ipsousers */ $ADODB_LANG_ARRAY = array ( 'LANG' => 'bgutf8', DB_ERROR => 'неизвестна грешка', DB_ERROR_ALREADY_EXISTS => 'вече съществува', DB_ERROR_CANNOT_CREATE => 'не може да бъде създадена', DB_ERROR_CANNOT_DELETE => 'не може да бъде изтрита', DB_ERROR_CANNOT_DROP => 'не може да бъде унищожена', DB_ERROR_CONSTRAINT => 'нарушено условие', DB_ERROR_DIVZERO => 'деление на нула', DB_ERROR_INVALID => 'неправилно', DB_ERROR_INVALID_DATE => 'некоректна дата или час', DB_ERROR_INVALID_NUMBER => 'невалиден номер', DB_ERROR_MISMATCH => 'погрешна употреба', DB_ERROR_NODBSELECTED => 'не е избрана база данни', DB_ERROR_NOSUCHFIELD => 'несъществуващо поле', DB_ERROR_NOSUCHTABLE => 'несъществуваща таблица', DB_ERROR_NOT_CAPABLE => 'DB backend not capable', DB_ERROR_NOT_FOUND => 'не е намерена', DB_ERROR_NOT_LOCKED => 'не е заключена', DB_ERROR_SYNTAX => 'грешен синтаксис', DB_ERROR_UNSUPPORTED => 'не се поддържа', DB_ERROR_VALUE_COUNT_ON_ROW => 'некоректен брой колони в реда', DB_ERROR_INVALID_DSN => 'невалиден DSN', DB_ERROR_CONNECT_FAILED => 'връзката не може да бъде осъществена', 0 => 'няма грешки', // DB_OK DB_ERROR_NEED_MORE_DATA => 'предоставените данни са недостатъчни', DB_ERROR_EXTENSION_NOT_FOUND=> 'разширението не е намерено', DB_ERROR_NOSUCHDB => 'несъществуваща база данни', DB_ERROR_ACCESS_VIOLATION => 'нямате достатъчно права' ); ?> phpgacl-3.3.7/adodb/lang/adodb-ru1251.inc.php0100644025754300001440000000364210074023170017331 0ustar ipsousers 'ru1251', DB_ERROR => ' ', DB_ERROR_ALREADY_EXISTS => ' ', DB_ERROR_CANNOT_CREATE => ' ', DB_ERROR_CANNOT_DELETE => ' ', DB_ERROR_CANNOT_DROP => ' (drop)', DB_ERROR_CONSTRAINT => ' ', DB_ERROR_DIVZERO => ' 0', DB_ERROR_INVALID => '', DB_ERROR_INVALID_DATE => ' ', DB_ERROR_INVALID_NUMBER => ' ', DB_ERROR_MISMATCH => '', DB_ERROR_NODBSELECTED => ' ', DB_ERROR_NOSUCHFIELD => ' ', DB_ERROR_NOSUCHTABLE => ' ', DB_ERROR_NOT_CAPABLE => ' ', DB_ERROR_NOT_FOUND => ' ', DB_ERROR_NOT_LOCKED => ' ', DB_ERROR_SYNTAX => ' ', DB_ERROR_UNSUPPORTED => ' ', DB_ERROR_VALUE_COUNT_ON_ROW => ' ', DB_ERROR_INVALID_DSN => ' DSN', DB_ERROR_CONNECT_FAILED => ' ', 0 => ' ', // DB_OK DB_ERROR_NEED_MORE_DATA => ' ', DB_ERROR_EXTENSION_NOT_FOUND=> ' ', DB_ERROR_NOSUCHDB => ' ', DB_ERROR_ACCESS_VIOLATION => ' ' ); ?>phpgacl-3.3.7/adodb/lang/adodb-cn.inc.php0100644025754300001440000000334210074023170016767 0ustar ipsousers 'cn', DB_ERROR => 'δ֪', DB_ERROR_ALREADY_EXISTS => 'Ѿ', DB_ERROR_CANNOT_CREATE => 'ܴ', DB_ERROR_CANNOT_DELETE => 'ɾ', DB_ERROR_CANNOT_DROP => 'ܶ', DB_ERROR_CONSTRAINT => 'Լ', DB_ERROR_DIVZERO => '0', DB_ERROR_INVALID => 'Ч', DB_ERROR_INVALID_DATE => 'Чڻʱ', DB_ERROR_INVALID_NUMBER => 'Ч', DB_ERROR_MISMATCH => 'ƥ', DB_ERROR_NODBSELECTED => 'ûݿⱻѡ', DB_ERROR_NOSUCHFIELD => 'ûӦֶ', DB_ERROR_NOSUCHTABLE => 'ûӦı', DB_ERROR_NOT_CAPABLE => 'ݿ̨', DB_ERROR_NOT_FOUND => 'ûз', DB_ERROR_NOT_LOCKED => 'ûб', DB_ERROR_SYNTAX => '﷨', DB_ERROR_UNSUPPORTED => '֧', DB_ERROR_VALUE_COUNT_ON_ROW => 'ۼֵ', DB_ERROR_INVALID_DSN => 'ЧԴ (DSN)', DB_ERROR_CONNECT_FAILED => 'ʧ', 0 => 'ûд', // DB_OK DB_ERROR_NEED_MORE_DATA => 'ṩݲܷҪ', DB_ERROR_EXTENSION_NOT_FOUND=> 'չûб', DB_ERROR_NOSUCHDB => 'ûӦݿ', DB_ERROR_ACCESS_VIOLATION => 'ûкʵȨ' ); ?>phpgacl-3.3.7/adodb/lang/adodb-en.inc.php0100644025754300001440000000333510074023170016773 0ustar ipsousers 'en', DB_ERROR => 'unknown error', DB_ERROR_ALREADY_EXISTS => 'already exists', DB_ERROR_CANNOT_CREATE => 'can not create', DB_ERROR_CANNOT_DELETE => 'can not delete', DB_ERROR_CANNOT_DROP => 'can not drop', DB_ERROR_CONSTRAINT => 'constraint violation', DB_ERROR_DIVZERO => 'division by zero', DB_ERROR_INVALID => 'invalid', DB_ERROR_INVALID_DATE => 'invalid date or time', DB_ERROR_INVALID_NUMBER => 'invalid number', DB_ERROR_MISMATCH => 'mismatch', DB_ERROR_NODBSELECTED => 'no database selected', DB_ERROR_NOSUCHFIELD => 'no such field', DB_ERROR_NOSUCHTABLE => 'no such table', DB_ERROR_NOT_CAPABLE => 'DB backend not capable', DB_ERROR_NOT_FOUND => 'not found', DB_ERROR_NOT_LOCKED => 'not locked', DB_ERROR_SYNTAX => 'syntax error', DB_ERROR_UNSUPPORTED => 'not supported', DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', DB_ERROR_INVALID_DSN => 'invalid DSN', DB_ERROR_CONNECT_FAILED => 'connect failed', 0 => 'no error', // DB_OK DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', DB_ERROR_NOSUCHDB => 'no such database', DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions' ); ?> phpgacl-3.3.7/adodb/lang/adodb-ar.inc.php0100644025754300001440000000274310476657245017023 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'ar', DB_ERROR => ' ', DB_ERROR_ALREADY_EXISTS => ' ', DB_ERROR_CANNOT_CREATE => ' ', DB_ERROR_CANNOT_DELETE => ' ', DB_ERROR_CANNOT_DROP => ' ', DB_ERROR_CONSTRAINT => ' ', DB_ERROR_DIVZERO => ' ', DB_ERROR_INVALID => ' ', DB_ERROR_INVALID_DATE => ' ', DB_ERROR_INVALID_NUMBER => ' ', DB_ERROR_MISMATCH => ' ', DB_ERROR_NODBSELECTED => ' ', DB_ERROR_NOSUCHFIELD => ' ', DB_ERROR_NOSUCHTABLE => ' ', DB_ERROR_NOT_CAPABLE => ' ', DB_ERROR_NOT_FOUND => ' ', DB_ERROR_NOT_LOCKED => ' ', DB_ERROR_SYNTAX => ' ', DB_ERROR_UNSUPPORTED => ' ', DB_ERROR_VALUE_COUNT_ON_ROW => ' ', DB_ERROR_INVALID_DSN => 'DSN ', DB_ERROR_CONNECT_FAILED => ' ', 0 => ' ', // DB_OK DB_ERROR_NEED_MORE_DATA => ' ', DB_ERROR_EXTENSION_NOT_FOUND=> ' ', DB_ERROR_NOSUCHDB => ' ', DB_ERROR_ACCESS_VIOLATION => ' ' ); ?> phpgacl-3.3.7/adodb/lang/adodb-fr.inc.php0100644025754300001440000000326710074023170017004 0ustar ipsousers 'fr', DB_ERROR => 'erreur inconnue', DB_ERROR_ALREADY_EXISTS => 'existe déjà', DB_ERROR_CANNOT_CREATE => 'crétion impossible', DB_ERROR_CANNOT_DELETE => 'effacement impossible', DB_ERROR_CANNOT_DROP => 'suppression impossible', DB_ERROR_CONSTRAINT => 'violation de contrainte', DB_ERROR_DIVZERO => 'division par zéro', DB_ERROR_INVALID => 'invalide', DB_ERROR_INVALID_DATE => 'date ou heure invalide', DB_ERROR_INVALID_NUMBER => 'nombre invalide', DB_ERROR_MISMATCH => 'erreur de concordance', DB_ERROR_NODBSELECTED => 'pas de base de donnéessélectionnée', DB_ERROR_NOSUCHFIELD => 'nom de colonne invalide', DB_ERROR_NOSUCHTABLE => 'table ou vue inexistante', DB_ERROR_NOT_CAPABLE => 'fonction optionnelle non installée', DB_ERROR_NOT_FOUND => 'pas trouvé', DB_ERROR_NOT_LOCKED => 'non verrouillé', DB_ERROR_SYNTAX => 'erreur de syntaxe', DB_ERROR_UNSUPPORTED => 'non supporté', DB_ERROR_VALUE_COUNT_ON_ROW => 'valeur insérée trop grande pour colonne', DB_ERROR_INVALID_DSN => 'DSN invalide', DB_ERROR_CONNECT_FAILED => 'échec à la connexion', 0 => "pas d'erreur", // DB_OK DB_ERROR_NEED_MORE_DATA => 'données fournies insuffisantes', DB_ERROR_EXTENSION_NOT_FOUND=> 'extension non trouvée', DB_ERROR_NOSUCHDB => 'base de données inconnue', DB_ERROR_ACCESS_VIOLATION => 'droits insuffisants' ); ?>phpgacl-3.3.7/adodb/lang/adodb-es.inc.php0100644025754300001440000000360110270772002016777 0ustar ipsousers $ADODB_LANG_ARRAY = array ( 'LANG' => 'es', DB_ERROR => 'error desconocido', DB_ERROR_ALREADY_EXISTS => 'ya existe', DB_ERROR_CANNOT_CREATE => 'imposible crear', DB_ERROR_CANNOT_DELETE => 'imposible borrar', DB_ERROR_CANNOT_DROP => 'imposible hacer drop', DB_ERROR_CONSTRAINT => 'violacion de constraint', DB_ERROR_DIVZERO => 'division por cero', DB_ERROR_INVALID => 'invalido', DB_ERROR_INVALID_DATE => 'fecha u hora invalida', DB_ERROR_INVALID_NUMBER => 'numero invalido', DB_ERROR_MISMATCH => 'error', DB_ERROR_NODBSELECTED => 'no hay base de datos seleccionada', DB_ERROR_NOSUCHFIELD => 'campo invalido', DB_ERROR_NOSUCHTABLE => 'tabla no existe', DB_ERROR_NOT_CAPABLE => 'capacidad invalida para esta DB', DB_ERROR_NOT_FOUND => 'no encontrado', DB_ERROR_NOT_LOCKED => 'no bloqueado', DB_ERROR_SYNTAX => 'error de sintaxis', DB_ERROR_UNSUPPORTED => 'no soportado', DB_ERROR_VALUE_COUNT_ON_ROW => 'la cantidad de columnas no corresponden a la cantidad de valores', DB_ERROR_INVALID_DSN => 'DSN invalido', DB_ERROR_CONNECT_FAILED => 'fallo la conexion', 0 => 'sin error', // DB_OK DB_ERROR_NEED_MORE_DATA => 'insuficientes datos', DB_ERROR_EXTENSION_NOT_FOUND=> 'extension no encontrada', DB_ERROR_NOSUCHDB => 'base de datos no encontrada', DB_ERROR_ACCESS_VIOLATION => 'permisos insuficientes' ); ?>phpgacl-3.3.7/adodb/lang/adodb-nl.inc.php0100644025754300001440000000354610074023170017006 0ustar ipsousers 'nl', DB_ERROR => 'onbekende fout', DB_ERROR_ALREADY_EXISTS => 'bestaat al', DB_ERROR_CANNOT_CREATE => 'kan niet aanmaken', DB_ERROR_CANNOT_DELETE => 'kan niet wissen', DB_ERROR_CANNOT_DROP => 'kan niet verwijderen', DB_ERROR_CONSTRAINT => 'constraint overtreding', DB_ERROR_DIVZERO => 'poging tot delen door nul', DB_ERROR_INVALID => 'ongeldig', DB_ERROR_INVALID_DATE => 'ongeldige datum of tijd', DB_ERROR_INVALID_NUMBER => 'ongeldig nummer', DB_ERROR_MISMATCH => 'is incorrect', DB_ERROR_NODBSELECTED => 'geen database geselecteerd', DB_ERROR_NOSUCHFIELD => 'onbekend veld', DB_ERROR_NOSUCHTABLE => 'onbekende tabel', DB_ERROR_NOT_CAPABLE => 'database systeem is niet tot uitvoer in staat', DB_ERROR_NOT_FOUND => 'niet gevonden', DB_ERROR_NOT_LOCKED => 'niet vergrendeld', DB_ERROR_SYNTAX => 'syntaxis fout', DB_ERROR_UNSUPPORTED => 'niet ondersteund', DB_ERROR_VALUE_COUNT_ON_ROW => 'waarde telling op rij', DB_ERROR_INVALID_DSN => 'ongeldige DSN', DB_ERROR_CONNECT_FAILED => 'connectie mislukt', 0 => 'geen fout', // DB_OK DB_ERROR_NEED_MORE_DATA => 'onvoldoende data gegeven', DB_ERROR_EXTENSION_NOT_FOUND=> 'extensie niet gevonden', DB_ERROR_NOSUCHDB => 'onbekende database', DB_ERROR_ACCESS_VIOLATION => 'onvoldoende rechten' ); ?>phpgacl-3.3.7/adodb/adodb-csvlib.inc.php0100644025754300001440000002016210476657245016755 0ustar ipsousersFieldCount() : 0; if ($sql) $sql = urlencode($sql); // metadata setup if ($max <= 0 || $rs->dataProvider == 'empty') { // is insert/update/delete if (is_object($conn)) { $sql .= ','.$conn->Affected_Rows(); $sql .= ','.$conn->Insert_ID(); } else $sql .= ',,'; $text = "====-1,0,$sql\n"; return $text; } $tt = ($rs->timeCreated) ? $rs->timeCreated : time(); ## changed format from ====0 to ====1 $line = "====1,$tt,$sql\n"; if ($rs->databaseType == 'array') { $rows =& $rs->_array; } else { $rows = array(); while (!$rs->EOF) { $rows[] = $rs->fields; $rs->MoveNext(); } } for($i=0; $i < $max; $i++) { $o =& $rs->FetchField($i); $flds[] = $o; } $savefetch = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode; $class = $rs->connection->arrayClass; $rs2 = new $class(); $rs2->sql = $rs->sql; $rs2->oldProvider = $rs->dataProvider; $rs2->InitArrayFields($rows,$flds); $rs2->fetchMode = $savefetch; return $line.serialize($rs2); } /** * Open CSV file and convert it into Data. * * @param url file/ftp/http url * @param err returns the error message * @param timeout dispose if recordset has been alive for $timeout secs * * @return recordset, or false if error occured. If no * error occurred in sql INSERT/UPDATE/DELETE, * empty recordset is returned */ function &csv2rs($url,&$err,$timeout=0, $rsclass='ADORecordSet_array') { $false = false; $err = false; $fp = @fopen($url,'rb'); if (!$fp) { $err = $url.' file/URL not found'; return $false; } @flock($fp, LOCK_SH); $arr = array(); $ttl = 0; if ($meta = fgetcsv($fp, 32000, ",")) { // check if error message if (strncmp($meta[0],'****',4) === 0) { $err = trim(substr($meta[0],4,1024)); fclose($fp); return $false; } // check for meta data // $meta[0] is -1 means return an empty recordset // $meta[1] contains a time if (strncmp($meta[0], '====',4) === 0) { if ($meta[0] == "====-1") { if (sizeof($meta) < 5) { $err = "Corrupt first line for format -1"; fclose($fp); return $false; } fclose($fp); if ($timeout > 0) { $err = " Illegal Timeout $timeout "; return $false; } $rs = new $rsclass($val=true); $rs->fields = array(); $rs->timeCreated = $meta[1]; $rs->EOF = true; $rs->_numOfFields = 0; $rs->sql = urldecode($meta[2]); $rs->affectedrows = (integer)$meta[3]; $rs->insertid = $meta[4]; return $rs; } # Under high volume loads, we want only 1 thread/process to _write_file # so that we don't have 50 processes queueing to write the same data. # We use probabilistic timeout, ahead of time. # # -4 sec before timeout, give processes 1/32 chance of timing out # -2 sec before timeout, give processes 1/16 chance of timing out # -1 sec after timeout give processes 1/4 chance of timing out # +0 sec after timeout, give processes 100% chance of timing out if (sizeof($meta) > 1) { if($timeout >0){ $tdiff = (integer)( $meta[1]+$timeout - time()); if ($tdiff <= 2) { switch($tdiff) { case 4: case 3: if ((rand() & 31) == 0) { fclose($fp); $err = "Timeout 3"; return $false; } break; case 2: if ((rand() & 15) == 0) { fclose($fp); $err = "Timeout 2"; return $false; } break; case 1: if ((rand() & 3) == 0) { fclose($fp); $err = "Timeout 1"; return $false; } break; default: fclose($fp); $err = "Timeout 0"; return $false; } // switch } // if check flush cache }// (timeout>0) $ttl = $meta[1]; } //================================================ // new cache format - use serialize extensively... if ($meta[0] === '====1') { // slurp in the data $MAXSIZE = 128000; $text = fread($fp,$MAXSIZE); if (strlen($text)) { while ($txt = fread($fp,$MAXSIZE)) { $text .= $txt; } } fclose($fp); $rs = unserialize($text); if (is_object($rs)) $rs->timeCreated = $ttl; else { $err = "Unable to unserialize recordset"; //echo htmlspecialchars($text),' !--END--!

'; } return $rs; } $meta = false; $meta = fgetcsv($fp, 32000, ","); if (!$meta) { fclose($fp); $err = "Unexpected EOF 1"; return $false; } } // Get Column definitions $flds = array(); foreach($meta as $o) { $o2 = explode(':',$o); if (sizeof($o2)!=3) { $arr[] = $meta; $flds = false; break; } $fld = new ADOFieldObject(); $fld->name = urldecode($o2[0]); $fld->type = $o2[1]; $fld->max_length = $o2[2]; $flds[] = $fld; } } else { fclose($fp); $err = "Recordset had unexpected EOF 2"; return $false; } // slurp in the data $MAXSIZE = 128000; $text = ''; while ($txt = fread($fp,$MAXSIZE)) { $text .= $txt; } fclose($fp); @$arr = unserialize($text); //var_dump($arr); if (!is_array($arr)) { $err = "Recordset had unexpected EOF (in serialized recordset)"; if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!"; return $false; } $rs = new $rsclass(); $rs->timeCreated = $ttl; $rs->InitArrayFields($arr,$flds); return $rs; } /** * Save a file $filename and its $contents (normally for caching) with file locking */ function adodb_write_file($filename, $contents,$debug=false) { # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows # So to simulate locking, we assume that rename is an atomic operation. # First we delete $filename, then we create a $tempfile write to it and # rename to the desired $filename. If the rename works, then we successfully # modified the file exclusively. # What a stupid need - having to simulate locking. # Risks: # 1. $tempfile name is not unique -- very very low # 2. unlink($filename) fails -- ok, rename will fail # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and cache updated if (strncmp(PHP_OS,'WIN',3) === 0) { // skip the decimal place $mtime = substr(str_replace(' ','_',microtime()),2); // getmypid() actually returns 0 on Win98 - never mind! $tmpname = $filename.uniqid($mtime).getmypid(); if (!($fd = @fopen($tmpname,'a'))) return false; $ok = ftruncate($fd,0); if (!fwrite($fd,$contents)) $ok = false; fclose($fd); chmod($tmpname,0644); // the tricky moment @unlink($filename); if (!@rename($tmpname,$filename)) { unlink($tmpname); $ok = false; } if (!$ok) { if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed')); } return $ok; } if (!($fd = @fopen($filename, 'a'))) return false; if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) { $ok = fwrite( $fd, $contents ); fclose($fd); chmod($filename,0644); }else { fclose($fd); if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename
\n"); $ok = false; } return $ok; } ?>phpgacl-3.3.7/adodb/adodb-php4.inc.php0100644025754300001440000000050110476657245016341 0ustar ipsousersphpgacl-3.3.7/adodb/toexport.inc.php0100644025754300001440000000641710476657245016317 0ustar ipsousersFieldTypesArray(); reset($fieldTypes); while(list(,$o) = each($fieldTypes)) { $v = $o->name; if ($escquote) $v = str_replace($quote,$escquotequote,$v); $v = strip_tags(str_replace("\n", $replaceNewLine, str_replace("\r\n",$replaceNewLine,str_replace($sep,$sepreplace,$v)))); $elements[] = $v; } $s .= implode($sep, $elements).$NEWLINE; } $hasNumIndex = isset($rs->fields[0]); $line = 0; $max = $rs->FieldCount(); while (!$rs->EOF) { $elements = array(); $i = 0; if ($hasNumIndex) { for ($j=0; $j < $max; $j++) { $v = $rs->fields[$j]; if (!is_object($v)) $v = trim($v); else $v = 'Object'; if ($escquote) $v = str_replace($quote,$escquotequote,$v); $v = strip_tags(str_replace("\n", $replaceNewLine, str_replace("\r\n",$replaceNewLine,str_replace($sep,$sepreplace,$v)))); if (strpos($v,$sep) !== false || strpos($v,$quote) !== false) $elements[] = "$quote$v$quote"; else $elements[] = $v; } } else { // ASSOCIATIVE ARRAY foreach($rs->fields as $v) { if ($escquote) $v = str_replace($quote,$escquotequote,trim($v)); $v = strip_tags(str_replace("\n", $replaceNewLine, str_replace("\r\n",$replaceNewLine,str_replace($sep,$sepreplace,$v)))); if (strpos($v,$sep) !== false || strpos($v,$quote) !== false) $elements[] = "$quote$v$quote"; else $elements[] = $v; } } $s .= implode($sep, $elements).$NEWLINE; $rs->MoveNext(); $line += 1; if ($fp && ($line % $BUFLINES) == 0) { if ($fp === true) echo $s; else fwrite($fp,$s); $s = ''; } } if ($fp) { if ($fp === true) echo $s; else fwrite($fp,$s); $s = ''; } return $s; } ?>phpgacl-3.3.7/adodb/session/0040755025754300001440000000000010476665054014625 5ustar ipsousersphpgacl-3.3.7/adodb/session/adodb-session-clob2.php0100644025754300001440000000105110476657431021061 0ustar ipsousersphpgacl-3.3.7/adodb/session/adodb-sessions.oracle.sql0100644025754300001440000000047110004632166021507 0ustar ipsousers-- $CVSHeader$ DROP TABLE adodb_sessions; CREATE TABLE sessions ( sesskey CHAR(32) DEFAULT '' NOT NULL, expiry INT DEFAULT 0 NOT NULL, expireref VARCHAR(64) DEFAULT '', data VARCHAR(4000) DEFAULT '', PRIMARY KEY (sesskey), INDEX expiry (expiry) ); CREATE INDEX ix_expiry ON sessions (expiry); QUIT; phpgacl-3.3.7/adodb/session/adodb-sessions.mysql.sql0100644025754300001440000000061410004632166021406 0ustar ipsousers-- $CVSHeader$ CREATE DATABASE /*! IF NOT EXISTS */ adodb_sessions; USE adodb_sessions; DROP TABLE /*! IF EXISTS */ sessions; CREATE TABLE /*! IF NOT EXISTS */ sessions ( sesskey CHAR(32) /*! BINARY */ NOT NULL DEFAULT '', expiry INT(11) /*! UNSIGNED */ NOT NULL DEFAULT 0, expireref VARCHAR(64) DEFAULT '', data LONGTEXT DEFAULT '', PRIMARY KEY (sesskey), INDEX expiry (expiry) ); phpgacl-3.3.7/adodb/session/adodb-cryptsession2.php0100644025754300001440000000116610476657431021235 0ustar ipsousersphpgacl-3.3.7/adodb/session/adodb-session.php0100644025754300001440000005136710476657245020104 0ustar ipsousersExecute('UPDATE '. ADODB_Session::table(). ' SET sesskey='. $conn->qstr($new_id). ' WHERE sesskey='.$conn->qstr($old_id)); /* it is possible that the update statement fails due to a collision */ if (!$ok) { session_id($old_id); if (empty($ck)) $ck = session_get_cookie_params(); setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); return false; } return true; } /* Generate database table for session data @see http://phplens.com/lens/lensforum/msgs.php?id=12280 @return 0 if failure, 1 if errors, 2 if successful. @author Markus Staab http://www.public-4u.de */ function adodb_session_create_table($schemaFile=null,$conn = null) { // set default values if ($schemaFile===null) $schemaFile = ADODB_SESSION . '/session_schema.xml'; if ($conn===null) $conn =& ADODB_Session::_conn(); if (!$conn) return 0; $schema = new adoSchema($conn); $schema->ParseSchema($schemaFile); return $schema->ExecuteSchema(); } /*! \static */ class ADODB_Session { ///////////////////// // getter/setter methods ///////////////////// /* function Lock($lock=null) { static $_lock = false; if (!is_null($lock)) $_lock = $lock; return $lock; } */ /*! */ function driver($driver = null) { static $_driver = 'mysql'; static $set = false; if (!is_null($driver)) { $_driver = trim($driver); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_DRIVER'])) { return $GLOBALS['ADODB_SESSION_DRIVER']; } } return $_driver; } /*! */ function host($host = null) { static $_host = 'localhost'; static $set = false; if (!is_null($host)) { $_host = trim($host); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_CONNECT'])) { return $GLOBALS['ADODB_SESSION_CONNECT']; } } return $_host; } /*! */ function user($user = null) { static $_user = 'root'; static $set = false; if (!is_null($user)) { $_user = trim($user); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_USER'])) { return $GLOBALS['ADODB_SESSION_USER']; } } return $_user; } /*! */ function password($password = null) { static $_password = ''; static $set = false; if (!is_null($password)) { $_password = $password; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_PWD'])) { return $GLOBALS['ADODB_SESSION_PWD']; } } return $_password; } /*! */ function database($database = null) { static $_database = 'xphplens_2'; static $set = false; if (!is_null($database)) { $_database = trim($database); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_DB'])) { return $GLOBALS['ADODB_SESSION_DB']; } } return $_database; } /*! */ function persist($persist = null) { static $_persist = true; if (!is_null($persist)) { $_persist = trim($persist); } return $_persist; } /*! */ function lifetime($lifetime = null) { static $_lifetime; static $set = false; if (!is_null($lifetime)) { $_lifetime = (int) $lifetime; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESS_LIFE'])) { return $GLOBALS['ADODB_SESS_LIFE']; } } if (!$_lifetime) { $_lifetime = ini_get('session.gc_maxlifetime'); if ($_lifetime <= 1) { // bug in PHP 4.0.3 pl 1 -- how about other versions? //print "

Session Error: PHP.INI setting session.gc_maxlifetimenot set: $lifetime

"; $_lifetime = 1440; } } return $_lifetime; } /*! */ function debug($debug = null) { static $_debug = false; static $set = false; if (!is_null($debug)) { $_debug = (bool) $debug; $conn = ADODB_Session::_conn(); if ($conn) { $conn->debug = $_debug; } $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESS_DEBUG'])) { return $GLOBALS['ADODB_SESS_DEBUG']; } } return $_debug; } /*! */ function expireNotify($expire_notify = null) { static $_expire_notify; static $set = false; if (!is_null($expire_notify)) { $_expire_notify = $expire_notify; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY'])) { return $GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY']; } } return $_expire_notify; } /*! */ function table($table = null) { static $_table = 'sessions'; static $set = false; if (!is_null($table)) { $_table = trim($table); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_TBL'])) { return $GLOBALS['ADODB_SESSION_TBL']; } } return $_table; } /*! */ function optimize($optimize = null) { static $_optimize = false; static $set = false; if (!is_null($optimize)) { $_optimize = (bool) $optimize; $set = true; } elseif (!$set) { // backwards compatibility if (defined('ADODB_SESSION_OPTIMIZE')) { return true; } } return $_optimize; } /*! */ function syncSeconds($sync_seconds = null) { static $_sync_seconds = 60; static $set = false; if (!is_null($sync_seconds)) { $_sync_seconds = (int) $sync_seconds; $set = true; } elseif (!$set) { // backwards compatibility if (defined('ADODB_SESSION_SYNCH_SECS')) { return ADODB_SESSION_SYNCH_SECS; } } return $_sync_seconds; } /*! */ function clob($clob = null) { static $_clob = false; static $set = false; if (!is_null($clob)) { $_clob = strtolower(trim($clob)); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_USE_LOBS'])) { return $GLOBALS['ADODB_SESSION_USE_LOBS']; } } return $_clob; } /*! */ function dataFieldName($data_field_name = null) { static $_data_field_name = 'data'; if (!is_null($data_field_name)) { $_data_field_name = trim($data_field_name); } return $_data_field_name; } /*! */ function filter($filter = null) { static $_filter = array(); if (!is_null($filter)) { if (!is_array($filter)) { $filter = array($filter); } $_filter = $filter; } return $_filter; } /*! */ function encryptionKey($encryption_key = null) { static $_encryption_key = 'CRYPTED ADODB SESSIONS ROCK!'; if (!is_null($encryption_key)) { $_encryption_key = $encryption_key; } return $_encryption_key; } ///////////////////// // private methods ///////////////////// /*! */ function &_conn($conn=null) { return $GLOBALS['ADODB_SESS_CONN']; } /*! */ function _crc($crc = null) { static $_crc = false; if (!is_null($crc)) { $_crc = $crc; } return $_crc; } /*! */ function _init() { session_module_name('user'); session_set_save_handler( array('ADODB_Session', 'open'), array('ADODB_Session', 'close'), array('ADODB_Session', 'read'), array('ADODB_Session', 'write'), array('ADODB_Session', 'destroy'), array('ADODB_Session', 'gc') ); } /*! */ function _sessionKey() { // use this function to create the encryption key for crypted sessions // crypt the used key, ADODB_Session::encryptionKey() as key and session_id() as salt return crypt(ADODB_Session::encryptionKey(), session_id()); } /*! */ function _dumprs($rs) { $conn =& ADODB_Session::_conn(); $debug = ADODB_Session::debug(); if (!$conn) { return; } if (!$debug) { return; } if (!$rs) { echo "
\$rs is null or false
\n"; return; } //echo "
\nAffected_Rows=",$conn->Affected_Rows(),"
\n"; if (!is_object($rs)) { return; } require_once ADODB_SESSION.'/../tohtml.inc.php'; rs2html($rs); } ///////////////////// // public methods ///////////////////// function config($driver, $host, $user, $password, $database=false,$options=false) { ADODB_Session::driver($driver); ADODB_Session::host($host); ADODB_Session::user($user); ADODB_Session::password($password); ADODB_Session::database($database); if ($driver == 'oci8' || $driver == 'oci8po') $options['lob'] = 'CLOB'; if (isset($options['table'])) ADODB_Session::table($options['table']); if (isset($options['lob'])) ADODB_Session::clob($options['lob']); if (isset($options['debug'])) ADODB_Session::debug($options['debug']); } /*! Create the connection to the database. If $conn already exists, reuse that connection */ function open($save_path, $session_name, $persist = null) { $conn =& ADODB_Session::_conn(); if ($conn) { return true; } $database = ADODB_Session::database(); $debug = ADODB_Session::debug(); $driver = ADODB_Session::driver(); $host = ADODB_Session::host(); $password = ADODB_Session::password(); $user = ADODB_Session::user(); if (!is_null($persist)) { ADODB_Session::persist($persist); } else { $persist = ADODB_Session::persist(); } # these can all be defaulted to in php.ini # assert('$database'); # assert('$driver'); # assert('$host'); $conn =& ADONewConnection($driver); if ($debug) { $conn->debug = true; // ADOConnection::outp( " driver=$driver user=$user pwd=$password db=$database "); } if ($persist) { switch($persist) { default: case 'P': $ok = $conn->PConnect($host, $user, $password, $database); break; case 'C': $ok = $conn->Connect($host, $user, $password, $database); break; case 'N': $ok = $conn->NConnect($host, $user, $password, $database); break; } } else { $ok = $conn->Connect($host, $user, $password, $database); } if ($ok) $GLOBALS['ADODB_SESS_CONN'] =& $conn; else ADOConnection::outp('

Session: connection failed

', false); return $ok; } /*! Close the connection */ function close() { /* $conn =& ADODB_Session::_conn(); if ($conn) $conn->Close(); */ return true; } /* Slurp in the session variables and return the serialized string */ function read($key) { $conn =& ADODB_Session::_conn(); $data = ADODB_Session::dataFieldName(); $filter = ADODB_Session::filter(); $table = ADODB_Session::table(); if (!$conn) { return ''; } //assert('$table'); $qkey = $conn->quote($key); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; $sql = "SELECT $data FROM $table WHERE sesskey = $binary $qkey AND expiry >= " . time(); /* Lock code does not work as it needs to hold transaction within whole page, and we don't know if developer has commited elsewhere... :( */ #if (ADODB_Session::Lock()) # $rs =& $conn->RowLock($table, "$binary sesskey = $qkey AND expiry >= " . time(), $data); #else $rs =& $conn->Execute($sql); //ADODB_Session::_dumprs($rs); if ($rs) { if ($rs->EOF) { $v = ''; } else { $v = reset($rs->fields); $filter = array_reverse($filter); foreach ($filter as $f) { if (is_object($f)) { $v = $f->read($v, ADODB_Session::_sessionKey()); } } $v = rawurldecode($v); } $rs->Close(); ADODB_Session::_crc(strlen($v) . crc32($v)); return $v; } return ''; } /*! Write the serialized data to a database. If the data has not been modified since the last read(), we do not write. */ function write($key, $val) { global $ADODB_SESSION_READONLY; if (!empty($ADODB_SESSION_READONLY)) return; $clob = ADODB_Session::clob(); $conn =& ADODB_Session::_conn(); $crc = ADODB_Session::_crc(); $data = ADODB_Session::dataFieldName(); $debug = ADODB_Session::debug(); $driver = ADODB_Session::driver(); $expire_notify = ADODB_Session::expireNotify(); $filter = ADODB_Session::filter(); $lifetime = ADODB_Session::lifetime(); $table = ADODB_Session::table(); if (!$conn) { return false; } $qkey = $conn->qstr($key); //assert('$table'); $expiry = time() + $lifetime; $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; // crc32 optimization since adodb 2.1 // now we only update expiry date, thx to sebastian thom in adodb 2.32 if ($crc !== false && $crc == (strlen($val) . crc32($val))) { if ($debug) { echo '

Session: Only updating date - crc32 not changed

'; } $expirevar = ''; if ($expire_notify) { $var = reset($expire_notify); global $$var; if (isset($$var)) { $expirevar = $$var; } } $sql = "UPDATE $table SET expiry = ".$conn->Param('0').",expireref=".$conn->Param('1')." WHERE $binary sesskey = ".$conn->Param('2')." AND expiry >= ".$conn->Param('3'); $rs =& $conn->Execute($sql,array($expiry,$expirevar,$key,time())); return true; } $val = rawurlencode($val); foreach ($filter as $f) { if (is_object($f)) { $val = $f->write($val, ADODB_Session::_sessionKey()); } } $arr = array('sesskey' => $key, 'expiry' => $expiry, $data => $val, 'expireref' => ''); if ($expire_notify) { $var = reset($expire_notify); global $$var; if (isset($$var)) { $arr['expireref'] = $$var; } } if (!$clob) { // no lobs, simply use replace() $arr[$data] = $conn->qstr($val); $rs = $conn->Replace($table, $arr, 'sesskey', $autoQuote = true); } else { // what value shall we insert/update for lob row? switch ($driver) { // empty_clob or empty_lob for oracle dbs case 'oracle': case 'oci8': case 'oci8po': case 'oci805': $lob_value = sprintf('empty_%s()', strtolower($clob)); break; // null for all other default: $lob_value = 'null'; break; } $conn->StartTrans(); $expiryref = $conn->qstr($arr['expireref']); // do we insert or update? => as for sesskey $rs =& $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = $qkey"); if ($rs && reset($rs->fields) > 0) { $sql = "UPDATE $table SET expiry = $expiry, $data = $lob_value, expireref=$expiryref WHERE sesskey = $qkey"; } else { $sql = "INSERT INTO $table (expiry, $data, sesskey,expireref) VALUES ($expiry, $lob_value, $qkey,$expiryref)"; } if ($rs)$rs->Close(); $err = ''; $rs1 =& $conn->Execute($sql); if (!$rs1) $err = $conn->ErrorMsg()."\n"; $rs2 =& $conn->UpdateBlob($table, $data, $val, " sesskey=$qkey", strtoupper($clob)); if (!$rs2) $err .= $conn->ErrorMsg()."\n"; $rs = ($rs && $rs2) ? true : false; $conn->CompleteTrans(); } if (!$rs) { ADOConnection::outp('

Session Replace: ' . $conn->ErrorMsg() . '

', false); return false; } else { // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($conn->databaseType == 'access') { $sql = "SELECT sesskey FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) { $rs->Close(); } } }/* if (ADODB_Session::Lock()) { $conn->CommitTrans(); }*/ return $rs ? true : false; } /*! */ function destroy($key) { $conn =& ADODB_Session::_conn(); $table = ADODB_Session::table(); $expire_notify = ADODB_Session::expireNotify(); if (!$conn) { return false; } //assert('$table'); $qkey = $conn->quote($key); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; if ($expire_notify) { reset($expire_notify); $fn = next($expire_notify); $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); $sql = "SELECT expireref, sesskey FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); $conn->SetFetchMode($savem); if (!$rs) { return false; } if (!$rs->EOF) { $ref = $rs->fields[0]; $key = $rs->fields[1]; //assert('$ref'); //assert('$key'); $fn($ref, $key); } $rs->Close(); } $sql = "DELETE FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); return $rs ? true : false; } /*! */ function gc($maxlifetime) { $conn =& ADODB_Session::_conn(); $debug = ADODB_Session::debug(); $expire_notify = ADODB_Session::expireNotify(); $optimize = ADODB_Session::optimize(); $sync_seconds = ADODB_Session::syncSeconds(); $table = ADODB_Session::table(); if (!$conn) { return false; } $time = time(); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; if ($expire_notify) { reset($expire_notify); $fn = next($expire_notify); $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); $sql = "SELECT expireref, sesskey FROM $table WHERE expiry < $time"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); $conn->SetFetchMode($savem); if ($rs) { $conn->StartTrans(); $keys = array(); while (!$rs->EOF) { $ref = $rs->fields[0]; $key = $rs->fields[1]; $fn($ref, $key); $del = $conn->Execute("DELETE FROM $table WHERE sesskey=".$conn->Param('0'),array($key)); $rs->MoveNext(); } $rs->Close(); $conn->CompleteTrans(); } } else { if (1) { $sql = "SELECT sesskey FROM $table WHERE expiry < $time"; $arr =& $conn->GetAll($sql); foreach ($arr as $row) { $sql2 = "DELETE FROM $table WHERE sesskey=".$conn->Param('0'); $conn->Execute($sql2,array($row[0])); } } else { $sql = "DELETE FROM $table WHERE expiry < $time"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) $rs->Close(); } if ($debug) { ADOConnection::outp("

Garbage Collection: $sql

"); } } // suggested by Cameron, "GaM3R" if ($optimize) { $driver = ADODB_Session::driver(); if (preg_match('/mysql/i', $driver)) { $sql = "OPTIMIZE TABLE $table"; } if (preg_match('/postgres/i', $driver)) { $sql = "VACUUM $table"; } if (!empty($sql)) { $conn->Execute($sql); } } if ($sync_seconds) { $sql = 'SELECT '; if ($conn->dataProvider === 'oci8') { $sql .= "TO_CHAR({$conn->sysTimeStamp}, 'RRRR-MM-DD HH24:MI:SS')"; } else { $sql .= $conn->sysTimeStamp; } $sql .= " FROM $table"; $rs =& $conn->SelectLimit($sql, 1); if ($rs && !$rs->EOF) { $dbts = reset($rs->fields); $rs->Close(); $dbt = $conn->UnixTimeStamp($dbts); $t = time(); if (abs($dbt - $t) >= $sync_seconds) { $msg = __FILE__ . ": Server time for webserver {$_SERVER['HTTP_HOST']} not in synch with database: " . " database=$dbt ($dbts), webserver=$t (diff=". (abs($dbt - $t) / 60) . ' minutes)'; error_log($msg); if ($debug) { ADOConnection::outp("

$msg

"); } } } } return true; } } ADODB_Session::_init(); if (empty($ADODB_SESSION_READONLY)) register_shutdown_function('session_write_close'); // for backwards compatability only function adodb_sess_open($save_path, $session_name, $persist = true) { return ADODB_Session::open($save_path, $session_name, $persist); } // for backwards compatability only function adodb_sess_gc($t) { return ADODB_Session::gc($t); } ?>phpgacl-3.3.7/adodb/session/session_schema2.xml0100644025754300001440000000130710476657431020432 0ustar ipsousers table for ADOdb session-management session key
phpgacl-3.3.7/adodb/session/adodb-encrypt-mcrypt.php0100644025754300001440000000344510476657245021413 0ustar ipsousers_cipher; } /** */ function setCipher($cipher) { $this->_cipher = $cipher; } /** */ function getMode() { return $this->_mode; } /** */ function setMode($mode) { $this->_mode = $mode; } /** */ function getSource() { return $this->_source; } /** */ function setSource($source) { $this->_source = $source; } /** */ function ADODB_Encrypt_MCrypt($cipher = null, $mode = null, $source = null) { if (!$cipher) { $cipher = MCRYPT_RIJNDAEL_256; } if (!$mode) { $mode = MCRYPT_MODE_ECB; } if (!$source) { $source = MCRYPT_RAND; } $this->_cipher = $cipher; $this->_mode = $mode; $this->_source = $source; } /** */ function write($data, $key) { $iv_size = mcrypt_get_iv_size($this->_cipher, $this->_mode); $iv = mcrypt_create_iv($iv_size, $this->_source); return mcrypt_encrypt($this->_cipher, $key, $data, $this->_mode, $iv); } /** */ function read($data, $key) { $iv_size = mcrypt_get_iv_size($this->_cipher, $this->_mode); $iv = mcrypt_create_iv($iv_size, $this->_source); $rv = mcrypt_decrypt($this->_cipher, $key, $data, $this->_mode, $iv); return rtrim($rv, "\0"); } } return 1; ?> phpgacl-3.3.7/adodb/session/adodb-session-clob.php0100644025754300001440000000105010476657245021001 0ustar ipsousersphpgacl-3.3.7/adodb/session/adodb-sessions.oracle.clob.sql0100644025754300001440000000043110004632166022421 0ustar ipsousers-- $CVSHeader$ DROP TABLE adodb_sessions; CREATE TABLE sessions ( sesskey CHAR(32) DEFAULT '' NOT NULL, expiry INT DEFAULT 0 NOT NULL, expireref VARCHAR(64) DEFAULT '', data CLOB DEFAULT '', PRIMARY KEY (sesskey) ); CREATE INDEX ix_expiry ON sessions (expiry); QUIT; phpgacl-3.3.7/adodb/session/adodb-session2.php0100644025754300001440000005161610476657431020160 0ustar ipsousersExecute('UPDATE '. ADODB_Session::table(). ' SET sesskey='. $conn->qstr($new_id). ' WHERE sesskey='.$conn->qstr($old_id)); /* it is possible that the update statement fails due to a collision */ if (!$ok) { session_id($old_id); if (empty($ck)) $ck = session_get_cookie_params(); setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); return false; } return true; } /* Generate database table for session data @see http://phplens.com/lens/lensforum/msgs.php?id=12280 @return 0 if failure, 1 if errors, 2 if successful. @author Markus Staab http://www.public-4u.de */ function adodb_session_create_table($schemaFile=null,$conn = null) { // set default values if ($schemaFile===null) $schemaFile = ADODB_SESSION . '/session_schema2.xml'; if ($conn===null) $conn =& ADODB_Session::_conn(); if (!$conn) return 0; $schema = new adoSchema($conn); $schema->ParseSchema($schemaFile); return $schema->ExecuteSchema(); } /*! \static */ class ADODB_Session { ///////////////////// // getter/setter methods ///////////////////// /* function Lock($lock=null) { static $_lock = false; if (!is_null($lock)) $_lock = $lock; return $lock; } */ /*! */ function driver($driver = null) { static $_driver = 'mysql'; static $set = false; if (!is_null($driver)) { $_driver = trim($driver); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_DRIVER'])) { return $GLOBALS['ADODB_SESSION_DRIVER']; } } return $_driver; } /*! */ function host($host = null) { static $_host = 'localhost'; static $set = false; if (!is_null($host)) { $_host = trim($host); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_CONNECT'])) { return $GLOBALS['ADODB_SESSION_CONNECT']; } } return $_host; } /*! */ function user($user = null) { static $_user = 'root'; static $set = false; if (!is_null($user)) { $_user = trim($user); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_USER'])) { return $GLOBALS['ADODB_SESSION_USER']; } } return $_user; } /*! */ function password($password = null) { static $_password = ''; static $set = false; if (!is_null($password)) { $_password = $password; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_PWD'])) { return $GLOBALS['ADODB_SESSION_PWD']; } } return $_password; } /*! */ function database($database = null) { static $_database = ''; static $set = false; if (!is_null($database)) { $_database = trim($database); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_DB'])) { return $GLOBALS['ADODB_SESSION_DB']; } } return $_database; } /*! */ function persist($persist = null) { static $_persist = true; if (!is_null($persist)) { $_persist = trim($persist); } return $_persist; } /*! */ function lifetime($lifetime = null) { static $_lifetime; static $set = false; if (!is_null($lifetime)) { $_lifetime = (int) $lifetime; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESS_LIFE'])) { return $GLOBALS['ADODB_SESS_LIFE']; } } if (!$_lifetime) { $_lifetime = ini_get('session.gc_maxlifetime'); if ($_lifetime <= 1) { // bug in PHP 4.0.3 pl 1 -- how about other versions? //print "

Session Error: PHP.INI setting session.gc_maxlifetimenot set: $lifetime

"; $_lifetime = 1440; } } return $_lifetime; } /*! */ function debug($debug = null) { static $_debug = false; static $set = false; if (!is_null($debug)) { $_debug = (bool) $debug; $conn = ADODB_Session::_conn(); if ($conn) { $conn->debug = $_debug; } $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESS_DEBUG'])) { return $GLOBALS['ADODB_SESS_DEBUG']; } } return $_debug; } /*! */ function expireNotify($expire_notify = null) { static $_expire_notify; static $set = false; if (!is_null($expire_notify)) { $_expire_notify = $expire_notify; $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY'])) { return $GLOBALS['ADODB_SESSION_EXPIRE_NOTIFY']; } } return $_expire_notify; } /*! */ function table($table = null) { static $_table = 'sessions2'; static $set = false; if (!is_null($table)) { $_table = trim($table); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_TBL'])) { return $GLOBALS['ADODB_SESSION_TBL']; } } return $_table; } /*! */ function optimize($optimize = null) { static $_optimize = false; static $set = false; if (!is_null($optimize)) { $_optimize = (bool) $optimize; $set = true; } elseif (!$set) { // backwards compatibility if (defined('ADODB_SESSION_OPTIMIZE')) { return true; } } return $_optimize; } /*! */ function syncSeconds($sync_seconds = null) { //echo ("

WARNING: ADODB_SESSION::syncSeconds is longer used, please remove this function for your code

"); return 0; } /*! */ function clob($clob = null) { static $_clob = false; static $set = false; if (!is_null($clob)) { $_clob = strtolower(trim($clob)); $set = true; } elseif (!$set) { // backwards compatibility if (isset($GLOBALS['ADODB_SESSION_USE_LOBS'])) { return $GLOBALS['ADODB_SESSION_USE_LOBS']; } } return $_clob; } /*! */ function dataFieldName($data_field_name = null) { //echo ("

WARNING: ADODB_SESSION::dataFieldName() is longer used, please remove this function for your code

"); return ''; } /*! */ function filter($filter = null) { static $_filter = array(); if (!is_null($filter)) { if (!is_array($filter)) { $filter = array($filter); } $_filter = $filter; } return $_filter; } /*! */ function encryptionKey($encryption_key = null) { static $_encryption_key = 'CRYPTED ADODB SESSIONS ROCK!'; if (!is_null($encryption_key)) { $_encryption_key = $encryption_key; } return $_encryption_key; } ///////////////////// // private methods ///////////////////// /*! */ function &_conn($conn=null) { return $GLOBALS['ADODB_SESS_CONN']; } /*! */ function _crc($crc = null) { static $_crc = false; if (!is_null($crc)) { $_crc = $crc; } return $_crc; } /*! */ function _init() { session_module_name('user'); session_set_save_handler( array('ADODB_Session', 'open'), array('ADODB_Session', 'close'), array('ADODB_Session', 'read'), array('ADODB_Session', 'write'), array('ADODB_Session', 'destroy'), array('ADODB_Session', 'gc') ); } /*! */ function _sessionKey() { // use this function to create the encryption key for crypted sessions // crypt the used key, ADODB_Session::encryptionKey() as key and session_id() as salt return crypt(ADODB_Session::encryptionKey(), session_id()); } /*! */ function _dumprs($rs) { $conn =& ADODB_Session::_conn(); $debug = ADODB_Session::debug(); if (!$conn) { return; } if (!$debug) { return; } if (!$rs) { echo "
\$rs is null or false
\n"; return; } //echo "
\nAffected_Rows=",$conn->Affected_Rows(),"
\n"; if (!is_object($rs)) { return; } require_once ADODB_SESSION.'/../tohtml.inc.php'; rs2html($rs); } ///////////////////// // public methods ///////////////////// function config($driver, $host, $user, $password, $database=false,$options=false) { ADODB_Session::driver($driver); ADODB_Session::host($host); ADODB_Session::user($user); ADODB_Session::password($password); ADODB_Session::database($database); if ($driver == 'oci8' || $driver == 'oci8po') $options['lob'] = 'CLOB'; if (isset($options['table'])) ADODB_Session::table($options['table']); if (isset($options['lob'])) ADODB_Session::clob($options['lob']); if (isset($options['debug'])) ADODB_Session::debug($options['debug']); } /*! Create the connection to the database. If $conn already exists, reuse that connection */ function open($save_path, $session_name, $persist = null) { $conn =& ADODB_Session::_conn(); if ($conn) { return true; } $database = ADODB_Session::database(); $debug = ADODB_Session::debug(); $driver = ADODB_Session::driver(); $host = ADODB_Session::host(); $password = ADODB_Session::password(); $user = ADODB_Session::user(); if (!is_null($persist)) { ADODB_Session::persist($persist); } else { $persist = ADODB_Session::persist(); } # these can all be defaulted to in php.ini # assert('$database'); # assert('$driver'); # assert('$host'); $conn =& ADONewConnection($driver); if ($debug) { $conn->debug = true; ADOConnection::outp( " driver=$driver user=$user db=$database "); } if ($persist) { switch($persist) { default: case 'P': $ok = $conn->PConnect($host, $user, $password, $database); break; case 'C': $ok = $conn->Connect($host, $user, $password, $database); break; case 'N': $ok = $conn->NConnect($host, $user, $password, $database); break; } } else { $ok = $conn->Connect($host, $user, $password, $database); } if ($ok) $GLOBALS['ADODB_SESS_CONN'] =& $conn; else ADOConnection::outp('

Session: connection failed

', false); return $ok; } /*! Close the connection */ function close() { /* $conn =& ADODB_Session::_conn(); if ($conn) $conn->Close(); */ return true; } /* Slurp in the session variables and return the serialized string */ function read($key) { $conn =& ADODB_Session::_conn(); $filter = ADODB_Session::filter(); $table = ADODB_Session::table(); if (!$conn) { return ''; } //assert('$table'); $qkey = $conn->quote($key); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; $sql = "SELECT sessdata FROM $table WHERE sesskey = $binary $qkey AND expiry >= " . $conn->sysTimeStamp; /* Lock code does not work as it needs to hold transaction within whole page, and we don't know if developer has commited elsewhere... :( */ #if (ADODB_Session::Lock()) # $rs =& $conn->RowLock($table, "$binary sesskey = $qkey AND expiry >= " . time(), sessdata); #else $rs =& $conn->Execute($sql); //ADODB_Session::_dumprs($rs); if ($rs) { if ($rs->EOF) { $v = ''; } else { $v = reset($rs->fields); $filter = array_reverse($filter); foreach ($filter as $f) { if (is_object($f)) { $v = $f->read($v, ADODB_Session::_sessionKey()); } } $v = rawurldecode($v); } $rs->Close(); ADODB_Session::_crc(strlen($v) . crc32($v)); return $v; } return ''; } /*! Write the serialized data to a database. If the data has not been modified since the last read(), we do not write. */ function write($key, $val) { global $ADODB_SESSION_READONLY; if (!empty($ADODB_SESSION_READONLY)) return; $clob = ADODB_Session::clob(); $conn =& ADODB_Session::_conn(); $crc = ADODB_Session::_crc(); $debug = ADODB_Session::debug(); $driver = ADODB_Session::driver(); $expire_notify = ADODB_Session::expireNotify(); $filter = ADODB_Session::filter(); $lifetime = ADODB_Session::lifetime(); $table = ADODB_Session::table(); if (!$conn) { return false; } $sysTimeStamp = $conn->sysTimeStamp; //assert('$table'); $expiry = $conn->OffsetDate($lifetime/(24*3600),$sysTimeStamp); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; // crc32 optimization since adodb 2.1 // now we only update expiry date, thx to sebastian thom in adodb 2.32 if ($crc !== false && $crc == (strlen($val) . crc32($val))) { if ($debug) { echo '

Session: Only updating date - crc32 not changed

'; } $expirevar = ''; if ($expire_notify) { $var = reset($expire_notify); global $$var; if (isset($$var)) { $expirevar = $$var; } } $sql = "UPDATE $table SET expiry = $expiry ,expireref=".$conn->Param('0').", modified = $sysTimeStamp WHERE $binary sesskey = ".$conn->Param('1')." AND expiry >= $sysTimeStamp"; $rs =& $conn->Execute($sql,array($expirevar,$key)); return true; } $val = rawurlencode($val); foreach ($filter as $f) { if (is_object($f)) { $val = $f->write($val, ADODB_Session::_sessionKey()); } } $expireref = ''; if ($expire_notify) { $var = reset($expire_notify); global $$var; if (isset($$var)) { $expireref = $$var; } } if (!$clob) { // no lobs, simply use replace() $rs =& $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = ".$conn->Param(0),array($key)); if ($rs) $rs->Close(); if ($rs && reset($rs->fields) > 0) { $sql = "UPDATE $table SET expiry=$expiry, sessdata=".$conn->Param(0).", expireref= ".$conn->Param(1).",modified=$sysTimeStamp WHERE sesskey = ".$conn->Param('2'); } else { $sql = "INSERT INTO $table (expiry, sessdata, expireref, sesskey, created, modified) VALUES ($expiry,".$conn->Param('0').", ". $conn->Param('1').", ".$conn->Param('2').", $sysTimeStamp, $sysTimeStamp)"; } $rs =& $conn->Execute($sql,array($val,$expireref,$key)); } else { // what value shall we insert/update for lob row? switch ($driver) { // empty_clob or empty_lob for oracle dbs case 'oracle': case 'oci8': case 'oci8po': case 'oci805': $lob_value = sprintf('empty_%s()', strtolower($clob)); break; // null for all other default: $lob_value = 'null'; break; } $conn->StartTrans(); $rs =& $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = ".$conn->Param(0),array($key)); if ($rs) $rs->Close(); if ($rs && reset($rs->fields) > 0) { $sql = "UPDATE $table SET expiry=$expiry, sessdata=$lob_value, expireref= ".$conn->Param(0).",modified=$sysTimeStamp WHERE sesskey = ".$conn->Param('1'); } else { $sql = "INSERT INTO $table (expiry, sessdata, expireref, sesskey, created, modified) VALUES ($expiry,$lob_value, ". $conn->Param('0').", ".$conn->Param('1').", $sysTimeStamp, $sysTimeStamp)"; } $rs =& $conn->Execute($sql,array($expireref,$key)); $qkey = $conn->qstr($key); $rs2 =& $conn->UpdateBlob($table, 'sessdata', $val, " sesskey=$qkey", strtoupper($clob)); $rs = $conn->CompleteTrans(); } if (!$rs) { ADOConnection::outp('

Session Replace: ' . $conn->ErrorMsg() . '

', false); return false; } else { // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($conn->databaseType == 'access') { $sql = "SELECT sesskey FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) { $rs->Close(); } } }/* if (ADODB_Session::Lock()) { $conn->CommitTrans(); }*/ return $rs ? true : false; } /*! */ function destroy($key) { $conn =& ADODB_Session::_conn(); $table = ADODB_Session::table(); $expire_notify = ADODB_Session::expireNotify(); if (!$conn) { return false; } //assert('$table'); $qkey = $conn->quote($key); $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; if ($expire_notify) { reset($expire_notify); $fn = next($expire_notify); $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); $sql = "SELECT expireref, sesskey FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); $conn->SetFetchMode($savem); if (!$rs) { return false; } if (!$rs->EOF) { $ref = $rs->fields[0]; $key = $rs->fields[1]; //assert('$ref'); //assert('$key'); $fn($ref, $key); } $rs->Close(); } $sql = "DELETE FROM $table WHERE $binary sesskey = $qkey"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) { $rs->Close(); } return $rs ? true : false; } /*! */ function gc($maxlifetime) { $conn =& ADODB_Session::_conn(); $debug = ADODB_Session::debug(); $expire_notify = ADODB_Session::expireNotify(); $optimize = ADODB_Session::optimize(); $table = ADODB_Session::table(); if (!$conn) { return false; } //assert('$table'); $time = $conn->sysTimeStamp; $binary = $conn->dataProvider === 'mysql' ? '/*! BINARY */' : ''; if ($expire_notify) { reset($expire_notify); $fn = next($expire_notify); $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); $sql = "SELECT expireref, sesskey FROM $table WHERE expiry < $time"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); $conn->SetFetchMode($savem); if ($rs) { $conn->StartTrans(); $keys = array(); while (!$rs->EOF) { $ref = $rs->fields[0]; $key = $rs->fields[1]; $fn($ref, $key); $del = $conn->Execute("DELETE FROM $table WHERE sesskey=".$conn->Param('0'),array($key)); $rs->MoveNext(); } $rs->Close(); $conn->CompleteTrans(); } } else { if (0) { $sql = "SELECT sesskey FROM $table WHERE expiry < $time"; $arr =& $conn->GetAll($sql); foreach ($arr as $row) { $sql2 = "DELETE FROM $table WHERE sesskey=".$conn->Param('0'); $conn->Execute($sql2,array($row[0])); } } else { $sql = "DELETE FROM $table WHERE expiry < $time"; $rs =& $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) $rs->Close(); } if ($debug) { ADOConnection::outp("

Garbage Collection: $sql

"); } } // suggested by Cameron, "GaM3R" if ($optimize) { $driver = ADODB_Session::driver(); if (preg_match('/mysql/i', $driver)) { $sql = "OPTIMIZE TABLE $table"; } if (preg_match('/postgres/i', $driver)) { $sql = "VACUUM $table"; } if (!empty($sql)) { $conn->Execute($sql); } } return true; } } ADODB_Session::_init(); if (empty($ADODB_SESSION_READONLY)) register_shutdown_function('session_write_close'); // for backwards compatability only function adodb_sess_open($save_path, $session_name, $persist = true) { return ADODB_Session::open($save_path, $session_name, $persist); } // for backwards compatability only function adodb_sess_gc($t) { return ADODB_Session::gc($t); } ?>phpgacl-3.3.7/adodb/session/adodb-compress-gzip.php0100644025754300001440000000274610476657245021220 0ustar ipsousers_level; } /** */ function setLevel($level) { assert('$level >= 0'); assert('$level <= 9'); $this->_level = (int) $level; } /** */ function getMinLength() { return $this->_min_length; } /** */ function setMinLength($min_length) { assert('$min_length >= 0'); $this->_min_length = (int) $min_length; } /** */ function ADODB_Compress_Gzip($level = null, $min_length = null) { if (!is_null($level)) { $this->setLevel($level); } if (!is_null($min_length)) { $this->setMinLength($min_length); } } /** */ function write($data, $key) { if (strlen($data) < $this->_min_length) { return $data; } if (!is_null($this->_level)) { return gzcompress($data, $this->_level); } else { return gzcompress($data); } } /** */ function read($data, $key) { return $data ? gzuncompress($data) : $data; } } return 1; ?>phpgacl-3.3.7/adodb/session/crypt.inc.php0100644025754300001440000000537610476657245017262 0ustar ipsousers class MD5Crypt{ function keyED($txt,$encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr=0; $tmp = ""; for ($i=0;$ikeyED($tmp,$key)); } function Decrypt($txt,$key) { $txt = $this->keyED(base64_decode($txt),$key); $tmp = ""; for ($i=0;$i= 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)) { $randnumber = rand(48,120); } $randomPassword .= chr($randnumber); } return $randomPassword; } } class SHA1Crypt{ function keyED($txt,$encrypt_key) { $encrypt_key = sha1($encrypt_key); $ctr=0; $tmp = ""; for ($i=0;$ikeyED($tmp,$key)); } function Decrypt($txt,$key) { $txt = $this->keyED(base64_decode($txt),$key); $tmp = ""; for ($i=0;$i= 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)) { $randnumber = rand(48,120); } $randomPassword .= chr($randnumber); } return $randomPassword; } } ?>phpgacl-3.3.7/adodb/session/session_schema.xml0100644025754300001440000000103110476657431020342 0ustar ipsousers table for ADOdb session-management session key
phpgacl-3.3.7/adodb/session/adodb-sess.txt0100644025754300001440000000730610074023170017372 0ustar ipsousersJohn, I have been an extremely satisfied ADODB user for several years now. To give you something back for all your hard work, I've spent the last 3 days rewriting the adodb-session.php code. ---------- What's New ---------- Here's a list of the new code's benefits: * Combines the functionality of the three files: adodb-session.php adodb-session-clob.php adodb-cryptsession.php each with very similar functionality, into a single file adodb-session.php. This will ease maintenance and support issues. * Supports multiple encryption and compression schemes. Currently, we support: MD5Crypt (crypt.inc.php) MCrypt Secure (Horde's emulation of MCrypt, if MCrypt module is not available.) GZip BZip2 These can be stacked, so if you want to compress and then encrypt your session data, it's easy. Also, the built-in MCrypt functions will be *much* faster, and more secure, than the MD5Crypt code. * adodb-session.php contains a single class ADODB_Session that encapsulates all functionality. This eliminates the use of global vars and defines (though they are supported for backwards compatibility). * All user defined parameters are now static functions in the ADODB_Session class. New parameters include: * encryptionKey(): Define the encryption key used to encrypt the session. Originally, it was a hard coded string. * persist(): Define if the database will be opened in persistent mode. Originally, the user had to call adodb_sess_open(). * dataFieldName(): Define the field name used to store the session data, as 'DATA' appears to be a reserved word in the following cases: ANSI SQL IBM DB2 MS SQL Server Postgres SAP * filter(): Used to support multiple, simulataneous encryption/compression schemes. * Debug support is improved thru _rsdump() function, which is called after every database call. ------------ What's Fixed ------------ The new code includes several bug fixes and enhancements: * sesskey is compared in BINARY mode for MySQL, to avoid problems with session keys that differ only by case. Of course, the user should define the sesskey field as BINARY, to correctly fix this problem, otherwise performance will suffer. * In ADODB_Session::gc(), if $expire_notify is true, the multiple DELETES in the original code have been optimized to a single DELETE. * In ADODB_Session::destroy(), since "SELECT expireref, sesskey FROM $table WHERE sesskey = $qkey" will only return a single value, we don't loop on the result, we simply process the row, if any. * We close $rs after every use. --------------- What's the Same --------------- I know backwards compatibility is *very* important to you. Therefore, the new code is 100% backwards compatible. If you like my code, but don't "trust" it's backwards compatible, maybe we offer it as beta code, in a new directory for a release or two? ------------ What's To Do ------------ I've vascillated over whether to use a single function to get/set parameters: $user = ADODB_Session::user(); // get ADODB_Session::user($user); // set or to use separate functions (which is the PEAR/Java way): $user = ADODB_Session::getUser(); ADODB_Session::setUser($user); I've chosen the former as it's makes for a simpler API, and reduces the amount of code, but I'd be happy to change it to the latter. Also, do you think the class should be a singleton class, versus a static class? Let me know if you find this code useful, and will be including it in the next release of ADODB. If so, I will modify the current documentation to detail the new functionality. To that end, what file(s) contain the documentation? Please send them to me if they are not publically available. Also, if there is *anything* in the code that you like to see changed, let me know. Thanks, Ross phpgacl-3.3.7/adodb/session/adodb-encrypt-secret.php0100644025754300001440000000175710476657245021366 0ustar ipsousers phpgacl-3.3.7/adodb/session/adodb-cryptsession.php0100644025754300001440000000116510476657245021155 0ustar ipsousersphpgacl-3.3.7/adodb/session/adodb-encrypt-sha1.php0100644025754300001440000000057010476657431020722 0ustar ipsousersencrypt($data, $key); } function read($data, $key) { $sha1crypt =& new SHA1Crypt(); return $sha1crypt->decrypt($data, $key); } } return 1; ?>phpgacl-3.3.7/adodb/session/adodb-encrypt-md5.php0100644025754300001440000000136610476657245020562 0ustar ipsousersencrypt($data, $key); } /** */ function read($data, $key) { $md5crypt =& new MD5Crypt(); return $md5crypt->decrypt($data, $key); } } return 1; ?>phpgacl-3.3.7/adodb/session/adodb-compress-bzip2.php0100644025754300001440000000401110476657245021260 0ustar ipsousers_block_size; } /** */ function setBlockSize($block_size) { assert('$block_size >= 1'); assert('$block_size <= 9'); $this->_block_size = (int) $block_size; } /** */ function getWorkLevel() { return $this->_work_level; } /** */ function setWorkLevel($work_level) { assert('$work_level >= 0'); assert('$work_level <= 250'); $this->_work_level = (int) $work_level; } /** */ function getMinLength() { return $this->_min_length; } /** */ function setMinLength($min_length) { assert('$min_length >= 0'); $this->_min_length = (int) $min_length; } /** */ function ADODB_Compress_Bzip2($block_size = null, $work_level = null, $min_length = null) { if (!is_null($block_size)) { $this->setBlockSize($block_size); } if (!is_null($work_level)) { $this->setWorkLevel($work_level); } if (!is_null($min_length)) { $this->setMinLength($min_length); } } /** */ function write($data, $key) { if (strlen($data) < $this->_min_length) { return $data; } if (!is_null($this->_block_size)) { if (!is_null($this->_work_level)) { return bzcompress($data, $this->_block_size, $this->_work_level); } else { return bzcompress($data, $this->_block_size); } } return bzcompress($data); } /** */ function read($data, $key) { return $data ? bzdecompress($data) : $data; } } return 1; ?> phpgacl-3.3.7/adodb/rsfilter.inc.php0100644025754300001440000000275410476657245016265 0ustar ipsousers $v) { $arr[$k] = ucwords($v); } } $rs = RSFilter($rs,'do_ucwords'); */ function &RSFilter($rs,$fn) { if ($rs->databaseType != 'array') { if (!$rs->connection) return false; $rs = &$rs->connection->_rs2rs($rs); } $rows = $rs->RecordCount(); for ($i=0; $i < $rows; $i++) { if (is_array ($fn)) { $obj = $fn[0]; $method = $fn[1]; $obj->$method ($rs->_array[$i],$rs); } else { $fn($rs->_array[$i],$rs); } } if (!$rs->EOF) { $rs->_currentRow = 0; $rs->fields = $rs->_array[0]; } return $rs; } ?>phpgacl-3.3.7/adodb/adodb-xmlschema.inc.php0100644025754300001440000015404210476657245017461 0ustar ipsousersparent =& $parent; } /** * XML Callback to process start elements * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { } function create() { return array(); } /** * Destroys the object */ function destroy() { unset( $this ); } /** * Checks whether the specified RDBMS is supported by the current * database object or its ranking ancestor. * * @param string $platform RDBMS platform name (from ADODB platform list). * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE. */ function supportedPlatform( $platform = NULL ) { return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE; } /** * Returns the prefix set by the ranking ancestor of the database object. * * @param string $name Prefix string. * @return string Prefix. */ function prefix( $name = '' ) { return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name; } /** * Extracts a field ID from the specified field. * * @param string $field Field. * @return string Field ID. */ function FieldID( $field ) { return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) ); } } /** * Creates a table object in ADOdb's datadict format * * This class stores information about a database table. As charactaristics * of the table are loaded from the external source, methods and properties * of this class are used to build up the table description in ADOdb's * datadict format. * * @package axmls * @access private */ class dbTable extends dbObject { /** * @var string Table name */ var $name; /** * @var array Field specifier: Meta-information about each field */ var $fields = array(); /** * @var array List of table indexes. */ var $indexes = array(); /** * @var array Table options: Table-level options */ var $opts = array(); /** * @var string Field index: Keeps track of which field is currently being processed */ var $current_field; /** * @var boolean Mark table for destruction * @access private */ var $drop_table; /** * @var boolean Mark field for destruction (not yet implemented) * @access private */ var $drop_field = array(); /** * Iniitializes a new table object. * * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. */ function dbTable( &$parent, $attributes = NULL ) { $this->parent =& $parent; $this->name = $this->prefix($attributes['NAME']); } /** * XML Callback to process start elements. Elements currently * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'INDEX': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { xml_set_object( $parser, $this->addIndex( $attributes ) ); } break; case 'DATA': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { xml_set_object( $parser, $this->addData( $attributes ) ); } break; case 'DROP': $this->drop(); break; case 'FIELD': // Add a field $fieldName = $attributes['NAME']; $fieldType = $attributes['TYPE']; $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL; $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL; $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); break; case 'KEY': case 'NOTNULL': case 'AUTOINCREMENT': // Add a field option $this->addFieldOpt( $this->current_field, $this->currentElement ); break; case 'DEFAULT': // Add a field option to the table object // Work around ADOdb datadict issue that misinterprets empty strings. if( $attributes['VALUE'] == '' ) { $attributes['VALUE'] = " '' "; } $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] ); break; case 'DEFDATE': case 'DEFTIMESTAMP': // Add a field option to the table object $this->addFieldOpt( $this->current_field, $this->currentElement ); break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Table constraint case 'CONSTRAINT': if( isset( $this->current_field ) ) { $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); } else { $this->addTableOpt( $cdata ); } break; // Table option case 'OPT': $this->addTableOpt( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'TABLE': $this->parent->addSQL( $this->create( $this->parent ) ); xml_set_object( $parser, $this->parent ); $this->destroy(); break; case 'FIELD': unset($this->current_field); break; } } /** * Adds an index to a table object * * @param array $attributes Index attributes * @return object dbIndex object */ function &addIndex( $attributes ) { $name = strtoupper( $attributes['NAME'] ); $this->indexes[$name] =& new dbIndex( $this, $attributes ); return $this->indexes[$name]; } /** * Adds data to a table object * * @param array $attributes Data attributes * @return object dbData object */ function &addData( $attributes ) { if( !isset( $this->data ) ) { $this->data =& new dbData( $this, $attributes ); } return $this->data; } /** * Adds a field to a table object * * $name is the name of the table to which the field should be added. * $type is an ADODB datadict field type. The following field types * are supported as of ADODB 3.40: * - C: varchar * - X: CLOB (character large object) or largest varchar size * if CLOB is not supported * - C2: Multibyte varchar * - X2: Multibyte CLOB * - B: BLOB (binary large object) * - D: Date (some databases do not support this, and we return a datetime type) * - T: Datetime or Timestamp * - L: Integer field suitable for storing booleans (0 or 1) * - I: Integer (mapped to I4) * - I1: 1-byte integer * - I2: 2-byte integer * - I4: 4-byte integer * - I8: 8-byte integer * - F: Floating point number * - N: Numeric or decimal number * * @param string $name Name of the table to which the field will be added. * @param string $type ADODB datadict field type. * @param string $size Field size * @param array $opts Field options array * @return array Field specifier array */ function addField( $name, $type, $size = NULL, $opts = NULL ) { $field_id = $this->FieldID( $name ); // Set the field index so we know where we are $this->current_field = $field_id; // Set the field name (required) $this->fields[$field_id]['NAME'] = $name; // Set the field type (required) $this->fields[$field_id]['TYPE'] = $type; // Set the field size (optional) if( isset( $size ) ) { $this->fields[$field_id]['SIZE'] = $size; } // Set the field options if( isset( $opts ) ) { $this->fields[$field_id]['OPTS'][] = $opts; } } /** * Adds a field option to the current field specifier * * This method adds a field option allowed by the ADOdb datadict * and appends it to the given field. * * @param string $field Field name * @param string $opt ADOdb field option * @param mixed $value Field option value * @return array Field specifier array */ function addFieldOpt( $field, $opt, $value = NULL ) { if( !isset( $value ) ) { $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt; // Add the option and value } else { $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value ); } } /** * Adds an option to the table * * This method takes a comma-separated list of table-level options * and appends them to the table object. * * @param string $opt Table option * @return array Options */ function addTableOpt( $opt ) { $this->opts[] = $opt; return $this->opts; } /** * Generates the SQL that will create the table in the database * * @param object $xmls adoSchema object * @return array Array containing table creation SQL */ function create( &$xmls ) { $sql = array(); // drop any existing indexes if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) { foreach( $legacy_indexes as $index => $index_details ) { $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name ); } } // remove fields to be dropped from table object foreach( $this->drop_field as $field ) { unset( $this->fields[$field] ); } // if table exists if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) { // drop table if( $this->drop_table ) { $sql[] = $xmls->dict->DropTableSQL( $this->name ); return $sql; } // drop any existing fields not in schema foreach( $legacy_fields as $field_id => $field ) { if( !isset( $this->fields[$field_id] ) ) { $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' ); } } // if table doesn't exist } else { if( $this->drop_table ) { return $sql; } $legacy_fields = array(); } // Loop through the field specifier array, building the associative array for the field options $fldarray = array(); foreach( $this->fields as $field_id => $finfo ) { // Set an empty size if it isn't supplied if( !isset( $finfo['SIZE'] ) ) { $finfo['SIZE'] = ''; } // Initialize the field array with the type and size $fldarray[$field_id] = array( 'NAME' => $finfo['NAME'], 'TYPE' => $finfo['TYPE'], 'SIZE' => $finfo['SIZE'] ); // Loop through the options array and add the field options. if( isset( $finfo['OPTS'] ) ) { foreach( $finfo['OPTS'] as $opt ) { // Option has an argument. if( is_array( $opt ) ) { $key = key( $opt ); $value = $opt[key( $opt )]; @$fldarray[$field_id][$key] .= $value; // Option doesn't have arguments } else { $fldarray[$field_id][$opt] = $opt; } } } } if( empty( $legacy_fields ) ) { // Create the new table $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); logMsg( end( $sql ), 'Generated CreateTableSQL' ); } else { // Upgrade an existing table logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); switch( $xmls->upgrade ) { // Use ChangeTableSQL case 'ALTER': logMsg( 'Generated ChangeTableSQL (ALTERing table)' ); $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts ); break; case 'REPLACE': logMsg( 'Doing upgrade REPLACE (testing)' ); $sql[] = $xmls->dict->DropTableSQL( $this->name ); $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); break; // ignore table default: return array(); } } foreach( $this->indexes as $index ) { $sql[] = $index->create( $xmls ); } if( isset( $this->data ) ) { $sql[] = $this->data->create( $xmls ); } return $sql; } /** * Marks a field or table for destruction */ function drop() { if( isset( $this->current_field ) ) { // Drop the current field logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" ); // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field ); $this->drop_field[$this->current_field] = $this->current_field; } else { // Drop the current table logMsg( "Dropping table '{$this->name}'" ); // $this->drop_table = $xmls->dict->DropTableSQL( $this->name ); $this->drop_table = TRUE; } } } /** * Creates an index object in ADOdb's datadict format * * This class stores information about a database index. As charactaristics * of the index are loaded from the external source, methods and properties * of this class are used to build up the index description in ADOdb's * datadict format. * * @package axmls * @access private */ class dbIndex extends dbObject { /** * @var string Index name */ var $name; /** * @var array Index options: Index-level options */ var $opts = array(); /** * @var array Indexed fields: Table columns included in this index */ var $columns = array(); /** * @var boolean Mark index for destruction * @access private */ var $drop = FALSE; /** * Initializes the new dbIndex object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbIndex( &$parent, $attributes = NULL ) { $this->parent =& $parent; $this->name = $this->prefix ($attributes['NAME']); } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'DROP': $this->drop(); break; case 'CLUSTERED': case 'BITMAP': case 'UNIQUE': case 'FULLTEXT': case 'HASH': // Add index Option $this->addIndexOpt( $this->currentElement ); break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * Processes XML cdata. * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name case 'COL': $this->addField( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'INDEX': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the index * * @param string $name Field name * @return string Field list */ function addField( $name ) { $this->columns[$this->FieldID( $name )] = $name; // Return the field list return $this->columns; } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addIndexOpt( $opt ) { $this->opts[] = $opt; // Return the options list return $this->opts; } /** * Generates the SQL that will create the index in the database * * @param object $xmls adoSchema object * @return array Array containing index creation SQL */ function create( &$xmls ) { if( $this->drop ) { return NULL; } // eliminate any columns that aren't in the table foreach( $this->columns as $id => $col ) { if( !isset( $this->parent->fields[$id] ) ) { unset( $this->columns[$id] ); } } return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); } /** * Marks an index for destruction */ function drop() { $this->drop = TRUE; } } /** * Creates a data object in ADOdb's datadict format * * This class stores information about table data. * * @package axmls * @access private */ class dbData extends dbObject { var $data = array(); var $row; /** * Initializes the new dbIndex object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbData( &$parent, $attributes = NULL ) { $this->parent =& $parent; } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'ROW': $this->row = count( $this->data ); $this->data[$this->row] = array(); break; case 'F': $this->addField($attributes); default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * Processes XML cdata. * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name case 'F': $this->addData( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'DATA': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the index * * @param string $name Field name * @return string Field list */ function addField( $attributes ) { if( isset( $attributes['NAME'] ) ) { $name = $attributes['NAME']; } else { $name = count($this->data[$this->row]); } // Set the field index so we know where we are $this->current_field = $this->FieldID( $name ); } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addData( $cdata ) { if( !isset( $this->data[$this->row] ) ) { $this->data[$this->row] = array(); } if( !isset( $this->data[$this->row][$this->current_field] ) ) { $this->data[$this->row][$this->current_field] = ''; } $this->data[$this->row][$this->current_field] .= $cdata; } /** * Generates the SQL that will create the index in the database * * @param object $xmls adoSchema object * @return array Array containing index creation SQL */ function create( &$xmls ) { $table = $xmls->dict->TableName($this->parent->name); $table_field_count = count($this->parent->fields); $sql = array(); // eliminate any columns that aren't in the table foreach( $this->data as $row ) { $table_fields = $this->parent->fields; $fields = array(); foreach( $row as $field_id => $field_data ) { if( !array_key_exists( $field_id, $table_fields ) ) { if( is_numeric( $field_id ) ) { $field_id = reset( array_keys( $table_fields ) ); } else { continue; } } $name = $table_fields[$field_id]['NAME']; switch( $table_fields[$field_id]['TYPE'] ) { case 'C': case 'C2': case 'X': case 'X2': $fields[$name] = $xmls->db->qstr( $field_data ); break; case 'I': case 'I1': case 'I2': case 'I4': case 'I8': $fields[$name] = intval($field_data); break; default: $fields[$name] = $field_data; } unset($table_fields[$field_id]); } // check that at least 1 column is specified if( empty( $fields ) ) { continue; } // check that no required columns are missing if( count( $fields ) < $table_field_count ) { foreach( $table_fields as $field ) { if (isset( $field['OPTS'] )) if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) { continue(2); } } } $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; } return $sql; } } /** * Creates the SQL to execute a list of provided SQL queries * * @package axmls * @access private */ class dbQuerySet extends dbObject { /** * @var array List of SQL queries */ var $queries = array(); /** * @var string String used to build of a query line by line */ var $query; /** * @var string Query prefix key */ var $prefixKey = ''; /** * @var boolean Auto prefix enable (TRUE) */ var $prefixMethod = 'AUTO'; /** * Initializes the query set. * * @param object $parent Parent object * @param array $attributes Attributes */ function dbQuerySet( &$parent, $attributes = NULL ) { $this->parent =& $parent; // Overrides the manual prefix key if( isset( $attributes['KEY'] ) ) { $this->prefixKey = $attributes['KEY']; } $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; // Enables or disables automatic prefix prepending switch( $prefixMethod ) { case 'AUTO': $this->prefixMethod = 'AUTO'; break; case 'MANUAL': $this->prefixMethod = 'MANUAL'; break; case 'NONE': $this->prefixMethod = 'NONE'; break; } } /** * XML Callback to process start elements. Elements currently * processed are: QUERY. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'QUERY': // Create a new query in a SQL queryset. // Ignore this query set if a platform is specified and it's different than the // current connection platform. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->newQuery(); } else { $this->discardQuery(); } break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Line of queryset SQL data case 'QUERY': $this->buildQuery( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'QUERY': // Add the finished query to the open query set. $this->addQuery(); break; case 'SQL': $this->parent->addSQL( $this->create( $this->parent ) ); xml_set_object( $parser, $this->parent ); $this->destroy(); break; default: } } /** * Re-initializes the query. * * @return boolean TRUE */ function newQuery() { $this->query = ''; return TRUE; } /** * Discards the existing query. * * @return boolean TRUE */ function discardQuery() { unset( $this->query ); return TRUE; } /** * Appends a line to a query that is being built line by line * * @param string $data Line of SQL data or NULL to initialize a new query * @return string SQL query string. */ function buildQuery( $sql = NULL ) { if( !isset( $this->query ) OR empty( $sql ) ) { return FALSE; } $this->query .= $sql; return $this->query; } /** * Adds a completed query to the query list * * @return string SQL of added query */ function addQuery() { if( !isset( $this->query ) ) { return FALSE; } $this->queries[] = $return = trim($this->query); unset( $this->query ); return $return; } /** * Creates and returns the current query set * * @param object $xmls adoSchema object * @return array Query set */ function create( &$xmls ) { foreach( $this->queries as $id => $query ) { switch( $this->prefixMethod ) { case 'AUTO': // Enable auto prefix replacement // Process object prefix. // Evaluate SQL statements to prepend prefix to objects $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); // SELECT statements aren't working yet #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data ); case 'MANUAL': // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX. // If prefixKey is not set, we use the default constant XMLS_PREFIX if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) { // Enable prefix override $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query ); } else { // Use default replacement $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query ); } } $this->queries[$id] = trim( $query ); } // Return the query set array return $this->queries; } /** * Rebuilds the query with the prefix attached to any objects * * @param string $regex Regex used to add prefix * @param string $query SQL query string * @param string $prefix Prefix to be appended to tables, indices, etc. * @return string Prefixed SQL query string. */ function prefixQuery( $regex, $query, $prefix = NULL ) { if( !isset( $prefix ) ) { return $query; } if( preg_match( $regex, $query, $match ) ) { $preamble = $match[1]; $postamble = $match[5]; $objectList = explode( ',', $match[3] ); // $prefix = $prefix . '_'; $prefixedList = ''; foreach( $objectList as $object ) { if( $prefixedList !== '' ) { $prefixedList .= ', '; } $prefixedList .= $prefix . trim( $object ); } $query = $preamble . ' ' . $prefixedList . ' ' . $postamble; } return $query; } } /** * Loads and parses an XML file, creating an array of "ready-to-run" SQL statements * * This class is used to load and parse the XML file, to create an array of SQL statements * that can be used to build a database, and to build the database using the SQL array. * * @tutorial getting_started.pkg * * @author Richard Tango-Lowy & Dan Cech * @version $Revision: 425 $ * * @package axmls */ class adoSchema { /** * @var array Array containing SQL queries to generate all objects * @access private */ var $sqlArray; /** * @var object ADOdb connection object * @access private */ var $db; /** * @var object ADOdb Data Dictionary * @access private */ var $dict; /** * @var string Current XML element * @access private */ var $currentElement = ''; /** * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database * @access private */ var $upgrade = ''; /** * @var string Optional object prefix * @access private */ var $objectPrefix = ''; /** * @var long Original Magic Quotes Runtime value * @access private */ var $mgq; /** * @var long System debug * @access private */ var $debug; /** * @var string Regular expression to find schema version * @access private */ var $versionRegex = '//'; /** * @var string Current schema version * @access private */ var $schemaVersion; /** * @var int Success of last Schema execution */ var $success; /** * @var bool Execute SQL inline as it is generated */ var $executeInline; /** * @var bool Continue SQL execution if errors occur */ var $continueOnError; /** * Creates an adoSchema object * * Creating an adoSchema object is the first step in processing an XML schema. * The only parameter is an ADOdb database connection object, which must already * have been created. * * @param object $db ADOdb database connection object. */ function adoSchema( &$db ) { // Initialize the environment $this->mgq = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); $this->db =& $db; $this->debug = $this->db->debug; $this->dict = NewDataDictionary( $this->db ); $this->sqlArray = array(); $this->schemaVersion = XMLS_SCHEMA_VERSION; $this->executeInline( XMLS_EXECUTE_INLINE ); $this->continueOnError( XMLS_CONTINUE_ON_ERROR ); $this->setUpgradeMethod(); } /** * Sets the method to be used for upgrading an existing database * * Use this method to specify how existing database objects should be upgraded. * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to * alter each database object directly, REPLACE attempts to rebuild each object * from scratch, BEST attempts to determine the best upgrade method for each * object, and NONE disables upgrading. * * This method is not yet used by AXMLS, but exists for backward compatibility. * The ALTER method is automatically assumed when the adoSchema object is * instantiated; other upgrade methods are not currently supported. * * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE) * @returns string Upgrade method used */ function SetUpgradeMethod( $method = '' ) { if( !is_string( $method ) ) { return FALSE; } $method = strtoupper( $method ); // Handle the upgrade methods switch( $method ) { case 'ALTER': $this->upgrade = $method; break; case 'REPLACE': $this->upgrade = $method; break; case 'BEST': $this->upgrade = 'ALTER'; break; case 'NONE': $this->upgrade = 'NONE'; break; default: // Use default if no legitimate method is passed. $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD; } return $this->upgrade; } /** * Enables/disables inline SQL execution. * * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution), * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema() * to apply the schema to the database. * * @param bool $mode execute * @return bool current execution mode * * @see ParseSchema(), ExecuteSchema() */ function ExecuteInline( $mode = NULL ) { if( is_bool( $mode ) ) { $this->executeInline = $mode; } return $this->executeInline; } /** * Enables/disables SQL continue on error. * * Call this method to enable or disable continuation of SQL execution if an error occurs. * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs. * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing * of the schema will continue. * * @param bool $mode execute * @return bool current continueOnError mode * * @see addSQL(), ExecuteSchema() */ function ContinueOnError( $mode = NULL ) { if( is_bool( $mode ) ) { $this->continueOnError = $mode; } return $this->continueOnError; } /** * Loads an XML schema from a file and converts it to SQL. * * Call this method to load the specified schema (see the DTD for the proper format) from * the filesystem and generate the SQL necessary to create the database described. * @see ParseSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ function ParseSchema( $filename, $returnSchema = FALSE ) { return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); } /** * Loads an XML schema from a file and converts it to SQL. * * Call this method to load the specified schema from a file (see the DTD for the proper format) * and generate the SQL necessary to create the database described by the schema. * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. * * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString() * @see ParseSchema(), ParseSchemaString() */ function ParseSchemaFile( $filename, $returnSchema = FALSE ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { // die( 'Unable to open file' ); return FALSE; } // do version detection here if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) { return FALSE; } if ( $returnSchema ) { $xmlstring = ''; while( $data = fread( $fp, 100000 ) ) { $xmlstring .= $data; } return $xmlstring; } $this->success = 2; $xmlParser = $this->create_parser(); // Process the file while( $data = fread( $fp, 4096 ) ) { if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { die( sprintf( "XML error: %s at line %d", xml_error_string( xml_get_error_code( $xmlParser) ), xml_get_current_line_number( $xmlParser) ) ); } } xml_parser_free( $xmlParser ); return $this->sqlArray; } /** * Converts an XML schema string to SQL. * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to create the database described by the schema. * @see ParseSchema() * * @param string $xmlstring XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { return FALSE; } // do version detection here if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) { return FALSE; } if ( $returnSchema ) { return $xmlstring; } $this->success = 2; $xmlParser = $this->create_parser(); if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) { die( sprintf( "XML error: %s at line %d", xml_error_string( xml_get_error_code( $xmlParser) ), xml_get_current_line_number( $xmlParser) ) ); } xml_parser_free( $xmlParser ); return $this->sqlArray; } /** * Loads an XML schema from a file and converts it to uninstallation SQL. * * Call this method to load the specified schema (see the DTD for the proper format) from * the filesystem and generate the SQL necessary to remove the database described. * @see RemoveSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ function RemoveSchema( $filename, $returnSchema = FALSE ) { return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); } /** * Converts an XML schema string to uninstallation SQL. * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to uninstall the database described by the schema. * @see RemoveSchema() * * @param string $schema XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ function RemoveSchemaString( $schema, $returnSchema = FALSE ) { // grab current version if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { return FALSE; } return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema ); } /** * Applies the current XML schema to the database (post execution). * * Call this method to apply the current schema (generally created by calling * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, * and executing other SQL specified in the schema) after parsing. * @see ParseSchema(), ParseSchemaString(), ExecuteInline() * * @param array $sqlArray Array of SQL statements that will be applied rather than * the current schema. * @param boolean $continueOnErr Continue to apply the schema even if an error occurs. * @returns integer 0 if failure, 1 if errors, 2 if successful. */ function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) { if( !is_bool( $continueOnErr ) ) { $continueOnErr = $this->ContinueOnError(); } if( !isset( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !is_array( $sqlArray ) ) { $this->success = 0; } else { $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); } return $this->success; } /** * Returns the current SQL array. * * Call this method to fetch the array of SQL queries resulting from * ParseSchema() or ParseSchemaString(). * * @param string $format Format: HTML, TEXT, or NONE (PHP array) * @return array Array of SQL statements or FALSE if an error occurs */ function PrintSQL( $format = 'NONE' ) { $sqlArray = null; return $this->getSQL( $format, $sqlArray ); } /** * Saves the current SQL array to the local filesystem as a list of SQL queries. * * Call this method to save the array of SQL queries (generally resulting from a * parsed XML schema) to the filesystem. * * @param string $filename Path and name where the file should be saved. * @return boolean TRUE if save is successful, else FALSE. */ function SaveSQL( $filename = './schema.sql' ) { if( !isset( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !isset( $sqlArray ) ) { return FALSE; } $fp = fopen( $filename, "w" ); foreach( $sqlArray as $key => $query ) { fwrite( $fp, $query . ";\n" ); } fclose( $fp ); } /** * Create an xml parser * * @return object PHP XML parser object * * @access private */ function &create_parser() { // Create the parser $xmlParser = xml_parser_create(); xml_set_object( $xmlParser, $this ); // Initialize the XML callback functions xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' ); xml_set_character_data_handler( $xmlParser, '_tag_cdata' ); return $xmlParser; } /** * XML Callback to process start elements * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { switch( strtoupper( $tag ) ) { case 'TABLE': $this->obj = new dbTable( $this, $attributes ); xml_set_object( $parser, $this->obj ); break; case 'SQL': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->obj = new dbQuerySet( $this, $attributes ); xml_set_object( $parser, $this->obj ); } break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { } /** * XML Callback to process end elements * * @access private * @internal */ function _tag_close( &$parser, $tag ) { } /** * Converts an XML schema string to the specified DTD version. * * Call this method to convert a string containing an XML schema to a different AXMLS * DTD version. For instance, to convert a schema created for an pre-1.0 version for * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. * @see ConvertSchemaFile() * * @param string $schema String containing XML schema that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { // grab current version if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { return FALSE; } if( !isset ($newVersion) ) { $newVersion = $this->schemaVersion; } if( $version == $newVersion ) { $result = $schema; } else { $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { fwrite( $fp, $result ); fclose( $fp ); } return $result; } // compat for pre-4.3 - jlim function _file_get_contents($path) { if (function_exists('file_get_contents')) return file_get_contents($path); return join('',file($path)); } /** * Converts an XML schema file to the specified DTD version. * * Call this method to convert the specified XML schema file to a different AXMLS * DTD version. For instance, to convert a schema created for an pre-1.0 version for * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. * @see ConvertSchemaString() * * @param string $filename Name of XML schema file that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { // grab current version if( !( $version = $this->SchemaFileVersion( $filename ) ) ) { return FALSE; } if( !isset ($newVersion) ) { $newVersion = $this->schemaVersion; } if( $version == $newVersion ) { $result = _file_get_contents( $filename ); // remove unicode BOM if present if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) { $result = substr( $result, 3 ); } } else { $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { fwrite( $fp, $result ); fclose( $fp ); } return $result; } function TransformSchema( $schema, $xsl, $schematype='string' ) { // Fail if XSLT extension is not available if( ! function_exists( 'xslt_create' ) ) { return FALSE; } $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl'; // look for xsl if( !is_readable( $xsl_file ) ) { return FALSE; } switch( $schematype ) { case 'file': if( !is_readable( $schema ) ) { return FALSE; } $schema = _file_get_contents( $schema ); break; case 'string': default: if( !is_string( $schema ) ) { return FALSE; } } $arguments = array ( '/_xml' => $schema, '/_xsl' => _file_get_contents( $xsl_file ) ); // create an XSLT processor $xh = xslt_create (); // set error handler xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler')); // process the schema $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); xslt_free ($xh); return $result; } /** * Processes XSLT transformation errors * * @param object $parser XML parser object * @param integer $errno Error number * @param integer $level Error level * @param array $fields Error information fields * * @access private */ function xslt_error_handler( $parser, $errno, $level, $fields ) { if( is_array( $fields ) ) { $msg = array( 'Message Type' => ucfirst( $fields['msgtype'] ), 'Message Code' => $fields['code'], 'Message' => $fields['msg'], 'Error Number' => $errno, 'Level' => $level ); switch( $fields['URI'] ) { case 'arg:/_xml': $msg['Input'] = 'XML'; break; case 'arg:/_xsl': $msg['Input'] = 'XSL'; break; default: $msg['Input'] = $fields['URI']; } $msg['Line'] = $fields['line']; } else { $msg = array( 'Message Type' => 'Error', 'Error Number' => $errno, 'Level' => $level, 'Fields' => var_export( $fields, TRUE ) ); } $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n" . '' . "\n"; foreach( $msg as $label => $details ) { $error_details .= '' . "\n"; } $error_details .= '
' . $label . ': ' . htmlentities( $details ) . '
'; trigger_error( $error_details, E_USER_ERROR ); } /** * Returns the AXMLS Schema Version of the requested XML schema file. * * Call this method to obtain the AXMLS DTD version of the requested XML schema file. * @see SchemaStringVersion() * * @param string $filename AXMLS schema file * @return string Schema version number or FALSE on error */ function SchemaFileVersion( $filename ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { // die( 'Unable to open file' ); return FALSE; } // Process the file while( $data = fread( $fp, 4096 ) ) { if( preg_match( $this->versionRegex, $data, $matches ) ) { return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; } } return FALSE; } /** * Returns the AXMLS Schema Version of the provided XML schema string. * * Call this method to obtain the AXMLS DTD version of the provided XML schema string. * @see SchemaFileVersion() * * @param string $xmlstring XML schema string * @return string Schema version number or FALSE on error */ function SchemaStringVersion( $xmlstring ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { return FALSE; } if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) { return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; } return FALSE; } /** * Extracts an XML schema from an existing database. * * Call this method to create an XML schema string from an existing database. * If the data parameter is set to TRUE, AXMLS will include the data from the database * in the schema. * * @param boolean $data Include data in schema dump * @return string Generated XML schema */ function ExtractSchema( $data = FALSE ) { $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM ); $schema = '' . "\n" . '' . "\n"; if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) { foreach( $tables as $table ) { $schema .= ' ' . "\n"; // grab details from database $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' ); $fields = $this->db->MetaColumns( $table ); $indexes = $this->db->MetaIndexes( $table ); if( is_array( $fields ) ) { foreach( $fields as $details ) { $extra = ''; $content = array(); if( $details->max_length > 0 ) { $extra .= ' size="' . $details->max_length . '"'; } if( $details->primary_key ) { $content[] = ''; } elseif( $details->not_null ) { $content[] = ''; } if( $details->has_default ) { $content[] = ''; } if( $details->auto_increment ) { $content[] = ''; } // this stops the creation of 'R' columns, // AUTOINCREMENT is used to create auto columns $details->primary_key = 0; $type = $rs->MetaType( $details ); $schema .= ' '; if( !empty( $content ) ) { $schema .= "\n " . implode( "\n ", $content ) . "\n "; } $schema .= '' . "\n"; } } if( is_array( $indexes ) ) { foreach( $indexes as $index => $details ) { $schema .= ' ' . "\n"; if( $details['unique'] ) { $schema .= ' ' . "\n"; } foreach( $details['columns'] as $column ) { $schema .= ' ' . $column . '' . "\n"; } $schema .= ' ' . "\n"; } } if( $data ) { $rs = $this->db->Execute( 'SELECT * FROM ' . $table ); if( is_object( $rs ) ) { $schema .= ' ' . "\n"; while( $row = $rs->FetchRow() ) { foreach( $row as $key => $val ) { $row[$key] = htmlentities($val); } $schema .= ' ' . implode( '', $row ) . '' . "\n"; } $schema .= ' ' . "\n"; } } $schema .= '
' . "\n"; } } $this->db->SetFetchMode( $old_mode ); $schema .= '
'; return $schema; } /** * Sets a prefix for database objects * * Call this method to set a standard prefix that will be prepended to all database tables * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix. * * @param string $prefix Prefix that will be prepended. * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix. * @return boolean TRUE if successful, else FALSE */ function SetPrefix( $prefix = '', $underscore = TRUE ) { switch( TRUE ) { // clear prefix case empty( $prefix ): logMsg( 'Cleared prefix' ); $this->objectPrefix = ''; return TRUE; // prefix too long case strlen( $prefix ) > XMLS_PREFIX_MAXLEN: // prefix contains invalid characters case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ): logMsg( 'Invalid prefix: ' . $prefix ); return FALSE; } if( $underscore AND substr( $prefix, -1 ) != '_' ) { $prefix .= '_'; } // prefix valid logMsg( 'Set prefix: ' . $prefix ); $this->objectPrefix = $prefix; return TRUE; } /** * Returns an object name with the current prefix prepended. * * @param string $name Name * @return string Prefixed name * * @access private */ function prefix( $name = '' ) { // if prefix is set if( !empty( $this->objectPrefix ) ) { // Prepend the object prefix to the table name // prepend after quote if used return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name ); } // No prefix set. Use name provided. return $name; } /** * Checks if element references a specific platform * * @param string $platform Requested platform * @returns boolean TRUE if platform check succeeds * * @access private */ function supportedPlatform( $platform = NULL ) { $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/'; if( !isset( $platform ) OR preg_match( $regex, $platform ) ) { logMsg( "Platform $platform is supported" ); return TRUE; } else { logMsg( "Platform $platform is NOT supported" ); return FALSE; } } /** * Clears the array of generated SQL. * * @access private */ function clearSQL() { $this->sqlArray = array(); } /** * Adds SQL into the SQL array. * * @param mixed $sql SQL to Add * @return boolean TRUE if successful, else FALSE. * * @access private */ function addSQL( $sql = NULL ) { if( is_array( $sql ) ) { foreach( $sql as $line ) { $this->addSQL( $line ); } return TRUE; } if( is_string( $sql ) ) { $this->sqlArray[] = $sql; // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL. if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) { $saved = $this->db->debug; $this->db->debug = $this->debug; $ok = $this->db->Execute( $sql ); $this->db->debug = $saved; if( !$ok ) { if( $this->debug ) { ADOConnection::outp( $this->db->ErrorMsg() ); } $this->success = 1; } } return TRUE; } return FALSE; } /** * Gets the SQL array in the specified format. * * @param string $format Format * @return mixed SQL * * @access private */ function getSQL( $format = NULL, $sqlArray = NULL ) { if( !is_array( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !is_array( $sqlArray ) ) { return FALSE; } switch( strtolower( $format ) ) { case 'string': case 'text': return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : ''; case'html': return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : ''; } return $this->sqlArray; } /** * Destroys an adoSchema object. * * Call this method to clean up after an adoSchema object that is no longer in use. * @deprecated adoSchema now cleans up automatically. */ function Destroy() { set_magic_quotes_runtime( $this->mgq ); unset( $this ); } } /** * Message logging function * * @access private */ function logMsg( $msg, $title = NULL, $force = FALSE ) { if( XMLS_DEBUG or $force ) { echo '
';
		
		if( isset( $title ) ) {
			echo '

' . htmlentities( $title ) . '

'; } if( is_object( $this ) ) { echo '[' . get_class( $this ) . '] '; } print_r( $msg ); echo '
'; } } ?>phpgacl-3.3.7/adodb/xmlschema.dtd0100644025754300001440000000265510476657245015630 0ustar ipsousers ] >phpgacl-3.3.7/adodb/server.php0100644025754300001440000000437510476657245015172 0ustar ipsousersConnect($host,$uid,$pwd,$database)) err($conn->ErrorNo(). $sep . $conn->ErrorMsg()); $sql = undomq($_REQUEST['sql']); if (isset($_REQUEST['fetch'])) $ADODB_FETCH_MODE = $_REQUEST['fetch']; if (isset($_REQUEST['nrows'])) { $nrows = $_REQUEST['nrows']; $offset = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : -1; $rs = $conn->SelectLimit($sql,$nrows,$offset); } else $rs = $conn->Execute($sql); if ($rs){ //$rs->timeToLive = 1; echo _rs2serialize($rs,$conn,$sql); $rs->Close(); } else err($conn->ErrorNo(). $sep .$conn->ErrorMsg()); ?>phpgacl-3.3.7/adodb/adodb-active-record.inc.php0100644025754300001440000003510410476657431020221 0ustar ipsousers_dbat $_ADODB_ACTIVE_DBS = array(); class ADODB_Active_DB { var $db; // ADOConnection var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename } class ADODB_Active_Table { var $name; // table name var $flds; // assoc array of adofieldobjs, indexed by fieldname var $keys; // assoc array of primary keys, indexed by fieldname var $_created; // only used when stored as a cached file } // returns index into $_ADODB_ACTIVE_DBS function ADODB_SetDatabaseAdapter(&$db) { global $_ADODB_ACTIVE_DBS; foreach($_ADODB_ACTIVE_DBS as $k => $d) { if ($d->db == $db) return $k; } $obj = new ADODB_Active_DB(); $obj->db =& $db; $obj->tables = array(); $_ADODB_ACTIVE_DBS[] = $obj; return sizeof($_ADODB_ACTIVE_DBS)-1; } class ADODB_Active_Record { var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat] var $_table; // tablename, if set in class definition then use it as table name var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat] var $_where; // where clause set in Load() var $_saved = false; // indicates whether data is already inserted. var $_lasterr = false; // last error message var $_original = false; // the original values loaded or inserted, refreshed on update // should be static function SetDatabaseAdapter(&$db) { return ADODB_SetDatabaseAdapter($db); } // php4 constructor function ADODB_Active_Record($table = false, $pkeyarr=false, $db=false) { ADODB_Active_Record::__construct($table,$pkeyarr,$db); } // php5 constructor function __construct($table = false, $pkeyarr=false, $db=false) { global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS; if ($db == false && is_object($pkeyarr)) { $db = $pkeyarr; $pkeyarr = false; } if (!$table) { if (!empty($this->_table)) $table = $this->_table; else $table = $this->_pluralize(get_class($this)); } if ($db) { $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db); } else $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1; if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor'); $this->_table = $table; $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future $this->UpdateActiveTable($pkeyarr); } function __wakeup() { $class = get_class($this); new $class; } function _pluralize($table) { $ut = strtoupper($table); $len = strlen($table); $lastc = $ut[$len-1]; $lastc2 = substr($ut,$len-2); switch ($lastc) { case 'S': return $table.'es'; case 'Y': return substr($table,0,$len-1).'ies'; case 'X': return $table.'es'; case 'H': if ($lastc2 == 'CH' || $lastc2 == 'SH') return $table.'es'; default: return $table.'s'; } } ////////////////////////////////// // update metadata function UpdateActiveTable($pkeys=false,$forceUpdate=false) { global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; $activedb =& $_ADODB_ACTIVE_DBS[$this->_dbat]; $table = $this->_table; $tables = $activedb->tables; $tableat = $this->_tableat; if (!$forceUpdate && !empty($tables[$tableat])) { $tobj =& $tables[$tableat]; foreach($tobj->flds as $name => $fld) $this->$name = null; return; } $db =& $activedb->db; $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { $fp = fopen($fname,'r'); @flock($fp, LOCK_SH); $acttab = unserialize(fread($fp,100000)); fclose($fp); if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { // abs(rand()) randomizes deletion, reducing contention to delete/refresh file // ideally, you should cache at least 32 secs $activedb->tables[$table] = $acttab; //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); return; } else if ($db->debug) { ADOConnection::outp("Refreshing cached active record file: $fname"); } } $activetab = new ADODB_Active_Table(); $activetab->name = $table; $cols = $db->MetaColumns($table); if (!$cols) { $this->Error("Invalid table name: $table",'UpdateActiveTable'); return false; } $fld = reset($cols); if (!$pkeys) { if (isset($fld->primary_key)) { $pkeys = array(); foreach($cols as $name => $fld) { if (!empty($fld->primary_key)) $pkeys[] = $name; } } else $pkeys = $this->GetPrimaryKeys($db, $table); } if (empty($pkeys)) { $this->Error("No primary key found for table $table",'UpdateActiveTable'); return false; } $attr = array(); $keys = array(); switch($ADODB_ASSOC_CASE) { case 0: foreach($cols as $name => $fldobj) { $name = strtolower($name); $this->$name = null; $attr[$name] = $fldobj; } foreach($pkeys as $k => $name) { $keys[strtolower($name)] = strtolower($name); } break; case 1: foreach($cols as $name => $fldobj) { $name = strtoupper($name); $this->$name = null; $attr[$name] = $fldobj; } foreach($pkeys as $k => $name) { $keys[strtoupper($name)] = strtoupper($name); } break; default: foreach($cols as $name => $fldobj) { $name = ($name); $this->$name = null; $attr[$name] = $fldobj; } foreach($pkeys as $k => $name) { $keys[$name] = ($name); } break; } $activetab->keys = $keys; $activetab->flds = $attr; if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) { $activetab->_created = time(); $s = serialize($activetab); if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php'); adodb_write_file($fname,$s); } $activedb->tables[$table] = $activetab; } function GetPrimaryKeys(&$db, $table) { return $db->MetaPrimaryKeys($table); } // error handler for both PHP4+5. function Error($err,$fn) { global $_ADODB_ACTIVE_DBS; $fn = get_class($this).'::'.$fn; $this->_lasterr = $fn.': '.$err; if ($this->_dbat < 0) $db = false; else { $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $db =& $activedb->db; } if (function_exists('adodb_throw')) { if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); } else if (!$db || $db->debug) ADOConnection::outp($this->_lasterr); } // return last error message function ErrorMsg() { if (!function_exists('adodb_throw')) { if ($this->_dbat < 0) $db = false; else $db = $this->DB(); // last error could be database error too if ($db && $db->ErrorMsg()) return $db->ErrorMsg(); } return $this->_lasterr; } // retrieve ADOConnection from _ADODB_Active_DBs function &DB() { global $_ADODB_ACTIVE_DBS; if ($this->_dbat < 0) { $false = false; $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB"); return $false; } $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $db =& $activedb->db; return $db; } // retrieve ADODB_Active_Table function &TableInfo() { global $_ADODB_ACTIVE_DBS; $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $table =& $activedb->tables[$this->_tableat]; return $table; } // set a numeric array (using natural table field ordering) as object properties function Set(&$row) { $db =& $this->DB(); if (!$row) { $this->_saved = false; return false; } $this->_saved = true; $table =& $this->TableInfo(); if (sizeof($table->flds) != sizeof($row)) { $this->Error("Table structure of $this->_table has changed","Load"); return false; } $cnt = 0; foreach($table->flds as $name=>$fld) { $this->$name = $row[$cnt]; $cnt += 1; } $this->_original = $row; return true; } // get last inserted id for INSERT function LastInsertID(&$db,$fieldname) { if ($db->hasInsertID) $val = $db->Insert_ID($this->_table,$fieldname); else $val = false; if (is_null($val) || $val === false) { // this might not work reliably in multi-user environment return $db->GetOne("select max(".$fieldname.") from ".$this->_table); } return $val; } // quote data in where clause function doquote(&$db, $val,$t) { switch($t) { case 'D': case 'T': if (empty($val)) return 'null'; case 'C': case 'X': if (is_null($val)) return 'null'; if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") { return $db->qstr($val); break; } default: return $val; break; } } // generate where clause for an UPDATE/SELECT function GenWhere(&$db, &$table) { $keys = $table->keys; $parr = array(); foreach($keys as $k) { $f = $table->flds[$k]; if ($f) { $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); } } return implode(' and ', $parr); } //------------------------------------------------------------ Public functions below function Load($where,$bindarr=false) { $db =& $this->DB(); if (!$db) return false; $this->_where = $where; $save = $db->SetFetchMode(ADODB_FETCH_NUM); $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr); $db->SetFetchMode($save); return $this->Set($row); } // false on error function Save() { if ($this->_saved) $ok = $this->Update(); else $ok = $this->Insert(); return $ok; } // false on error function Insert() { $db =& $this->DB(); if (!$db) return false; $cnt = 0; $table =& $this->TableInfo(); $valarr = array(); $names = array(); $valstr = array(); foreach($table->flds as $name=>$fld) { $val = $this->$name; if(!is_null($val) || !array_key_exists($name, $table->keys)) { $valarr[] = $val; $names[] = $name; $valstr[] = $db->Param($cnt); $cnt += 1; } } if (empty($names)){ foreach($table->flds as $name=>$fld) { $valarr[] = null; $names[] = $name; $valstr[] = $db->Param($cnt); $cnt += 1; } } $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; $ok = $db->Execute($sql,$valarr); if ($ok) { $this->_saved = true; $autoinc = false; foreach($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; } } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); $this->$k = $this->LastInsertID($db,$k); } } $this->_original = $valarr; return !empty($ok); } function Delete() { $db =& $this->DB(); if (!$db) return false; $table =& $this->TableInfo(); $where = $this->GenWhere($db,$table); $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; $ok = $db->Execute($sql); return $ok ? true : false; } // returns an array of active record objects function &Find($whereOrderBy,$bindarr=false,$pkeysArr=false) { $db =& $this->DB(); if (!$db || empty($this->_table)) return false; $arr =& $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr); return $arr; } // returns 0 on error, 1 on update, 2 on insert function Replace() { global $ADODB_ASSOC_CASE; $db =& $this->DB(); if (!$db) return false; $table =& $this->TableInfo(); $pkey = $table->keys; foreach($table->flds as $name=>$fld) { $val = $this->$name; /* if (is_null($val)) { if (isset($fld->not_null) && $fld->not_null) { if (isset($fld->default_value) && strlen($fld->default_value)) continue; else { $this->Error("Cannot update null into $name","Replace"); return false; } } }*/ if (is_null($val) && !empty($fld->auto_increment)) { continue; } $t = $db->MetaType($fld->type); $arr[$name] = $this->doquote($db,$val,$t); $valarr[] = $val; } if (!is_array($pkey)) $pkey = array($pkey); if ($ADODB_ASSOC_CASE == 0) foreach($pkey as $k => $v) $pkey[$k] = strtolower($v); elseif ($ADODB_ASSOC_CASE == 0) foreach($pkey as $k => $v) $pkey[$k] = strtoupper($v); $ok = $db->Replace($this->_table,$arr,$pkey); if ($ok) { $this->_saved = true; // 1= update 2=insert if ($ok == 2) { $autoinc = false; foreach($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; } } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); $this->$k = $this->LastInsertID($db,$k); } } $this->_original =& $valarr; } return $ok; } // returns 0 on error, 1 on update, -1 if no change in data (no update) function Update() { $db =& $this->DB(); if (!$db) return false; $table =& $this->TableInfo(); $where = $this->GenWhere($db, $table); if (!$where) { $this->error("Where missing for table $table", "Update"); return false; } $valarr = array(); $neworig = array(); $pairs = array(); $i = -1; $cnt = 0; foreach($table->flds as $name=>$fld) { $i += 1; $val = $this->$name; $neworig[] = $val; if (isset($table->keys[$name])) { continue; } if (is_null($val)) { if (isset($fld->not_null) && $fld->not_null) { if (isset($fld->default_value) && strlen($fld->default_value)) continue; else { $this->Error("Cannot set field $name to NULL","Update"); return false; } } } if (isset($this->_original[$i]) && $val == $this->_original[$i]) { continue; } $valarr[] = $val; $pairs[] = $name.'='.$db->Param($cnt); $cnt += 1; } if (!$cnt) return -1; $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; $ok = $db->Execute($sql,$valarr); if ($ok) { $this->_original =& $neworig; return 1; } return 0; } function GetAttributeNames() { $table =& $this->TableInfo(); if (!$table) return false; return array_keys($table->flds); } }; ?>phpgacl-3.3.7/adodb/adodb-pear.inc.php0100644025754300001440000002253410476657245016427 0ustar ipsousers | * and Tomas V.V.Cox . Portions (c)1997-2002 The PHP Group. */ /* We support: DB_Common --------- query - returns PEAR_Error on error limitQuery - return PEAR_Error on error prepare - does not return PEAR_Error on error execute - does not return PEAR_Error on error setFetchMode - supports ASSOC and ORDERED errorNative quote nextID disconnect getOne getAssoc getRow getCol getAll DB_Result --------- numRows - returns -1 if not supported numCols fetchInto - does not support passing of fetchmode fetchRows - does not support passing of fetchmode free */ define('ADODB_PEAR',dirname(__FILE__)); include_once "PEAR.php"; include_once ADODB_PEAR."/adodb-errorpear.inc.php"; include_once ADODB_PEAR."/adodb.inc.php"; if (!defined('DB_OK')) { define("DB_OK", 1); define("DB_ERROR",-1); // autoExecute constants define('DB_AUTOQUERY_INSERT', 1); define('DB_AUTOQUERY_UPDATE', 2); /** * This is a special constant that tells DB the user hasn't specified * any particular get mode, so the default should be used. */ define('DB_FETCHMODE_DEFAULT', 0); /** * Column data indexed by numbers, ordered from 0 and up */ define('DB_FETCHMODE_ORDERED', 1); /** * Column data indexed by column names */ define('DB_FETCHMODE_ASSOC', 2); /* for compatibility */ define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); /** * these are constants for the tableInfo-function * they are bitwised or'ed. so if there are more constants to be defined * in the future, adjust DB_TABLEINFO_FULL accordingly */ define('DB_TABLEINFO_ORDER', 1); define('DB_TABLEINFO_ORDERTABLE', 2); define('DB_TABLEINFO_FULL', 3); } /** * The main "DB" class is simply a container class with some static * methods for creating DB objects as well as some utility functions * common to all parts of DB. * */ class DB { /** * Create a new DB object for the specified database type * * @param $type string database type, for example "mysql" * * @return object a newly created DB object, or a DB error code on * error */ function &factory($type) { include_once(ADODB_DIR."/drivers/adodb-$type.inc.php"); $obj = &NewADOConnection($type); if (!is_object($obj)) $obj =& new PEAR_Error('Unknown Database Driver: '.$dsninfo['phptype'],-1); return $obj; } /** * Create a new DB object and connect to the specified database * * @param $dsn mixed "data source name", see the DB::parseDSN * method for a description of the dsn format. Can also be * specified as an array of the format returned by DB::parseDSN. * * @param $options mixed if boolean (or scalar), tells whether * this connection should be persistent (for backends that support * this). This parameter can also be an array of options, see * DB_common::setOption for more information on connection * options. * * @return object a newly created DB connection object, or a DB * error object on error * * @see DB::parseDSN * @see DB::isError */ function &connect($dsn, $options = false) { if (is_array($dsn)) { $dsninfo = $dsn; } else { $dsninfo = DB::parseDSN($dsn); } switch ($dsninfo["phptype"]) { case 'pgsql': $type = 'postgres7'; break; case 'ifx': $type = 'informix9'; break; default: $type = $dsninfo["phptype"]; break; } if (is_array($options) && isset($options["debug"]) && $options["debug"] >= 2) { // expose php errors with sufficient debug level @include_once("adodb-$type.inc.php"); } else { @include_once("adodb-$type.inc.php"); } @$obj =& NewADOConnection($type); if (!is_object($obj)) { $obj =& new PEAR_Error('Unknown Database Driver: '.$dsninfo['phptype'],-1); return $obj; } if (is_array($options)) { foreach($options as $k => $v) { switch(strtolower($k)) { case 'persist': case 'persistent': $persist = $v; break; #ibase case 'dialect': $obj->dialect = $v; break; case 'charset': $obj->charset = $v; break; case 'buffers': $obj->buffers = $v; break; #ado case 'charpage': $obj->charPage = $v; break; #mysql case 'clientflags': $obj->clientFlags = $v; break; } } } else { $persist = false; } if (isset($dsninfo['socket'])) $dsninfo['hostspec'] .= ':'.$dsninfo['socket']; else if (isset($dsninfo['port'])) $dsninfo['hostspec'] .= ':'.$dsninfo['port']; if($persist) $ok = $obj->PConnect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']); else $ok = $obj->Connect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']); if (!$ok) $obj = ADODB_PEAR_Error(); return $obj; } /** * Return the DB API version * * @return int the DB API version number */ function apiVersion() { return 2; } /** * Tell whether a result code from a DB method is an error * * @param $value int result code * * @return bool whether $value is an error */ function isError($value) { if (!is_object($value)) return false; $class = get_class($value); return $class == 'pear_error' || is_subclass_of($value, 'pear_error') || $class == 'db_error' || is_subclass_of($value, 'db_error'); } /** * Tell whether a result code from a DB method is a warning. * Warnings differ from errors in that they are generated by DB, * and are not fatal. * * @param $value mixed result value * * @return bool whether $value is a warning */ function isWarning($value) { return false; /* return is_object($value) && (get_class( $value ) == "db_warning" || is_subclass_of($value, "db_warning"));*/ } /** * Parse a data source name * * @param $dsn string Data Source Name to be parsed * * @return array an associative array with the following keys: * * phptype: Database backend used in PHP (mysql, odbc etc.) * dbsyntax: Database used with regards to SQL syntax etc. * protocol: Communication protocol to use (tcp, unix etc.) * hostspec: Host specification (hostname[:port]) * database: Database to use on the DBMS server * username: User name for login * password: Password for login * * The format of the supplied DSN is in its fullest form: * * phptype(dbsyntax)://username:password@protocol+hostspec/database * * Most variations are allowed: * * phptype://username:password@protocol+hostspec:110//usr/db_file.db * phptype://username:password@hostspec/database_name * phptype://username:password@hostspec * phptype://username@hostspec * phptype://hostspec/database * phptype://hostspec * phptype(dbsyntax) * phptype * * @author Tomas V.V.Cox */ function parseDSN($dsn) { if (is_array($dsn)) { return $dsn; } $parsed = array( 'phptype' => false, 'dbsyntax' => false, 'protocol' => false, 'hostspec' => false, 'database' => false, 'username' => false, 'password' => false ); // Find phptype and dbsyntax if (($pos = strpos($dsn, '://')) !== false) { $str = substr($dsn, 0, $pos); $dsn = substr($dsn, $pos + 3); } else { $str = $dsn; $dsn = NULL; } // Get phptype and dbsyntax // $str => phptype(dbsyntax) if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { $parsed['phptype'] = $arr[1]; $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2]; } else { $parsed['phptype'] = $str; $parsed['dbsyntax'] = $str; } if (empty($dsn)) { return $parsed; } // Get (if found): username and password // $dsn => username:password@protocol+hostspec/database if (($at = strpos($dsn,'@')) !== false) { $str = substr($dsn, 0, $at); $dsn = substr($dsn, $at + 1); if (($pos = strpos($str, ':')) !== false) { $parsed['username'] = urldecode(substr($str, 0, $pos)); $parsed['password'] = urldecode(substr($str, $pos + 1)); } else { $parsed['username'] = urldecode($str); } } // Find protocol and hostspec // $dsn => protocol+hostspec/database if (($pos=strpos($dsn, '/')) !== false) { $str = substr($dsn, 0, $pos); $dsn = substr($dsn, $pos + 1); } else { $str = $dsn; $dsn = NULL; } // Get protocol + hostspec // $str => protocol+hostspec if (($pos=strpos($str, '+')) !== false) { $parsed['protocol'] = substr($str, 0, $pos); $parsed['hostspec'] = urldecode(substr($str, $pos + 1)); } else { $parsed['hostspec'] = urldecode($str); } // Get dabase if any // $dsn => database if (!empty($dsn)) { $parsed['database'] = $dsn; } return $parsed; } /** * Load a PHP database extension if it is not loaded already. * * @access public * * @param $name the base name of the extension (without the .so or * .dll suffix) * * @return bool true if the extension was already or successfully * loaded, false if it could not be loaded */ function assertExtension($name) { if (!extension_loaded($name)) { $dlext = (strncmp(PHP_OS,'WIN',3) === 0) ? '.dll' : '.so'; @dl($name . $dlext); } if (!extension_loaded($name)) { return false; } return true; } } ?>phpgacl-3.3.7/adodb/license.txt0100644025754300001440000006245210074023170015310 0ustar ipsousersADOdb is dual licensed using BSD and LGPL. In plain English, you do not need to distribute your application in source code form, nor do you need to distribute ADOdb source code, provided you follow the rest of terms of the BSD license. For more info about ADOdb, visit http://adodb.sourceforge.net/ BSD Style-License ================= Copyright (c) 2000, 2001, 2002, 2003, 2004 John Lim All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 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. Neither the name of the John Lim nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. DISCLAIMER: 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 JOHN LIM 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. ========================================================== GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONSphpgacl-3.3.7/adodb/adodb-time.inc.php0100644025754300001440000011557310476657245016444 0ustar ipsousers 4 digit year conversion. The maximum is billions of years in the future, but this is a theoretical limit as the computation of that year would take too long with the current implementation of adodb_mktime(). This library replaces native functions as follows:
	
	getdate()  with  adodb_getdate()
	date()     with  adodb_date() 
	gmdate()   with  adodb_gmdate()
	mktime()   with  adodb_mktime()
	gmmktime() with  adodb_gmmktime()
	strftime() with  adodb_strftime()
	strftime() with  adodb_gmstrftime()
The parameters are identical, except that adodb_date() accepts a subset of date()'s field formats. Mktime() will convert from local time to GMT, and date() will convert from GMT to local time, but daylight savings is not handled currently. This library is independant of the rest of ADOdb, and can be used as standalone code. PERFORMANCE For high speed, this library uses the native date functions where possible, and only switches to PHP code when the dates fall outside the 32-bit signed integer range. GREGORIAN CORRECTION Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). Since 0.06, we handle this correctly, so: adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) == 24 * 3600 (1 day) ============================================================================= COPYRIGHT (c) 2003-2005 John Lim and released under BSD-style license except for code by jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year and originally found at http://www.php.net/manual/en/function.mktime.php ============================================================================= BUG REPORTS These should be posted to the ADOdb forums at http://phplens.com/lens/lensforum/topics.php?id=4 ============================================================================= FUNCTION DESCRIPTIONS ** FUNCTION adodb_getdate($date=false) Returns an array containing date information, as getdate(), but supports dates greater than 1901 to 2038. The local date/time format is derived from a heuristic the first time adodb_getdate is called. ** FUNCTION adodb_date($fmt, $timestamp = false) Convert a timestamp to a formatted local date. If $timestamp is not defined, the current timestamp is used. Unlike the function date(), it supports dates outside the 1901 to 2038 range. The format fields that adodb_date supports:
	a - "am" or "pm" 
	A - "AM" or "PM" 
	d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" 
	D - day of the week, textual, 3 letters; e.g. "Fri" 
	F - month, textual, long; e.g. "January" 
	g - hour, 12-hour format without leading zeros; i.e. "1" to "12" 
	G - hour, 24-hour format without leading zeros; i.e. "0" to "23" 
	h - hour, 12-hour format; i.e. "01" to "12" 
	H - hour, 24-hour format; i.e. "00" to "23" 
	i - minutes; i.e. "00" to "59" 
	j - day of the month without leading zeros; i.e. "1" to "31" 
	l (lowercase 'L') - day of the week, textual, long; e.g. "Friday"  
	L - boolean for whether it is a leap year; i.e. "0" or "1" 
	m - month; i.e. "01" to "12" 
	M - month, textual, 3 letters; e.g. "Jan" 
	n - month without leading zeros; i.e. "1" to "12" 
	O - Difference to Greenwich time in hours; e.g. "+0200" 
	Q - Quarter, as in 1, 2, 3, 4 
	r - RFC 2822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" 
	s - seconds; i.e. "00" to "59" 
	S - English ordinal suffix for the day of the month, 2 characters; 
	   			i.e. "st", "nd", "rd" or "th" 
	t - number of days in the given month; i.e. "28" to "31"
	T - Timezone setting of this machine; e.g. "EST" or "MDT" 
	U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)  
	w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) 
	Y - year, 4 digits; e.g. "1999" 
	y - year, 2 digits; e.g. "99" 
	z - day of the year; i.e. "0" to "365" 
	Z - timezone offset in seconds (i.e. "-43200" to "43200"). 
	   			The offset for timezones west of UTC is always negative, 
				and for those east of UTC is always positive. 
Unsupported:
	B - Swatch Internet time 
	I (capital i) - "1" if Daylight Savings Time, "0" otherwise.
	W - ISO-8601 week number of year, weeks starting on Monday 

** FUNCTION adodb_date2($fmt, $isoDateString = false) Same as adodb_date, but 2nd parameter accepts iso date, eg. adodb_date2('d-M-Y H:i','2003-12-25 13:01:34'); ** FUNCTION adodb_gmdate($fmt, $timestamp = false) Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the current timestamp is used. Unlike the function date(), it supports dates outside the 1901 to 2038 range. ** FUNCTION adodb_mktime($hr, $min, $sec[, $month, $day, $year]) Converts a local date to a unix timestamp. Unlike the function mktime(), it supports dates outside the 1901 to 2038 range. All parameters are optional. ** FUNCTION adodb_gmmktime($hr, $min, $sec [, $month, $day, $year]) Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters are currently compulsory. ** FUNCTION adodb_gmstrftime($fmt, $timestamp = false) Convert a timestamp to a formatted GMT date. ** FUNCTION adodb_strftime($fmt, $timestamp = false) Convert a timestamp to a formatted local date. Internally converts $fmt into adodb_date format, then echo result. For best results, you can define the local date format yourself. Define a global variable $ADODB_DATE_LOCALE which is an array, 1st element is date format using adodb_date syntax, and 2nd element is the time format, also in adodb_date syntax. eg. $ADODB_DATE_LOCALE = array('d/m/Y','H:i:s'); Supported format codes:
	%a - abbreviated weekday name according to the current locale 
	%A - full weekday name according to the current locale 
	%b - abbreviated month name according to the current locale 
	%B - full month name according to the current locale 
	%c - preferred date and time representation for the current locale 
	%d - day of the month as a decimal number (range 01 to 31) 
	%D - same as %m/%d/%y 
	%e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31') 
	%h - same as %b
	%H - hour as a decimal number using a 24-hour clock (range 00 to 23) 
	%I - hour as a decimal number using a 12-hour clock (range 01 to 12) 
	%m - month as a decimal number (range 01 to 12) 
	%M - minute as a decimal number 
	%n - newline character 
	%p - either `am' or `pm' according to the given time value, or the corresponding strings for the current locale 
	%r - time in a.m. and p.m. notation 
	%R - time in 24 hour notation 
	%S - second as a decimal number 
	%t - tab character 
	%T - current time, equal to %H:%M:%S 
	%x - preferred date representation for the current locale without the time 
	%X - preferred time representation for the current locale without the date 
	%y - year as a decimal number without a century (range 00 to 99) 
	%Y - year as a decimal number including the century 
	%Z - time zone or name or abbreviation 
	%% - a literal `%' character 
Unsupported codes:
	%C - century number (the year divided by 100 and truncated to an integer, range 00 to 99) 
	%g - like %G, but without the century. 
	%G - The 4-digit year corresponding to the ISO week number (see %V). 
	     This has the same format and value as %Y, except that if the ISO week number belongs 
		 to the previous or next year, that year is used instead. 
	%j - day of the year as a decimal number (range 001 to 366) 
	%u - weekday as a decimal number [1,7], with 1 representing Monday 
	%U - week number of the current year as a decimal number, starting 
	    with the first Sunday as the first day of the first week 
	%V - The ISO 8601:1988 week number of the current year as a decimal number, 
	     range 01 to 53, where week 1 is the first week that has at least 4 days in the 
		 current year, and with Monday as the first day of the week. (Use %G or %g for 
		 the year component that corresponds to the week number for the specified timestamp.) 
	%w - day of the week as a decimal, Sunday being 0 
	%W - week number of the current year as a decimal number, starting with the 
	     first Monday as the first day of the first week 
============================================================================= NOTES Useful url for generating test timestamps: http://www.4webhelp.net/us/timestamp.php Possible future optimizations include a. Using an algorithm similar to Plauger's in "The Standard C Library" (page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not work outside 32-bit signed range, so i decided not to implement it. b. Implement daylight savings, which looks awfully complicated, see http://webexhibits.org/daylightsaving/ CHANGELOG - 19 March 2006 0.24 Changed strftime() locale detection, because some locales prepend the day of week to the date when %c is used. - 10 Feb 2006 0.23 PHP5 compat: when we detect PHP5, the RFC2822 format for gmt 0000hrs is changed from -0000 to +0000. In PHP4, we will still use -0000 for 100% compat with PHP4. - 08 Sept 2005 0.22 In adodb_date2(), $is_gmt not supported properly. Fixed. - 18 July 2005 0.21 In PHP 4.3.11, the 'r' format has changed. Leading 0 in day is added. Changed for compat. Added support for negative months in adodb_mktime(). - 24 Feb 2005 0.20 Added limited strftime/gmstrftime support. x10 improvement in performance of adodb_date(). - 21 Dec 2004 0.17 In adodb_getdate(), the timestamp was accidentally converted to gmt when $is_gmt is false. Also adodb_mktime(0,0,0) did not work properly. Both fixed thx Mauro. - 17 Nov 2004 0.16 Removed intval typecast in adodb_mktime() for secs, allowing: adodb_mktime(0,0,0 + 2236672153,1,1,1934); Suggested by Ryan. - 18 July 2004 0.15 All params in adodb_mktime were formerly compulsory. Now only the hour, min, secs is compulsory. This brings it more in line with mktime (still not identical). - 23 June 2004 0.14 Allow you to define your own daylights savings function, adodb_daylight_sv. If the function is defined (somewhere in an include), then you can correct for daylights savings. In this example, we apply daylights savings in June or July, adding one hour. This is extremely unrealistic as it does not take into account time-zone, geographic location, current year. function adodb_daylight_sv(&$arr, $is_gmt) { if ($is_gmt) return; $m = $arr['mon']; if ($m == 6 || $m == 7) $arr['hours'] += 1; } This is only called by adodb_date() and not by adodb_mktime(). The format of $arr is Array ( [seconds] => 0 [minutes] => 0 [hours] => 0 [mday] => 1 # day of month, eg 1st day of the month [mon] => 2 # month (eg. Feb) [year] => 2102 [yday] => 31 # days in current year [leap] => # true if leap year [ndays] => 28 # no of days in current month ) - 28 Apr 2004 0.13 Fixed adodb_date to properly support $is_gmt. Thx to Dimitar Angelov. - 20 Mar 2004 0.12 Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32. - 26 Oct 2003 0.11 Because of daylight savings problems (some systems apply daylight savings to January!!!), changed adodb_get_gmt_diff() to ignore daylight savings. - 9 Aug 2003 0.10 Fixed bug with dates after 2038. See http://phplens.com/lens/lensforum/msgs.php?id=6980 - 1 July 2003 0.09 Added support for Q (Quarter). Added adodb_date2(), which accepts ISO date in 2nd param - 3 March 2003 0.08 Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS if you want PHP to handle negative timestamps between 1901 to 1969. - 27 Feb 2003 0.07 All negative numbers handled by adodb now because of RH 7.3+ problems. See http://bugs.php.net/bug.php?id=20048&edit=2 - 4 Feb 2003 0.06 Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates are now correctly handled. - 29 Jan 2003 0.05 Leap year checking differs under Julian calendar (pre 1582). Also leap year code optimized by checking for most common case first. We also handle month overflow correctly in mktime (eg month set to 13). Day overflow for less than one month's days is supported. - 28 Jan 2003 0.04 Gregorian correction handled. In PHP5, we might throw an error if mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. - 27 Jan 2003 0.03 Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. Fixed calculation of days since start of year for <1970. - 27 Jan 2003 0.02 Changed _adodb_getdate() to inline leap year checking for better performance. Fixed problem with time-zones west of GMT +0000. - 24 Jan 2003 0.01 First implementation. */ /* Initialization */ /* Version Number */ define('ADODB_DATE_VERSION',0.24); /* This code was originally for windows. But apparently this problem happens also with Linux, RH 7.3 and later! glibc-2.2.5-34 and greater has been changed to return -1 for dates < 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 References: http://bugs.php.net/bug.php?id=20048&edit=2 http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html */ if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); function adodb_date_test_date($y1,$m,$d=13) { $t = adodb_mktime(0,0,0,$m,$d,$y1); $rez = adodb_date('Y-n-j H:i:s',$t); if ("$y1-$m-$d 00:00:00" != $rez) { print "$y1 error, expected=$y1-$m-$d 00:00:00, adodb=$rez
"; return false; } return true; } function adodb_date_test_strftime($fmt) { $s1 = strftime($fmt); $s2 = adodb_strftime($fmt); if ($s1 == $s2) return true; echo "error for $fmt, strftime=$s1, $adodb=$s2
"; return false; } /** Test Suite */ function adodb_date_test() { error_reporting(E_ALL); print "

Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION.' PHP='.PHP_VERSION."

"; @set_time_limit(0); $fail = false; // This flag disables calling of PHP native functions, so we can properly test the code if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1); adodb_date_test_strftime('%Y %m %x %X'); adodb_date_test_strftime("%A %d %B %Y"); adodb_date_test_strftime("%H %M S"); $t = adodb_mktime(0,0,0); if (!(adodb_date('Y-m-d') == date('Y-m-d'))) print 'Error in '.adodb_mktime(0,0,0).'
'; $t = adodb_mktime(0,0,0,6,1,2102); if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'
'; $t = adodb_mktime(0,0,0,2,1,2102); if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'
'; print "

Testing gregorian <=> julian conversion

"; $t = adodb_mktime(0,0,0,10,11,1492); //http://www.holidayorigins.com/html/columbus_day.html - Friday check if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing
'; $t = adodb_mktime(0,0,0,2,29,1500); if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years
'; $t = adodb_mktime(0,0,0,2,29,1700); if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years
'; print adodb_mktime(0,0,0,10,4,1582).' '; print adodb_mktime(0,0,0,10,15,1582); $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)); if ($diff != 3600*24) print " Error in gregorian correction = ".($diff/3600/24)." days
"; print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : 'Error')."
"; print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : 'Error')."
"; print "

Testing overflow

"; $t = adodb_mktime(0,0,0,3,33,1965); if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1
'; $t = adodb_mktime(0,0,0,4,33,1971); if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2
'; $t = adodb_mktime(0,0,0,1,60,1965); if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).'
'; $t = adodb_mktime(0,0,0,12,32,1965); if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).'
'; $t = adodb_mktime(0,0,0,12,63,1965); if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).'
'; $t = adodb_mktime(0,0,0,13,3,1965); if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1
'; print "Testing 2-digit => 4-digit year conversion

"; if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000
"; if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010
"; if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020
"; if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030
"; if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940
"; if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950
"; if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990
"; // Test string formating print "

Testing date formating

"; $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C2822 r s t U w y Y z Z 2003'; $s1 = date($fmt,0); $s2 = adodb_date($fmt,0); if ($s1 != $s2) { print " date() 0 failed
$s1
$s2
"; } flush(); for ($i=100; --$i > 0; ) { $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000); $s1 = date($fmt,$ts); $s2 = adodb_date($fmt,$ts); //print "$s1
$s2

"; $pos = strcmp($s1,$s2); if (($s1) != ($s2)) { for ($j=0,$k=strlen($s1); $j < $k; $j++) { if ($s1[$j] != $s2[$j]) { print substr($s1,$j).' '; break; } } print "Error date(): $ts

 
  \"$s1\" (date len=".strlen($s1).")
  \"$s2\" (adodb_date len=".strlen($s2).")

"; $fail = true; } $a1 = getdate($ts); $a2 = adodb_getdate($ts); $rez = array_diff($a1,$a2); if (sizeof($rez)>0) { print "Error getdate() $ts
"; print_r($a1); print "
"; print_r($a2); print "

"; $fail = true; } } // Test generation of dates outside 1901-2038 print "

Testing random dates between 100 and 4000

"; adodb_date_test_date(100,1); for ($i=100; --$i >= 0;) { $y1 = 100+rand(0,1970-100); $m = rand(1,12); adodb_date_test_date($y1,$m); $y1 = 3000-rand(0,3000-1970); adodb_date_test_date($y1,$m); } print '

'; $start = 1960+rand(0,10); $yrs = 12; $i = 365.25*86400*($start-1970); $offset = 36000+rand(10000,60000); $max = 365*$yrs*86400; $lastyear = 0; // we generate a timestamp, convert it to a date, and convert it back to a timestamp // and check if the roundtrip broke the original timestamp value. print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; $cnt = 0; for ($max += $i; $i < $max; $i += $offset) { $ret = adodb_date('m,d,Y,H,i,s',$i); $arr = explode(',',$ret); if ($lastyear != $arr[2]) { $lastyear = $arr[2]; print " $lastyear "; flush(); } $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]); if ($i != $newi) { print "Error at $i, adodb_mktime returned $newi ($ret)"; $fail = true; break; } $cnt += 1; } echo "Tested $cnt dates
"; if (!$fail) print "

Passed !

"; else print "

Failed :-(

"; } /** Returns day of week, 0 = Sunday,... 6=Saturday. Algorithm from PEAR::Date_Calc */ function adodb_dow($year, $month, $day) { /* Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and proclaimed that from that time onwards 3 days would be dropped from the calendar every 400 years. Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). */ if ($year <= 1582) { if ($year < 1582 || ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; else $greg_correction = 0; } else $greg_correction = 0; if($month > 2) $month -= 2; else { $month += 10; $year--; } $day = floor((13 * $month - 1) / 5) + $day + ($year % 100) + floor(($year % 100) / 4) + floor(($year / 100) / 4) - 2 * floor($year / 100) + 77 + $greg_correction; return $day - 7 * floor($day / 7); } /** Checks for leap year, returns true if it is. No 2-digit year check. Also handles julian calendar correctly. */ function _adodb_is_leap_year($year) { if ($year % 4 != 0) return false; if ($year % 400 == 0) { return true; // if gregorian calendar (>1582), century not-divisible by 400 is not leap } else if ($year > 1582 && $year % 100 == 0 ) { return false; } return true; } /** checks for leap year, returns true if it is. Has 2-digit year check */ function adodb_is_leap_year($year) { return _adodb_is_leap_year(adodb_year_digit_check($year)); } /** Fix 2-digit years. Works for any century. Assumes that if 2-digit is more than 30 years in future, then previous century. */ function adodb_year_digit_check($y) { if ($y < 100) { $yr = (integer) date("Y"); $century = (integer) ($yr /100); if ($yr%100 > 50) { $c1 = $century + 1; $c0 = $century; } else { $c1 = $century; $c0 = $century - 1; } $c1 *= 100; // if 2-digit year is less than 30 years in future, set it to this century // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. if (($y + $c1) < $yr+30) $y = $y + $c1; else $y = $y + $c0*100; } return $y; } /** get local time zone offset from GMT */ function adodb_get_gmt_diff() { static $TZ; if (isset($TZ)) return $TZ; $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0); return $TZ; } /** Returns an array with date info. */ function adodb_getdate($d=false,$fast=false) { if ($d === false) return getdate(); if (!defined('ADODB_TEST_DATES')) { if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer return @getdate($d); } } return _adodb_getdate($d); } /* // generate $YRS table for _adodb_getdate() function adodb_date_gentable($out=true) { for ($i=1970; $i >= 1600; $i-=10) { $s = adodb_gmmktime(0,0,0,1,1,$i); echo "$i => $s,
"; } } adodb_date_gentable(); for ($i=1970; $i > 1500; $i--) { echo "
$i "; adodb_date_test_date($i,1,1); } */ $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); function adodb_validdate($y,$m,$d) { global $_month_table_normal,$_month_table_leaf; if (_adodb_is_leap_year($y)) $marr =& $_month_table_leaf; else $marr =& $_month_table_normal; if ($m > 12 || $m < 1) return false; if ($d > 31 || $d < 1) return false; if ($marr[$m] < $d) return false; if ($y < 1000 && $y > 3000) return false; return true; } /** Low-level function that returns the getdate() array. We have a special $fast flag, which if set to true, will return fewer array values, and is much faster as it does not calculate dow, etc. */ function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) { static $YRS; global $_month_table_normal,$_month_table_leaf; $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff()); $_day_power = 86400; $_hour_power = 3600; $_min_power = 60; if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); $d366 = $_day_power * 366; $d365 = $_day_power * 365; if ($d < 0) { if (empty($YRS)) $YRS = array( 1970 => 0, 1960 => -315619200, 1950 => -631152000, 1940 => -946771200, 1930 => -1262304000, 1920 => -1577923200, 1910 => -1893456000, 1900 => -2208988800, 1890 => -2524521600, 1880 => -2840140800, 1870 => -3155673600, 1860 => -3471292800, 1850 => -3786825600, 1840 => -4102444800, 1830 => -4417977600, 1820 => -4733596800, 1810 => -5049129600, 1800 => -5364662400, 1790 => -5680195200, 1780 => -5995814400, 1770 => -6311347200, 1760 => -6626966400, 1750 => -6942499200, 1740 => -7258118400, 1730 => -7573651200, 1720 => -7889270400, 1710 => -8204803200, 1700 => -8520336000, 1690 => -8835868800, 1680 => -9151488000, 1670 => -9467020800, 1660 => -9782640000, 1650 => -10098172800, 1640 => -10413792000, 1630 => -10729324800, 1620 => -11044944000, 1610 => -11360476800, 1600 => -11676096000); if ($is_gmt) $origd = $d; // The valid range of a 32bit signed timestamp is typically from // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT // # old algorithm iterates through all years. new algorithm does it in # 10 year blocks /* # old algo for ($a = 1970 ; --$a >= 0;) { $lastd = $d; if ($leaf = _adodb_is_leap_year($a)) $d += $d366; else $d += $d365; if ($d >= 0) { $year = $a; break; } } */ $lastsecs = 0; $lastyear = 1970; foreach($YRS as $year => $secs) { if ($d >= $secs) { $a = $lastyear; break; } $lastsecs = $secs; $lastyear = $year; } $d -= $lastsecs; if (!isset($a)) $a = $lastyear; //echo ' yr=',$a,' ', $d,'.'; for (; --$a >= 0;) { $lastd = $d; if ($leaf = _adodb_is_leap_year($a)) $d += $d366; else $d += $d365; if ($d >= 0) { $year = $a; break; } } /**/ $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; $d = $lastd; $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; for ($a = 13 ; --$a > 0;) { $lastd = $d; $d += $mtab[$a] * $_day_power; if ($d >= 0) { $month = $a; $ndays = $mtab[$a]; break; } } $d = $lastd; $day = $ndays + ceil(($d+1) / ($_day_power)); $d += ($ndays - $day+1)* $_day_power; $hour = floor($d/$_hour_power); } else { for ($a = 1970 ;; $a++) { $lastd = $d; if ($leaf = _adodb_is_leap_year($a)) $d -= $d366; else $d -= $d365; if ($d < 0) { $year = $a; break; } } $secsInYear = $lastd; $d = $lastd; $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; for ($a = 1 ; $a <= 12; $a++) { $lastd = $d; $d -= $mtab[$a] * $_day_power; if ($d < 0) { $month = $a; $ndays = $mtab[$a]; break; } } $d = $lastd; $day = ceil(($d+1) / $_day_power); $d = $d - ($day-1) * $_day_power; $hour = floor($d /$_hour_power); } $d -= $hour * $_hour_power; $min = floor($d/$_min_power); $secs = $d - $min * $_min_power; if ($fast) { return array( 'seconds' => $secs, 'minutes' => $min, 'hours' => $hour, 'mday' => $day, 'mon' => $month, 'year' => $year, 'yday' => floor($secsInYear/$_day_power), 'leap' => $leaf, 'ndays' => $ndays ); } $dow = adodb_dow($year,$month,$day); return array( 'seconds' => $secs, 'minutes' => $min, 'hours' => $hour, 'mday' => $day, 'wday' => $dow, 'mon' => $month, 'year' => $year, 'yday' => floor($secsInYear/$_day_power), 'weekday' => gmdate('l',$_day_power*(3+$dow)), 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), 0 => $origd ); } function adodb_gmdate($fmt,$d=false) { return adodb_date($fmt,$d,true); } // accepts unix timestamp and iso date format in $d function adodb_date2($fmt, $d=false, $is_gmt=false) { if ($d !== false) { if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($d), $rr)) return adodb_date($fmt,false,$is_gmt); if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt); // h-m-s-MM-DD-YY if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1],false,$is_gmt); else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1],false,$is_gmt); } return adodb_date($fmt,$d,$is_gmt); } /** Return formatted date based on timestamp $d */ function adodb_date($fmt,$d=false,$is_gmt=false) { static $daylight; if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt); if (!defined('ADODB_TEST_DATES')) { if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); } } $_day_power = 86400; $arr = _adodb_getdate($d,true,$is_gmt); if (!isset($daylight)) $daylight = function_exists('adodb_daylight_sv'); if ($daylight) adodb_daylight_sv($arr, $is_gmt); $year = $arr['year']; $month = $arr['mon']; $day = $arr['mday']; $hour = $arr['hours']; $min = $arr['minutes']; $secs = $arr['seconds']; $max = strlen($fmt); $dates = ''; $isphp5 = PHP_VERSION >= 5; /* at this point, we have the following integer vars to manipulate: $year, $month, $day, $hour, $min, $secs */ for ($i=0; $i < $max; $i++) { switch($fmt[$i]) { case 'T': $dates .= date('T');break; // YEAR case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 // 4.3.11 uses '04 Jun 2004' // 4.3.8 uses ' 4 Jun 2004' $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; $gmt = adodb_get_gmt_diff(); if ($isphp5) $dates .= sprintf(' %s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); else $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; case 'Y': $dates .= $year; break; case 'y': $dates .= substr($year,strlen($year)-2,2); break; // MONTH case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; case 'Q': $dates .= ($month+3)>>2; break; case 'n': $dates .= $month; break; case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; // DAY case 't': $dates .= $arr['ndays']; break; case 'z': $dates .= $arr['yday']; break; case 'w': $dates .= adodb_dow($year,$month,$day); break; case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; case 'j': $dates .= $day; break; case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; case 'S': $d10 = $day % 10; if ($d10 == 1) $dates .= 'st'; else if ($d10 == 2 && $day != 12) $dates .= 'nd'; else if ($d10 == 3) $dates .= 'rd'; else $dates .= 'th'; break; // HOUR case 'Z': $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff(); break; case 'O': $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff(); if ($isphp5) $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); else $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break; case 'H': if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; break; case 'h': if ($hour > 12) $hh = $hour - 12; else { if ($hour == 0) $hh = '12'; else $hh = $hour; } if ($hh < 10) $dates .= '0'.$hh; else $dates .= $hh; break; case 'G': $dates .= $hour; break; case 'g': if ($hour > 12) $hh = $hour - 12; else { if ($hour == 0) $hh = '12'; else $hh = $hour; } $dates .= $hh; break; // MINUTES case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; // SECONDS case 'U': $dates .= $d; break; case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; // AM/PM // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM case 'a': if ($hour>=12) $dates .= 'pm'; else $dates .= 'am'; break; case 'A': if ($hour>=12) $dates .= 'PM'; else $dates .= 'AM'; break; default: $dates .= $fmt[$i]; break; // ESCAPE case "\\": $i++; if ($i < $max) $dates .= $fmt[$i]; break; } } return $dates; } /** Returns a timestamp given a GMT/UTC time. Note that $is_dst is not implemented and is ignored. */ function adodb_gmmktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false) { return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true); } /** Return a timestamp given a local time. Originally by jackbbs. Note that $is_dst is not implemented and is ignored. Not a very fast algorithm - O(n) operation. Could be optimized to O(1). */ function adodb_mktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false,$is_gmt=false) { if (!defined('ADODB_TEST_DATES')) { if ($mon === false) { return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec); } // for windows, we don't check 1970 because with timezone differences, // 1 Jan 1970 could generate negative timestamp, which is illegal if (1971 < $year && $year < 2038 || !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038) ) { return $is_gmt ? @gmmktime($hr,$min,$sec,$mon,$day,$year): @mktime($hr,$min,$sec,$mon,$day,$year); } } $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff(); /* # disabled because some people place large values in $sec. # however we need it for $mon because we use an array... $hr = intval($hr); $min = intval($min); $sec = intval($sec); */ $mon = intval($mon); $day = intval($day); $year = intval($year); $year = adodb_year_digit_check($year); if ($mon > 12) { $y = floor($mon / 12); $year += $y; $mon -= $y*12; } else if ($mon < 1) { $y = ceil((1-$mon) / 12); $year -= $y; $mon += $y*12; } $_day_power = 86400; $_hour_power = 3600; $_min_power = 60; $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); $_total_date = 0; if ($year >= 1970) { for ($a = 1970 ; $a <= $year; $a++) { $leaf = _adodb_is_leap_year($a); if ($leaf == true) { $loop_table = $_month_table_leaf; $_add_date = 366; } else { $loop_table = $_month_table_normal; $_add_date = 365; } if ($a < $year) { $_total_date += $_add_date; } else { for($b=1;$b<$mon;$b++) { $_total_date += $loop_table[$b]; } } } $_total_date +=$day-1; $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; } else { for ($a = 1969 ; $a >= $year; $a--) { $leaf = _adodb_is_leap_year($a); if ($leaf == true) { $loop_table = $_month_table_leaf; $_add_date = 366; } else { $loop_table = $_month_table_normal; $_add_date = 365; } if ($a > $year) { $_total_date += $_add_date; } else { for($b=12;$b>$mon;$b--) { $_total_date += $loop_table[$b]; } } } $_total_date += $loop_table[$mon] - $day; $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; $_day_time = $_day_power - $_day_time; $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. } //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; return $ret; } function adodb_gmstrftime($fmt, $ts=false) { return adodb_strftime($fmt,$ts,true); } // hack - convert to adodb_date function adodb_strftime($fmt, $ts=false,$is_gmt=false) { global $ADODB_DATE_LOCALE; if (!defined('ADODB_TEST_DATES')) { if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range if (!defined('ADODB_NO_NEGATIVE_TS') || $ts >= 0) // if windows, must be +ve integer return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts); } } if (empty($ADODB_DATE_LOCALE)) { /* $tstr = strtoupper(gmstrftime('%c',31366800)); // 30 Dec 1970, 1 am $sep = substr($tstr,2,1); $hasAM = strrpos($tstr,'M') !== false; */ # see http://phplens.com/lens/lensforum/msgs.php?id=14865 for reasoning, and changelog for version 0.24 $dstr = gmstrftime('%x',31366800); // 30 Dec 1970, 1 am $sep = substr($dstr,2,1); $tstr = strtoupper(gmstrftime('%X',31366800)); // 30 Dec 1970, 1 am $hasAM = strrpos($tstr,'M') !== false; $ADODB_DATE_LOCALE = array(); $ADODB_DATE_LOCALE[] = strncmp($tstr,'30',2) == 0 ? 'd'.$sep.'m'.$sep.'y' : 'm'.$sep.'d'.$sep.'y'; $ADODB_DATE_LOCALE[] = ($hasAM) ? 'h:i:s a' : 'H:i:s'; } $inpct = false; $fmtdate = ''; for ($i=0,$max = strlen($fmt); $i < $max; $i++) { $ch = $fmt[$i]; if ($ch == '%') { if ($inpct) { $fmtdate .= '%'; $inpct = false; } else $inpct = true; } else if ($inpct) { $inpct = false; switch($ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'E': case 'O': /* ignore format modifiers */ $inpct = true; break; case 'a': $fmtdate .= 'D'; break; case 'A': $fmtdate .= 'l'; break; case 'h': case 'b': $fmtdate .= 'M'; break; case 'B': $fmtdate .= 'F'; break; case 'c': $fmtdate .= $ADODB_DATE_LOCALE[0].$ADODB_DATE_LOCALE[1]; break; case 'C': $fmtdate .= '\C?'; break; // century case 'd': $fmtdate .= 'd'; break; case 'D': $fmtdate .= 'm/d/y'; break; case 'e': $fmtdate .= 'j'; break; case 'g': $fmtdate .= '\g?'; break; //? case 'G': $fmtdate .= '\G?'; break; //? case 'H': $fmtdate .= 'H'; break; case 'I': $fmtdate .= 'h'; break; case 'j': $fmtdate .= '?z'; $parsej = true; break; // wrong as j=1-based, z=0-basd case 'm': $fmtdate .= 'm'; break; case 'M': $fmtdate .= 'i'; break; case 'n': $fmtdate .= "\n"; break; case 'p': $fmtdate .= 'a'; break; case 'r': $fmtdate .= 'h:i:s a'; break; case 'R': $fmtdate .= 'H:i:s'; break; case 'S': $fmtdate .= 's'; break; case 't': $fmtdate .= "\t"; break; case 'T': $fmtdate .= 'H:i:s'; break; case 'u': $fmtdate .= '?u'; $parseu = true; break; // wrong strftime=1-based, date=0-based case 'U': $fmtdate .= '?U'; $parseU = true; break;// wrong strftime=1-based, date=0-based case 'x': $fmtdate .= $ADODB_DATE_LOCALE[0]; break; case 'X': $fmtdate .= $ADODB_DATE_LOCALE[1]; break; case 'w': $fmtdate .= '?w'; $parseu = true; break; // wrong strftime=1-based, date=0-based case 'W': $fmtdate .= '?W'; $parseU = true; break;// wrong strftime=1-based, date=0-based case 'y': $fmtdate .= 'y'; break; case 'Y': $fmtdate .= 'Y'; break; case 'Z': $fmtdate .= 'T'; break; } } else if (('A' <= ($ch) && ($ch) <= 'Z' ) || ('a' <= ($ch) && ($ch) <= 'z' )) $fmtdate .= "\\".$ch; else $fmtdate .= $ch; } //echo "fmt=",$fmtdate,"
"; if ($ts === false) $ts = time(); $ret = adodb_date($fmtdate, $ts, $is_gmt); return $ret; } ?>phpgacl-3.3.7/adodb/tohtml.inc.php0100644025754300001440000001263310476657245015737 0ustar ipsousers */ // specific code for tohtml GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND; $ADODB_ROUND=4; // rounding $gSQLMaxRows = 1000; // max no of rows to download $gSQLBlockRows=20; // max no of rows per table block // RecordSet to HTML Table //------------------------------------------------------------ // Convert a recordset to a html table. Multiple tables are generated // if the number of rows is > $gSQLBlockRows. This is because // web browsers normally require the whole table to be downloaded // before it can be rendered, so we break the output into several // smaller faster rendering tables. // // $rs: the recordset // $ztabhtml: the table tag attributes (optional) // $zheaderarray: contains the replacement strings for the headers (optional) // // USAGE: // include('adodb.inc.php'); // $db = ADONewConnection('mysql'); // $db->Connect('mysql','userid','password','database'); // $rs = $db->Execute('select col1,col2,col3 from table'); // rs2html($rs, 'BORDER=2', array('Title1', 'Title2', 'Title3')); // $rs->Close(); // // RETURNS: number of rows displayed function rs2html(&$rs,$ztabhtml=false,$zheaderarray=false,$htmlspecialchars=true,$echo = true) { $s ='';$rows=0;$docnt = false; GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND; if (!$rs) { printf(ADODB_BAD_RS,'rs2html'); return false; } if (! $ztabhtml) $ztabhtml = "BORDER='1' WIDTH='98%'"; //else $docnt = true; $typearr = array(); $ncols = $rs->FieldCount(); $hdr = "\n\n"; for ($i=0; $i < $ncols; $i++) { $field = $rs->FetchField($i); if ($field) { if ($zheaderarray) $fname = $zheaderarray[$i]; else $fname = htmlspecialchars($field->name); $typearr[$i] = $rs->MetaType($field->type,$field->max_length); //print " $field->name $field->type $typearr[$i] "; } else { $fname = 'Field '.($i+1); $typearr[$i] = 'C'; } if (strlen($fname)==0) $fname = ' '; $hdr .= ""; } $hdr .= "\n"; if ($echo) print $hdr."\n\n"; else $html = $hdr; // smart algorithm - handles ADODB_FETCH_MODE's correctly by probing... $numoffset = isset($rs->fields[0]) ||isset($rs->fields[1]) || isset($rs->fields[2]); while (!$rs->EOF) { $s .= "\n"; for ($i=0; $i < $ncols; $i++) { if ($i===0) $v=($numoffset) ? $rs->fields[0] : reset($rs->fields); else $v = ($numoffset) ? $rs->fields[$i] : next($rs->fields); $type = $typearr[$i]; switch($type) { case 'D': if (empty($v)) $s .= "\n"; else if (!strpos($v,':')) { $s .= " \n"; } break; case 'T': if (empty($v)) $s .= "\n"; else $s .= " \n"; break; case 'N': if (abs(abs($v) - round($v,0)) < 0.00000001) $v = round($v); else $v = round($v,$ADODB_ROUND); case 'I': $s .= " \n"; break; /* case 'B': if (substr($v,8,2)=="BM" ) $v = substr($v,8); $mtime = substr(str_replace(' ','_',microtime()),2); $tmpname = "tmp/".uniqid($mtime).getmypid(); $fd = @fopen($tmpname,'a'); @ftruncate($fd,0); @fwrite($fd,$v); @fclose($fd); if (!function_exists ("mime_content_type")) { function mime_content_type ($file) { return exec("file -bi ".escapeshellarg($file)); } } $t = mime_content_type($tmpname); $s .= (substr($t,0,5)=="image") ? " \\n" : " \\n"; break; */ default: if ($htmlspecialchars) $v = htmlspecialchars(trim($v)); $v = trim($v); if (strlen($v) == 0) $v = ' '; $s .= " \n"; } } // for $s .= "\n\n"; $rows += 1; if ($rows >= $gSQLMaxRows) { $rows = "

Truncated at $gSQLMaxRows

"; break; } // switch $rs->MoveNext(); // additional EOF check to prevent a widow header if (!$rs->EOF && $rows % $gSQLBlockRows == 0) { //if (connection_aborted()) break;// not needed as PHP aborts script, unlike ASP if ($echo) print $s . "
$fname
  ".$rs->UserDate($v,"D d, M Y") ."    ".$rs->UserTimeStamp($v,"D d, M Y, h:i:s") ." ".stripslashes((trim($v))) ." $t$t". str_replace("\n",'
',stripslashes($v)) ."
\n\n"; else $html .= $s ."\n\n"; $s = $hdr; } } // while if ($echo) print $s."\n\n"; else $html .= $s."\n\n"; if ($docnt) if ($echo) print "

".$rows." Rows

"; return ($echo) ? $rows : $html; } // pass in 2 dimensional array function arr2html(&$arr,$ztabhtml='',$zheaderarray='') { if (!$ztabhtml) $ztabhtml = 'BORDER=1'; $s = "";//';print_r($arr); if ($zheaderarray) { $s .= ''; for ($i=0; $i\n"; } else $s .= " \n"; $s .= "\n\n"; } $s .= '
 
'; print $s; } ?>phpgacl-3.3.7/adodb/adodb.inc.php0100644025754300001440000035300310476657245015500 0ustar ipsousersfields is available on EOF $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default... //============================================================================================== // GLOBAL SETUP //============================================================================================== $ADODB_EXTENSION = defined('ADODB_EXTENSION'); //********************************************************// /* Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3). Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi 0 = ignore empty fields. All empty fields in array are ignored. 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values. 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values. 3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values. */ define('ADODB_FORCE_IGNORE',0); define('ADODB_FORCE_NULL',1); define('ADODB_FORCE_EMPTY',2); define('ADODB_FORCE_VALUE',3); //********************************************************// if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) { define('ADODB_BAD_RS','

Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;

'); // allow [ ] @ ` " and . in table names define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)'); // prefetching used by oracle if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10); /* Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names. This currently works only with mssql, odbc, oci8po and ibase derived drivers. 0 = assoc lowercase field names. $rs->fields['orderid'] 1 = assoc uppercase field names. $rs->fields['ORDERID'] 2 = use native-case field names. $rs->fields['OrderID'] */ define('ADODB_FETCH_DEFAULT',0); define('ADODB_FETCH_NUM',1); define('ADODB_FETCH_ASSOC',2); define('ADODB_FETCH_BOTH',3); if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100); // PHP's version scheme makes converting to numbers difficult - workaround $_adodb_ver = (float) PHP_VERSION; if ($_adodb_ver >= 5.0) { define('ADODB_PHPVER',0x5000); } else if ($_adodb_ver > 4.299999) { # 4.3 define('ADODB_PHPVER',0x4300); } else if ($_adodb_ver > 4.199999) { # 4.2 define('ADODB_PHPVER',0x4200); } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) { define('ADODB_PHPVER',0x4050); } else { define('ADODB_PHPVER',0x4000); } } //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); /** Accepts $src and $dest arrays, replacing string $data */ function ADODB_str_replace($src, $dest, $data) { if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data); $s = reset($src); $d = reset($dest); while ($s !== false) { $data = str_replace($s,$d,$data); $s = next($src); $d = next($dest); } return $data; } function ADODB_Setup() { GLOBAL $ADODB_vers, // database version $ADODB_COUNTRECS, // count number of records returned - slows down query $ADODB_CACHE_DIR, // directory to cache recordsets $ADODB_FETCH_MODE, $ADODB_FORCE_TYPE; $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE; if (!isset($ADODB_CACHE_DIR)) { $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp'; } else { // do not accept url based paths, eg. http:/ or ftp:/ if (strpos($ADODB_CACHE_DIR,'://') !== false) die("Illegal path http:// or ftp://"); } // Initialize random number generator for randomizing cache flushes srand(((double)microtime())*1000000); /** * ADODB version as a string. */ $ADODB_vers = 'V4.90 8 June 2006 (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.'; /** * Determines whether recordset->RecordCount() is used. * Set to false for highest performance -- RecordCount() will always return -1 then * for databases that provide "virtual" recordcounts... */ if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true; } //============================================================================================== // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB //============================================================================================== ADODB_Setup(); //============================================================================================== // CLASS ADOFieldObject //============================================================================================== /** * Helper class for FetchFields -- holds info on a column */ class ADOFieldObject { var $name = ''; var $max_length=0; var $type=""; /* // additional fields by dannym... (danny_milo@yahoo.com) var $not_null = false; // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ // so we can as well make not_null standard (leaving it at "false" does not harm anyways) var $has_default = false; // this one I have done only in mysql and postgres for now ... // others to come (dannym) var $default_value; // default, if any, and supported. Check has_default first. */ } function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) { //print "Errorno ($fn errno=$errno m=$errmsg) "; $thisConnection->_transOK = false; if ($thisConnection->_oldRaiseFn) { $fn = $thisConnection->_oldRaiseFn; $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection); } } //============================================================================================== // CLASS ADOConnection //============================================================================================== /** * Connection object. For connecting to databases, and executing queries. */ class ADOConnection { // // PUBLIC VARS // var $dataProvider = 'native'; var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql var $database = ''; /// Name of database to be used. var $host = ''; /// The hostname of the database server var $user = ''; /// The username which is used to connect to the database server. var $password = ''; /// Password for the username. For security, we no longer store it. var $debug = false; /// if set to true will output sql statements var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase var $substr = 'substr'; /// substring operator var $length = 'length'; /// string length ofperator var $random = 'rand()'; /// random function var $upperCase = 'upper'; /// uppercase function var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt. var $true = '1'; /// string that represents TRUE for a database var $false = '0'; /// string that represents FALSE for a database var $replaceQuote = "\\'"; /// string to use to replace quotes var $nameQuote = '"'; /// string to use to quote identifiers and names var $charSet=false; /// character set to use - only for interbase, postgres and oci8 var $metaDatabasesSQL = ''; var $metaTablesSQL = ''; var $uniqueOrderBy = false; /// All order by columns have to be unique var $emptyDate = ' '; var $emptyTimeStamp = ' '; var $lastInsID = false; //-- var $hasInsertID = false; /// supports autoincrement ID? var $hasAffectedRows = false; /// supports affected rows for update/delete? var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10 var $readOnly = false; /// this is a readonly database - used by phpLens var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards var $hasGenID = false; /// can generate sequences using GenID(); var $hasTransactions = true; /// has transactions //-- var $genID = 0; /// sequence id used by GenID(); var $raiseErrorFn = false; /// error function to call var $isoDates = false; /// accepts dates in ISO format var $cacheSecs = 3600; /// cache for 1 hour // memcache var $memCache = false; /// should we use memCache instead of caching in files var $memCacheHost; /// memCache host var $memCachePort = 11211; /// memCache port var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib) var $sysDate = false; /// name of function that returns the current date var $sysTimeStamp = false; /// name of function that returns the current timestamp var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' ' var $numCacheHits = 0; var $numCacheMisses = 0; var $pageExecuteCountRows = true; var $uniqueSort = false; /// indicates that all fields in order by must be unique var $leftOuter = false; /// operator to use for left outer join in WHERE clause var $rightOuter = false; /// operator to use for right outer join in WHERE clause var $ansiOuter = false; /// whether ansi outer join syntax supported var $autoRollback = false; // autoRollback on PConnect(). var $poorAffectedRows = false; // affectedRows not working or unreliable var $fnExecute = false; var $fnCacheExecute = false; var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char var $rsPrefix = "ADORecordSet_"; var $autoCommit = true; /// do not modify this yourself - actually private var $transOff = 0; /// temporarily disable transactions var $transCnt = 0; /// count of nested transactions var $fetchMode=false; // // PRIVATE VARS // var $_oldRaiseFn = false; var $_transOK = null; var $_connectionID = false; /// The returned link identifier whenever a successful database connection is made. var $_errorMsg = false; /// A variable which was used to keep the returned last error message. The value will /// then returned by the errorMsg() function var $_errorCode = false; /// Last error code, not guaranteed to be used - only by oci8 var $_queryID = false; /// This variable keeps the last created result link identifier var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */ var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters. var $_evalAll = false; var $_affected = false; var $_logsql = false; var $_transmode = ''; // transaction mode /** * Constructor */ function ADOConnection() { die('Virtual Class -- cannot instantiate'); } function Version() { global $ADODB_vers; return (float) substr($ADODB_vers,1); } /** Get server version info... @returns An array with 2 elements: $arr['string'] is the description string, and $arr[version] is the version (also a string). */ function ServerInfo() { return array('description' => '', 'version' => ''); } function IsConnected() { return !empty($this->_connectionID); } function _findvers($str) { if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1]; else return ''; } /** * All error messages go through this bottleneck function. * You can define your own handler by defining the function name in ADODB_OUTP. */ function outp($msg,$newline=true) { global $ADODB_FLUSH,$ADODB_OUTP; if (defined('ADODB_OUTP')) { $fn = ADODB_OUTP; $fn($msg,$newline); return; } else if (isset($ADODB_OUTP)) { $fn = $ADODB_OUTP; $fn($msg,$newline); return; } if ($newline) $msg .= "
\n"; if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg; else echo strip_tags($msg); if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan } function Time() { $rs =& $this->_Execute("select $this->sysTimeStamp"); if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields)); return false; } /** * Connect to database * * @param [argHostname] Host to connect to * @param [argUsername] Userid to login * @param [argPassword] Associated password * @param [argDatabaseName] database * @param [forceNew] force new connection * * @return true or false */ function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) { if ($argHostname != "") $this->host = $argHostname; if ($argUsername != "") $this->user = $argUsername; if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons if ($argDatabaseName != "") $this->database = $argDatabaseName; $this->_isPersistentConnection = false; if ($forceNew) { if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true; } else { if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true; } if (isset($rez)) { $err = $this->ErrorMsg(); if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; $ret = false; } else { $err = "Missing extension for ".$this->dataProvider; $ret = 0; } if ($fn = $this->raiseErrorFn) $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); $this->_connectionID = false; if ($this->debug) ADOConnection::outp( $this->host.': '.$err); return $ret; } function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); } /** * Always force a new connection to database - currently only works with oracle * * @param [argHostname] Host to connect to * @param [argUsername] Userid to login * @param [argPassword] Associated password * @param [argDatabaseName] database * * @return true or false */ function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") { return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); } /** * Establish persistent connect to database * * @param [argHostname] Host to connect to * @param [argUsername] Userid to login * @param [argPassword] Associated password * @param [argDatabaseName] database * * @return return true or false */ function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") { if (defined('ADODB_NEVER_PERSIST')) return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName); if ($argHostname != "") $this->host = $argHostname; if ($argUsername != "") $this->user = $argUsername; if ($argPassword != "") $this->password = $argPassword; if ($argDatabaseName != "") $this->database = $argDatabaseName; $this->_isPersistentConnection = true; if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true; if (isset($rez)) { $err = $this->ErrorMsg(); if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'"; $ret = false; } else { $err = "Missing extension for ".$this->dataProvider; $ret = 0; } if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this); } $this->_connectionID = false; if ($this->debug) ADOConnection::outp( $this->host.': '.$err); return $ret; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysDate; return $col; // child class implement } /** * Should prepare the sql statement and return the stmt resource. * For databases that do not support this, we return the $sql. To ensure * compatibility with databases that do not support prepare: * * $stmt = $db->Prepare("insert into table (id, name) values (?,?)"); * $db->Execute($stmt,array(1,'Jill')) or die('insert failed'); * $db->Execute($stmt,array(2,'Joe')) or die('insert failed'); * * @param sql SQL to send to database * * @return return FALSE, or the prepared statement, or the original sql if * if the database does not support prepare. * */ function Prepare($sql) { return $sql; } /** * Some databases, eg. mssql require a different function for preparing * stored procedures. So we cannot use Prepare(). * * Should prepare the stored procedure and return the stmt resource. * For databases that do not support this, we return the $sql. To ensure * compatibility with databases that do not support prepare: * * @param sql SQL to send to database * * @return return FALSE, or the prepared statement, or the original sql if * if the database does not support prepare. * */ function PrepareSP($sql,$param=true) { return $this->Prepare($sql,$param); } /** * PEAR DB Compat */ function Quote($s) { return $this->qstr($s,false); } /** Requested by "Karsten Dambekalns" */ function QMagic($s) { return $this->qstr($s,get_magic_quotes_gpc()); } function q(&$s) { #if (!empty($this->qNull)) if ($s == 'null') return $s; $s = $this->qstr($s,false); } /** * PEAR DB Compat - do not use internally. */ function ErrorNative() { return $this->ErrorNo(); } /** * PEAR DB Compat - do not use internally. */ function nextId($seq_name) { return $this->GenID($seq_name); } /** * Lock a row, will escalate and lock the table if row locking not supported * will normally free the lock at the end of the transaction * * @param $table name of table to lock * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock */ function RowLock($table,$where) { return false; } function CommitLock($table) { return $this->CommitTrans(); } function RollbackLock($table) { return $this->RollbackTrans(); } /** * PEAR DB Compat - do not use internally. * * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical * for easy porting :-) * * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM * @returns The previous fetch mode */ function SetFetchMode($mode) { $old = $this->fetchMode; $this->fetchMode = $mode; if ($old === false) { global $ADODB_FETCH_MODE; return $ADODB_FETCH_MODE; } return $old; } /** * PEAR DB Compat - do not use internally. */ function &Query($sql, $inputarr=false) { $rs = &$this->Execute($sql, $inputarr); if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); return $rs; } /** * PEAR DB Compat - do not use internally */ function &LimitQuery($sql, $offset, $count, $params=false) { $rs = &$this->SelectLimit($sql, $count, $offset, $params); if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error(); return $rs; } /** * PEAR DB Compat - do not use internally */ function Disconnect() { return $this->Close(); } /* Returns placeholder for parameter, eg. $DB->Param('a') will return ':a' for Oracle, and '?' for most other databases... For databases that require positioned params, eg $1, $2, $3 for postgresql, pass in Param(false) before setting the first parameter. */ function Param($name,$type='C') { return '?'; } /* InParameter and OutParameter are self-documenting versions of Parameter(). */ function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) { return $this->Parameter($stmt,$var,$name,false,$maxLen,$type); } /* */ function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) { return $this->Parameter($stmt,$var,$name,true,$maxLen,$type); } /* Usage in oracle $stmt = $db->Prepare('select * from table where id =:myid and group=:group'); $db->Parameter($stmt,$id,'myid'); $db->Parameter($stmt,$group,'group',64); $db->Execute(); @param $stmt Statement returned by Prepare() or PrepareSP(). @param $var PHP variable to bind to @param $name Name of stored procedure variable name to bind to. @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. @param [$maxLen] Holds an maximum length of the variable. @param [$type] The data type of $var. Legal values depend on driver. */ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) { return false; } function IgnoreErrors($saveErrs=false) { if (!$saveErrs) { $saveErrs = array($this->raiseErrorFn,$this->_transOK); $this->raiseErrorFn = false; return $saveErrs; } else { $this->raiseErrorFn = $saveErrs[0]; $this->_transOK = $saveErrs[1]; } } /** Improved method of initiating a transaction. Used together with CompleteTrans(). Advantages include: a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans. Only the outermost block is treated as a transaction.
b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.
c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block are disabled, making it backward compatible. */ function StartTrans($errfn = 'ADODB_TransMonitor') { if ($this->transOff > 0) { $this->transOff += 1; return; } $this->_oldRaiseFn = $this->raiseErrorFn; $this->raiseErrorFn = $errfn; $this->_transOK = true; if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); $this->BeginTrans(); $this->transOff = 1; } /** Used together with StartTrans() to end a transaction. Monitors connection for sql errors, and will commit or rollback as appropriate. @autoComplete if true, monitor sql errors and commit and rollback as appropriate, and if set to false force rollback even if no SQL error detected. @returns true on commit, false on rollback. */ function CompleteTrans($autoComplete = true) { if ($this->transOff > 1) { $this->transOff -= 1; return true; } $this->raiseErrorFn = $this->_oldRaiseFn; $this->transOff = 0; if ($this->_transOK && $autoComplete) { if (!$this->CommitTrans()) { $this->_transOK = false; if ($this->debug) ADOConnection::outp("Smart Commit failed"); } else if ($this->debug) ADOConnection::outp("Smart Commit occurred"); } else { $this->_transOK = false; $this->RollbackTrans(); if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred"); } return $this->_transOK; } /* At the end of a StartTrans/CompleteTrans block, perform a rollback. */ function FailTrans() { if ($this->debug) if ($this->transOff == 0) { ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); } else { ADOConnection::outp("FailTrans was called"); adodb_backtrace(); } $this->_transOK = false; } /** Check if transaction has failed, only for Smart Transactions. */ function HasFailedTrans() { if ($this->transOff > 0) return $this->_transOK == false; return false; } /** * Execute SQL * * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text) * @param [inputarr] holds the input data to bind to. Null elements will be set to null. * @return RecordSet or false */ function &Execute($sql,$inputarr=false) { if ($this->fnExecute) { $fn = $this->fnExecute; $ret =& $fn($this,$sql,$inputarr); if (isset($ret)) return $ret; } if ($inputarr) { if (!is_array($inputarr)) $inputarr = array($inputarr); $element0 = reset($inputarr); # is_object check because oci8 descriptors can be passed in $array_2d = is_array($element0) && !is_object(reset($element0)); //remove extra memory copy of input -mikefedyk unset($element0); if (!is_array($sql) && !$this->_bindInputArray) { $sqlarr = explode('?',$sql); if (!$array_2d) $inputarr = array($inputarr); foreach($inputarr as $arr) { $sql = ''; $i = 0; //Use each() instead of foreach to reduce memory usage -mikefedyk while(list(, $v) = each($arr)) { $sql .= $sqlarr[$i]; // from Ron Baldwin // Only quote string types $typ = gettype($v); if ($typ == 'string') //New memory copy of input created here -mikefedyk $sql .= $this->qstr($v); else if ($typ == 'double') $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1 else if ($typ == 'boolean') $sql .= $v ? $this->true : $this->false; else if ($typ == 'object') { if (method_exists($v, '__toString')) $sql .= $this->qstr($v->__toString()); else $sql .= $this->qstr((string) $v); } else if ($v === null) $sql .= 'NULL'; else $sql .= $v; $i += 1; } if (isset($sqlarr[$i])) { $sql .= $sqlarr[$i]; if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql)); } else if ($i != sizeof($sqlarr)) ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql)); $ret =& $this->_Execute($sql); if (!$ret) return $ret; } } else { if ($array_2d) { if (is_string($sql)) $stmt = $this->Prepare($sql); else $stmt = $sql; foreach($inputarr as $arr) { $ret =& $this->_Execute($stmt,$arr); if (!$ret) return $ret; } } else { $ret =& $this->_Execute($sql,$inputarr); } } } else { $ret =& $this->_Execute($sql,false); } return $ret; } function &_Execute($sql,$inputarr=false) { if ($this->debug) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr); } else { $this->_queryID = @$this->_query($sql,$inputarr); } /************************ // OK, query executed *************************/ if ($this->_queryID === false) { // error handling if query fails if ($this->debug == 99) adodb_backtrace(true,5); $fn = $this->raiseErrorFn; if ($fn) { $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this); } $false = false; return $false; } if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead $rs =& new ADORecordSet_empty(); return $rs; } // return real recordset from select statement $rsclass = $this->rsPrefix.$this->databaseType; $rs = new $rsclass($this->_queryID,$this->fetchMode); $rs->connection = &$this; // Pablo suggestion $rs->Init(); if (is_array($sql)) $rs->sql = $sql[0]; else $rs->sql = $sql; if ($rs->_numOfRows <= 0) { global $ADODB_COUNTRECS; if ($ADODB_COUNTRECS) { if (!$rs->EOF) { $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql)); $rs->_queryID = $this->_queryID; } else $rs->_numOfRows = 0; } } return $rs; } function CreateSequence($seqname='adodbseq',$startID=1) { if (empty($this->_genSeqSQL)) return false; return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); } function DropSequence($seqname='adodbseq') { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } /** * Generates a sequence id and stores it in $this->genID; * GenID is only available if $this->hasGenID = true; * * @param seqname name of sequence to use * @param startID if sequence does not exist, start at this ID * @return 0 if not supported, otherwise a sequence id */ function GenID($seqname='adodbseq',$startID=1) { if (!$this->hasGenID) { return 0; // formerly returns false pre 1.60 } $getnext = sprintf($this->_genIDSQL,$seqname); $holdtransOK = $this->_transOK; $save_handler = $this->raiseErrorFn; $this->raiseErrorFn = ''; @($rs = $this->Execute($getnext)); $this->raiseErrorFn = $save_handler; if (!$rs) { $this->_transOK = $holdtransOK; //if the status was ok before reset $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); $rs = $this->Execute($getnext); } if ($rs && !$rs->EOF) $this->genID = reset($rs->fields); else $this->genID = 0; // false if ($rs) $rs->Close(); return $this->genID; } /** * @param $table string name of the table, not needed by all databases (eg. mysql), default '' * @param $column string name of the column, not needed by all databases (eg. mysql), default '' * @return the last inserted ID. Not all databases support this. */ function Insert_ID($table='',$column='') { if ($this->_logsql && $this->lastInsID) return $this->lastInsID; if ($this->hasInsertID) return $this->_insertid($table,$column); if ($this->debug) { ADOConnection::outp( '

Insert_ID error

'); adodb_backtrace(); } return false; } /** * Portable Insert ID. Pablo Roca * * @return the last inserted ID. All databases support this. But aware possible * problems in multiuser environments. Heavy test this before deploying. */ function PO_Insert_ID($table="", $id="") { if ($this->hasInsertID){ return $this->Insert_ID($table,$id); } else { return $this->GetOne("SELECT MAX($id) FROM $table"); } } /** * @return # rows affected by UPDATE/DELETE */ function Affected_Rows() { if ($this->hasAffectedRows) { if ($this->fnExecute === 'adodb_log_sql') { if ($this->_logsql && $this->_affected !== false) return $this->_affected; } $val = $this->_affectedrows(); return ($val < 0) ? false : $val; } if ($this->debug) ADOConnection::outp( '

Affected_Rows error

',false); return false; } /** * @return the last error message */ function ErrorMsg() { if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg; else return ''; } /** * @return the last error number. Normally 0 means no error. */ function ErrorNo() { return ($this->_errorMsg) ? -1 : 0; } function MetaError($err=false) { include_once(ADODB_DIR."/adodb-error.inc.php"); if ($err === false) $err = $this->ErrorNo(); return adodb_error($this->dataProvider,$this->databaseType,$err); } function MetaErrorMsg($errno) { include_once(ADODB_DIR."/adodb-error.inc.php"); return adodb_errormsg($errno); } /** * @returns an array with the primary key columns in it. */ function MetaPrimaryKeys($table, $owner=false) { // owner not used in base class - see oci8 $p = array(); $objs =& $this->MetaColumns($table); if ($objs) { foreach($objs as $v) { if (!empty($v->primary_key)) $p[] = $v->name; } } if (sizeof($p)) return $p; if (function_exists('ADODB_VIEW_PRIMARYKEYS')) return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); return false; } /** * @returns assoc array where keys are tables, and values are foreign keys */ function MetaForeignKeys($table, $owner=false, $upper=false) { return false; } /** * Choose a database to connect to. Many databases do not support this. * * @param dbName is the name of the database to select * @return true or false */ function SelectDB($dbName) {return false;} /** * Will select, getting rows from $offset (1-based), for $nrows. * This simulates the MySQL "select * from table limit $offset,$nrows" , and * the PostgreSQL "select * from table limit $nrows offset $offset". Note that * MySQL and PostgreSQL parameter ordering is the opposite of the other. * eg. * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based) * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based) * * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set) * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set * * @param sql * @param [offset] is the row to start calculations from (1-based) * @param [nrows] is the number of rows to get * @param [inputarr] array of bind variables * @param [secs2cache] is a private parameter only used by jlim * @return the recordset ($rs->databaseType == 'array') */ function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { if ($this->hasTop && $nrows > 0) { // suggested by Reinhard Balling. Access requires top after distinct // Informix requires first before distinct - F Riosa $ismssql = (strpos($this->databaseType,'mssql') !== false); if ($ismssql) $isaccess = false; else $isaccess = (strpos($this->databaseType,'access') !== false); if ($offset <= 0) { // access includes ties in result if ($isaccess) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql); if ($secs2cache != 0) { $ret =& $this->CacheExecute($secs2cache, $sql,$inputarr); } else { $ret =& $this->Execute($sql,$inputarr); } return $ret; // PHP5 fix } else if ($ismssql){ $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql); } else { $sql = preg_replace( '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql); } } else { $nn = $nrows + $offset; if ($isaccess || $ismssql) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); } else { $sql = preg_replace( '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); } } } // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS. global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; if ($offset>0){ if ($secs2cache != 0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); else $rs = &$this->Execute($sql,$inputarr); } else { if ($secs2cache != 0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); else $rs = &$this->Execute($sql,$inputarr); } $ADODB_COUNTRECS = $savec; if ($rs && !$rs->EOF) { $rs =& $this->_rs2rs($rs,$nrows,$offset); } //print_r($rs); return $rs; } /** * Create serializable recordset. Breaks rs link to connection. * * @param rs the recordset to serialize */ function &SerializableRS(&$rs) { $rs2 =& $this->_rs2rs($rs); $ignore = false; $rs2->connection =& $ignore; return $rs2; } /** * Convert database recordset to an array recordset * input recordset's cursor should be at beginning, and * old $rs will be closed. * * @param rs the recordset to copy * @param [nrows] number of rows to retrieve (optional) * @param [offset] offset by number of rows (optional) * @return the new recordset */ function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) { if (! $rs) { $false = false; return $false; } $dbtype = $rs->databaseType; if (!$dbtype) { $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ? return $rs; } if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) { $rs->MoveFirst(); $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ? return $rs; } $flds = array(); for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { $flds[] = $rs->FetchField($i); } $arr =& $rs->GetArrayLimit($nrows,$offset); //print_r($arr); if ($close) $rs->Close(); $arrayClass = $this->arrayClass; $rs2 = new $arrayClass(); $rs2->connection = &$this; $rs2->sql = $rs->sql; $rs2->dataProvider = $this->dataProvider; $rs2->InitArrayFields($arr,$flds); $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode; return $rs2; } /* * Return all rows. Compat with PEAR DB */ function &GetAll($sql, $inputarr=false) { $arr =& $this->GetArray($sql,$inputarr); return $arr; } function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) { $rs =& $this->Execute($sql, $inputarr); if (!$rs) { $false = false; return $false; } $arr =& $rs->GetAssoc($force_array,$first2cols); return $arr; } function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) { if (!is_numeric($secs2cache)) { $first2cols = $force_array; $force_array = $inputarr; } $rs =& $this->CacheExecute($secs2cache, $sql, $inputarr); if (!$rs) { $false = false; return $false; } $arr =& $rs->GetAssoc($force_array,$first2cols); return $arr; } /** * Return first element of first row of sql statement. Recordset is disposed * for you. * * @param sql SQL statement * @param [inputarr] input bind array */ function GetOne($sql,$inputarr=false) { global $ADODB_COUNTRECS; $crecs = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $ret = false; $rs = &$this->Execute($sql,$inputarr); if ($rs) { if (!$rs->EOF) $ret = reset($rs->fields); $rs->Close(); } $ADODB_COUNTRECS = $crecs; return $ret; } function CacheGetOne($secs2cache,$sql=false,$inputarr=false) { $ret = false; $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); if ($rs) { if (!$rs->EOF) $ret = reset($rs->fields); $rs->Close(); } return $ret; } function GetCol($sql, $inputarr = false, $trim = false) { $rv = false; $rs = &$this->Execute($sql, $inputarr); if ($rs) { $rv = array(); if ($trim) { while (!$rs->EOF) { $rv[] = trim(reset($rs->fields)); $rs->MoveNext(); } } else { while (!$rs->EOF) { $rv[] = reset($rs->fields); $rs->MoveNext(); } } $rs->Close(); } return $rv; } function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) { $rv = false; $rs = &$this->CacheExecute($secs, $sql, $inputarr); if ($rs) { if ($trim) { while (!$rs->EOF) { $rv[] = trim(reset($rs->fields)); $rs->MoveNext(); } } else { while (!$rs->EOF) { $rv[] = reset($rs->fields); $rs->MoveNext(); } } $rs->Close(); } return $rv; } function &Transpose(&$rs) { $rs2 =& $this->_rs2rs($rs); $false = false; if (!$rs2) return $false; $rs2->_transpose(); return $rs2; } /* Calculate the offset of a date for a particular database and generate appropriate SQL. Useful for calculating future/past dates and storing in a database. If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour. */ function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; return '('.$date.'+'.$dayFraction.')'; } /** * * @param sql SQL statement * @param [inputarr] input bind array */ function &GetArray($sql,$inputarr=false) { global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs =& $this->Execute($sql,$inputarr); $ADODB_COUNTRECS = $savec; if (!$rs) if (defined('ADODB_PEAR')) { $cls = ADODB_PEAR_Error(); return $cls; } else { $false = false; return $false; } $arr =& $rs->GetArray(); $rs->Close(); return $arr; } function &CacheGetAll($secs2cache,$sql=false,$inputarr=false) { return $this->CacheGetArray($secs2cache,$sql,$inputarr); } function &CacheGetArray($secs2cache,$sql=false,$inputarr=false) { global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr); $ADODB_COUNTRECS = $savec; if (!$rs) if (defined('ADODB_PEAR')) { $cls = ADODB_PEAR_Error(); return $cls; } else { $false = false; return $false; } $arr =& $rs->GetArray(); $rs->Close(); return $arr; } /** * Return one row of sql statement. Recordset is disposed for you. * * @param sql SQL statement * @param [inputarr] input bind array */ function &GetRow($sql,$inputarr=false) { global $ADODB_COUNTRECS; $crecs = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs =& $this->Execute($sql,$inputarr); $ADODB_COUNTRECS = $crecs; if ($rs) { if (!$rs->EOF) $arr = $rs->fields; else $arr = array(); $rs->Close(); return $arr; } $false = false; return $false; } function &CacheGetRow($secs2cache,$sql=false,$inputarr=false) { $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr); if ($rs) { $arr = false; if (!$rs->EOF) $arr = $rs->fields; $rs->Close(); return $arr; } $false = false; return $false; } /** * Insert or replace a single record. Note: this is not the same as MySQL's replace. * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL. * Also note that no table locking is done currently, so it is possible that the * record be inserted twice by two programs... * * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname'); * * $table table name * $fieldArray associative array of data (you must quote strings yourself). * $keyCol the primary key field name or if compound key, array of field names * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers * but does not work with dates nor SQL functions. * has_autoinc the primary key is an auto-inc field, so skip in insert. * * Currently blob replace not supported * * returns 0 = fail, 1 = update, 2 = insert */ function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc); } /** * Will select, getting rows from $offset (1-based), for $nrows. * This simulates the MySQL "select * from table limit $offset,$nrows" , and * the PostgreSQL "select * from table limit $nrows offset $offset". Note that * MySQL and PostgreSQL parameter ordering is the opposite of the other. * eg. * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based) * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based) * * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set * * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional * @param sql * @param [offset] is the row to start calculations from (1-based) * @param [nrows] is the number of rows to get * @param [inputarr] array of bind variables * @return the recordset ($rs->databaseType == 'array') */ function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) { if (!is_numeric($secs2cache)) { if ($sql === false) $sql = -1; if ($offset == -1) $offset = false; // sql, nrows, offset,inputarr $rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs); } else { if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()"); $rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); } return $rs; } /** * Flush cached recordsets that match a particular $sql statement. * If $sql == false, then we purge all files in the cache. */ /** * Flush cached recordsets that match a particular $sql statement. * If $sql == false, then we purge all files in the cache. */ function CacheFlush($sql=false,$inputarr=false) { global $ADODB_CACHE_DIR; if ($this->memCache) { global $ADODB_INCLUDED_MEMCACHE; $key = false; if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php'); if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true); FlushMemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug); return; } if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) { /*if (strncmp(PHP_OS,'WIN',3) === 0) $dir = str_replace('/', '\\', $ADODB_CACHE_DIR); else */ $dir = $ADODB_CACHE_DIR; if ($this->debug) { ADOConnection::outp( "CacheFlush: $dir
\n", $this->_dirFlush($dir),"
"); } else { $this->_dirFlush($dir); } return; } global $ADODB_INCLUDED_CSV; if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php'); $f = $this->_gencachename($sql.serialize($inputarr),false); adodb_write_file($f,''); // is adodb_write_file needed? if (!@unlink($f)) { if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f"); } } /** * Private function to erase all of the files and subdirectories in a directory. * * Just specify the directory, and tell it if you want to delete the directory or just clear it out. * Note: $kill_top_level is used internally in the function to flush subdirectories. */ function _dirFlush($dir, $kill_top_level = false) { if(!$dh = @opendir($dir)) return; while (($obj = readdir($dh))) { if($obj=='.' || $obj=='..') continue; if (!@unlink($dir.'/'.$obj)) $this->_dirFlush($dir.'/'.$obj, true); } if ($kill_top_level === true) @rmdir($dir); return true; } function xCacheFlush($sql=false,$inputarr=false) { global $ADODB_CACHE_DIR; if ($this->memCache) { global $ADODB_INCLUDED_MEMCACHE; $key = false; if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php'); if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true); flushmemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug); return; } if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) { if (strncmp(PHP_OS,'WIN',3) === 0) { $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache'; } else { //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f'; $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/'; // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`'; } if ($this->debug) { ADOConnection::outp( "CacheFlush: $cmd
\n", system($cmd),"
"); } else { exec($cmd); } return; } global $ADODB_INCLUDED_CSV; if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php'); $f = $this->_gencachename($sql.serialize($inputarr),false); adodb_write_file($f,''); // is adodb_write_file needed? if (!@unlink($f)) { if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f"); } } /** * Private function to generate filename for caching. * Filename is generated based on: * * - sql statement * - database type (oci8, ibase, ifx, etc) * - database name * - userid * - setFetchMode (adodb 4.23) * * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). * Assuming that we can have 50,000 files per directory with good performance, * then we can scale to 12.8 million unique cached recordsets. Wow! */ function _gencachename($sql,$createdir,$memcache=false) { global $ADODB_CACHE_DIR; static $notSafeMode; if ($this->fetchMode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } else { $mode = $this->fetchMode; } $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode); if ($memcache) return $m; if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode'); $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR; if ($createdir && $notSafeMode && !file_exists($dir)) { $oldu = umask(0); if (!mkdir($dir,0771)) if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql"); umask($oldu); } return $dir.'/adodb_'.$m.'.cache'; } /** * Execute SQL, caching recordsets. * * @param [secs2cache] seconds to cache data, set to 0 to force query. * This is an optional parameter. * @param sql SQL statement to execute * @param [inputarr] holds the input data to bind to * @return RecordSet or false */ function &CacheExecute($secs2cache,$sql=false,$inputarr=false) { if (!is_numeric($secs2cache)) { $inputarr = $sql; $sql = $secs2cache; $secs2cache = $this->cacheSecs; } if (is_array($sql)) { $sqlparam = $sql; $sql = $sql[0]; } else $sqlparam = $sql; if ($this->memCache) { global $ADODB_INCLUDED_MEMCACHE; if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php'); $md5file = $this->_gencachename($sql.serialize($inputarr),false,true); } else { global $ADODB_INCLUDED_CSV; if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php'); $md5file = $this->_gencachename($sql.serialize($inputarr),true); } $err = ''; if ($secs2cache > 0){ if ($this->memCache) $rs = &getmemCache($md5file,$err,$secs2cache, $this->memCacheHost, $this->memCachePort); else $rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass); $this->numCacheHits += 1; } else { $err='Timeout 1'; $rs = false; $this->numCacheMisses += 1; } if (!$rs) { // no cached rs found if ($this->debug) { if (get_magic_quotes_runtime() && !$this->memCache) { ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :("); } if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)"); } $rs = &$this->Execute($sqlparam,$inputarr); if ($rs && $this->memCache) { $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately if(!putmemCache($md5file, $rs, $this->memCacheHost, $this->memCachePort, $this->memCacheCompress, $this->debug)) { if ($fn = $this->raiseErrorFn) $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this); if ($this->debug) ADOConnection::outp( " Cache write error"); } } else if ($rs) { $eof = $rs->EOF; $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately $txt = _rs2serialize($rs,false,$sql); // serialize if (!adodb_write_file($md5file,$txt,$this->debug)) { if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this); } if ($this->debug) ADOConnection::outp( " Cache write error"); } if ($rs->EOF && !$eof) { $rs->MoveFirst(); //$rs = &csv2rs($md5file,$err); $rs->connection = &$this; // Pablo suggestion } } else if (!$this->memCache) @unlink($md5file); } else { $this->_errorMsg = ''; $this->_errorCode = 0; if ($this->fnCacheExecute) { $fn = $this->fnCacheExecute; $fn($this, $secs2cache, $sql, $inputarr); } // ok, set cached object found $rs->connection = &$this; // Pablo suggestion if ($this->debug){ $inBrowser = isset($_SERVER['HTTP_USER_AGENT']); $ttl = $rs->timeCreated + $secs2cache - time(); $s = is_array($sql) ? $sql[0] : $sql; if ($inBrowser) $s = ''.htmlspecialchars($s).''; ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]"); } } return $rs; } /* Similar to PEAR DB's autoExecute(), except that $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE If $mode == 'UPDATE', then $where is compulsory as a safety measure. $forceUpdate means that even if the data has not changed, perform update. */ function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false) { $false = false; $sql = 'SELECT * FROM '.$table; if ($where!==FALSE) $sql .= ' WHERE '.$where; else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) { ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause'); return $false; } $rs =& $this->SelectLimit($sql,1); if (!$rs) return $false; // table does not exist $rs->tableName = $table; switch((string) $mode) { case 'UPDATE': case '2': $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq); break; case 'INSERT': case '1': $sql = $this->GetInsertSQL($rs, $fields_values, $magicq); break; default: ADOConnection::outp("AutoExecute: Unknown mode=$mode"); return $false; } $ret = false; if ($sql) $ret = $this->Execute($sql); if ($ret) $ret = true; return $ret; } /** * Generates an Update Query based on an existing recordset. * $arrFields is an associative array of fields with the value * that should be assigned. * * Note: This function should only be used on a recordset * that is run against a single table and sql should only * be a simple select stmt with no groupby/orderby/limit * * "Jonathan Younger" */ function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null) { global $ADODB_INCLUDED_LIB; //********************************************************// //This is here to maintain compatibility //with older adodb versions. Sets force type to force nulls if $forcenulls is set. if (!isset($force)) { global $ADODB_FORCE_TYPE; $force = $ADODB_FORCE_TYPE; } //********************************************************// if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force); } /** * Generates an Insert Query based on an existing recordset. * $arrFields is an associative array of fields with the value * that should be assigned. * * Note: This function should only be used on a recordset * that is run against a single table. */ function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null) { global $ADODB_INCLUDED_LIB; if (!isset($force)) { global $ADODB_FORCE_TYPE; $force = $ADODB_FORCE_TYPE; } if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force); } /** * Update a blob column, given a where clause. There are more sophisticated * blob handling functions that we could have implemented, but all require * a very complex API. Instead we have chosen something that is extremely * simple to understand and use. * * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course. * * Usage to update a $blobvalue which has a primary key blob_id=1 into a * field blobtable.blobcolumn: * * UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1'); * * Insert example: * * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; } /** * Usage: * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1'); * * $blobtype supports 'BLOB' and 'CLOB' * * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1'); */ function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') { $fd = fopen($path,'rb'); if ($fd === false) return false; $val = fread($fd,filesize($path)); fclose($fd); return $this->UpdateBlob($table,$column,$val,$where,$blobtype); } function BlobDecode($blob) { return $blob; } function BlobEncode($blob) { return $blob; } function SetCharSet($charset) { return false; } function IfNull( $field, $ifNull ) { return " CASE WHEN $field is null THEN $ifNull ELSE $field END "; } function LogSQL($enable=true) { include_once(ADODB_DIR.'/adodb-perf.inc.php'); if ($enable) $this->fnExecute = 'adodb_log_sql'; else $this->fnExecute = false; $old = $this->_logsql; $this->_logsql = $enable; if ($enable && !$old) $this->_affected = false; return $old; } function GetCharSet() { return false; } /** * Usage: * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB'); * * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)'); * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1'); */ function UpdateClob($table,$column,$val,$where) { return $this->UpdateBlob($table,$column,$val,$where,'CLOB'); } // not the fastest implementation - quick and dirty - jlim // for best performance, use the actual $rs->MetaType(). function MetaType($t,$len=-1,$fieldobj=false) { if (empty($this->_metars)) { $rsclass = $this->rsPrefix.$this->databaseType; $this->_metars =& new $rsclass(false,$this->fetchMode); $this->_metars->connection =& $this; } return $this->_metars->MetaType($t,$len,$fieldobj); } /** * Change the SQL connection locale to a specified locale. * This is used to get the date formats written depending on the client locale. */ function SetDateLocale($locale = 'En') { $this->locale = $locale; switch (strtoupper($locale)) { case 'EN': $this->fmtDate="'Y-m-d'"; $this->fmtTimeStamp = "'Y-m-d H:i:s'"; break; case 'US': $this->fmtDate = "'m-d-Y'"; $this->fmtTimeStamp = "'m-d-Y H:i:s'"; break; case 'NL': case 'FR': case 'RO': case 'IT': $this->fmtDate="'d-m-Y'"; $this->fmtTimeStamp = "'d-m-Y H:i:s'"; break; case 'GE': $this->fmtDate="'d.m.Y'"; $this->fmtTimeStamp = "'d.m.Y H:i:s'"; break; default: $this->fmtDate="'Y-m-d'"; $this->fmtTimeStamp = "'Y-m-d H:i:s'"; break; } } function &GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false) { global $_ADODB_ACTIVE_DBS; $save = $this->SetFetchMode(ADODB_FETCH_NUM); if (empty($whereOrderBy)) $whereOrderBy = '1=1'; $rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr); $this->SetFetchMode($save); $false = false; if ($rows === false) { return $false; } if (!isset($_ADODB_ACTIVE_DBS)) { include(ADODB_DIR.'/adodb-active-record.inc.php'); } if (!class_exists($class)) { ADOConnection::outp("Unknown class $class in GetActiveRcordsClass()"); return $false; } $arr = array(); foreach($rows as $row) { $obj =& new $class($table,$primkeyArr,$this); if ($obj->ErrorMsg()){ $this->_errorMsg = $obj->ErrorMsg(); return $false; } $obj->Set($row); $arr[] =& $obj; } return $arr; } function &GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) { $arr =& $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr); return $arr; } /** * Close Connection */ function Close() { $rez = $this->_close(); $this->_connectionID = false; return $rez; } /** * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans(). * * @return true if succeeded or false if database does not support transactions */ function BeginTrans() {return false;} /* set transaction mode */ function SetTransactionMode( $transaction_mode ) { $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider); $this->_transmode = $transaction_mode; } /* http://msdn2.microsoft.com/en-US/ms173763.aspx http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm */ function MetaTransaction($mode,$db) { $mode = strtoupper($mode); $mode = str_replace('ISOLATION LEVEL ','',$mode); switch($mode) { case 'READ UNCOMMITTED': switch($db) { case 'oci8': case 'oracle': return 'ISOLATION LEVEL READ COMMITTED'; default: return 'ISOLATION LEVEL READ UNCOMMITTED'; } break; case 'READ COMMITTED': return 'ISOLATION LEVEL READ COMMITTED'; break; case 'REPEATABLE READ': switch($db) { case 'oci8': case 'oracle': return 'ISOLATION LEVEL SERIALIZABLE'; default: return 'ISOLATION LEVEL REPEATABLE READ'; } break; case 'SERIALIZABLE': return 'ISOLATION LEVEL SERIALIZABLE'; break; default: return $mode; } } /** * If database does not support transactions, always return true as data always commited * * @param $ok set to false to rollback transaction, true to commit * * @return true/false. */ function CommitTrans($ok=true) { return true;} /** * If database does not support transactions, rollbacks always fail, so return false * * @return true/false. */ function RollbackTrans() { return false;} /** * return the databases that the driver can connect to. * Some databases will return an empty array. * * @return an array of database names. */ function MetaDatabases() { global $ADODB_FETCH_MODE; if ($this->metaDatabasesSQL) { $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $arr = $this->GetCol($this->metaDatabasesSQL); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; return $arr; } return false; } /** * @param ttype can either be 'VIEW' or 'TABLE' or false. * If false, both views and tables are returned. * "VIEW" returns only views * "TABLE" returns only tables * @param showSchema returns the schema/user with the table name, eg. USER.TABLE * @param mask is the input mask - only supported by oci8 and postgresql * * @return array of tables for current database. */ function &MetaTables($ttype=false,$showSchema=false,$mask=false) { global $ADODB_FETCH_MODE; $false = false; if ($mask) { return $false; } if ($this->metaTablesSQL) { $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute($this->metaTablesSQL); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false) return $false; $arr =& $rs->GetArray(); $arr2 = array(); if ($hast = ($ttype && isset($arr[0][1]))) { $showt = strncmp($ttype,'T',1); } for ($i=0; $i < sizeof($arr); $i++) { if ($hast) { if ($showt == 0) { if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); } else { if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); } } else $arr2[] = trim($arr[$i][0]); } $rs->Close(); return $arr2; } return $false; } function _findschema(&$table,&$schema) { if (!$schema && ($at = strpos($table,'.')) !== false) { $schema = substr($table,0,$at); $table = substr($table,$at+1); } } /** * List columns in a database as an array of ADOFieldObjects. * See top of file for definition of object. * * @param $table table name to query * @param $normalize makes table name case-insensitive (required by some databases) * @schema is optional database schema to use - not supported by all databases. * * @return array of ADOFieldObjects for current table. */ function &MetaColumns($table,$normalize=true) { global $ADODB_FETCH_MODE; $false = false; if (!empty($this->metaColumnsSQL)) { $schema = false; $this->_findschema($table,$schema); $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false || $rs->EOF) return $false; $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; if (isset($rs->fields[3]) && $rs->fields[3]) { if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3]; $fld->scale = $rs->fields[4]; if ($fld->scale>0) $fld->max_length += 1; } else $fld->max_length = $rs->fields[2]; if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); return $retarr; } return $false; } /** * List indexes on a table as an array. * @param table table name to query * @param primary true to only show primary keys. Not actually used for most databases * * @return array of indexes on current table. Each element represents an index, and is itself an associative array. Array ( [name_of_index] => Array ( [unique] => true or false [columns] => Array ( [0] => firstname [1] => lastname ) ) */ function &MetaIndexes($table, $primary = false, $owner = false) { $false = false; return $false; } /** * List columns names in a table as an array. * @param table table name to query * * @return array of column names for current table. */ function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) { $objarr =& $this->MetaColumns($table); if (!is_array($objarr)) { $false = false; return $false; } $arr = array(); if ($numIndexes) { $i = 0; if ($useattnum) { foreach($objarr as $v) $arr[$v->attnum] = $v->name; } else foreach($objarr as $v) $arr[$i++] = $v->name; } else foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; return $arr; } /** * Different SQL databases used different methods to combine strings together. * This function provides a wrapper. * * param s variable number of string parameters * * Usage: $db->Concat($str1,$str2); * * @return concatenated string */ function Concat() { $arr = func_get_args(); return implode($this->concat_operator, $arr); } /** * Converts a date "d" to a string that the database can understand. * * @param d a date in Unix date time format. * * @return date string in database date format */ function DBDate($d) { if (empty($d) && $d !== 0) return 'null'; if (is_string($d) && !is_numeric($d)) { if ($d === 'null' || strncmp($d,"'",1) === 0) return $d; if ($this->isoDates) return "'$d'"; $d = ADOConnection::UnixDate($d); } return adodb_date($this->fmtDate,$d); } function BindDate($d) { $d = $this->DBDate($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } function BindTimeStamp($d) { $d = $this->DBTimeStamp($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } /** * Converts a timestamp "ts" to a string that the database can understand. * * @param ts a timestamp in Unix date time format. * * @return timestamp string in database timestamp format */ function DBTimeStamp($ts) { if (empty($ts) && $ts !== 0) return 'null'; # strlen(14) allows YYYYMMDDHHMMSS format if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) return adodb_date($this->fmtTimeStamp,$ts); if ($ts === 'null') return $ts; if ($this->isoDates && strlen($ts) !== 14) return "'$ts'"; $ts = ADOConnection::UnixTimeStamp($ts); return adodb_date($this->fmtTimeStamp,$ts); } /** * Also in ADORecordSet. * @param $v is a date string in YYYY-MM-DD format * * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ function UnixDate($v) { if (is_object($v)) { // odbtp support //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); } if (is_numeric($v) && strlen($v) !== 8) return $v; if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) return false; if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0; // h-m-s-MM-DD-YY return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); } /** * Also in ADORecordSet. * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format * * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ function UnixTimeStamp($v) { if (is_object($v)) { // odbtp support //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); } if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) return false; if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0; // h-m-s-MM-DD-YY if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); } /** * Also in ADORecordSet. * * Format database date based on user defined format. * * @param v is the character date in YYYY-MM-DD format, returned by database * @param fmt is the format to apply to it, using date() * * @return a date formated as user desires */ function UserDate($v,$fmt='Y-m-d',$gmt=false) { $tt = $this->UnixDate($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR if (($tt === false || $tt == -1) && $v != false) return $v; else if ($tt == 0) return $this->emptyDate; else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR } return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); } /** * * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format * @param fmt is the format to apply to it, using date() * * @return a timestamp formated as user desires */ function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) { if (!isset($v)) return $this->emptyTimeStamp; # strlen(14) allows YYYYMMDDHHMMSS format if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); $tt = $this->UnixTimeStamp($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR if (($tt === false || $tt == -1) && $v != false) return $v; if ($tt == 0) return $this->emptyTimeStamp; return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); } function escape($s,$magic_quotes=false) { return $this->addq($s,$magic_quotes); } /** * Quotes a string, without prefixing nor appending quotes. */ function addq($s,$magic_quotes=false) { if (!$magic_quotes) { if ($this->replaceQuote[0] == '\\'){ // only since php 4.0.5 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); } return str_replace("'",$this->replaceQuote,$s); } // undo magic quotes for " $s = str_replace('\\"','"',$s); if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything return $s; else {// change \' to '' for sybase/mssql $s = str_replace('\\\\','\\',$s); return str_replace("\\'",$this->replaceQuote,$s); } } /** * Correctly quotes a string so that all strings are escaped. We prefix and append * to the string single-quotes. * An example is $db->qstr("Don't bother",magic_quotes_runtime()); * * @param s the string to quote * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). * This undoes the stupidity of magic quotes for GPC. * * @return quoted string to be sent back to database */ function qstr($s,$magic_quotes=false) { if (!$magic_quotes) { if ($this->replaceQuote[0] == '\\'){ // only since php 4.0.5 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); } return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " $s = str_replace('\\"','"',$s); if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything return "'$s'"; else {// change \' to '' for sybase/mssql $s = str_replace('\\\\','\\',$s); return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; } } /** * Will select the supplied $page number from a recordset, given that it is paginated in pages of * $nrows rows per page. It also saves two boolean values saying if the given page is the first * and/or last one of the recordset. Added by Ivn Oliva to provide recordset pagination. * * See readme.htm#ex8 for an example of usage. * * @param sql * @param nrows is the number of rows per page to get * @param page is the page number to get (1-based) * @param [inputarr] array of bind variables * @param [secs2cache] is a private parameter only used by jlim * @return the recordset ($rs->databaseType == 'array') * * NOTE: phpLens uses a different algorithm and does not use PageExecute(). * */ function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache); else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache); return $rs; } /** * Will select the supplied $page number from a recordset, given that it is paginated in pages of * $nrows rows per page. It also saves two boolean values saying if the given page is the first * and/or last one of the recordset. Added by Ivn Oliva to provide recordset pagination. * * @param secs2cache seconds to cache data, set to 0 to force query * @param sql * @param nrows is the number of rows per page to get * @param page is the page number to get (1-based) * @param [inputarr] array of bind variables * @return the recordset ($rs->databaseType == 'array') */ function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) { /*switch($this->dataProvider) { case 'postgres': case 'mysql': break; default: $secs2cache = 0; break; }*/ $rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache); return $rs; } } // end class ADOConnection //============================================================================================== // CLASS ADOFetchObj //============================================================================================== /** * Internal placeholder for record objects. Used by ADORecordSet->FetchObj(). */ class ADOFetchObj { }; //============================================================================================== // CLASS ADORecordSet_empty //============================================================================================== /** * Lightweight recordset when there are no records to be returned */ class ADORecordSet_empty { var $dataProvider = 'empty'; var $databaseType = false; var $EOF = true; var $_numOfRows = 0; var $fields = false; var $connection = false; function RowCount() {return 0;} function RecordCount() {return 0;} function PO_RecordCount(){return 0;} function Close(){return true;} function FetchRow() {return false;} function FieldCount(){ return 0;} function Init() {} } //============================================================================================== // DATE AND TIME FUNCTIONS //============================================================================================== if (!defined('ADODB_DATE_VERSION')) include(ADODB_DIR.'/adodb-time.inc.php'); //============================================================================================== // CLASS ADORecordSet //============================================================================================== if (PHP_VERSION < 5) include_once(ADODB_DIR.'/adodb-php4.inc.php'); else include_once(ADODB_DIR.'/adodb-iterator.inc.php'); /** * RecordSet class that represents the dataset returned by the database. * To keep memory overhead low, this class holds only the current row in memory. * No prefetching of data is done, so the RecordCount() can return -1 ( which * means recordcount not known). */ class ADORecordSet extends ADODB_BASE_RS { /* * public variables */ var $dataProvider = "native"; var $fields = false; /// holds the current row data var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob /// in other words, we use a text area for editing. var $canSeek = false; /// indicates that seek is supported var $sql; /// sql text var $EOF = false; /// Indicates that the current record position is after the last record in a Recordset object. var $emptyTimeStamp = ' '; /// what to display when $time==0 var $emptyDate = ' '; /// what to display when $time==0 var $debug = false; var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets var $bind = false; /// used by Fields() to hold array - should be private? var $fetchMode; /// default fetch mode var $connection = false; /// the parent connection /* * private variables */ var $_numOfRows = -1; /** number of rows, or -1 */ var $_numOfFields = -1; /** number of fields in recordset */ var $_queryID = -1; /** This variable keeps the result link identifier. */ var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */ var $_closed = false; /** has recordset been closed */ var $_inited = false; /** Init() should only be called once */ var $_obj; /** Used by FetchObj */ var $_names; /** Used by FetchObj */ var $_currentPage = -1; /** Added by Ivn Oliva to implement recordset pagination */ var $_atFirstPage = false; /** Added by Ivn Oliva to implement recordset pagination */ var $_atLastPage = false; /** Added by Ivn Oliva to implement recordset pagination */ var $_lastPageNo = -1; var $_maxRecordCount = 0; var $datetime = false; /** * Constructor * * @param queryID this is the queryID returned by ADOConnection->_query() * */ function ADORecordSet($queryID) { $this->_queryID = $queryID; } function Init() { if ($this->_inited) return; $this->_inited = true; if ($this->_queryID) @$this->_initrs(); else { $this->_numOfRows = 0; $this->_numOfFields = 0; } if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) { $this->_currentRow = 0; if ($this->EOF = ($this->_fetch() === false)) { $this->_numOfRows = 0; // _numOfRows could be -1 } } else { $this->EOF = true; } } /** * Generate a SELECT tag string from a recordset, and return the string. * If the recordset has 2 cols, we treat the 1st col as the containing * the text to display to the user, and 2nd col as the return value. Default * strings are compared with the FIRST column. * * @param name name of SELECT tag * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox. * @param [blank1stItem] true to leave the 1st item in list empty * @param [multiple] true for listbox, false for popup * @param [size] #rows to show for listbox. not used by popup * @param [selectAttr] additional attributes to defined for SELECT tag. * useful for holding javascript onChange='...' handlers. & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with * column 0 (1st col) if this is true. This is not documented. * * @return HTML * * changes by glen.davies@cce.ac.nz to support multiple hilited items */ function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false, $size=0, $selectAttr='',$compareFields0=true) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple, $size, $selectAttr,$compareFields0); } /** * Generate a SELECT tag string from a recordset, and return the string. * If the recordset has 2 cols, we treat the 1st col as the containing * the text to display to the user, and 2nd col as the return value. Default * strings are compared with the SECOND column. * */ function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='') { return $this->GetMenu($name,$defstr,$blank1stItem,$multiple, $size, $selectAttr,false); } /* Grouped Menu */ function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false, $size=0, $selectAttr='') { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple, $size, $selectAttr,false); } /** * return recordset as a 2-dimensional array. * * @param [nRows] is the number of rows to return. -1 means every row. * * @return an array indexed by the rows (0-based) from the recordset */ function &GetArray($nRows = -1) { global $ADODB_EXTENSION; if ($ADODB_EXTENSION) { $results = adodb_getall($this,$nRows); return $results; } $results = array(); $cnt = 0; while (!$this->EOF && $nRows != $cnt) { $results[] = $this->fields; $this->MoveNext(); $cnt++; } return $results; } function &GetAll($nRows = -1) { $arr =& $this->GetArray($nRows); return $arr; } /* * Some databases allow multiple recordsets to be returned. This function * will return true if there is a next recordset, or false if no more. */ function NextRecordSet() { return false; } /** * return recordset as a 2-dimensional array. * Helper function for ADOConnection->SelectLimit() * * @param offset is the row to start calculations from (1-based) * @param [nrows] is the number of rows to return * * @return an array indexed by the rows (0-based) from the recordset */ function &GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { $arr =& $this->GetArray($nrows); return $arr; } $this->Move($offset); $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { $results[$cnt++] = $this->fields; $this->MoveNext(); } return $results; } /** * Synonym for GetArray() for compatibility with ADO. * * @param [nRows] is the number of rows to return. -1 means every row. * * @return an array indexed by the rows (0-based) from the recordset */ function &GetRows($nRows = -1) { $arr =& $this->GetArray($nRows); return $arr; } /** * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. * The first column is treated as the key and is not included in the array. * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless * $force_array == true. * * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing, * read the source. * * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and * instead of returning array[col0] => array(remaining cols), return array[col0] => col1 * * @return an associative array indexed by the first column of the array, * or false if the data has less than 2 cols. */ function &GetAssoc($force_array = false, $first2cols = false) { global $ADODB_EXTENSION; $cols = $this->_numOfFields; if ($cols < 2) { $false = false; return $false; } $numIndex = isset($this->fields[0]); $results = array(); if (!$first2cols && ($cols > 2 || $force_array)) { if ($ADODB_EXTENSION) { if ($numIndex) { while (!$this->EOF) { // $results[trim($this->fields[0])] = array_slice($this->fields, 1); // Fix for array_slice re-numbering numeric associative keys in PHP5 $keys = array_slice(array_keys($this->fields), 1); $sliced_array = array(); foreach($keys as $key) { $sliced_array[$key] = $this->fields[$key]; } $results[trim(reset($this->fields))] = $sliced_array; adodb_movenext($this); } } else { while (!$this->EOF) { $results[trim(reset($this->fields))] = array_slice($this->fields, 1); adodb_movenext($this); } } } else { if ($numIndex) { while (!$this->EOF) { //$results[trim($this->fields[0])] = array_slice($this->fields, 1); // Fix for array_slice re-numbering numeric associative keys in PHP5 $keys = array_slice(array_keys($this->fields), 1); $sliced_array = array(); foreach($keys as $key) { $sliced_array[$key] = $this->fields[$key]; } $results[trim(reset($this->fields))] = $sliced_array; $this->MoveNext(); } } else { while (!$this->EOF) { $results[trim(reset($this->fields))] = array_slice($this->fields, 1); $this->MoveNext(); } } } } else { if ($ADODB_EXTENSION) { // return scalar values if ($numIndex) { while (!$this->EOF) { // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string $results[trim(($this->fields[0]))] = $this->fields[1]; adodb_movenext($this); } } else { while (!$this->EOF) { // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string $v1 = trim(reset($this->fields)); $v2 = ''.next($this->fields); $results[$v1] = $v2; adodb_movenext($this); } } } else { if ($numIndex) { while (!$this->EOF) { // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string $results[trim(($this->fields[0]))] = $this->fields[1]; $this->MoveNext(); } } else { while (!$this->EOF) { // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string $v1 = trim(reset($this->fields)); $v2 = ''.next($this->fields); $results[$v1] = $v2; $this->MoveNext(); } } } } $ref =& $results; # workaround accelerator incompat with PHP 4.4 :( return $ref; } /** * * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format * @param fmt is the format to apply to it, using date() * * @return a timestamp formated as user desires */ function UserTimeStamp($v,$fmt='Y-m-d H:i:s') { if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v); $tt = $this->UnixTimeStamp($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR if (($tt === false || $tt == -1) && $v != false) return $v; if ($tt === 0) return $this->emptyTimeStamp; return adodb_date($fmt,$tt); } /** * @param v is the character date in YYYY-MM-DD format, returned by database * @param fmt is the format to apply to it, using date() * * @return a date formated as user desires */ function UserDate($v,$fmt='Y-m-d') { $tt = $this->UnixDate($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR if (($tt === false || $tt == -1) && $v != false) return $v; else if ($tt == 0) return $this->emptyDate; else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR } return adodb_date($fmt,$tt); } /** * @param $v is a date string in YYYY-MM-DD format * * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ function UnixDate($v) { return ADOConnection::UnixDate($v); } /** * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format * * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format */ function UnixTimeStamp($v) { return ADOConnection::UnixTimeStamp($v); } /** * PEAR DB Compat - do not use internally */ function Free() { return $this->Close(); } /** * PEAR DB compat, number of rows */ function NumRows() { return $this->_numOfRows; } /** * PEAR DB compat, number of cols */ function NumCols() { return $this->_numOfFields; } /** * Fetch a row, returning false if no more rows. * This is PEAR DB compat mode. * * @return false or array containing the current record */ function &FetchRow() { if ($this->EOF) { $false = false; return $false; } $arr = $this->fields; $this->_currentRow++; if (!$this->_fetch()) $this->EOF = true; return $arr; } /** * Fetch a row, returning PEAR_Error if no more rows. * This is PEAR DB compat mode. * * @return DB_OK or error object */ function FetchInto(&$arr) { if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false; $arr = $this->fields; $this->MoveNext(); return 1; // DB_OK } /** * Move to the first row in the recordset. Many databases do NOT support this. * * @return true or false */ function MoveFirst() { if ($this->_currentRow == 0) return true; return $this->Move(0); } /** * Move to the last row in the recordset. * * @return true or false */ function MoveLast() { if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1); if ($this->EOF) return false; while (!$this->EOF) { $f = $this->fields; $this->MoveNext(); } $this->fields = $f; $this->EOF = false; return true; } /** * Move to next record in the recordset. * * @return true if there still rows available, or false if there are no more rows (EOF). */ function MoveNext() { if (!$this->EOF) { $this->_currentRow++; if ($this->_fetch()) return true; } $this->EOF = true; /* -- tested error handling when scrolling cursor -- seems useless. $conn = $this->connection; if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) { $fn = $conn->raiseErrorFn; $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database); } */ return false; } /** * Random access to a specific row in the recordset. Some databases do not support * access to previous rows in the databases (no scrolling backwards). * * @param rowNumber is the row to move to (0-based) * * @return true if there still rows available, or false if there are no more rows (EOF). */ function Move($rowNumber = 0) { $this->EOF = false; if ($rowNumber == $this->_currentRow) return true; if ($rowNumber >= $this->_numOfRows) if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2; if ($this->canSeek) { if ($this->_seek($rowNumber)) { $this->_currentRow = $rowNumber; if ($this->_fetch()) { return true; } } else { $this->EOF = true; return false; } } else { if ($rowNumber < $this->_currentRow) return false; global $ADODB_EXTENSION; if ($ADODB_EXTENSION) { while (!$this->EOF && $this->_currentRow < $rowNumber) { adodb_movenext($this); } } else { while (! $this->EOF && $this->_currentRow < $rowNumber) { $this->_currentRow++; if (!$this->_fetch()) $this->EOF = true; } } return !($this->EOF); } $this->fields = false; $this->EOF = true; return false; } /** * Get the value of a field in the current row by column name. * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM. * * @param colname is the field to access * * @return the value of $colname column */ function Fields($colname) { return $this->fields[$colname]; } function GetAssocKeys($upper=true) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); if ($upper === 2) $this->bind[$o->name] = $i; else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i; } } /** * Use associative array to get fields array for databases that do not support * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it * * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC * before you execute your SQL statement, and access $rs->fields['col'] directly. * * $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField */ function &GetRowAssoc($upper=1) { $record = array(); // if (!$this->fields) return $record; if (!$this->bind) { $this->GetAssocKeys($upper); } foreach($this->bind as $k => $v) { $record[$k] = $this->fields[$v]; } return $record; } /** * Clean up recordset * * @return true or false */ function Close() { // free connection object - this seems to globally free the object // and not merely the reference, so don't do this... // $this->connection = false; if (!$this->_closed) { $this->_closed = true; return $this->_close(); } else return true; } /** * synonyms RecordCount and RowCount * * @return the number of rows or -1 if this is not supported */ function RecordCount() {return $this->_numOfRows;} /* * If we are using PageExecute(), this will return the maximum possible rows * that can be returned when paging a recordset. */ function MaxRecordCount() { return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount(); } /** * synonyms RecordCount and RowCount * * @return the number of rows or -1 if this is not supported */ function RowCount() {return $this->_numOfRows;} /** * Portable RecordCount. Pablo Roca * * @return the number of records from a previous SELECT. All databases support this. * * But aware possible problems in multiuser environments. For better speed the table * must be indexed by the condition. Heavy test this before deploying. */ function PO_RecordCount($table="", $condition="") { $lnumrows = $this->_numOfRows; // the database doesn't support native recordcount, so we do a workaround if ($lnumrows == -1 && $this->connection) { IF ($table) { if ($condition) $condition = " WHERE " . $condition; $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition"); if ($resultrows) $lnumrows = reset($resultrows->fields); } } return $lnumrows; } /** * @return the current row in the recordset. If at EOF, will return the last row. 0-based. */ function CurrentRow() {return $this->_currentRow;} /** * synonym for CurrentRow -- for ADO compat * * @return the current row in the recordset. If at EOF, will return the last row. 0-based. */ function AbsolutePosition() {return $this->_currentRow;} /** * @return the number of columns in the recordset. Some databases will set this to 0 * if no records are returned, others will return the number of columns in the query. */ function FieldCount() {return $this->_numOfFields;} /** * Get the ADOFieldObject of a specific column. * * @param fieldoffset is the column position to access(0-based). * * @return the ADOFieldObject for that column, or false. */ function &FetchField($fieldoffset) { // must be defined by child class } /** * Get the ADOFieldObjects of all columns in an array. * */ function& FieldTypesArray() { $arr = array(); for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) $arr[] = $this->FetchField($i); return $arr; } /** * Return the fields array of the current row as an object for convenience. * The default case is lowercase field names. * * @return the object with the properties set to the fields of the current row */ function &FetchObj() { $o =& $this->FetchObject(false); return $o; } /** * Return the fields array of the current row as an object for convenience. * The default case is uppercase. * * @param $isupper to set the object property names to uppercase * * @return the object with the properties set to the fields of the current row */ function &FetchObject($isupper=true) { if (empty($this->_obj)) { $this->_obj = new ADOFetchObj(); $this->_names = array(); for ($i=0; $i <$this->_numOfFields; $i++) { $f = $this->FetchField($i); $this->_names[] = $f->name; } } $i = 0; if (PHP_VERSION >= 5) $o = clone($this->_obj); else $o = $this->_obj; for ($i=0; $i <$this->_numOfFields; $i++) { $name = $this->_names[$i]; if ($isupper) $n = strtoupper($name); else $n = $name; $o->$n = $this->Fields($name); } return $o; } /** * Return the fields array of the current row as an object for convenience. * The default is lower-case field names. * * @return the object with the properties set to the fields of the current row, * or false if EOF * * Fixed bug reported by tim@orotech.net */ function &FetchNextObj() { $o =& $this->FetchNextObject(false); return $o; } /** * Return the fields array of the current row as an object for convenience. * The default is upper case field names. * * @param $isupper to set the object property names to uppercase * * @return the object with the properties set to the fields of the current row, * or false if EOF * * Fixed bug reported by tim@orotech.net */ function &FetchNextObject($isupper=true) { $o = false; if ($this->_numOfRows != 0 && !$this->EOF) { $o = $this->FetchObject($isupper); $this->_currentRow++; if ($this->_fetch()) return $o; } $this->EOF = true; return $o; } /** * Get the metatype of the column. This is used for formatting. This is because * many databases use different names for the same type, so we transform the original * type to our standardised version which uses 1 character codes: * * @param t is the type passed in. Normally is ADOFieldObject->type. * @param len is the maximum length of that field. This is because we treat character * fields bigger than a certain size as a 'B' (blob). * @param fieldobj is the field object returned by the database driver. Can hold * additional info (eg. primary_key for mysql). * * @return the general type of the data: * C for character < 250 chars * X for teXt (>= 250 chars) * B for Binary * N for numeric or floating point * D for date * T for timestamp * L for logical/Boolean * I for integer * R for autoincrement counter/integer * * */ function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } // changed in 2.32 to hashing instead of switch stmt for speed... static $typeMap = array( 'VARCHAR' => 'C', 'VARCHAR2' => 'C', 'CHAR' => 'C', 'C' => 'C', 'STRING' => 'C', 'NCHAR' => 'C', 'NVARCHAR' => 'C', 'VARYING' => 'C', 'BPCHAR' => 'C', 'CHARACTER' => 'C', 'INTERVAL' => 'C', # Postgres 'MACADDR' => 'C', # postgres ## 'LONGCHAR' => 'X', 'TEXT' => 'X', 'NTEXT' => 'X', 'M' => 'X', 'X' => 'X', 'CLOB' => 'X', 'NCLOB' => 'X', 'LVARCHAR' => 'X', ## 'BLOB' => 'B', 'IMAGE' => 'B', 'BINARY' => 'B', 'VARBINARY' => 'B', 'LONGBINARY' => 'B', 'B' => 'B', ## 'YEAR' => 'D', // mysql 'DATE' => 'D', 'D' => 'D', ## 'TIME' => 'T', 'TIMESTAMP' => 'T', 'DATETIME' => 'T', 'TIMESTAMPTZ' => 'T', 'T' => 'T', 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql ## 'BOOL' => 'L', 'BOOLEAN' => 'L', 'BIT' => 'L', 'L' => 'L', ## 'COUNTER' => 'R', 'R' => 'R', 'SERIAL' => 'R', // ifx 'INT IDENTITY' => 'R', ## 'INT' => 'I', 'INT2' => 'I', 'INT4' => 'I', 'INT8' => 'I', 'INTEGER' => 'I', 'INTEGER UNSIGNED' => 'I', 'SHORT' => 'I', 'TINYINT' => 'I', 'SMALLINT' => 'I', 'I' => 'I', ## 'LONG' => 'N', // interbase is numeric, oci8 is blob 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers 'DECIMAL' => 'N', 'DEC' => 'N', 'REAL' => 'N', 'DOUBLE' => 'N', 'DOUBLE PRECISION' => 'N', 'SMALLFLOAT' => 'N', 'FLOAT' => 'N', 'NUMBER' => 'N', 'NUM' => 'N', 'NUMERIC' => 'N', 'MONEY' => 'N', ## informix 9.2 'SQLINT' => 'I', 'SQLSERIAL' => 'I', 'SQLSMINT' => 'I', 'SQLSMFLOAT' => 'N', 'SQLFLOAT' => 'N', 'SQLMONEY' => 'N', 'SQLDECIMAL' => 'N', 'SQLDATE' => 'D', 'SQLVCHAR' => 'C', 'SQLCHAR' => 'C', 'SQLDTIME' => 'T', 'SQLINTERVAL' => 'N', 'SQLBYTES' => 'B', 'SQLTEXT' => 'X', ## informix 10 "SQLINT8" => 'I8', "SQLSERIAL8" => 'I8', "SQLNCHAR" => 'C', "SQLNVCHAR" => 'C', "SQLLVARCHAR" => 'X', "SQLBOOL" => 'L' ); $tmap = false; $t = strtoupper($t); $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; switch ($tmap) { case 'C': // is the char field is too long, return as text field... if ($this->blobSize >= 0) { if ($len > $this->blobSize) return 'X'; } else if ($len > 250) { return 'X'; } return 'C'; case 'I': if (!empty($fieldobj->primary_key)) return 'R'; return 'I'; case false: return 'N'; case 'B': if (isset($fieldobj->binary)) return ($fieldobj->binary) ? 'B' : 'X'; return 'B'; case 'D': if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T'; return 'D'; default: if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; return $tmap; } } function _close() {} /** * set/returns the current recordset page when paginating */ function AbsolutePage($page=-1) { if ($page != -1) $this->_currentPage = $page; return $this->_currentPage; } /** * set/returns the status of the atFirstPage flag when paginating */ function AtFirstPage($status=false) { if ($status != false) $this->_atFirstPage = $status; return $this->_atFirstPage; } function LastPageNo($page = false) { if ($page != false) $this->_lastPageNo = $page; return $this->_lastPageNo; } /** * set/returns the status of the atLastPage flag when paginating */ function AtLastPage($status=false) { if ($status != false) $this->_atLastPage = $status; return $this->_atLastPage; } } // end class ADORecordSet //============================================================================================== // CLASS ADORecordSet_array //============================================================================================== /** * This class encapsulates the concept of a recordset created in memory * as an array. This is useful for the creation of cached recordsets. * * Note that the constructor is different from the standard ADORecordSet */ class ADORecordSet_array extends ADORecordSet { var $databaseType = 'array'; var $_array; // holds the 2-dimensional data array var $_types; // the array of types of each column (C B I L M) var $_colnames; // names of each column in array var $_skiprow1; // skip 1st row because it holds column names var $_fieldobjects; // holds array of field objects var $canSeek = true; var $affectedrows = false; var $insertid = false; var $sql = ''; var $compat = false; /** * Constructor * */ function ADORecordSet_array($fakeid=1) { global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH; // fetch() on EOF does not delete $this->fields $this->compat = !empty($ADODB_COMPAT_FETCH); $this->ADORecordSet($fakeid); // fake queryID $this->fetchMode = $ADODB_FETCH_MODE; } function _transpose() { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); $hdr = true; adodb_transpose($this->_array, $newarr, $hdr); //adodb_pr($newarr); $this->_skiprow1 = false; $this->_array =& $newarr; $this->_colnames = $hdr; adodb_probetypes($newarr,$this->_types); $this->_fieldobjects = array(); foreach($hdr as $k => $name) { $f = new ADOFieldObject(); $f->name = $name; $f->type = $this->_types[$k]; $f->max_length = -1; $this->_fieldobjects[] = $f; } $this->fields = reset($this->_array); $this->_initrs(); } /** * Setup the array. * * @param array is a 2-dimensional array holding the data. * The first row should hold the column names * unless paramter $colnames is used. * @param typearr holds an array of types. These are the same types * used in MetaTypes (C,B,L,I,N). * @param [colnames] array of column names. If set, then the first row of * $array should not hold the column names. */ function InitArray($array,$typearr,$colnames=false) { $this->_array = $array; $this->_types = $typearr; if ($colnames) { $this->_skiprow1 = false; $this->_colnames = $colnames; } else { $this->_skiprow1 = true; $this->_colnames = $array[0]; } $this->Init(); } /** * Setup the Array and datatype file objects * * @param array is a 2-dimensional array holding the data. * The first row should hold the column names * unless paramter $colnames is used. * @param fieldarr holds an array of ADOFieldObject's. */ function InitArrayFields(&$array,&$fieldarr) { $this->_array =& $array; $this->_skiprow1= false; if ($fieldarr) { $this->_fieldobjects =& $fieldarr; } $this->Init(); } function &GetArray($nRows=-1) { if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) { return $this->_array; } else { $arr =& ADORecordSet::GetArray($nRows); return $arr; } } function _initrs() { $this->_numOfRows = sizeof($this->_array); if ($this->_skiprow1) $this->_numOfRows -= 1; $this->_numOfFields =(isset($this->_fieldobjects)) ? sizeof($this->_fieldobjects):sizeof($this->_types); } /* Use associative array to get fields array */ function Fields($colname) { $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode; if ($mode & ADODB_FETCH_ASSOC) { if (!isset($this->fields[$colname])) $colname = strtolower($colname); return $this->fields[$colname]; } if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function &FetchField($fieldOffset = -1) { if (isset($this->_fieldobjects)) { return $this->_fieldobjects[$fieldOffset]; } $o = new ADOFieldObject(); $o->name = $this->_colnames[$fieldOffset]; $o->type = $this->_types[$fieldOffset]; $o->max_length = -1; // length not known return $o; } function _seek($row) { if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) { $this->_currentRow = $row; if ($this->_skiprow1) $row += 1; $this->fields = $this->_array[$row]; return true; } return false; } function MoveNext() { if (!$this->EOF) { $this->_currentRow++; $pos = $this->_currentRow; if ($this->_numOfRows <= $pos) { if (!$this->compat) $this->fields = false; } else { if ($this->_skiprow1) $pos += 1; $this->fields = $this->_array[$pos]; return true; } $this->EOF = true; } return false; } function _fetch() { $pos = $this->_currentRow; if ($this->_numOfRows <= $pos) { if (!$this->compat) $this->fields = false; return false; } if ($this->_skiprow1) $pos += 1; $this->fields = $this->_array[$pos]; return true; } function _close() { return true; } } // ADORecordSet_array //============================================================================================== // HELPER FUNCTIONS //============================================================================================== /** * Synonym for ADOLoadCode. Private function. Do not use. * * @deprecated */ function ADOLoadDB($dbType) { return ADOLoadCode($dbType); } /** * Load the code for a specific database driver. Private function. Do not use. */ function ADOLoadCode($dbType) { global $ADODB_LASTDB; if (!$dbType) return false; $db = strtolower($dbType); switch ($db) { case 'ado': if (PHP_VERSION >= 5) $db = 'ado5'; $class = 'ado'; break; case 'ifx': case 'maxsql': $class = $db = 'mysqlt'; break; case 'postgres': case 'postgres8': case 'pgsql': $class = $db = 'postgres7'; break; default: $class = $db; break; } $file = ADODB_DIR."/drivers/adodb-".$db.".inc.php"; @include_once($file); $ADODB_LASTDB = $class; if (class_exists("ADODB_" . $class)) return $class; //ADOConnection::outp(adodb_pr(get_declared_classes(),true)); if (!file_exists($file)) ADOConnection::outp("Missing file: $file"); else ADOConnection::outp("Syntax error in file: $file"); return false; } /** * synonym for ADONewConnection for people like me who cannot remember the correct name */ function &NewADOConnection($db='') { $tmp =& ADONewConnection($db); return $tmp; } /** * Instantiate a new Connection class for a specific database driver. * * @param [db] is the database Connection object to create. If undefined, * use the last database driver that was loaded by ADOLoadCode(). * * @return the freshly created instance of the Connection class. */ function &ADONewConnection($db='') { GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB; if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false; $false = false; if ($at = strpos($db,'://')) { $origdsn = $db; if (PHP_VERSION < 5) $dsna = @parse_url($db); else { $fakedsn = 'fake'.substr($db,$at); $dsna = @parse_url($fakedsn); $dsna['scheme'] = substr($db,0,$at); if (strncmp($db,'pdo',3) == 0) { $sch = explode('_',$dsna['scheme']); if (sizeof($sch)>1) { $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : ''; $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host'])); $dsna['scheme'] = 'pdo'; } } } if (!$dsna) { // special handling of oracle, which might not have host $db = str_replace('@/','@adodb-fakehost/',$db); $dsna = parse_url($db); if (!$dsna) return $false; $dsna['host'] = ''; } $db = @$dsna['scheme']; if (!$db) return $false; $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : ''; $dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : ''; $dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : ''; $dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial / if (isset($dsna['query'])) { $opt1 = explode('&',$dsna['query']); foreach($opt1 as $k => $v) { $arr = explode('=',$v); $opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1; } } else $opt = array(); } /* * phptype: Database backend used in PHP (mysql, odbc etc.) * dbsyntax: Database used with regards to SQL syntax etc. * protocol: Communication protocol to use (tcp, unix etc.) * hostspec: Host specification (hostname[:port]) * database: Database to use on the DBMS server * username: User name for login * password: Password for login */ if (!empty($ADODB_NEWCONNECTION)) { $obj = $ADODB_NEWCONNECTION($db); } else { if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = ''; if (empty($db)) $db = $ADODB_LASTDB; if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db); if (!$db) { if (isset($origdsn)) $db = $origdsn; if ($errorfn) { // raise an error $ignore = false; $errorfn('ADONewConnection', 'ADONewConnection', -998, "could not load the database driver for '$db'", $db,false,$ignore); } else ADOConnection::outp( "

ADONewConnection: Unable to load database driver '$db'

",false); return $false; } $cls = 'ADODB_'.$db; if (!class_exists($cls)) { adodb_backtrace(); return $false; } $obj = new $cls(); } # constructor should not fail if ($obj) { if ($errorfn) $obj->raiseErrorFn = $errorfn; if (isset($dsna)) { if (isset($dsna['port'])) $obj->port = $dsna['port']; foreach($opt as $k => $v) { switch(strtolower($k)) { case 'new': $nconnect = true; $persist = true; break; case 'persist': case 'persistent': $persist = $v; break; case 'debug': $obj->debug = (integer) $v; break; #ibase case 'role': $obj->role = $v; break; case 'dialect': $obj->dialect = (integer) $v; break; case 'charset': $obj->charset = $v; $obj->charSet=$v; break; case 'buffers': $obj->buffers = $v; break; case 'fetchmode': $obj->SetFetchMode($v); break; #ado case 'charpage': $obj->charPage = $v; break; #mysql, mysqli case 'clientflags': $obj->clientFlags = $v; break; #mysql, mysqli, postgres case 'port': $obj->port = $v; break; #mysqli case 'socket': $obj->socket = $v; break; #oci8 case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break; } } if (empty($persist)) $ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']); else if (empty($nconnect)) $ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']); else $ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']); if (!$ok) return $false; } } return $obj; } // $perf == true means called by NewPerfMonitor(), otherwise for data dictionary function _adodb_getdriver($provider,$drivername,$perf=false) { switch ($provider) { case 'odbtp': if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6); case 'odbc' : if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5); case 'ado' : if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4); case 'native': break; default: return $provider; } switch($drivername) { case 'mysqlt': case 'mysqli': $drivername='mysql'; break; case 'postgres7': case 'postgres8': $drivername = 'postgres'; break; case 'firebird15': $drivername = 'firebird'; break; case 'oracle': $drivername = 'oci8'; break; case 'access': if ($perf) $drivername = ''; break; case 'db2' : break; case 'sapdb' : break; default: $drivername = 'generic'; break; } return $drivername; } function &NewPerfMonitor(&$conn) { $false = false; $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true); if (!$drivername || $drivername == 'generic') return $false; include_once(ADODB_DIR.'/adodb-perf.inc.php'); @include_once(ADODB_DIR."/perf/perf-$drivername.inc.php"); $class = "Perf_$drivername"; if (!class_exists($class)) return $false; $perf = new $class($conn); return $perf; } function &NewDataDictionary(&$conn,$drivername=false) { $false = false; if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType); include_once(ADODB_DIR.'/adodb-lib.inc.php'); include_once(ADODB_DIR.'/adodb-datadict.inc.php'); $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php"; if (!file_exists($path)) { ADOConnection::outp("Dictionary driver '$path' not available"); return $false; } include_once($path); $class = "ADODB2_$drivername"; $dict = new $class(); $dict->dataProvider = $conn->dataProvider; $dict->connection = &$conn; $dict->upperName = strtoupper($drivername); $dict->quote = $conn->nameQuote; if (!empty($conn->_connectionID)) $dict->serverInfo = $conn->ServerInfo(); return $dict; } /* Perform a print_r, with pre tags for better formatting. */ function adodb_pr($var,$as_string=false) { if ($as_string) ob_start(); if (isset($_SERVER['HTTP_USER_AGENT'])) { echo "
\n";print_r($var);echo "
\n"; } else print_r($var); if ($as_string) { $s = ob_get_contents(); ob_end_clean(); return $s; } } /* Perform a stack-crawl and pretty print it. @param printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then). @param levels Number of levels to display */ function adodb_backtrace($printOrArr=true,$levels=9999) { global $ADODB_INCLUDED_LIB; if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php'); return _adodb_backtrace($printOrArr,$levels); } } ?> phpgacl-3.3.7/adodb/adodb-memcache.lib.inc.php0100644025754300001440000000601210476657431017775 0ustar ipsouserspconnect($host, $port)) { $err = 'Can\'t connect to memcache server on: '.$host.':'.$port; return $false; } $rs = $memcache->get($key); if (!$rs) { $err = 'Item with such key doesn\'t exists on the memcached server.'; return $false; } $tdiff = intval($rs->timeCreated+$timeout - time()); if ($tdiff <= 2) { switch($tdiff) { case 2: if ((rand() & 15) == 0) { $err = "Timeout 2"; return $false; } break; case 1: if ((rand() & 3) == 0) { $err = "Timeout 1"; return $false; } break; default: $err = "Timeout 0"; return $false; } } return $rs; } function putmemcache($key, $rs, $host, $port, $compress, $debug=false) { $false = false; $true = true; if (!function_exists('memcache_pconnect')) { if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!
\n"); return $false; } $memcache = new Memcache; if (!@$memcache->pconnect($host, $port)) { if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port
\n"); return $false; } $rs->timeCreated = time(); if (!$memcache->set($key, $rs, $compress, 0)) { if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!
\n"); return $false; } return $true; } function flushmemcache($key=false, $host, $port, $debug=false) { if (!function_exists('memcache_pconnect')) { if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!
\n"); return; } $memcache = new Memcache; if (!@$memcache->pconnect($host, $port)) { if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port
\n"); return; } if ($key) { if (!$memcache->delete($key)) { if ($debug) ADOConnection::outp("CacheFlush: $key entery doesn't exist on memcached server!
\n"); } else { if ($debug) ADOConnection::outp("CacheFlush: $key entery flushed from memcached server!
\n"); } } else { if (!$memcache->flush()) { if ($debug) ADOConnection::outp("CacheFlush: Failure flushing all enteries from memcached server!
\n"); } else { if ($debug) ADOConnection::outp("CacheFlush: All enteries flushed from memcached server!
\n"); } } return; } ?> phpgacl-3.3.7/adodb/adodb-xmlschema03.inc.php0100644025754300001440000017231310476657431017622 0ustar ipsousersparent =& $parent; } /** * XML Callback to process start elements * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { } function create() { return array(); } /** * Destroys the object */ function destroy() { unset( $this ); } /** * Checks whether the specified RDBMS is supported by the current * database object or its ranking ancestor. * * @param string $platform RDBMS platform name (from ADODB platform list). * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE. */ function supportedPlatform( $platform = NULL ) { return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE; } /** * Returns the prefix set by the ranking ancestor of the database object. * * @param string $name Prefix string. * @return string Prefix. */ function prefix( $name = '' ) { return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name; } /** * Extracts a field ID from the specified field. * * @param string $field Field. * @return string Field ID. */ function FieldID( $field ) { return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) ); } } /** * Creates a table object in ADOdb's datadict format * * This class stores information about a database table. As charactaristics * of the table are loaded from the external source, methods and properties * of this class are used to build up the table description in ADOdb's * datadict format. * * @package axmls * @access private */ class dbTable extends dbObject { /** * @var string Table name */ var $name; /** * @var array Field specifier: Meta-information about each field */ var $fields = array(); /** * @var array List of table indexes. */ var $indexes = array(); /** * @var array Table options: Table-level options */ var $opts = array(); /** * @var string Field index: Keeps track of which field is currently being processed */ var $current_field; /** * @var boolean Mark table for destruction * @access private */ var $drop_table; /** * @var boolean Mark field for destruction (not yet implemented) * @access private */ var $drop_field = array(); /** * @var array Platform-specific options * @access private */ var $currentPlatform = true; /** * Iniitializes a new table object. * * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. */ function dbTable( &$parent, $attributes = NULL ) { $this->parent =& $parent; $this->name = $this->prefix($attributes['NAME']); } /** * XML Callback to process start elements. Elements currently * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'INDEX': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { xml_set_object( $parser, $this->addIndex( $attributes ) ); } break; case 'DATA': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { xml_set_object( $parser, $this->addData( $attributes ) ); } break; case 'DROP': $this->drop(); break; case 'FIELD': // Add a field $fieldName = $attributes['NAME']; $fieldType = $attributes['TYPE']; $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL; $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL; $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts ); break; case 'KEY': case 'NOTNULL': case 'AUTOINCREMENT': case 'DEFDATE': case 'DEFTIMESTAMP': case 'UNSIGNED': // Add a field option $this->addFieldOpt( $this->current_field, $this->currentElement ); break; case 'DEFAULT': // Add a field option to the table object // Work around ADOdb datadict issue that misinterprets empty strings. if( $attributes['VALUE'] == '' ) { $attributes['VALUE'] = " '' "; } $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] ); break; case 'OPT': case 'CONSTRAINT': // Accept platform-specific options $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ); break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Table/field constraint case 'CONSTRAINT': if( isset( $this->current_field ) ) { $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata ); } else { $this->addTableOpt( $cdata ); } break; // Table/field option case 'OPT': if( isset( $this->current_field ) ) { $this->addFieldOpt( $this->current_field, $cdata ); } else { $this->addTableOpt( $cdata ); } break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'TABLE': $this->parent->addSQL( $this->create( $this->parent ) ); xml_set_object( $parser, $this->parent ); $this->destroy(); break; case 'FIELD': unset($this->current_field); break; case 'OPT': case 'CONSTRAINT': $this->currentPlatform = true; break; default: } } /** * Adds an index to a table object * * @param array $attributes Index attributes * @return object dbIndex object */ function &addIndex( $attributes ) { $name = strtoupper( $attributes['NAME'] ); $this->indexes[$name] =& new dbIndex( $this, $attributes ); return $this->indexes[$name]; } /** * Adds data to a table object * * @param array $attributes Data attributes * @return object dbData object */ function &addData( $attributes ) { if( !isset( $this->data ) ) { $this->data =& new dbData( $this, $attributes ); } return $this->data; } /** * Adds a field to a table object * * $name is the name of the table to which the field should be added. * $type is an ADODB datadict field type. The following field types * are supported as of ADODB 3.40: * - C: varchar * - X: CLOB (character large object) or largest varchar size * if CLOB is not supported * - C2: Multibyte varchar * - X2: Multibyte CLOB * - B: BLOB (binary large object) * - D: Date (some databases do not support this, and we return a datetime type) * - T: Datetime or Timestamp * - L: Integer field suitable for storing booleans (0 or 1) * - I: Integer (mapped to I4) * - I1: 1-byte integer * - I2: 2-byte integer * - I4: 4-byte integer * - I8: 8-byte integer * - F: Floating point number * - N: Numeric or decimal number * * @param string $name Name of the table to which the field will be added. * @param string $type ADODB datadict field type. * @param string $size Field size * @param array $opts Field options array * @return array Field specifier array */ function addField( $name, $type, $size = NULL, $opts = NULL ) { $field_id = $this->FieldID( $name ); // Set the field index so we know where we are $this->current_field = $field_id; // Set the field name (required) $this->fields[$field_id]['NAME'] = $name; // Set the field type (required) $this->fields[$field_id]['TYPE'] = $type; // Set the field size (optional) if( isset( $size ) ) { $this->fields[$field_id]['SIZE'] = $size; } // Set the field options if( isset( $opts ) ) { $this->fields[$field_id]['OPTS'] = array($opts); } else { $this->fields[$field_id]['OPTS'] = array(); } } /** * Adds a field option to the current field specifier * * This method adds a field option allowed by the ADOdb datadict * and appends it to the given field. * * @param string $field Field name * @param string $opt ADOdb field option * @param mixed $value Field option value * @return array Field specifier array */ function addFieldOpt( $field, $opt, $value = NULL ) { if( $this->currentPlatform ) { if( !isset( $value ) ) { $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt; // Add the option and value } else { $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value ); } } } /** * Adds an option to the table * * This method takes a comma-separated list of table-level options * and appends them to the table object. * * @param string $opt Table option * @return array Options */ function addTableOpt( $opt ) { if( $this->currentPlatform ) { $this->opts[] = $opt; } return $this->opts; } /** * Generates the SQL that will create the table in the database * * @param object $xmls adoSchema object * @return array Array containing table creation SQL */ function create( &$xmls ) { $sql = array(); // drop any existing indexes if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) { foreach( $legacy_indexes as $index => $index_details ) { $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name ); } } // remove fields to be dropped from table object foreach( $this->drop_field as $field ) { unset( $this->fields[$field] ); } // if table exists if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) { // drop table if( $this->drop_table ) { $sql[] = $xmls->dict->DropTableSQL( $this->name ); return $sql; } // drop any existing fields not in schema foreach( $legacy_fields as $field_id => $field ) { if( !isset( $this->fields[$field_id] ) ) { $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name ); } } // if table doesn't exist } else { if( $this->drop_table ) { return $sql; } $legacy_fields = array(); } // Loop through the field specifier array, building the associative array for the field options $fldarray = array(); foreach( $this->fields as $field_id => $finfo ) { // Set an empty size if it isn't supplied if( !isset( $finfo['SIZE'] ) ) { $finfo['SIZE'] = ''; } // Initialize the field array with the type and size $fldarray[$field_id] = array( 'NAME' => $finfo['NAME'], 'TYPE' => $finfo['TYPE'], 'SIZE' => $finfo['SIZE'] ); // Loop through the options array and add the field options. if( isset( $finfo['OPTS'] ) ) { foreach( $finfo['OPTS'] as $opt ) { // Option has an argument. if( is_array( $opt ) ) { $key = key( $opt ); $value = $opt[key( $opt )]; @$fldarray[$field_id][$key] .= $value; // Option doesn't have arguments } else { $fldarray[$field_id][$opt] = $opt; } } } } if( empty( $legacy_fields ) ) { // Create the new table $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); logMsg( end( $sql ), 'Generated CreateTableSQL' ); } else { // Upgrade an existing table logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" ); switch( $xmls->upgrade ) { // Use ChangeTableSQL case 'ALTER': logMsg( 'Generated ChangeTableSQL (ALTERing table)' ); $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts ); break; case 'REPLACE': logMsg( 'Doing upgrade REPLACE (testing)' ); $sql[] = $xmls->dict->DropTableSQL( $this->name ); $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts ); break; // ignore table default: return array(); } } foreach( $this->indexes as $index ) { $sql[] = $index->create( $xmls ); } if( isset( $this->data ) ) { $sql[] = $this->data->create( $xmls ); } return $sql; } /** * Marks a field or table for destruction */ function drop() { if( isset( $this->current_field ) ) { // Drop the current field logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" ); // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field ); $this->drop_field[$this->current_field] = $this->current_field; } else { // Drop the current table logMsg( "Dropping table '{$this->name}'" ); // $this->drop_table = $xmls->dict->DropTableSQL( $this->name ); $this->drop_table = TRUE; } } } /** * Creates an index object in ADOdb's datadict format * * This class stores information about a database index. As charactaristics * of the index are loaded from the external source, methods and properties * of this class are used to build up the index description in ADOdb's * datadict format. * * @package axmls * @access private */ class dbIndex extends dbObject { /** * @var string Index name */ var $name; /** * @var array Index options: Index-level options */ var $opts = array(); /** * @var array Indexed fields: Table columns included in this index */ var $columns = array(); /** * @var boolean Mark index for destruction * @access private */ var $drop = FALSE; /** * Initializes the new dbIndex object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbIndex( &$parent, $attributes = NULL ) { $this->parent =& $parent; $this->name = $this->prefix ($attributes['NAME']); } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'DROP': $this->drop(); break; case 'CLUSTERED': case 'BITMAP': case 'UNIQUE': case 'FULLTEXT': case 'HASH': // Add index Option $this->addIndexOpt( $this->currentElement ); break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * Processes XML cdata. * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name case 'COL': $this->addField( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'INDEX': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the index * * @param string $name Field name * @return string Field list */ function addField( $name ) { $this->columns[$this->FieldID( $name )] = $name; // Return the field list return $this->columns; } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addIndexOpt( $opt ) { $this->opts[] = $opt; // Return the options list return $this->opts; } /** * Generates the SQL that will create the index in the database * * @param object $xmls adoSchema object * @return array Array containing index creation SQL */ function create( &$xmls ) { if( $this->drop ) { return NULL; } // eliminate any columns that aren't in the table foreach( $this->columns as $id => $col ) { if( !isset( $this->parent->fields[$id] ) ) { unset( $this->columns[$id] ); } } return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); } /** * Marks an index for destruction */ function drop() { $this->drop = TRUE; } } /** * Creates a data object in ADOdb's datadict format * * This class stores information about table data, and is called * when we need to load field data into a table. * * @package axmls * @access private */ class dbData extends dbObject { var $data = array(); var $row; /** * Initializes the new dbData object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbData( &$parent, $attributes = NULL ) { $this->parent =& $parent; } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: ROW and F (field). * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'ROW': $this->row = count( $this->data ); $this->data[$this->row] = array(); break; case 'F': $this->addField($attributes); default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * Processes XML cdata. * * @access private */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name case 'F': $this->addData( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'DATA': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the insert * * @param string $name Field name * @return string Field list */ function addField( $attributes ) { // check we're in a valid row if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) { return; } // Set the field index so we know where we are if( isset( $attributes['NAME'] ) ) { $this->current_field = $this->FieldID( $attributes['NAME'] ); } else { $this->current_field = count( $this->data[$this->row] ); } // initialise data if( !isset( $this->data[$this->row][$this->current_field] ) ) { $this->data[$this->row][$this->current_field] = ''; } } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addData( $cdata ) { // check we're in a valid field if ( isset( $this->data[$this->row][$this->current_field] ) ) { // add data to field $this->data[$this->row][$this->current_field] .= $cdata; } } /** * Generates the SQL that will add/update the data in the database * * @param object $xmls adoSchema object * @return array Array containing index creation SQL */ function create( &$xmls ) { $table = $xmls->dict->TableName($this->parent->name); $table_field_count = count($this->parent->fields); $tables = $xmls->db->MetaTables(); $sql = array(); $ukeys = $xmls->db->MetaPrimaryKeys( $table ); if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) { foreach( $this->parent->indexes as $indexObj ) { if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name; } } // eliminate any columns that aren't in the table foreach( $this->data as $row ) { $table_fields = $this->parent->fields; $fields = array(); $rawfields = array(); // Need to keep some of the unprocessed data on hand. foreach( $row as $field_id => $field_data ) { if( !array_key_exists( $field_id, $table_fields ) ) { if( is_numeric( $field_id ) ) { $field_id = reset( array_keys( $table_fields ) ); } else { continue; } } $name = $table_fields[$field_id]['NAME']; switch( $table_fields[$field_id]['TYPE'] ) { case 'I': case 'I1': case 'I2': case 'I4': case 'I8': $fields[$name] = intval($field_data); break; case 'C': case 'C2': case 'X': case 'X2': default: $fields[$name] = $xmls->db->qstr( $field_data ); $rawfields[$name] = $field_data; } unset($table_fields[$field_id]); } // check that at least 1 column is specified if( empty( $fields ) ) { continue; } // check that no required columns are missing if( count( $fields ) < $table_field_count ) { foreach( $table_fields as $field ) { if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) { continue(2); } } } // The rest of this method deals with updating existing data records. if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) { // Table doesn't yet exist, so it's safe to insert. logMsg( "$table doesn't exist, inserting or mode is INSERT" ); $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; continue; } // Prepare to test for potential violations. Get primary keys and unique indexes $mfields = array_merge( $fields, $rawfields ); $keyFields = array_intersect( $ukeys, array_keys( $mfields ) ); if( empty( $ukeys ) or count( $keyFields ) == 0 ) { // No unique keys in schema, so safe to insert logMsg( "Either schema or data has no unique keys, so safe to insert" ); $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; continue; } // Select record containing matching unique keys. $where = ''; foreach( $ukeys as $key ) { if( isset( $mfields[$key] ) and $mfields[$key] ) { if( $where ) $where .= ' AND '; $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] ); } } $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where ); switch( $records->RecordCount() ) { case 0: // No matching record, so safe to insert. logMsg( "No matching records. Inserting new row with unique data" ); $sql[] = $xmls->db->GetInsertSQL( $records, $mfields ); break; case 1: // Exactly one matching record, so we can update if the mode permits. logMsg( "One matching record..." ); if( $mode == XMLS_MODE_UPDATE ) { logMsg( "...Updating existing row from unique data" ); $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields ); } break; default: // More than one matching record; the result is ambiguous, so we must ignore the row. logMsg( "More than one matching record. Ignoring row." ); } } return $sql; } } /** * Creates the SQL to execute a list of provided SQL queries * * @package axmls * @access private */ class dbQuerySet extends dbObject { /** * @var array List of SQL queries */ var $queries = array(); /** * @var string String used to build of a query line by line */ var $query; /** * @var string Query prefix key */ var $prefixKey = ''; /** * @var boolean Auto prefix enable (TRUE) */ var $prefixMethod = 'AUTO'; /** * Initializes the query set. * * @param object $parent Parent object * @param array $attributes Attributes */ function dbQuerySet( &$parent, $attributes = NULL ) { $this->parent =& $parent; // Overrides the manual prefix key if( isset( $attributes['KEY'] ) ) { $this->prefixKey = $attributes['KEY']; } $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; // Enables or disables automatic prefix prepending switch( $prefixMethod ) { case 'AUTO': $this->prefixMethod = 'AUTO'; break; case 'MANUAL': $this->prefixMethod = 'MANUAL'; break; case 'NONE': $this->prefixMethod = 'NONE'; break; } } /** * XML Callback to process start elements. Elements currently * processed are: QUERY. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'QUERY': // Create a new query in a SQL queryset. // Ignore this query set if a platform is specified and it's different than the // current connection platform. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->newQuery(); } else { $this->discardQuery(); } break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Line of queryset SQL data case 'QUERY': $this->buildQuery( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'QUERY': // Add the finished query to the open query set. $this->addQuery(); break; case 'SQL': $this->parent->addSQL( $this->create( $this->parent ) ); xml_set_object( $parser, $this->parent ); $this->destroy(); break; default: } } /** * Re-initializes the query. * * @return boolean TRUE */ function newQuery() { $this->query = ''; return TRUE; } /** * Discards the existing query. * * @return boolean TRUE */ function discardQuery() { unset( $this->query ); return TRUE; } /** * Appends a line to a query that is being built line by line * * @param string $data Line of SQL data or NULL to initialize a new query * @return string SQL query string. */ function buildQuery( $sql = NULL ) { if( !isset( $this->query ) OR empty( $sql ) ) { return FALSE; } $this->query .= $sql; return $this->query; } /** * Adds a completed query to the query list * * @return string SQL of added query */ function addQuery() { if( !isset( $this->query ) ) { return FALSE; } $this->queries[] = $return = trim($this->query); unset( $this->query ); return $return; } /** * Creates and returns the current query set * * @param object $xmls adoSchema object * @return array Query set */ function create( &$xmls ) { foreach( $this->queries as $id => $query ) { switch( $this->prefixMethod ) { case 'AUTO': // Enable auto prefix replacement // Process object prefix. // Evaluate SQL statements to prepend prefix to objects $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); // SELECT statements aren't working yet #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data ); case 'MANUAL': // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX. // If prefixKey is not set, we use the default constant XMLS_PREFIX if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) { // Enable prefix override $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query ); } else { // Use default replacement $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query ); } } $this->queries[$id] = trim( $query ); } // Return the query set array return $this->queries; } /** * Rebuilds the query with the prefix attached to any objects * * @param string $regex Regex used to add prefix * @param string $query SQL query string * @param string $prefix Prefix to be appended to tables, indices, etc. * @return string Prefixed SQL query string. */ function prefixQuery( $regex, $query, $prefix = NULL ) { if( !isset( $prefix ) ) { return $query; } if( preg_match( $regex, $query, $match ) ) { $preamble = $match[1]; $postamble = $match[5]; $objectList = explode( ',', $match[3] ); // $prefix = $prefix . '_'; $prefixedList = ''; foreach( $objectList as $object ) { if( $prefixedList !== '' ) { $prefixedList .= ', '; } $prefixedList .= $prefix . trim( $object ); } $query = $preamble . ' ' . $prefixedList . ' ' . $postamble; } return $query; } } /** * Loads and parses an XML file, creating an array of "ready-to-run" SQL statements * * This class is used to load and parse the XML file, to create an array of SQL statements * that can be used to build a database, and to build the database using the SQL array. * * @tutorial getting_started.pkg * * @author Richard Tango-Lowy & Dan Cech * @version $Revision: 1.62 $ * * @package axmls */ class adoSchema { /** * @var array Array containing SQL queries to generate all objects * @access private */ var $sqlArray; /** * @var object ADOdb connection object * @access private */ var $db; /** * @var object ADOdb Data Dictionary * @access private */ var $dict; /** * @var string Current XML element * @access private */ var $currentElement = ''; /** * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database * @access private */ var $upgrade = ''; /** * @var string Optional object prefix * @access private */ var $objectPrefix = ''; /** * @var long Original Magic Quotes Runtime value * @access private */ var $mgq; /** * @var long System debug * @access private */ var $debug; /** * @var string Regular expression to find schema version * @access private */ var $versionRegex = '//'; /** * @var string Current schema version * @access private */ var $schemaVersion; /** * @var int Success of last Schema execution */ var $success; /** * @var bool Execute SQL inline as it is generated */ var $executeInline; /** * @var bool Continue SQL execution if errors occur */ var $continueOnError; /** * @var int How to handle existing data rows (insert, update, or ignore) */ var $existingData; /** * Creates an adoSchema object * * Creating an adoSchema object is the first step in processing an XML schema. * The only parameter is an ADOdb database connection object, which must already * have been created. * * @param object $db ADOdb database connection object. */ function adoSchema( &$db ) { // Initialize the environment $this->mgq = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); $this->db =& $db; $this->debug = $this->db->debug; $this->dict = NewDataDictionary( $this->db ); $this->sqlArray = array(); $this->schemaVersion = XMLS_SCHEMA_VERSION; $this->executeInline( XMLS_EXECUTE_INLINE ); $this->continueOnError( XMLS_CONTINUE_ON_ERROR ); $this->existingData( XMLS_EXISTING_DATA ); $this->setUpgradeMethod(); } /** * Sets the method to be used for upgrading an existing database * * Use this method to specify how existing database objects should be upgraded. * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to * alter each database object directly, REPLACE attempts to rebuild each object * from scratch, BEST attempts to determine the best upgrade method for each * object, and NONE disables upgrading. * * This method is not yet used by AXMLS, but exists for backward compatibility. * The ALTER method is automatically assumed when the adoSchema object is * instantiated; other upgrade methods are not currently supported. * * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE) * @returns string Upgrade method used */ function SetUpgradeMethod( $method = '' ) { if( !is_string( $method ) ) { return FALSE; } $method = strtoupper( $method ); // Handle the upgrade methods switch( $method ) { case 'ALTER': $this->upgrade = $method; break; case 'REPLACE': $this->upgrade = $method; break; case 'BEST': $this->upgrade = 'ALTER'; break; case 'NONE': $this->upgrade = 'NONE'; break; default: // Use default if no legitimate method is passed. $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD; } return $this->upgrade; } /** * Specifies how to handle existing data row when there is a unique key conflict. * * The existingData setting specifies how the parser should handle existing rows * when a unique key violation occurs during the insert. This can happen when inserting * data into an existing table with one or more primary keys or unique indexes. * The existingData method takes one of three options: XMLS_MODE_INSERT attempts * to always insert the data as a new row. In the event of a unique key violation, * the database will generate an error. XMLS_MODE_UPDATE attempts to update the * any existing rows with the new data based upon primary or unique key fields in * the schema. If the data row in the schema specifies no unique fields, the row * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows * that would result in a unique key violation be ignored; no inserts or updates will * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT, * but XMLS_MODE_UPDATE will generally be the most appropriate setting. * * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE * @return int current mode */ function ExistingData( $mode = NULL ) { if( is_int( $mode ) ) { switch( $mode ) { case XMLS_MODE_UPDATE: $mode = XMLS_MODE_UPDATE; break; case XMLS_MODE_IGNORE: $mode = XMLS_MODE_IGNORE; break; case XMLS_MODE_INSERT: $mode = XMLS_MODE_INSERT; break; default: $mode = XMLS_EXISITNG_DATA; break; } $this->existingData = $mode; } return $this->existingData; } /** * Enables/disables inline SQL execution. * * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution), * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema() * to apply the schema to the database. * * @param bool $mode execute * @return bool current execution mode * * @see ParseSchema(), ExecuteSchema() */ function ExecuteInline( $mode = NULL ) { if( is_bool( $mode ) ) { $this->executeInline = $mode; } return $this->executeInline; } /** * Enables/disables SQL continue on error. * * Call this method to enable or disable continuation of SQL execution if an error occurs. * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs. * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing * of the schema will continue. * * @param bool $mode execute * @return bool current continueOnError mode * * @see addSQL(), ExecuteSchema() */ function ContinueOnError( $mode = NULL ) { if( is_bool( $mode ) ) { $this->continueOnError = $mode; } return $this->continueOnError; } /** * Loads an XML schema from a file and converts it to SQL. * * Call this method to load the specified schema (see the DTD for the proper format) from * the filesystem and generate the SQL necessary to create the database * described. This method automatically converts the schema to the latest * axmls schema version. * @see ParseSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ function ParseSchema( $filename, $returnSchema = FALSE ) { return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); } /** * Loads an XML schema from a file and converts it to SQL. * * Call this method to load the specified schema directly from a file (see * the DTD for the proper format) and generate the SQL necessary to create * the database described by the schema. Use this method when you are dealing * with large schema files. Otherwise, ParseSchema() is faster. * This method does not automatically convert the schema to the latest axmls * schema version. You must convert the schema manually using either the * ConvertSchemaFile() or ConvertSchemaString() method. * @see ParseSchema() * @see ConvertSchemaFile() * @see ConvertSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. * * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString() * @see ParseSchema(), ParseSchemaString() */ function ParseSchemaFile( $filename, $returnSchema = FALSE ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { logMsg( 'Unable to open file' ); return FALSE; } // do version detection here if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) { logMsg( 'Invalid Schema Version' ); return FALSE; } if( $returnSchema ) { $xmlstring = ''; while( $data = fread( $fp, 4096 ) ) { $xmlstring .= $data . "\n"; } return $xmlstring; } $this->success = 2; $xmlParser = $this->create_parser(); // Process the file while( $data = fread( $fp, 4096 ) ) { if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) { die( sprintf( "XML error: %s at line %d", xml_error_string( xml_get_error_code( $xmlParser) ), xml_get_current_line_number( $xmlParser) ) ); } } xml_parser_free( $xmlParser ); return $this->sqlArray; } /** * Converts an XML schema string to SQL. * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to create the database described by the schema. * @see ParseSchema() * * @param string $xmlstring XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { logMsg( 'Empty or Invalid Schema' ); return FALSE; } // do version detection here if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) { logMsg( 'Invalid Schema Version' ); return FALSE; } if( $returnSchema ) { return $xmlstring; } $this->success = 2; $xmlParser = $this->create_parser(); if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) { die( sprintf( "XML error: %s at line %d", xml_error_string( xml_get_error_code( $xmlParser) ), xml_get_current_line_number( $xmlParser) ) ); } xml_parser_free( $xmlParser ); return $this->sqlArray; } /** * Loads an XML schema from a file and converts it to uninstallation SQL. * * Call this method to load the specified schema (see the DTD for the proper format) from * the filesystem and generate the SQL necessary to remove the database described. * @see RemoveSchemaString() * * @param string $file Name of XML schema file. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute */ function RemoveSchema( $filename, $returnSchema = FALSE ) { return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema ); } /** * Converts an XML schema string to uninstallation SQL. * * Call this method to parse a string containing an XML schema (see the DTD for the proper format) * and generate the SQL necessary to uninstall the database described by the schema. * @see RemoveSchema() * * @param string $schema XML schema string. * @param bool $returnSchema Return schema rather than parsing. * @return array Array of SQL queries, ready to execute. */ function RemoveSchemaString( $schema, $returnSchema = FALSE ) { // grab current version if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { return FALSE; } return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema ); } /** * Applies the current XML schema to the database (post execution). * * Call this method to apply the current schema (generally created by calling * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, * and executing other SQL specified in the schema) after parsing. * @see ParseSchema(), ParseSchemaString(), ExecuteInline() * * @param array $sqlArray Array of SQL statements that will be applied rather than * the current schema. * @param boolean $continueOnErr Continue to apply the schema even if an error occurs. * @returns integer 0 if failure, 1 if errors, 2 if successful. */ function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) { if( !is_bool( $continueOnErr ) ) { $continueOnErr = $this->ContinueOnError(); } if( !isset( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !is_array( $sqlArray ) ) { $this->success = 0; } else { $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr ); } return $this->success; } /** * Returns the current SQL array. * * Call this method to fetch the array of SQL queries resulting from * ParseSchema() or ParseSchemaString(). * * @param string $format Format: HTML, TEXT, or NONE (PHP array) * @return array Array of SQL statements or FALSE if an error occurs */ function PrintSQL( $format = 'NONE' ) { $sqlArray = null; return $this->getSQL( $format, $sqlArray ); } /** * Saves the current SQL array to the local filesystem as a list of SQL queries. * * Call this method to save the array of SQL queries (generally resulting from a * parsed XML schema) to the filesystem. * * @param string $filename Path and name where the file should be saved. * @return boolean TRUE if save is successful, else FALSE. */ function SaveSQL( $filename = './schema.sql' ) { if( !isset( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !isset( $sqlArray ) ) { return FALSE; } $fp = fopen( $filename, "w" ); foreach( $sqlArray as $key => $query ) { fwrite( $fp, $query . ";\n" ); } fclose( $fp ); } /** * Create an xml parser * * @return object PHP XML parser object * * @access private */ function &create_parser() { // Create the parser $xmlParser = xml_parser_create(); xml_set_object( $xmlParser, $this ); // Initialize the XML callback functions xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' ); xml_set_character_data_handler( $xmlParser, '_tag_cdata' ); return $xmlParser; } /** * XML Callback to process start elements * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { switch( strtoupper( $tag ) ) { case 'TABLE': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->obj = new dbTable( $this, $attributes ); xml_set_object( $parser, $this->obj ); } break; case 'SQL': if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->obj = new dbQuerySet( $this, $attributes ); xml_set_object( $parser, $this->obj ); } break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements * * @access private */ function _tag_cdata( &$parser, $cdata ) { } /** * XML Callback to process end elements * * @access private * @internal */ function _tag_close( &$parser, $tag ) { } /** * Converts an XML schema string to the specified DTD version. * * Call this method to convert a string containing an XML schema to a different AXMLS * DTD version. For instance, to convert a schema created for an pre-1.0 version for * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. * @see ConvertSchemaFile() * * @param string $schema String containing XML schema that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) { // grab current version if( !( $version = $this->SchemaStringVersion( $schema ) ) ) { return FALSE; } if( !isset ($newVersion) ) { $newVersion = $this->schemaVersion; } if( $version == $newVersion ) { $result = $schema; } else { $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { fwrite( $fp, $result ); fclose( $fp ); } return $result; } /* // compat for pre-4.3 - jlim function _file_get_contents($path) { if (function_exists('file_get_contents')) return file_get_contents($path); return join('',file($path)); }*/ /** * Converts an XML schema file to the specified DTD version. * * Call this method to convert the specified XML schema file to a different AXMLS * DTD version. For instance, to convert a schema created for an pre-1.0 version for * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version * parameter is specified, the schema will be converted to the current DTD version. * If the newFile parameter is provided, the converted schema will be written to the specified * file. * @see ConvertSchemaString() * * @param string $filename Name of XML schema file that will be converted. * @param string $newVersion DTD version to convert to. * @param string $newFile File name of (converted) output file. * @return string Converted XML schema or FALSE if an error occurs. */ function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) { // grab current version if( !( $version = $this->SchemaFileVersion( $filename ) ) ) { return FALSE; } if( !isset ($newVersion) ) { $newVersion = $this->schemaVersion; } if( $version == $newVersion ) { $result = _file_get_contents( $filename ); // remove unicode BOM if present if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) { $result = substr( $result, 3 ); } } else { $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' ); } if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) { fwrite( $fp, $result ); fclose( $fp ); } return $result; } function TransformSchema( $schema, $xsl, $schematype='string' ) { // Fail if XSLT extension is not available if( ! function_exists( 'xslt_create' ) ) { return FALSE; } $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl'; // look for xsl if( !is_readable( $xsl_file ) ) { return FALSE; } switch( $schematype ) { case 'file': if( !is_readable( $schema ) ) { return FALSE; } $schema = _file_get_contents( $schema ); break; case 'string': default: if( !is_string( $schema ) ) { return FALSE; } } $arguments = array ( '/_xml' => $schema, '/_xsl' => _file_get_contents( $xsl_file ) ); // create an XSLT processor $xh = xslt_create (); // set error handler xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler')); // process the schema $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); xslt_free ($xh); return $result; } /** * Processes XSLT transformation errors * * @param object $parser XML parser object * @param integer $errno Error number * @param integer $level Error level * @param array $fields Error information fields * * @access private */ function xslt_error_handler( $parser, $errno, $level, $fields ) { if( is_array( $fields ) ) { $msg = array( 'Message Type' => ucfirst( $fields['msgtype'] ), 'Message Code' => $fields['code'], 'Message' => $fields['msg'], 'Error Number' => $errno, 'Level' => $level ); switch( $fields['URI'] ) { case 'arg:/_xml': $msg['Input'] = 'XML'; break; case 'arg:/_xsl': $msg['Input'] = 'XSL'; break; default: $msg['Input'] = $fields['URI']; } $msg['Line'] = $fields['line']; } else { $msg = array( 'Message Type' => 'Error', 'Error Number' => $errno, 'Level' => $level, 'Fields' => var_export( $fields, TRUE ) ); } $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n" . '' . "\n"; foreach( $msg as $label => $details ) { $error_details .= '' . "\n"; } $error_details .= '
' . $label . ': ' . htmlentities( $details ) . '
'; trigger_error( $error_details, E_USER_ERROR ); } /** * Returns the AXMLS Schema Version of the requested XML schema file. * * Call this method to obtain the AXMLS DTD version of the requested XML schema file. * @see SchemaStringVersion() * * @param string $filename AXMLS schema file * @return string Schema version number or FALSE on error */ function SchemaFileVersion( $filename ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { // die( 'Unable to open file' ); return FALSE; } // Process the file while( $data = fread( $fp, 4096 ) ) { if( preg_match( $this->versionRegex, $data, $matches ) ) { return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; } } return FALSE; } /** * Returns the AXMLS Schema Version of the provided XML schema string. * * Call this method to obtain the AXMLS DTD version of the provided XML schema string. * @see SchemaFileVersion() * * @param string $xmlstring XML schema string * @return string Schema version number or FALSE on error */ function SchemaStringVersion( $xmlstring ) { if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) { return FALSE; } if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) { return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION; } return FALSE; } /** * Extracts an XML schema from an existing database. * * Call this method to create an XML schema string from an existing database. * If the data parameter is set to TRUE, AXMLS will include the data from the database * in the schema. * * @param boolean $data Include data in schema dump * @indent string indentation to use * @prefix string extract only tables with given prefix * @stripprefix strip prefix string when storing in XML schema * @return string Generated XML schema */ function ExtractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) { $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM ); $schema = '' . "\n" . '' . "\n"; if( is_array( $tables = $this->db->MetaTables( 'TABLES' , ($prefix) ? $prefix.'%' : '') ) ) { foreach( $tables as $table ) { if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table); $schema .= $indent . '' . "\n"; // grab details from database $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' ); $fields = $this->db->MetaColumns( $table ); $indexes = $this->db->MetaIndexes( $table ); if( is_array( $fields ) ) { foreach( $fields as $details ) { $extra = ''; $content = array(); if( isset($details->max_length) && $details->max_length > 0 ) { $extra .= ' size="' . $details->max_length . '"'; } if( isset($details->primary_key) && $details->primary_key ) { $content[] = ''; } elseif( isset($details->not_null) && $details->not_null ) { $content[] = ''; } if( isset($details->has_default) && $details->has_default ) { $content[] = ''; } if( isset($details->auto_increment) && $details->auto_increment ) { $content[] = ''; } if( isset($details->unsigned) && $details->unsigned ) { $content[] = ''; } // this stops the creation of 'R' columns, // AUTOINCREMENT is used to create auto columns $details->primary_key = 0; $type = $rs->MetaType( $details ); $schema .= str_repeat( $indent, 2 ) . '' . "\n"; } else { $schema .= "/>\n"; } } } if( is_array( $indexes ) ) { foreach( $indexes as $index => $details ) { $schema .= str_repeat( $indent, 2 ) . '' . "\n"; if( $details['unique'] ) { $schema .= str_repeat( $indent, 3 ) . '' . "\n"; } foreach( $details['columns'] as $column ) { $schema .= str_repeat( $indent, 3 ) . '' . htmlentities( $column ) . '' . "\n"; } $schema .= str_repeat( $indent, 2 ) . '' . "\n"; } } if( $data ) { $rs = $this->db->Execute( 'SELECT * FROM ' . $table ); if( is_object( $rs ) && !$rs->EOF ) { $schema .= str_repeat( $indent, 2 ) . "\n"; while( $row = $rs->FetchRow() ) { foreach( $row as $key => $val ) { if ( $val != htmlentities( $val ) ) { $row[$key] = ''; } } $schema .= str_repeat( $indent, 3 ) . '' . implode( '', $row ) . "\n"; } $schema .= str_repeat( $indent, 2 ) . "\n"; } } $schema .= $indent . "
\n"; } } $this->db->SetFetchMode( $old_mode ); $schema .= '
'; return $schema; } /** * Sets a prefix for database objects * * Call this method to set a standard prefix that will be prepended to all database tables * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix. * * @param string $prefix Prefix that will be prepended. * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix. * @return boolean TRUE if successful, else FALSE */ function SetPrefix( $prefix = '', $underscore = TRUE ) { switch( TRUE ) { // clear prefix case empty( $prefix ): logMsg( 'Cleared prefix' ); $this->objectPrefix = ''; return TRUE; // prefix too long case strlen( $prefix ) > XMLS_PREFIX_MAXLEN: // prefix contains invalid characters case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ): logMsg( 'Invalid prefix: ' . $prefix ); return FALSE; } if( $underscore AND substr( $prefix, -1 ) != '_' ) { $prefix .= '_'; } // prefix valid logMsg( 'Set prefix: ' . $prefix ); $this->objectPrefix = $prefix; return TRUE; } /** * Returns an object name with the current prefix prepended. * * @param string $name Name * @return string Prefixed name * * @access private */ function prefix( $name = '' ) { // if prefix is set if( !empty( $this->objectPrefix ) ) { // Prepend the object prefix to the table name // prepend after quote if used return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name ); } // No prefix set. Use name provided. return $name; } /** * Checks if element references a specific platform * * @param string $platform Requested platform * @returns boolean TRUE if platform check succeeds * * @access private */ function supportedPlatform( $platform = NULL ) { if( !empty( $platform ) ) { $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i'; if( preg_match( '/^- /', $platform ) ) { if (preg_match ( $regex, substr( $platform, 2 ) ) ) { logMsg( 'Platform ' . $platform . ' is NOT supported' ); return FALSE; } } else { if( !preg_match ( $regex, $platform ) ) { logMsg( 'Platform ' . $platform . ' is NOT supported' ); return FALSE; } } } logMsg( 'Platform ' . $platform . ' is supported' ); return TRUE; } /** * Clears the array of generated SQL. * * @access private */ function clearSQL() { $this->sqlArray = array(); } /** * Adds SQL into the SQL array. * * @param mixed $sql SQL to Add * @return boolean TRUE if successful, else FALSE. * * @access private */ function addSQL( $sql = NULL ) { if( is_array( $sql ) ) { foreach( $sql as $line ) { $this->addSQL( $line ); } return TRUE; } if( is_string( $sql ) ) { $this->sqlArray[] = $sql; // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL. if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) { $saved = $this->db->debug; $this->db->debug = $this->debug; $ok = $this->db->Execute( $sql ); $this->db->debug = $saved; if( !$ok ) { if( $this->debug ) { ADOConnection::outp( $this->db->ErrorMsg() ); } $this->success = 1; } } return TRUE; } return FALSE; } /** * Gets the SQL array in the specified format. * * @param string $format Format * @return mixed SQL * * @access private */ function getSQL( $format = NULL, $sqlArray = NULL ) { if( !is_array( $sqlArray ) ) { $sqlArray = $this->sqlArray; } if( !is_array( $sqlArray ) ) { return FALSE; } switch( strtolower( $format ) ) { case 'string': case 'text': return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : ''; case'html': return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : ''; } return $this->sqlArray; } /** * Destroys an adoSchema object. * * Call this method to clean up after an adoSchema object that is no longer in use. * @deprecated adoSchema now cleans up automatically. */ function Destroy() { set_magic_quotes_runtime( $this->mgq ); unset( $this ); } } /** * Message logging function * * @access private */ function logMsg( $msg, $title = NULL, $force = FALSE ) { if( XMLS_DEBUG or $force ) { echo '
';
		
		if( isset( $title ) ) {
			echo '

' . htmlentities( $title ) . '

'; } if( @is_object( $this ) ) { echo '[' . get_class( $this ) . '] '; } print_r( $msg ); echo '
'; } } ?>phpgacl-3.3.7/adodb/adodb-datadict.inc.php0100644025754300001440000005106310476657245017254 0ustar ipsousers$str

"; $a= Lens_ParseArgs($str); print "
";
print_r($a);
print "
"; } if (!function_exists('ctype_alnum')) { function ctype_alnum($text) { return preg_match('/^[a-z0-9]*$/i', $text); } } //Lens_ParseTest(); /** Parse arguments, treat "text" (text) and 'text' as quotation marks. To escape, use "" or '' or )) Will read in "abc def" sans quotes, as: abc def Same with 'abc def'. However if `abc def`, then will read in as `abc def` @param endstmtchar Character that indicates end of statement @param tokenchars Include the following characters in tokens apart from A-Z and 0-9 @returns 2 dimensional array containing parsed tokens. */ function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-') { $pos = 0; $intoken = false; $stmtno = 0; $endquote = false; $tokens = array(); $tokens[$stmtno] = array(); $max = strlen($args); $quoted = false; $tokarr = array(); while ($pos < $max) { $ch = substr($args,$pos,1); switch($ch) { case ' ': case "\t": case "\n": case "\r": if (!$quoted) { if ($intoken) { $intoken = false; $tokens[$stmtno][] = implode('',$tokarr); } break; } $tokarr[] = $ch; break; case '`': if ($intoken) $tokarr[] = $ch; case '(': case ')': case '"': case "'": if ($intoken) { if (empty($endquote)) { $tokens[$stmtno][] = implode('',$tokarr); if ($ch == '(') $endquote = ')'; else $endquote = $ch; $quoted = true; $intoken = true; $tokarr = array(); } else if ($endquote == $ch) { $ch2 = substr($args,$pos+1,1); if ($ch2 == $endquote) { $pos += 1; $tokarr[] = $ch2; } else { $quoted = false; $intoken = false; $tokens[$stmtno][] = implode('',$tokarr); $endquote = ''; } } else $tokarr[] = $ch; }else { if ($ch == '(') $endquote = ')'; else $endquote = $ch; $quoted = true; $intoken = true; $tokarr = array(); if ($ch == '`') $tokarr[] = '`'; } break; default: if (!$intoken) { if ($ch == $endstmtchar) { $stmtno += 1; $tokens[$stmtno] = array(); break; } $intoken = true; $quoted = false; $endquote = false; $tokarr = array(); } if ($quoted) $tokarr[] = $ch; else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch; else { if ($ch == $endstmtchar) { $tokens[$stmtno][] = implode('',$tokarr); $stmtno += 1; $tokens[$stmtno] = array(); $intoken = false; $tokarr = array(); break; } $tokens[$stmtno][] = implode('',$tokarr); $tokens[$stmtno][] = $ch; $intoken = false; } } $pos += 1; } if ($intoken) $tokens[$stmtno][] = implode('',$tokarr); return $tokens; } class ADODB_DataDict { var $connection; var $debug = false; var $dropTable = 'DROP TABLE %s'; var $renameTable = 'RENAME TABLE %s TO %s'; var $dropIndex = 'DROP INDEX %s'; var $addCol = ' ADD'; var $alterCol = ' ALTER COLUMN'; var $dropCol = ' DROP COLUMN'; var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s'; // table, old-column, new-column, column-definitions (not used by default) var $nameRegex = '\w'; var $nameRegexBrackets = 'a-zA-Z0-9_\(\)'; var $schema = false; var $serverInfo = array(); var $autoIncrement = false; var $dataProvider; var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changetablesql var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob /// in other words, we use a text area for editting. function GetCommentSQL($table,$col) { return false; } function SetCommentSQL($table,$col,$cmt) { return false; } function MetaTables() { if (!$this->connection->IsConnected()) return array(); return $this->connection->MetaTables(); } function MetaColumns($tab, $upper=true, $schema=false) { if (!$this->connection->IsConnected()) return array(); return $this->connection->MetaColumns($this->TableName($tab), $upper, $schema); } function MetaPrimaryKeys($tab,$owner=false,$intkey=false) { if (!$this->connection->IsConnected()) return array(); return $this->connection->MetaPrimaryKeys($this->TableName($tab), $owner, $intkey); } function MetaIndexes($table, $primary = false, $owner = false) { if (!$this->connection->IsConnected()) return array(); return $this->connection->MetaIndexes($this->TableName($table), $primary, $owner); } function MetaType($t,$len=-1,$fieldobj=false) { return ADORecordSet::MetaType($t,$len,$fieldobj); } function NameQuote($name = NULL,$allowBrackets=false) { if (!is_string($name)) { return FALSE; } $name = trim($name); if ( !is_object($this->connection) ) { return $name; } $quote = $this->connection->nameQuote; // if name is of the form `name`, quote it if ( preg_match('/^`(.+)`$/', $name, $matches) ) { return $quote . $matches[1] . $quote; } // if name contains special characters, quote it $regex = ($allowBrackets) ? $this->nameRegexBrackets : $this->nameRegex; if ( !preg_match('/^[' . $regex . ']+$/', $name) ) { return $quote . $name . $quote; } return $name; } function TableName($name) { if ( $this->schema ) { return $this->NameQuote($this->schema) .'.'. $this->NameQuote($name); } return $this->NameQuote($name); } // Executes the sql array returned by GetTableSQL and GetIndexSQL function ExecuteSQLArray($sql, $continueOnError = true) { $rez = 2; $conn = &$this->connection; $saved = $conn->debug; foreach($sql as $line) { if ($this->debug) $conn->debug = true; $ok = $conn->Execute($line); $conn->debug = $saved; if (!$ok) { if ($this->debug) ADOConnection::outp($conn->ErrorMsg()); if (!$continueOnError) return 0; $rez = 1; } } return $rez; } /* Returns the actual type given a character code. C: varchar X: CLOB (character large object) or largest varchar size if CLOB is not supported C2: Multibyte varchar X2: Multibyte CLOB B: BLOB (binary large object) D: Date T: Date-time L: Integer field suitable for storing booleans (0 or 1) I: Integer F: Floating point number N: Numeric or decimal number */ function ActualType($meta) { return $meta; } function CreateDatabase($dbname,$options=false) { $options = $this->_Options($options); $sql = array(); $s = 'CREATE DATABASE ' . $this->NameQuote($dbname); if (isset($options[$this->upperName])) $s .= ' '.$options[$this->upperName]; $sql[] = $s; return $sql; } /* Generates the SQL to create index. Returns an array of sql strings. */ function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false) { if (!is_array($flds)) { $flds = explode(',',$flds); } foreach($flds as $key => $fld) { # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32) $flds[$key] = $this->NameQuote($fld,$allowBrackets=true); } return $this->_IndexSQL($this->NameQuote($idxname), $this->TableName($tabname), $flds, $this->_Options($idxoptions)); } function DropIndexSQL ($idxname, $tabname = NULL) { return array(sprintf($this->dropIndex, $this->NameQuote($idxname), $this->TableName($tabname))); } function SetSchema($schema) { $this->schema = $schema; } function AddColumnSQL($tabname, $flds) { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; foreach($lines as $v) { $sql[] = $alter . $v; } return $sql; } /** * Change the definition of one column * * As some DBM's can't do that on there own, you need to supply the complete defintion of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { $tabname = $this->TableName ($tabname); $sql = array(); list($lines,$pkey) = $this->_GenFields($flds); $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' '; foreach($lines as $v) { $sql[] = $alter . $v; } return $sql; } /** * Rename one column * * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql) * @param string $tabname table-name * @param string $oldcolumn column-name to be renamed * @param string $newcolumn new column-name * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default='' * @return array with SQL strings */ function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') { $tabname = $this->TableName ($tabname); if ($flds) { list($lines,$pkey) = $this->_GenFields($flds); list(,$first) = each($lines); list(,$column_def) = split("[\t ]+",$first,2); } return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def)); } /** * Drop one column * * Some DBM's can't do that on there own, you need to supply the complete defintion of the new table, * to allow, recreating the table and copying the content over to the new table * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' * @return array with SQL strings */ function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { $tabname = $this->TableName ($tabname); if (!is_array($flds)) $flds = explode(',',$flds); $sql = array(); $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' '; foreach($flds as $v) { $sql[] = $alter . $this->NameQuote($v); } return $sql; } function DropTableSQL($tabname) { return array (sprintf($this->dropTable, $this->TableName($tabname))); } function RenameTableSQL($tabname,$newname) { return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname))); } /* Generate the SQL to create table. Returns an array of sql strings. */ function CreateTableSQL($tabname, $flds, $tableoptions=false) { if (!$tableoptions) $tableoptions = array(); list($lines,$pkey) = $this->_GenFields($flds, true); $taboptions = $this->_Options($tableoptions); $tabname = $this->TableName ($tabname); $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions); $tsql = $this->_Triggers($tabname,$taboptions); foreach($tsql as $s) $sql[] = $s; return $sql; } function _GenFields($flds,$widespacing=false) { if (is_string($flds)) { $padding = ' '; $txt = $flds.$padding; $flds = array(); $flds0 = Lens_ParseArgs($txt,','); $hasparam = false; foreach($flds0 as $f0) { $f1 = array(); foreach($f0 as $token) { switch (strtoupper($token)) { case 'CONSTRAINT': case 'DEFAULT': $hasparam = $token; break; default: if ($hasparam) $f1[$hasparam] = $token; else $f1[] = $token; $hasparam = false; break; } } $flds[] = $f1; } } $this->autoIncrement = false; $lines = array(); $pkey = array(); foreach($flds as $fld) { $fld = _array_change_key_case($fld); $fname = false; $fdefault = false; $fautoinc = false; $ftype = false; $fsize = false; $fprec = false; $fprimary = false; $fnoquote = false; $fdefts = false; $fdefdate = false; $fconstraint = false; $fnotnull = false; $funsigned = false; //----------------- // Parse attributes foreach($fld as $attr => $v) { if ($attr == 2 && is_numeric($v)) $attr = 'SIZE'; else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v); switch($attr) { case '0': case 'NAME': $fname = $v; break; case '1': case 'TYPE': $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break; case 'SIZE': $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,','); if ($dotat === false) $fsize = $v; else { $fsize = substr($v,0,$dotat); $fprec = substr($v,$dotat+1); } break; case 'UNSIGNED': $funsigned = true; break; case 'AUTOINCREMENT': case 'AUTO': $fautoinc = true; $fnotnull = true; break; case 'KEY': case 'PRIMARY': $fprimary = $v; $fnotnull = true; break; case 'DEF': case 'DEFAULT': $fdefault = $v; break; case 'NOTNULL': $fnotnull = $v; break; case 'NOQUOTE': $fnoquote = $v; break; case 'DEFDATE': $fdefdate = $v; break; case 'DEFTIMESTAMP': $fdefts = $v; break; case 'CONSTRAINT': $fconstraint = $v; break; } //switch } // foreach $fld //-------------------- // VALIDATE FIELD INFO if (!strlen($fname)) { if ($this->debug) ADOConnection::outp("Undefined NAME"); return false; } $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname)); $fname = $this->NameQuote($fname); if (!strlen($ftype)) { if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'"); return false; } else { $ftype = strtoupper($ftype); } $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec); if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls if ($fprimary) $pkey[] = $fname; // some databases do not allow blobs to have defaults if ($ty == 'X') $fdefault = false; //-------------------- // CONSTRUCT FIELD SQL if ($fdefts) { if (substr($this->connection->databaseType,0,5) == 'mysql') { $ftype = 'TIMESTAMP'; } else { $fdefault = $this->connection->sysTimeStamp; } } else if ($fdefdate) { if (substr($this->connection->databaseType,0,5) == 'mysql') { $ftype = 'TIMESTAMP'; } else { $fdefault = $this->connection->sysDate; } } else if ($fdefault !== false && !$fnoquote) if ($ty == 'C' or $ty == 'X' or ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ') $fdefault = trim($fdefault); else if (strtolower($fdefault) != 'null') $fdefault = $this->connection->qstr($fdefault); $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned); if ($widespacing) $fname = str_pad($fname,24); $lines[$fid] = $fname.' '.$ftype.$suffix; if ($fautoinc) $this->autoIncrement = true; } // foreach $flds return array($lines,$pkey); } /* GENERATE THE SIZE PART OF THE DATATYPE $ftype is the actual type $ty is the type defined originally in the DDL */ function _GetSize($ftype, $ty, $fsize, $fprec) { if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) { $ftype .= "(".$fsize; if (strlen($fprec)) $ftype .= ",".$fprec; $ftype .= ')'; } return $ftype; } // return string must begin with space function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint) { $suffix = ''; if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; if ($fnotnull) $suffix .= ' NOT NULL'; if ($fconstraint) $suffix .= ' '.$fconstraint; return $suffix; } function _IndexSQL($idxname, $tabname, $flds, $idxoptions) { $sql = array(); if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { $sql[] = sprintf ($this->dropIndex, $idxname); if ( isset($idxoptions['DROP']) ) return $sql; } if ( empty ($flds) ) { return $sql; } $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; if ( isset($idxoptions[$this->upperName]) ) $s .= $idxoptions[$this->upperName]; if ( is_array($flds) ) $flds = implode(', ',$flds); $s .= '(' . $flds . ')'; $sql[] = $s; return $sql; } function _DropAutoIncrement($tabname) { return false; } function _TableSQL($tabname,$lines,$pkey,$tableoptions) { $sql = array(); if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) { $sql[] = sprintf($this->dropTable,$tabname); if ($this->autoIncrement) { $sInc = $this->_DropAutoIncrement($tabname); if ($sInc) $sql[] = $sInc; } if ( isset ($tableoptions['DROP']) ) { return $sql; } } $s = "CREATE TABLE $tabname (\n"; $s .= implode(",\n", $lines); if (sizeof($pkey)>0) { $s .= ",\n PRIMARY KEY ("; $s .= implode(", ",$pkey).")"; } if (isset($tableoptions['CONSTRAINTS'])) $s .= "\n".$tableoptions['CONSTRAINTS']; if (isset($tableoptions[$this->upperName.'_CONSTRAINTS'])) $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS']; $s .= "\n)"; if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName]; $sql[] = $s; return $sql; } /* GENERATE TRIGGERS IF NEEDED used when table has auto-incrementing field that is emulated using triggers */ function _Triggers($tabname,$taboptions) { return array(); } /* Sanitize options, so that array elements with no keys are promoted to keys */ function _Options($opts) { if (!is_array($opts)) return array(); $newopts = array(); foreach($opts as $k => $v) { if (is_numeric($k)) $newopts[strtoupper($v)] = $v; else $newopts[strtoupper($k)] = $v; } return $newopts; } /* "Florian Buzin [ easywe ]" This function changes/adds new fields to your table. You don't have to know if the col is new or not. It will check on its own. */ function ChangeTableSQL($tablename, $flds, $tableoptions = false) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; if ($this->connection->fetchMode !== false) $savem = $this->connection->SetFetchMode(false); // check table exists $save_handler = $this->connection->raiseErrorFn; $this->connection->raiseErrorFn = ''; $cols = $this->MetaColumns($tablename); $this->connection->raiseErrorFn = $save_handler; if (isset($savem)) $this->connection->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ( empty($cols)) { return $this->CreateTableSQL($tablename, $flds, $tableoptions); } if (is_array($flds)) { // Cycle through the update fields, comparing // existing fields to fields to update. // if the Metatype and size is exactly the // same, ignore - by Mark Newham $holdflds = array(); foreach($flds as $k=>$v) { if ( isset($cols[$k]) && is_object($cols[$k]) ) { // If already not allowing nulls, then don't change $obj = $cols[$k]; if (isset($obj->not_null) && $obj->not_null) $v = str_replace('NOT NULL','',$v); $c = $cols[$k]; $ml = $c->max_length; $mt = $this->MetaType($c->type,$ml); if ($ml == -1) $ml = ''; if ($mt == 'X') $ml = $v['SIZE']; if (($mt != $v['TYPE']) || $ml != $v['SIZE']) { $holdflds[$k] = $v; } } else { $holdflds[$k] = $v; } } $flds = $holdflds; } // already exists, alter table instead list($lines,$pkey) = $this->_GenFields($flds); $alter = 'ALTER TABLE ' . $this->TableName($tablename); $sql = array(); foreach ( $lines as $id => $v ) { if ( isset($cols[$id]) && is_object($cols[$id]) ) { $flds = Lens_ParseArgs($v,','); // We are trying to change the size of the field, if not allowed, simply ignore the request. if ($flds && in_array(strtoupper(substr($flds[0][1],0,4)),$this->invalidResizeTypes4)) continue; $sql[] = $alter . $this->alterCol . ' ' . $v; } else { $sql[] = $alter . $this->addCol . ' ' . $v; } } return $sql; } } // class ?>phpgacl-3.3.7/adodb/perf/0040755025754300001440000000000010476665054014076 5ustar ipsousersphpgacl-3.3.7/adodb/perf/perf-postgres.inc.php0100644025754300001440000001100410476657245020153 0ustar ipsousers array('RATIO', "select case when count(*)=3 then 'TRUE' else 'FALSE' end from pg_settings where (name='stats_block_level' or name='stats_row_level' or name='stats_start_collector') and setting='on' ", 'Value must be TRUE to enable hit ratio statistics (stats_start_collector,stats_row_level and stats_block_level must be set to true in postgresql.conf)'), 'data cache hit ratio' => array('RATIO', "select case when blks_hit=0 then 0 else round( ((1-blks_read::float/blks_hit)*100)::numeric, 2) end from pg_stat_database where datname='\$DATABASE'", '=WarnCacheRatio'), 'IO', 'data reads' => array('IO', 'select sum(heap_blks_read+toast_blks_read) from pg_statio_user_tables', ), 'data writes' => array('IO', 'select round((sum(n_tup_ins/4.0+n_tup_upd/8.0+n_tup_del/4.0)/16)::numeric,2) from pg_stat_user_tables', 'Count of inserts/updates/deletes * coef'), 'Data Cache', 'data cache buffers' => array('DATAC', "select setting from pg_settings where name='shared_buffers'", 'Number of cache buffers. Tuning'), 'cache blocksize' => array('DATAC', 'select 8192', '(estimate)' ), 'data cache size' => array( 'DATAC', "select setting::integer*8192 from pg_settings where name='shared_buffers'", '' ), 'operating system cache size' => array( 'DATA', "select setting::integer*8192 from pg_settings where name='effective_cache_size'", '(effective cache size)' ), 'Memory Usage', # Postgres 7.5 changelog: Rename server parameters SortMem and VacuumMem to work_mem and maintenance_work_mem; 'sort/work buffer size' => array('CACHE', "select setting::integer*1024 from pg_settings where name='sort_mem' or name = 'work_mem' order by name", 'Size of sort buffer (per query)' ), 'Connections', 'current connections' => array('SESS', 'select count(*) from pg_stat_activity', ''), 'max connections' => array('SESS', "select setting from pg_settings where name='max_connections'", ''), 'Parameters', 'rollback buffers' => array('COST', "select setting from pg_settings where name='wal_buffers'", 'WAL buffers'), 'random page cost' => array('COST', "select setting from pg_settings where name='random_page_cost'", 'Cost of doing a seek (default=4). See random_page_cost'), false ); function perf_postgres(&$conn) { $this->conn =& $conn; } function Explain($sql,$partial=false) { $save = $this->conn->LogSQL(false); if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $arr = $this->conn->GetArray("select distinct distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); if (crc32($sql) == $partial) break; } } } $sql = str_replace('?',"''",$sql); $s = '

Explain: '.htmlspecialchars($sql).'

'; $rs = $this->conn->Execute('EXPLAIN '.$sql); $this->conn->LogSQL($save); $s .= '
';
		if ($rs)
			while (!$rs->EOF) {
				$s .= reset($rs->fields)."\n";
				$rs->MoveNext();
			}
		$s .= '
'; $s .= $this->Tracer($sql,$partial); return $s; } } ?>phpgacl-3.3.7/adodb/perf/perf-mssql.inc.php0100644025754300001440000001132610476657245017453 0ustar ipsousers array('RATIO', "select round((a.cntr_value*100.0)/b.cntr_value,2) from master.dbo.sysperfinfo a, master.dbo.sysperfinfo b where a.counter_name = 'Buffer cache hit ratio' and b.counter_name='Buffer cache hit ratio base'", '=WarnCacheRatio'), 'prepared sql hit ratio' => array('RATIO', array('dbcc cachestats','Prepared',1,100), ''), 'adhoc sql hit ratio' => array('RATIO', array('dbcc cachestats','Adhoc',1,100), ''), 'IO', 'data reads' => array('IO', "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page reads/sec'"), 'data writes' => array('IO', "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page writes/sec'"), 'Data Cache', 'data cache size' => array('DATAC', "select cntr_value*8192 from master.dbo.sysperfinfo where counter_name = 'Total Pages' and object_name='SQLServer:Buffer Manager'", '' ), 'data cache blocksize' => array('DATAC', "select 8192",'page size'), 'Connections', 'current connections' => array('SESS', '=sp_who', ''), 'max connections' => array('SESS', "SELECT @@MAX_CONNECTIONS", ''), false ); function perf_mssql(&$conn) { if ($conn->dataProvider == 'odbc') { $this->sql1 = 'sql1'; //$this->explain = false; } $this->conn =& $conn; } function Explain($sql,$partial=false) { $save = $this->conn->LogSQL(false); if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); if (crc32($sql) == $partial) break; } } } $s = '

Explain: '.htmlspecialchars($sql).'

'; $this->conn->Execute("SET SHOWPLAN_ALL ON;"); $sql = str_replace('?',"''",$sql); global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $rs =& $this->conn->Execute($sql); //adodb_printr($rs); $ADODB_FETCH_MODE = $save; if ($rs) { $rs->MoveNext(); $s .= ''; while (!$rs->EOF) { $s .= '\n"; ## NOTE CORRUPT tag is intentional!!!! $rs->MoveNext(); } $s .= '
Rows IO CPU     Plan
'.round($rs->fields[8],1).''.round($rs->fields[9],3).''.round($rs->fields[10],3).'
'.htmlspecialchars($rs->fields[0])."
'; $rs->NextRecordSet(); } $this->conn->Execute("SET SHOWPLAN_ALL OFF;"); $this->conn->LogSQL($save); $s .= $this->Tracer($sql); return $s; } function Tables() { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; //$this->conn->debug=1; $s = ''; $rs1 = $this->conn->Execute("select distinct name from sysobjects where xtype='U'"); if ($rs1) { while (!$rs1->EOF) { $tab = $rs1->fields[0]; $tabq = $this->conn->qstr($tab); $rs2 = $this->conn->Execute("sp_spaceused $tabq"); if ($rs2) { $s .= ''; $rs2->Close(); } $rs1->MoveNext(); } $rs1->Close(); } $ADODB_FETCH_MODE = $save; return $s.'
tablenamesize_in_kindex sizereserved size
'.$tab.''.$rs2->fields[3].''.$rs2->fields[4].''.$rs2->fields[2].'
'; } function sp_who() { $arr = $this->conn->GetArray('sp_who'); return sizeof($arr); } function HealthCheck($cli=false) { $this->conn->Execute('dbcc traceon(3604)'); $html = adodb_perf::HealthCheck($cli); $this->conn->Execute('dbcc traceoff(3604)'); return $html; } } ?>phpgacl-3.3.7/adodb/perf/perf-db2.inc.php0100644025754300001440000000570010476657245016762 0ustar ipsousers array('RATIO', "SELECT case when sum(POOL_DATA_L_READS+POOL_INDEX_L_READS)=0 then 0 else 100*(1-sum(POOL_DATA_P_READS+POOL_INDEX_P_READS)/sum(POOL_DATA_L_READS+POOL_INDEX_L_READS)) end FROM TABLE(SNAPSHOT_APPL('',-2)) as t", '=WarnCacheRatio'), 'Data Cache', 'data cache buffers' => array('DATAC', 'select sum(npages) from SYSCAT.BUFFERPOOLS', 'See tuning reference.' ), 'cache blocksize' => array('DATAC', 'select avg(pagesize) from SYSCAT.BUFFERPOOLS', '' ), 'data cache size' => array('DATAC', 'select sum(npages*pagesize) from SYSCAT.BUFFERPOOLS', '' ), 'Connections', 'current connections' => array('SESS', "SELECT count(*) FROM TABLE(SNAPSHOT_APPL_INFO('',-2)) as t", ''), false ); function perf_db2(&$conn) { $this->conn =& $conn; } function Explain($sql,$partial=false) { $save = $this->conn->LogSQL(false); if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); if (crc32($sql) == $partial) break; } } } $qno = rand(); $ok = $this->conn->Execute("EXPLAIN PLAN SET QUERYNO=$qno FOR $sql"); ob_start(); if (!$ok) echo "

Have EXPLAIN tables been created?

"; else { $rs = $this->conn->Execute("select * from explain_statement where queryno=$qno"); if ($rs) rs2html($rs); } $s = ob_get_contents(); ob_end_clean(); $this->conn->LogSQL($save); $s .= $this->Tracer($sql); return $s; } function Tables() { $rs = $this->conn->Execute("select tabschema,tabname,card as rows, npages pages_used,fpages pages_allocated, tbspace tablespace from syscat.tables where tabschema not in ('SYSCAT','SYSIBM','SYSSTAT') order by 1,2"); return rs2html($rs,false,false,false,false); } } ?>phpgacl-3.3.7/adodb/perf/perf-informix.inc.php0100644025754300001440000000406210476657245020146 0ustar ipsousers array('RATIOH', "select round((1-(wt.value / (rd.value + wr.value)))*100,2) from sysmaster:sysprofile wr, sysmaster:sysprofile rd, sysmaster:sysprofile wt where rd.name = 'pagreads' and wr.name = 'pagwrites' and wt.name = 'buffwts'", '=WarnCacheRatio'), 'IO', 'data reads' => array('IO', "select value from sysmaster:sysprofile where name='pagreads'", 'Page reads'), 'data writes' => array('IO', "select value from sysmaster:sysprofile where name='pagwrites'", 'Page writes'), 'Connections', 'current connections' => array('SESS', 'select count(*) from sysmaster:syssessions', 'Number of sessions'), false ); function perf_informix(&$conn) { $this->conn =& $conn; } } ?> phpgacl-3.3.7/adodb/perf/perf-oci8.inc.php0100644025754300001440000004015410476657245017157 0ustar ipsousers array('RATIOH', "select round((1-(phy.value / (cur.value + con.value)))*100,2) from v\$sysstat cur, v\$sysstat con, v\$sysstat phy where cur.name = 'db block gets' and con.name = 'consistent gets' and phy.name = 'physical reads'", '=WarnCacheRatio'), 'sql cache hit ratio' => array( 'RATIOH', 'select round(100*(sum(pins)-sum(reloads))/sum(pins),2) from v$librarycache', 'increase shared_pool_size if too ratio low'), 'datadict cache hit ratio' => array('RATIOH', "select round((1 - (sum(getmisses) / (sum(gets) + sum(getmisses))))*100,2) from v\$rowcache", 'increase shared_pool_size if too ratio low'), 'memory sort ratio' => array('RATIOH', "SELECT ROUND((100 * b.VALUE) /DECODE ((a.VALUE + b.VALUE), 0,1,(a.VALUE + b.VALUE)),2) FROM v\$sysstat a, v\$sysstat b WHERE a.name = 'sorts (disk)' AND b.name = 'sorts (memory)'", "% of memory sorts compared to disk sorts - should be over 95%"), 'IO', 'data reads' => array('IO', "select value from v\$sysstat where name='physical reads'"), 'data writes' => array('IO', "select value from v\$sysstat where name='physical writes'"), 'Data Cache', 'data cache buffers' => array( 'DATAC', "select a.value/b.value from v\$parameter a, v\$parameter b where a.name = 'db_cache_size' and b.name= 'db_block_size'", 'Number of cache buffers. Tune db_cache_size if the data cache hit ratio is too low.'), 'data cache blocksize' => array('DATAC', "select value from v\$parameter where name='db_block_size'", '' ), 'Memory Pools', 'data cache size' => array('DATAC', "select value from v\$parameter where name = 'db_cache_size'", 'db_cache_size' ), 'shared pool size' => array('DATAC', "select value from v\$parameter where name = 'shared_pool_size'", 'shared_pool_size, which holds shared sql, stored procedures, dict cache and similar shared structs' ), 'java pool size' => array('DATAJ', "select value from v\$parameter where name = 'java_pool_size'", 'java_pool_size' ), 'large pool buffer size' => array('CACHE', "select value from v\$parameter where name='large_pool_size'", 'this pool is for large mem allocations (not because it is larger than shared pool), for MTS sessions, parallel queries, io buffers (large_pool_size) ' ), 'pga buffer size' => array('CACHE', "select value from v\$parameter where name='pga_aggregate_target'", 'program global area is private memory for sorting, and hash and bitmap merges - since oracle 9i (pga_aggregate_target)' ), 'Connections', 'current connections' => array('SESS', 'select count(*) from sys.v_$session where username is not null', ''), 'max connections' => array( 'SESS', "select value from v\$parameter where name='sessions'", ''), 'Memory Utilization', 'data cache utilization ratio' => array('RATIOU', "select round((1-bytes/sgasize)*100, 2) from (select sum(bytes) sgasize from sys.v_\$sgastat) s, sys.v_\$sgastat f where name = 'free memory' and pool = 'shared pool'", 'Percentage of data cache actually in use - should be over 85%'), 'shared pool utilization ratio' => array('RATIOU', 'select round((sga.bytes/p.value)*100,2) from v$sgastat sga, v$parameter p where sga.name = \'free memory\' and sga.pool = \'shared pool\' and p.name = \'shared_pool_size\'', 'Percentage of shared pool actually used - too low is bad, too high is worse'), 'large pool utilization ratio' => array('RATIOU', "select round((1-bytes/sgasize)*100, 2) from (select sum(bytes) sgasize from sys.v_\$sgastat) s, sys.v_\$sgastat f where name = 'free memory' and pool = 'large pool'", 'Percentage of large_pool actually in use - too low is bad, too high is worse'), 'sort buffer size' => array('CACHE', "select value from v\$parameter where name='sort_area_size'", 'max in-mem sort_area_size (per query), uses memory in pga' ), 'pga usage at peak' => array('RATIOU', '=PGA','Mb utilization at peak transactions (requires Oracle 9i+)'), 'Transactions', 'rollback segments' => array('ROLLBACK', "select count(*) from sys.v_\$rollstat", ''), 'peak transactions' => array('ROLLBACK', "select max_utilization tx_hwm from sys.v_\$resource_limit where resource_name = 'transactions'", 'Taken from high-water-mark'), 'max transactions' => array('ROLLBACK', "select value from v\$parameter where name = 'transactions'", 'max transactions / rollback segments < 3.5 (or transactions_per_rollback_segment)'), 'Parameters', 'cursor sharing' => array('CURSOR', "select value from v\$parameter where name = 'cursor_sharing'", 'Cursor reuse strategy. Recommended is FORCE (8i+) or SIMILAR (9i+). See cursor_sharing.'), /* 'cursor reuse' => array('CURSOR', "select count(*) from (select sql_text_wo_constants, count(*) from t1 group by sql_text_wo_constants having count(*) > 100)",'These are sql statements that should be using bind variables'),*/ 'index cache cost' => array('COST', "select value from v\$parameter where name = 'optimizer_index_caching'", '=WarnIndexCost'), 'random page cost' => array('COST', "select value from v\$parameter where name = 'optimizer_index_cost_adj'", '=WarnPageCost'), false ); function perf_oci8(&$conn) { $savelog = $conn->LogSQL(false); $this->version = $conn->ServerInfo(); $conn->LogSQL($savelog); $this->conn =& $conn; } function WarnPageCost($val) { if ($val == 100) $s = 'Too High. '; else $s = ''; return $s.'Recommended is 20-50 for TP, and 50 for data warehouses. Default is 100. See optimizer_index_cost_adj. '; } function WarnIndexCost($val) { if ($val == 0) $s = 'Too Low. '; else $s = ''; return $s.'Percentage of indexed data blocks expected in the cache. Recommended is 20 (fast disk array) to 50 (slower hard disks). Default is 0. See optimizer_index_caching.'; } function PGA() { if ($this->version['version'] < 9) return 'Oracle 9i or later required'; $rs = $this->conn->Execute("select a.mb,a.targ as pga_size_pct,a.pct from (select round(pga_target_for_estimate/1024.0/1024.0,0) Mb, pga_target_factor targ,estd_pga_cache_hit_percentage pct,rownum as r from v\$pga_target_advice) a left join (select round(pga_target_for_estimate/1024.0/1024.0,0) Mb, pga_target_factor targ,estd_pga_cache_hit_percentage pct,rownum as r from v\$pga_target_advice) b on a.r = b.r+1 where b.pct < 100"); if (!$rs) return "Only in 9i or later"; $rs->Close(); if ($rs->EOF) return "PGA could be too big"; return reset($rs->fields); } function Explain($sql,$partial=false) { $savelog = $this->conn->LogSQL(false); $rs =& $this->conn->SelectLimit("select ID FROM PLAN_TABLE"); if (!$rs) { echo "

Missing PLAN_TABLE

CREATE TABLE PLAN_TABLE (
  STATEMENT_ID                    VARCHAR2(30),
  TIMESTAMP                       DATE,
  REMARKS                         VARCHAR2(80),
  OPERATION                       VARCHAR2(30),
  OPTIONS                         VARCHAR2(30),
  OBJECT_NODE                     VARCHAR2(128),
  OBJECT_OWNER                    VARCHAR2(30),
  OBJECT_NAME                     VARCHAR2(30),
  OBJECT_INSTANCE                 NUMBER(38),
  OBJECT_TYPE                     VARCHAR2(30),
  OPTIMIZER                       VARCHAR2(255),
  SEARCH_COLUMNS                  NUMBER,
  ID                              NUMBER(38),
  PARENT_ID                       NUMBER(38),
  POSITION                        NUMBER(38),
  COST                            NUMBER(38),
  CARDINALITY                     NUMBER(38),
  BYTES                           NUMBER(38),
  OTHER_TAG                       VARCHAR2(255),
  PARTITION_START                 VARCHAR2(255),
  PARTITION_STOP                  VARCHAR2(255),
  PARTITION_ID                    NUMBER(38),
  OTHER                           LONG,
  DISTRIBUTION                    VARCHAR2(30)
);
"; return false; } $rs->Close(); // $this->conn->debug=1; if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $arr = $this->conn->GetArray("select distinct distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); if (crc32($sql) == $partial) break; } } } $s = "

Explain: ".htmlspecialchars($sql)."

"; $this->conn->BeginTrans(); $id = "ADODB ".microtime(); $rs =& $this->conn->Execute("EXPLAIN PLAN SET STATEMENT_ID='$id' FOR $sql"); $m = $this->conn->ErrorMsg(); if ($m) { $this->conn->RollbackTrans(); $this->conn->LogSQL($savelog); $s .= "

$m

"; return $s; } $rs =& $this->conn->Execute(" select '
'||lpad('--', (level-1)*2,'-') || trim(operation) || ' ' || trim(options)||'
' as Operation, object_name,COST,CARDINALITY,bytes FROM plan_table START WITH id = 0 and STATEMENT_ID='$id' CONNECT BY prior id=parent_id and statement_id='$id'"); $s .= rs2html($rs,false,false,false,false); $this->conn->RollbackTrans(); $this->conn->LogSQL($savelog); $s .= $this->Tracer($sql,$partial); return $s; } function CheckMemory() { if ($this->version['version'] < 9) return 'Oracle 9i or later required'; $rs =& $this->conn->Execute(" select a.size_for_estimate as cache_mb_estimate, case when a.size_factor=1 then '<<= current' when a.estd_physical_read_factor-b.estd_physical_read_factor > 0 and a.estd_physical_read_factor<1 then '- BETTER - ' else ' ' end as currsize, a.estd_physical_read_factor-b.estd_physical_read_factor as best_when_0 from (select size_for_estimate,size_factor,estd_physical_read_factor,rownum r from v\$db_cache_advice) a , (select size_for_estimate,size_factor,estd_physical_read_factor,rownum r from v\$db_cache_advice) b where a.r = b.r-1"); if (!$rs) return false; /* The v$db_cache_advice utility show the marginal changes in physical data block reads for different sizes of db_cache_size */ $s = "

Data Cache Estimate

"; if ($rs->EOF) { $s .= "

Cache that is 50% of current size is still too big

"; } else { $s .= "Ideal size of Data Cache is when \"best_when_0\" changes from a positive number and becomes zero."; $s .= rs2html($rs,false,false,false,false); } return $s; } /* Generate html for suspicious/expensive sql */ function tohtml(&$rs,$type) { $o1 = $rs->FetchField(0); $o2 = $rs->FetchField(1); $o3 = $rs->FetchField(2); if ($rs->EOF) return '

None found

'; $check = ''; $sql = ''; $s = "\n\n'; while (!$rs->EOF) { if ($check != $rs->fields[0].'::'.$rs->fields[1]) { if ($check) { $carr = explode('::',$check); $prefix = "'; $suffix = ''; if (strlen($prefix)>2000) { $prefix = ''; $suffix = ''; } $s .= "\n'; } $sql = $rs->fields[2]; $check = $rs->fields[0].'::'.$rs->fields[1]; } else $sql .= $rs->fields[2]; if (substr($sql,strlen($sql)-1) == "\0") $sql = substr($sql,0,strlen($sql)-1); $rs->MoveNext(); } $rs->Close(); $carr = explode('::',$check); $prefix = "'; $suffix = ''; if (strlen($prefix)>2000) { $prefix = ''; $suffix = ''; } $s .= "\n'; return $s."
".$o1->name.''.$o2->name.''.$o3->name.'
".$carr[0].''.$carr[1].''.$prefix.$sql.$suffix.'
".$carr[0].''.$carr[1].''.$prefix.$sql.$suffix.'
\n\n"; } // code thanks to Ixora. // http://www.ixora.com.au/scripts/query_opt.htm // requires oracle 8.1.7 or later function SuspiciousSQL($numsql=10) { $sql = " select substr(to_char(s.pct, '99.00'), 2) || '%' load, s.executions executes, p.sql_text from ( select address, buffer_gets, executions, pct, rank() over (order by buffer_gets desc) ranking from ( select address, buffer_gets, executions, 100 * ratio_to_report(buffer_gets) over () pct from sys.v_\$sql where command_type != 47 and module != 'T.O.A.D.' ) where buffer_gets > 50 * executions ) s, sys.v_\$sqltext p where s.ranking <= $numsql and p.address = s.address order by 1 desc, s.address, p.piece"; global $ADODB_CACHE_MODE; if (isset($_GET['expsixora']) && isset($_GET['sql'])) { $partial = empty($_GET['part']); echo "".$this->Explain($_GET['sql'],$partial)."\n"; } if (isset($_GET['sql'])) return $this->_SuspiciousSQL($numsql); $s = ''; $s .= $this->_SuspiciousSQL($numsql); $s .= '

'; $save = $ADODB_CACHE_MODE; $ADODB_CACHE_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $savelog = $this->conn->LogSQL(false); $rs =& $this->conn->SelectLimit($sql); $this->conn->LogSQL($savelog); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_CACHE_MODE = $save; if ($rs) { $s .= "\n

Ixora Suspicious SQL

"; $s .= $this->tohtml($rs,'expsixora'); } return $s; } // code thanks to Ixora. // http://www.ixora.com.au/scripts/query_opt.htm // requires oracle 8.1.7 or later function ExpensiveSQL($numsql = 10) { $sql = " select substr(to_char(s.pct, '99.00'), 2) || '%' load, s.executions executes, p.sql_text from ( select address, disk_reads, executions, pct, rank() over (order by disk_reads desc) ranking from ( select address, disk_reads, executions, 100 * ratio_to_report(disk_reads) over () pct from sys.v_\$sql where command_type != 47 and module != 'T.O.A.D.' ) where disk_reads > 50 * executions ) s, sys.v_\$sqltext p where s.ranking <= $numsql and p.address = s.address order by 1 desc, s.address, p.piece "; global $ADODB_CACHE_MODE; if (isset($_GET['expeixora']) && isset($_GET['sql'])) { $partial = empty($_GET['part']); echo "".$this->Explain($_GET['sql'],$partial)."\n"; } if (isset($_GET['sql'])) { $var = $this->_ExpensiveSQL($numsql); return $var; } $s = ''; $s .= $this->_ExpensiveSQL($numsql); $s .= '

'; $save = $ADODB_CACHE_MODE; $ADODB_CACHE_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $savelog = $this->conn->LogSQL(false); $rs =& $this->conn->Execute($sql); $this->conn->LogSQL($savelog); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_CACHE_MODE = $save; if ($rs) { $s .= "\n

Ixora Expensive SQL

"; $s .= $this->tohtml($rs,'expeixora'); } return $s; } } ?>phpgacl-3.3.7/adodb/perf/perf-mysql.inc.php0100644025754300001440000002035610476657245017464 0ustar ipsousers array('RATIO', '=GetKeyHitRatio', '=WarnCacheRatio'), 'InnoDB cache hit ratio' => array('RATIO', '=GetInnoDBHitRatio', '=WarnCacheRatio'), 'data cache hit ratio' => array('HIDE', # only if called '=FindDBHitRatio', '=WarnCacheRatio'), 'sql cache hit ratio' => array('RATIO', '=GetQHitRatio', ''), 'IO', 'data reads' => array('IO', '=GetReads', 'Number of selects (Key_reads is not accurate)'), 'data writes' => array('IO', '=GetWrites', 'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'), 'Data Cache', 'MyISAM data cache size' => array('DATAC', array("show variables", 'key_buffer_size'), '' ), 'BDB data cache size' => array('DATAC', array("show variables", 'bdb_cache_size'), '' ), 'InnoDB data cache size' => array('DATAC', array("show variables", 'innodb_buffer_pool_size'), '' ), 'Memory Usage', 'read buffer size' => array('CACHE', array("show variables", 'read_buffer_size'), '(per session)'), 'sort buffer size' => array('CACHE', array("show variables", 'sort_buffer_size'), 'Size of sort buffer (per session)' ), 'table cache' => array('CACHE', array("show variables", 'table_cache'), 'Number of tables to keep open'), 'Connections', 'current connections' => array('SESS', array('show status','Threads_connected'), ''), 'max connections' => array( 'SESS', array("show variables",'max_connections'), ''), false ); function perf_mysql(&$conn) { $this->conn =& $conn; } function Explain($sql,$partial=false) { if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '

Unable to EXPLAIN non-select statement

'; $save = $this->conn->LogSQL(false); if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); if (crc32($sql) == $partial) break; } } } $sql = str_replace('?',"''",$sql); if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq"); } $s = '

Explain: '.htmlspecialchars($sql).'

'; $rs = $this->conn->Execute('EXPLAIN '.$sql); $s .= rs2html($rs,false,false,false,false); $this->conn->LogSQL($save); $s .= $this->Tracer($sql); return $s; } function Tables() { if (!$this->tablesSQL) return false; $rs = $this->conn->Execute($this->tablesSQL); if (!$rs) return false; $html = rs2html($rs,false,false,false,false); return $html; } function GetReads() { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs = $this->conn->Execute('show status'); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) return 0; $val = 0; while (!$rs->EOF) { switch($rs->fields[0]) { case 'Com_select': $val = $rs->fields[1]; $rs->Close(); return $val; } $rs->MoveNext(); } $rs->Close(); return $val; } function GetWrites() { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs = $this->conn->Execute('show status'); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) return 0; $val = 0.0; while (!$rs->EOF) { switch($rs->fields[0]) { case 'Com_insert': $val += $rs->fields[1]; break; case 'Com_delete': $val += $rs->fields[1]; break; case 'Com_update': $val += $rs->fields[1]/2; $rs->Close(); return $val; } $rs->MoveNext(); } $rs->Close(); return $val; } function FindDBHitRatio() { // first find out type of table //$this->conn->debug=1; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs = $this->conn->Execute('show table status'); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) return ''; $type = strtoupper($rs->fields[1]); $rs->Close(); switch($type){ case 'MYISAM': case 'ISAM': return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)'; case 'INNODB': return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)'; default: return $type.' not supported'; } } function GetQHitRatio() { //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached $hits = $this->_DBParameter(array("show status","Qcache_hits")); $total = $this->_DBParameter(array("show status","Qcache_inserts")); $total += $this->_DBParameter(array("show status","Qcache_not_cached")); $total += $hits; if ($total) return round(($hits*100)/$total,2); return 0; } /* Use session variable to store Hit percentage, because MySQL does not remember last value of SHOW INNODB STATUS hit ratio # 1st query to SHOW INNODB STATUS 0.00 reads/s, 0.00 creates/s, 0.00 writes/s Buffer pool hit rate 1000 / 1000 # 2nd query to SHOW INNODB STATUS 0.00 reads/s, 0.00 creates/s, 0.00 writes/s No buffer pool activity since the last printout */ function GetInnoDBHitRatio() { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs = $this->conn->Execute('show innodb status'); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs || $rs->EOF) return 0; $stat = $rs->fields[0]; $rs->Close(); $at = strpos($stat,'Buffer pool hit rate'); $stat = substr($stat,$at,200); if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) { $val = 100*$arr[1]/$arr[2]; $_SESSION['INNODB_HIT_PCT'] = $val; return round($val,2); } else { if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT']; return 0; } return 0; } function GetKeyHitRatio() { $hits = $this->_DBParameter(array("show status","Key_read_requests")); $reqs = $this->_DBParameter(array("show status","Key_reads")); if ($reqs == 0) return 0; return round(($hits/($reqs+$hits))*100,2); } // start hack var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK'; var $optimizeTableHigh = 'OPTIMIZE TABLE %s'; /** * @see adodb_perf#optimizeTable */ function optimizeTable( $table, $mode = ADODB_OPT_LOW) { if ( !is_string( $table)) return false; $conn = $this->conn; if ( !$conn) return false; $sql = ''; switch( $mode) { case ADODB_OPT_LOW : $sql = $this->optimizeTableLow; break; case ADODB_OPT_HIGH : $sql = $this->optimizeTableHigh; break; default : { // May dont use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0) ADOConnection::outp( sprintf( "

%s: '%s' using of undefined mode '%s'

", __CLASS__, __FUNCTION__, $mode)); return false; } } $sql = sprintf( $sql, $table); return $conn->Execute( $sql) !== false; } // end hack } ?>phpgacl-3.3.7/adodb/adodb-error.inc.php0100644025754300001440000002053710476657245016632 0ustar ipsousers DB_ERROR_NOSUCHTABLE, '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS, '/divide by zero$/' => DB_ERROR_DIVZERO, '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER, '/ttribute [\"\'].*[\"\'] not found|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD, '/parser: parse error at or near \"/' => DB_ERROR_SYNTAX, '/referential integrity violation/' => DB_ERROR_CONSTRAINT, '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*|duplicate key violates unique constraint/' => DB_ERROR_ALREADY_EXISTS ); reset($error_regexps); while (list($regexp,$code) = each($error_regexps)) { if (preg_match($regexp, $errormsg)) { return $code; } } // Fall back to DB_ERROR if there was no mapping. return DB_ERROR; } function adodb_error_odbc() { static $MAP = array( '01004' => DB_ERROR_TRUNCATED, '07001' => DB_ERROR_MISMATCH, '21S01' => DB_ERROR_MISMATCH, '21S02' => DB_ERROR_MISMATCH, '22003' => DB_ERROR_INVALID_NUMBER, '22008' => DB_ERROR_INVALID_DATE, '22012' => DB_ERROR_DIVZERO, '23000' => DB_ERROR_CONSTRAINT, '24000' => DB_ERROR_INVALID, '34000' => DB_ERROR_INVALID, '37000' => DB_ERROR_SYNTAX, '42000' => DB_ERROR_SYNTAX, 'IM001' => DB_ERROR_UNSUPPORTED, 'S0000' => DB_ERROR_NOSUCHTABLE, 'S0001' => DB_ERROR_NOT_FOUND, 'S0002' => DB_ERROR_NOSUCHTABLE, 'S0011' => DB_ERROR_ALREADY_EXISTS, 'S0012' => DB_ERROR_NOT_FOUND, 'S0021' => DB_ERROR_ALREADY_EXISTS, 'S0022' => DB_ERROR_NOT_FOUND, 'S1000' => DB_ERROR_NOSUCHTABLE, 'S1009' => DB_ERROR_INVALID, 'S1090' => DB_ERROR_INVALID, 'S1C00' => DB_ERROR_NOT_CAPABLE ); return $MAP; } function adodb_error_ibase() { static $MAP = array( -104 => DB_ERROR_SYNTAX, -150 => DB_ERROR_ACCESS_VIOLATION, -151 => DB_ERROR_ACCESS_VIOLATION, -155 => DB_ERROR_NOSUCHTABLE, -157 => DB_ERROR_NOSUCHFIELD, -158 => DB_ERROR_VALUE_COUNT_ON_ROW, -170 => DB_ERROR_MISMATCH, -171 => DB_ERROR_MISMATCH, -172 => DB_ERROR_INVALID, -204 => DB_ERROR_INVALID, -205 => DB_ERROR_NOSUCHFIELD, -206 => DB_ERROR_NOSUCHFIELD, -208 => DB_ERROR_INVALID, -219 => DB_ERROR_NOSUCHTABLE, -297 => DB_ERROR_CONSTRAINT, -530 => DB_ERROR_CONSTRAINT, -803 => DB_ERROR_CONSTRAINT, -551 => DB_ERROR_ACCESS_VIOLATION, -552 => DB_ERROR_ACCESS_VIOLATION, -922 => DB_ERROR_NOSUCHDB, -923 => DB_ERROR_CONNECT_FAILED, -924 => DB_ERROR_CONNECT_FAILED ); return $MAP; } function adodb_error_ifx() { static $MAP = array( '-201' => DB_ERROR_SYNTAX, '-206' => DB_ERROR_NOSUCHTABLE, '-217' => DB_ERROR_NOSUCHFIELD, '-329' => DB_ERROR_NODBSELECTED, '-1204' => DB_ERROR_INVALID_DATE, '-1205' => DB_ERROR_INVALID_DATE, '-1206' => DB_ERROR_INVALID_DATE, '-1209' => DB_ERROR_INVALID_DATE, '-1210' => DB_ERROR_INVALID_DATE, '-1212' => DB_ERROR_INVALID_DATE ); return $MAP; } function adodb_error_oci8() { static $MAP = array( 1 => DB_ERROR_ALREADY_EXISTS, 900 => DB_ERROR_SYNTAX, 904 => DB_ERROR_NOSUCHFIELD, 923 => DB_ERROR_SYNTAX, 942 => DB_ERROR_NOSUCHTABLE, 955 => DB_ERROR_ALREADY_EXISTS, 1476 => DB_ERROR_DIVZERO, 1722 => DB_ERROR_INVALID_NUMBER, 2289 => DB_ERROR_NOSUCHTABLE, 2291 => DB_ERROR_CONSTRAINT, 2449 => DB_ERROR_CONSTRAINT ); return $MAP; } function adodb_error_mssql() { static $MAP = array( 208 => DB_ERROR_NOSUCHTABLE, 2601 => DB_ERROR_ALREADY_EXISTS ); return $MAP; } function adodb_error_sqlite() { static $MAP = array( 1 => DB_ERROR_SYNTAX ); return $MAP; } function adodb_error_mysql() { static $MAP = array( 1004 => DB_ERROR_CANNOT_CREATE, 1005 => DB_ERROR_CANNOT_CREATE, 1006 => DB_ERROR_CANNOT_CREATE, 1007 => DB_ERROR_ALREADY_EXISTS, 1008 => DB_ERROR_CANNOT_DROP, 1045 => DB_ERROR_ACCESS_VIOLATION, 1046 => DB_ERROR_NODBSELECTED, 1049 => DB_ERROR_NOSUCHDB, 1050 => DB_ERROR_ALREADY_EXISTS, 1051 => DB_ERROR_NOSUCHTABLE, 1054 => DB_ERROR_NOSUCHFIELD, 1062 => DB_ERROR_ALREADY_EXISTS, 1064 => DB_ERROR_SYNTAX, 1100 => DB_ERROR_NOT_LOCKED, 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1146 => DB_ERROR_NOSUCHTABLE, 1048 => DB_ERROR_CONSTRAINT, 2002 => DB_ERROR_CONNECT_FAILED, 2005 => DB_ERROR_CONNECT_FAILED ); return $MAP; } ?>phpgacl-3.3.7/adodb/adodb-exceptions.inc.php0100644025754300001440000000425010476657245017654 0ustar ipsouserssql = $p1; $this->params = $p2; $s = "$dbms error: [$errno: $errmsg] in $fn(\"$p1\")\n"; break; case 'PCONNECT': case 'CONNECT': $user = $thisConnection->user; $s = "$dbms error: [$errno: $errmsg] in $fn($p1, '$user', '****', $p2)\n"; break; default: $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n"; break; } $this->dbms = $dbms; if ($thisConnection) { $this->host = $thisConnection->host; $this->database = $thisConnection->database; } $this->fn = $fn; $this->msg = $errmsg; if (!is_numeric($errno)) $errno = -1; parent::__construct($s,$errno); } } /** * Default Error Handler. This will be called with the following params * * @param $dbms the RDBMS you are connecting to * @param $fn the name of the calling function (in uppercase) * @param $errno the native error number from the database * @param $errmsg the native error msg from the database * @param $p1 $fn specific parameter - see below * @param $P2 $fn specific parameter - see below */ function adodb_throw($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection) { global $ADODB_EXCEPTION; if (error_reporting() == 0) return; // obey @ protocol if (is_string($ADODB_EXCEPTION)) $errfn = $ADODB_EXCEPTION; else $errfn = 'ADODB_EXCEPTION'; throw new $errfn($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection); } ?>phpgacl-3.3.7/adodb/contrib/0040755025754300001440000000000010476665054014602 5ustar ipsousersphpgacl-3.3.7/adodb/contrib/toxmlrpc.inc.php0100644025754300001440000001471510476657245017743 0ustar ipsousersGetArray()) would work with: * - ADODB_FETCH_BOTH * - null values */ /** * Include the main libraries */ require_once('xmlrpc.inc'); if (!defined('ADODB_DIR')) require_once('adodb.inc.php'); /** * Builds an xmlrpc struct value out of an AdoDB recordset */ function rs2xmlrpcval(&$adodbrs) { $header =& rs2xmlrpcval_header($adodbrs); $body =& rs2xmlrpcval_body($adodbrs); // put it all together and build final xmlrpc struct $xmlrpcrs =& new xmlrpcval ( array( "header" => $header, "body" => $body, ), "struct"); return $xmlrpcrs; } /** * Builds an xmlrpc struct value describing an AdoDB recordset */ function rs2xmlrpcval_header($adodbrs) { $numfields = $adodbrs->FieldCount(); $numrecords = $adodbrs->RecordCount(); // build structure holding recordset information $fieldstruct = array(); for ($i = 0; $i < $numfields; $i++) { $fld = $adodbrs->FetchField($i); $fieldarray = array(); if (isset($fld->name)) $fieldarray["name"] =& new xmlrpcval ($fld->name); if (isset($fld->type)) $fieldarray["type"] =& new xmlrpcval ($fld->type); if (isset($fld->max_length)) $fieldarray["max_length"] =& new xmlrpcval ($fld->max_length, "int"); if (isset($fld->not_null)) $fieldarray["not_null"] =& new xmlrpcval ($fld->not_null, "boolean"); if (isset($fld->has_default)) $fieldarray["has_default"] =& new xmlrpcval ($fld->has_default, "boolean"); if (isset($fld->default_value)) $fieldarray["default_value"] =& new xmlrpcval ($fld->default_value); $fieldstruct[$i] =& new xmlrpcval ($fieldarray, "struct"); } $fieldcount =& new xmlrpcval ($numfields, "int"); $recordcount =& new xmlrpcval ($numrecords, "int"); $sql =& new xmlrpcval ($adodbrs->sql); $fieldinfo =& new xmlrpcval ($fieldstruct, "array"); $header =& new xmlrpcval ( array( "fieldcount" => $fieldcount, "recordcount" => $recordcount, "sql" => $sql, "fieldinfo" => $fieldinfo ), "struct"); return $header; } /** * Builds an xmlrpc struct value out of an AdoDB recordset * (data values only, no data definition) */ function rs2xmlrpcval_body($adodbrs) { $numfields = $adodbrs->FieldCount(); // build structure containing recordset data $adodbrs->MoveFirst(); $rows = array(); while (!$adodbrs->EOF) { $columns = array(); // This should work on all cases of fetch mode: assoc, num, both or default if ($adodbrs->fetchMode == 'ADODB_FETCH_BOTH' || count($adodbrs->fields) == 2 * $adodbrs->FieldCount()) for ($i = 0; $i < $numfields; $i++) if ($adodbrs->fields[$i] === null) $columns[$i] =& new xmlrpcval (''); else $columns[$i] =& xmlrpc_encode ($adodbrs->fields[$i]); else foreach ($adodbrs->fields as $val) if ($val === null) $columns[] =& new xmlrpcval (''); else $columns[] =& xmlrpc_encode ($val); $rows[] =& new xmlrpcval ($columns, "array"); $adodbrs->MoveNext(); } $body =& new xmlrpcval ($rows, "array"); return $body; } /** * Returns an xmlrpc struct value as string out of an AdoDB recordset */ function rs2xmlrpcstring (&$adodbrs) { $xmlrpc = rs2xmlrpcval ($adodbrs); if ($xmlrpc) return $xmlrpc->serialize(); else return null; } /** * Given a well-formed xmlrpc struct object returns an AdoDB object * * @todo add some error checking on the input value */ function xmlrpcval2rs (&$xmlrpcval) { $fields_array = array(); $data_array = array(); // rebuild column information $header =& $xmlrpcval->structmem('header'); $numfields = $header->structmem('fieldcount'); $numfields = $numfields->scalarval(); $numrecords = $header->structmem('recordcount'); $numrecords = $numrecords->scalarval(); $sqlstring = $header->structmem('sql'); $sqlstring = $sqlstring->scalarval(); $fieldinfo =& $header->structmem('fieldinfo'); for ($i = 0; $i < $numfields; $i++) { $temp =& $fieldinfo->arraymem($i); $fld =& new ADOFieldObject(); while (list($key,$value) = $temp->structeach()) { if ($key == "name") $fld->name = $value->scalarval(); if ($key == "type") $fld->type = $value->scalarval(); if ($key == "max_length") $fld->max_length = $value->scalarval(); if ($key == "not_null") $fld->not_null = $value->scalarval(); if ($key == "has_default") $fld->has_default = $value->scalarval(); if ($key == "default_value") $fld->default_value = $value->scalarval(); } // while $fields_array[] = $fld; } // for // fetch recordset information into php array $body =& $xmlrpcval->structmem('body'); for ($i = 0; $i < $numrecords; $i++) { $data_array[$i]= array(); $xmlrpcrs_row =& $body->arraymem($i); for ($j = 0; $j < $numfields; $j++) { $temp =& $xmlrpcrs_row->arraymem($j); $data_array[$i][$j] = $temp->scalarval(); } // for j } // for i // finally build in-memory recordset object and return it $rs =& new ADORecordSet_array(); $rs->InitArrayFields($data_array,$fields_array); return $rs; } ?>phpgacl-3.3.7/adodb/xmlschema03.dtd0100644025754300001440000000326710476657431015770 0ustar ipsousers ]>phpgacl-3.3.7/adodb/adodb-perf.inc.php0100644025754300001440000007422110476657245016434 0ustar ipsousers=2) return (integer) $memarr[1]; return 0; } // avoids localization problems where , is used instead of . function adodb_round($n,$prec) { return number_format($n, $prec, '.', ''); } /* return microtime value as a float */ function adodb_microtime() { $t = microtime(); $t = explode(' ',$t); return (float)$t[1]+ (float)$t[0]; } /* sql code timing */ function& adodb_log_sql(&$conn,$sql,$inputarr) { $perf_table = adodb_perf::table(); $conn->fnExecute = false; $t0 = microtime(); $rs =& $conn->Execute($sql,$inputarr); $t1 = microtime(); if (!empty($conn->_logsql) && (empty($conn->_logsqlErrors) || !$rs)) { $conn->_logsql = false; // disable logsql error simulation $dbT = $conn->databaseType; $a0 = split(' ',$t0); $a0 = (float)$a0[1]+(float)$a0[0]; $a1 = split(' ',$t1); $a1 = (float)$a1[1]+(float)$a1[0]; $time = $a1 - $a0; if (!$rs) { $errM = $conn->ErrorMsg(); $errN = $conn->ErrorNo(); $conn->lastInsID = 0; $tracer = substr('ERROR: '.htmlspecialchars($errM),0,250); } else { $tracer = ''; $errM = ''; $errN = 0; $dbg = $conn->debug; $conn->debug = false; if (!is_object($rs) || $rs->dataProvider == 'empty') $conn->_affected = $conn->affected_rows(true); $conn->lastInsID = @$conn->Insert_ID(); $conn->debug = $dbg; } if (isset($_SERVER['HTTP_HOST'])) { $tracer .= '
'.$_SERVER['HTTP_HOST']; if (isset($_SERVER['PHP_SELF'])) $tracer .= $_SERVER['PHP_SELF']; } else if (isset($_SERVER['PHP_SELF'])) $tracer .= '
'.$_SERVER['PHP_SELF']; //$tracer .= (string) adodb_backtrace(false); $tracer = (string) substr($tracer,0,500); if (is_array($inputarr)) { if (is_array(reset($inputarr))) $params = 'Array sizeof='.sizeof($inputarr); else { // Quote string parameters so we can see them in the // performance stats. This helps spot disabled indexes. $xar_params = $inputarr; foreach ($xar_params as $xar_param_key => $xar_param) { if (gettype($xar_param) == 'string') $xar_params[$xar_param_key] = '"' . $xar_param . '"'; } $params = implode(', ', $xar_params); if (strlen($params) >= 3000) $params = substr($params, 0, 3000); } } else { $params = ''; } if (is_array($sql)) $sql = $sql[0]; $arr = array('b'=>strlen($sql).'.'.crc32($sql), 'c'=>substr($sql,0,3900), 'd'=>$params,'e'=>$tracer,'f'=>adodb_round($time,6)); //var_dump($arr); $saved = $conn->debug; $conn->debug = 0; $d = $conn->sysTimeStamp; if (empty($d)) $d = date("'Y-m-d H:i:s'"); if ($conn->dataProvider == 'oci8' && $dbT != 'oci8po') { $isql = "insert into $perf_table values($d,:b,:c,:d,:e,:f)"; } else if ($dbT == 'odbc_mssql' || $dbT == 'informix' || $dbT == 'odbtp') { $timer = $arr['f']; if ($dbT == 'informix') $sql2 = substr($sql2,0,230); $sql1 = $conn->qstr($arr['b']); $sql2 = $conn->qstr($arr['c']); $params = $conn->qstr($arr['d']); $tracer = $conn->qstr($arr['e']); $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values($d,$sql1,$sql2,$params,$tracer,$timer)"; if ($dbT == 'informix') $isql = str_replace(chr(10),' ',$isql); $arr = false; } else { if ($dbT == 'db2') $arr['f'] = (float) $arr['f']; $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values( $d,?,?,?,?,?)"; } $ok = $conn->Execute($isql,$arr); $conn->debug = $saved; if ($ok) { $conn->_logsql = true; } else { $err2 = $conn->ErrorMsg(); $conn->_logsql = true; // enable logsql error simulation $perf =& NewPerfMonitor($conn); if ($perf) { if ($perf->CreateLogTable()) $ok = $conn->Execute($isql,$arr); } else { $ok = $conn->Execute("create table $perf_table ( created varchar(50), sql0 varchar(250), sql1 varchar(4000), params varchar(3000), tracer varchar(500), timer decimal(16,6))"); } if (!$ok) { ADOConnection::outp( "

LOGSQL Insert Failed: $isql
$err2

"); $conn->_logsql = false; } } $conn->_errorMsg = $errM; $conn->_errorCode = $errN; } $conn->fnExecute = 'adodb_log_sql'; return $rs; } /* The settings data structure is an associative array that database parameter per element. Each database parameter element in the array is itself an array consisting of: 0: category code, used to group related db parameters 1: either a. sql string to retrieve value, eg. "select value from v\$parameter where name='db_block_size'", b. array holding sql string and field to look for, e.g. array('show variables','table_cache'), c. a string prefixed by =, then a PHP method of the class is invoked, e.g. to invoke $this->GetIndexValue(), set this array element to '=GetIndexValue', 2: description of the database parameter */ class adodb_perf { var $conn; var $color = '#F0F0F0'; var $table = ''; var $titles = ''; var $warnRatio = 90; var $tablesSQL = false; var $cliFormat = "%32s => %s \r\n"; var $sql1 = 'sql1'; // used for casting sql1 to text for mssql var $explain = true; var $helpurl = "LogSQL help"; var $createTableSQL = false; var $maxLength = 2000; // Sets the tablename to be used function table($newtable = false) { static $_table; if (!empty($newtable)) $_table = $newtable; if (empty($_table)) $_table = 'adodb_logsql'; return $_table; } // returns array with info to calculate CPU Load function _CPULoad() { /* cpu 524152 2662 2515228 336057010 cpu0 264339 1408 1257951 168025827 cpu1 259813 1254 1257277 168031181 page 622307 25475680 swap 24 1891 intr 890153570 868093576 6 0 4 4 0 6 1 2 0 0 0 124 0 8098760 2 13961053 0 0 0 0 0 0 0 0 0 0 0 0 0 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 disk_io: (3,0):(3144904,54369,610378,3090535,50936192) (3,1):(3630212,54097,633016,3576115,50951320) ctxt 66155838 btime 1062315585 processes 69293 */ // Algorithm is taken from // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp if (strncmp(PHP_OS,'WIN',3)==0) { if (PHP_VERSION == '5.0.0') return false; if (PHP_VERSION == '5.0.1') return false; if (PHP_VERSION == '5.0.2') return false; if (PHP_VERSION == '5.0.3') return false; if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737 @$c = new COM("WinMgmts:{impersonationLevel=impersonate}!Win32_PerfRawData_PerfOS_Processor.Name='_Total'"); if (!$c) return false; $info[0] = $c->PercentProcessorTime; $info[1] = 0; $info[2] = 0; $info[3] = $c->TimeStamp_Sys100NS; //print_r($info); return $info; } // Algorithm - Steve Blinch (BlitzAffe Online, http://www.blitzaffe.com) $statfile = '/proc/stat'; if (!file_exists($statfile)) return false; $fd = fopen($statfile,"r"); if (!$fd) return false; $statinfo = explode("\n",fgets($fd, 1024)); fclose($fd); foreach($statinfo as $line) { $info = explode(" ",$line); if($info[0]=="cpu") { array_shift($info); // pop off "cpu" if(!$info[0]) array_shift($info); // pop off blank space (if any) return $info; } } return false; } /* NOT IMPLEMENTED */ function MemInfo() { /* total: used: free: shared: buffers: cached: Mem: 1055289344 917299200 137990144 0 165437440 599773184 Swap: 2146775040 11055104 2135719936 MemTotal: 1030556 kB MemFree: 134756 kB MemShared: 0 kB Buffers: 161560 kB Cached: 581384 kB SwapCached: 4332 kB Active: 494468 kB Inact_dirty: 322856 kB Inact_clean: 24256 kB Inact_target: 168316 kB HighTotal: 131064 kB HighFree: 1024 kB LowTotal: 899492 kB LowFree: 133732 kB SwapTotal: 2096460 kB SwapFree: 2085664 kB Committed_AS: 348732 kB */ } /* Remember that this is client load, not db server load! */ var $_lastLoad; function CPULoad() { $info = $this->_CPULoad(); if (!$info) return false; if (empty($this->_lastLoad)) { sleep(1); $this->_lastLoad = $info; $info = $this->_CPULoad(); } $last = $this->_lastLoad; $this->_lastLoad = $info; $d_user = $info[0] - $last[0]; $d_nice = $info[1] - $last[1]; $d_system = $info[2] - $last[2]; $d_idle = $info[3] - $last[3]; //printf("Delta - User: %f Nice: %f System: %f Idle: %f
",$d_user,$d_nice,$d_system,$d_idle); if (strncmp(PHP_OS,'WIN',3)==0) { if ($d_idle < 1) $d_idle = 1; return 100*(1-$d_user/$d_idle); }else { $total=$d_user+$d_nice+$d_system+$d_idle; if ($total<1) $total=1; return 100*($d_user+$d_nice+$d_system)/$total; } } function Tracer($sql) { $perf_table = adodb_perf::table(); $saveE = $this->conn->fnExecute; $this->conn->fnExecute = false; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $sqlq = $this->conn->qstr($sql); $arr = $this->conn->GetArray( "select count(*),tracer from $perf_table where sql1=$sqlq group by tracer order by 1 desc"); $s = ''; if ($arr) { $s .= '

Scripts Affected

'; foreach($arr as $k) { $s .= sprintf("%4d",$k[0]).'   '.strip_tags($k[1]).'
'; } } if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_CACHE_MODE = $save; $this->conn->fnExecute = $saveE; return $s; } /* Explain Plan for $sql. If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the actual sql. */ function Explain($sql,$partial=false) { return false; } function InvalidSQL($numsql = 10) { if (isset($_GET['sql'])) return; $s = '

Invalid SQL

'; $saveE = $this->conn->fnExecute; $this->conn->fnExecute = false; $perf_table = adodb_perf::table(); $rs =& $this->conn->SelectLimit("select distinct count(*),sql1,tracer as error_msg from $perf_table where tracer like 'ERROR:%' group by sql1,tracer order by 1 desc",$numsql);//,$numsql); $this->conn->fnExecute = $saveE; if ($rs) { $s .= rs2html($rs,false,false,false,false); } else return "

$this->helpurl. ".$this->conn->ErrorMsg()."

"; return $s; } /* This script identifies the longest running SQL */ function _SuspiciousSQL($numsql = 10) { global $ADODB_FETCH_MODE; $perf_table = adodb_perf::table(); $saveE = $this->conn->fnExecute; $this->conn->fnExecute = false; if (isset($_GET['exps']) && isset($_GET['sql'])) { $partial = !empty($_GET['part']); echo "".$this->Explain($_GET['sql'],$partial)."\n"; } if (isset($_GET['sql'])) return; $sql1 = $this->sql1; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); //$this->conn->debug=1; $rs =& $this->conn->SelectLimit( "select avg(timer) as avg_timer,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer from $perf_table where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT') and (tracer is null or tracer not like 'ERROR:%') group by sql1 order by 1 desc",$numsql); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; $this->conn->fnExecute = $saveE; if (!$rs) return "

$this->helpurl. ".$this->conn->ErrorMsg()."

"; $s = "

Suspicious SQL

The following SQL have high average execution times
ParameterValueDescription
\n"; $max = $this->maxLength; while (!$rs->EOF) { $sql = $rs->fields[1]; $raw = urlencode($sql); if (strlen($raw)>$max-100) { $sql2 = substr($sql,0,$max-500); $raw = urlencode($sql2).'&part='.crc32($sql); } $prefix = ""; $suffix = ""; if ($this->explain == false || strlen($prefix)>$max) { $suffix = ' ... String too long for GET parameter: '.strlen($prefix).''; $prefix = ''; } $s .= ""; $rs->MoveNext(); } return $s."
Avg TimeCountSQLMaxMin
".adodb_round($rs->fields[0],6)."".$rs->fields[2]."".$prefix.htmlspecialchars($sql).$suffix."". "".$rs->fields[3]."".$rs->fields[4]."
"; } function CheckMemory() { return ''; } function SuspiciousSQL($numsql=10) { return adodb_perf::_SuspiciousSQL($numsql); } function ExpensiveSQL($numsql=10) { return adodb_perf::_ExpensiveSQL($numsql); } /* This reports the percentage of load on the instance due to the most expensive few SQL statements. Tuning these statements can often make huge improvements in overall system performance. */ function _ExpensiveSQL($numsql = 10) { global $ADODB_FETCH_MODE; $perf_table = adodb_perf::table(); $saveE = $this->conn->fnExecute; $this->conn->fnExecute = false; if (isset($_GET['expe']) && isset($_GET['sql'])) { $partial = !empty($_GET['part']); echo "".$this->Explain($_GET['sql'],$partial)."\n"; } if (isset($_GET['sql'])) return; $sql1 = $this->sql1; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs =& $this->conn->SelectLimit( "select sum(timer) as total,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer from $perf_table where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT') and (tracer is null or tracer not like 'ERROR:%') group by sql1 having count(*)>1 order by 1 desc",$numsql); if (isset($savem)) $this->conn->SetFetchMode($savem); $this->conn->fnExecute = $saveE; $ADODB_FETCH_MODE = $save; if (!$rs) return "

$this->helpurl. ".$this->conn->ErrorMsg()."

"; $s = "

Expensive SQL

Tuning the following SQL could reduce the server load substantially
\n"; $max = $this->maxLength; while (!$rs->EOF) { $sql = $rs->fields[1]; $raw = urlencode($sql); if (strlen($raw)>$max-100) { $sql2 = substr($sql,0,$max-500); $raw = urlencode($sql2).'&part='.crc32($sql); } $prefix = ""; $suffix = ""; if($this->explain == false || strlen($prefix>$max)) { $prefix = ''; $suffix = ''; } $s .= ""; $rs->MoveNext(); } return $s."
LoadCountSQLMaxMin
".adodb_round($rs->fields[0],6)."".$rs->fields[2]."".$prefix.htmlspecialchars($sql).$suffix."". "".$rs->fields[3]."".$rs->fields[4]."
"; } /* Raw function to return parameter value from $settings. */ function DBParameter($param) { if (empty($this->settings[$param])) return false; $sql = $this->settings[$param][1]; return $this->_DBParameter($sql); } /* Raw function returning array of poll paramters */ function &PollParameters() { $arr[0] = (float)$this->DBParameter('data cache hit ratio'); $arr[1] = (float)$this->DBParameter('data reads'); $arr[2] = (float)$this->DBParameter('data writes'); $arr[3] = (integer) $this->DBParameter('current connections'); return $arr; } /* Low-level Get Database Parameter */ function _DBParameter($sql) { $savelog = $this->conn->LogSQL(false); if (is_array($sql)) { global $ADODB_FETCH_MODE; $sql1 = $sql[0]; $key = $sql[1]; if (sizeof($sql)>2) $pos = $sql[2]; else $pos = 1; if (sizeof($sql)>3) $coef = $sql[3]; else $coef = false; $ret = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false); $rs = $this->conn->Execute($sql1); if (isset($savem)) $this->conn->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs) { while (!$rs->EOF) { $keyf = reset($rs->fields); if (trim($keyf) == $key) { $ret = $rs->fields[$pos]; if ($coef) $ret *= $coef; break; } $rs->MoveNext(); } $rs->Close(); } $this->conn->LogSQL($savelog); return $ret; } else { if (strncmp($sql,'=',1) == 0) { $fn = substr($sql,1); return $this->$fn(); } $sql = str_replace('$DATABASE',$this->conn->database,$sql); $ret = $this->conn->GetOne($sql); $this->conn->LogSQL($savelog); return $ret; } } /* Warn if cache ratio falls below threshold. Displayed in "Description" column. */ function WarnCacheRatio($val) { if ($val < $this->warnRatio) return 'Cache ratio should be at least '.$this->warnRatio.'%'; else return ''; } /***********************************************************************************************/ // HIGH LEVEL UI FUNCTIONS /***********************************************************************************************/ function UI($pollsecs=5) { $perf_table = adodb_perf::table(); $conn = $this->conn; $app = $conn->host; if ($conn->host && $conn->database) $app .= ', db='; $app .= $conn->database; if ($app) $app .= ', '; $savelog = $this->conn->LogSQL(false); $info = $conn->ServerInfo(); if (isset($_GET['clearsql'])) { $this->conn->Execute("delete from $perf_table"); } $this->conn->LogSQL($savelog); // magic quotes if (isset($_GET['sql']) && get_magic_quotes_gpc()) { $_GET['sql'] = $_GET['sql'] = str_replace(array("\\'",'\"'),array("'",'"'),$_GET['sql']); } if (!isset($_SESSION['ADODB_PERF_SQL'])) $nsql = $_SESSION['ADODB_PERF_SQL'] = 10; else $nsql = $_SESSION['ADODB_PERF_SQL']; $app .= $info['description']; if (isset($_GET['do'])) $do = $_GET['do']; else if (isset($_POST['do'])) $do = $_POST['do']; else if (isset($_GET['sql'])) $do = 'viewsql'; else $do = 'stats'; if (isset($_GET['nsql'])) { if ($_GET['nsql'] > 0) $nsql = $_SESSION['ADODB_PERF_SQL'] = (integer) $_GET['nsql']; } echo "ADOdb Performance Monitor on $app"; if ($do == 'viewsql') $form = "
# SQL:
"; else $form = " "; $allowsql = !defined('ADODB_PERF_NO_RUN_SQL'); if (empty($_GET['hidem'])) echo "
ADOdb Performance Monitor for $app
Performance Stats   View SQL   View Tables   Poll Stats", $allowsql ? '   Run SQL' : '', "$form", "
"; switch ($do) { default: case 'stats': echo $this->HealthCheck(); //$this->conn->debug=1; echo $this->CheckMemory(); break; case 'poll': echo ""; break; case 'poll2': echo "
";
			$this->Poll($pollsecs);
			break;
		
		case 'dosql':
			if (!$allowsql) break;
			
			$this->DoSQLForm();
			break;
		case 'viewsql':
			if (empty($_GET['hidem']))
				echo "  Clear SQL Log
"; echo($this->SuspiciousSQL($nsql)); echo($this->ExpensiveSQL($nsql)); echo($this->InvalidSQL($nsql)); break; case 'tables': echo $this->Tables(); break; } global $ADODB_vers; echo "

$ADODB_vers Sponsored by phpLens
"; } /* Runs in infinite loop, returning real-time statistics */ function Poll($secs=5) { $this->conn->fnExecute = false; //$this->conn->debug=1; if ($secs <= 1) $secs = 1; echo "Accumulating statistics, every $secs seconds...\n";flush(); $arro =& $this->PollParameters(); $cnt = 0; set_time_limit(0); sleep($secs); while (1) { $arr =& $this->PollParameters(); $hits = sprintf('%2.2f',$arr[0]); $reads = sprintf('%12.4f',($arr[1]-$arro[1])/$secs); $writes = sprintf('%12.4f',($arr[2]-$arro[2])/$secs); $sess = sprintf('%5d',$arr[3]); $load = $this->CPULoad(); if ($load !== false) { $oslabel = 'WS-CPU%'; $osval = sprintf(" %2.1f ",(float) $load); }else { $oslabel = ''; $osval = ''; } if ($cnt % 10 == 0) echo " Time ".$oslabel." Hit% Sess Reads/s Writes/s\n"; $cnt += 1; echo date('H:i:s').' '.$osval."$hits $sess $reads $writes\n"; flush(); if (connection_aborted()) return; sleep($secs); $arro = $arr; } } /* Returns basic health check in a command line interface */ function HealthCheckCLI() { return $this->HealthCheck(true); } /* Returns basic health check as HTML */ function HealthCheck($cli=false) { $saveE = $this->conn->fnExecute; $this->conn->fnExecute = false; if ($cli) $html = ''; else $html = $this->table.'

'.$this->conn->databaseType.'

'.$this->titles; $oldc = false; $bgc = ''; foreach($this->settings as $name => $arr) { if ($arr === false) break; if (!is_string($name)) { if ($cli) $html .= " -- $arr -- \n"; else $html .= "color>$arr  "; continue; } if (!is_array($arr)) break; $category = $arr[0]; $how = $arr[1]; if (sizeof($arr)>2) $desc = $arr[2]; else $desc = '   '; if ($category == 'HIDE') continue; $val = $this->_DBParameter($how); if ($desc && strncmp($desc,"=",1) === 0) { $fn = substr($desc,1); $desc = $this->$fn($val); } if ($val === false) { $m = $this->conn->ErrorMsg(); $val = "Error: $m"; } else { if (is_numeric($val) && $val >= 256*1024) { if ($val % (1024*1024) == 0) { $val /= (1024*1024); $val .= 'M'; } else if ($val % 1024 == 0) { $val /= 1024; $val .= 'K'; } //$val = htmlspecialchars($val); } } if ($category != $oldc) { $oldc = $category; //$bgc = ($bgc == ' bgcolor='.$this->color) ? ' bgcolor=white' : ' bgcolor='.$this->color; } if (strlen($desc)==0) $desc = ' '; if (strlen($val)==0) $val = ' '; if ($cli) { $html .= str_replace(' ','',sprintf($this->cliFormat,strip_tags($name),strip_tags($val),strip_tags($desc))); }else { $html .= "".$name.''.$val.''.$desc."\n"; } } if (!$cli) $html .= "\n"; $this->conn->fnExecute = $saveE; return $html; } function Tables($orderby='1') { if (!$this->tablesSQL) return false; $savelog = $this->conn->LogSQL(false); $rs = $this->conn->Execute($this->tablesSQL.' order by '.$orderby); $this->conn->LogSQL($savelog); $html = rs2html($rs,false,false,false,false); return $html; } function CreateLogTable() { if (!$this->createTableSQL) return false; $table = $this->table(); $sql = str_replace('adodb_logsql',$table,$this->createTableSQL); $savelog = $this->conn->LogSQL(false); $ok = $this->conn->Execute($sql); $this->conn->LogSQL($savelog); return ($ok) ? true : false; } function DoSQLForm() { $PHP_SELF = $_SERVER['PHP_SELF']; $sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : ''; if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows']; else $rows = 3; if (isset($_REQUEST['SMALLER'])) { $rows /= 2; if ($rows < 3) $rows = 3; $_SESSION['phplens_sqlrows'] = $rows; } if (isset($_REQUEST['BIGGER'])) { $rows *= 2; $_SESSION['phplens_sqlrows'] = $rows; } ?>
Form size:
undomq(trim($sql)); if (substr($sql,strlen($sql)-1) === ';') { $print = true; $sqla = $this->SplitSQL($sql); } else { $print = false; $sqla = array($sql); } foreach($sqla as $sqls) { if (!$sqls) continue; if ($print) { print "

".htmlspecialchars($sqls)."

"; flush(); } $savelog = $this->conn->LogSQL(false); $rs = $this->conn->Execute($sqls); $this->conn->LogSQL($savelog); if ($rs && is_object($rs) && !$rs->EOF) { rs2html($rs); while ($rs->NextRecordSet()) { print "
 
"; rs2html($rs); } } else { $e1 = (integer) $this->conn->ErrorNo(); $e2 = $this->conn->ErrorMsg(); if (($e1) || ($e2)) { if (empty($e1)) $e1 = '-1'; // postgresql fix print '   '.$e1.': '.$e2; } else { print "

No Recordset returned

"; } } } // foreach } function SplitSQL($sql) { $arr = explode(';',$sql); return $arr; } function undomq($m) { if (get_magic_quotes_gpc()) { // undo the damage $m = str_replace('\\\\','\\',$m); $m = str_replace('\"','"',$m); $m = str_replace('\\\'','\'',$m); } return $m; } /************************************************************************/ /** * Reorganise multiple table-indices/statistics/.. * OptimizeMode could be given by last Parameter * * @example *
     *          optimizeTables( 'tableA');
     *      
*
     *          optimizeTables( 'tableA', 'tableB', 'tableC');
     *      
*
     *          optimizeTables( 'tableA', 'tableB', ADODB_OPT_LOW);
     *      
* * @param string table name of the table to optimize * @param int mode optimization-mode * ADODB_OPT_HIGH for full optimization * ADODB_OPT_LOW for CPU-less optimization * Default is LOW ADODB_OPT_LOW * @author Markus Staab * @return Returns true on success and false on error */ function OptimizeTables() { $args = func_get_args(); $numArgs = func_num_args(); if ( $numArgs == 0) return false; $mode = ADODB_OPT_LOW; $lastArg = $args[ $numArgs - 1]; if ( !is_string($lastArg)) { $mode = $lastArg; unset( $args[ $numArgs - 1]); } foreach( $args as $table) { $this->optimizeTable( $table, $mode); } } /** * Reorganise the table-indices/statistics/.. depending on the given mode. * Default Implementation throws an error. * * @param string table name of the table to optimize * @param int mode optimization-mode * ADODB_OPT_HIGH for full optimization * ADODB_OPT_LOW for CPU-less optimization * Default is LOW ADODB_OPT_LOW * @author Markus Staab * @return Returns true on success and false on error */ function OptimizeTable( $table, $mode = ADODB_OPT_LOW) { ADOConnection::outp( sprintf( "

%s: '%s' not implemented for driver '%s'

", __CLASS__, __FUNCTION__, $this->conn->databaseType)); return false; } /** * Reorganise current database. * Default implementation loops over all MetaTables() and * optimize each using optmizeTable() * * @author Markus Staab * @return Returns true on success and false on error */ function optimizeDatabase() { $conn = $this->conn; if ( !$conn) return false; $tables = $conn->MetaTables( 'TABLES'); if ( !$tables ) return false; foreach( $tables as $table) { if ( !$this->optimizeTable( $table)) { return false; } } return true; } // end hack } ?>phpgacl-3.3.7/adodb/adodb-errorhandler.inc.php0100644025754300001440000000527610476657245020173 0ustar ipsousers$s

"; trigger_error($s,ADODB_ERROR_HANDLER_TYPE); } ?> phpgacl-3.3.7/adodb/adodb-iterator.inc.php0100644025754300001440000000307610476657245017331 0ustar ipsousersExecute("select * from adoxyz"); foreach($rs as $k => $v) { echo $k; print_r($v); echo "
"; } Iterator code based on http://cvs.php.net/cvs.php/php-src/ext/spl/examples/cachingiterator.inc?login=2 */ class ADODB_Iterator implements Iterator { private $rs; function __construct($rs) { $this->rs = $rs; } function rewind() { $this->rs->MoveFirst(); } function valid() { return !$this->rs->EOF; } function key() { return $this->rs->_currentRow; } function current() { return $this->rs->fields; } function next() { $this->rs->MoveNext(); } function __call($func, $params) { return call_user_func_array(array($this->rs, $func), $params); } function hasMore() { return !$this->rs->EOF; } } class ADODB_BASE_RS implements IteratorAggregate { function getIterator() { return new ADODB_Iterator($this); } /* this is experimental - i don't really know what to return... */ function __toString() { include_once(ADODB_DIR.'/toexport.inc.php'); return _adodb_export($this,',',',',false,true); } } ?>phpgacl-3.3.7/adodb/drivers/0040755025754300001440000000000010476665055014621 5ustar ipsousersphpgacl-3.3.7/adodb/drivers/adodb-borland_ibase.inc.php0100644025754300001440000000431210476657245021734 0ustar ipsousersADODB_ibase(); } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->autoCommit = false; $this->_transactionID = ibase_trans($this->ibasetrans, $this->_connectionID); return $this->_transactionID; } function ServerInfo() { $arr['dialect'] = $this->dialect; switch($arr['dialect']) { case '': case '1': $s = 'Interbase 6.5, Dialect 1'; break; case '2': $s = 'Interbase 6.5, Dialect 2'; break; default: case '3': $s = 'Interbase 6.5, Dialect 3'; break; } $arr['version'] = '6.5'; $arr['description'] = $s; return $arr; } // Note that Interbase 6.5 uses ROWS instead - don't you love forking wars! // SELECT col1, col2 FROM table ROWS 5 -- get 5 rows // SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 // Firebird uses // SELECT FIRST 5 SKIP 2 col1, col2 FROM TABLE function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { if ($nrows > 0) { if ($offset <= 0) $str = " ROWS $nrows "; else { $a = $offset+1; $b = $offset+$nrows; $str = " ROWS $a TO $b"; } } else { // ok, skip $a = $offset + 1; $str = " ROWS $a TO 999999999"; // 999 million } $sql .= $str; return ($secs2cache) ? $this->CacheExecute($secs2cache,$sql,$inputarr) : $this->Execute($sql,$inputarr); } }; class ADORecordSet_borland_ibase extends ADORecordSet_ibase { var $databaseType = "borland_ibase"; function ADORecordSet_borland_ibase($id,$mode=false) { $this->ADORecordSet_ibase($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-postgres64.inc.php0100644025754300001440000007535010476657245021202 0ustar ipsousers jlim - changed concat operator to || and data types to MetaType to match documented pgsql types see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. 31 Jan 2002 jlim - finally installed postgresql. testing 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type See http://www.varlena.com/varlena/GeneralBits/47.php -- What indexes are on my table? select * from pg_indexes where tablename = 'tablename'; -- What triggers are on my table? select c.relname as "Table", t.tgname as "Trigger Name", t.tgconstrname as "Constraint Name", t.tgenabled as "Enabled", t.tgisconstraint as "Is Constraint", cc.relname as "Referenced Table", p.proname as "Function Name" from pg_trigger t, pg_class c, pg_class cc, pg_proc p where t.tgfoid = p.oid and t.tgrelid = c.oid and t.tgconstrrelid = cc.oid and c.relname = 'tablename'; -- What constraints are on my table? select r.relname as "Table", c.conname as "Constraint Name", contype as "Constraint Type", conkey as "Key Columns", confkey as "Foreign Columns", consrc as "Source" from pg_class r, pg_constraint c where r.oid = c.conrelid and relname = 'tablename'; */ // security - hide paths if (!defined('ADODB_DIR')) die(); function adodb_addslashes($s) { $len = strlen($s); if ($len == 0) return "''"; if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s; // already quoted return "'".addslashes($s)."'"; } class ADODB_postgres64 extends ADOConnection{ var $databaseType = 'postgres64'; var $dataProvider = 'postgres'; var $hasInsertID = true; var $_resultid = false; var $concat_operator='||'; var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages', 'sql_packages', 'sql_sizing', 'sql_sizing_profiles') union select viewname,'V' from pg_views where viewname not like 'pg\_%'"; //"select tablename from pg_tables where tablename not like 'pg_%' order by 1"; var $isoDates = true; // accepts dates in ISO format var $sysDate = "CURRENT_DATE"; var $sysTimeStamp = "CURRENT_TIMESTAMP"; var $blobEncodeType = 'C'; var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum FROM pg_class c, pg_attribute a,pg_type t WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // used when schema defined var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and c.relnamespace=n.oid and n.nspname='%s' and a.attname not like '....%%' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // get primary key etc -- from Freek Dijkstra var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; var $hasAffectedRows = true; var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 // below suggested by Freek Dijkstra var $true = 'TRUE'; // string that represents TRUE for a database var $false = 'FALSE'; // string that represents FALSE for a database var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database var $fmtTimeStamp = "'Y-m-d H:i:s'"; // used by DBTimeStamp as the default timestamp fmt. var $hasMoveFirst = true; var $hasGenID = true; var $_genIDSQL = "SELECT NEXTVAL('%s')"; var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; var $_dropSeqSQL = "DROP SEQUENCE %s"; var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; var $random = 'random()'; /// random function var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4 // http://bugs.php.net/bug.php?id=25404 var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance. // The last (fmtTimeStamp is not entirely correct: // PostgreSQL also has support for time zones, // and writes these time in this format: "2001-03-01 18:59:26+02". // There is no code for the "+02" time zone information, so I just left that out. // I'm not familiar enough with both ADODB as well as Postgres // to know what the concequences are. The other values are correct (wheren't in 0.94) // -- Freek Dijkstra function ADODB_postgres64() { // changes the metaColumnsSQL, adds columns: attnum[6] } function ServerInfo() { if (isset($this->version)) return $this->version; $arr['description'] = $this->GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); $this->version = $arr; return $arr; } function IfNull( $field, $ifNull ) { return " coalesce($field, $ifNull) "; } // get the last id - never tested function pg_insert_id($tablename,$fieldname) { $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq"); if ($result) { $arr = @pg_fetch_row($result,0); pg_freeresult($result); if (isset($arr[0])) return $arr[0]; } return false; } /* Warning from http://www.php.net/manual/function.pg-getlastoid.php: Using a OID as a unique identifier is not generally wise. Unless you are very careful, you might end up with a tuple having a different OID if a database must be reloaded. */ function _insertid($table,$column) { if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; $oid = pg_getlastoid($this->_resultid); // to really return the id, we need the table and column-name, else we can only return the oid != id return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid); } // I get this error with PHP before 4.0.6 - jlim // Warning: This compilation does not support pg_cmdtuples() in adodb-postgres.inc.php on line 44 function _affectedrows() { if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; return pg_cmdtuples($this->_resultid); } // returns true/false function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; return @pg_Exec($this->_connectionID, "begin ".$this->_transmode); } function RowLock($tables,$where,$flds='1 as ignore') { if (!$this->transCnt) $this->BeginTrans(); return $this->GetOne("select $flds from $tables where $where for update"); } // returns true/false. function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); $this->transCnt -= 1; return @pg_Exec($this->_connectionID, "commit"); } // returns true/false function RollbackTrans() { if ($this->transOff) return true; $this->transCnt -= 1; return @pg_Exec($this->_connectionID, "rollback"); } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { $info = $this->ServerInfo(); if ($info['version'] >= 7.3) { $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') union select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; } if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(strtolower($mask)); if ($info['version']>=7.3) $this->metaTablesSQL = " select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') union select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; else $this->metaTablesSQL = " select tablename,'T' from pg_tables where tablename like $mask union select viewname,'V' from pg_views where viewname like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } // if magic quotes disabled, use pg_escape_string() function qstr($s,$magic_quotes=false) { if (!$magic_quotes) { if (ADODB_PHPVER >= 0x4200) { return "'".pg_escape_string($s)."'"; } if ($this->replaceQuote[0] == '\\'){ $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s); } return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " $s = str_replace('\\"','"',$s); return "'$s'"; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = 'TO_CHAR('.$col.",'"; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= 'YYYY'; break; case 'Q': case 'q': $s .= 'Q'; break; case 'M': $s .= 'Mon'; break; case 'm': $s .= 'MM'; break; case 'D': case 'd': $s .= 'DD'; break; case 'H': $s.= 'HH24'; break; case 'h': $s .= 'HH'; break; case 'i': $s .= 'MI'; break; case 's': $s .= 'SS'; break; case 'a': case 'A': $s .= 'AM'; break; case 'w': $s .= 'D'; break; case 'l': $s .= 'DAY'; break; case 'W': $s .= 'WW'; break; default: // handle escape characters... if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; else $s .= '"'.$ch.'"'; } } return $s. "')"; } /* * Load a Large Object from a file * - the procedure stores the object id in the table and imports the object using * postgres proprietary blob handling routines * * contributed by Mattia Rossi mattia@technologist.com * modified for safe mode by juraj chlebec */ function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') { pg_exec ($this->_connectionID, "begin"); $fd = fopen($path,'r'); $contents = fread($fd,filesize($path)); fclose($fd); $oid = pg_lo_create($this->_connectionID); $handle = pg_lo_open($this->_connectionID, $oid, 'w'); pg_lo_write($handle, $contents); pg_lo_close($handle); // $oid = pg_lo_import ($path); pg_exec($this->_connectionID, "commit"); $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); $rez = !empty($rs); return $rez; } /* * Deletes/Unlinks a Blob from the database, otherwise it * will be left behind * * Returns TRUE on success or FALSE on failure. * * contributed by Todd Rogers todd#windfox.net */ function BlobDelete( $blob ) { pg_exec ($this->_connectionID, "begin"); $result = @pg_lo_unlink($blob); pg_exec ($this->_connectionID, "commit"); return( $result ); } /* Hueristic - not guaranteed to work. */ function GuessOID($oid) { if (strlen($oid)>16) return false; return is_numeric($oid); } /* * If an OID is detected, then we use pg_lo_* to open the oid file and read the * real blob from the db using the oid supplied as a parameter. If you are storing * blobs using bytea, we autodetect and process it so this function is not needed. * * contributed by Mattia Rossi mattia@technologist.com * * see http://www.postgresql.org/idocs/index.php?largeobjects.html * * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also * added maxsize parameter, which defaults to $db->maxblobsize if not defined. */ function BlobDecode($blob,$maxsize=false,$hastrans=true) { if (!$this->GuessOID($blob)) return $blob; if ($hastrans) @pg_exec($this->_connectionID,"begin"); $fd = @pg_lo_open($this->_connectionID,$blob,"r"); if ($fd === false) { if ($hastrans) @pg_exec($this->_connectionID,"commit"); return $blob; } if (!$maxsize) $maxsize = $this->maxblobsize; $realblob = @pg_loread($fd,$maxsize); @pg_loclose($fd); if ($hastrans) @pg_exec($this->_connectionID,"commit"); return $realblob; } /* See http://www.postgresql.org/idocs/index.php?datatype-binary.html NOTE: SQL string literals (input strings) must be preceded with two backslashes due to the fact that they must pass through two parsers in the PostgreSQL backend. */ function BlobEncode($blob) { if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob); /*92=backslash, 0=null, 39=single-quote*/ $badch = array(chr(92),chr(0),chr(39)); # \ null ' $fixch = array('\\\\134','\\\\000','\\\\047'); return adodb_str_replace($badch,$fixch,$blob); // note that there is a pg_escape_bytea function only for php 4.2.0 or later } // assumes bytea for blob, and varchar for clob function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { if ($blobtype == 'CLOB') { return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where"); } // do not use bind params which uses qstr(), as blobencode() already quotes data return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where"); } function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; else if (strncmp($date,"'",1) == 0) { $len = strlen($date); if (10 <= $len && $len <= 12) $date = 'date '.$date; else $date = 'timestamp '.$date; } return "($date+interval'$dayFraction days')"; } // for schema support, pass in the $table param "$schema.$tabname". // converts field names to lowercase, $upper is ignored // see http://phplens.com/lens/lensforum/msgs.php?id=14018 for more info function &MetaColumns($table,$normalize=true) { global $ADODB_FETCH_MODE; $schema = false; $false = false; $this->_findschema($table,$schema); if ($normalize) $table = strtolower($table); $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false) { return $false; } if (!empty($this->metaKeySQL)) { // If we want the primary keys, we have to issue a separate query // Of course, a modified version of the metaColumnsSQL query using a // LEFT JOIN would have been much more elegant, but postgres does // not support OUTER JOINS. So here is the clumsy way. $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); // fetch all result in once for performance. $keys =& $rskey->GetArray(); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; $rskey->Close(); unset($rskey); } $rsdefa = array(); if (!empty($this->metaDefaultsSQL)) { $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $sql = sprintf($this->metaDefaultsSQL, ($table)); $rsdef = $this->Execute($sql); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rsdef) { while (!$rsdef->EOF) { $num = $rsdef->fields['num']; $s = $rsdef->fields['def']; if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ $s = substr($s, 1); $s = substr($s, 0, strlen($s) - 1); } $rsdefa[$num] = $s; $rsdef->MoveNext(); } } else { ADOConnection::outp( "==> SQL => " . $sql); } unset($rsdef); } $retarr = array(); while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->attnum = $rs->fields[6]; if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; if ($fld->max_length <= 0) $fld->max_length = -1; if ($fld->type == 'numeric') { $fld->scale = $fld->max_length & 0xFFFF; $fld->max_length >>= 16; } // dannym // 5 hasdefault; 6 num-of-column $fld->has_default = ($rs->fields[5] == 't'); if ($fld->has_default) { $fld->default_value = $rsdefa[$rs->fields[6]]; } //Freek $fld->not_null = $rs->fields[4] == 't'; // Freek if (is_array($keys)) { foreach($keys as $key) { if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') $fld->primary_key = true; if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') $fld->unique = true; // What name is more compatible? } } if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) return $false; else return $retarr; } function &MetaIndexes ($table, $primary = FALSE) { global $ADODB_FETCH_MODE; $schema = false; $this->_findschema($table,$schema); if ($schema) { // requires pgsql 7.3+ - pg_namespace used. $sql = ' SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" FROM pg_catalog.pg_class c JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid ,pg_namespace n WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\''; } else { $sql = ' SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" FROM pg_catalog.pg_class c JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; } if ($primary == FALSE) { $sql .= ' AND i.indisprimary=false;'; } $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $rs = $this->Execute(sprintf($sql,$table,$table,$schema)); if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { $false = false; return $false; } $col_names = $this->MetaColumnNames($table,true,true); //3rd param is use attnum, // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976 $indexes = array(); while ($row = $rs->FetchRow()) { $columns = array(); foreach (explode(' ', $row[2]) as $col) { $columns[] = $col_names[$col]; } $indexes[$row[0]] = array( 'unique' => ($row[1] == 't'), 'columns' => $columns ); } return $indexes; } // returns true or false // // examples: // $db->Connect("host=host1 user=user1 password=secret port=4341"); // $db->Connect('host1','user1','secret'); function _connect($str,$user='',$pwd='',$db='',$ctype=0) { if (!function_exists('pg_connect')) return null; $this->_errorMsg = false; if ($user || $pwd || $db) { $user = adodb_addslashes($user); $pwd = adodb_addslashes($pwd); if (strlen($db) == 0) $db = 'template1'; $db = adodb_addslashes($db); if ($str) { $host = split(":", $str); if ($host[0]) $str = "host=".adodb_addslashes($host[0]); else $str = ''; if (isset($host[1])) $str .= " port=$host[1]"; else if (!empty($this->port)) $str .= " port=".$this->port; } if ($user) $str .= " user=".$user; if ($pwd) $str .= " password=".$pwd; if ($db) $str .= " dbname=".$db; } //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432"; if ($ctype === 1) { // persistent $this->_connectionID = pg_pconnect($str); } else { if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str static $ncnt; if (empty($ncnt)) $ncnt = 1; else $ncnt += 1; $str .= str_repeat(' ',$ncnt); } $this->_connectionID = pg_connect($str); } if ($this->_connectionID === false) return false; $this->Execute("set datestyle='ISO'"); return true; } function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1); } // returns true or false // // examples: // $db->PConnect("host=host1 user=user1 password=secret port=4341"); // $db->PConnect('host1','user1','secret'); function _pconnect($str,$user='',$pwd='',$db='') { return $this->_connect($str,$user,$pwd,$db,1); } // returns queryID or false function _query($sql,$inputarr) { $this->_errorMsg = false; if ($inputarr) { /* It appears that PREPARE/EXECUTE is slower for many queries. For query executed 1000 times: "select id,firstname,lastname from adoxyz where firstname not like ? and lastname not like ? and id = ?" with plan = 1.51861286163 secs no plan = 1.26903700829 secs */ $plan = 'P'.md5($sql); $execp = ''; foreach($inputarr as $v) { if ($execp) $execp .= ','; if (is_string($v)) { if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v); } else { $execp .= $v; } } if ($execp) $exsql = "EXECUTE $plan ($execp)"; else $exsql = "EXECUTE $plan"; $rez = @pg_exec($this->_connectionID,$exsql); if (!$rez) { # Perhaps plan does not exist? Prepare/compile plan. $params = ''; foreach($inputarr as $v) { if ($params) $params .= ','; if (is_string($v)) { $params .= 'VARCHAR'; } else if (is_integer($v)) { $params .= 'INTEGER'; } else { $params .= "REAL"; } } $sqlarr = explode('?',$sql); //print_r($sqlarr); $sql = ''; $i = 1; foreach($sqlarr as $v) { $sql .= $v.' $'.$i; $i++; } $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2); //adodb_pr($s); pg_exec($this->_connectionID,$s); //echo $this->ErrorMsg(); } $rez = pg_exec($this->_connectionID,$exsql); } else { //adodb_backtrace(); $rez = pg_exec($this->_connectionID,$sql); } // check if no data returned, then no need to create real recordset if ($rez && pg_numfields($rez) <= 0) { if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') { pg_freeresult($this->_resultid); } $this->_resultid = $rez; return true; } return $rez; } function _errconnect() { if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED; else return 'Database connection failed'; } /* Returns: the last error message from previous database operation */ function ErrorMsg() { if ($this->_errorMsg !== false) return $this->_errorMsg; if (ADODB_PHPVER >= 0x4300) { if (!empty($this->_resultid)) { $this->_errorMsg = @pg_result_error($this->_resultid); if ($this->_errorMsg) return $this->_errorMsg; } if (!empty($this->_connectionID)) { $this->_errorMsg = @pg_last_error($this->_connectionID); } else $this->_errorMsg = $this->_errconnect(); } else { if (empty($this->_connectionID)) $this->_errconnect(); else $this->_errorMsg = @pg_errormessage($this->_connectionID); } return $this->_errorMsg; } function ErrorNo() { $e = $this->ErrorMsg(); if (strlen($e)) { return ADOConnection::MetaError($e); } return 0; } // returns true or false function _close() { if ($this->transCnt) $this->RollbackTrans(); if ($this->_resultid) { @pg_freeresult($this->_resultid); $this->_resultid = false; } @pg_close($this->_connectionID); $this->_connectionID = false; return true; } /* * Maximum size of C field */ function CharMax() { return 1000000000; // should be 1 Gb? } /* * Maximum size of X field */ function TextMax() { return 1000000000; // should be 1 Gb? } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_postgres64 extends ADORecordSet{ var $_blobArr; var $databaseType = "postgres64"; var $canSeek = true; function ADORecordSet_postgres64($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = PGSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function &GetRowAssoc($upper=true) { if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields; $row =& ADORecordSet::GetRowAssoc($upper); return $row; } function _initrs() { global $ADODB_COUNTRECS; $qid = $this->_queryID; $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1; $this->_numOfFields = @pg_numfields($qid); // cache types for blob decode check // apparently pg_fieldtype actually performs an sql query on the database to get the type. if (empty($this->connection->noBlobs)) for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { if (pg_fieldtype($qid,$i) == 'bytea') { $this->_blobArr[$i] = pg_fieldname($qid,$i); } } } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function &FetchField($off = 0) { // offsets begin at 0 $o= new ADOFieldObject(); $o->name = @pg_fieldname($this->_queryID,$off); $o->type = @pg_fieldtype($this->_queryID,$off); $o->max_length = @pg_fieldsize($this->_queryID,$off); return $o; } function _seek($row) { return @pg_fetch_row($this->_queryID,$row); } function _decode($blob) { eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";'); return $realblob; } function _fixblobs() { if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) { foreach($this->_blobArr as $k => $v) { $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]); } } if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { foreach($this->_blobArr as $k => $v) { $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); } } } // 10% speedup to move MoveNext to child class function MoveNext() { if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); if (is_array($this->fields) && $this->fields) { if (isset($this->_blobArr)) $this->_fixblobs(); return true; } } $this->fields = false; $this->EOF = true; } return false; } function _fetch() { if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) return false; $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); if ($this->fields && isset($this->_blobArr)) $this->_fixblobs(); return (is_array($this->fields)); } function _close() { return @pg_freeresult($this->_queryID); } function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'MONEY': // stupid, postgres expects money to be a string case 'INTERVAL': case 'CHAR': case 'CHARACTER': case 'VARCHAR': case 'NAME': case 'BPCHAR': case '_VARCHAR': case 'INET': case 'MACADDR': if ($len <= $this->blobSize) return 'C'; case 'TEXT': return 'X'; case 'IMAGE': // user defined type case 'BLOB': // user defined type case 'BIT': // This is a bit string, not a single bit, so don't return 'L' case 'VARBIT': case 'BYTEA': return 'B'; case 'BOOL': case 'BOOLEAN': return 'L'; case 'DATE': return 'D'; case 'TIMESTAMP WITHOUT TIME ZONE': case 'TIME': case 'DATETIME': case 'TIMESTAMP': case 'TIMESTAMPTZ': return 'T'; case 'SMALLINT': case 'BIGINT': case 'INTEGER': case 'INT8': case 'INT4': case 'INT2': if (isset($fieldobj) && empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I'; case 'OID': case 'SERIAL': return 'R'; default: return 'N'; } } } ?>phpgacl-3.3.7/adodb/drivers/adodb-pdo_oci.inc.php0100644025754300001440000000513410476657245020567 0ustar ipsousers_bindInputArray = true; $parentDriver->_nestedSQL = true; if ($this->_initdate) { $parentDriver->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); } } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(strtoupper($mask)); $this->metaTablesSQL .= " AND table_name like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } function &MetaColumns($table) { global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) { return $false; } $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->scale = $rs->fields[3]; if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) { $fld->type ='INT'; $fld->max_length = $rs->fields[4]; } $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) return $false; else return $retarr; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-ldap.inc.php0100644025754300001440000002732010476657245020074 0ustar ipsousersport); if ( strstr( $host, ':' ) ) { $conn_info = split( ':', $host ); } $this->_connectionID = ldap_connect( $conn_info[0], $conn_info[1] ); if (!$this->_connectionID) { $e = 'Could not connect to ' . $conn_info[0]; $this->_errorMsg = $e; if ($this->debug) ADOConnection::outp($e); return false; } if( count( $LDAP_CONNECT_OPTIONS ) > 0 ) { $this->_inject_bind_options( $LDAP_CONNECT_OPTIONS ); } if ($username) { $bind = ldap_bind( $this->_connectionID, $username, $password ); } else { $username = 'anonymous'; $bind = ldap_bind( $this->_connectionID ); } if (!$bind) { $e = 'Could not bind to ' . $conn_info[0] . " as ".$username; $this->_errorMsg = $e; if ($this->debug) ADOConnection::outp($e); return false; } $this->_errorMsg = ''; $this->database = $ldapbase; return $this->_connectionID; } /* Valid Domain Values for LDAP Options: LDAP_OPT_DEREF (integer) LDAP_OPT_SIZELIMIT (integer) LDAP_OPT_TIMELIMIT (integer) LDAP_OPT_PROTOCOL_VERSION (integer) LDAP_OPT_ERROR_NUMBER (integer) LDAP_OPT_REFERRALS (boolean) LDAP_OPT_RESTART (boolean) LDAP_OPT_HOST_NAME (string) LDAP_OPT_ERROR_STRING (string) LDAP_OPT_MATCHED_DN (string) LDAP_OPT_SERVER_CONTROLS (array) LDAP_OPT_CLIENT_CONTROLS (array) Make sure to set this BEFORE calling Connect() Example: $LDAP_CONNECT_OPTIONS = Array( Array ( "OPTION_NAME"=>LDAP_OPT_DEREF, "OPTION_VALUE"=>2 ), Array ( "OPTION_NAME"=>LDAP_OPT_SIZELIMIT, "OPTION_VALUE"=>100 ), Array ( "OPTION_NAME"=>LDAP_OPT_TIMELIMIT, "OPTION_VALUE"=>30 ), Array ( "OPTION_NAME"=>LDAP_OPT_PROTOCOL_VERSION, "OPTION_VALUE"=>3 ), Array ( "OPTION_NAME"=>LDAP_OPT_ERROR_NUMBER, "OPTION_VALUE"=>13 ), Array ( "OPTION_NAME"=>LDAP_OPT_REFERRALS, "OPTION_VALUE"=>FALSE ), Array ( "OPTION_NAME"=>LDAP_OPT_RESTART, "OPTION_VALUE"=>FALSE ) ); */ function _inject_bind_options( $options ) { foreach( $options as $option ) { ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] ) or die( "Unable to set server option: " . $option["OPTION_NAME"] ); } } /* returns _queryID or false */ function _query($sql,$inputarr) { $rs = ldap_search( $this->_connectionID, $this->database, $sql ); $this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql; return $rs; } /* closes the LDAP connection */ function _close() { @ldap_close( $this->_connectionID ); $this->_connectionID = false; } function SelectDB($db) { $this->database = $db; return true; } // SelectDB function ServerInfo() { if( !empty( $this->version ) ) return $this->version; $version = array(); /* Determines how aliases are handled during search. LDAP_DEREF_NEVER (0x00) LDAP_DEREF_SEARCHING (0x01) LDAP_DEREF_FINDING (0x02) LDAP_DEREF_ALWAYS (0x03) The LDAP_DEREF_SEARCHING value means aliases are dereferenced during the search but not when locating the base object of the search. The LDAP_DEREF_FINDING value means aliases are dereferenced when locating the base object but not during the search. Default: LDAP_DEREF_NEVER */ ldap_get_option( $this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF'] ) ; switch ( $version['LDAP_OPT_DEREF'] ) { case 0: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_NEVER'; case 1: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_SEARCHING'; case 2: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_FINDING'; case 3: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_ALWAYS'; } /* A limit on the number of entries to return from a search. LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ ldap_get_option( $this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT'] ); if ( $version['LDAP_OPT_SIZELIMIT'] == 0 ) { $version['LDAP_OPT_SIZELIMIT'] = 'LDAP_NO_LIMIT'; } /* A limit on the number of seconds to spend on a search. LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ ldap_get_option( $this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT'] ); if ( $version['LDAP_OPT_TIMELIMIT'] == 0 ) { $version['LDAP_OPT_TIMELIMIT'] = 'LDAP_NO_LIMIT'; } /* Determines whether the LDAP library automatically follows referrals returned by LDAP servers or not. LDAP_OPT_ON LDAP_OPT_OFF Default: ON */ ldap_get_option( $this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS'] ); if ( $version['LDAP_OPT_REFERRALS'] == 0 ) { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_ON'; } /* Determines whether LDAP I/O operations are automatically restarted if they abort prematurely. LDAP_OPT_ON LDAP_OPT_OFF Default: OFF */ ldap_get_option( $this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART'] ); if ( $version['LDAP_OPT_RESTART'] == 0 ) { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_ON'; } /* This option indicates the version of the LDAP protocol used when communicating with the primary LDAP server. LDAP_VERSION2 (2) LDAP_VERSION3 (3) Default: LDAP_VERSION2 (2) */ ldap_get_option( $this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION'] ); if ( $version['LDAP_OPT_PROTOCOL_VERSION'] == 2 ) { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION2'; } else { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION3'; } /* The host name (or list of hosts) for the primary LDAP server. */ ldap_get_option( $this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME'] ); ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER'] ); ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING'] ); ldap_get_option( $this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN'] ); return $this->version = $version; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_ldap extends ADORecordSet{ var $databaseType = "ldap"; var $canSeek = false; var $_entryID; /* keeps track of the entry resource identifier */ function ADORecordSet_ldap($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = LDAP_NUM; break; case ADODB_FETCH_ASSOC: $this->fetchMode = LDAP_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = LDAP_BOTH; break; } $this->ADORecordSet($queryID); } function _initrs() { /* This could be teaked to respect the $COUNTRECS directive from ADODB It's currently being used in the _fetch() function and the GetAssoc() function */ $this->_numOfRows = ldap_count_entries( $this->connection->_connectionID, $this->_queryID ); } /* Return whole recordset as a multi-dimensional associative array */ function &GetAssoc($force_array = false, $first2cols = false) { $records = $this->_numOfRows; $results = array(); for ( $i=0; $i < $records; $i++ ) { foreach ( $this->fields as $k=>$v ) { if ( is_array( $v ) ) { if ( $v['count'] == 1 ) { $results[$i][$k] = $v[0]; } else { array_shift( $v ); $results[$i][$k] = $v; } } } } return $results; } function &GetRowAssoc() { $results = array(); foreach ( $this->fields as $k=>$v ) { if ( is_array( $v ) ) { if ( $v['count'] == 1 ) { $results[$k] = $v[0]; } else { array_shift( $v ); $results[$k] = $v; } } } return $results; } function GetRowNums() { $results = array(); foreach ( $this->fields as $k=>$v ) { static $i = 0; if (is_array( $v )) { if ( $v['count'] == 1 ) { $results[$i] = $v[0]; } else { array_shift( $v ); $results[$i] = $v; } $i++; } } return $results; } function _fetch() { if ( $this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0 ) return false; if ( $this->_currentRow == 0 ) { $this->_entryID = ldap_first_entry( $this->connection->_connectionID, $this->_queryID ); } else { $this->_entryID = ldap_next_entry( $this->connection->_connectionID, $this->_entryID ); } $this->fields = ldap_get_attributes( $this->connection->_connectionID, $this->_entryID ); $this->_numOfFields = $this->fields['count']; switch ( $this->fetchMode ) { case LDAP_ASSOC: $this->fields = $this->GetRowAssoc(); break; case LDAP_NUM: $this->fields = array_merge($this->GetRowNums(),$this->GetRowAssoc()); break; case LDAP_BOTH: default: $this->fields = $this->GetRowNums(); break; } return ( is_array( $this->fields ) ); } function _close() { @ldap_free_result( $this->_queryID ); $this->_queryID = false; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-odbtp_unicode.inc.php0100644025754300001440000000215710476657245021773 0ustar ipsousers // security - hide paths if (!defined('ADODB_DIR')) die(); /* Because the ODBTP server sends and reads UNICODE text data using UTF-8 encoding, the following HTML meta tag must be included within the HTML head section of every HTML form and script page: Also, all SQL query strings must be submitted as UTF-8 encoded text. */ if (!defined('_ADODB_ODBTP_LAYER')) { include(ADODB_DIR."/drivers/adodb-odbtp.inc.php"); } class ADODB_odbtp_unicode extends ADODB_odbtp { var $databaseType = 'odbtp'; var $_useUnicodeSQL = true; function ADODB_odbtp_unicode() { $this->ADODB_odbtp(); } } ?> phpgacl-3.3.7/adodb/drivers/adodb-ado_mssql.inc.php0100644025754300001440000001024210476657245021131 0ustar ipsousers= 5) include(ADODB_DIR."/drivers/adodb-ado5.inc.php"); else include(ADODB_DIR."/drivers/adodb-ado.inc.php"); } class ADODB_ado_mssql extends ADODB_ado { var $databaseType = 'ado_mssql'; var $hasTop = 'top'; var $hasInsertID = true; var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; var $sysTimeStamp = 'GetDate()'; var $leftOuter = '*='; var $rightOuter = '=*'; var $ansiOuter = true; // for mssql7 or later var $substr = "substring"; var $length = 'len'; //var $_inTransaction = 1; // always open recordsets, so no transaction problems. function ADODB_ado_mssql() { $this->ADODB_ado(); } function _insertid() { return $this->GetOne('select @@identity'); } function _affectedrows() { return $this->GetOne('select @@rowcount'); } function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET TRANSACTION ".$transaction_mode); } function MetaColumns($table) { $table = strtoupper($table); $arr= array(); $dbc = $this->_connectionID; $osoptions = array(); $osoptions[0] = null; $osoptions[1] = null; $osoptions[2] = $table; $osoptions[3] = null; $adors=@$dbc->OpenSchema(4, $osoptions);//tables if ($adors){ while (!$adors->EOF){ $fld = new ADOFieldObject(); $c = $adors->Fields(3); $fld->name = $c->Value; $fld->type = 'CHAR'; // cannot discover type in ADO! $fld->max_length = -1; $arr[strtoupper($fld->name)]=$fld; $adors->MoveNext(); } $adors->Close(); } $false = false; return empty($arr) ? $false : $arr; } function CreateSequence($seq='adodbseq',$start=1) { $this->Execute('BEGIN TRANSACTION adodbseq'); $start -= 1; $this->Execute("create table $seq (id float(53))"); $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); if (!$ok) { $this->Execute('ROLLBACK TRANSACTION adodbseq'); return false; } $this->Execute('COMMIT TRANSACTION adodbseq'); return true; } function GenID($seq='adodbseq',$start=1) { //$this->debug=1; $this->Execute('BEGIN TRANSACTION adodbseq'); $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1"); if (!$ok) { $this->Execute("create table $seq (id float(53))"); $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); if (!$ok) { $this->Execute('ROLLBACK TRANSACTION adodbseq'); return false; } $this->Execute('COMMIT TRANSACTION adodbseq'); return $start; } $num = $this->GetOne("select id from $seq"); $this->Execute('COMMIT TRANSACTION adodbseq'); return $num; // in old implementation, pre 1.90, we returned GUID... //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'"); } } // end class class ADORecordSet_ado_mssql extends ADORecordSet_ado { var $databaseType = 'ado_mssql'; function ADORecordSet_ado_mssql($id,$mode=false) { return $this->ADORecordSet_ado($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-ibase.inc.php0100644025754300001440000006140410476657245020240 0ustar ipsousers changed transaction handling and added experimental blob stuff Docs to interbase at the website http://www.synectics.co.za/php3/tutorial/IB_PHP3_API.html To use gen_id(), see http://www.volny.cz/iprenosil/interbase/ip_ib_code.htm#_code_creategen $rs = $conn->Execute('select gen_id(adodb,1) from rdb$database'); $id = $rs->fields[0]; $conn->Execute("insert into table (id, col1,...) values ($id, $val1,...)"); */ // security - hide paths if (!defined('ADODB_DIR')) die(); class ADODB_ibase extends ADOConnection { var $databaseType = "ibase"; var $dataProvider = "ibase"; var $replaceQuote = "''"; // string to use to replace quotes var $ibase_datefmt = '%Y-%m-%d'; // For hours,mins,secs change to '%Y-%m-%d %H:%M:%S'; var $fmtDate = "'Y-m-d'"; var $ibase_timestampfmt = "%Y-%m-%d %H:%M:%S"; var $ibase_timefmt = "%H:%M:%S"; var $fmtTimeStamp = "'Y-m-d, H:i:s'"; var $concat_operator='||'; var $_transactionID; var $metaTablesSQL = "select rdb\$relation_name from rdb\$relations where rdb\$relation_name not like 'RDB\$%'"; //OPN STUFF start var $metaColumnsSQL = "select a.rdb\$field_name, a.rdb\$null_flag, a.rdb\$default_source, b.rdb\$field_length, b.rdb\$field_scale, b.rdb\$field_sub_type, b.rdb\$field_precision, b.rdb\$field_type from rdb\$relation_fields a, rdb\$fields b where a.rdb\$field_source = b.rdb\$field_name and a.rdb\$relation_name = '%s' order by a.rdb\$field_position asc"; //OPN STUFF end var $ibasetrans; var $hasGenID = true; var $_bindInputArray = true; var $buffers = 0; var $dialect = 1; var $sysDate = "cast('TODAY' as timestamp)"; var $sysTimeStamp = "CURRENT_TIMESTAMP"; //"cast('NOW' as timestamp)"; var $ansiOuter = true; var $hasAffectedRows = false; var $poorAffectedRows = true; var $blobEncodeType = 'C'; var $role = false; function ADODB_ibase() { if (defined('IBASE_DEFAULT')) $this->ibasetrans = IBASE_DEFAULT; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false) { if (!function_exists('ibase_pconnect')) return null; if ($argDatabasename) $argHostname .= ':'.$argDatabasename; $fn = ($persist) ? 'ibase_pconnect':'ibase_connect'; if ($this->role) $this->_connectionID = $fn($argHostname,$argUsername,$argPassword, $this->charSet,$this->buffers,$this->dialect,$this->role); else $this->_connectionID = $fn($argHostname,$argUsername,$argPassword, $this->charSet,$this->buffers,$this->dialect); if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html $this->replaceQuote = "''"; } if ($this->_connectionID === false) { $this->_handleerror(); return false; } // PHP5 change. if (function_exists('ibase_timefmt')) { ibase_timefmt($this->ibase_datefmt,IBASE_DATE ); if ($this->dialect == 1) ibase_timefmt($this->ibase_datefmt,IBASE_TIMESTAMP ); else ibase_timefmt($this->ibase_timestampfmt,IBASE_TIMESTAMP ); ibase_timefmt($this->ibase_timefmt,IBASE_TIME ); } else { ini_set("ibase.timestampformat", $this->ibase_timestampfmt); ini_set("ibase.dateformat", $this->ibase_datefmt); ini_set("ibase.timeformat", $this->ibase_timefmt); } return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,true); } function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false) { if ($internalKey) return array('RDB$DB_KEY'); $table = strtoupper($table); $sql = 'SELECT S.RDB$FIELD_NAME AFIELDNAME FROM RDB$INDICES I JOIN RDB$INDEX_SEGMENTS S ON I.RDB$INDEX_NAME=S.RDB$INDEX_NAME WHERE I.RDB$RELATION_NAME=\''.$table.'\' and I.RDB$INDEX_NAME like \'RDB$PRIMARY%\' ORDER BY I.RDB$INDEX_NAME,S.RDB$FIELD_POSITION'; $a = $this->GetCol($sql,false,true); if ($a && sizeof($a)>0) return $a; return false; } function ServerInfo() { $arr['dialect'] = $this->dialect; switch($arr['dialect']) { case '': case '1': $s = 'Interbase 5.5 or earlier'; break; case '2': $s = 'Interbase 5.6'; break; default: case '3': $s = 'Interbase 6.0'; break; } $arr['version'] = ADOConnection::_findvers($s); $arr['description'] = $s; return $arr; } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->autoCommit = false; $this->_transactionID = $this->_connectionID;//ibase_trans($this->ibasetrans, $this->_connectionID); return $this->_transactionID; } function CommitTrans($ok=true) { if (!$ok) return $this->RollbackTrans(); if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $ret = false; $this->autoCommit = true; if ($this->_transactionID) { //print ' commit '; $ret = ibase_commit($this->_transactionID); } $this->_transactionID = false; return $ret; } // there are some compat problems with ADODB_COUNTRECS=false and $this->_logsql currently. // it appears that ibase extension cannot support multiple concurrent queryid's function &_Execute($sql,$inputarr=false) { global $ADODB_COUNTRECS; if ($this->_logsql) { $savecrecs = $ADODB_COUNTRECS; $ADODB_COUNTRECS = true; // force countrecs $ret =& ADOConnection::_Execute($sql,$inputarr); $ADODB_COUNTRECS = $savecrecs; } else { $ret =& ADOConnection::_Execute($sql,$inputarr); } return $ret; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $ret = false; $this->autoCommit = true; if ($this->_transactionID) $ret = ibase_rollback($this->_transactionID); $this->_transactionID = false; return $ret; } function &MetaIndexes ($table, $primary = FALSE, $owner=false) { // save old fetch mode global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $table = strtoupper($table); $sql = "SELECT * FROM RDB\$INDICES WHERE RDB\$RELATION_NAME = '".$table."'"; if (!$primary) { $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$%'"; } else { $sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$FOREIGN%'"; } // get index details $rs = $this->Execute($sql); if (!is_object($rs)) { // restore fetchmode if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; return $false; } $indexes = array(); while ($row = $rs->FetchRow()) { $index = $row[0]; if (!isset($indexes[$index])) { if (is_null($row[3])) {$row[3] = 0;} $indexes[$index] = array( 'unique' => ($row[3] == 1), 'columns' => array() ); } $sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '".$index."' ORDER BY RDB\$FIELD_POSITION ASC"; $rs1 = $this->Execute($sql); while ($row1 = $rs1->FetchRow()) { $indexes[$index]['columns'][$row1[2]] = $row1[1]; } } // restore fetchmode if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; return $indexes; } // See http://community.borland.com/article/0,1410,25844,00.html function RowLock($tables,$where,$col) { if ($this->autoCommit) $this->BeginTrans(); $this->Execute("UPDATE $table SET $col=$col WHERE $where "); // is this correct - jlim? return 1; } function CreateSequence($seqname,$startID=1) { $ok = $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); if (!$ok) return false; return $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); } function DropSequence($seqname) { $seqname = strtoupper($seqname); $this->Execute("delete from RDB\$GENERATORS where RDB\$GENERATOR_NAME='$seqname'"); } function GenID($seqname='adodbseq',$startID=1) { $getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE"); $rs = @$this->Execute($getnext); if (!$rs) { $this->Execute(("INSERT INTO RDB\$GENERATORS (RDB\$GENERATOR_NAME) VALUES (UPPER('$seqname'))" )); $this->Execute("SET GENERATOR $seqname TO ".($startID-1).';'); $rs = $this->Execute($getnext); } if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields); else $this->genID = 0; // false if ($rs) $rs->Close(); return $this->genID; } function SelectDB($dbName) { return false; } function _handleerror() { $this->_errorMsg = ibase_errmsg(); } function ErrorNo() { if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1]; else return 0; } function ErrorMsg() { return $this->_errorMsg; } function Prepare($sql) { $stmt = ibase_prepare($this->_connectionID,$sql); if (!$stmt) return false; return array($sql,$stmt); } // returns query ID if successful, otherwise false // there have been reports of problems with nested queries - the code is probably not re-entrant? function _query($sql,$iarr=false) { if (!$this->autoCommit && $this->_transactionID) { $conn = $this->_transactionID; $docommit = false; } else { $conn = $this->_connectionID; $docommit = true; } if (is_array($sql)) { $fn = 'ibase_execute'; $sql = $sql[1]; if (is_array($iarr)) { if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 if ( !isset($iarr[0]) ) $iarr[0] = ''; // PHP5 compat hack $fnarr =& array_merge( array($sql) , $iarr); $ret = call_user_func_array($fn,$fnarr); } else { switch(sizeof($iarr)) { case 1: $ret = $fn($sql,$iarr[0]); break; case 2: $ret = $fn($sql,$iarr[0],$iarr[1]); break; case 3: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2]); break; case 4: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; case 5: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; case 6: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; case 7: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; default: ADOConnection::outp( "Too many parameters to ibase query $sql"); case 8: $ret = $fn($sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; } } } else $ret = $fn($sql); } else { $fn = 'ibase_query'; if (is_array($iarr)) { if (ADODB_PHPVER >= 0x4050) { // actually 4.0.4 if (sizeof($iarr) == 0) $iarr[0] = ''; // PHP5 compat hack $fnarr =& array_merge( array($conn,$sql) , $iarr); $ret = call_user_func_array($fn,$fnarr); } else { switch(sizeof($iarr)) { case 1: $ret = $fn($conn,$sql,$iarr[0]); break; case 2: $ret = $fn($conn,$sql,$iarr[0],$iarr[1]); break; case 3: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2]); break; case 4: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3]); break; case 5: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4]); break; case 6: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5]); break; case 7: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6]); break; default: ADOConnection::outp( "Too many parameters to ibase query $sql"); case 8: $ret = $fn($conn,$sql,$iarr[0],$iarr[1],$iarr[2],$iarr[3],$iarr[4],$iarr[5],$iarr[6],$iarr[7]); break; } } } else $ret = $fn($conn,$sql); } if ($docommit && $ret === true) ibase_commit($this->_connectionID); $this->_handleerror(); return $ret; } // returns true or false function _close() { if (!$this->autoCommit) @ibase_rollback($this->_connectionID); return @ibase_close($this->_connectionID); } //OPN STUFF start function _ConvertFieldType(&$fld, $ftype, $flen, $fscale, $fsubtype, $fprecision, $dialect3) { $fscale = abs($fscale); $fld->max_length = $flen; $fld->scale = null; switch($ftype){ case 7: case 8: if ($dialect3) { switch($fsubtype){ case 0: $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); break; case 1: $fld->type = 'numeric'; $fld->max_length = $fprecision; $fld->scale = $fscale; break; case 2: $fld->type = 'decimal'; $fld->max_length = $fprecision; $fld->scale = $fscale; break; } // switch } else { if ($fscale !=0) { $fld->type = 'decimal'; $fld->scale = $fscale; $fld->max_length = ($ftype == 7 ? 4 : 9); } else { $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); } } break; case 16: if ($dialect3) { switch($fsubtype){ case 0: $fld->type = 'decimal'; $fld->max_length = 18; $fld->scale = 0; break; case 1: $fld->type = 'numeric'; $fld->max_length = $fprecision; $fld->scale = $fscale; break; case 2: $fld->type = 'decimal'; $fld->max_length = $fprecision; $fld->scale = $fscale; break; } // switch } break; case 10: $fld->type = 'float'; break; case 14: $fld->type = 'char'; break; case 27: if ($fscale !=0) { $fld->type = 'decimal'; $fld->max_length = 15; $fld->scale = 5; } else { $fld->type = 'double'; } break; case 35: if ($dialect3) { $fld->type = 'timestamp'; } else { $fld->type = 'date'; } break; case 12: $fld->type = 'date'; break; case 13: $fld->type = 'time'; break; case 37: $fld->type = 'varchar'; break; case 40: $fld->type = 'cstring'; break; case 261: $fld->type = 'blob'; $fld->max_length = -1; break; } // switch } //OPN STUFF end // returns array of ADOFieldObjects for current table function &MetaColumns($table) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); $ADODB_FETCH_MODE = $save; $false = false; if ($rs === false) { return $false; } $retarr = array(); //OPN STUFF start $dialect3 = ($this->dialect==3 ? true : false); //OPN STUFF end while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = trim($rs->fields[0]); //OPN STUFF start $this->_ConvertFieldType($fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3); if (isset($rs->fields[1]) && $rs->fields[1]) { $fld->not_null = true; } if (isset($rs->fields[2])) { $fld->has_default = true; $d = substr($rs->fields[2],strlen('default ')); switch ($fld->type) { case 'smallint': case 'integer': $fld->default_value = (int) $d; break; case 'char': case 'blob': case 'text': case 'varchar': $fld->default_value = (string) substr($d,1,strlen($d)-2); break; case 'double': case 'float': $fld->default_value = (float) $d; break; default: $fld->default_value = $d; break; } // case 35:$tt = 'TIMESTAMP'; break; } if ((isset($rs->fields[5])) && ($fld->type == 'blob')) { $fld->sub_type = $rs->fields[5]; } else { $fld->sub_type = null; } //OPN STUFF end if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); if ( empty($retarr)) return $false; else return $retarr; } function BlobEncode( $blob ) { $blobid = ibase_blob_create( $this->_connectionID); ibase_blob_add( $blobid, $blob ); return ibase_blob_close( $blobid ); } // since we auto-decode all blob's since 2.42, // BlobDecode should not do any transforms function BlobDecode($blob) { return $blob; } // old blobdecode function // still used to auto-decode all blob's function _BlobDecode_old( $blob ) { $blobid = ibase_blob_open($this->_connectionID, $blob ); $realblob = ibase_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet while($string = ibase_blob_get($blobid, 8192)){ $realblob .= $string; } ibase_blob_close( $blobid ); return( $realblob ); } function _BlobDecode( $blob ) { if (ADODB_PHPVER >= 0x5000) { $blob_data = ibase_blob_info($this->_connectionID, $blob ); $blobid = ibase_blob_open($this->_connectionID, $blob ); } else { $blob_data = ibase_blob_info( $blob ); $blobid = ibase_blob_open( $blob ); } if( $blob_data[0] > $this->maxblobsize ) { $realblob = ibase_blob_get($blobid, $this->maxblobsize); while($string = ibase_blob_get($blobid, 8192)){ $realblob .= $string; } } else { $realblob = ibase_blob_get($blobid, $blob_data[0]); } ibase_blob_close( $blobid ); return( $realblob ); } function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') { $fd = fopen($path,'rb'); if ($fd === false) return false; $blob_id = ibase_blob_create($this->_connectionID); /* fill with data */ while ($val = fread($fd,32768)){ ibase_blob_add($blob_id, $val); } /* close and get $blob_id_str for inserting into table */ $blob_id_str = ibase_blob_close($blob_id); fclose($fd); return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; } /* Insert a null into the blob field of the table first. Then use UpdateBlob to store the blob. Usage: $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { $blob_id = ibase_blob_create($this->_connectionID); // ibase_blob_add($blob_id, $val); // replacement that solves the problem by which only the first modulus 64K / // of $val are stored at the blob field //////////////////////////////////// // Thx Abel Berenstein aberenstein#afip.gov.ar $len = strlen($val); $chunk_size = 32768; $tail_size = $len % $chunk_size; $n_chunks = ($len - $tail_size) / $chunk_size; for ($n = 0; $n < $n_chunks; $n++) { $start = $n * $chunk_size; $data = substr($val, $start, $chunk_size); ibase_blob_add($blob_id, $data); } if ($tail_size) { $start = $n_chunks * $chunk_size; $data = substr($val, $start, $tail_size); ibase_blob_add($blob_id, $data); } // end replacement ///////////////////////////////////////////////////////// $blob_id_str = ibase_blob_close($blob_id); return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; } function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { $blob_id = ibase_blob_create($this->_connectionID); ibase_blob_add($blob_id, $val); $blob_id_str = ibase_blob_close($blob_id); return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; } // Format date column in sql string given an input format that understands Y M D // Only since Interbase 6.0 - uses EXTRACT // problem - does not zero-fill the day and month yet function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysDate; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '||'; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= "extract(year from $col)"; break; case 'M': case 'm': $s .= "extract(month from $col)"; break; case 'Q': case 'q': $s .= "cast(((extract(month from $col)+2) / 3) as integer)"; break; case 'D': case 'd': $s .= "(extract(day from $col))"; break; case 'H': case 'h': $s .= "(extract(hour from $col))"; break; case 'I': case 'i': $s .= "(extract(minute from $col))"; break; case 'S': case 's': $s .= "CAST((extract(second from $col)) AS INTEGER)"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); break; } } return $s; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_ibase extends ADORecordSet { var $databaseType = "ibase"; var $bind=false; var $_cacheType; function ADORecordset_ibase($id,$mode=false) { global $ADODB_FETCH_MODE; $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; $this->ADORecordSet($id); } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $ibf = ibase_field_info($this->_queryID,$fieldOffset); switch (ADODB_ASSOC_CASE) { case 2: // the default $fld->name = ($ibf['alias']); if (empty($fld->name)) $fld->name = ($ibf['name']); break; case 0: $fld->name = strtoupper($ibf['alias']); if (empty($fld->name)) $fld->name = strtoupper($ibf['name']); break; case 1: $fld->name = strtolower($ibf['alias']); if (empty($fld->name)) $fld->name = strtolower($ibf['name']); break; } $fld->type = $ibf['type']; $fld->max_length = $ibf['length']; /* This needs to be populated from the metadata */ $fld->not_null = false; $fld->has_default = false; $fld->default_value = 'null'; return $fld; } function _initrs() { $this->_numOfRows = -1; $this->_numOfFields = @ibase_num_fields($this->_queryID); // cache types for blob decode check for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { $f1 = $this->FetchField($i); $this->_cacheType[] = $f1->type; } } function _seek($row) { return false; } function _fetch() { $f = @ibase_fetch_row($this->_queryID); if ($f === false) { $this->fields = false; return false; } // OPN stuff start - optimized // fix missing nulls and decode blobs automatically global $ADODB_ANSI_PADDING_OFF; //$ADODB_ANSI_PADDING_OFF=1; $rtrim = !empty($ADODB_ANSI_PADDING_OFF); for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { if ($this->_cacheType[$i]=="BLOB") { if (isset($f[$i])) { $f[$i] = $this->connection->_BlobDecode($f[$i]); } else { $f[$i] = null; } } else { if (!isset($f[$i])) { $f[$i] = null; } else if ($rtrim && is_string($f[$i])) { $f[$i] = rtrim($f[$i]); } } } // OPN stuff end $this->fields = $f; if ($this->fetchMode == ADODB_FETCH_ASSOC) { $this->fields = &$this->GetRowAssoc(ADODB_ASSOC_CASE); } else if ($this->fetchMode == ADODB_FETCH_BOTH) { $this->fields =& array_merge($this->fields,$this->GetRowAssoc(ADODB_ASSOC_CASE)); } return true; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _close() { return @ibase_free_result($this->_queryID); } function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'CHAR': return 'C'; case 'TEXT': case 'VARCHAR': case 'VARYING': if ($len <= $this->blobSize) return 'C'; return 'X'; case 'BLOB': return 'B'; case 'TIMESTAMP': case 'DATE': return 'D'; case 'TIME': return 'T'; //case 'T': return 'T'; //case 'L': return 'L'; case 'INT': case 'SHORT': case 'INTEGER': return 'I'; default: return 'N'; } } } ?>phpgacl-3.3.7/adodb/drivers/adodb-pdo_pgsql.inc.php0100644025754300001440000002043510476657245021144 0ustar ipsousers 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // used when schema defined var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and c.relnamespace=n.oid and n.nspname='%s' and a.attname not like '....%%' AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // get primary key etc -- from Freek Dijkstra var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; var $hasAffectedRows = true; var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 // below suggested by Freek Dijkstra var $true = 't'; // string that represents TRUE for a database var $false = 'f'; // string that represents FALSE for a database var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt. var $hasMoveFirst = true; var $hasGenID = true; var $_genIDSQL = "SELECT NEXTVAL('%s')"; var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; var $_dropSeqSQL = "DROP SEQUENCE %s"; var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; var $random = 'random()'; /// random function var $concat_operator='||'; function _init($parentDriver) { $parentDriver->hasTransactions = false; ## <<< BUG IN PDO pgsql driver $parentDriver->hasInsertID = true; $parentDriver->_nestedSQL = true; } function ServerInfo() { $arr['description'] = ADOConnection::GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $offsetStr = ($offset >= 0) ? " OFFSET $offset" : ''; $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ''; if ($secs2cache) $rs =& $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr); else $rs =& $this->Execute($sql."$limitStr$offsetStr",$inputarr); return $rs; } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { $info = $this->ServerInfo(); if ($info['version'] >= 7.3) { $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') union select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; } if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(strtolower($mask)); if ($info['version']>=7.3) $this->metaTablesSQL = " select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') union select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; else $this->metaTablesSQL = " select tablename,'T' from pg_tables where tablename like $mask union select viewname,'V' from pg_views where viewname like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } function &MetaColumns($table,$normalize=true) { global $ADODB_FETCH_MODE; $schema = false; $this->_findschema($table,$schema); if ($normalize) $table = strtolower($table); $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false) { $false = false; return $false; } if (!empty($this->metaKeySQL)) { // If we want the primary keys, we have to issue a separate query // Of course, a modified version of the metaColumnsSQL query using a // LEFT JOIN would have been much more elegant, but postgres does // not support OUTER JOINS. So here is the clumsy way. $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); // fetch all result in once for performance. $keys =& $rskey->GetArray(); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; $rskey->Close(); unset($rskey); } $rsdefa = array(); if (!empty($this->metaDefaultsSQL)) { $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $sql = sprintf($this->metaDefaultsSQL, ($table)); $rsdef = $this->Execute($sql); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rsdef) { while (!$rsdef->EOF) { $num = $rsdef->fields['num']; $s = $rsdef->fields['def']; if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ $s = substr($s, 1); $s = substr($s, 0, strlen($s) - 1); } $rsdefa[$num] = $s; $rsdef->MoveNext(); } } else { ADOConnection::outp( "==> SQL => " . $sql); } unset($rsdef); } $retarr = array(); while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; if ($fld->max_length <= 0) $fld->max_length = -1; if ($fld->type == 'numeric') { $fld->scale = $fld->max_length & 0xFFFF; $fld->max_length >>= 16; } // dannym // 5 hasdefault; 6 num-of-column $fld->has_default = ($rs->fields[5] == 't'); if ($fld->has_default) { $fld->default_value = $rsdefa[$rs->fields[6]]; } //Freek if ($rs->fields[4] == $this->true) { $fld->not_null = true; } // Freek if (is_array($keys)) { foreach($keys as $key) { if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true) $fld->primary_key = true; if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true) $fld->unique = true; // What name is more compatible? } } if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) { $false = false; return $false; } else return $retarr; } } ?> phpgacl-3.3.7/adodb/drivers/adodb-firebird.inc.php0100644025754300001440000000362210476657245020741 0ustar ipsousersADODB_ibase(); } function ServerInfo() { $arr['dialect'] = $this->dialect; switch($arr['dialect']) { case '': case '1': $s = 'Firebird Dialect 1'; break; case '2': $s = 'Firebird Dialect 2'; break; default: case '3': $s = 'Firebird Dialect 3'; break; } $arr['version'] = ADOConnection::_findvers($s); $arr['description'] = $s; return $arr; } // Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars! // SELECT col1, col2 FROM table ROWS 5 -- get 5 rows // SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2 function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs=0) { $nrows = (integer) $nrows; $offset = (integer) $offset; $str = 'SELECT '; if ($nrows >= 0) $str .= "FIRST $nrows "; $str .=($offset>=0) ? "SKIP $offset " : ''; $sql = preg_replace('/^[ \t]*select/i',$str,$sql); if ($secs) $rs =& $this->CacheExecute($secs,$sql,$inputarr); else $rs =& $this->Execute($sql,$inputarr); return $rs; } }; class ADORecordSet_firebird extends ADORecordSet_ibase { var $databaseType = "firebird"; function ADORecordSet_firebird($id,$mode=false) { $this->ADORecordSet_ibase($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-sybase.inc.php0100644025754300001440000003035610476657245020445 0ustar ipsousersGetOne('select @@identity'); } // might require begintrans -- committrans function _affectedrows() { return $this->GetOne('select @@rowcount'); } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->Execute('BEGIN TRAN'); return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); $this->transCnt -= 1; $this->Execute('COMMIT TRAN'); return true; } function RollbackTrans() { if ($this->transOff) return true; $this->transCnt -= 1; $this->Execute('ROLLBACK TRAN'); return true; } // http://www.isug.com/Sybase_FAQ/ASE/section6.1.html#6.1.4 function RowLock($tables,$where,$flds='top 1 null as ignore') { if (!$this->_hastrans) $this->BeginTrans(); $tables = str_replace(',',' HOLDLOCK,',$tables); return $this->GetOne("select $flds from $tables HOLDLOCK where $where"); } function SelectDB($dbName) { $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions if ($this->_connectionID) { return @sybase_select_db($dbName); } else return false; } /* Returns: the last error message from previous database operation Note: This function is NOT available for Microsoft SQL Server. */ function ErrorMsg() { if ($this->_logsql) return $this->_errorMsg; if (function_exists('sybase_get_last_message')) $this->_errorMsg = sybase_get_last_message(); else $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : 'SYBASE error messages not supported on this platform'; return $this->_errorMsg; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('sybase_connect')) return null; $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('sybase_connect')) return null; $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns query ID if successful, otherwise false function _query($sql,$inputarr) { global $ADODB_COUNTRECS; if ($ADODB_COUNTRECS == false && ADODB_PHPVER >= 0x4300) return sybase_unbuffered_query($sql,$this->_connectionID); else return sybase_query($sql,$this->_connectionID); } // See http://www.isug.com/Sybase_FAQ/ASE/section6.2.html#6.2.12 function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { if ($secs2cache > 0) {// we do not cache rowcount, so we have to load entire recordset $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $rs; } $nrows = (integer) $nrows; $offset = (integer) $offset; $cnt = ($nrows >= 0) ? $nrows : 999999999; if ($offset > 0 && $cnt) $cnt += $offset; $this->Execute("set rowcount $cnt"); $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,0); $this->Execute("set rowcount 0"); return $rs; } // returns true or false function _close() { return @sybase_close($this->_connectionID); } function UnixDate($v) { return ADORecordSet_array_sybase::UnixDate($v); } function UnixTimeStamp($v) { return ADORecordSet_array_sybase::UnixTimeStamp($v); } # Added 2003-10-05 by Chris Phillipson # Used ASA SQL Reference Manual -- http://sybooks.sybase.com/onlinebooks/group-aw/awg0800e/dbrfen8/@ebt-link;pt=16756?target=%25N%15_12018_START_RESTART_N%25 # to convert similar Microsoft SQL*Server (mssql) API into Sybase compatible version // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '+'; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= "datename(yy,$col)"; break; case 'M': $s .= "convert(char(3),$col,0)"; break; case 'm': $s .= "replace(str(month($col),2),' ','0')"; break; case 'Q': case 'q': $s .= "datename(qq,$col)"; break; case 'D': case 'd': $s .= "replace(str(datepart(dd,$col),2),' ','0')"; break; case 'h': $s .= "substring(convert(char(14),$col,0),13,2)"; break; case 'H': $s .= "replace(str(datepart(hh,$col),2),' ','0')"; break; case 'i': $s .= "replace(str(datepart(mi,$col),2),' ','0')"; break; case 's': $s .= "replace(str(datepart(ss,$col),2),' ','0')"; break; case 'a': case 'A': $s .= "substring(convert(char(19),$col,0),18,2)"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); break; } } return $s; } # Added 2003-10-07 by Chris Phillipson # Used ASA SQL Reference Manual -- http://sybooks.sybase.com/onlinebooks/group-aw/awg0800e/dbrfen8/@ebt-link;pt=5981;uf=0?target=0;window=new;showtoc=true;book=dbrfen8 # to convert similar Microsoft SQL*Server (mssql) API into Sybase compatible version function MetaPrimaryKeys($table) { $sql = "SELECT c.column_name " . "FROM syscolumn c, systable t " . "WHERE t.table_name='$table' AND c.table_id=t.table_id " . "AND t.table_type='BASE' " . "AND c.pkey = 'Y' " . "ORDER BY c.column_id"; $a = $this->GetCol($sql); if ($a && sizeof($a)>0) return $a; return false; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ global $ADODB_sybase_mths; $ADODB_sybase_mths = array( 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6, 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); class ADORecordset_sybase extends ADORecordSet { var $databaseType = "sybase"; var $canSeek = true; // _mths works only in non-localised system var $_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); function ADORecordset_sybase($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; else $this->fetchMode = $mode; $this->ADORecordSet($id,$mode); } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &FetchField($fieldOffset = -1) { if ($fieldOffset != -1) { $o = @sybase_fetch_field($this->_queryID, $fieldOffset); } else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ $o = @sybase_fetch_field($this->_queryID); } // older versions of PHP did not support type, only numeric if ($o && !isset($o->type)) $o->type = ($o->numeric) ? 'float' : 'varchar'; return $o; } function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS)? @sybase_num_rows($this->_queryID):-1; $this->_numOfFields = @sybase_num_fields($this->_queryID); } function _seek($row) { return @sybase_data_seek($this->_queryID, $row); } function _fetch($ignore_fields=false) { if ($this->fetchMode == ADODB_FETCH_NUM) { $this->fields = @sybase_fetch_row($this->_queryID); } else if ($this->fetchMode == ADODB_FETCH_ASSOC) { $this->fields = @sybase_fetch_row($this->_queryID); if (is_array($this->fields)) { $this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE); return true; } return false; } else { $this->fields = @sybase_fetch_array($this->_queryID); } if ( is_array($this->fields)) { return true; } return false; } /* close() only needs to be called if you are worried about using too much memory while your script is running. All associated result memory for the specified result identifier will automatically be freed. */ function _close() { return @sybase_free_result($this->_queryID); } // sybase/mssql uses a default date like Dec 30 2000 12:00AM function UnixDate($v) { return ADORecordSet_array_sybase::UnixDate($v); } function UnixTimeStamp($v) { return ADORecordSet_array_sybase::UnixTimeStamp($v); } } class ADORecordSet_array_sybase extends ADORecordSet_array { function ADORecordSet_array_sybase($id=-1) { $this->ADORecordSet_array($id); } // sybase/mssql uses a default date like Dec 30 2000 12:00AM function UnixDate($v) { global $ADODB_sybase_mths; //Dec 30 2000 12:00AM if (!ereg( "([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})" ,$v, $rr)) return parent::UnixDate($v); if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $themth = substr(strtoupper($rr[1]),0,3); $themth = $ADODB_sybase_mths[$themth]; if ($themth <= 0) return false; // h-m-s-MM-DD-YY return mktime(0,0,0,$themth,$rr[2],$rr[3]); } function UnixTimeStamp($v) { global $ADODB_sybase_mths; //11.02.2001 Toni Tunkkari toni.tunkkari@finebyte.com //Changed [0-9] to [0-9 ] in day conversion if (!ereg( "([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})" ,$v, $rr)) return parent::UnixTimeStamp($v); if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $themth = substr(strtoupper($rr[1]),0,3); $themth = $ADODB_sybase_mths[$themth]; if ($themth <= 0) return false; switch (strtoupper($rr[6])) { case 'P': if ($rr[4]<12) $rr[4] += 12; break; case 'A': if ($rr[4]==12) $rr[4] = 0; break; default: break; } // h-m-s-MM-DD-YY return mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); } } ?> phpgacl-3.3.7/adodb/drivers/adodb-postgres7.inc.php0100644025754300001440000001610010476657245021103 0ustar ipsousersADODB_postgres64(); if (ADODB_ASSOC_CASE !== 2) { $this->rsPrefix .= 'assoc_'; } $this->_bindInputArray = PHP_VERSION >= 5.1; $info = $this->ServerInfo(); $this->pgVersion = (float) substr($info['version'],0,3); if ($this->pgVersion >= 7.1) { // good till version 999 $this->_nestedSQL = true; } } // the following should be compat with postgresql 7.2, // which makes obsolete the LIMIT limit,offset syntax function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $offsetStr = ($offset >= 0) ? " OFFSET ".((integer)$offset) : ''; $limitStr = ($nrows >= 0) ? " LIMIT ".((integer)$nrows) : ''; if ($secs2cache) $rs =& $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr); else $rs =& $this->Execute($sql."$limitStr$offsetStr",$inputarr); return $rs; } /* function Prepare($sql) { $info = $this->ServerInfo(); if ($info['version']>=7.3) { return array($sql,false); } return $sql; } */ // from Edward Jaramilla, improved version - works on pg 7.4 function MetaForeignKeys($table, $owner=false, $upper=false) { $sql = 'SELECT t.tgargs as args FROM pg_trigger t,pg_class c,pg_proc p WHERE t.tgenabled AND t.tgrelid = c.oid AND t.tgfoid = p.oid AND p.proname = \'RI_FKey_check_ins\' AND c.relname = \''.strtolower($table).'\' ORDER BY t.tgrelid'; $rs =& $this->Execute($sql); if (!$rs || $rs->EOF) return false; $arr =& $rs->GetArray(); $a = array(); foreach($arr as $v) { $data = explode(chr(0), $v['args']); $size = count($data)-1; //-1 because the last node is empty for($i = 4; $i < $size; $i++) { if ($upper) $a[strtoupper($data[2])][] = strtoupper($data[$i].'='.$data[++$i]); else $a[$data[2]][] = $data[$i].'='.$data[++$i]; } } return $a; } function _query($sql,$inputarr) { if (! $this->_bindInputArray) { // We don't have native support for parameterized queries, so let's emulate it at the parent return ADODB_postgres64::_query($sql, $inputarr); } $this->_errorMsg = false; // -- added Cristiano da Cunha Duarte if ($inputarr) { $sqlarr = explode('?',trim($sql)); $sql = ''; $i = 1; $last = sizeof($sqlarr)-1; foreach($sqlarr as $v) { if ($last < $i) $sql .= $v; else $sql .= $v.' $'.$i; $i++; } $rez = pg_query_params($this->_connectionID,$sql, $inputarr); } else { $rez = pg_query($this->_connectionID,$sql); } // check if no data returned, then no need to create real recordset if ($rez && pg_numfields($rez) <= 0) { if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') { pg_freeresult($this->_resultid); } $this->_resultid = $rez; return true; } return $rez; } // this is a set of functions for managing client encoding - very important if the encodings // of your database and your output target (i.e. HTML) don't match //for instance, you may have UNICODE database and server it on-site as WIN1251 etc. // GetCharSet - get the name of the character set the client is using now // the functions should work with Postgres 7.0 and above, the set of charsets supported // depends on compile flags of postgres distribution - if no charsets were compiled into the server // it will return 'SQL_ANSI' always function GetCharSet() { //we will use ADO's builtin property charSet $this->charSet = @pg_client_encoding($this->_connectionID); if (!$this->charSet) { return false; } else { return $this->charSet; } } // SetCharSet - switch the client encoding function SetCharSet($charset_name) { $this->GetCharSet(); if ($this->charSet !== $charset_name) { $if = pg_set_client_encoding($this->_connectionID, $charset_name); if ($if == "0" & $this->GetCharSet() == $charset_name) { return true; } else return false; } else return true; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_postgres7 extends ADORecordSet_postgres64{ var $databaseType = "postgres7"; function ADORecordSet_postgres7($queryID,$mode=false) { $this->ADORecordSet_postgres64($queryID,$mode); } // 10% speedup to move MoveNext to child class function MoveNext() { if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); if (is_array($this->fields)) { if ($this->fields && isset($this->_blobArr)) $this->_fixblobs(); return true; } } $this->fields = false; $this->EOF = true; } return false; } } class ADORecordSet_assoc_postgres7 extends ADORecordSet_postgres64{ var $databaseType = "postgres7"; function ADORecordSet_assoc_postgres7($queryID,$mode=false) { $this->ADORecordSet_postgres64($queryID,$mode); } function _fetch() { if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) return false; $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); if ($this->fields) { if (isset($this->_blobArr)) $this->_fixblobs(); $this->_updatefields(); } return (is_array($this->fields)); } // Create associative array function _updatefields() { if (ADODB_ASSOC_CASE == 2) return; // native $arr = array(); $lowercase = (ADODB_ASSOC_CASE == 0); foreach($this->fields as $k => $v) { if (is_integer($k)) $arr[$k] = $v; else { if ($lowercase) $arr[strtolower($k)] = $v; else $arr[strtoupper($k)] = $v; } } $this->fields = $arr; } function MoveNext() { if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); if (is_array($this->fields)) { if ($this->fields) { if (isset($this->_blobArr)) $this->_fixblobs(); $this->_updatefields(); } return true; } } $this->fields = false; $this->EOF = true; } return false; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-sapdb.inc.php0100644025754300001440000001174710476657245020253 0ustar ipsouserscurmode = SQL_CUR_USE_ODBC; $this->ADODB_odbc(); } function ServerInfo() { $info = ADODB_odbc::ServerInfo(); if (!$info['version'] && preg_match('/([0-9.]+)/',$info['description'],$matches)) { $info['version'] = $matches[1]; } return $info; } function MetaPrimaryKeys($table) { $table = $this->Quote(strtoupper($table)); return $this->GetCol("SELECT columnname FROM COLUMNS WHERE tablename=$table AND mode='KEY' ORDER BY pos"); } function &MetaIndexes ($table, $primary = FALSE) { $table = $this->Quote(strtoupper($table)); $sql = "SELECT INDEXNAME,TYPE,COLUMNNAME FROM INDEXCOLUMNS ". " WHERE TABLENAME=$table". " ORDER BY INDEXNAME,COLUMNNO"; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $rs = $this->Execute($sql); if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { return FALSE; } $indexes = array(); while ($row = $rs->FetchRow()) { $indexes[$row[0]]['unique'] = $row[1] == 'UNIQUE'; $indexes[$row[0]]['columns'][] = $row[2]; } if ($primary) { $indexes['SYSPRIMARYKEYINDEX'] = array( 'unique' => True, // by definition 'columns' => $this->GetCol("SELECT columnname FROM COLUMNS WHERE tablename=$table AND mode='KEY' ORDER BY pos"), ); } return $indexes; } function &MetaColumns ($table) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $table = $this->Quote(strtoupper($table)); $retarr = array(); foreach($this->GetAll("SELECT COLUMNNAME,DATATYPE,LEN,DEC,NULLABLE,MODE,\"DEFAULT\",CASE WHEN \"DEFAULT\" IS NULL THEN 0 ELSE 1 END AS HAS_DEFAULT FROM COLUMNS WHERE tablename=$table ORDER BY pos") as $column) { $fld = new ADOFieldObject(); $fld->name = $column[0]; $fld->type = $column[1]; $fld->max_length = $fld->type == 'LONG' ? 2147483647 : $column[2]; $fld->scale = $column[3]; $fld->not_null = $column[4] == 'NO'; $fld->primary_key = $column[5] == 'KEY'; if ($fld->has_default = $column[7]) { if ($fld->primary_key && $column[6] == 'DEFAULT SERIAL (1)') { $fld->auto_increment = true; $fld->has_default = false; } else { $fld->default_value = $column[6]; switch($fld->type) { case 'VARCHAR': case 'CHARACTER': case 'LONG': $fld->default_value = $column[6]; break; default: $fld->default_value = trim($column[6]); break; } } } $retarr[$fld->name] = $fld; } if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; return $retarr; } function MetaColumnNames($table) { $table = $this->Quote(strtoupper($table)); return $this->GetCol("SELECT columnname FROM COLUMNS WHERE tablename=$table ORDER BY pos"); } // unlike it seems, this depends on the db-session and works in a multiuser environment function _insertid($table,$column) { return empty($table) ? False : $this->GetOne("SELECT $table.CURRVAL FROM DUAL"); } /* SelectLimit implementation problems: The following will return random 10 rows as order by performed after "WHERE rowno<10" which is not ideal... select * from table where rowno < 10 order by 1 This means that we have to use the adoconnection base class SelectLimit when there is an "order by". See http://listserv.sap.com/pipermail/sapdb.general/2002-January/010405.html */ }; class ADORecordSet_sapdb extends ADORecordSet_odbc { var $databaseType = "sapdb"; function ADORecordSet_sapdb($id,$mode=false) { $this->ADORecordSet_odbc($id,$mode); } } } //define ?>phpgacl-3.3.7/adodb/drivers/adodb-csv.inc.php0100644025754300001440000001137610476657245017753 0ustar ipsousers_insertid; } function _affectedrows() { return $this->_affectedrows; } function &MetaDatabases() { return false; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (strtolower(substr($argHostname,0,7)) !== 'http://') return false; $this->_url = $argHostname; return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (strtolower(substr($argHostname,0,7)) !== 'http://') return false; $this->_url = $argHostname; return true; } function &MetaColumns($table) { return false; } // parameters use PostgreSQL convention, not MySQL function &SelectLimit($sql,$nrows=-1,$offset=-1) { global $ADODB_FETCH_MODE; $url = $this->_url.'?sql='.urlencode($sql)."&nrows=$nrows&fetch=". (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE). "&offset=$offset"; $err = false; $rs = csv2rs($url,$err,false); if ($this->debug) print "$url
$err
"; $at = strpos($err,'::::'); if ($at === false) { $this->_errorMsg = $err; $this->_errorNo = (integer)$err; } else { $this->_errorMsg = substr($err,$at+4,1024); $this->_errorNo = -9999; } if ($this->_errorNo) if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,''); } if (is_object($rs)) { $rs->databaseType='csv'; $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; $rs->connection = &$this; } return $rs; } // returns queryID or false function &_Execute($sql,$inputarr=false) { global $ADODB_FETCH_MODE; if (!$this->_bindInputArray && $inputarr) { $sqlarr = explode('?',$sql); $sql = ''; $i = 0; foreach($inputarr as $v) { $sql .= $sqlarr[$i]; if (gettype($v) == 'string') $sql .= $this->qstr($v); else if ($v === null) $sql .= 'NULL'; else $sql .= $v; $i += 1; } $sql .= $sqlarr[$i]; if ($i+1 != sizeof($sqlarr)) print "Input Array does not match ?: ".htmlspecialchars($sql); $inputarr = false; } $url = $this->_url.'?sql='.urlencode($sql)."&fetch=". (($this->fetchMode !== false)?$this->fetchMode : $ADODB_FETCH_MODE); $err = false; $rs = csv2rs($url,$err,false); if ($this->debug) print urldecode($url)."
$err
"; $at = strpos($err,'::::'); if ($at === false) { $this->_errorMsg = $err; $this->_errorNo = (integer)$err; } else { $this->_errorMsg = substr($err,$at+4,1024); $this->_errorNo = -9999; } if ($this->_errorNo) if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr); } if (is_object($rs)) { $rs->fetchMode = ($this->fetchMode !== false) ? $this->fetchMode : $ADODB_FETCH_MODE; $this->_affectedrows = $rs->affectedrows; $this->_insertid = $rs->insertid; $rs->databaseType='csv'; $rs->connection = &$this; } return $rs; } /* Returns: the last error message from previous database operation */ function ErrorMsg() { return $this->_errorMsg; } /* Returns: the last error number from previous database operation */ function ErrorNo() { return $this->_errorNo; } // returns true or false function _close() { return true; } } // class class ADORecordset_csv extends ADORecordset { function ADORecordset_csv($id,$mode=false) { $this->ADORecordset($id,$mode); } function _close() { return true; } } } // define ?>phpgacl-3.3.7/adodb/drivers/adodb-pdo_mssql.inc.php0100644025754300001440000000255310476657431021153 0ustar ipsousershasTransactions = false; ## <<< BUG IN PDO mssql driver $parentDriver->_bindInputArray = false; $parentDriver->hasInsertID = true; } function ServerInfo() { return ADOConnection::ServerInfo(); } function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $ret = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $ret; } function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET TRANSACTION ".$transaction_mode); } function MetaTables() { return false; } function MetaColumns() { return false; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-postgres8.inc.php0100644025754300001440000000054110476657245021106 0ustar ipsousersphpgacl-3.3.7/adodb/drivers/adodb-mssql.inc.php0100644025754300001440000007340610476657245020321 0ustar ipsousers= 0x4300) { // docs say 4.2.0, but testing shows only since 4.3.0 does it work! ini_set('mssql.datetimeconvert',0); } else { global $ADODB_mssql_mths; // array, months must be upper-case $ADODB_mssql_date_order = 'mdy'; $ADODB_mssql_mths = array( 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6, 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); } //--------------------------------------------------------------------------- // Call this to autoset $ADODB_mssql_date_order at the beginning of your code, // just after you connect to the database. Supports mdy and dmy only. // Not required for PHP 4.2.0 and above. function AutoDetect_MSSQL_Date_Order($conn) { global $ADODB_mssql_date_order; $adate = $conn->GetOne('select getdate()'); if ($adate) { $anum = (int) $adate; if ($anum > 0) { if ($anum > 31) { //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently"); } else $ADODB_mssql_date_order = 'dmy'; } else $ADODB_mssql_date_order = 'mdy'; } } class ADODB_mssql extends ADOConnection { var $databaseType = "mssql"; var $dataProvider = "mssql"; var $replaceQuote = "''"; // string to use to replace quotes var $fmtDate = "'Y-m-d'"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $hasInsertID = true; var $substr = "substring"; var $length = 'len'; var $hasAffectedRows = true; var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'"; var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))"; var $metaColumnsSQL = # xtype==61 is datetime "select c.name,t.name,c.length, (case when c.xusertype=61 then 0 else c.xprec end), (case when c.xusertype=61 then 0 else c.xscale end) from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'"; var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE var $hasGenID = true; var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; var $sysTimeStamp = 'GetDate()'; var $_has_mssql_init; var $maxParameterLen = 4000; var $arrayClass = 'ADORecordSet_array_mssql'; var $uniqueSort = true; var $leftOuter = '*='; var $rightOuter = '=*'; var $ansiOuter = true; // for mssql7 or later var $poorAffectedRows = true; var $identitySQL = 'select @@IDENTITY'; // 'select SCOPE_IDENTITY'; # for mssql 2000 var $uniqueOrderBy = true; var $_bindInputArray = true; function ADODB_mssql() { $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0); } function ServerInfo() { global $ADODB_FETCH_MODE; $stmt = $this->PrepareSP('sp_server_info'); $val = 2; if ($this->fetchMode === false) { $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; } else $savem = $this->SetFetchMode(ADODB_FETCH_NUM); $this->Parameter($stmt,$val,'attribute_id'); $row = $this->GetRow($stmt); //$row = $this->GetRow("execute sp_server_info 2"); if ($this->fetchMode === false) { $ADODB_FETCH_MODE = $savem; } else $this->SetFetchMode($savem); $arr['description'] = $row[2]; $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function IfNull( $field, $ifNull ) { return " ISNULL($field, $ifNull) "; // if MS SQL Server } function _insertid() { // SCOPE_IDENTITY() // Returns the last IDENTITY value inserted into an IDENTITY column in // the same scope. A scope is a module -- a stored procedure, trigger, // function, or batch. Thus, two statements are in the same scope if // they are in the same stored procedure, function, or batch. return $this->GetOne($this->identitySQL); } function _affectedrows() { return $this->GetOne('select @@rowcount'); } var $_dropSeqSQL = "drop table %s"; function CreateSequence($seq='adodbseq',$start=1) { $this->Execute('BEGIN TRANSACTION adodbseq'); $start -= 1; $this->Execute("create table $seq (id float(53))"); $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); if (!$ok) { $this->Execute('ROLLBACK TRANSACTION adodbseq'); return false; } $this->Execute('COMMIT TRANSACTION adodbseq'); return true; } function GenID($seq='adodbseq',$start=1) { //$this->debug=1; $this->Execute('BEGIN TRANSACTION adodbseq'); $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1"); if (!$ok) { $this->Execute("create table $seq (id float(53))"); $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)"); if (!$ok) { $this->Execute('ROLLBACK TRANSACTION adodbseq'); return false; } $this->Execute('COMMIT TRANSACTION adodbseq'); return $start; } $num = $this->GetOne("select id from $seq"); $this->Execute('COMMIT TRANSACTION adodbseq'); return $num; // in old implementation, pre 1.90, we returned GUID... //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'"); } function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { if ($nrows > 0 && $offset <= 0) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql); $rs =& $this->Execute($sql,$inputarr); } else $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $rs; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '+'; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= "datename(yyyy,$col)"; break; case 'M': $s .= "convert(char(3),$col,0)"; break; case 'm': $s .= "replace(str(month($col),2),' ','0')"; break; case 'Q': case 'q': $s .= "datename(quarter,$col)"; break; case 'D': case 'd': $s .= "replace(str(day($col),2),' ','0')"; break; case 'h': $s .= "substring(convert(char(14),$col,0),13,2)"; break; case 'H': $s .= "replace(str(datepart(hh,$col),2),' ','0')"; break; case 'i': $s .= "replace(str(datepart(mi,$col),2),' ','0')"; break; case 's': $s .= "replace(str(datepart(ss,$col),2),' ','0')"; break; case 'a': case 'A': $s .= "substring(convert(char(19),$col,0),18,2)"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); break; } } return $s; } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->Execute('BEGIN TRAN'); return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->Execute('COMMIT TRAN'); return true; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->Execute('ROLLBACK TRAN'); return true; } function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET TRANSACTION ".$transaction_mode); } /* Usage: $this->BeginTrans(); $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables # some operation on both tables table1 and table2 $this->CommitTrans(); See http://www.swynk.com/friends/achigrik/SQL70Locks.asp */ function RowLock($tables,$where,$flds='top 1 null as ignore') { if (!$this->transCnt) $this->BeginTrans(); return $this->GetOne("select $flds from $tables with (ROWLOCK,HOLDLOCK) where $where"); } function &MetaIndexes($table,$primary=false) { $table = $this->qstr($table); $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno, CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK, CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table ORDER BY O.name, I.Name, K.keyno"; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $rs = $this->Execute($sql); if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { return FALSE; } $indexes = array(); while ($row = $rs->FetchRow()) { if (!$primary && $row[5]) continue; $indexes[$row[0]]['unique'] = $row[6]; $indexes[$row[0]]['columns'][] = $row[1]; } return $indexes; } function MetaForeignKeys($table, $owner=false, $upper=false) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $table = $this->qstr(strtoupper($table)); $sql = "select object_name(constid) as constraint_name, col_name(fkeyid, fkey) as column_name, object_name(rkeyid) as referenced_table_name, col_name(rkeyid, rkey) as referenced_column_name from sysforeignkeys where upper(object_name(fkeyid)) = $table order by constraint_name, referenced_table_name, keyno"; $constraints =& $this->GetArray($sql); $ADODB_FETCH_MODE = $save; $arr = false; foreach($constraints as $constr) { //print_r($constr); $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3]; } if (!$arr) return false; $arr2 = false; foreach($arr as $k => $v) { foreach($v as $a => $b) { if ($upper) $a = strtoupper($a); $arr2[$a] = $b; } } return $arr2; } //From: Fernando Moreira function MetaDatabases() { if(@mssql_select_db("master")) { $qry=$this->metaDatabasesSQL; if($rs=@mssql_query($qry)){ $tmpAr=$ar=array(); while($tmpAr=@mssql_fetch_row($rs)) $ar[]=$tmpAr[0]; @mssql_select_db($this->database); if(sizeof($ar)) return($ar); else return(false); } else { @mssql_select_db($this->database); return(false); } } return(false); } // "Stein-Aksel Basma" // tested with MSSQL 2000 function &MetaPrimaryKeys($table) { global $ADODB_FETCH_MODE; $schema = ''; $this->_findschema($table,$schema); if (!$schema) $schema = $this->database; if ($schema) $schema = "and k.table_catalog like '$schema%'"; $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k, information_schema.table_constraints tc where tc.constraint_name = k.constraint_name and tc.constraint_type = 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position "; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $a = $this->GetCol($sql); $ADODB_FETCH_MODE = $savem; if ($a && sizeof($a)>0) return $a; $false = false; return $false; } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(($mask)); $this->metaTablesSQL .= " AND name like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } function SelectDB($dbName) { $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions if ($this->_connectionID) { return @mssql_select_db($dbName); } else return false; } function ErrorMsg() { if (empty($this->_errorMsg)){ $this->_errorMsg = mssql_get_last_message(); } return $this->_errorMsg; } function ErrorNo() { if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode; if (empty($this->_errorMsg)) { $this->_errorMsg = mssql_get_last_message(); } $id = @mssql_query("select @@ERROR",$this->_connectionID); if (!$id) return false; $arr = mssql_fetch_array($id); @mssql_free_result($id); if (is_array($arr)) return $arr[0]; else return -1; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('mssql_pconnect')) return null; $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('mssql_pconnect')) return null; $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; // persistent connections can forget to rollback on crash, so we do it here. if ($this->autoRollback) { $cnt = $this->GetOne('select @@TRANCOUNT'); while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN'); } if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } function Prepare($sql) { $sqlarr = explode('?',$sql); if (sizeof($sqlarr) <= 1) return $sql; $sql2 = $sqlarr[0]; for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) { $sql2 .= '@P'.($i-1) . $sqlarr[$i]; } return array($sql,$this->qstr($sql2),$max); } function PrepareSP($sql) { if (!$this->_has_mssql_init) { ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); return $sql; } $stmt = mssql_init($sql,$this->_connectionID); if (!$stmt) return $sql; return array($sql,$stmt); } // returns concatenated string // MSSQL requires integers to be cast as strings // automatically cast every datatype to VARCHAR(255) // @author David Rogers (introspectshun) function Concat() { $s = ""; $arr = func_get_args(); // Split single record on commas, if possible if (sizeof($arr) == 1) { foreach ($arr as $arg) { $args = explode(',', $arg); } $arr = $args; } array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";')); $s = implode('+',$arr); if (sizeof($arr) > 0) return "$s"; return ''; } /* Usage: $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group # note that the parameter does not have @ in front! $db->Parameter($stmt,$id,'myid'); $db->Parameter($stmt,$group,'group',false,64); $db->Execute($stmt); @param $stmt Statement returned by Prepare() or PrepareSP(). @param $var PHP variable to bind to. Can set to null (for isNull support). @param $name Name of stored procedure variable name to bind to. @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. @param [$maxLen] Holds an maximum length of the variable. @param [$type] The data type of $var. Legal values depend on driver. See mssql_bind documentation at php.net. */ function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false) { if (!$this->_has_mssql_init) { ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0"); return false; } $isNull = is_null($var); // php 4.0.4 and above... if ($type === false) switch(gettype($var)) { default: case 'string': $type = SQLCHAR; break; case 'double': $type = SQLFLT8; break; case 'integer': $type = SQLINT4; break; case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0 } if ($this->debug) { $prefix = ($isOutput) ? 'Out' : 'In'; $ztype = (empty($type)) ? 'false' : $type; ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);"); } /* See http://phplens.com/lens/lensforum/msgs.php?id=7231 RETVAL is HARD CODED into php_mssql extension: The return value (a long integer value) is treated like a special OUTPUT parameter, called "RETVAL" (without the @). See the example at mssql_execute to see how it works. - type: one of this new supported PHP constants. SQLTEXT, SQLVARCHAR,SQLCHAR, SQLINT1,SQLINT2, SQLINT4, SQLBIT,SQLFLT8 */ if ($name !== 'RETVAL') $name = '@'.$name; return mssql_bind($stmt[1], $name, $var, $type, $isOutput, $isNull, $maxLen); } /* Unfortunately, it appears that mssql cannot handle varbinary > 255 chars So all your blobs must be of type "image". Remember to set in php.ini the following... ; Valid range 0 - 2147483647. Default = 4096. mssql.textlimit = 0 ; zero to pass through ; Valid range 0 - 2147483647. Default = 4096. mssql.textsize = 0 ; zero to pass through */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { if (strtoupper($blobtype) == 'CLOB') { $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where"; return $this->Execute($sql) != false; } $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where"; return $this->Execute($sql) != false; } // returns query ID if successful, otherwise false function _query($sql,$inputarr) { $this->_errorMsg = false; if (is_array($inputarr)) { # bind input params with sp_executesql: # see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm # works only with sql server 7 and newer if (!is_array($sql)) $sql = $this->Prepare($sql); $params = ''; $decl = ''; $i = 0; foreach($inputarr as $v) { if ($decl) { $decl .= ', '; $params .= ', '; } if (is_string($v)) { $len = strlen($v); if ($len == 0) $len = 1; if ($len > 4000 ) { // NVARCHAR is max 4000 chars. Let's use NTEXT $decl .= "@P$i NTEXT"; } else { $decl .= "@P$i NVARCHAR($len)"; } $params .= "@P$i=N". (strncmp($v,"'",1)==0? $v : $this->qstr($v)); } else if (is_integer($v)) { $decl .= "@P$i INT"; $params .= "@P$i=".$v; } else if (is_float($v)) { $decl .= "@P$i FLOAT"; $params .= "@P$i=".$v; } else if (is_bool($v)) { $decl .= "@P$i INT"; # Used INT just in case BIT in not supported on the user's MSSQL version. It will cast appropriately. $params .= "@P$i=".(($v)?'1':'0'); # True == 1 in MSSQL BIT fields and acceptable for storing logical true in an int field } else { $decl .= "@P$i CHAR"; # Used char because a type is required even when the value is to be NULL. $params .= "@P$i=NULL"; } $i += 1; } $decl = $this->qstr($decl); if ($this->debug) ADOConnection::outp("sp_executesql N{$sql[1]},N$decl,$params"); $rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params"); } else if (is_array($sql)) { # PrepareSP() $rez = mssql_execute($sql[1]); } else { $rez = mssql_query($sql,$this->_connectionID); } return $rez; } // returns true or false function _close() { if ($this->transCnt) $this->RollbackTrans(); $rez = @mssql_close($this->_connectionID); $this->_connectionID = false; return $rez; } // mssql uses a default date like Dec 30 2000 12:00AM function UnixDate($v) { return ADORecordSet_array_mssql::UnixDate($v); } function UnixTimeStamp($v) { return ADORecordSet_array_mssql::UnixTimeStamp($v); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_mssql extends ADORecordSet { var $databaseType = "mssql"; var $canSeek = true; var $hasFetchAssoc; // see http://phplens.com/lens/lensforum/msgs.php?id=6083 // _mths works only in non-localised system function ADORecordset_mssql($id,$mode=false) { // freedts check... $this->hasFetchAssoc = function_exists('mssql_fetch_assoc'); if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; return $this->ADORecordSet($id,$mode); } function _initrs() { GLOBAL $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1; $this->_numOfFields = @mssql_num_fields($this->_queryID); } //Contributed by "Sven Axelsson" // get next resultset - requires PHP 4.0.5 or later function NextRecordSet() { if (!mssql_next_result($this->_queryID)) return false; $this->_inited = false; $this->bind = false; $this->_currentRow = -1; $this->Init(); return true; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &FetchField($fieldOffset = -1) { if ($fieldOffset != -1) { $f = @mssql_fetch_field($this->_queryID, $fieldOffset); } else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ $f = @mssql_fetch_field($this->_queryID); } $false = false; if (empty($f)) return $false; return $f; } function _seek($row) { return @mssql_data_seek($this->_queryID, $row); } // speedup function MoveNext() { if ($this->EOF) return false; $this->_currentRow++; if ($this->fetchMode & ADODB_FETCH_ASSOC) { if ($this->fetchMode & ADODB_FETCH_NUM) { //ADODB_FETCH_BOTH mode $this->fields = @mssql_fetch_array($this->_queryID); } else { if ($this->hasFetchAssoc) {// only for PHP 4.2.0 or later $this->fields = @mssql_fetch_assoc($this->_queryID); } else { $flds = @mssql_fetch_array($this->_queryID); if (is_array($flds)) { $fassoc = array(); foreach($flds as $k => $v) { if (is_numeric($k)) continue; $fassoc[$k] = $v; } $this->fields = $fassoc; } else $this->fields = false; } } if (is_array($this->fields)) { if (ADODB_ASSOC_CASE == 0) { foreach($this->fields as $k=>$v) { $this->fields[strtolower($k)] = $v; } } else if (ADODB_ASSOC_CASE == 1) { foreach($this->fields as $k=>$v) { $this->fields[strtoupper($k)] = $v; } } } } else { $this->fields = @mssql_fetch_row($this->_queryID); } if ($this->fields) return true; $this->EOF = true; return false; } // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4 // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot! function _fetch($ignore_fields=false) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { if ($this->fetchMode & ADODB_FETCH_NUM) { //ADODB_FETCH_BOTH mode $this->fields = @mssql_fetch_array($this->_queryID); } else { if ($this->hasFetchAssoc) // only for PHP 4.2.0 or later $this->fields = @mssql_fetch_assoc($this->_queryID); else { $this->fields = @mssql_fetch_array($this->_queryID); if (@is_array($$this->fields)) { $fassoc = array(); foreach($$this->fields as $k => $v) { if (is_integer($k)) continue; $fassoc[$k] = $v; } $this->fields = $fassoc; } } } if (!$this->fields) { } else if (ADODB_ASSOC_CASE == 0) { foreach($this->fields as $k=>$v) { $this->fields[strtolower($k)] = $v; } } else if (ADODB_ASSOC_CASE == 1) { foreach($this->fields as $k=>$v) { $this->fields[strtoupper($k)] = $v; } } } else { $this->fields = @mssql_fetch_row($this->_queryID); } return $this->fields; } /* close() only needs to be called if you are worried about using too much memory while your script is running. All associated result memory for the specified result identifier will automatically be freed. */ function _close() { $rez = mssql_free_result($this->_queryID); $this->_queryID = false; return $rez; } // mssql uses a default date like Dec 30 2000 12:00AM function UnixDate($v) { return ADORecordSet_array_mssql::UnixDate($v); } function UnixTimeStamp($v) { return ADORecordSet_array_mssql::UnixTimeStamp($v); } } class ADORecordSet_array_mssql extends ADORecordSet_array { function ADORecordSet_array_mssql($id=-1,$mode=false) { $this->ADORecordSet_array($id,$mode); } // mssql uses a default date like Dec 30 2000 12:00AM function UnixDate($v) { if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v); global $ADODB_mssql_mths,$ADODB_mssql_date_order; //Dec 30 2000 12:00AM if ($ADODB_mssql_date_order == 'dmy') { if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { return parent::UnixDate($v); } if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $theday = $rr[1]; $themth = substr(strtoupper($rr[2]),0,3); } else { if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) { return parent::UnixDate($v); } if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $theday = $rr[2]; $themth = substr(strtoupper($rr[1]),0,3); } $themth = $ADODB_mssql_mths[$themth]; if ($themth <= 0) return false; // h-m-s-MM-DD-YY return mktime(0,0,0,$themth,$theday,$rr[3]); } function UnixTimeStamp($v) { if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v); global $ADODB_mssql_mths,$ADODB_mssql_date_order; //Dec 30 2000 12:00AM if ($ADODB_mssql_date_order == 'dmy') { if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" ,$v, $rr)) return parent::UnixTimeStamp($v); if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $theday = $rr[1]; $themth = substr(strtoupper($rr[2]),0,3); } else { if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|" ,$v, $rr)) return parent::UnixTimeStamp($v); if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0; $theday = $rr[2]; $themth = substr(strtoupper($rr[1]),0,3); } $themth = $ADODB_mssql_mths[$themth]; if ($themth <= 0) return false; switch (strtoupper($rr[6])) { case 'P': if ($rr[4]<12) $rr[4] += 12; break; case 'A': if ($rr[4]==12) $rr[4] = 0; break; default: break; } // h-m-s-MM-DD-YY return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]); } } /* Code Example 1: select object_name(constid) as constraint_name, object_name(fkeyid) as table_name, col_name(fkeyid, fkey) as column_name, object_name(rkeyid) as referenced_table_name, col_name(rkeyid, rkey) as referenced_column_name from sysforeignkeys where object_name(fkeyid) = x order by constraint_name, table_name, referenced_table_name, keyno Code Example 2: select constraint_name, column_name, ordinal_position from information_schema.key_column_usage where constraint_catalog = db_name() and table_name = x order by constraint_name, ordinal_position http://www.databasejournal.com/scripts/article.php/1440551 */ ?>phpgacl-3.3.7/adodb/drivers/adodb-sqlitepo.inc.php0100644025754300001440000000351010476657245021007 0ustar ipsousersADODB_sqlite(); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_sqlitepo extends ADORecordset_sqlite { var $databaseType = 'sqlitepo'; function ADORecordset_sqlitepo($queryID,$mode=false) { $this->ADORecordset_sqlite($queryID,$mode); } // Modified to strip table names from returned fields function _fetch($ignore_fields=false) { $this->fields = array(); $fields = @sqlite_fetch_array($this->_queryID,$this->fetchMode); if(is_array($fields)) foreach($fields as $n => $v) { if(($p = strpos($n, ".")) !== false) $n = substr($n, $p+1); $this->fields[$n] = $v; } return !empty($this->fields); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-pdo_mysql.inc.php0100644025754300001440000001124410476657245021161 0ustar ipsousershasTransactions = false; $parentDriver->_bindInputArray = false; $parentDriver->hasInsertID = true; $parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true); } // dayFraction is a day in floating point function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; // return "from_unixtime(unix_timestamp($date)+$fraction)"; } function ServerInfo() { $arr['description'] = ADOConnection::GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { $save = $this->metaTablesSQL; if ($showSchema && is_string($showSchema)) { $this->metaTablesSQL .= " from $showSchema"; } if ($mask) { $mask = $this->qstr($mask); $this->metaTablesSQL .= " like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); $this->metaTablesSQL = $save; return $ret; } function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); } function &MetaColumns($table) { $this->_findschema($table,$schema); if ($schema) { $dbName = $this->database; $this->SelectDB($schema); } global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if ($schema) { $this->SelectDB($dbName); } if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { $false = false; return $false; } $retarr = array(); while (!$rs->EOF){ $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $type = $rs->fields[1]; // split type into type(length): $fld->scale = null; if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1; } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) { $fld->type = $query_array[1]; $arr = explode(",",$query_array[2]); $fld->enums = $arr; $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6 $fld->max_length = ($zlen > 0) ? $zlen : 1; } else { $fld->type = $type; $fld->max_length = -1; } $fld->not_null = ($rs->fields[2] != 'YES'); $fld->primary_key = ($rs->fields[3] == 'PRI'); $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); $fld->binary = (strpos($type,'blob') !== false); $fld->unsigned = (strpos($type,'unsigned') !== false); if (!$fld->binary) { $d = $rs->fields[4]; if ($d != '' && $d != 'NULL') { $fld->has_default = true; $fld->default_value = $d; } else { $fld->has_default = false; } } if ($save == ADODB_FETCH_NUM) { $retarr[] = $fld; } else { $retarr[strtoupper($fld->name)] = $fld; } $rs->MoveNext(); } $rs->Close(); return $retarr; } // parameters use PostgreSQL convention, not MySQL function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0) { $offsetStr =($offset>=0) ? "$offset," : ''; // jason judge, see http://phplens.com/lens/lensforum/msgs.php?id=9220 if ($nrows < 0) $nrows = '18446744073709551615'; if ($secs) $rs =& $this->CacheExecute($secs,$sql." LIMIT $offsetStr$nrows",$inputarr); else $rs =& $this->Execute($sql." LIMIT $offsetStr$nrows",$inputarr); return $rs; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-oci8po.inc.php0100644025754300001440000001257610476657245020364 0ustar ipsousers Should some emulation of RecordCount() be implemented? */ // security - hide paths if (!defined('ADODB_DIR')) die(); include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php'); class ADODB_oci8po extends ADODB_oci8 { var $databaseType = 'oci8po'; var $dataProvider = 'oci8'; var $metaColumnsSQL = "select lower(cname),coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net var $metaTablesSQL = "select lower(table_name),table_type from cat where table_type in ('TABLE','VIEW')"; function ADODB_oci8po() { $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; # oci8po does not support adodb extension: adodb_movenext() } function Param($name) { return '?'; } function Prepare($sql,$cursor=false) { $sqlarr = explode('?',$sql); $sql = $sqlarr[0]; for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) { $sql .= ':'.($i-1) . $sqlarr[$i]; } return ADODB_oci8::Prepare($sql,$cursor); } // emulate handling of parameters ? ?, replacing with :bind0 :bind1 function _query($sql,$inputarr) { if (is_array($inputarr)) { $i = 0; if (is_array($sql)) { foreach($inputarr as $v) { $arr['bind'.$i++] = $v; } } else { $sqlarr = explode('?',$sql); $sql = $sqlarr[0]; foreach($inputarr as $k => $v) { $sql .= ":$k" . $sqlarr[++$i]; } } } return ADODB_oci8::_query($sql,$inputarr); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_oci8po extends ADORecordset_oci8 { var $databaseType = 'oci8po'; function ADORecordset_oci8po($queryID,$mode=false) { $this->ADORecordset_oci8($queryID,$mode); } function Fields($colname) { if ($this->fetchMode & OCI_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } // lowercase field names... function &_FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $fieldOffset += 1; $fld->name = strtolower(OCIcolumnname($this->_queryID, $fieldOffset)); $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); if ($fld->type == 'NUMBER') { //$p = OCIColumnPrecision($this->_queryID, $fieldOffset); $sc = OCIColumnScale($this->_queryID, $fieldOffset); if ($sc == 0) $fld->type = 'INT'; } return $fld; } /* function MoveNext() { if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { $this->_currentRow += 1; return true; } if (!$this->EOF) { $this->_currentRow += 1; $this->EOF = true; } return false; }*/ // 10% speedup to move MoveNext to child class function MoveNext() { if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { global $ADODB_ANSI_PADDING_OFF; $this->_currentRow++; if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); if (!empty($ADODB_ANSI_PADDING_OFF)) { foreach($this->fields as $k => $v) { if (is_string($v)) $this->fields[$k] = rtrim($v); } } return true; } if (!$this->EOF) { $this->EOF = true; $this->_currentRow++; } return false; } /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ function &GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { $arr = $this->GetArray($nrows); return $arr; } for ($i=1; $i < $offset; $i++) if (!@OCIFetch($this->_queryID)) { $arr = array(); return $arr; } if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { $arr = array(); return $arr; } if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { $results[$cnt++] = $this->fields; $this->MoveNext(); } return $results; } // Create associative array function _updatefields() { if (ADODB_ASSOC_CASE == 2) return; // native $arr = array(); $lowercase = (ADODB_ASSOC_CASE == 0); foreach($this->fields as $k => $v) { if (is_integer($k)) $arr[$k] = $v; else { if ($lowercase) $arr[strtolower($k)] = $v; else $arr[strtoupper($k)] = $v; } } $this->fields = $arr; } function _fetch() { $ret = @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); if ($ret) { global $ADODB_ANSI_PADDING_OFF; if ($this->fetchMode & OCI_ASSOC) $this->_updatefields(); if (!empty($ADODB_ANSI_PADDING_OFF)) { foreach($this->fields as $k => $v) { if (is_string($v)) $this->fields[$k] = rtrim($v); } } } return $ret; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-access.inc.php0100644025754300001440000000415510476657245020416 0ustar ipsousersADODB_odbc(); } function Time() { return time(); } function BeginTrans() { return false;} function IfNull( $field, $ifNull ) { return " IIF(IsNull($field), $ifNull, $field) "; // if Access } /* function &MetaTables() { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = odbc_tables($this->_connectionID); $rs = new ADORecordSet_odbc($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return false; $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr = &$rs->GetArray(); //print_pre($arr); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { if ($arr[$i][2] && $arr[$i][3] != 'SYSTEM TABLE') $arr2[] = $arr[$i][2]; } return $arr2; }*/ } class ADORecordSet_access extends ADORecordSet_odbc { var $databaseType = "access"; function ADORecordSet_access($id,$mode=false) { return $this->ADORecordSet_odbc($id,$mode); } }// class } ?>phpgacl-3.3.7/adodb/drivers/adodb-ado.inc.php0100644025754300001440000003637510476657245017731 0ustar ipsousers_affectedRows = new VARIANT; } function ServerInfo() { if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider; return array('description' => $desc, 'version' => ''); } function _affectedrows() { if (PHP_VERSION >= 5) return $this->_affectedRows; return $this->_affectedRows->value; } // you can also pass a connection string like this: // // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB'); function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL') { $u = 'UID'; $p = 'PWD'; if (!empty($this->charPage)) $dbc = new COM('ADODB.Connection',null,$this->charPage); else $dbc = new COM('ADODB.Connection'); if (! $dbc) return false; /* special support if provider is mssql or access */ if ($argProvider=='mssql') { $u = 'User Id'; //User parameter name for OLEDB $p = 'Password'; $argProvider = "SQLOLEDB"; // SQL Server Provider // not yet //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; //use trusted conection for SQL if username not specified if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; } else if ($argProvider=='access') $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider if ($argProvider) $dbc->Provider = $argProvider; if ($argUsername) $argHostname .= ";$u=$argUsername"; if ($argPassword)$argHostname .= ";$p=$argPassword"; if ($this->debug) ADOConnection::outp( "Host=".$argHostname."
\n version=$dbc->version"); // @ added below for php 4.0.1 and earlier @$dbc->Open((string) $argHostname); $this->_connectionID = $dbc; $dbc->CursorLocation = $this->_cursor_location; return $dbc->State > 0; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL') { return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider); } /* adSchemaCatalogs = 1, adSchemaCharacterSets = 2, adSchemaCollations = 3, adSchemaColumns = 4, adSchemaCheckConstraints = 5, adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, adSchemaReferentialContraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, adSchemaColumnPrivileges = 13, adSchemaTablePrivileges = 14, adSchemaUsagePrivileges = 15, adSchemaProcedures = 16, adSchemaSchemata = 17, adSchemaSQLLanguages = 18, adSchemaStatistics = 19, adSchemaTables = 20, adSchemaTranslations = 21, adSchemaProviderTypes = 22, adSchemaViews = 23, adSchemaViewColumnUsage = 24, adSchemaViewTableUsage = 25, adSchemaProcedureParameters = 26, adSchemaForeignKeys = 27, adSchemaPrimaryKeys = 28, adSchemaProcedureColumns = 29, adSchemaDBInfoKeywords = 30, adSchemaDBInfoLiterals = 31, adSchemaCubes = 32, adSchemaDimensions = 33, adSchemaHierarchies = 34, adSchemaLevels = 35, adSchemaMeasures = 36, adSchemaProperties = 37, adSchemaMembers = 38 */ function &MetaTables() { $arr= array(); $dbc = $this->_connectionID; $adors=@$dbc->OpenSchema(20);//tables if ($adors){ $f = $adors->Fields(2);//table/view name $t = $adors->Fields(3);//table type while (!$adors->EOF){ $tt=substr($t->value,0,6); if ($tt!='SYSTEM' && $tt !='ACCESS') $arr[]=$f->value; //print $f->value . ' ' . $t->value.'
'; $adors->MoveNext(); } $adors->Close(); } return $arr; } function &MetaColumns($table) { $table = strtoupper($table); $arr = array(); $dbc = $this->_connectionID; $adors=@$dbc->OpenSchema(4);//tables if ($adors){ $t = $adors->Fields(2);//table/view name while (!$adors->EOF){ if (strtoupper($t->Value) == $table) { $fld = new ADOFieldObject(); $c = $adors->Fields(3); $fld->name = $c->Value; $fld->type = 'CHAR'; // cannot discover type in ADO! $fld->max_length = -1; $arr[strtoupper($fld->name)]=$fld; } $adors->MoveNext(); } $adors->Close(); } $false = false; return empty($arr) ? $false : $arr; } /* returns queryID or false */ function &_query($sql,$inputarr=false) { $dbc = $this->_connectionID; $false = false; // return rs if ($inputarr) { if (!empty($this->charPage)) $oCmd = new COM('ADODB.Command',null,$this->charPage); else $oCmd = new COM('ADODB.Command'); $oCmd->ActiveConnection = $dbc; $oCmd->CommandText = $sql; $oCmd->CommandType = 1; foreach($inputarr as $val) { // name, type, direction 1 = input, len, $this->adoParameterType = 130; $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val); //print $p->Type.' '.$p->value; $oCmd->Parameters->Append($p); } $p = false; $rs = $oCmd->Execute(); $e = $dbc->Errors; if ($dbc->Errors->Count > 0) return $false; return $rs; } $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option); if ($dbc->Errors->Count > 0) return $false; if (! $rs) return $false; if ($rs->State == 0) { $true = true; return $true; // 0 = adStateClosed means no records returned } return $rs; } function BeginTrans() { if ($this->transOff) return true; if (isset($this->_thisTransactions)) if (!$this->_thisTransactions) return false; else { $o = $this->_connectionID->Properties("Transaction DDL"); $this->_thisTransactions = $o ? true : false; if (!$o) return false; } @$this->_connectionID->BeginTrans(); $this->transCnt += 1; return true; } function CommitTrans($ok=true) { if (!$ok) return $this->RollbackTrans(); if ($this->transOff) return true; @$this->_connectionID->CommitTrans(); if ($this->transCnt) @$this->transCnt -= 1; return true; } function RollbackTrans() { if ($this->transOff) return true; @$this->_connectionID->RollbackTrans(); if ($this->transCnt) @$this->transCnt -= 1; return true; } /* Returns: the last error message from previous database operation */ function ErrorMsg() { if (!$this->_connectionID) return "No connection established"; $errc = $this->_connectionID->Errors; if (!$errc) return "No Errors object found"; if ($errc->Count == 0) return ''; $err = $errc->Item($errc->Count-1); return $err->Description; } function ErrorNo() { $errc = $this->_connectionID->Errors; if ($errc->Count == 0) return 0; $err = $errc->Item($errc->Count-1); return $err->NativeError; } // returns true or false function _close() { if ($this->_connectionID) $this->_connectionID->Close(); $this->_connectionID = false; return true; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_ado extends ADORecordSet { var $bind = false; var $databaseType = "ado"; var $dataProvider = "ado"; var $_tarr = false; // caches the types var $_flds; // and field objects var $canSeek = true; var $hideErrors = true; function ADORecordSet_ado($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; return $this->ADORecordSet($id,$mode); } // returns the field object function &FetchField($fieldOffset = -1) { $off=$fieldOffset+1; // offsets begin at 1 $o= new ADOFieldObject(); $rs = $this->_queryID; $f = $rs->Fields($fieldOffset); $o->name = $f->Name; $t = $f->Type; $o->type = $this->MetaType($t); $o->max_length = $f->DefinedSize; $o->ado_type = $t; //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; return $o; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _initrs() { $rs = $this->_queryID; $this->_numOfRows = $rs->RecordCount; $f = $rs->Fields; $this->_numOfFields = $f->Count; } // should only be used to move forward as we normally use forward-only cursors function _seek($row) { $rs = $this->_queryID; // absoluteposition doesn't work -- my maths is wrong ? // $rs->AbsolutePosition->$row-2; // return true; if ($this->_currentRow > $row) return false; @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst return true; } /* OLEDB types enum DBTYPEENUM { DBTYPE_EMPTY = 0, DBTYPE_NULL = 1, DBTYPE_I2 = 2, DBTYPE_I4 = 3, DBTYPE_R4 = 4, DBTYPE_R8 = 5, DBTYPE_CY = 6, DBTYPE_DATE = 7, DBTYPE_BSTR = 8, DBTYPE_IDISPATCH = 9, DBTYPE_ERROR = 10, DBTYPE_BOOL = 11, DBTYPE_VARIANT = 12, DBTYPE_IUNKNOWN = 13, DBTYPE_DECIMAL = 14, DBTYPE_UI1 = 17, DBTYPE_ARRAY = 0x2000, DBTYPE_BYREF = 0x4000, DBTYPE_I1 = 16, DBTYPE_UI2 = 18, DBTYPE_UI4 = 19, DBTYPE_I8 = 20, DBTYPE_UI8 = 21, DBTYPE_GUID = 72, DBTYPE_VECTOR = 0x1000, DBTYPE_RESERVED = 0x8000, DBTYPE_BYTES = 128, DBTYPE_STR = 129, DBTYPE_WSTR = 130, DBTYPE_NUMERIC = 131, DBTYPE_UDT = 132, DBTYPE_DBDATE = 133, DBTYPE_DBTIME = 134, DBTYPE_DBTIMESTAMP = 135 ADO Types adEmpty = 0, adTinyInt = 16, adSmallInt = 2, adInteger = 3, adBigInt = 20, adUnsignedTinyInt = 17, adUnsignedSmallInt = 18, adUnsignedInt = 19, adUnsignedBigInt = 21, adSingle = 4, adDouble = 5, adCurrency = 6, adDecimal = 14, adNumeric = 131, adBoolean = 11, adError = 10, adUserDefined = 132, adVariant = 12, adIDispatch = 9, adIUnknown = 13, adGUID = 72, adDate = 7, adDBDate = 133, adDBTime = 134, adDBTimeStamp = 135, adBSTR = 8, adChar = 129, adVarChar = 200, adLongVarChar = 201, adWChar = 130, adVarWChar = 202, adLongVarWChar = 203, adBinary = 128, adVarBinary = 204, adLongVarBinary = 205, adChapter = 136, adFileTime = 64, adDBFileTime = 137, adPropVariant = 138, adVarNumeric = 139 */ function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } if (!is_numeric($t)) return $t; switch ($t) { case 0: case 12: // variant case 8: // bstr case 129: //char case 130: //wc case 200: // varc case 202:// varWC case 128: // bin case 204: // varBin case 72: // guid if ($len <= $this->blobSize) return 'C'; case 201: case 203: return 'X'; case 128: case 204: case 205: return 'B'; case 7: case 133: return 'D'; case 134: case 135: return 'T'; case 11: return 'L'; case 16:// adTinyInt = 16, case 2://adSmallInt = 2, case 3://adInteger = 3, case 4://adBigInt = 20, case 17://adUnsignedTinyInt = 17, case 18://adUnsignedSmallInt = 18, case 19://adUnsignedInt = 19, case 20://adUnsignedBigInt = 21, return 'I'; default: return 'N'; } } // time stamp not supported yet function _fetch() { $rs = $this->_queryID; if (!$rs or $rs->EOF) { $this->fields = false; return false; } $this->fields = array(); if (!$this->_tarr) { $tarr = array(); $flds = array(); for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { $f = $rs->Fields($i); $flds[] = $f; $tarr[] = $f->Type; } // bind types and flds only once $this->_tarr = $tarr; $this->_flds = $flds; } $t = reset($this->_tarr); $f = reset($this->_flds); if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { //echo "

",$t,' ';var_dump($f->value); echo '

'; switch($t) { case 135: // timestamp if (!strlen((string)$f->value)) $this->fields[] = false; else { if (!is_numeric($f->value)) # $val = variant_date_to_timestamp($f->value); // VT_DATE stores dates as (float) fractional days since 1899/12/30 00:00:00 $val=(float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; $this->fields[] = adodb_date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) if ($val = $f->value) { $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2); } else $this->fields[] = false; break; case 7: // adDate if (!strlen((string)$f->value)) $this->fields[] = false; else { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); } break; case 1: // null $this->fields[] = false; break; case 6: // currency is not supported properly; ADOConnection::outp( ''.$f->Name.': currency type not supported by PHP'); $this->fields[] = (float) $f->value; break; default: $this->fields[] = $f->value; break; } //print " $f->value $t, "; $f = next($this->_flds); $t = next($this->_tarr); } // for if ($this->hideErrors) error_reporting($olde); @$rs->MoveNext(); // @ needed for some versions of PHP! if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields = &$this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } function NextRecordSet() { $rs = $this->_queryID; $this->_queryID = $rs->NextRecordSet(); //$this->_queryID = $this->_QueryId->NextRecordSet(); if ($this->_queryID == null) return false; $this->_currentRow = -1; $this->_currentPage = -1; $this->bind = false; $this->fields = false; $this->_flds = false; $this->_tarr = false; $this->_inited = false; $this->Init(); return true; } function _close() { $this->_flds = false; @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk) $this->_queryID = false; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-mysql.inc.php0100644025754300001440000005031610476657245020322 0ustar ipsousersrsPrefix .= 'ext_'; } function ServerInfo() { $arr['description'] = ADOConnection::GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function IfNull( $field, $ifNull ) { return " IFNULL($field, $ifNull) "; // if MySQL } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { $save = $this->metaTablesSQL; if ($showSchema && is_string($showSchema)) { $this->metaTablesSQL .= " from $showSchema"; } if ($mask) { $mask = $this->qstr($mask); $this->metaTablesSQL .= " like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); $this->metaTablesSQL = $save; return $ret; } function &MetaIndexes ($table, $primary = FALSE, $owner=false) { // save old fetch mode global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } // get index details $rs = $this->Execute(sprintf('SHOW INDEX FROM %s',$table)); // restore fetchmode if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { return $false; } $indexes = array (); // parse index data into array while ($row = $rs->FetchRow()) { if ($primary == FALSE AND $row[2] == 'PRIMARY') { continue; } if (!isset($indexes[$row[2]])) { $indexes[$row[2]] = array( 'unique' => ($row[1] == 0), 'columns' => array() ); } $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4]; } // sort columns by order in the index foreach ( array_keys ($indexes) as $index ) { ksort ($indexes[$index]['columns']); } return $indexes; } // if magic quotes disabled, use mysql_real_escape_string() function qstr($s,$magic_quotes=false) { if (!$magic_quotes) { if (ADODB_PHPVER >= 0x4300) { if (is_resource($this->_connectionID)) return "'".mysql_real_escape_string($s,$this->_connectionID)."'"; } if ($this->replaceQuote[0] == '\\'){ $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); } return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " $s = str_replace('\\"','"',$s); return "'$s'"; } function _insertid() { return ADOConnection::GetOne('SELECT LAST_INSERT_ID()'); //return mysql_insert_id($this->_connectionID); } function GetOne($sql,$inputarr=false) { if ($this->compat323 == false && strncasecmp($sql,'sele',4) == 0) { $rs =& $this->SelectLimit($sql,1,-1,$inputarr); if ($rs) { $rs->Close(); if ($rs->EOF) return false; return reset($rs->fields); } } else { return ADOConnection::GetOne($sql,$inputarr); } return false; } function BeginTrans() { if ($this->debug) ADOConnection::outp("Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver"); } function _affectedrows() { return mysql_affected_rows($this->_connectionID); } // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html // Reference on Last_Insert_ID on the recommended way to simulate sequences var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; var $_genSeqSQL = "create table %s (id int not null)"; var $_genSeq2SQL = "insert into %s values (%s)"; var $_dropSeqSQL = "drop table %s"; function CreateSequence($seqname='adodbseq',$startID=1) { if (empty($this->_genSeqSQL)) return false; $u = strtoupper($seqname); $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); } function GenID($seqname='adodbseq',$startID=1) { // post-nuke sets hasGenID to false if (!$this->hasGenID) return false; $savelog = $this->_logsql; $this->_logsql = false; $getnext = sprintf($this->_genIDSQL,$seqname); $holdtransOK = $this->_transOK; // save the current status $rs = @$this->Execute($getnext); if (!$rs) { if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset $u = strtoupper($seqname); $this->Execute(sprintf($this->_genSeqSQL,$seqname)); $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); $rs = $this->Execute($getnext); } $this->genID = mysql_insert_id($this->_connectionID); if ($rs) $rs->Close(); $this->_logsql = $savelog; return $this->genID; } function &MetaDatabases() { $qid = mysql_list_dbs($this->_connectionID); $arr = array(); $i = 0; $max = mysql_num_rows($qid); while ($i < $max) { $db = mysql_tablename($qid,$i); if ($db != 'mysql') $arr[] = $db; $i += 1; } return $arr; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = 'DATE_FORMAT('.$col.",'"; $concat = false; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { $ch = $fmt[$i]; switch($ch) { default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } /** FALL THROUGH */ case '-': case '/': $s .= $ch; break; case 'Y': case 'y': $s .= '%Y'; break; case 'M': $s .= '%b'; break; case 'm': $s .= '%m'; break; case 'D': case 'd': $s .= '%d'; break; case 'Q': case 'q': $s .= "'),Quarter($col)"; if ($len > $i+1) $s .= ",DATE_FORMAT($col,'"; else $s .= ",('"; $concat = true; break; case 'H': $s .= '%H'; break; case 'h': $s .= '%I'; break; case 'i': $s .= '%i'; break; case 's': $s .= '%s'; break; case 'a': case 'A': $s .= '%p'; break; case 'w': $s .= '%w'; break; case 'W': $s .= '%U'; break; case 'l': $s .= '%W'; break; } } $s.="')"; if ($concat) $s = "CONCAT($s)"; return $s; } // returns concatenated string // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator function Concat() { $s = ""; $arr = func_get_args(); // suggestion by andrew005@mnogo.ru $s = implode(',',$arr); if (strlen($s) > 0) return "CONCAT($s)"; else return ''; } function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; // return "from_unixtime(unix_timestamp($date)+$fraction)"; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!empty($this->port)) $argHostname .= ":".$this->port; if (ADODB_PHPVER >= 0x4300) $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, $this->forceNewConnect,$this->clientFlags); else if (ADODB_PHPVER >= 0x4200) $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword, $this->forceNewConnect); else $this->_connectionID = mysql_connect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!empty($this->port)) $argHostname .= ":".$this->port; if (ADODB_PHPVER >= 0x4300) $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword,$this->clientFlags); else $this->_connectionID = mysql_pconnect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($this->autoRollback) $this->RollbackTrans(); if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { $this->forceNewConnect = true; return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); } function &MetaColumns($table) { $this->_findschema($table,$schema); if ($schema) { $dbName = $this->database; $this->SelectDB($schema); } global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if ($schema) { $this->SelectDB($dbName); } if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { $false = false; return $false; } $retarr = array(); while (!$rs->EOF){ $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $type = $rs->fields[1]; // split type into type(length): $fld->scale = null; if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1; } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) { $fld->type = $query_array[1]; $arr = explode(",",$query_array[2]); $fld->enums = $arr; $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6 $fld->max_length = ($zlen > 0) ? $zlen : 1; } else { $fld->type = $type; $fld->max_length = -1; } $fld->not_null = ($rs->fields[2] != 'YES'); $fld->primary_key = ($rs->fields[3] == 'PRI'); $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); $fld->binary = (strpos($type,'blob') !== false); $fld->unsigned = (strpos($type,'unsigned') !== false); if (!$fld->binary) { $d = $rs->fields[4]; if ($d != '' && $d != 'NULL') { $fld->has_default = true; $fld->default_value = $d; } else { $fld->has_default = false; } } if ($save == ADODB_FETCH_NUM) { $retarr[] = $fld; } else { $retarr[strtoupper($fld->name)] = $fld; } $rs->MoveNext(); } $rs->Close(); return $retarr; } // returns true or false function SelectDB($dbName) { $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions if ($this->_connectionID) { return @mysql_select_db($dbName,$this->_connectionID); } else return false; } // parameters use PostgreSQL convention, not MySQL function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0) { $offsetStr =($offset>=0) ? ((integer)$offset)."," : ''; // jason judge, see http://phplens.com/lens/lensforum/msgs.php?id=9220 if ($nrows < 0) $nrows = '18446744073709551615'; if ($secs) $rs =& $this->CacheExecute($secs,$sql." LIMIT $offsetStr".((integer)$nrows),$inputarr); else $rs =& $this->Execute($sql." LIMIT $offsetStr".((integer)$nrows),$inputarr); return $rs; } // returns queryID or false function _query($sql,$inputarr) { //global $ADODB_COUNTRECS; //if($ADODB_COUNTRECS) return mysql_query($sql,$this->_connectionID); //else return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6 } /* Returns: the last error message from previous database operation */ function ErrorMsg() { if ($this->_logsql) return $this->_errorMsg; if (empty($this->_connectionID)) $this->_errorMsg = @mysql_error(); else $this->_errorMsg = @mysql_error($this->_connectionID); return $this->_errorMsg; } /* Returns: the last error number from previous database operation */ function ErrorNo() { if ($this->_logsql) return $this->_errorCode; if (empty($this->_connectionID)) return @mysql_errno(); else return @mysql_errno($this->_connectionID); } // returns true or false function _close() { @mysql_close($this->_connectionID); $this->_connectionID = false; } /* * Maximum size of C field */ function CharMax() { return 255; } /* * Maximum size of X field */ function TextMax() { return 4294967295; } // "Innox - Juan Carlos Gonzalez" function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) { global $ADODB_FETCH_MODE; if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true; if ( !empty($owner) ) { $table = "$owner.$table"; } $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table)); if ($associative) $create_sql = $a_create_table["Create Table"]; else $create_sql = $a_create_table[1]; $matches = array(); if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false; $foreign_keys = array(); $num_keys = count($matches[0]); for ( $i = 0; $i < $num_keys; $i ++ ) { $my_field = explode('`, `', $matches[1][$i]); $ref_table = $matches[2][$i]; $ref_field = explode('`, `', $matches[3][$i]); if ( $upper ) { $ref_table = strtoupper($ref_table); } $foreign_keys[$ref_table] = array(); $num_fields = count($my_field); for ( $j = 0; $j < $num_fields; $j ++ ) { if ( $associative ) { $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; } else { $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; } } } return $foreign_keys; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_mysql extends ADORecordSet{ var $databaseType = "mysql"; var $canSeek = true; function ADORecordSet_mysql($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = MYSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function _initrs() { //GLOBAL $ADODB_COUNTRECS; // $this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1; $this->_numOfRows = @mysql_num_rows($this->_queryID); $this->_numOfFields = @mysql_num_fields($this->_queryID); } function &FetchField($fieldOffset = -1) { if ($fieldOffset != -1) { $o = @mysql_fetch_field($this->_queryID, $fieldOffset); $f = @mysql_field_flags($this->_queryID,$fieldOffset); $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); // suggested by: Jim Nicholson (jnich@att.com) //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable $o->binary = (strpos($f,'binary')!== false); } else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ $o = @mysql_fetch_field($this->_queryID); $o->max_length = @mysql_field_len($this->_queryID); // suggested by: Jim Nicholson (jnich@att.com) //$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable } return $o; } function &GetRowAssoc($upper=true) { if ($this->fetchMode == MYSQL_ASSOC && !$upper) $row = $this->fields; else $row =& ADORecordSet::GetRowAssoc($upper); return $row; } /* Use associative array to get fields array */ function Fields($colname) { // added @ by "Michael William Miller" if ($this->fetchMode != MYSQL_NUM) return @$this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _seek($row) { if ($this->_numOfRows == 0) return false; return @mysql_data_seek($this->_queryID,$row); } function MoveNext() { //return adodb_movenext($this); //if (defined('ADODB_EXTENSION')) return adodb_movenext($this); if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) { $this->_currentRow += 1; return true; } if (!$this->EOF) { $this->_currentRow += 1; $this->EOF = true; } return false; } function _fetch() { $this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode); return is_array($this->fields); } function _close() { @mysql_free_result($this->_queryID); $this->_queryID = false; } function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { case 'STRING': case 'CHAR': case 'VARCHAR': case 'TINYBLOB': case 'TINYTEXT': case 'ENUM': case 'SET': if ($len <= $this->blobSize) return 'C'; case 'TEXT': case 'LONGTEXT': case 'MEDIUMTEXT': return 'X'; // php_mysql extension always returns 'blob' even if 'text' // so we have to check whether binary... case 'IMAGE': case 'LONGBLOB': case 'BLOB': case 'MEDIUMBLOB': return !empty($fieldobj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': return 'D'; case 'TIME': case 'DATETIME': case 'TIMESTAMP': return 'T'; case 'INT': case 'INTEGER': case 'BIGINT': case 'TINYINT': case 'MEDIUMINT': case 'SMALLINT': if (!empty($fieldobj->primary_key)) return 'R'; else return 'I'; default: return 'N'; } } } class ADORecordSet_ext_mysql extends ADORecordSet_mysql { function ADORecordSet_ext_mysql($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = MYSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function MoveNext() { return @adodb_movenext($this); } } } ?>phpgacl-3.3.7/adodb/drivers/adodb-postgres.inc.php0100644025754300001440000000101010476657245021006 0ustar ipsousersphpgacl-3.3.7/adodb/drivers/adodb-oci805.inc.php0100644025754300001440000000305410476657245020161 0ustar ipsousersADODB_oci8(); } function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { // seems that oracle only supports 1 hint comment in 8i if (strpos($sql,'/*+') !== false) $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql); else $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql); /* The following is only available from 8.1.5 because order by in inline views not available before then... http://www.jlcomp.demon.co.uk/faq/top_sql.html if ($nrows > 0) { if ($offset > 0) $nrows += $offset; $sql = "select * from ($sql) where rownum <= $nrows"; $nrows = -1; } */ return ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); } } class ADORecordset_oci805 extends ADORecordset_oci8 { var $databaseType = "oci805"; function ADORecordset_oci805($id,$mode=false) { $this->ADORecordset_oci8($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-db2.inc.php0100644025754300001440000005104410476657245017623 0ustar ipsousers_haserrorfunctions = ADODB_PHPVER >= 0x4050; } // returns true or false function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; if (!function_exists('db2_connect')) { ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension."); return null; } // This needs to be set before the connect(). // Replaces the odbc_binmode() call that was in Execute() ini_set('ibm_db2.binmode', $this->binmode); if ($argDatabasename) { $this->_connectionID = db2_connect($argDatabasename,$argUsername,$argPassword); } else { $this->_connectionID = db2_connect($argDSN,$argUsername,$argPassword); } if (isset($php_errormsg)) $php_errormsg = ''; // For db2_connect(), there is an optional 4th arg. If present, it must be // an array of valid options. So far, we don't use them. $this->_errorMsg = @db2_conn_errormsg(); if (isset($this->connectStmt)) $this->Execute($this->connectStmt); return $this->_connectionID != false; } // returns true or false function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; if (!function_exists('db2_connect')) return null; // This needs to be set before the connect(). // Replaces the odbc_binmode() call that was in Execute() ini_set('ibm_db2.binmode', $this->binmode); if (isset($php_errormsg)) $php_errormsg = ''; $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; if ($argDatabasename) { $this->_connectionID = db2_pconnect($argDatabasename,$argUsername,$argPassword); } else { $this->_connectionID = db2_pconnect($argDSN,$argUsername,$argPassword); } if (isset($php_errormsg)) $php_errormsg = ''; $this->_errorMsg = @db2_conn_errormsg(); if ($this->_connectionID && $this->autoRollback) @db2_rollback($this->_connectionID); if (isset($this->connectStmt)) $this->Execute($this->connectStmt); return $this->_connectionID != false; } // format and return date string in database timestamp format function DBTimeStamp($ts) { if (empty($ts) && $ts !== 0) return 'null'; if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')"; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { // use right() and replace() ? if (!$col) $col = $this->sysDate; /* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */ if ($fmt== 'Y-m-d H:i:s') return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')"; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= $this->concat_operator; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': if ($len==1) return "year($col)"; $s .= "char(year($col))"; break; case 'M': if ($len==1) return "monthname($col)"; $s .= "substr(monthname($col),1,3)"; break; case 'm': if ($len==1) return "month($col)"; $s .= "right(digits(month($col)),2)"; break; case 'D': case 'd': if ($len==1) return "day($col)"; $s .= "right(digits(day($col)),2)"; break; case 'H': case 'h': if ($len==1) return "hour($col)"; if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)"; else $s .= "''"; break; case 'i': case 'I': if ($len==1) return "minute($col)"; if ($col != $this->sysDate) $s .= "right(digits(minute($col)),2)"; else $s .= "''"; break; case 'S': case 's': if ($len==1) return "second($col)"; if ($col != $this->sysDate) $s .= "right(digits(second($col)),2)"; else $s .= "''"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); } } return $s; } function ServerInfo() { if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { $dsn = strtoupper($this->host); $first = true; $found = false; if (!function_exists('db2_data_source')) return false; while(true) { $rez = @db2_data_source($this->_connectionID, $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); $first = false; if (!is_array($rez)) break; if (strtoupper($rez['server']) == $dsn) { $found = true; break; } } if (!$found) return ADOConnection::ServerInfo(); if (!isset($rez['version'])) $rez['version'] = ''; return $rez; } else { return ADOConnection::ServerInfo(); } } function CreateSequence($seqname='adodbseq',$start=1) { if (empty($this->_genSeqSQL)) return false; $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; return true; } function DropSequence($seqname) { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } /* This algorithm is not very efficient, but works even if table locking is not available. Will return false if unable to generate an ID after $MAXLOOPS attempts. */ function GenID($seq='adodbseq',$start=1) { // if you have to modify the parameter below, your database is overloaded, // or you need to implement generation of id's yourself! $num = $this->GetOne("VALUES NEXTVAL FOR $seq"); return $num; } function ErrorMsg() { if ($this->_haserrorfunctions) { if ($this->_errorMsg !== false) return $this->_errorMsg; if (empty($this->_connectionID)) return @db2_conn_errormsg(); return @db2_conn_errormsg($this->_connectionID); } else return ADOConnection::ErrorMsg(); } function ErrorNo() { if ($this->_haserrorfunctions) { if ($this->_errorCode !== false) { // bug in 4.0.6, error number can be corrupted string (should be 6 digits) return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; } if (empty($this->_connectionID)) $e = @db2_conn_error(); else $e = @db2_conn_error($this->_connectionID); // bug in 4.0.6, error number can be corrupted string (should be 6 digits) // so we check and patch if (strlen($e)<=2) return 0; return $e; } else return ADOConnection::ErrorNo(); } function BeginTrans() { if (!$this->hasTransactions) return false; if ($this->transOff) return true; $this->transCnt += 1; $this->_autocommit = false; return db2_autocommit($this->_connectionID,false); } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = db2_commit($this->_connectionID); db2_autocommit($this->_connectionID,true); return $ret; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = db2_rollback($this->_connectionID); db2_autocommit($this->_connectionID,true); return $ret; } function MetaPrimaryKeys($table) { global $ADODB_FETCH_MODE; if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = @db2_primarykeys($this->_connectionID,'',$schema,$table); if (!$qid) { $ADODB_FETCH_MODE = $savem; return false; } $rs = new ADORecordSet_db2($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return false; $arr =& $rs->GetArray(); $rs->Close(); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { if ($arr[$i][3]) $arr2[] = $arr[$i][3]; } return $arr2; } function MetaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE ) { global $ADODB_FETCH_MODE; if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = @db2_foreign_keys($this->_connectionID,'',$schema,$table); if (!$qid) { $ADODB_FETCH_MODE = $savem; return false; } $rs = new ADORecordSet_db2($qid); $ADODB_FETCH_MODE = $savem; /* $rs->fields indices 0 PKTABLE_CAT 1 PKTABLE_SCHEM 2 PKTABLE_NAME 3 PKCOLUMN_NAME 4 FKTABLE_CAT 5 FKTABLE_SCHEM 6 FKTABLE_NAME 7 FKCOLUMN_NAME */ if (!$rs) return false; $foreign_keys = array(); while (!$rs->EOF) { if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { if (!is_array($foreign_keys[$rs->fields[5].'.'.$rs->fields[6]])) $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]] = array(); $foreign_keys[$rs->fields[5].'.'.$rs->fields[6]][$rs->fields[7]] = $rs->fields[3]; } $rs->MoveNext(); } $rs->Close(); return $foreign_key; } function &MetaTables($ttype=false,$schema=false) { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = db2_tables($this->_connectionID); $rs = new ADORecordSet_db2($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) { $false = false; return $false; } $arr =& $rs->GetArray(); $rs->Close(); $arr2 = array(); if ($ttype) { $isview = strncmp($ttype,'V',1) === 0; } for ($i=0; $i < sizeof($arr); $i++) { if (!$arr[$i][2]) continue; $type = $arr[$i][3]; $schemaval = ($schema) ? $arr[$i][1].'.' : ''; if ($ttype) { if ($isview) { if (strncmp($type,'V',1) === 0) $arr2[] = $schemaval.$arr[$i][2]; } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2]; } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2]; } return $arr2; } /* See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp / SQL data type codes / #define SQL_UNKNOWN_TYPE 0 #define SQL_CHAR 1 #define SQL_NUMERIC 2 #define SQL_DECIMAL 3 #define SQL_INTEGER 4 #define SQL_SMALLINT 5 #define SQL_FLOAT 6 #define SQL_REAL 7 #define SQL_DOUBLE 8 #if (DB2VER >= 0x0300) #define SQL_DATETIME 9 #endif #define SQL_VARCHAR 12 / One-parameter shortcuts for date/time data types / #if (DB2VER >= 0x0300) #define SQL_TYPE_DATE 91 #define SQL_TYPE_TIME 92 #define SQL_TYPE_TIMESTAMP 93 #define SQL_UNICODE (-95) #define SQL_UNICODE_VARCHAR (-96) #define SQL_UNICODE_LONGVARCHAR (-97) */ function DB2Types($t) { switch ((integer)$t) { case 1: case 12: case 0: case -95: case -96: return 'C'; case -97: case -1: //text return 'X'; case -4: //image return 'B'; case 9: case 91: return 'D'; case 10: case 11: case 92: case 93: return 'T'; case 4: case 5: case -6: return 'I'; case -11: // uniqidentifier return 'R'; case -7: //bit return 'L'; default: return 'N'; } } function &MetaColumns($table) { global $ADODB_FETCH_MODE; $false = false; if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $colname = "%"; $qid = db2_columns($this->_connectionID, "", $schema, $table, $colname); if (empty($qid)) return $false; $rs =& new ADORecordSet_db2($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return $false; $rs->_fetch(); $retarr = array(); /* $rs->fields indices 0 TABLE_QUALIFIER 1 TABLE_SCHEM 2 TABLE_NAME 3 COLUMN_NAME 4 DATA_TYPE 5 TYPE_NAME 6 PRECISION 7 LENGTH 8 SCALE 9 RADIX 10 NULLABLE 11 REMARKS */ while (!$rs->EOF) { if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[3]; $fld->type = $this->DB2Types($rs->fields[4]); // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp // access uses precision to store length for char/varchar if ($fld->type == 'C' or $fld->type == 'X') { if ($rs->fields[4] <= -95) // UNICODE $fld->max_length = $rs->fields[7]/2; else $fld->max_length = $rs->fields[7]; } else $fld->max_length = $rs->fields[7]; $fld->not_null = !empty($rs->fields[10]); $fld->scale = $rs->fields[8]; $fld->primary_key = false; $retarr[strtoupper($fld->name)] = $fld; } else if (sizeof($retarr)>0) break; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) $retarr = false; $qid = db2_primary_keys($this->_connectionID, "", $schema, $table); if (empty($qid)) return $false; $rs =& new ADORecordSet_db2($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return $retarr; $rs->_fetch(); /* $rs->fields indices 0 TABLE_CAT 1 TABLE_SCHEM 2 TABLE_NAME 3 COLUMN_NAME 4 KEY_SEQ 5 PK_NAME */ while (!$rs->EOF) { if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { $retarr[strtoupper($rs->fields[3])]->primary_key = true; } else if (sizeof($retarr)>0) break; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) $retarr = false; return $retarr; } function Prepare($sql) { if (! $this->_bindInputArray) return $sql; // no binding $stmt = db2_prepare($this->_connectionID,$sql); if (!$stmt) { // we don't know whether db2 driver is parsing prepared stmts, so just return sql return $sql; } return array($sql,$stmt,false); } /* returns queryID or false */ function _query($sql,$inputarr=false) { GLOBAL $php_errormsg; if (isset($php_errormsg)) $php_errormsg = ''; $this->_error = ''; if ($inputarr) { if (is_array($sql)) { $stmtid = $sql[1]; } else { $stmtid = db2_prepare($this->_connectionID,$sql); if ($stmtid == false) { $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; return false; } } if (! db2_execute($stmtid,$inputarr)) { if ($this->_haserrorfunctions) { $this->_errorMsg = db2_stmt_errormsg(); $this->_errorCode = db2_stmt_error(); } return false; } } else if (is_array($sql)) { $stmtid = $sql[1]; if (!db2_execute($stmtid)) { if ($this->_haserrorfunctions) { $this->_errorMsg = db2_stmt_errormsg(); $this->_errorCode = db2_stmt_error(); } return false; } } else $stmtid = @db2_exec($this->_connectionID,$sql); $this->_lastAffectedRows = 0; if ($stmtid) { if (@db2_num_fields($stmtid) == 0) { $this->_lastAffectedRows = db2_num_rows($stmtid); $stmtid = true; } else { $this->_lastAffectedRows = 0; } if ($this->_haserrorfunctions) { $this->_errorMsg = ''; $this->_errorCode = 0; } else $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; } else { if ($this->_haserrorfunctions) { $this->_errorMsg = db2_stmt_errormsg(); $this->_errorCode = db2_stmt_error(); } else $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; } return $stmtid; } /* Insert a null into the blob field of the table first. Then use UpdateBlob to store the blob. Usage: $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; } // returns true or false function _close() { $ret = @db2_close($this->_connectionID); $this->_connectionID = false; return $ret; } function _affectedrows() { return $this->_lastAffectedRows; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_db2 extends ADORecordSet { var $bind = false; var $databaseType = "db2"; var $dataProvider = "db2"; var $useFetchArray; function ADORecordSet_db2($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; $this->_queryID = $id; } // returns the field object function &FetchField($offset = -1) { $o= new ADOFieldObject(); $o->name = @db2_field_name($this->_queryID,$offset); $o->type = @db2_field_type($this->_queryID,$offset); $o->max_length = db2_field_width($this->_queryID,$offset); if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); return $o; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1; $this->_numOfFields = @db2_num_fields($this->_queryID); // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 if ($this->_numOfRows == 0) $this->_numOfRows = -1; } function _seek($row) { return false; } // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated function &GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { $rs =& $this->GetArray($nrows); return $rs; } $savem = $this->fetchMode; $this->fetchMode = ADODB_FETCH_NUM; $this->Move($offset); $this->fetchMode = $savem; if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { $results[$cnt++] = $this->fields; $this->MoveNext(); } return $results; } function MoveNext() { if ($this->_numOfRows != 0 && !$this->EOF) { $this->_currentRow++; $this->fields = @db2_fetch_array($this->_queryID); if ($this->fields) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } } $this->fields = false; $this->EOF = true; return false; } function _fetch() { $this->fields = db2_fetch_array($this->_queryID); if ($this->fields) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } $this->fields = false; return false; } function _close() { return @db2_free_result($this->_queryID); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-fbsql.inc.php0100644025754300001440000001456010476657245020265 0ustar ipsousers. Set tabs to 8. */ // security - hide paths if (!defined('ADODB_DIR')) die(); if (! defined("_ADODB_FBSQL_LAYER")) { define("_ADODB_FBSQL_LAYER", 1 ); class ADODB_fbsql extends ADOConnection { var $databaseType = 'fbsql'; var $hasInsertID = true; var $hasAffectedRows = true; var $metaTablesSQL = "SHOW TABLES"; var $metaColumnsSQL = "SHOW COLUMNS FROM %s"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $hasLimit = false; function ADODB_fbsql() { } function _insertid() { return fbsql_insert_id($this->_connectionID); } function _affectedrows() { return fbsql_affected_rows($this->_connectionID); } function &MetaDatabases() { $qid = fbsql_list_dbs($this->_connectionID); $arr = array(); $i = 0; $max = fbsql_num_rows($qid); while ($i < $max) { $arr[] = fbsql_tablename($qid,$i); $i += 1; } return $arr; } // returns concatenated string function Concat() { $s = ""; $arr = func_get_args(); $first = true; $s = implode(',',$arr); if (sizeof($arr) > 0) return "CONCAT($s)"; else return ''; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { $this->_connectionID = fbsql_connect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { $this->_connectionID = fbsql_pconnect($argHostname,$argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } function &MetaColumns($table) { if ($this->metaColumnsSQL) { $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if ($rs === false) return false; $retarr = array(); while (!$rs->EOF){ $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; // split type into type(length): if (preg_match("/^(.+)\((\d+)\)$/", $fld->type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = $query_array[2]; } else { $fld->max_length = -1; } $fld->not_null = ($rs->fields[2] != 'YES'); $fld->primary_key = ($rs->fields[3] == 'PRI'); $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); $fld->binary = (strpos($fld->type,'blob') !== false); $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); return $retarr; } return false; } // returns true or false function SelectDB($dbName) { $this->database = $dbName; if ($this->_connectionID) { return @fbsql_select_db($dbName,$this->_connectionID); } else return false; } // returns queryID or false function _query($sql,$inputarr) { return fbsql_query("$sql;",$this->_connectionID); } /* Returns: the last error message from previous database operation */ function ErrorMsg() { $this->_errorMsg = @fbsql_error($this->_connectionID); return $this->_errorMsg; } /* Returns: the last error number from previous database operation */ function ErrorNo() { return @fbsql_errno($this->_connectionID); } // returns true or false function _close() { return @fbsql_close($this->_connectionID); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_fbsql extends ADORecordSet{ var $databaseType = "fbsql"; var $canSeek = true; function ADORecordSet_fbsql($queryID,$mode=false) { if (!$mode) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = FBSQL_NUM; break; case ADODB_FETCH_ASSOC: $this->fetchMode = FBSQL_ASSOC; break; case ADODB_FETCH_BOTH: default: $this->fetchMode = FBSQL_BOTH; break; } return $this->ADORecordSet($queryID); } function _initrs() { GLOBAL $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS) ? @fbsql_num_rows($this->_queryID):-1; $this->_numOfFields = @fbsql_num_fields($this->_queryID); } function &FetchField($fieldOffset = -1) { if ($fieldOffset != -1) { $o = @fbsql_fetch_field($this->_queryID, $fieldOffset); //$o->max_length = -1; // fbsql returns the max length less spaces -- so it is unrealiable $f = @fbsql_field_flags($this->_queryID,$fieldOffset); $o->binary = (strpos($f,'binary')!== false); } else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */ $o = @fbsql_fetch_field($this->_queryID);// fbsql returns the max length less spaces -- so it is unrealiable //$o->max_length = -1; } return $o; } function _seek($row) { return @fbsql_data_seek($this->_queryID,$row); } function _fetch($ignore_fields=false) { $this->fields = @fbsql_fetch_array($this->_queryID,$this->fetchMode); return ($this->fields == true); } function _close() { return @fbsql_free_result($this->_queryID); } function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } $len = -1; // fbsql max_length is not accurate switch (strtoupper($t)) { case 'CHARACTER': case 'CHARACTER VARYING': case 'BLOB': case 'CLOB': case 'BIT': case 'BIT VARYING': if ($len <= $this->blobSize) return 'C'; // so we have to check whether binary... case 'IMAGE': case 'LONGBLOB': case 'BLOB': case 'MEDIUMBLOB': return !empty($fieldobj->binary) ? 'B' : 'X'; case 'DATE': return 'D'; case 'TIME': case 'TIME WITH TIME ZONE': case 'TIMESTAMP': case 'TIMESTAMP WITH TIME ZONE': return 'T'; case 'PRIMARY_KEY': return 'R'; case 'INTEGER': case 'SMALLINT': case 'BOOLEAN': if (!empty($fieldobj->primary_key)) return 'R'; else return 'I'; default: return 'N'; } } } //class } // defined ?>phpgacl-3.3.7/adodb/drivers/adodb-sybase_ase.inc.php0100644025754300001440000000630410476657245021271 0ustar ipsousersmetaTablesSQL) { // complicated state saving by the need for backward compat if ($ttype == 'VIEWS'){ $sql = str_replace('U', 'V', $this->metaTablesSQL); }elseif (false === $ttype){ $sql = str_replace('U',"U' OR type='V", $this->metaTablesSQL); }else{ // TABLES OR ANY OTHER $sql = $this->metaTablesSQL; } $rs = $this->Execute($sql); if ($rs === false || !method_exists($rs, 'GetArray')){ return $false; } $arr =& $rs->GetArray(); $arr2 = array(); foreach($arr as $key=>$value){ $arr2[] = trim($value['name']); } return $arr2; } return $false; } function MetaDatabases() { $arr = array(); if ($this->metaDatabasesSQL!='') { $rs = $this->Execute($this->metaDatabasesSQL); if ($rs && !$rs->EOF){ while (!$rs->EOF){ $arr[] = $rs->Fields('name'); $rs->MoveNext(); } return $arr; } } return false; } // fix a bug which prevent the metaColumns query to be executed for Sybase ASE function &MetaColumns($table,$upper=false) { $false = false; if (!empty($this->metaColumnsSQL)) { $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if ($rs === false) return $false; $retarr = array(); while (!$rs->EOF) { $fld =& new ADOFieldObject(); $fld->name = $rs->Fields('field_name'); $fld->type = $rs->Fields('type'); $fld->max_length = $rs->Fields('width'); $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); return $retarr; } return $false; } function getProcedureList($schema) { return false; } function ErrorMsg() { if (!function_exists('sybase_connect')){ return 'Your PHP doesn\'t contain the Sybase connection module!'; } return parent::ErrorMsg(); } } class adorecordset_sybase_ase extends ADORecordset_sybase { var $databaseType = "sybase_ase"; function ADORecordset_sybase_ase($id,$mode=false) { $this->ADORecordSet_sybase($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-mysqli.inc.php0100644025754300001440000006256310476657245020502 0ustar ipsousers_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); } // returns true or false // To add: parameter int $port, // parameter string $socket function _connect($argHostname = NULL, $argUsername = NULL, $argPassword = NULL, $argDatabasename = NULL, $persist=false) { if(!extension_loaded("mysqli")) { return null; } $this->_connectionID = @mysqli_init(); if (is_null($this->_connectionID)) { // mysqli_init only fails if insufficient memory if ($this->debug) ADOConnection::outp("mysqli_init() failed : " . $this->ErrorMsg()); return false; } /* I suggest a simple fix which would enable adodb and mysqli driver to read connection options from the standard mysql configuration file /etc/my.cnf - "Bastien Duclaux" */ foreach($this->optionFlags as $arr) { mysqli_options($this->_connectionID,$arr[0],$arr[1]); } #if (!empty($this->port)) $argHostname .= ":".$this->port; $ok = mysqli_real_connect($this->_connectionID, $argHostname, $argUsername, $argPassword, $argDatabasename, $this->port, $this->socket, $this->clientFlags); if ($ok) { if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } else { if ($this->debug) ADOConnection::outp("Could't connect : " . $this->ErrorMsg()); return false; } } // returns true or false // How to force a persistent connection function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); } // When is this used? Close old connection first? // In _connect(), check $this->forceNewConnect? function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { $this->forceNewConnect = true; return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); } function IfNull( $field, $ifNull ) { return " IFNULL($field, $ifNull) "; // if MySQL } function ServerInfo() { $arr['description'] = $this->GetOne("select version()"); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->Execute('SET AUTOCOMMIT=0'); $this->Execute('BEGIN'); return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->Execute('COMMIT'); $this->Execute('SET AUTOCOMMIT=1'); return true; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->Execute('ROLLBACK'); $this->Execute('SET AUTOCOMMIT=1'); return true; } function RowLock($tables,$where='',$flds='1 as adodb_ignore') { if ($this->transCnt==0) $this->BeginTrans(); if ($where) $where = ' where '.$where; $rs =& $this->Execute("select $flds from $tables $where for update"); return !empty($rs); } // if magic quotes disabled, use mysql_real_escape_string() // From readme.htm: // Quotes a string to be sent to the database. The $magic_quotes_enabled // parameter may look funny, but the idea is if you are quoting a // string extracted from a POST/GET variable, then // pass get_magic_quotes_gpc() as the second parameter. This will // ensure that the variable is not quoted twice, once by qstr and once // by the magic_quotes_gpc. // //Eg. $s = $db->qstr(_GET['name'],get_magic_quotes_gpc()); function qstr($s, $magic_quotes = false) { if (!$magic_quotes) { if (PHP_VERSION >= 5) return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'"; if ($this->replaceQuote[0] == '\\') $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " $s = str_replace('\\"','"',$s); return "'$s'"; } function _insertid() { $result = @mysqli_insert_id($this->_connectionID); if ($result == -1){ if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : " . $this->ErrorMsg()); } return $result; } // Only works for INSERT, UPDATE and DELETE query's function _affectedrows() { $result = @mysqli_affected_rows($this->_connectionID); if ($result == -1) { if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->ErrorMsg()); } return $result; } // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html // Reference on Last_Insert_ID on the recommended way to simulate sequences var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; var $_genSeqSQL = "create table %s (id int not null)"; var $_genSeq2SQL = "insert into %s values (%s)"; var $_dropSeqSQL = "drop table %s"; function CreateSequence($seqname='adodbseq',$startID=1) { if (empty($this->_genSeqSQL)) return false; $u = strtoupper($seqname); $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); } function GenID($seqname='adodbseq',$startID=1) { // post-nuke sets hasGenID to false if (!$this->hasGenID) return false; $getnext = sprintf($this->_genIDSQL,$seqname); $holdtransOK = $this->_transOK; // save the current status $rs = @$this->Execute($getnext); if (!$rs) { if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset $u = strtoupper($seqname); $this->Execute(sprintf($this->_genSeqSQL,$seqname)); $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); $rs = $this->Execute($getnext); } $this->genID = mysqli_insert_id($this->_connectionID); if ($rs) $rs->Close(); return $this->genID; } function &MetaDatabases() { $query = "SHOW DATABASES"; $ret =& $this->Execute($query); if ($ret && is_object($ret)){ $arr = array(); while (!$ret->EOF){ $db = $ret->Fields('Database'); if ($db != 'mysql') $arr[] = $db; $ret->MoveNext(); } return $arr; } return $ret; } function &MetaIndexes ($table, $primary = FALSE) { // save old fetch mode global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } // get index details $rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table)); // restore fetchmode if (isset($savem)) { $this->SetFetchMode($savem); } $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { return $false; } $indexes = array (); // parse index data into array while ($row = $rs->FetchRow()) { if ($primary == FALSE AND $row[2] == 'PRIMARY') { continue; } if (!isset($indexes[$row[2]])) { $indexes[$row[2]] = array( 'unique' => ($row[1] == 0), 'columns' => array() ); } $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4]; } // sort columns by order in the index foreach ( array_keys ($indexes) as $index ) { ksort ($indexes[$index]['columns']); } return $indexes; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = 'DATE_FORMAT('.$col.",'"; $concat = false; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= '%Y'; break; case 'Q': case 'q': $s .= "'),Quarter($col)"; if ($len > $i+1) $s .= ",DATE_FORMAT($col,'"; else $s .= ",('"; $concat = true; break; case 'M': $s .= '%b'; break; case 'm': $s .= '%m'; break; case 'D': case 'd': $s .= '%d'; break; case 'H': $s .= '%H'; break; case 'h': $s .= '%I'; break; case 'i': $s .= '%i'; break; case 's': $s .= '%s'; break; case 'a': case 'A': $s .= '%p'; break; case 'w': $s .= '%w'; break; case 'l': $s .= '%W'; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $ch; break; } } $s.="')"; if ($concat) $s = "CONCAT($s)"; return $s; } // returns concatenated string // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator function Concat() { $s = ""; $arr = func_get_args(); // suggestion by andrew005@mnogo.ru $s = implode(',',$arr); if (strlen($s) > 0) return "CONCAT($s)"; else return ''; } // dayFraction is a day in floating point function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; // return "from_unixtime(unix_timestamp($date)+$fraction)"; } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { $save = $this->metaTablesSQL; if ($showSchema && is_string($showSchema)) { $this->metaTablesSQL .= " from $showSchema"; } if ($mask) { $mask = $this->qstr($mask); $this->metaTablesSQL .= " like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); $this->metaTablesSQL = $save; return $ret; } // "Innox - Juan Carlos Gonzalez" function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) { global $ADODB_FETCH_MODE; if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true; if ( !empty($owner) ) { $table = "$owner.$table"; } $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table)); if ($associative) $create_sql = $a_create_table["Create Table"]; else $create_sql = $a_create_table[1]; $matches = array(); if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false; $foreign_keys = array(); $num_keys = count($matches[0]); for ( $i = 0; $i < $num_keys; $i ++ ) { $my_field = explode('`, `', $matches[1][$i]); $ref_table = $matches[2][$i]; $ref_field = explode('`, `', $matches[3][$i]); if ( $upper ) { $ref_table = strtoupper($ref_table); } $foreign_keys[$ref_table] = array(); $num_fields = count($my_field); for ( $j = 0; $j < $num_fields; $j ++ ) { if ( $associative ) { $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; } else { $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; } } } return $foreign_keys; } function &MetaColumns($table) { $false = false; if (!$this->metaColumnsSQL) return $false; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) return $false; $retarr = array(); while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $type = $rs->fields[1]; // split type into type(length): $fld->scale = null; if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1; } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) { $fld->type = $query_array[1]; $fld->max_length = max(array_map("strlen",explode(",",$query_array[2]))) - 2; // PHP >= 4.0.6 $fld->max_length = ($fld->max_length == 0 ? 1 : $fld->max_length); } else { $fld->type = $type; $fld->max_length = -1; } $fld->not_null = ($rs->fields[2] != 'YES'); $fld->primary_key = ($rs->fields[3] == 'PRI'); $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); $fld->binary = (strpos($type,'blob') !== false); $fld->unsigned = (strpos($type,'unsigned') !== false); if (!$fld->binary) { $d = $rs->fields[4]; if ($d != '' && $d != 'NULL') { $fld->has_default = true; $fld->default_value = $d; } else { $fld->has_default = false; } } if ($save == ADODB_FETCH_NUM) { $retarr[] = $fld; } else { $retarr[strtoupper($fld->name)] = $fld; } $rs->MoveNext(); } $rs->Close(); return $retarr; } // returns true or false function SelectDB($dbName) { // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions if ($this->_connectionID) { $result = @mysqli_select_db($this->_connectionID, $dbName); if (!$result) { ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg()); } return $result; } return false; } // parameters use PostgreSQL convention, not MySQL function &SelectLimit($sql, $nrows = -1, $offset = -1, $inputarr = false, $arg3 = false, $secs = 0) { $offsetStr = ($offset >= 0) ? "$offset," : ''; if ($nrows < 0) $nrows = '18446744073709551615'; if ($secs) $rs =& $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3); else $rs =& $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3); return $rs; } function Prepare($sql) { return $sql; $stmt = $this->_connectionID->prepare($sql); if (!$stmt) { echo $this->ErrorMsg(); return $sql; } return array($sql,$stmt); } // returns queryID or false function _query($sql, $inputarr) { global $ADODB_COUNTRECS; if (is_array($sql)) { $stmt = $sql[1]; $a = ''; foreach($inputarr as $k => $v) { if (is_string($v)) $a .= 's'; else if (is_integer($v)) $a .= 'i'; else $a .= 'd'; } $fnarr = array_merge( array($stmt,$a) , $inputarr); $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr); $ret = mysqli_stmt_execute($stmt); return $ret; } if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) { if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg()); return false; } return $mysql_res; } /* Returns: the last error message from previous database operation */ function ErrorMsg() { if (empty($this->_connectionID)) $this->_errorMsg = @mysqli_connect_error(); else $this->_errorMsg = @mysqli_error($this->_connectionID); return $this->_errorMsg; } /* Returns: the last error number from previous database operation */ function ErrorNo() { if (empty($this->_connectionID)) return @mysqli_connect_errno(); else return @mysqli_errno($this->_connectionID); } // returns true or false function _close() { @mysqli_close($this->_connectionID); $this->_connectionID = false; } /* * Maximum size of C field */ function CharMax() { return 255; } /* * Maximum size of X field */ function TextMax() { return 4294967295; } // this is a set of functions for managing client encoding - very important if the encodings // of your database and your output target (i.e. HTML) don't match // for instance, you may have UTF8 database and server it on-site as latin1 etc. // GetCharSet - get the name of the character set the client is using now // Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported // depends on compile flags of mysql distribution function GetCharSet() { //we will use ADO's builtin property charSet if (!method_exists($this->_connectionID,'character_set_name')) return false; $this->charSet = @$this->_connectionID->character_set_name(); if (!$this->charSet) { return false; } else { return $this->charSet; } } // SetCharSet - switch the client encoding function SetCharSet($charset_name) { if (!method_exists($this->_connectionID,'set_charset')) return false; if ($this->charSet !== $charset_name) { $if = @$this->_connectionID->set_charset($charset_name); if ($if == "0" & $this->GetCharSet() == $charset_name) { return true; } else return false; } else return true; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_mysqli extends ADORecordSet{ var $databaseType = "mysqli"; var $canSeek = true; function ADORecordSet_mysqli($queryID, $mode = false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQLI_NUM; break; case ADODB_FETCH_ASSOC: $this->fetchMode = MYSQLI_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = MYSQLI_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1; $this->_numOfFields = @mysqli_num_fields($this->_queryID); } /* 1 = MYSQLI_NOT_NULL_FLAG 2 = MYSQLI_PRI_KEY_FLAG 4 = MYSQLI_UNIQUE_KEY_FLAG 8 = MYSQLI_MULTIPLE_KEY_FLAG 16 = MYSQLI_BLOB_FLAG 32 = MYSQLI_UNSIGNED_FLAG 64 = MYSQLI_ZEROFILL_FLAG 128 = MYSQLI_BINARY_FLAG 256 = MYSQLI_ENUM_FLAG 512 = MYSQLI_AUTO_INCREMENT_FLAG 1024 = MYSQLI_TIMESTAMP_FLAG 2048 = MYSQLI_SET_FLAG 32768 = MYSQLI_NUM_FLAG 16384 = MYSQLI_PART_KEY_FLAG 32768 = MYSQLI_GROUP_FLAG 65536 = MYSQLI_UNIQUE_FLAG 131072 = MYSQLI_BINCMP_FLAG */ function &FetchField($fieldOffset = -1) { $fieldnr = $fieldOffset; if ($fieldOffset != -1) { $fieldOffset = mysqli_field_seek($this->_queryID, $fieldnr); } $o = mysqli_fetch_field($this->_queryID); /* Properties of an ADOFieldObject as set by MetaColumns */ $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG; $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG; $o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG; $o->binary = $o->flags & MYSQLI_BINARY_FLAG; // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */ $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG; return $o; } function &GetRowAssoc($upper = true) { if ($this->fetchMode == MYSQLI_ASSOC && !$upper) return $this->fields; $row =& ADORecordSet::GetRowAssoc($upper); return $row; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode != MYSQLI_NUM) return @$this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i = 0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _seek($row) { if ($this->_numOfRows == 0) return false; if ($row < 0) return false; mysqli_data_seek($this->_queryID, $row); $this->EOF = false; return true; } // 10% speedup to move MoveNext to child class // This is the only implementation that works now (23-10-2003). // Other functions return no or the wrong results. function MoveNext() { if ($this->EOF) return false; $this->_currentRow++; $this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode); if (is_array($this->fields)) return true; $this->EOF = true; return false; } function _fetch() { $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode); return is_array($this->fields); } function _close() { mysqli_free_result($this->_queryID); $this->_queryID = false; } /* 0 = MYSQLI_TYPE_DECIMAL 1 = MYSQLI_TYPE_CHAR 1 = MYSQLI_TYPE_TINY 2 = MYSQLI_TYPE_SHORT 3 = MYSQLI_TYPE_LONG 4 = MYSQLI_TYPE_FLOAT 5 = MYSQLI_TYPE_DOUBLE 6 = MYSQLI_TYPE_NULL 7 = MYSQLI_TYPE_TIMESTAMP 8 = MYSQLI_TYPE_LONGLONG 9 = MYSQLI_TYPE_INT24 10 = MYSQLI_TYPE_DATE 11 = MYSQLI_TYPE_TIME 12 = MYSQLI_TYPE_DATETIME 13 = MYSQLI_TYPE_YEAR 14 = MYSQLI_TYPE_NEWDATE 247 = MYSQLI_TYPE_ENUM 248 = MYSQLI_TYPE_SET 249 = MYSQLI_TYPE_TINY_BLOB 250 = MYSQLI_TYPE_MEDIUM_BLOB 251 = MYSQLI_TYPE_LONG_BLOB 252 = MYSQLI_TYPE_BLOB 253 = MYSQLI_TYPE_VAR_STRING 254 = MYSQLI_TYPE_STRING 255 = MYSQLI_TYPE_GEOMETRY */ function MetaType($t, $len = -1, $fieldobj = false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } $len = -1; // mysql max_length is not accurate switch (strtoupper($t)) { case 'STRING': case 'CHAR': case 'VARCHAR': case 'TINYBLOB': case 'TINYTEXT': case 'ENUM': case 'SET': case MYSQLI_TYPE_TINY_BLOB : case MYSQLI_TYPE_CHAR : case MYSQLI_TYPE_STRING : case MYSQLI_TYPE_ENUM : case MYSQLI_TYPE_SET : case 253 : if ($len <= $this->blobSize) return 'C'; case 'TEXT': case 'LONGTEXT': case 'MEDIUMTEXT': return 'X'; // php_mysql extension always returns 'blob' even if 'text' // so we have to check whether binary... case 'IMAGE': case 'LONGBLOB': case 'BLOB': case 'MEDIUMBLOB': case MYSQLI_TYPE_BLOB : case MYSQLI_TYPE_LONG_BLOB : case MYSQLI_TYPE_MEDIUM_BLOB : return !empty($fieldobj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': case MYSQLI_TYPE_DATE : case MYSQLI_TYPE_YEAR : return 'D'; case 'TIME': case 'DATETIME': case 'TIMESTAMP': case MYSQLI_TYPE_DATETIME : case MYSQLI_TYPE_NEWDATE : case MYSQLI_TYPE_TIME : case MYSQLI_TYPE_TIMESTAMP : return 'T'; case 'INT': case 'INTEGER': case 'BIGINT': case 'TINYINT': case 'MEDIUMINT': case 'SMALLINT': case MYSQLI_TYPE_INT24 : case MYSQLI_TYPE_LONG : case MYSQLI_TYPE_LONGLONG : case MYSQLI_TYPE_SHORT : case MYSQLI_TYPE_TINY : if (!empty($fieldobj->primary_key)) return 'R'; return 'I'; // Added floating-point types // Maybe not necessery. case 'FLOAT': case 'DOUBLE': // case 'DOUBLE PRECISION': case 'DECIMAL': case 'DEC': case 'FIXED': default: //if (!is_numeric($t)) echo "

--- Error in type matching $t -----

"; return 'N'; } } // function } // rs class } ?>phpgacl-3.3.7/adodb/drivers/adodb-ado_access.inc.php0100644025754300001440000000262310476657245021237 0ustar ipsousers= 5) include(ADODB_DIR."/drivers/adodb-ado5.inc.php"); else include(ADODB_DIR."/drivers/adodb-ado.inc.php"); } class ADODB_ado_access extends ADODB_ado { var $databaseType = 'ado_access'; var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE var $fmtDate = "#Y-m-d#"; var $fmtTimeStamp = "#Y-m-d h:i:sA#";// note no comma var $sysDate = "FORMAT(NOW,'yyyy-mm-dd')"; var $sysTimeStamp = 'NOW'; var $hasTransactions = false; function ADODB_ado_access() { $this->ADODB_ado(); } function BeginTrans() { return false;} function CommitTrans() { return false;} function RollbackTrans() { return false;} } class ADORecordSet_ado_access extends ADORecordSet_ado { var $databaseType = "ado_access"; function ADORecordSet_ado_access($id,$mode=false) { return $this->ADORecordSet_ado($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-netezza.inc.php0100644025754300001440000001204710476657245020634 0ustar ipsousers 0 ORDER BY attnum"; var $metaColumnsSQL1 = "SELECT attname, atttype FROM _v_relation_column_def WHERE name = '%s' AND attnum > 0 ORDER BY attnum"; // netezza doesn't have keys. it does have distributions, so maybe this is // something that can be pulled from the system tables var $metaKeySQL = ""; var $hasAffectedRows = true; var $hasLimit = true; var $true = 't'; // string that represents TRUE for a database var $false = 'f'; // string that represents FALSE for a database var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt. var $ansiOuter = true; var $autoRollback = true; // apparently pgsql does not autorollback properly before 4.3.4 // http://bugs.php.net/bug.php?id=25404 function ADODB_netezza() { } function &MetaColumns($table,$upper=true) { // Changed this function to support Netezza which has no concept of keys // could posisbly work on other things from the system table later. global $ADODB_FETCH_MODE; $table = strtolower($table); $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false) return false; $retarr = array(); while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; // since we're returning type and length as one string, // split them out here. if ($first = strstr($rs->fields[1], "(")) { $fld->max_length = trim($first, "()"); } else { $fld->max_length = -1; } if ($first = strpos($rs->fields[1], "(")) { $fld->type = substr($rs->fields[1], 0, $first); } else { $fld->type = $rs->fields[1]; } switch ($fld->type) { case "byteint": case "boolean": $fld->max_length = 1; break; case "smallint": $fld->max_length = 2; break; case "integer": case "numeric": case "date": $fld->max_length = 4; break; case "bigint": case "time": case "timestamp": $fld->max_length = 8; break; case "timetz": case "time with time zone": $fld->max_length = 12; break; } if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[($upper) ? strtoupper($fld->name) : $fld->name] = $fld; $rs->MoveNext(); } $rs->Close(); return $retarr; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_netezza extends ADORecordSet_postgres64 { var $databaseType = "netezza"; var $canSeek = true; function ADORecordSet_netezza($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = PGSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } // _initrs modified to disable blob handling function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($this->_queryID):-1; $this->_numOfFields = @pg_numfields($this->_queryID); } } ?> phpgacl-3.3.7/adodb/drivers/adodb-proxy.inc.php0100644025754300001440000000145210476657245020333 0ustar ipsousersADORecordset($id,$mode); } }; } // define ?>phpgacl-3.3.7/adodb/drivers/adodb-pdo.inc.php0100644025754300001440000003203510476657245017735 0ustar ipsousers_bindInputArray = true; #$parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true); } function ServerInfo() { return ADOConnection::ServerInfo(); } function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $ret = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $ret; } function MetaTables() { return false; } function MetaColumns() { return false; } } class ADODB_pdo extends ADOConnection { var $databaseType = "pdo"; var $dataProvider = "pdo"; var $fmtDate = "'Y-m-d'"; var $fmtTimeStamp = "'Y-m-d, h:i:sA'"; var $replaceQuote = "''"; // string to use to replace quotes var $hasAffectedRows = true; var $_bindInputArray = true; var $_genSeqSQL = "create table %s (id integer)"; var $_autocommit = true; var $_haserrorfunctions = true; var $_lastAffectedRows = 0; var $_errormsg = false; var $_errorno = false; var $dsnType = ''; var $stmt = false; function ADODB_pdo() { } function _UpdatePDO() { $d = &$this->_driver; $this->fmtDate = $d->fmtDate; $this->fmtTimeStamp = $d->fmtTimeStamp; $this->replaceQuote = $d->replaceQuote; $this->sysDate = $d->sysDate; $this->sysTimeStamp = $d->sysTimeStamp; $this->random = $d->random; $this->concat_operator = $d->concat_operator; $this->nameQuote = $d->nameQuote; $d->_init($this); } function Time() { if (!empty($this->_driver->_hasdual)) $sql = "select $this->sysTimeStamp from dual"; else $sql = "select $this->sysTimeStamp"; $rs =& $this->_Execute($sql); if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields)); return false; } // returns true or false function _connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persist=false) { $at = strpos($argDSN,':'); $this->dsnType = substr($argDSN,0,$at); if ($argDatabasename) { $argDSN .= ';dbname='.$argDatabasename; } try { $this->_connectionID = new PDO($argDSN, $argUsername, $argPassword); } catch (Exception $e) { $this->_connectionID = false; $this->_errorno = -1; //var_dump($e); $this->_errormsg = 'Connection attempt failed: '.$e->getMessage(); return false; } if ($this->_connectionID) { switch(ADODB_ASSOC_CASE){ case 0: $m = PDO::CASE_LOWER; break; case 1: $m = PDO::CASE_UPPER; break; default: case 2: $m = PDO::CASE_NATURAL; break; } //$this->_connectionID->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT ); $this->_connectionID->setAttribute(PDO::ATTR_CASE,$m); $class = 'ADODB_pdo_'.$this->dsnType; //$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true); switch($this->dsnType) { case 'oci': case 'mysql': case 'pgsql': case 'mssql': include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php'); break; } if (class_exists($class)) $this->_driver = new $class(); else $this->_driver = new ADODB_pdo_base(); $this->_driver->_connectionID = $this->_connectionID; $this->_UpdatePDO(); return true; } $this->_driver = new ADODB_pdo_base(); return false; } // returns true or false function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argDSN, $argUsername, $argPassword, $argDatabasename, true); } /*------------------------------------------------------------------------------*/ function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $save = $this->_driver->fetchMode; $this->_driver->fetchMode = $this->fetchMode; $this->_driver->debug = $this->debug; $ret = $this->_driver->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); $this->_driver->fetchMode = $save; return $ret; } function ServerInfo() { return $this->_driver->ServerInfo(); } function MetaTables($ttype=false,$showSchema=false,$mask=false) { return $this->_driver->MetaTables($ttype,$showSchema,$mask); } function MetaColumns($table,$normalize=true) { return $this->_driver->MetaColumns($table,$normalize); } function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) { $obj = $stmt[1]; if ($type) $obj->bindParam($name,$var,$type,$maxLen); else $obj->bindParam($name, $var); } function ErrorMsg() { if ($this->_errormsg !== false) return $this->_errormsg; if (!empty($this->_stmt)) $arr = $this->_stmt->errorInfo(); else if (!empty($this->_connectionID)) $arr = $this->_connectionID->errorInfo(); else return 'No Connection Established'; if ($arr) { if (sizeof($arr)<2) return ''; if ((integer)$arr[1]) return $arr[2]; else return ''; } else return '-1'; } function ErrorNo() { if ($this->_errorno !== false) return $this->_errorno; if (!empty($this->_stmt)) $err = $this->_stmt->errorCode(); else if (!empty($this->_connectionID)) { $arr = $this->_connectionID->errorInfo(); if (isset($arr[0])) $err = $arr[0]; else $err = -1; } else return 0; if ($err == '00000') return 0; // allows empty check return $err; } function BeginTrans() { if (!$this->hasTransactions) return false; if ($this->transOff) return true; $this->transCnt += 1; $this->_autocommit = false; $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,false); return $this->_connectionID->beginTransaction(); } function CommitTrans($ok=true) { if (!$this->hasTransactions) return false; if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = $this->_connectionID->commit(); $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true); return $ret; } function RollbackTrans() { if (!$this->hasTransactions) return false; if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = $this->_connectionID->rollback(); $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true); return $ret; } function Prepare($sql) { $this->_stmt = $this->_connectionID->prepare($sql); if ($this->_stmt) return array($sql,$this->_stmt); return false; } function PrepareStmt($sql) { $stmt = $this->_connectionID->prepare($sql); if (!$stmt) return false; $obj = new ADOPDOStatement($stmt,$this); return $obj; } /* returns queryID or false */ function _query($sql,$inputarr=false) { if (is_array($sql)) { $stmt = $sql[1]; } else { $stmt = $this->_connectionID->prepare($sql); } #adodb_backtrace(); #var_dump($this->_bindInputArray); if ($stmt) { $this->_driver->debug = $this->debug; if ($inputarr) $ok = $stmt->execute($inputarr); else $ok = $stmt->execute(); } $this->_errormsg = false; $this->_errorno = false; if ($ok) { $this->_stmt = $stmt; return $stmt; } if ($stmt) { $arr = $stmt->errorinfo(); if ((integer)$arr[1]) { $this->_errormsg = $arr[2]; $this->_errorno = $arr[1]; } } else { $this->_errormsg = false; $this->_errorno = false; } return false; } // returns true or false function _close() { $this->_stmt = false; return true; } function _affectedrows() { return ($this->_stmt) ? $this->_stmt->rowCount() : 0; } function _insertid() { return ($this->_connectionID) ? $this->_connectionID->lastInsertId() : 0; } } class ADOPDOStatement { var $databaseType = "pdo"; var $dataProvider = "pdo"; var $_stmt; var $_connectionID; function ADOPDOStatement($stmt,$connection) { $this->_stmt = $stmt; $this->_connectionID = $connection; } function Execute($inputArr=false) { $savestmt = $this->_connectionID->_stmt; $rs = $this->_connectionID->Execute(array(false,$this->_stmt),$inputArr); $this->_connectionID->_stmt = $savestmt; return $rs; } function InParameter(&$var,$name,$maxLen=4000,$type=false) { if ($type) $this->_stmt->bindParam($name,$var,$type,$maxLen); else $this->_stmt->bindParam($name, $var); } function Affected_Rows() { return ($this->_stmt) ? $this->_stmt->rowCount() : 0; } function ErrorMsg() { if ($this->_stmt) $arr = $this->_stmt->errorInfo(); else $arr = $this->_connectionID->errorInfo(); if (is_array($arr)) { if ((integer) $arr[0] && isset($arr[2])) return $arr[2]; else return ''; } else return '-1'; } function NumCols() { return ($this->_stmt) ? $this->_stmt->columnCount() : 0; } function ErrorNo() { if ($this->_stmt) return $this->_stmt->errorCode(); else return $this->_connectionID->errorInfo(); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_pdo extends ADORecordSet { var $bind = false; var $databaseType = "pdo"; var $dataProvider = "pdo"; function ADORecordSet_pdo($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->adodbFetchMode = $mode; switch($mode) { case ADODB_FETCH_NUM: $mode = PDO::FETCH_NUM; break; case ADODB_FETCH_ASSOC: $mode = PDO::FETCH_ASSOC; break; case ADODB_FETCH_BOTH: default: $mode = PDO::FETCH_BOTH; break; } $this->fetchMode = $mode; $this->_queryID = $id; $this->ADORecordSet($id); } function Init() { if ($this->_inited) return; $this->_inited = true; if ($this->_queryID) @$this->_initrs(); else { $this->_numOfRows = 0; $this->_numOfFields = 0; } if ($this->_numOfRows != 0 && $this->_currentRow == -1) { $this->_currentRow = 0; if ($this->EOF = ($this->_fetch() === false)) { $this->_numOfRows = 0; // _numOfRows could be -1 } } else { $this->EOF = true; } } function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS) ? @$this->_queryID->rowCount() : -1; if (!$this->_numOfRows) $this->_numOfRows = -1; $this->_numOfFields = $this->_queryID->columnCount(); } // returns the field object function &FetchField($fieldOffset = -1) { $off=$fieldOffset+1; // offsets begin at 1 $o= new ADOFieldObject(); $arr = @$this->_queryID->getColumnMeta($fieldOffset); if (!$arr) { $o->name = 'bad getColumnMeta()'; $o->max_length = -1; $o->type = 'VARCHAR'; $o->precision = 0; # $false = false; return $o; } //adodb_pr($arr); $o->name = $arr['name']; if (isset($arr['native_type'])) $o->type = $arr['native_type']; else $o->type = adodb_pdo_type($arr['pdo_type']); $o->max_length = $arr['len']; $o->precision = $arr['precision']; if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); return $o; } function _seek($row) { return false; } function _fetch() { if (!$this->_queryID) return false; $this->fields = $this->_queryID->fetch($this->fetchMode); return !empty($this->fields); } function _close() { $this->_queryID = false; } function Fields($colname) { if ($this->adodbFetchMode != ADODB_FETCH_NUM) return @$this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-odbc_db2.inc.php0100644025754300001440000002222110476657431020602 0ustar ipsouserscurMode = SQL_CUR_USE_ODBC; $db->Connect($dsn, $userid, $pwd); USING CLI INTERFACE =================== I have had reports that the $host and $database params have to be reversed in Connect() when using the CLI interface. From Halmai Csongor csongor.halmai#nexum.hu: > The symptom is that if I change the database engine from postgres or any other to DB2 then the following > connection command becomes wrong despite being described this version to be correct in the docs. > > $connection_object->Connect( $DATABASE_HOST, $DATABASE_AUTH_USER_NAME, $DATABASE_AUTH_PASSWORD, $DATABASE_NAME ) > > In case of DB2 I had to swap the first and last arguments in order to connect properly. System Error 5 ============== IF you get a System Error 5 when trying to Connect/Load, it could be a permission problem. Give the user connecting to DB2 full rights to the DB2 SQLLIB directory, and place the user in the DBUSERS group. */ // security - hide paths if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_ODBC_DB2')){ define('ADODB_ODBC_DB2',1); class ADODB_ODBC_DB2 extends ADODB_odbc { var $databaseType = "db2"; var $concat_operator = '||'; var $sysTime = 'CURRENT TIME'; var $sysDate = 'CURRENT DATE'; var $sysTimeStamp = 'CURRENT TIMESTAMP'; // The complete string representation of a timestamp has the form // yyyy-mm-dd-hh.mm.ss.nnnnnn. var $fmtTimeStamp = "'Y-m-d-H.i.s'"; var $ansiOuter = true; var $identitySQL = 'values IDENTITY_VAL_LOCAL()'; var $_bindInputArray = true; var $hasInsertID = true; var $rsPrefix = 'ADORecordset_odbc_'; function ADODB_DB2() { if (strncmp(PHP_OS,'WIN',3) === 0) $this->curmode = SQL_CUR_USE_ODBC; $this->ADODB_odbc(); } function IfNull( $field, $ifNull ) { return " COALESCE($field, $ifNull) "; // if DB2 UDB } function ServerInfo() { //odbc_setoption($this->_connectionID,1,101 /*SQL_ATTR_ACCESS_MODE*/, 1 /*SQL_MODE_READ_ONLY*/); $vers = $this->GetOne('select versionnumber from sysibm.sysversions'); //odbc_setoption($this->_connectionID,1,101, 0 /*SQL_MODE_READ_WRITE*/); return array('description'=>'DB2 ODBC driver', 'version'=>$vers); } function _insertid() { return $this->GetOne($this->identitySQL); } function RowLock($tables,$where,$flds='1 as ignore') { if ($this->_autocommit) $this->BeginTrans(); return $this->GetOne("select $flds from $tables where $where for update"); } function &MetaTables($ttype=false,$showSchema=false, $qtable="%", $qschema="%") { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = odbc_tables($this->_connectionID, "", $qschema, $qtable, ""); $rs = new ADORecordSet_odbc($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) { $false = false; return $false; } $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr =& $rs->GetArray(); //print_r($arr); $rs->Close(); $arr2 = array(); if ($ttype) { $isview = strncmp($ttype,'V',1) === 0; } for ($i=0; $i < sizeof($arr); $i++) { if (!$arr[$i][2]) continue; if (strncmp($arr[$i][1],'SYS',3) === 0) continue; $type = $arr[$i][3]; if ($showSchema) $arr[$i][2] = $arr[$i][1].'.'.$arr[$i][2]; if ($ttype) { if ($isview) { if (strncmp($type,'V',1) === 0) $arr2[] = $arr[$i][2]; } else if (strncmp($type,'T',1) === 0) $arr2[] = $arr[$i][2]; } else if (strncmp($type,'S',1) !== 0) $arr2[] = $arr[$i][2]; } return $arr2; } function &MetaIndexes ($table, $primary = FALSE, $owner=false) { // save old fetch mode global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $false = false; // get index details $table = strtoupper($table); $SQL="SELECT NAME, UNIQUERULE, COLNAMES FROM SYSIBM.SYSINDEXES WHERE TBNAME='$table'"; if ($primary) $SQL.= " AND UNIQUERULE='P'"; $rs = $this->Execute($SQL); if (!is_object($rs)) { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; return $false; } $indexes = array (); // parse index data into array while ($row = $rs->FetchRow()) { $indexes[$row[0]] = array( 'unique' => ($row[1] == 'U' || $row[1] == 'P'), 'columns' => array() ); $cols = ltrim($row[2],'+'); $indexes[$row[0]]['columns'] = explode('+', $cols); } if (isset($savem)) { $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; } return $indexes; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { // use right() and replace() ? if (!$col) $col = $this->sysDate; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '||'; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= "char(year($col))"; break; case 'M': $s .= "substr(monthname($col),1,3)"; break; case 'm': $s .= "right(digits(month($col)),2)"; break; case 'D': case 'd': $s .= "right(digits(day($col)),2)"; break; case 'H': case 'h': if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)"; else $s .= "''"; break; case 'i': case 'I': if ($col != $this->sysDate) $s .= "right(digits(minute($col)),2)"; else $s .= "''"; break; case 'S': case 's': if ($col != $this->sysDate) $s .= "right(digits(second($col)),2)"; else $s .= "''"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); } } return $s; } function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false) { $nrows = (integer) $nrows; if ($offset <= 0) { // could also use " OPTIMIZE FOR $nrows ROWS " if ($nrows >= 0) $sql .= " FETCH FIRST $nrows ROWS ONLY "; $rs =& $this->Execute($sql,$inputArr); } else { if ($offset > 0 && $nrows < 0); else { $nrows += $offset; $sql .= " FETCH FIRST $nrows ROWS ONLY "; } $rs =& ADOConnection::SelectLimit($sql,-1,$offset,$inputArr); } return $rs; } }; class ADORecordSet_odbc_db2 extends ADORecordSet_odbc { var $databaseType = "db2"; function ADORecordSet_db2($id,$mode=false) { $this->ADORecordSet_odbc($id,$mode); } function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'VARCHAR': case 'CHAR': case 'CHARACTER': case 'C': if ($len <= $this->blobSize) return 'C'; case 'LONGCHAR': case 'TEXT': case 'CLOB': case 'DBCLOB': // double-byte case 'X': return 'X'; case 'BLOB': case 'GRAPHIC': case 'VARGRAPHIC': return 'B'; case 'DATE': case 'D': return 'D'; case 'TIME': case 'TIMESTAMP': case 'T': return 'T'; //case 'BOOLEAN': //case 'BIT': // return 'L'; //case 'COUNTER': // return 'R'; case 'INT': case 'INTEGER': case 'BIGINT': case 'SMALLINT': case 'I': return 'I'; default: return 'N'; } } } } //define ?>phpgacl-3.3.7/adodb/drivers/adodb-odbc_mssql.inc.php0100644025754300001440000001725710476657245021312 0ustar ipsousersADODB_odbc(); //$this->curmode = SQL_CUR_USE_ODBC; } // crashes php... function ServerInfo() { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $row = $this->GetRow("execute sp_server_info 2"); $ADODB_FETCH_MODE = $save; if (!is_array($row)) return false; $arr['description'] = $row[2]; $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } function IfNull( $field, $ifNull ) { return " ISNULL($field, $ifNull) "; // if MS SQL Server } function _insertid() { // SCOPE_IDENTITY() // Returns the last IDENTITY value inserted into an IDENTITY column in // the same scope. A scope is a module -- a stored procedure, trigger, // function, or batch. Thus, two statements are in the same scope if // they are in the same stored procedure, function, or batch. return $this->GetOne($this->identitySQL); } function MetaForeignKeys($table, $owner=false, $upper=false) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $table = $this->qstr(strtoupper($table)); $sql = "select object_name(constid) as constraint_name, col_name(fkeyid, fkey) as column_name, object_name(rkeyid) as referenced_table_name, col_name(rkeyid, rkey) as referenced_column_name from sysforeignkeys where upper(object_name(fkeyid)) = $table order by constraint_name, referenced_table_name, keyno"; $constraints =& $this->GetArray($sql); $ADODB_FETCH_MODE = $save; $arr = false; foreach($constraints as $constr) { //print_r($constr); $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3]; } if (!$arr) return false; $arr2 = false; foreach($arr as $k => $v) { foreach($v as $a => $b) { if ($upper) $a = strtoupper($a); $arr2[$a] = $b; } } return $arr2; } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { if ($mask) {$this->debug=1; $save = $this->metaTablesSQL; $mask = $this->qstr($mask); $this->metaTablesSQL .= " AND name like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } function &MetaColumns($table) { $arr = ADOConnection::MetaColumns($table); return $arr; } function _query($sql,$inputarr) { if (is_string($sql)) $sql = str_replace('||','+',$sql); return ADODB_odbc::_query($sql,$inputarr); } function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET TRANSACTION ".$transaction_mode); } // "Stein-Aksel Basma" // tested with MSSQL 2000 function &MetaPrimaryKeys($table) { global $ADODB_FETCH_MODE; $schema = ''; $this->_findschema($table,$schema); //if (!$schema) $schema = $this->database; if ($schema) $schema = "and k.table_catalog like '$schema%'"; $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k, information_schema.table_constraints tc where tc.constraint_name = k.constraint_name and tc.constraint_type = 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position "; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $a = $this->GetCol($sql); $ADODB_FETCH_MODE = $savem; if ($a && sizeof($a)>0) return $a; $false = false; return $false; } function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { if ($nrows > 0 && $offset <= 0) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql); $rs =& $this->Execute($sql,$inputarr); } else $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $rs; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = ''; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { if ($s) $s .= '+'; $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= "datename(yyyy,$col)"; break; case 'M': $s .= "convert(char(3),$col,0)"; break; case 'm': $s .= "replace(str(month($col),2),' ','0')"; break; case 'Q': case 'q': $s .= "datename(quarter,$col)"; break; case 'D': case 'd': $s .= "replace(str(day($col),2),' ','0')"; break; case 'h': $s .= "substring(convert(char(14),$col,0),13,2)"; break; case 'H': $s .= "replace(str(datepart(hh,$col),2),' ','0')"; break; case 'i': $s .= "replace(str(datepart(mi,$col),2),' ','0')"; break; case 's': $s .= "replace(str(datepart(ss,$col),2),' ','0')"; break; case 'a': case 'A': $s .= "substring(convert(char(19),$col,0),18,2)"; break; default: if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } $s .= $this->qstr($ch); break; } } return $s; } } class ADORecordSet_odbc_mssql extends ADORecordSet_odbc { var $databaseType = 'odbc_mssql'; function ADORecordSet_odbc_mssql($id,$mode=false) { return $this->ADORecordSet_odbc($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-ado5.inc.php0100644025754300001440000004012210476657245017777 0ustar ipsousers_affectedRows = new VARIANT; } function ServerInfo() { if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider; return array('description' => $desc, 'version' => ''); } function _affectedrows() { if (PHP_VERSION >= 5) return $this->_affectedRows; return $this->_affectedRows->value; } // you can also pass a connection string like this: // // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB'); function _connect($argHostname, $argUsername, $argPassword,$argDBorProvider, $argProvider= '') { // two modes // - if $argProvider is empty, we assume that $argDBorProvider holds provider -- this is for backward compat // - if $argProvider is not empty, then $argDBorProvider holds db if ($argProvider) { $argDatabasename = $argDBorProvider; } else { $argDatabasename = ''; if ($argDBorProvider) $argProvider = $argDBorProvider; else $argProvider = 'MSDASQL'; } try { $u = 'UID'; $p = 'PWD'; if (!empty($this->charPage)) $dbc = new COM('ADODB.Connection',null,$this->charPage); else $dbc = new COM('ADODB.Connection'); if (! $dbc) return false; /* special support if provider is mssql or access */ if ($argProvider=='mssql') { $u = 'User Id'; //User parameter name for OLEDB $p = 'Password'; $argProvider = "SQLOLEDB"; // SQL Server Provider // not yet //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename"; //use trusted conection for SQL if username not specified if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes"; } else if ($argProvider=='access') $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider if ($argProvider) $dbc->Provider = $argProvider; if ($argProvider) $argHostname = "PROVIDER=$argProvider;DRIVER={SQL Server};SERVER=$argHostname"; if ($argDatabasename) $argHostname .= ";DATABASE=$argDatabasename"; if ($argUsername) $argHostname .= ";$u=$argUsername"; if ($argPassword)$argHostname .= ";$p=$argPassword"; if ($this->debug) ADOConnection::outp( "Host=".$argHostname."
\n version=$dbc->version"); // @ added below for php 4.0.1 and earlier @$dbc->Open((string) $argHostname); $this->_connectionID = $dbc; $dbc->CursorLocation = $this->_cursor_location; return $dbc->State > 0; } catch (exception $e) { } return false; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL') { return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider); } /* adSchemaCatalogs = 1, adSchemaCharacterSets = 2, adSchemaCollations = 3, adSchemaColumns = 4, adSchemaCheckConstraints = 5, adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, adSchemaReferentialContraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, adSchemaColumnPrivileges = 13, adSchemaTablePrivileges = 14, adSchemaUsagePrivileges = 15, adSchemaProcedures = 16, adSchemaSchemata = 17, adSchemaSQLLanguages = 18, adSchemaStatistics = 19, adSchemaTables = 20, adSchemaTranslations = 21, adSchemaProviderTypes = 22, adSchemaViews = 23, adSchemaViewColumnUsage = 24, adSchemaViewTableUsage = 25, adSchemaProcedureParameters = 26, adSchemaForeignKeys = 27, adSchemaPrimaryKeys = 28, adSchemaProcedureColumns = 29, adSchemaDBInfoKeywords = 30, adSchemaDBInfoLiterals = 31, adSchemaCubes = 32, adSchemaDimensions = 33, adSchemaHierarchies = 34, adSchemaLevels = 35, adSchemaMeasures = 36, adSchemaProperties = 37, adSchemaMembers = 38 */ function &MetaTables() { $arr= array(); $dbc = $this->_connectionID; $adors=@$dbc->OpenSchema(20);//tables if ($adors){ $f = $adors->Fields(2);//table/view name $t = $adors->Fields(3);//table type while (!$adors->EOF){ $tt=substr($t->value,0,6); if ($tt!='SYSTEM' && $tt !='ACCESS') $arr[]=$f->value; //print $f->value . ' ' . $t->value.'
'; $adors->MoveNext(); } $adors->Close(); } return $arr; } function &MetaColumns($table) { $table = strtoupper($table); $arr= array(); $dbc = $this->_connectionID; $adors=@$dbc->OpenSchema(4);//tables if ($adors){ $t = $adors->Fields(2);//table/view name while (!$adors->EOF){ if (strtoupper($t->Value) == $table) { $fld = new ADOFieldObject(); $c = $adors->Fields(3); $fld->name = $c->Value; $fld->type = 'CHAR'; // cannot discover type in ADO! $fld->max_length = -1; $arr[strtoupper($fld->name)]=$fld; } $adors->MoveNext(); } $adors->Close(); } return $arr; } /* returns queryID or false */ function &_query($sql,$inputarr=false) { try { // In PHP5, all COM errors are exceptions, so to maintain old behaviour... $dbc = $this->_connectionID; // return rs $false = false; if ($inputarr) { if (!empty($this->charPage)) $oCmd = new COM('ADODB.Command',null,$this->charPage); else $oCmd = new COM('ADODB.Command'); $oCmd->ActiveConnection = $dbc; $oCmd->CommandText = $sql; $oCmd->CommandType = 1; foreach($inputarr as $val) { // name, type, direction 1 = input, len, $this->adoParameterType = 130; $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val); //print $p->Type.' '.$p->value; $oCmd->Parameters->Append($p); } $p = false; $rs = $oCmd->Execute(); $e = $dbc->Errors; if ($dbc->Errors->Count > 0) return $false; return $rs; } $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option); if ($dbc->Errors->Count > 0) return $false; if (! $rs) return $false; if ($rs->State == 0) { $true = true; return $true; // 0 = adStateClosed means no records returned } return $rs; } catch (exception $e) { } return $false; } function BeginTrans() { if ($this->transOff) return true; if (isset($this->_thisTransactions)) if (!$this->_thisTransactions) return false; else { $o = $this->_connectionID->Properties("Transaction DDL"); $this->_thisTransactions = $o ? true : false; if (!$o) return false; } @$this->_connectionID->BeginTrans(); $this->transCnt += 1; return true; } function CommitTrans($ok=true) { if (!$ok) return $this->RollbackTrans(); if ($this->transOff) return true; @$this->_connectionID->CommitTrans(); if ($this->transCnt) @$this->transCnt -= 1; return true; } function RollbackTrans() { if ($this->transOff) return true; @$this->_connectionID->RollbackTrans(); if ($this->transCnt) @$this->transCnt -= 1; return true; } /* Returns: the last error message from previous database operation */ function ErrorMsg() { if (!$this->_connectionID) return "No connection established"; $errmsg = ''; try { $errc = $this->_connectionID->Errors; if (!$errc) return "No Errors object found"; if ($errc->Count == 0) return ''; $err = $errc->Item($errc->Count-1); $errmsg = $err->Description; }catch(exception $e) { } return $errmsg; } function ErrorNo() { $errc = $this->_connectionID->Errors; if ($errc->Count == 0) return 0; $err = $errc->Item($errc->Count-1); return $err->NativeError; } // returns true or false function _close() { if ($this->_connectionID) $this->_connectionID->Close(); $this->_connectionID = false; return true; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_ado extends ADORecordSet { var $bind = false; var $databaseType = "ado"; var $dataProvider = "ado"; var $_tarr = false; // caches the types var $_flds; // and field objects var $canSeek = true; var $hideErrors = true; function ADORecordSet_ado($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; return $this->ADORecordSet($id,$mode); } // returns the field object function &FetchField($fieldOffset = -1) { $off=$fieldOffset+1; // offsets begin at 1 $o= new ADOFieldObject(); $rs = $this->_queryID; $f = $rs->Fields($fieldOffset); $o->name = $f->Name; $t = $f->Type; $o->type = $this->MetaType($t); $o->max_length = $f->DefinedSize; $o->ado_type = $t; //print "off=$off name=$o->name type=$o->type len=$o->max_length
"; return $o; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _initrs() { $rs = $this->_queryID; $this->_numOfRows = $rs->RecordCount; $f = $rs->Fields; $this->_numOfFields = $f->Count; } // should only be used to move forward as we normally use forward-only cursors function _seek($row) { $rs = $this->_queryID; // absoluteposition doesn't work -- my maths is wrong ? // $rs->AbsolutePosition->$row-2; // return true; if ($this->_currentRow > $row) return false; @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst return true; } /* OLEDB types enum DBTYPEENUM { DBTYPE_EMPTY = 0, DBTYPE_NULL = 1, DBTYPE_I2 = 2, DBTYPE_I4 = 3, DBTYPE_R4 = 4, DBTYPE_R8 = 5, DBTYPE_CY = 6, DBTYPE_DATE = 7, DBTYPE_BSTR = 8, DBTYPE_IDISPATCH = 9, DBTYPE_ERROR = 10, DBTYPE_BOOL = 11, DBTYPE_VARIANT = 12, DBTYPE_IUNKNOWN = 13, DBTYPE_DECIMAL = 14, DBTYPE_UI1 = 17, DBTYPE_ARRAY = 0x2000, DBTYPE_BYREF = 0x4000, DBTYPE_I1 = 16, DBTYPE_UI2 = 18, DBTYPE_UI4 = 19, DBTYPE_I8 = 20, DBTYPE_UI8 = 21, DBTYPE_GUID = 72, DBTYPE_VECTOR = 0x1000, DBTYPE_RESERVED = 0x8000, DBTYPE_BYTES = 128, DBTYPE_STR = 129, DBTYPE_WSTR = 130, DBTYPE_NUMERIC = 131, DBTYPE_UDT = 132, DBTYPE_DBDATE = 133, DBTYPE_DBTIME = 134, DBTYPE_DBTIMESTAMP = 135 ADO Types adEmpty = 0, adTinyInt = 16, adSmallInt = 2, adInteger = 3, adBigInt = 20, adUnsignedTinyInt = 17, adUnsignedSmallInt = 18, adUnsignedInt = 19, adUnsignedBigInt = 21, adSingle = 4, adDouble = 5, adCurrency = 6, adDecimal = 14, adNumeric = 131, adBoolean = 11, adError = 10, adUserDefined = 132, adVariant = 12, adIDispatch = 9, adIUnknown = 13, adGUID = 72, adDate = 7, adDBDate = 133, adDBTime = 134, adDBTimeStamp = 135, adBSTR = 8, adChar = 129, adVarChar = 200, adLongVarChar = 201, adWChar = 130, adVarWChar = 202, adLongVarWChar = 203, adBinary = 128, adVarBinary = 204, adLongVarBinary = 205, adChapter = 136, adFileTime = 64, adDBFileTime = 137, adPropVariant = 138, adVarNumeric = 139 */ function MetaType($t,$len=-1,$fieldobj=false) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } if (!is_numeric($t)) return $t; switch ($t) { case 0: case 12: // variant case 8: // bstr case 129: //char case 130: //wc case 200: // varc case 202:// varWC case 128: // bin case 204: // varBin case 72: // guid if ($len <= $this->blobSize) return 'C'; case 201: case 203: return 'X'; case 128: case 204: case 205: return 'B'; case 7: case 133: return 'D'; case 134: case 135: return 'T'; case 11: return 'L'; case 16:// adTinyInt = 16, case 2://adSmallInt = 2, case 3://adInteger = 3, case 4://adBigInt = 20, case 17://adUnsignedTinyInt = 17, case 18://adUnsignedSmallInt = 18, case 19://adUnsignedInt = 19, case 20://adUnsignedBigInt = 21, return 'I'; default: return 'N'; } } // time stamp not supported yet function _fetch() { $rs = $this->_queryID; if (!$rs or $rs->EOF) { $this->fields = false; return false; } $this->fields = array(); if (!$this->_tarr) { $tarr = array(); $flds = array(); for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { $f = $rs->Fields($i); $flds[] = $f; $tarr[] = $f->Type; } // bind types and flds only once $this->_tarr = $tarr; $this->_flds = $flds; } $t = reset($this->_tarr); $f = reset($this->_flds); if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) { //echo "

",$t,' ';var_dump($f->value); echo '

'; switch($t) { case 135: // timestamp if (!strlen((string)$f->value)) $this->fields[] = false; else { if (!is_numeric($f->value)) # $val = variant_date_to_timestamp($f->value); // VT_DATE stores dates as (float) fractional days since 1899/12/30 00:00:00 $val= (float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; $this->fields[] = adodb_date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) if ($val = $f->value) { $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2); } else $this->fields[] = false; break; case 7: // adDate if (!strlen((string)$f->value)) $this->fields[] = false; else { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); } break; case 1: // null $this->fields[] = false; break; case 6: // currency is not supported properly; ADOConnection::outp( ''.$f->Name.': currency type not supported by PHP'); $this->fields[] = (float) $f->value; break; default: $this->fields[] = $f->value; break; } //print " $f->value $t, "; $f = next($this->_flds); $t = next($this->_tarr); } // for if ($this->hideErrors) error_reporting($olde); @$rs->MoveNext(); // @ needed for some versions of PHP! if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields = &$this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } function NextRecordSet() { $rs = $this->_queryID; $this->_queryID = $rs->NextRecordSet(); //$this->_queryID = $this->_QueryId->NextRecordSet(); if ($this->_queryID == null) return false; $this->_currentRow = -1; $this->_currentPage = -1; $this->bind = false; $this->fields = false; $this->_flds = false; $this->_tarr = false; $this->_inited = false; $this->Init(); return true; } function _close() { $this->_flds = false; @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk) $this->_queryID = false; } } ?>phpgacl-3.3.7/adodb/drivers/adodb-mysqlt.inc.php0100644025754300001440000000727610476657245020515 0ustar ipsousers Requires mysql client. Works on Windows and Unix. */ // security - hide paths if (!defined('ADODB_DIR')) die(); include_once(ADODB_DIR."/drivers/adodb-mysql.inc.php"); class ADODB_mysqlt extends ADODB_mysql { var $databaseType = 'mysqlt'; var $ansiOuter = true; // for Version 3.23.17 or later var $hasTransactions = true; var $autoRollback = true; // apparently mysql does not autorollback properly function ADODB_mysqlt() { global $ADODB_EXTENSION; if ($ADODB_EXTENSION) $this->rsPrefix .= 'ext_'; } /* set transaction mode SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE } */ function SetTransactionMode( $transaction_mode ) { $this->_transmode = $transaction_mode; if (empty($transaction_mode)) { $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); return; } if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->Execute('SET AUTOCOMMIT=0'); $this->Execute('BEGIN'); return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->Execute('COMMIT'); $this->Execute('SET AUTOCOMMIT=1'); return true; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->Execute('ROLLBACK'); $this->Execute('SET AUTOCOMMIT=1'); return true; } function RowLock($tables,$where='',$flds='1 as adodb_ignore') { if ($this->transCnt==0) $this->BeginTrans(); if ($where) $where = ' where '.$where; $rs =& $this->Execute("select $flds from $tables $where for update"); return !empty($rs); } } class ADORecordSet_mysqlt extends ADORecordSet_mysql{ var $databaseType = "mysqlt"; function ADORecordSet_mysqlt($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = MYSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function MoveNext() { if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) { $this->_currentRow += 1; return true; } if (!$this->EOF) { $this->_currentRow += 1; $this->EOF = true; } return false; } } class ADORecordSet_ext_mysqlt extends ADORecordSet_mysqlt { function ADORecordSet_ext_mysqlt($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH: default: $this->fetchMode = MYSQL_BOTH; break; } $this->adodbFetchMode = $mode; $this->ADORecordSet($queryID); } function MoveNext() { return adodb_movenext($this); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-informix.inc.php0100644025754300001440000000173110476657245021005 0ustar ipsousersADORecordset_informix72($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-vfp.inc.php0100644025754300001440000000466410476657245017755 0ustar ipsousersADODB_odbc(); } function Time() { return time(); } function BeginTrans() { return false;} // quote string to be sent back to database function qstr($s,$nofixquotes=false) { if (!$nofixquotes) return "'".str_replace("\r\n","'+chr(13)+'",str_replace("'",$this->replaceQuote,$s))."'"; return "'".$s."'"; } // TOP requires ORDER BY for VFP function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { $this->hasTop = preg_match('/ORDER[ \t\r\n]+BY/is',$sql) ? 'top' : false; $ret = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $ret; } }; class ADORecordSet_vfp extends ADORecordSet_odbc { var $databaseType = "vfp"; function ADORecordSet_vfp($id,$mode=false) { return $this->ADORecordSet_odbc($id,$mode); } function MetaType($t,$len=-1) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'C': if ($len <= $this->blobSize) return 'C'; case 'M': return 'X'; case 'D': return 'D'; case 'T': return 'T'; case 'L': return 'L'; case 'I': return 'I'; default: return 'N'; } } } } //define ?>phpgacl-3.3.7/adodb/drivers/adodb-odbc_oracle.inc.php0100644025754300001440000000616210476657245021411 0ustar ipsousersADODB_odbc(); } function &MetaTables() { $false = false; $rs = $this->Execute($this->metaTablesSQL); if ($rs === false) return $false; $arr = $rs->GetArray(); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { $arr2[] = $arr[$i][0]; } $rs->Close(); return $arr2; } function &MetaColumns($table) { global $ADODB_FETCH_MODE; $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); if ($rs === false) { $false = false; return $false; } $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); return $retarr; } // returns true or false function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; $php_errormsg = ''; $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); $this->_errorMsg = $php_errormsg; $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); return $this->_connectionID != false; } // returns true or false function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; $php_errormsg = ''; $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,SQL_CUR_USE_ODBC ); $this->_errorMsg = $php_errormsg; $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); //if ($this->_connectionID) odbc_autocommit($this->_connectionID,true); return $this->_connectionID != false; } } class ADORecordSet_odbc_oracle extends ADORecordSet_odbc { var $databaseType = 'odbc_oracle'; function ADORecordSet_odbc_oracle($id,$mode=false) { return $this->ADORecordSet_odbc($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-sqlanywhere.inc.php0100644025754300001440000001021310476657245021507 0ustar ipsouserscreate_blobvar($blobVarName); b) load blob var from file. $filename must be complete path $dbcon->load_blobvar_from_file($blobVarName, $filename); c) Use the $blobVarName in SQL insert or update statement in the values clause: $recordSet = $dbconn->Execute('INSERT INTO tabname (idcol, blobcol) ' . 'VALUES (\'test\', ' . $blobVarName . ')'); instead of loading blob from a file, you can also load from an unformatted (raw) blob variable: $dbcon->load_blobvar_from_var($blobVarName, $varName); d) drop blob variable on db server to free up resources: $dbconn->drop_blobvar($blobVarName); Sybase_SQLAnywhere data driver. Requires ODBC. */ // security - hide paths if (!defined('ADODB_DIR')) die(); if (!defined('_ADODB_ODBC_LAYER')) { include(ADODB_DIR."/drivers/adodb-odbc.inc.php"); } if (!defined('ADODB_SYBASE_SQLANYWHERE')){ define('ADODB_SYBASE_SQLANYWHERE',1); class ADODB_sqlanywhere extends ADODB_odbc { var $databaseType = "sqlanywhere"; var $hasInsertID = true; function ADODB_sqlanywhere() { $this->ADODB_odbc(); } function _insertid() { return $this->GetOne('select @@identity'); } function create_blobvar($blobVarName) { $this->Execute("create variable $blobVarName long binary"); return; } function drop_blobvar($blobVarName) { $this->Execute("drop variable $blobVarName"); return; } function load_blobvar_from_file($blobVarName, $filename) { $chunk_size = 1000; $fd = fopen ($filename, "rb"); $integer_chunks = (integer)filesize($filename) / $chunk_size; $modulus = filesize($filename) % $chunk_size; if ($modulus != 0){ $integer_chunks += 1; } for($loop=1;$loop<=$integer_chunks;$loop++){ $contents = fread ($fd, $chunk_size); $contents = bin2hex($contents); $hexstring = ''; for($loop2=0;$loop2qstr($hexstring); $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); } fclose ($fd); return; } function load_blobvar_from_var($blobVarName, &$varName) { $chunk_size = 1000; $integer_chunks = (integer)strlen($varName) / $chunk_size; $modulus = strlen($varName) % $chunk_size; if ($modulus != 0){ $integer_chunks += 1; } for($loop=1;$loop<=$integer_chunks;$loop++){ $contents = substr ($varName, (($loop - 1) * $chunk_size), $chunk_size); $contents = bin2hex($contents); $hexstring = ''; for($loop2=0;$loop2qstr($hexstring); $this->Execute("set $blobVarName = $blobVarName || " . $hexstring); } return; } /* Insert a null into the blob field of the table first. Then use UpdateBlob to store the blob. Usage: $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,&$val,$where,$blobtype='BLOB') { $blobVarName = 'hold_blob'; $this->create_blobvar($blobVarName); $this->load_blobvar_from_var($blobVarName, $val); $this->Execute("UPDATE $table SET $column=$blobVarName WHERE $where"); $this->drop_blobvar($blobVarName); return true; } }; //class class ADORecordSet_sqlanywhere extends ADORecordSet_odbc { var $databaseType = "sqlanywhere"; function ADORecordSet_sqlanywhere($id,$mode=false) { $this->ADORecordSet_odbc($id,$mode); } }; //class } //define ?> phpgacl-3.3.7/adodb/drivers/adodb-mssqlpo.inc.php0100644025754300001440000000276710476657245020662 0ustar ipsousers_has_mssql_init) { ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0"); return $sql; } if (is_string($sql)) $sql = str_replace('||','+',$sql); $stmt = mssql_init($sql,$this->_connectionID); if (!$stmt) return $sql; return array($sql,$stmt); } function _query($sql,$inputarr) { if (is_string($sql)) $sql = str_replace('||','+',$sql); return ADODB_mssql::_query($sql,$inputarr); } } class ADORecordset_mssqlpo extends ADORecordset_mssql { var $databaseType = "mssqlpo"; function ADORecordset_mssqlpo($id,$mode=false) { $this->ADORecordset_mssql($id,$mode); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-sqlite.inc.php0100644025754300001440000002430210476657245020452 0ustar ipsousersfmtDate)."'"; case 'sysTimeStamp' : return "'".date($this->sysTimeStamp)."'"; } }*/ function ServerInfo() { $arr['version'] = sqlite_libversion(); $arr['description'] = 'SQLite '; $arr['encoding'] = sqlite_libencoding(); return $arr; } function BeginTrans() { if ($this->transOff) return true; $ret = $this->Execute("BEGIN TRANSACTION"); $this->transCnt += 1; return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); $ret = $this->Execute("COMMIT"); if ($this->transCnt>0)$this->transCnt -= 1; return !empty($ret); } function RollbackTrans() { if ($this->transOff) return true; $ret = $this->Execute("ROLLBACK"); if ($this->transCnt>0)$this->transCnt -= 1; return !empty($ret); } // mark newnham function &MetaColumns($tab) { global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute("PRAGMA table_info('$tab')"); if (isset($savem)) $this->SetFetchMode($savem); if (!$rs) { $ADODB_FETCH_MODE = $save; return $false; } $arr = array(); while ($r = $rs->FetchRow()) { $type = explode('(',$r['type']); $size = ''; if (sizeof($type)==2) $size = trim($type[1],')'); $fn = strtoupper($r['name']); $fld = new ADOFieldObject; $fld->name = $r['name']; $fld->type = $type[0]; $fld->max_length = $size; $fld->not_null = $r['notnull']; $fld->default_value = $r['dflt_value']; $fld->scale = 0; if ($save == ADODB_FETCH_NUM) $arr[] = $fld; else $arr[strtoupper($fld->name)] = $fld; } $rs->Close(); $ADODB_FETCH_MODE = $save; return $arr; } function _init($parentDriver) { $parentDriver->hasTransactions = false; $parentDriver->hasInsertID = true; } function _insertid() { return sqlite_last_insert_rowid($this->_connectionID); } function _affectedrows() { return sqlite_changes($this->_connectionID); } function ErrorMsg() { if ($this->_logsql) return $this->_errorMsg; return ($this->_errorNo) ? sqlite_error_string($this->_errorNo) : ''; } function ErrorNo() { return $this->_errorNo; } function SQLDate($fmt, $col=false) { $fmt = $this->qstr($fmt); return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; } function _createFunctions() { @sqlite_create_function($this->_connectionID, 'adodb_date', 'adodb_date', 1); @sqlite_create_function($this->_connectionID, 'adodb_date2', 'adodb_date2', 2); } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('sqlite_open')) return null; if (empty($argHostname) && $argDatabasename) $argHostname = $argDatabasename; $this->_connectionID = sqlite_open($argHostname); if ($this->_connectionID === false) return false; $this->_createFunctions(); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('sqlite_open')) return null; if (empty($argHostname) && $argDatabasename) $argHostname = $argDatabasename; $this->_connectionID = sqlite_popen($argHostname); if ($this->_connectionID === false) return false; $this->_createFunctions(); return true; } // returns query ID if successful, otherwise false function _query($sql,$inputarr=false) { $rez = sqlite_query($sql,$this->_connectionID); if (!$rez) { $this->_errorNo = sqlite_last_error($this->_connectionID); } return $rez; } function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) { $offsetStr = ($offset >= 0) ? " OFFSET $offset" : ''; $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ($offset >= 0 ? ' LIMIT 999999999' : ''); if ($secs2cache) $rs =& $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr); else $rs =& $this->Execute($sql."$limitStr$offsetStr",$inputarr); return $rs; } /* This algorithm is not very efficient, but works even if table locking is not available. Will return false if unable to generate an ID after $MAXLOOPS attempts. */ var $_genSeqSQL = "create table %s (id integer)"; function GenID($seq='adodbseq',$start=1) { // if you have to modify the parameter below, your database is overloaded, // or you need to implement generation of id's yourself! $MAXLOOPS = 100; //$this->debug=1; while (--$MAXLOOPS>=0) { @($num = $this->GetOne("select id from $seq")); if ($num === false) { $this->Execute(sprintf($this->_genSeqSQL ,$seq)); $start -= 1; $num = '0'; $ok = $this->Execute("insert into $seq values($start)"); if (!$ok) return false; } $this->Execute("update $seq set id=id+1 where id=$num"); if ($this->affected_rows() > 0) { $num += 1; $this->genID = $num; return $num; } } if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); } return false; } function CreateSequence($seqname='adodbseq',$start=1) { if (empty($this->_genSeqSQL)) return false; $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; $start -= 1; return $this->Execute("insert into $seqname values($start)"); } var $_dropSeqSQL = 'drop table %s'; function DropSequence($seqname) { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } // returns true or false function _close() { return @sqlite_close($this->_connectionID); } function &MetaIndexes($table, $primary = FALSE, $owner=false) { $false = false; // save old fetch mode global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } $SQL=sprintf("SELECT name,sql FROM sqlite_master WHERE type='index' AND tbl_name='%s'", strtolower($table)); $rs = $this->Execute($SQL); if (!is_object($rs)) { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; return $false; } $indexes = array (); while ($row = $rs->FetchRow()) { if ($primary && preg_match("/primary/i",$row[1]) == 0) continue; if (!isset($indexes[$row[0]])) { $indexes[$row[0]] = array( 'unique' => preg_match("/unique/i",$row[1]), 'columns' => array()); } /** * There must be a more elegant way of doing this, * the index elements appear in the SQL statement * in cols[1] between parentheses * e.g CREATE UNIQUE INDEX ware_0 ON warehouse (org,warehouse) */ $cols = explode("(",$row[1]); $cols = explode(")",$cols[1]); array_pop($cols); $indexes[$row[0]]['columns'] = $cols; } if (isset($savem)) { $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; } return $indexes; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_sqlite extends ADORecordSet { var $databaseType = "sqlite"; var $bind = false; function ADORecordset_sqlite($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch($mode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE_NUM; break; case ADODB_FETCH_ASSOC: $this->fetchMode = SQLITE_ASSOC; break; default: $this->fetchMode = SQLITE_BOTH; break; } $this->adodbFetchMode = $mode; $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); if ($queryID) { $this->_currentRow = 0; $this->EOF = !$this->_fetch(); @$this->_initrs(); } else { $this->_numOfRows = 0; $this->_numOfFields = 0; $this->EOF = true; } return $this->_queryID; } function &FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $fld->name = sqlite_field_name($this->_queryID, $fieldOffset); $fld->type = 'VARCHAR'; $fld->max_length = -1; return $fld; } function _initrs() { $this->_numOfRows = @sqlite_num_rows($this->_queryID); $this->_numOfFields = @sqlite_num_fields($this->_queryID); } function Fields($colname) { if ($this->fetchMode != SQLITE_NUM) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _seek($row) { return sqlite_seek($this->_queryID, $row); } function _fetch($ignore_fields=false) { $this->fields = @sqlite_fetch_array($this->_queryID,$this->fetchMode); return !empty($this->fields); } function _close() { } } ?>phpgacl-3.3.7/adodb/drivers/adodb-oci8.inc.php0100644025754300001440000012361610476657245020023 0ustar ipsousers 13 Nov 2000 jlim - removed all ora_* references. */ // security - hide paths if (!defined('ADODB_DIR')) die(); /* NLS_Date_Format Allows you to use a date format other than the Oracle Lite default. When a literal character string appears where a date value is expected, the Oracle Lite database tests the string to see if it matches the formats of Oracle, SQL-92, or the value specified for this parameter in the POLITE.INI file. Setting this parameter also defines the default format used in the TO_CHAR or TO_DATE functions when no other format string is supplied. For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is yy-mm-dd or yyyy-mm-dd. Using 'RR' in the format forces two-digit years less than or equal to 49 to be interpreted as years in the 21st century (20002049), and years over 50 as years in the 20th century (19501999). Setting the RR format as the default for all two-digit year entries allows you to become year-2000 compliant. For example: NLS_DATE_FORMAT='RR-MM-DD' You can also modify the date format using the ALTER SESSION command. */ # define the LOB descriptor type for the given type # returns false if no LOB descriptor function oci_lob_desc($type) { switch ($type) { case OCI_B_BFILE: $result = OCI_D_FILE; break; case OCI_B_CFILEE: $result = OCI_D_FILE; break; case OCI_B_CLOB: $result = OCI_D_LOB; break; case OCI_B_BLOB: $result = OCI_D_LOB; break; case OCI_B_ROWID: $result = OCI_D_ROWID; break; default: $result = false; break; } return $result; } class ADODB_oci8 extends ADOConnection { var $databaseType = 'oci8'; var $dataProvider = 'oci8'; var $replaceQuote = "''"; // string to use to replace quotes var $concat_operator='||'; var $sysDate = "TRUNC(SYSDATE)"; var $sysTimeStamp = 'SYSDATE'; var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1"; var $_stmt; var $_commit = OCI_COMMIT_ON_SUCCESS; var $_initdate = true; // init date to YYYY-MM-DD var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW')"; var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net var $_bindInputArray = true; var $hasGenID = true; var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL"; var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s"; var $_dropSeqSQL = "DROP SEQUENCE %s"; var $hasAffectedRows = true; var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)"; var $noNullStrings = false; var $connectSID = false; var $_bind = false; var $_nestedSQL = true; var $_hasOCIFetchStatement = false; var $_getarray = false; // currently not working var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER var $session_sharing_force_blob = false; // alter session on updateblob if set to true var $firstrows = true; // enable first rows optimization on SelectLimit() var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit. var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS' var $useDBDateFormatForTextInput=false; var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true) var $_refLOBs = array(); // var $ansiOuter = true; // if oracle9 function ADODB_oci8() { $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_'; } /* Function &MetaColumns($table) added by smondino@users.sourceforge.net*/ function &MetaColumns($table) { global $ADODB_FETCH_MODE; $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) { return $false; } $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->scale = $rs->fields[3]; if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) { $fld->type ='INT'; $fld->max_length = $rs->fields[4]; } $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; else $retarr[strtoupper($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); if (empty($retarr)) return $false; else return $retarr; } function Time() { $rs =& $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual"); if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields)); return false; } /* Multiple modes of connection are supported: a. Local Database $conn->Connect(false,'scott','tiger'); b. From tnsnames.ora $conn->Connect(false,'scott','tiger',$tnsname); $conn->Connect($tnsname,'scott','tiger'); c. Server + service name $conn->Connect($serveraddress,'scott,'tiger',$service_name); d. Server + SID $conn->connectSID = true; $conn->Connect($serveraddress,'scott,'tiger',$SID); Example TNSName: --------------- NATSOFT.DOMAIN = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523)) ) (CONNECT_DATA = (SERVICE_NAME = natsoft.domain) ) ) There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection */ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0) { if (!function_exists('OCIPLogon')) return null; $this->_errorMsg = false; $this->_errorCode = false; if($argHostname) { // added by Jorma Tuomainen if (empty($argDatabasename)) $argDatabasename = $argHostname; else { if(strpos($argHostname,":")) { $argHostinfo=explode(":",$argHostname); $argHostname=$argHostinfo[0]; $argHostport=$argHostinfo[1]; } else { $argHostport = empty($this->port)? "1521" : $this->port; } if ($this->connectSID) { $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))"; } else $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; } } //if ($argHostname) print "

Connect: 1st argument should be left blank for $this->databaseType

"; if ($mode==1) { $this->_connectionID = ($this->charSet) ? OCIPLogon($argUsername,$argPassword, $argDatabasename) : OCIPLogon($argUsername,$argPassword, $argDatabasename, $this->charSet) ; if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID); } else if ($mode==2) { $this->_connectionID = ($this->charSet) ? OCINLogon($argUsername,$argPassword, $argDatabasename) : OCINLogon($argUsername,$argPassword, $argDatabasename, $this->charSet); } else { $this->_connectionID = ($this->charSet) ? OCILogon($argUsername,$argPassword, $argDatabasename) : OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet); } if (!$this->_connectionID) return false; if ($this->_initdate) { $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); } // looks like: // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production // $vers = OCIServerVersion($this->_connectionID); // if (strpos($vers,'8i') !== false) $this->ansiOuter = true; return true; } function ServerInfo() { $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level'); $arr['description'] = @OCIServerVersion($this->_connectionID); $arr['version'] = ADOConnection::_findvers($arr['description']); return $arr; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1); } // returns true or false function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2); } function _affectedrows() { if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt); return 0; } function IfNull( $field, $ifNull ) { return " NVL($field, $ifNull) "; // if Oracle } // format and return date string in database date format function DBDate($d) { if (empty($d) && $d !== 0) return 'null'; if (is_string($d)) $d = ADORecordSet::UnixDate($d); return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->NLS_DATE_FORMAT."')"; } function BindDate($d) { $d = ADOConnection::DBDate($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } function BindTimeStamp($d) { $d = ADOConnection::DBTimeStamp($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } // format and return date string in database timestamp format function DBTimeStamp($ts) { if (empty($ts) && $ts !== 0) return 'null'; if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; } function RowLock($tables,$where,$flds='1 as ignore') { if ($this->autoCommit) $this->BeginTrans(); return $this->GetOne("select $flds from $tables where $where for update"); } function &MetaTables($ttype=false,$showSchema=false,$mask=false) { if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(strtoupper($mask)); $this->metaTablesSQL .= " AND upper(table_name) like $mask"; } $ret =& ADOConnection::MetaTables($ttype,$showSchema); if ($mask) { $this->metaTablesSQL = $save; } return $ret; } // Mark Newnham function &MetaIndexes ($table, $primary = FALSE, $owner=false) { // save old fetch mode global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== FALSE) { $savem = $this->SetFetchMode(FALSE); } // get index details $table = strtoupper($table); // get Primary index $primary_key = ''; $false = false; $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table)); if ($row = $rs->FetchRow()) $primary_key = $row[1]; //constraint_name if ($primary==TRUE && $primary_key=='') { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; return $false; //There is no primary key } $rs = $this->Execute(sprintf("SELECT ALL_INDEXES.INDEX_NAME, ALL_INDEXES.UNIQUENESS, ALL_IND_COLUMNS.COLUMN_POSITION, ALL_IND_COLUMNS.COLUMN_NAME FROM ALL_INDEXES,ALL_IND_COLUMNS WHERE UPPER(ALL_INDEXES.TABLE_NAME)='%s' AND ALL_IND_COLUMNS.INDEX_NAME=ALL_INDEXES.INDEX_NAME",$table)); if (!is_object($rs)) { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; return $false; } $indexes = array (); // parse index data into array while ($row = $rs->FetchRow()) { if ($primary && $row[0] != $primary_key) continue; if (!isset($indexes[$row[0]])) { $indexes[$row[0]] = array( 'unique' => ($row[1] == 'UNIQUE'), 'columns' => array() ); } $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3]; } // sort columns by order in the index foreach ( array_keys ($indexes) as $index ) { ksort ($indexes[$index]['columns']); } if (isset($savem)) { $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; } return $indexes; } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->autoCommit = false; $this->_commit = OCI_DEFAULT; if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode); return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $ret = OCIcommit($this->_connectionID); $this->_commit = OCI_COMMIT_ON_SUCCESS; $this->autoCommit = true; return $ret; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $ret = OCIrollback($this->_connectionID); $this->_commit = OCI_COMMIT_ON_SUCCESS; $this->autoCommit = true; return $ret; } function SelectDB($dbName) { return false; } function ErrorMsg() { if ($this->_errorMsg !== false) return $this->_errorMsg; if (is_resource($this->_stmt)) $arr = @OCIerror($this->_stmt); if (empty($arr)) { $arr = @OCIerror($this->_connectionID); if ($arr === false) $arr = @OCIError(); if ($arr === false) return ''; } $this->_errorMsg = $arr['message']; $this->_errorCode = $arr['code']; return $this->_errorMsg; } function ErrorNo() { if ($this->_errorCode !== false) return $this->_errorCode; if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt); if (empty($arr)) { $arr = @OCIError($this->_connectionID); if ($arr == false) $arr = @OCIError(); if ($arr == false) return ''; } $this->_errorMsg = $arr['message']; $this->_errorCode = $arr['code']; return $arr['code']; } // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; $s = 'TO_CHAR('.$col.",'"; $len = strlen($fmt); for ($i=0; $i < $len; $i++) { $ch = $fmt[$i]; switch($ch) { case 'Y': case 'y': $s .= 'YYYY'; break; case 'Q': case 'q': $s .= 'Q'; break; case 'M': $s .= 'Mon'; break; case 'm': $s .= 'MM'; break; case 'D': case 'd': $s .= 'DD'; break; case 'H': $s.= 'HH24'; break; case 'h': $s .= 'HH'; break; case 'i': $s .= 'MI'; break; case 's': $s .= 'SS'; break; case 'a': case 'A': $s .= 'AM'; break; case 'w': $s .= 'D'; break; case 'l': $s .= 'DAY'; break; case 'W': $s .= 'WW'; break; default: // handle escape characters... if ($ch == '\\') { $i++; $ch = substr($fmt,$i,1); } if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; else $s .= '"'.$ch.'"'; } } return $s. "')"; } /* This algorithm makes use of a. FIRST_ROWS hint The FIRST_ROWS hint explicitly chooses the approach to optimize response time, that is, minimum resource usage to return the first row. Results will be returned as soon as they are identified. b. Uses rownum tricks to obtain only the required rows from a given offset. As this uses complicated sql statements, we only use this if the $offset >= 100. This idea by Tomas V V Cox. This implementation does not appear to work with oracle 8.0.5 or earlier. Comment out this function then, and the slower SelectLimit() in the base class will be used. */ function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { // seems that oracle only supports 1 hint comment in 8i if ($this->firstrows) { if (strpos($sql,'/*+') !== false) $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql); else $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql); } if ($offset < $this->selectOffsetAlg1) { if ($nrows > 0) { if ($offset > 0) $nrows += $offset; //$inputarr['adodb_rownum'] = $nrows; if ($this->databaseType == 'oci8po') { $sql = "select * from (".$sql.") where rownum <= ?"; } else { $sql = "select * from (".$sql.") where rownum <= :adodb_offset"; } $inputarr['adodb_offset'] = $nrows; $nrows = -1; } // note that $nrows = 0 still has to work ==> no rows returned $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $rs; } else { // Algorithm by Tomas V V Cox, from PEAR DB oci8.php // Let Oracle return the name of the columns $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL"; $false = false; if (! $stmt_arr = $this->Prepare($q_fields)) { return $false; } $stmt = $stmt_arr[1]; if (is_array($inputarr)) { foreach($inputarr as $k => $v) { if (is_array($v)) { if (sizeof($v) == 2) // suggested by g.giunta@libero. OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); else OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); } else { $len = -1; if ($v === ' ') $len = 1; if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again $bindarr[$k] = $v; } else { // dynamic sql, so rebind every time OCIBindByName($stmt,":$k",$inputarr[$k],$len); } } } } if (!OCIExecute($stmt, OCI_DEFAULT)) { OCIFreeStatement($stmt); return $false; } $ncols = OCINumCols($stmt); for ( $i = 1; $i <= $ncols; $i++ ) { $cols[] = '"'.OCIColumnName($stmt, $i).'"'; } $result = false; OCIFreeStatement($stmt); $fields = implode(',', $cols); $nrows += $offset; $offset += 1; // in Oracle rownum starts at 1 if ($this->databaseType == 'oci8po') { $sql = "SELECT $fields FROM". "(SELECT rownum as adodb_rownum, $fields FROM". " ($sql) WHERE rownum <= ?". ") WHERE adodb_rownum >= ?"; } else { $sql = "SELECT $fields FROM". "(SELECT rownum as adodb_rownum, $fields FROM". " ($sql) WHERE rownum <= :adodb_nrows". ") WHERE adodb_rownum >= :adodb_offset"; } $inputarr['adodb_nrows'] = $nrows; $inputarr['adodb_offset'] = $offset; if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr); else $rs =& $this->Execute($sql,$inputarr); return $rs; } } /** * Usage: * Store BLOBs and CLOBs * * Example: to store $var in a blob * * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())'); * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB'); * * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'. * * to get length of LOB: * select DBMS_LOB.GETLENGTH(ablob) from TABLE * * If you are using CURSOR_SHARING = force, it appears this will case a segfault * under oracle 8.1.7.0. Run: * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT'); * before UpdateBlob() then... */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false; switch(strtoupper($blobtype)) { default: ADOConnection::outp("UpdateBlob: Unknown blobtype=$blobtype"); return false; case 'BLOB': $type = OCI_B_BLOB; break; case 'CLOB': $type = OCI_B_CLOB; break; } if ($this->databaseType == 'oci8po') $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?"; else $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); $arr['blob'] = array($desc,-1,$type); if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT'); $commit = $this->autoCommit; if ($commit) $this->BeginTrans(); $rs = $this->_Execute($sql,$arr); if ($rez = !empty($rs)) $desc->save($val); $desc->free(); if ($commit) $this->CommitTrans(); if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE'); if ($rez) $rs->Close(); return $rez; } /** * Usage: store file pointed to by $var in a blob */ function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB') { switch(strtoupper($blobtype)) { default: ADOConnection::outp( "UpdateBlob: Unknown blobtype=$blobtype"); return false; case 'BLOB': $type = OCI_B_BLOB; break; case 'CLOB': $type = OCI_B_CLOB; break; } if ($this->databaseType == 'oci8po') $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?"; else $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); $arr['blob'] = array($desc,-1,$type); $this->BeginTrans(); $rs = ADODB_oci8::Execute($sql,$arr); if ($rez = !empty($rs)) $desc->savefile($val); $desc->free(); $this->CommitTrans(); if ($rez) $rs->Close(); return $rez; } /** * Execute SQL * * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text) * @param [inputarr] holds the input data to bind to. Null elements will be set to null. * @return RecordSet or false */ function &Execute($sql,$inputarr=false) { if ($this->fnExecute) { $fn = $this->fnExecute; $ret =& $fn($this,$sql,$inputarr); if (isset($ret)) return $ret; } if ($inputarr) { #if (!is_array($inputarr)) $inputarr = array($inputarr); $element0 = reset($inputarr); # is_object check because oci8 descriptors can be passed in if (is_array($element0) && !is_object(reset($element0))) { if (is_string($sql)) $stmt = $this->Prepare($sql); else $stmt = $sql; foreach($inputarr as $arr) { $ret =& $this->_Execute($stmt,$arr); if (!$ret) return $ret; } } else { $ret =& $this->_Execute($sql,$inputarr); } } else { $ret =& $this->_Execute($sql,false); } return $ret; } /* Example of usage: $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)'); */ function Prepare($sql,$cursor=false) { static $BINDNUM = 0; $stmt = OCIParse($this->_connectionID,$sql); if (!$stmt) return false; $BINDNUM += 1; $sttype = @OCIStatementType($stmt); if ($sttype == 'BEGIN' || $sttype == 'DECLARE') { return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false); } return array($sql,$stmt,0,$BINDNUM); } /* Call an oracle stored procedure and returns a cursor variable as a recordset. Concept by Robert Tuttle robert@ud.com Example: Note: we return a cursor variable in :RS2 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2'); $rs = $db->ExecuteCursor( "BEGIN :RS2 = adodb.getdata(:VAR1); END;", 'RS2', array('VAR1' => 'Mr Bean')); */ function &ExecuteCursor($sql,$cursorName='rs',$params=false) { if (is_array($sql)) $stmt = $sql; else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor if (is_array($stmt) && sizeof($stmt) >= 5) { $hasref = true; $ignoreCur = false; $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR); if ($params) { foreach($params as $k => $v) { $this->Parameter($stmt,$params[$k], $k); } } } else $hasref = false; $rs =& $this->Execute($stmt); if ($rs) { if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]); else if ($hasref) $rs->_refcursor = $stmt[4]; } return $rs; } /* Bind a variable -- very, very fast for executing repeated statements in oracle. Better than using for ($i = 0; $i < $max; $i++) { $p1 = ?; $p2 = ?; $p3 = ?; $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", array($p1,$p2,$p3)); } Usage: $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)"); $DB->Bind($stmt, $p1); $DB->Bind($stmt, $p2); $DB->Bind($stmt, $p3); for ($i = 0; $i < $max; $i++) { $p1 = ?; $p2 = ?; $p3 = ?; $DB->Execute($stmt); } Some timings: ** Test table has 3 cols, and 1 index. Test to insert 1000 records Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute Now if PHP only had batch/bulk updating like Java or PL/SQL... Note that the order of parameters differs from OCIBindByName, because we default the names to :0, :1, :2 */ function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false) { if (!is_array($stmt)) return false; if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type); } if ($name == false) { if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type); else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator $stmt[2] += 1; } else if (oci_lob_desc($type)) { if ($this->debug) { ADOConnection::outp("Bind: name = $name"); } //we have to create a new Descriptor here $numlob = count($this->_refLOBs); $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type)); $this->_refLOBs[$numlob]['TYPE'] = $isOutput; $tmp = &$this->_refLOBs[$numlob]['LOB']; $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type); if ($this->debug) { ADOConnection::outp("Bind: descriptor has been allocated, var (".$name.") binded"); } // if type is input then write data to lob now if ($isOutput == false) { $var = $this->BlobEncode($var); $tmp->WriteTemporary($var); $this->_refLOBs[$numlob]['VAR'] = &$var; if ($this->debug) { ADOConnection::outp("Bind: LOB has been written to temp"); } } else { $this->_refLOBs[$numlob]['VAR'] = &$var; } $rez = $tmp; } else { if ($this->debug) ADOConnection::outp("Bind: name = $name"); if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type); else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator } return $rez; } function Param($name,$type=false) { return ':'.$name; } /* Usage: $stmt = $db->Prepare('select * from table where id =:myid and group=:group'); $db->Parameter($stmt,$id,'myid'); $db->Parameter($stmt,$group,'group'); $db->Execute($stmt); @param $stmt Statement returned by Prepare() or PrepareSP(). @param $var PHP variable to bind to @param $name Name of stored procedure variable name to bind to. @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. @param [$maxLen] Holds an maximum length of the variable. @param [$type] The data type of $var. Legal values depend on driver. See OCIBindByName documentation at php.net. */ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) { if ($this->debug) { $prefix = ($isOutput) ? 'Out' : 'In'; $ztype = (empty($type)) ? 'false' : $type; ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);"); } return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput); } /* returns query ID if successful, otherwise false this version supports: 1. $db->execute('select * from table'); 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)'); $db->execute($prepared_statement, array(1,2,3)); 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3)); 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)'); $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); $db->execute($stmt); */ function _query($sql,$inputarr) { if (is_array($sql)) { // is prepared sql $stmt = $sql[1]; // we try to bind to permanent array, so that OCIBindByName is persistent // and carried out once only - note that max array element size is 4000 chars if (is_array($inputarr)) { $bindpos = $sql[3]; if (isset($this->_bind[$bindpos])) { // all tied up already $bindarr = &$this->_bind[$bindpos]; } else { // one statement to bind them all $bindarr = array(); foreach($inputarr as $k => $v) { $bindarr[$k] = $v; OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000); } $this->_bind[$bindpos] = &$bindarr; } } } else { $stmt=OCIParse($this->_connectionID,$sql); } $this->_stmt = $stmt; if (!$stmt) return false; if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS); if (is_array($inputarr)) { foreach($inputarr as $k => $v) { if (is_array($v)) { if (sizeof($v) == 2) // suggested by g.giunta@libero. OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); else OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'
'; } else { $len = -1; if ($v === ' ') $len = 1; if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again $bindarr[$k] = $v; } else { // dynamic sql, so rebind every time OCIBindByName($stmt,":$k",$inputarr[$k],$len); } } } } $this->_errorMsg = false; $this->_errorCode = false; if (OCIExecute($stmt,$this->_commit)) { //OCIInternalDebug(1); if (count($this -> _refLOBs) > 0) { foreach ($this -> _refLOBs as $key => $value) { if ($this -> _refLOBs[$key]['TYPE'] == true) { $tmp = $this -> _refLOBs[$key]['LOB'] -> load(); if ($this -> debug) { ADOConnection::outp("OUT LOB: LOB has been loaded.
"); } //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp; $this -> _refLOBs[$key]['VAR'] = $tmp; } else { $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']); $this -> _refLOBs[$key]['LOB']->free(); unset($this -> _refLOBs[$key]); if ($this->debug) { ADOConnection::outp("IN LOB: LOB has been saved.
"); } } } } switch (@OCIStatementType($stmt)) { case "SELECT": return $stmt; case 'DECLARE': case "BEGIN": if (is_array($sql) && !empty($sql[4])) { $cursor = $sql[4]; if (is_resource($cursor)) { $ok = OCIExecute($cursor); return $cursor; } return $stmt; } else { if (is_resource($stmt)) { OCIFreeStatement($stmt); return true; } return $stmt; } break; default : // ociclose -- no because it could be used in a LOB? return true; } } return false; } // returns true or false function _close() { if (!$this->_connectionID) return; if (!$this->autoCommit) OCIRollback($this->_connectionID); if (count($this->_refLOBs) > 0) { foreach ($this ->_refLOBs as $key => $value) { $this->_refLOBs[$key]['LOB']->free(); unset($this->_refLOBs[$key]); } } OCILogoff($this->_connectionID); $this->_stmt = false; $this->_connectionID = false; } function MetaPrimaryKeys($table, $owner=false,$internalKey=false) { if ($internalKey) return array('ROWID'); // tested with oracle 8.1.7 $table = strtoupper($table); if ($owner) { $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))"; $ptab = 'ALL_'; } else { $owner_clause = ''; $ptab = 'USER_'; } $sql = " SELECT /*+ RULE */ distinct b.column_name FROM {$ptab}CONSTRAINTS a , {$ptab}CONS_COLUMNS b WHERE ( UPPER(b.table_name) = ('$table')) AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P') $owner_clause AND (a.constraint_name = b.constraint_name)"; $rs = $this->Execute($sql); if ($rs && !$rs->EOF) { $arr =& $rs->GetArray(); $a = array(); foreach($arr as $v) { $a[] = reset($v); } return $a; } else return false; } // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html function MetaForeignKeys($table, $owner=false) { global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $table = $this->qstr(strtoupper($table)); if (!$owner) { $owner = $this->user; $tabp = 'user_'; } else $tabp = 'all_'; $owner = ' and owner='.$this->qstr(strtoupper($owner)); $sql = "select constraint_name,r_owner,r_constraint_name from {$tabp}constraints where constraint_type = 'R' and table_name = $table $owner"; $constraints =& $this->GetArray($sql); $arr = false; foreach($constraints as $constr) { $cons = $this->qstr($constr[0]); $rowner = $this->qstr($constr[1]); $rcons = $this->qstr($constr[2]); $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position"); $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position"); if ($cols && $tabcol) for ($i=0, $max=sizeof($cols); $i < $max; $i++) { $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1]; } } $ADODB_FETCH_MODE = $save; return $arr; } function CharMax() { return 4000; } function TextMax() { return 4000; } /** * Quotes a string. * An example is $db->qstr("Don't bother",magic_quotes_runtime()); * * @param s the string to quote * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). * This undoes the stupidity of magic quotes for GPC. * * @return quoted string to be sent back to database */ function qstr($s,$magic_quotes=false) { //$nofixquotes=false; if ($this->noNullStrings && strlen($s)==0)$s = ' '; if (!$magic_quotes) { if ($this->replaceQuote[0] == '\\'){ $s = str_replace('\\','\\\\',$s); } return "'".str_replace("'",$this->replaceQuote,$s)."'"; } // undo magic quotes for " $s = str_replace('\\"','"',$s); $s = str_replace('\\\\','\\',$s); return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_oci8 extends ADORecordSet { var $databaseType = 'oci8'; var $bind=false; var $_fieldobjs; //var $_arr = false; function ADORecordset_oci8($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; case ADODB_FETCH_NUM: default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; } $this->adodbFetchMode = $mode; $this->_queryID = $queryID; } function Init() { if ($this->_inited) return; $this->_inited = true; if ($this->_queryID) { $this->_currentRow = 0; @$this->_initrs(); $this->EOF = !$this->_fetch(); /* // based on idea by Gaetano Giunta to detect unusual oracle errors // see http://phplens.com/lens/lensforum/msgs.php?id=6771 $err = OCIError($this->_queryID); if ($err && $this->connection->debug) ADOConnection::outp($err); */ if (!is_array($this->fields)) { $this->_numOfRows = 0; $this->fields = array(); } } else { $this->fields = array(); $this->_numOfRows = 0; $this->_numOfFields = 0; $this->EOF = true; } } function _initrs() { $this->_numOfRows = -1; $this->_numOfFields = OCInumcols($this->_queryID); if ($this->_numOfFields>0) { $this->_fieldobjs = array(); $max = $this->_numOfFields; for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i); } } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &_FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $fieldOffset += 1; $fld->name =OCIcolumnname($this->_queryID, $fieldOffset); $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); if ($fld->type == 'NUMBER') { $p = OCIColumnPrecision($this->_queryID, $fieldOffset); $sc = OCIColumnScale($this->_queryID, $fieldOffset); if ($p != 0 && $sc == 0) $fld->type = 'INT'; //echo " $this->name ($p.$sc) "; } return $fld; } /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */ function &FetchField($fieldOffset = -1) { return $this->_fieldobjs[$fieldOffset]; } /* // 10% speedup to move MoveNext to child class function _MoveNext() { //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this); if ($this->EOF) return false; $this->_currentRow++; if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return true; $this->EOF = true; return false; } */ function MoveNext() { if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { $this->_currentRow += 1; return true; } if (!$this->EOF) { $this->_currentRow += 1; $this->EOF = true; } return false; } /* # does not work as first record is retrieved in _initrs(), so is not included in GetArray() function &GetArray($nRows = -1) { global $ADODB_OCI8_GETARRAY; if (true || !empty($ADODB_OCI8_GETARRAY)) { # does not support $ADODB_ANSI_PADDING_OFF //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement switch($this->adodbFetchMode) { case ADODB_FETCH_NUM: $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM); $results = array_merge(array($this->fields),$results); return $results; case ADODB_FETCH_ASSOC: if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break; $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW); $results =& array_merge(array($this->fields),$assoc); return $results; default: break; } } $results =& ADORecordSet::GetArray($nRows); return $results; } */ /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ function &GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { $arr =& $this->GetArray($nrows); return $arr; } for ($i=1; $i < $offset; $i++) if (!@OCIFetch($this->_queryID)) return array(); if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { $results[$cnt++] = $this->fields; $this->MoveNext(); } return $results; } /* Use associative array to get fields array */ function Fields($colname) { if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _seek($row) { return false; } function _fetch() { return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); } /* close() only needs to be called if you are worried about using too much memory while your script is running. All associated result memory for the specified result identifier will automatically be freed. */ function _close() { if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false; if (!empty($this->_refcursor)) { OCIFreeCursor($this->_refcursor); $this->_refcursor = false; } @OCIFreeStatement($this->_queryID); $this->_queryID = false; } function MetaType($t,$len=-1) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'VARCHAR': case 'VARCHAR2': case 'CHAR': case 'VARBINARY': case 'BINARY': case 'NCHAR': case 'NVARCHAR': case 'NVARCHAR2': if (isset($this) && $len <= $this->blobSize) return 'C'; case 'NCLOB': case 'LONG': case 'LONG VARCHAR': case 'CLOB': return 'X'; case 'LONG RAW': case 'LONG VARBINARY': case 'BLOB': return 'B'; case 'DATE': return ($this->connection->datetime) ? 'T' : 'D'; case 'TIMESTAMP': return 'T'; case 'INT': case 'SMALLINT': case 'INTEGER': return 'I'; default: return 'N'; } } } class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 { function ADORecordSet_ext_oci8($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } switch ($mode) { case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; case ADODB_FETCH_DEFAULT: case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; case ADODB_FETCH_NUM: default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; } $this->adodbFetchMode = $mode; $this->_queryID = $queryID; } function MoveNext() { return adodb_movenext($this); } } ?> phpgacl-3.3.7/adodb/drivers/adodb-informix72.inc.php0100644025754300001440000003230410476657245021156 0ustar ipsousers */ // security - hide paths if (!defined('ADODB_DIR')) die(); if (!defined('IFX_SCROLL')) define('IFX_SCROLL',1); class ADODB_informix72 extends ADOConnection { var $databaseType = "informix72"; var $dataProvider = "informix"; var $replaceQuote = "''"; // string to use to replace quotes var $fmtDate = "'Y-m-d'"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; var $hasInsertID = true; var $hasAffectedRows = true; var $substr = 'substr'; var $metaTablesSQL="select tabname,tabtype from systables where tabtype in ('T','V') and owner!='informix'"; //Don't get informix tables and pseudo-tables var $metaColumnsSQL = "select c.colname, c.coltype, c.collength, d.default,c.colno from syscolumns c, systables t,outer sysdefaults d where c.tabid=t.tabid and d.tabid=t.tabid and d.colno=c.colno and tabname='%s' order by c.colno"; var $metaPrimaryKeySQL = "select part1,part2,part3,part4,part5,part6,part7,part8 from systables t,sysconstraints s,sysindexes i where t.tabname='%s' and s.tabid=t.tabid and s.constrtype='P' and i.idxname=s.idxname"; var $concat_operator = '||'; var $lastQuery = false; var $has_insertid = true; var $_autocommit = true; var $_bindInputArray = true; // set to true if ADOConnection.Execute() permits binding of array parameters. var $sysDate = 'TODAY'; var $sysTimeStamp = 'CURRENT'; var $cursorType = IFX_SCROLL; // IFX_SCROLL or IFX_HOLD or 0 function ADODB_informix72() { // alternatively, use older method: //putenv("DBDATE=Y4MD-"); // force ISO date format putenv('GL_DATE=%Y-%m-%d'); if (function_exists('ifx_byteasvarchar')) { ifx_byteasvarchar(1); // Mode "0" will return a blob id, and mode "1" will return a varchar with text content. ifx_textasvarchar(1); // Mode "0" will return a blob id, and mode "1" will return a varchar with text content. ifx_blobinfile_mode(0); // Mode "0" means save Byte-Blobs in memory, and mode "1" means save Byte-Blobs in a file. } } function ServerInfo() { if (isset($this->version)) return $this->version; $arr['description'] = $this->GetOne("select DBINFO('version','full') from systables where tabid = 1"); $arr['version'] = $this->GetOne("select DBINFO('version','major') || DBINFO('version','minor') from systables where tabid = 1"); $this->version = $arr; return $arr; } function _insertid() { $sqlca =ifx_getsqlca($this->lastQuery); return @$sqlca["sqlerrd1"]; } function _affectedrows() { if ($this->lastQuery) { return @ifx_affected_rows ($this->lastQuery); } return 0; } function BeginTrans() { if ($this->transOff) return true; $this->transCnt += 1; $this->Execute('BEGIN'); $this->_autocommit = false; return true; } function CommitTrans($ok=true) { if (!$ok) return $this->RollbackTrans(); if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->Execute('COMMIT'); $this->_autocommit = true; return true; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->Execute('ROLLBACK'); $this->_autocommit = true; return true; } function RowLock($tables,$where,$flds='1 as ignore') { if ($this->_autocommit) $this->BeginTrans(); return $this->GetOne("select $flds from $tables where $where for update"); } /* Returns: the last error message from previous database operation Note: This function is NOT available for Microsoft SQL Server. */ function ErrorMsg() { if (!empty($this->_logsql)) return $this->_errorMsg; $this->_errorMsg = ifx_errormsg(); return $this->_errorMsg; } function ErrorNo() { preg_match("/.*SQLCODE=([^\]]*)/",ifx_error(),$parse); if (is_array($parse) && isset($parse[1])) return (int)$parse[1]; return 0; } function &MetaColumns($table) { global $ADODB_FETCH_MODE; $false = false; if (!empty($this->metaColumnsSQL)) { $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if ($rs === false) return $false; $rspkey = $this->Execute(sprintf($this->metaPrimaryKeySQL,$table)); //Added to get primary key colno items $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; /* //!eos. $rs->fields[1] is not the correct adodb type $rs->fields[2] is not correct max_length, because can include not-null bit $fld->type = $rs->fields[1]; $fld->primary_key=$rspkey->fields && array_search($rs->fields[4],$rspkey->fields); //Added to set primary key flag $fld->max_length = $rs->fields[2];*/ $pr=ifx_props($rs->fields[1],$rs->fields[2]); //!eos $fld->type = $pr[0] ;//!eos $fld->primary_key=$rspkey->fields && array_search($rs->fields[4],$rspkey->fields); $fld->max_length = $pr[1]; //!eos $fld->precision = $pr[2] ;//!eos $fld->not_null = $pr[3]=="N"; //!eos if (trim($rs->fields[3]) != "AAAAAA 0") { $fld->has_default = 1; $fld->default_value = $rs->fields[3]; } else { $fld->has_default = 0; } $retarr[strtolower($fld->name)] = $fld; $rs->MoveNext(); } $rs->Close(); $rspkey->Close(); //!eos return $retarr; } return $false; } function &xMetaColumns($table) { return ADOConnection::MetaColumns($table,false); } function MetaForeignKeys($table, $owner=false, $upper=false) //!Eos { $sql = " select tr.tabname,updrule,delrule, i.part1 o1,i2.part1 d1,i.part2 o2,i2.part2 d2,i.part3 o3,i2.part3 d3,i.part4 o4,i2.part4 d4, i.part5 o5,i2.part5 d5,i.part6 o6,i2.part6 d6,i.part7 o7,i2.part7 d7,i.part8 o8,i2.part8 d8 from systables t,sysconstraints s,sysindexes i, sysreferences r,systables tr,sysconstraints s2,sysindexes i2 where t.tabname='$table' and s.tabid=t.tabid and s.constrtype='R' and r.constrid=s.constrid and i.idxname=s.idxname and tr.tabid=r.ptabid and s2.constrid=r.primary and i2.idxname=s2.idxname"; $rs = $this->Execute($sql); if (!$rs || $rs->EOF) return false; $arr =& $rs->GetArray(); $a = array(); foreach($arr as $v) { $coldest=$this->metaColumnNames($v["tabname"]); $colorig=$this->metaColumnNames($table); $colnames=array(); for($i=1;$i<=8 && $v["o$i"] ;$i++) { $colnames[]=$coldest[$v["d$i"]-1]."=".$colorig[$v["o$i"]-1]; } if($upper) $a[strtoupper($v["tabname"])] = $colnames; else $a[$v["tabname"]] = $colnames; } return $a; } function UpdateBlob($table, $column, $val, $where, $blobtype = 'BLOB') { $type = ($blobtype == 'TEXT') ? 1 : 0; $blobid = ifx_create_blob($type,0,$val); return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blobid)); } function BlobDecode($blobid) { return function_exists('ifx_byteasvarchar') ? $blobid : @ifx_get_blob($blobid); } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('ifx_connect')) return null; $dbs = $argDatabasename . "@" . $argHostname; if ($argHostname) putenv("INFORMIXSERVER=$argHostname"); putenv("INFORMIXSERVER=".trim($argHostname)); $this->_connectionID = ifx_connect($dbs,$argUsername,$argPassword); if ($this->_connectionID === false) return false; #if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (!function_exists('ifx_connect')) return null; $dbs = $argDatabasename . "@" . $argHostname; putenv("INFORMIXSERVER=".trim($argHostname)); $this->_connectionID = ifx_pconnect($dbs,$argUsername,$argPassword); if ($this->_connectionID === false) return false; #if ($argDatabasename) return $this->SelectDB($argDatabasename); return true; } /* // ifx_do does not accept bind parameters - weird ??? function Prepare($sql) { $stmt = ifx_prepare($sql); if (!$stmt) return $sql; else return array($sql,$stmt); } */ // returns query ID if successful, otherwise false function _query($sql,$inputarr) { global $ADODB_COUNTRECS; // String parameters have to be converted using ifx_create_char if ($inputarr) { foreach($inputarr as $v) { if (gettype($v) == 'string') { $tab[] = ifx_create_char($v); } else { $tab[] = $v; } } } // In case of select statement, we use a scroll cursor in order // to be able to call "move", or "movefirst" statements if (!$ADODB_COUNTRECS && preg_match("/^\s*select/is", $sql)) { if ($inputarr) { $this->lastQuery = ifx_query($sql,$this->_connectionID, $this->cursorType, $tab); } else { $this->lastQuery = ifx_query($sql,$this->_connectionID, $this->cursorType); } } else { if ($inputarr) { $this->lastQuery = ifx_query($sql,$this->_connectionID, $tab); } else { $this->lastQuery = ifx_query($sql,$this->_connectionID); } } // Following line have been commented because autocommit mode is // not supported by informix SE 7.2 //if ($this->_autocommit) ifx_query('COMMIT',$this->_connectionID); return $this->lastQuery; } // returns true or false function _close() { $this->lastQuery = false; return ifx_close($this->_connectionID); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_informix72 extends ADORecordSet { var $databaseType = "informix72"; var $canSeek = true; var $_fieldprops = false; function ADORecordset_informix72($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; return $this->ADORecordSet($id); } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &FetchField($fieldOffset = -1) { if (empty($this->_fieldprops)) { $fp = ifx_fieldproperties($this->_queryID); foreach($fp as $k => $v) { $o = new ADOFieldObject; $o->name = $k; $arr = split(';',$v); //"SQLTYPE;length;precision;scale;ISNULLABLE" $o->type = $arr[0]; $o->max_length = $arr[1]; $this->_fieldprops[] = $o; $o->not_null = $arr[4]=="N"; } } $ret = $this->_fieldprops[$fieldOffset]; return $ret; } function _initrs() { $this->_numOfRows = -1; // ifx_affected_rows not reliable, only returns estimate -- ($ADODB_COUNTRECS)? ifx_affected_rows($this->_queryID):-1; $this->_numOfFields = ifx_num_fields($this->_queryID); } function _seek($row) { return @ifx_fetch_row($this->_queryID, (int) $row); } function MoveLast() { $this->fields = @ifx_fetch_row($this->_queryID, "LAST"); if ($this->fields) $this->EOF = false; $this->_currentRow = -1; if ($this->fetchMode == ADODB_FETCH_NUM) { foreach($this->fields as $v) { $arr[] = $v; } $this->fields = $arr; } return true; } function MoveFirst() { $this->fields = @ifx_fetch_row($this->_queryID, "FIRST"); if ($this->fields) $this->EOF = false; $this->_currentRow = 0; if ($this->fetchMode == ADODB_FETCH_NUM) { foreach($this->fields as $v) { $arr[] = $v; } $this->fields = $arr; } return true; } function _fetch($ignore_fields=false) { $this->fields = @ifx_fetch_row($this->_queryID); if (!is_array($this->fields)) return false; if ($this->fetchMode == ADODB_FETCH_NUM) { foreach($this->fields as $v) { $arr[] = $v; } $this->fields = $arr; } return true; } /* close() only needs to be called if you are worried about using too much memory while your script is running. All associated result memory for the specified result identifier will automatically be freed. */ function _close() { return ifx_free_result($this->_queryID); } } /** !Eos * Auxiliar function to Parse coltype,collength. Used by Metacolumns * return: array ($mtype,$length,$precision,$nullable) (similar to ifx_fieldpropierties) */ function ifx_props($coltype,$collength){ $itype=fmod($coltype+1,256); $nullable=floor(($coltype+1) /256) ?"N":"Y"; $mtype=substr(" CIIFFNNDN TBXCC ",$itype,1); switch ($itype){ case 2: $length=4; case 6: case 9: case 14: $length=floor($collength/256); $precision=fmod($collength,256); break; default: $precision=0; $length=$collength; } return array($mtype,$length,$precision,$nullable); } ?>phpgacl-3.3.7/adodb/drivers/adodb-oracle.inc.php0100644025754300001440000002210010476657245020410 0ustar ipsousersfmtDate,$d).",'YYYY-MM-DD')"; } // format and return date string in database timestamp format function DBTimeStamp($ts) { if (is_string($ts)) $d = ADORecordSet::UnixTimeStamp($ts); return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; } function BindDate($d) { $d = ADOConnection::DBDate($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } function BindTimeStamp($d) { $d = ADOConnection::DBTimeStamp($d); if (strncmp($d,"'",1)) return $d; return substr($d,1,strlen($d)-2); } function BeginTrans() { $this->autoCommit = false; ora_commitoff($this->_connectionID); return true; } function CommitTrans($ok=true) { if (!$ok) return $this->RollbackTrans(); $ret = ora_commit($this->_connectionID); ora_commiton($this->_connectionID); return $ret; } function RollbackTrans() { $ret = ora_rollback($this->_connectionID); ora_commiton($this->_connectionID); return $ret; } /* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */ function ErrorMsg() { if ($this->_errorMsg !== false) return $this->_errorMsg; if (is_resource($this->_curs)) $this->_errorMsg = @ora_error($this->_curs); if (empty($this->_errorMsg)) $this->_errorMsg = @ora_error($this->_connectionID); return $this->_errorMsg; } function ErrorNo() { if ($this->_errorCode !== false) return $this->_errorCode; if (is_resource($this->_curs)) $this->_errorCode = @ora_errorcode($this->_curs); if (empty($this->_errorCode)) $this->_errorCode = @ora_errorcode($this->_connectionID); return $this->_errorCode; } // returns true or false function _connect($argHostname, $argUsername, $argPassword, $argDatabasename, $mode=0) { if (!function_exists('ora_plogon')) return null; // Reset error messages before connecting $this->_errorMsg = false; $this->_errorCode = false; // G. Giunta 2003/08/13 - This looks danegrously suspicious: why should we want to set // the oracle home to the host name of remote DB? // if ($argHostname) putenv("ORACLE_HOME=$argHostname"); if($argHostname) { // code copied from version submitted for oci8 by Jorma Tuomainen if (empty($argDatabasename)) $argDatabasename = $argHostname; else { if(strpos($argHostname,":")) { $argHostinfo=explode(":",$argHostname); $argHostname=$argHostinfo[0]; $argHostport=$argHostinfo[1]; } else { $argHostport="1521"; } if ($this->connectSID) { $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))"; } else $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; } } if ($argDatabasename) $argUsername .= "@$argDatabasename"; //if ($argHostname) print "

Connect: 1st argument should be left blank for $this->databaseType

"; if ($mode = 1) $this->_connectionID = ora_plogon($argUsername,$argPassword); else $this->_connectionID = ora_logon($argUsername,$argPassword); if ($this->_connectionID === false) return false; if ($this->autoCommit) ora_commiton($this->_connectionID); if ($this->_initdate) { $rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'"); if ($rs) ora_close($rs); } return true; } // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, 1); } // returns query ID if successful, otherwise false function _query($sql,$inputarr=false) { // Reset error messages before executing $this->_errorMsg = false; $this->_errorCode = false; $curs = ora_open($this->_connectionID); if ($curs === false) return false; $this->_curs = $curs; if (!ora_parse($curs,$sql)) return false; if (ora_exec($curs)) return $curs; // before we close the cursor, we have to store the error message // that we can obtain ONLY from the cursor (and not from the connection) $this->_errorCode = @ora_errorcode($curs); $this->_errorMsg = @ora_error($curs); // @ora_close($curs); return false; } // returns true or false function _close() { return @ora_logoff($this->_connectionID); } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordset_oracle extends ADORecordSet { var $databaseType = "oracle"; var $bind = false; function ADORecordset_oracle($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); if ($queryID) { $this->_currentRow = 0; $this->EOF = !$this->_fetch(); @$this->_initrs(); } else { $this->_numOfRows = 0; $this->_numOfFields = 0; $this->EOF = true; } return $this->_queryID; } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ function &FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; $fld->name = ora_columnname($this->_queryID, $fieldOffset); $fld->type = ora_columntype($this->_queryID, $fieldOffset); $fld->max_length = ora_columnsize($this->_queryID, $fieldOffset); return $fld; } /* Use associative array to get fields array */ function Fields($colname) { if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _initrs() { $this->_numOfRows = -1; $this->_numOfFields = @ora_numcols($this->_queryID); } function _seek($row) { return false; } function _fetch($ignore_fields=false) { // should remove call by reference, but ora_fetch_into requires it in 4.0.3pl1 if ($this->fetchMode & ADODB_FETCH_ASSOC) return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS|ORA_FETCHINTO_ASSOC); else return @ora_fetch_into($this->_queryID,&$this->fields,ORA_FETCHINTO_NULLS); } /* close() only needs to be called if you are worried about using too much memory while your script is running. All associated result memory for the specified result identifier will automatically be freed. */ function _close() { return @ora_close($this->_queryID); } function MetaType($t,$len=-1) { if (is_object($t)) { $fieldobj = $t; $t = $fieldobj->type; $len = $fieldobj->max_length; } switch (strtoupper($t)) { case 'VARCHAR': case 'VARCHAR2': case 'CHAR': case 'VARBINARY': case 'BINARY': if ($len <= $this->blobSize) return 'C'; case 'LONG': case 'LONG VARCHAR': case 'CLOB': return 'X'; case 'LONG RAW': case 'LONG VARBINARY': case 'BLOB': return 'B'; case 'DATE': return 'D'; //case 'T': return 'T'; case 'BIT': return 'L'; case 'INT': case 'SMALLINT': case 'INTEGER': return 'I'; default: return 'N'; } } } ?>phpgacl-3.3.7/adodb/drivers/adodb-odbc.inc.php0100644025754300001440000004606510476657245020072 0ustar ipsousers_haserrorfunctions = ADODB_PHPVER >= 0x4050; $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; } // returns true or false function _connect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; if (!function_exists('odbc_connect')) return null; if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') { ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter."); } if (isset($php_errormsg)) $php_errormsg = ''; if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); else $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode); $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; if (isset($this->connectStmt)) $this->Execute($this->connectStmt); return $this->_connectionID != false; } // returns true or false function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { global $php_errormsg; if (!function_exists('odbc_connect')) return null; if (isset($php_errormsg)) $php_errormsg = ''; $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; if ($this->debug && $argDatabasename) { ADOConnection::outp("For odbc PConnect(), $argDatabasename is not used. Place dsn in 1st parameter."); } // print "dsn=$argDSN u=$argUsername p=$argPassword
"; flush(); if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword); else $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,$this->curmode); $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; if ($this->_connectionID && $this->autoRollback) @odbc_rollback($this->_connectionID); if (isset($this->connectStmt)) $this->Execute($this->connectStmt); return $this->_connectionID != false; } function ServerInfo() { if (!empty($this->host) && ADODB_PHPVER >= 0x4300) { $dsn = strtoupper($this->host); $first = true; $found = false; if (!function_exists('odbc_data_source')) return false; while(true) { $rez = @odbc_data_source($this->_connectionID, $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); $first = false; if (!is_array($rez)) break; if (strtoupper($rez['server']) == $dsn) { $found = true; break; } } if (!$found) return ADOConnection::ServerInfo(); if (!isset($rez['version'])) $rez['version'] = ''; return $rez; } else { return ADOConnection::ServerInfo(); } } function CreateSequence($seqname='adodbseq',$start=1) { if (empty($this->_genSeqSQL)) return false; $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); if (!$ok) return false; $start -= 1; return $this->Execute("insert into $seqname values($start)"); } var $_dropSeqSQL = 'drop table %s'; function DropSequence($seqname) { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } /* This algorithm is not very efficient, but works even if table locking is not available. Will return false if unable to generate an ID after $MAXLOOPS attempts. */ function GenID($seq='adodbseq',$start=1) { // if you have to modify the parameter below, your database is overloaded, // or you need to implement generation of id's yourself! $MAXLOOPS = 100; //$this->debug=1; while (--$MAXLOOPS>=0) { $num = $this->GetOne("select id from $seq"); if ($num === false) { $this->Execute(sprintf($this->_genSeqSQL ,$seq)); $start -= 1; $num = '0'; $ok = $this->Execute("insert into $seq values($start)"); if (!$ok) return false; } $this->Execute("update $seq set id=id+1 where id=$num"); if ($this->affected_rows() > 0) { $num += 1; $this->genID = $num; return $num; } } if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); } return false; } function ErrorMsg() { if ($this->_haserrorfunctions) { if ($this->_errorMsg !== false) return $this->_errorMsg; if (empty($this->_connectionID)) return @odbc_errormsg(); return @odbc_errormsg($this->_connectionID); } else return ADOConnection::ErrorMsg(); } function ErrorNo() { if ($this->_haserrorfunctions) { if ($this->_errorCode !== false) { // bug in 4.0.6, error number can be corrupted string (should be 6 digits) return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode; } if (empty($this->_connectionID)) $e = @odbc_error(); else $e = @odbc_error($this->_connectionID); // bug in 4.0.6, error number can be corrupted string (should be 6 digits) // so we check and patch if (strlen($e)<=2) return 0; return $e; } else return ADOConnection::ErrorNo(); } function BeginTrans() { if (!$this->hasTransactions) return false; if ($this->transOff) return true; $this->transCnt += 1; $this->_autocommit = false; return odbc_autocommit($this->_connectionID,false); } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = odbc_commit($this->_connectionID); odbc_autocommit($this->_connectionID,true); return $ret; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->_autocommit = true; $ret = odbc_rollback($this->_connectionID); odbc_autocommit($this->_connectionID,true); return $ret; } function MetaPrimaryKeys($table) { global $ADODB_FETCH_MODE; if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = @odbc_primarykeys($this->_connectionID,'',$schema,$table); if (!$qid) { $ADODB_FETCH_MODE = $savem; return false; } $rs = new ADORecordSet_odbc($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return false; $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr =& $rs->GetArray(); $rs->Close(); //print_r($arr); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { if ($arr[$i][3]) $arr2[] = $arr[$i][3]; } return $arr2; } function &MetaTables($ttype=false) { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $qid = odbc_tables($this->_connectionID); $rs = new ADORecordSet_odbc($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) { $false = false; return $false; } $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $arr =& $rs->GetArray(); //print_r($arr); $rs->Close(); $arr2 = array(); if ($ttype) { $isview = strncmp($ttype,'V',1) === 0; } for ($i=0; $i < sizeof($arr); $i++) { if (!$arr[$i][2]) continue; $type = $arr[$i][3]; if ($ttype) { if ($isview) { if (strncmp($type,'V',1) === 0) $arr2[] = $arr[$i][2]; } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2]; } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2]; } return $arr2; } /* See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcdatetime_data_type_changes.asp / SQL data type codes / #define SQL_UNKNOWN_TYPE 0 #define SQL_CHAR 1 #define SQL_NUMERIC 2 #define SQL_DECIMAL 3 #define SQL_INTEGER 4 #define SQL_SMALLINT 5 #define SQL_FLOAT 6 #define SQL_REAL 7 #define SQL_DOUBLE 8 #if (ODBCVER >= 0x0300) #define SQL_DATETIME 9 #endif #define SQL_VARCHAR 12 / One-parameter shortcuts for date/time data types / #if (ODBCVER >= 0x0300) #define SQL_TYPE_DATE 91 #define SQL_TYPE_TIME 92 #define SQL_TYPE_TIMESTAMP 93 #define SQL_UNICODE (-95) #define SQL_UNICODE_VARCHAR (-96) #define SQL_UNICODE_LONGVARCHAR (-97) */ function ODBCTypes($t) { switch ((integer)$t) { case 1: case 12: case 0: case -95: case -96: return 'C'; case -97: case -1: //text return 'X'; case -4: //image return 'B'; case 9: case 91: return 'D'; case 10: case 11: case 92: case 93: return 'T'; case 4: case 5: case -6: return 'I'; case -11: // uniqidentifier return 'R'; case -7: //bit return 'L'; default: return 'N'; } } function &MetaColumns($table) { global $ADODB_FETCH_MODE; $false = false; if ($this->uCaseTables) $table = strtoupper($table); $schema = ''; $this->_findschema($table,$schema); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; /*if (false) { // after testing, confirmed that the following does not work becoz of a bug $qid2 = odbc_tables($this->_connectionID); $rs = new ADORecordSet_odbc($qid2); $ADODB_FETCH_MODE = $savem; if (!$rs) return false; $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $rs->_fetch(); while (!$rs->EOF) { if ($table == strtoupper($rs->fields[2])) { $q = $rs->fields[0]; $o = $rs->fields[1]; break; } $rs->MoveNext(); } $rs->Close(); $qid = odbc_columns($this->_connectionID,$q,$o,strtoupper($table),'%'); } */ switch ($this->databaseType) { case 'access': case 'vfp': $qid = odbc_columns($this->_connectionID);#,'%','',strtoupper($table),'%'); break; case 'db2': $colname = "%"; $qid = odbc_columns($this->_connectionID, "", $schema, $table, $colname); break; default: $qid = @odbc_columns($this->_connectionID,'%','%',strtoupper($table),'%'); if (empty($qid)) $qid = odbc_columns($this->_connectionID); break; } if (empty($qid)) return $false; $rs =& new ADORecordSet_odbc($qid); $ADODB_FETCH_MODE = $savem; if (!$rs) return $false; $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change; $rs->_fetch(); $retarr = array(); /* $rs->fields indices 0 TABLE_QUALIFIER 1 TABLE_SCHEM 2 TABLE_NAME 3 COLUMN_NAME 4 DATA_TYPE 5 TYPE_NAME 6 PRECISION 7 LENGTH 8 SCALE 9 RADIX 10 NULLABLE 11 REMARKS */ while (!$rs->EOF) { // adodb_pr($rs->fields); if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[3]; $fld->type = $this->ODBCTypes($rs->fields[4]); // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp // access uses precision to store length for char/varchar if ($fld->type == 'C' or $fld->type == 'X') { if ($this->databaseType == 'access') $fld->max_length = $rs->fields[6]; else if ($rs->fields[4] <= -95) // UNICODE $fld->max_length = $rs->fields[7]/2; else $fld->max_length = $rs->fields[7]; } else $fld->max_length = $rs->fields[7]; $fld->not_null = !empty($rs->fields[10]); $fld->scale = $rs->fields[8]; $retarr[strtoupper($fld->name)] = $fld; } else if (sizeof($retarr)>0) break; $rs->MoveNext(); } $rs->Close(); //-- crashes 4.03pl1 -- why? if (empty($retarr)) $retarr = false; return $retarr; } function Prepare($sql) { if (! $this->_bindInputArray) return $sql; // no binding $stmt = odbc_prepare($this->_connectionID,$sql); if (!$stmt) { // we don't know whether odbc driver is parsing prepared stmts, so just return sql return $sql; } return array($sql,$stmt,false); } /* returns queryID or false */ function _query($sql,$inputarr=false) { GLOBAL $php_errormsg; if (isset($php_errormsg)) $php_errormsg = ''; $this->_error = ''; if ($inputarr) { if (is_array($sql)) { $stmtid = $sql[1]; } else { $stmtid = odbc_prepare($this->_connectionID,$sql); if ($stmtid == false) { $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; return false; } } if (! odbc_execute($stmtid,$inputarr)) { //@odbc_free_result($stmtid); if ($this->_haserrorfunctions) { $this->_errorMsg = odbc_errormsg(); $this->_errorCode = odbc_error(); } return false; } } else if (is_array($sql)) { $stmtid = $sql[1]; if (!odbc_execute($stmtid)) { //@odbc_free_result($stmtid); if ($this->_haserrorfunctions) { $this->_errorMsg = odbc_errormsg(); $this->_errorCode = odbc_error(); } return false; } } else $stmtid = odbc_exec($this->_connectionID,$sql); $this->_lastAffectedRows = 0; if ($stmtid) { if (@odbc_num_fields($stmtid) == 0) { $this->_lastAffectedRows = odbc_num_rows($stmtid); $stmtid = true; } else { $this->_lastAffectedRows = 0; odbc_binmode($stmtid,$this->binmode); odbc_longreadlen($stmtid,$this->maxblobsize); } if ($this->_haserrorfunctions) { $this->_errorMsg = ''; $this->_errorCode = 0; } else $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; } else { if ($this->_haserrorfunctions) { $this->_errorMsg = odbc_errormsg(); $this->_errorCode = odbc_error(); } else $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : ''; } return $stmtid; } /* Insert a null into the blob field of the table first. Then use UpdateBlob to store the blob. Usage: $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') { return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false; } // returns true or false function _close() { $ret = @odbc_close($this->_connectionID); $this->_connectionID = false; return $ret; } function _affectedrows() { return $this->_lastAffectedRows; } } /*-------------------------------------------------------------------------------------- Class Name: Recordset --------------------------------------------------------------------------------------*/ class ADORecordSet_odbc extends ADORecordSet { var $bind = false; var $databaseType = "odbc"; var $dataProvider = "odbc"; var $useFetchArray; var $_has_stupid_odbc_fetch_api_change; function ADORecordSet_odbc($id,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; $this->_queryID = $id; // the following is required for mysql odbc driver in 4.3.1 -- why? $this->EOF = false; $this->_currentRow = -1; //$this->ADORecordSet($id); } // returns the field object function &FetchField($fieldOffset = -1) { $off=$fieldOffset+1; // offsets begin at 1 $o= new ADOFieldObject(); $o->name = @odbc_field_name($this->_queryID,$off); $o->type = @odbc_field_type($this->_queryID,$off); $o->max_length = @odbc_field_len($this->_queryID,$off); if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); return $o; } /* Use associative array to get fields array */ function Fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $o = $this->FetchField($i); $this->bind[strtoupper($o->name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _initrs() { global $ADODB_COUNTRECS; $this->_numOfRows = ($ADODB_COUNTRECS) ? @odbc_num_rows($this->_queryID) : -1; $this->_numOfFields = @odbc_num_fields($this->_queryID); // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0 if ($this->_numOfRows == 0) $this->_numOfRows = -1; //$this->useFetchArray = $this->connection->useFetchArray; $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200; } function _seek($row) { return false; } // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated function &GetArrayLimit($nrows,$offset=-1) { if ($offset <= 0) { $rs =& $this->GetArray($nrows); return $rs; } $savem = $this->fetchMode; $this->fetchMode = ADODB_FETCH_NUM; $this->Move($offset); $this->fetchMode = $savem; if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } $results = array(); $cnt = 0; while (!$this->EOF && $nrows != $cnt) { $results[$cnt++] = $this->fields; $this->MoveNext(); } return $results; } function MoveNext() { if ($this->_numOfRows != 0 && !$this->EOF) { $this->_currentRow++; if ($this->_has_stupid_odbc_fetch_api_change) $rez = @odbc_fetch_into($this->_queryID,$this->fields); else { $row = 0; $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); } if ($rez) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } } $this->fields = false; $this->EOF = true; return false; } function _fetch() { if ($this->_has_stupid_odbc_fetch_api_change) $rez = @odbc_fetch_into($this->_queryID,$this->fields); else { $row = 0; $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields); } if ($rez) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE); } return true; } $this->fields = false; return false; } function _close() { return @odbc_free_result($this->_queryID); } } ?>phpgacl-3.3.7/adodb/drivers/adodb-odbtp.inc.php0100644025754300001440000005037610476657245020273 0ustar ipsousers // security - hide paths if (!defined('ADODB_DIR')) die(); define("_ADODB_ODBTP_LAYER", 2 ); class ADODB_odbtp extends ADOConnection{ var $databaseType = "odbtp"; var $dataProvider = "odbtp"; var $fmtDate = "'Y-m-d'"; var $fmtTimeStamp = "'Y-m-d, h:i:sA'"; var $replaceQuote = "''"; // string to use to replace quotes var $odbc_driver = 0; var $hasAffectedRows = true; var $hasInsertID = false; var $hasGenID = true; var $hasMoveFirst = true; var $_genSeqSQL = "create table %s (seq_name char(30) not null unique , seq_value integer not null)"; var $_dropSeqSQL = "delete from adodb_seq where seq_name = '%s'"; var $_bindInputArray = false; var $_useUnicodeSQL = false; var $_canPrepareSP = false; var $_dontPoolDBC = true; function ADODB_odbtp() { } function ServerInfo() { return array('description' => @odbtp_get_attr( ODB_ATTR_DBMSNAME, $this->_connectionID), 'version' => @odbtp_get_attr( ODB_ATTR_DBMSVER, $this->_connectionID)); } function ErrorMsg() { if (empty($this->_connectionID)) return @odbtp_last_error(); return @odbtp_last_error($this->_connectionID); } function ErrorNo() { if (empty($this->_connectionID)) return @odbtp_last_error_state(); return @odbtp_last_error_state($this->_connectionID); } function _insertid() { // SCOPE_IDENTITY() // Returns the last IDENTITY value inserted into an IDENTITY column in // the same scope. A scope is a module -- a stored procedure, trigger, // function, or batch. Thus, two statements are in the same scope if // they are in the same stored procedure, function, or batch. return $this->GetOne($this->identitySQL); } function _affectedrows() { if ($this->_queryID) { return @odbtp_affected_rows ($this->_queryID); } else return 0; } function CreateSequence($seqname='adodbseq',$start=1) { //verify existence $num = $this->GetOne("select seq_value from adodb_seq"); $seqtab='adodb_seq'; if( $this->odbc_driver == ODB_DRIVER_FOXPRO ) { $path = @odbtp_get_attr( ODB_ATTR_DATABASENAME, $this->_connectionID ); //if using vfp dbc file if( !strcasecmp(strrchr($path, '.'), '.dbc') ) $path = substr($path,0,strrpos($path,'\/')); $seqtab = $path . '/' . $seqtab; } if($num == false) { if (empty($this->_genSeqSQL)) return false; $ok = $this->Execute(sprintf($this->_genSeqSQL ,$seqtab)); } $num = $this->GetOne("select seq_value from adodb_seq where seq_name='$seqname'"); if ($num) { return false; } $start -= 1; return $this->Execute("insert into adodb_seq values('$seqname',$start)"); } function DropSequence($seqname) { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } function GenID($seq='adodbseq',$start=1) { $seqtab='adodb_seq'; if( $this->odbc_driver == ODB_DRIVER_FOXPRO) { $path = @odbtp_get_attr( ODB_ATTR_DATABASENAME, $this->_connectionID ); //if using vfp dbc file if( !strcasecmp(strrchr($path, '.'), '.dbc') ) $path = substr($path,0,strrpos($path,'\/')); $seqtab = $path . '/' . $seqtab; } $MAXLOOPS = 100; while (--$MAXLOOPS>=0) { $num = $this->GetOne("select seq_value from adodb_seq where seq_name='$seq'"); if ($num === false) { //verify if abodb_seq table exist $ok = $this->GetOne("select seq_value from adodb_seq "); if(!$ok) { //creating the sequence table adodb_seq $this->Execute(sprintf($this->_genSeqSQL ,$seqtab)); } $start -= 1; $num = '0'; $ok = $this->Execute("insert into adodb_seq values('$seq',$start)"); if (!$ok) return false; } $ok = $this->Execute("update adodb_seq set seq_value=seq_value+1 where seq_name='$seq'"); if($ok) { $num += 1; $this->genID = $num; return $num; } } if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); } return false; } //example for $UserOrDSN //for visual fox : DRIVER={Microsoft Visual FoxPro Driver};SOURCETYPE=DBF;SOURCEDB=c:\YourDbfFileDir;EXCLUSIVE=NO; //for visual fox dbc: DRIVER={Microsoft Visual FoxPro Driver};SOURCETYPE=DBC;SOURCEDB=c:\YourDbcFileDir\mydb.dbc;EXCLUSIVE=NO; //for access : DRIVER={Microsoft Access Driver (*.mdb)};DBQ=c:\path_to_access_db\base_test.mdb;UID=root;PWD=; //for mssql : DRIVER={SQL Server};SERVER=myserver;UID=myuid;PWD=mypwd;DATABASE=OdbtpTest; //if uid & pwd can be separate function _connect($HostOrInterface, $UserOrDSN='', $argPassword='', $argDatabase='') { $this->_connectionID = @odbtp_connect($HostOrInterface,$UserOrDSN,$argPassword,$argDatabase); odbtp_convert_datetime($this->_connectionID,true); if ($this->_connectionID === false) { $this->_errorMsg = $this->ErrorMsg() ; return false; } if ($this->_dontPoolDBC) { if (function_exists('odbtp_dont_pool_dbc')) @odbtp_dont_pool_dbc($this->_connectionID); } else { $this->_dontPoolDBC = true; } $this->odbc_driver = @odbtp_get_attr(ODB_ATTR_DRIVER, $this->_connectionID); $dbms = strtolower(@odbtp_get_attr(ODB_ATTR_DBMSNAME, $this->_connectionID)); $this->odbc_name = $dbms; // Account for inconsistent DBMS names if( $this->odbc_driver == ODB_DRIVER_ORACLE ) $dbms = 'oracle'; else if( $this->odbc_driver == ODB_DRIVER_SYBASE ) $dbms = 'sybase'; // Set DBMS specific attributes switch( $dbms ) { case 'microsoft sql server': $this->databaseType = 'odbtp_mssql'; $this->fmtDate = "'Y-m-d'"; $this->fmtTimeStamp = "'Y-m-d h:i:sA'"; $this->sysDate = 'convert(datetime,convert(char,GetDate(),102),102)'; $this->sysTimeStamp = 'GetDate()'; $this->ansiOuter = true; $this->leftOuter = '*='; $this->rightOuter = '=*'; $this->hasTop = 'top'; $this->hasInsertID = true; $this->hasTransactions = true; $this->_bindInputArray = true; $this->_canSelectDb = true; $this->substr = "substring"; $this->length = 'len'; $this->identitySQL = 'select @@IDENTITY'; $this->metaDatabasesSQL = "select name from master..sysdatabases where name <> 'master'"; $this->_canPrepareSP = true; break; case 'access': $this->databaseType = 'odbtp_access'; $this->fmtDate = "#Y-m-d#"; $this->fmtTimeStamp = "#Y-m-d h:i:sA#"; $this->sysDate = "FORMAT(NOW,'yyyy-mm-dd')"; $this->sysTimeStamp = 'NOW'; $this->hasTop = 'top'; $this->hasTransactions = false; $this->_canPrepareSP = true; // For MS Access only. break; case 'visual foxpro': $this->databaseType = 'odbtp_vfp'; $this->fmtDate = "{^Y-m-d}"; $this->fmtTimeStamp = "{^Y-m-d, h:i:sA}"; $this->sysDate = 'date()'; $this->sysTimeStamp = 'datetime()'; $this->ansiOuter = true; $this->hasTop = 'top'; $this->hasTransactions = false; $this->replaceQuote = "'+chr(39)+'"; $this->true = '.T.'; $this->false = '.F.'; break; case 'oracle': $this->databaseType = 'odbtp_oci8'; $this->fmtDate = "'Y-m-d 00:00:00'"; $this->fmtTimeStamp = "'Y-m-d h:i:sA'"; $this->sysDate = 'TRUNC(SYSDATE)'; $this->sysTimeStamp = 'SYSDATE'; $this->hasTransactions = true; $this->_bindInputArray = true; $this->concat_operator = '||'; break; case 'sybase': $this->databaseType = 'odbtp_sybase'; $this->fmtDate = "'Y-m-d'"; $this->fmtTimeStamp = "'Y-m-d H:i:s'"; $this->sysDate = 'GetDate()'; $this->sysTimeStamp = 'GetDate()'; $this->leftOuter = '*='; $this->rightOuter = '=*'; $this->hasInsertID = true; $this->hasTransactions = true; $this->identitySQL = 'select @@IDENTITY'; break; default: $this->databaseType = 'odbtp'; if( @odbtp_get_attr(ODB_ATTR_TXNCAPABLE, $this->_connectionID) ) $this->hasTransactions = true; else $this->hasTransactions = false; } @odbtp_set_attr(ODB_ATTR_FULLCOLINFO, TRUE, $this->_connectionID ); if ($this->_useUnicodeSQL ) @odbtp_set_attr(ODB_ATTR_UNICODESQL, TRUE, $this->_connectionID); return true; } function _pconnect($HostOrInterface, $UserOrDSN='', $argPassword='', $argDatabase='') { $this->_dontPoolDBC = false; return $this->_connect($HostOrInterface, $UserOrDSN, $argPassword, $argDatabase); } function SelectDB($dbName) { if (!@odbtp_select_db($dbName, $this->_connectionID)) { return false; } $this->database = $dbName; $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions return true; } function &MetaTables($ttype='',$showSchema=false,$mask=false) { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savefm = $this->SetFetchMode(false); $arr =& $this->GetArray("||SQLTables||||$ttype"); if (isset($savefm)) $this->SetFetchMode($savefm); $ADODB_FETCH_MODE = $savem; $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { if ($arr[$i][3] == 'SYSTEM TABLE' ) continue; if ($arr[$i][2]) $arr2[] = $showSchema ? $arr[$i][1].'.'.$arr[$i][2] : $arr[$i][2]; } return $arr2; } function &MetaColumns($table,$upper=true) { global $ADODB_FETCH_MODE; $schema = false; $this->_findschema($table,$schema); if ($upper) $table = strtoupper($table); $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savefm = $this->SetFetchMode(false); $rs = $this->Execute( "||SQLColumns||$schema|$table" ); if (isset($savefm)) $this->SetFetchMode($savefm); $ADODB_FETCH_MODE = $savem; if (!$rs || $rs->EOF) { $false = false; return $false; } $retarr = array(); while (!$rs->EOF) { //print_r($rs->fields); if (strtoupper($rs->fields[2]) == $table) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[3]; $fld->type = $rs->fields[5]; $fld->max_length = $rs->fields[6]; $fld->not_null = !empty($rs->fields[9]); $fld->scale = $rs->fields[7]; if (!is_null($rs->fields[12])) { $fld->has_default = true; $fld->default_value = $rs->fields[12]; } $retarr[strtoupper($fld->name)] = $fld; } else if (!empty($retarr)) break; $rs->MoveNext(); } $rs->Close(); return $retarr; } function &MetaPrimaryKeys($table, $owner='') { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $arr =& $this->GetArray("||SQLPrimaryKeys||$owner|$table"); $ADODB_FETCH_MODE = $savem; //print_r($arr); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { if ($arr[$i][3]) $arr2[] = $arr[$i][3]; } return $arr2; } function &MetaForeignKeys($table, $owner='', $upper=false) { global $ADODB_FETCH_MODE; $savem = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; $constraints =& $this->GetArray("||SQLForeignKeys|||||$owner|$table"); $ADODB_FETCH_MODE = $savem; $arr = false; foreach($constraints as $constr) { //print_r($constr); $arr[$constr[11]][$constr[2]][] = $constr[7].'='.$constr[3]; } if (!$arr) { $false = false; return $false; } $arr2 = array(); foreach($arr as $k => $v) { foreach($v as $a => $b) { if ($upper) $a = strtoupper($a); $arr2[$a] = $b; } } return $arr2; } function BeginTrans() { if (!$this->hasTransactions) return false; if ($this->transOff) return true; $this->transCnt += 1; $this->autoCommit = false; if (defined('ODB_TXN_DEFAULT')) $txn = ODB_TXN_DEFAULT; else $txn = ODB_TXN_READUNCOMMITTED; $rs = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS,$txn,$this->_connectionID); if(!$rs) return false; return true; } function CommitTrans($ok=true) { if ($this->transOff) return true; if (!$ok) return $this->RollbackTrans(); if ($this->transCnt) $this->transCnt -= 1; $this->autoCommit = true; if( ($ret = @odbtp_commit($this->_connectionID)) ) $ret = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS, ODB_TXN_NONE, $this->_connectionID);//set transaction off return $ret; } function RollbackTrans() { if ($this->transOff) return true; if ($this->transCnt) $this->transCnt -= 1; $this->autoCommit = true; if( ($ret = @odbtp_rollback($this->_connectionID)) ) $ret = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS, ODB_TXN_NONE, $this->_connectionID);//set transaction off return $ret; } function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) { // TOP requires ORDER BY for Visual FoxPro if( $this->odbc_driver == ODB_DRIVER_FOXPRO ) { if (!preg_match('/ORDER[ \t\r\n]+BY/is',$sql)) $sql .= ' ORDER BY 1'; } $ret =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); return $ret; } function Prepare($sql) { if (! $this->_bindInputArray) return $sql; // no binding $stmt = @odbtp_prepare($sql,$this->_connectionID); if (!$stmt) { // print "Prepare Error for ($sql) ".$this->ErrorMsg()."
"; return $sql; } return array($sql,$stmt,false); } function PrepareSP($sql) { if (!$this->_canPrepareSP) return $sql; // Can't prepare procedures $stmt = @odbtp_prepare_proc($sql,$this->_connectionID); if (!$stmt) return false; return array($sql,$stmt); } /* Usage: $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group # note that the parameter does not have @ in front! $db->Parameter($stmt,$id,'myid'); $db->Parameter($stmt,$group,'group',false,64); $db->Parameter($stmt,$group,'photo',false,100000,ODB_BINARY); $db->Execute($stmt); @param $stmt Statement returned by Prepare() or PrepareSP(). @param $var PHP variable to bind to. Can set to null (for isNull support). @param $name Name of stored procedure variable name to bind to. @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in odbtp. @param [$maxLen] Holds an maximum length of the variable. @param [$type] The data type of $var. Legal values depend on driver. See odbtp_attach_param documentation at http://odbtp.sourceforge.net. */ function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=0, $type=0) { if ( $this->odbc_driver == ODB_DRIVER_JET ) { $name = '['.$name.']'; if( !$type && $this->_useUnicodeSQL && @odbtp_param_bindtype($stmt[1], $name) == ODB_CHAR ) { $type = ODB_WCHAR; } } else { $name = '@'.$name; } return @odbtp_attach_param($stmt[1], $name, $var, $type, $maxLen); } /* Insert a null into the blob field of the table first. Then use UpdateBlob to store the blob. Usage: $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1'); */ function UpdateBlob($table,$column,$val,$where,$blobtype='image') { $sql = "UPDATE $table SET $column = ? WHERE $where"; if( !($stmt = @odbtp_prepare($sql, $this->_connectionID)) ) return false; if( !@odbtp_input( $stmt, 1, ODB_BINARY, 1000000, $blobtype ) ) return false; if( !@odbtp_set( $stmt, 1, $val ) ) return false; return @odbtp_execute( $stmt ) != false; } function IfNull( $field, $ifNull ) { switch( $this->odbc_driver ) { case ODB_DRIVER_MSSQL: return " ISNULL($field, $ifNull) "; case ODB_DRIVER_JET: return " IIF(IsNull($field), $ifNull, $field) "; } return " CASE WHEN $field is null THEN $ifNull ELSE $field END "; } function _query($sql,$inputarr=false) { global $php_errormsg; if ($inputarr) { if (is_array($sql)) { $stmtid = $sql[1]; } else { $stmtid = @odbtp_prepare($sql,$this->_connectionID); if ($stmtid == false) { $this->_errorMsg = $php_errormsg; return false; } } $num_params = @odbtp_num_params( $stmtid ); for( $param = 1; $param <= $num_params; $param++ ) { @odbtp_input( $stmtid, $param ); @odbtp_set( $stmtid, $param, $inputarr[$param-1] ); } if (!@odbtp_execute($stmtid) ) { return false; } } else if (is_array($sql)) { $stmtid = $sql[1]; if (!@odbtp_execute($stmtid)) { return false; } } else { $stmtid = @odbtp_query($sql,$this->_connectionID); } $this->_lastAffectedRows = 0; if ($stmtid) { $this->_lastAffectedRows = @odbtp_affected_rows($stmtid); } return $stmtid; } function _close() { $ret = @odbtp_close($this->_connectionID); $this->_connectionID = false; return $ret; } } class ADORecordSet_odbtp extends ADORecordSet { var $databaseType = 'odbtp'; var $canSeek = true; function ADORecordSet_odbtp($queryID,$mode=false) { if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; } $this->fetchMode = $mode; $this->ADORecordSet($queryID); } function _initrs() { $this->_numOfFields = @odbtp_num_fields($this->_queryID); if (!($this->_numOfRows = @odbtp_num_rows($this->_queryID))) $this->_numOfRows = -1; if (!$this->connection->_useUnicodeSQL) return; if ($this->connection->odbc_driver == ODB_DRIVER_JET) { if (!@odbtp_get_attr(ODB_ATTR_MAPCHARTOWCHAR, $this->connection->_connectionID)) { for ($f = 0; $f < $this->_numOfFields; $f++) { if (@odbtp_field_bindtype($this->_queryID, $f) == ODB_CHAR) @odbtp_bind_field($this->_queryID, $f, ODB_WCHAR); } } } } function &FetchField($fieldOffset = 0) { $off=$fieldOffset; // offsets begin at 0 $o= new ADOFieldObject(); $o->name = @odbtp_field_name($this->_queryID,$off); $o->type = @odbtp_field_type($this->_queryID,$off); $o->max_length = @odbtp_field_length($this->_queryID,$off); if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name); else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name); return $o; } function _seek($row) { return @odbtp_data_seek($this->_queryID, $row); } function fields($colname) { if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname]; if (!$this->bind) { $this->bind = array(); for ($i=0; $i < $this->_numOfFields; $i++) { $name = @odbtp_field_name( $this->_queryID, $i ); $this->bind[strtoupper($name)] = $i; } } return $this->fields[$this->bind[strtoupper($colname)]]; } function _fetch_odbtp($type=0) { switch ($this->fetchMode) { case ADODB_FETCH_NUM: $this->fields = @odbtp_fetch_row($this->_queryID, $type); break; case ADODB_FETCH_ASSOC: $this->fields = @odbtp_fetch_assoc($this->_queryID, $type); break; default: $this->fields = @odbtp_fetch_array($this->_queryID, $type); } return is_array($this->fields); } function _fetch() { return $this->_fetch_odbtp(); } function MoveFirst() { if (!$this->_fetch_odbtp(ODB_FETCH_FIRST)) return false; $this->EOF = false; $this->_currentRow = 0; return true; } function MoveLast() { if (!$this->_fetch_odbtp(ODB_FETCH_LAST)) return false; $this->EOF = false; $this->_currentRow = $this->_numOfRows - 1; return true; } function NextRecordSet() { if (!@odbtp_next_result($this->_queryID)) return false; $this->_inited = false; $this->bind = false; $this->_currentRow = -1; $this->Init(); return true; } function _close() { return @odbtp_free_query($this->_queryID); } } class ADORecordSet_odbtp_mssql extends ADORecordSet_odbtp { var $databaseType = 'odbtp_mssql'; function ADORecordSet_odbtp_mssql($id,$mode=false) { return $this->ADORecordSet_odbtp($id,$mode); } } class ADORecordSet_odbtp_access extends ADORecordSet_odbtp { var $databaseType = 'odbtp_access'; function ADORecordSet_odbtp_access($id,$mode=false) { return $this->ADORecordSet_odbtp($id,$mode); } } class ADORecordSet_odbtp_vfp extends ADORecordSet_odbtp { var $databaseType = 'odbtp_vfp'; function ADORecordSet_odbtp_vfp($id,$mode=false) { return $this->ADORecordSet_odbtp($id,$mode); } } class ADORecordSet_odbtp_oci8 extends ADORecordSet_odbtp { var $databaseType = 'odbtp_oci8'; function ADORecordSet_odbtp_oci8($id,$mode=false) { return $this->ADORecordSet_odbtp($id,$mode); } } class ADORecordSet_odbtp_sybase extends ADORecordSet_odbtp { var $databaseType = 'odbtp_sybase'; function ADORecordSet_odbtp_sybase($id,$mode=false) { return $this->ADORecordSet_odbtp($id,$mode); } } ?> phpgacl-3.3.7/adodb/adodb-errorpear.inc.php0100644025754300001440000000434510476657245017501 0ustar ipsousers!$s

"; } /** * Returns last PEAR_Error object. This error might be for an error that * occured several sql statements ago. */ function &ADODB_PEAR_Error() { global $ADODB_Last_PEAR_Error; return $ADODB_Last_PEAR_Error; } ?>phpgacl-3.3.7/adodb/adodb-pager.inc.php0100644025754300001440000001766710476657245016611 0ustar ipsousers implemented Render_PageLinks(). Please note, this class is entirely unsupported, and no free support requests except for bug reports will be entertained by the author. */ class ADODB_Pager { var $id; // unique id for pager (defaults to 'adodb') var $db; // ADODB connection object var $sql; // sql used var $rs; // recordset generated var $curr_page; // current page number before Render() called, calculated in constructor var $rows; // number of rows per page var $linksPerPage=10; // number of links per page in navigation bar var $showPageLinks; var $gridAttributes = 'width=100% border=1 bgcolor=white'; // Localize text strings here var $first = '|<'; var $prev = '<<'; var $next = '>>'; var $last = '>|'; var $moreLinks = '...'; var $startLinks = '...'; var $gridHeader = false; var $htmlSpecialChars = true; var $page = 'Page'; var $linkSelectedColor = 'red'; var $cache = 0; #secs to cache with CachePageExecute() //---------------------------------------------- // constructor // // $db adodb connection object // $sql sql statement // $id optional id to identify which pager, // if you have multiple on 1 page. // $id should be only be [a-z0-9]* // function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false) { global $PHP_SELF; $curr_page = $id.'_curr_page'; if (empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks $this->sql = $sql; $this->id = $id; $this->db = $db; $this->showPageLinks = $showPageLinks; $next_page = $id.'_next_page'; if (isset($_GET[$next_page])) { $_SESSION[$curr_page] = (integer) $_GET[$next_page]; } if (empty($_SESSION[$curr_page])) $_SESSION[$curr_page] = 1; ## at first page $this->curr_page = $_SESSION[$curr_page]; } //--------------------------- // Display link to first page function Render_First($anchor=true) { global $PHP_SELF; if ($anchor) { ?> first;?>   first   "; } } //-------------------------- // Display link to next page function render_next($anchor=true) { global $PHP_SELF; if ($anchor) { ?> next;?>   next   "; } } //------------------ // Link to last page // // for better performance with large recordsets, you can set // $this->db->pageExecuteCountRows = false, which disables // last page counting. function render_last($anchor=true) { global $PHP_SELF; if (!$this->db->pageExecuteCountRows) return; if ($anchor) { ?> last;?>   last   "; } } //--------------------------------------------------- // original code by "Pablo Costa" function render_pagelinks() { global $PHP_SELF; $pages = $this->rs->LastPageNo(); $linksperpage = $this->linksPerPage ? $this->linksPerPage : $pages; for($i=1; $i <= $pages; $i+=$linksperpage) { if($this->rs->AbsolutePage() >= $i) { $start = $i; } } $numbers = ''; $end = $start+$linksperpage-1; $link = $this->id . "_next_page"; if($end > $pages) $end = $pages; if ($this->startLinks && $start > 1) { $pos = $start - 1; $numbers .= "$this->startLinks "; } for($i=$start; $i <= $end; $i++) { if ($this->rs->AbsolutePage() == $i) $numbers .= "linkSelectedColor>$i "; else $numbers .= "$i "; } if ($this->moreLinks && $end < $pages) $numbers .= "$this->moreLinks "; print $numbers . '   '; } // Link to previous page function render_prev($anchor=true) { global $PHP_SELF; if ($anchor) { ?> prev;?>   prev   "; } } //-------------------------------------------------------- // Simply rendering of grid. You should override this for // better control over the format of the grid // // We use output buffering to keep code clean and readable. function RenderGrid() { global $gSQLBlockRows; // used by rs2html to indicate how many rows to display include_once(ADODB_DIR.'/tohtml.inc.php'); ob_start(); $gSQLBlockRows = $this->rows; rs2html($this->rs,$this->gridAttributes,$this->gridHeader,$this->htmlSpecialChars); $s = ob_get_contents(); ob_end_clean(); return $s; } //------------------------------------------------------- // Navigation bar // // we use output buffering to keep the code easy to read. function RenderNav() { ob_start(); if (!$this->rs->AtFirstPage()) { $this->Render_First(); $this->Render_Prev(); } else { $this->Render_First(false); $this->Render_Prev(false); } if ($this->showPageLinks){ $this->Render_PageLinks(); } if (!$this->rs->AtLastPage()) { $this->Render_Next(); $this->Render_Last(); } else { $this->Render_Next(false); $this->Render_Last(false); } $s = ob_get_contents(); ob_end_clean(); return $s; } //------------------- // This is the footer function RenderPageCount() { if (!$this->db->pageExecuteCountRows) return ''; $lastPage = $this->rs->LastPageNo(); if ($lastPage == -1) $lastPage = 1; // check for empty rs. if ($this->curr_page > $lastPage) $this->curr_page = 1; return "$this->page ".$this->curr_page."/".$lastPage.""; } //----------------------------------- // Call this class to draw everything. function Render($rows=10) { global $ADODB_COUNTRECS; $this->rows = $rows; if ($this->db->dataProvider == 'informix') $this->db->cursorType = IFX_SCROLL; $savec = $ADODB_COUNTRECS; if ($this->db->pageExecuteCountRows) $ADODB_COUNTRECS = true; if ($this->cache) $rs = &$this->db->CachePageExecute($this->cache,$this->sql,$rows,$this->curr_page); else $rs = &$this->db->PageExecute($this->sql,$rows,$this->curr_page); $ADODB_COUNTRECS = $savec; $this->rs = &$rs; if (!$rs) { print "

Query failed: $this->sql

"; return; } if (!$rs->EOF && (!$rs->AtFirstPage() || !$rs->AtLastPage())) $header = $this->RenderNav(); else $header = " "; $grid = $this->RenderGrid(); $footer = $this->RenderPageCount(); $this->RenderLayout($header,$grid,$footer); $rs->Close(); $this->rs = false; } //------------------------------------------------------ // override this to control overall layout and formating function RenderLayout($header,$grid,$footer,$attributes='border=1 bgcolor=beige') { echo "
", $header, "
", $grid, "
", $footer, "
"; } } ?>phpgacl-3.3.7/adodb/pivottable.inc.php0100644025754300001440000001420410476657245016575 0ustar ipsousersdatabaseType,'access') !== false; // note - vfp 6 still doesn' work even with IIF enabled || $db->databaseType == 'vfp'; //$hidecnt = false; if ($where) $where = "\nWHERE $where"; if (!is_array($colfield)) $colarr = $db->GetCol("select distinct $colfield from $tables $where order by 1"); if (!$aggfield) $hidecnt = false; $sel = "$rowfields, "; if (is_array($colfield)) { foreach ($colfield as $k => $v) { $k = trim($k); if (!$hidecnt) { $sel .= $iif ? "\n\t$aggfn(IIF($v,1,0)) AS \"$k\", " : "\n\t$aggfn(CASE WHEN $v THEN 1 ELSE 0 END) AS \"$k\", "; } if ($aggfield) { $sel .= $iif ? "\n\t$aggfn(IIF($v,$aggfield,0)) AS \"$sumlabel$k\", " : "\n\t$aggfn(CASE WHEN $v THEN $aggfield ELSE 0 END) AS \"$sumlabel$k\", "; } } } else { foreach ($colarr as $v) { if (!is_numeric($v)) $vq = $db->qstr($v); else $vq = $v; $v = trim($v); if (strlen($v) == 0 ) $v = 'null'; if (!$hidecnt) { $sel .= $iif ? "\n\t$aggfn(IIF($colfield=$vq,1,0)) AS \"$v\", " : "\n\t$aggfn(CASE WHEN $colfield=$vq THEN 1 ELSE 0 END) AS \"$v\", "; } if ($aggfield) { if ($hidecnt) $label = $v; else $label = "{$v}_$aggfield"; $sel .= $iif ? "\n\t$aggfn(IIF($colfield=$vq,$aggfield,0)) AS \"$label\", " : "\n\t$aggfn(CASE WHEN $colfield=$vq THEN $aggfield ELSE 0 END) AS \"$label\", "; } } } if ($aggfield && $aggfield != '1'){ $agg = "$aggfn($aggfield)"; $sel .= "\n\t$agg as \"$sumlabel$aggfield\", "; } if ($showcount) $sel .= "\n\tSUM(1) as Total"; else $sel = substr($sel,0,strlen($sel)-2); // Strip aliases $rowfields = preg_replace('/ AS (\w+)/i', '', $rowfields); $sql = "SELECT $sel \nFROM $tables $where \nGROUP BY $rowfields"; return $sql; } /* EXAMPLES USING MS NORTHWIND DATABASE */ if (0) { # example1 # # Query the main "product" table # Set the rows to CompanyName and QuantityPerUnit # and the columns to the Categories # and define the joins to link to lookup tables # "categories" and "suppliers" # $sql = PivotTableSQL( $gDB, # adodb connection 'products p ,categories c ,suppliers s', # tables 'CompanyName,QuantityPerUnit', # row fields 'CategoryName', # column fields 'p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID' # joins/where ); print "
$sql";
 $rs = $gDB->Execute($sql);
 rs2html($rs);
 
/*
Generated SQL:

SELECT CompanyName,QuantityPerUnit, 
	SUM(CASE WHEN CategoryName='Beverages' THEN 1 ELSE 0 END) AS "Beverages", 
	SUM(CASE WHEN CategoryName='Condiments' THEN 1 ELSE 0 END) AS "Condiments", 
	SUM(CASE WHEN CategoryName='Confections' THEN 1 ELSE 0 END) AS "Confections", 
	SUM(CASE WHEN CategoryName='Dairy Products' THEN 1 ELSE 0 END) AS "Dairy Products", 
	SUM(CASE WHEN CategoryName='Grains/Cereals' THEN 1 ELSE 0 END) AS "Grains/Cereals", 
	SUM(CASE WHEN CategoryName='Meat/Poultry' THEN 1 ELSE 0 END) AS "Meat/Poultry", 
	SUM(CASE WHEN CategoryName='Produce' THEN 1 ELSE 0 END) AS "Produce", 
	SUM(CASE WHEN CategoryName='Seafood' THEN 1 ELSE 0 END) AS "Seafood", 
	SUM(1) as Total 
FROM products p ,categories c ,suppliers s  WHERE p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID 
GROUP BY CompanyName,QuantityPerUnit
*/
//=====================================================================

# example2
#
# Query the main "product" table
# Set the rows to CompanyName and QuantityPerUnit
# and the columns to the UnitsInStock for diiferent ranges
# and define the joins to link to lookup tables 
# "categories" and "suppliers"
#
 $sql = PivotTableSQL(
 	$gDB,										# adodb connection
 	'products p ,categories c ,suppliers s',	# tables
	'CompanyName,QuantityPerUnit',				# row fields
												# column ranges
array(										
' 0 ' => 'UnitsInStock <= 0',
"1 to 5" => '0 < UnitsInStock and UnitsInStock <= 5',
"6 to 10" => '5 < UnitsInStock and UnitsInStock <= 10',
"11 to 15"  => '10 < UnitsInStock and UnitsInStock <= 15',
"16+" =>'15 < UnitsInStock'
),
	' p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID', # joins/where
	'UnitsInStock', 							# sum this field
	'Sum'										# sum label prefix
);
 print "
$sql";
 $rs = $gDB->Execute($sql);
 rs2html($rs);
 /*
 Generated SQL:
 
SELECT CompanyName,QuantityPerUnit, 
	SUM(CASE WHEN UnitsInStock <= 0 THEN UnitsInStock ELSE 0 END) AS "Sum  0 ", 
	SUM(CASE WHEN 0 < UnitsInStock and UnitsInStock <= 5 THEN UnitsInStock ELSE 0 END) AS "Sum 1 to 5", 
	SUM(CASE WHEN 5 < UnitsInStock and UnitsInStock <= 10 THEN UnitsInStock ELSE 0 END) AS "Sum 6 to 10", 
	SUM(CASE WHEN 10 < UnitsInStock and UnitsInStock <= 15 THEN UnitsInStock ELSE 0 END) AS "Sum 11 to 15", 
	SUM(CASE WHEN 15 < UnitsInStock THEN UnitsInStock ELSE 0 END) AS "Sum 16+",
	SUM(UnitsInStock) AS "Sum UnitsInStock", 
	SUM(1) as Total 
FROM products p ,categories c ,suppliers s  WHERE  p.CategoryID = c.CategoryID and s.SupplierID= p.SupplierID 
GROUP BY CompanyName,QuantityPerUnit
 */
}
?>phpgacl-3.3.7/adodb/readme.txt0100644025754300001440000000320610152170437015121 0ustar  ipsousers>> ADODB Library for PHP4

(c) 2000-2004 John Lim (jlim@natsoft.com.my)

Released under both BSD and GNU Lesser GPL library license. 
This means you can use it in proprietary products.
 
 
>> Introduction

PHP's database access functions are not standardised. This creates a 
need for a database class library to hide the differences between the 
different databases (encapsulate the differences) so we can easily 
switch databases.

We currently support MySQL, Interbase, Sybase, PostgreSQL, Oracle, 
Microsoft SQL server,  Foxpro ODBC, Access ODBC, Informix, DB2,
Sybase SQL Anywhere, generic ODBC and Microsoft's ADO. 

We hope more people will contribute drivers to support other databases.


>> Documentation and Examples

Refer to the adodb/docs directory for full documentation and examples. 
There is also a  tutorial tute.htm that contrasts ADODB code with 
mysql code.


>>> Files
Adodb.inc.php is the main file. You need to include only this file.

Adodb-*.inc.php are the database specific driver code.

Test.php contains a list of test commands to exercise the class library.

Adodb-session.php is the PHP4 session handling code.

Testdatabases.inc.php contains the list of databases to apply the tests on.

Benchmark.php is a simple benchmark to test the throughput of a simple SELECT 
statement for databases described in testdatabases.inc.php. The benchmark
tables are created in test.php.

readme.htm is the main documentation.

tute.htm is the tutorial.


>> More Info

For more information, including installation see readme.htm
or visit
           http://adodb.sourceforge.net/


>> Feature Requests and Bug Reports

Email to jlim@natsoft.com.my 


 phpgacl-3.3.7/adodb/adodb-lib.inc.php0100644025754300001440000010257410476657245016251 0ustar  ipsousers sizeof($array)) $max = sizeof($array);
	else $max = $probe;
	
	
	for ($j=0;$j < $max; $j++) {
		$row =& $array[$j];
		if (!$row) break;
		$i = -1;
		foreach($row as $v) {
			$i += 1;

			if (isset($types[$i]) && $types[$i]=='C') continue;
			
			//print " ($i ".$types[$i]. "$v) ";
			$v = trim($v);
			
			if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
				$types[$i] = 'C'; // once C, always C
				
				continue;
			}
			if ($j == 0) { 
			// If empty string, we presume is character
			// test for integer for 1st row only
			// after that it is up to testing other rows to prove
			// that it is not an integer
				if (strlen($v) == 0) $types[$i] = 'C';
				if (strpos($v,'.') !== false) $types[$i] = 'N';
				else  $types[$i] = 'I';
				continue;
			}
			
			if (strpos($v,'.') !== false) $types[$i] = 'N';
			
		}
	}
}

function  &adodb_transpose(&$arr, &$newarr, &$hdr)
{
	$oldX = sizeof(reset($arr));
	$oldY = sizeof($arr);	
	
	if ($hdr) {
		$startx = 1;
		$hdr = array();
		for ($y = 0; $y < $oldY; $y++) {
			$hdr[] = $arr[$y][0];
		}
	} else
		$startx = 0;

	for ($x = $startx; $x < $oldX; $x++) {
		$newarr[] = array();
		for ($y = 0; $y < $oldY; $y++) {
			$newarr[$x-$startx][] = $arr[$y][$x];
		}
	}
}

// Force key to upper. 
// See also http://www.php.net/manual/en/function.array-change-key-case.php
function _array_change_key_case($an_array)
{
	if (is_array($an_array)) {
		$new_array = array();
		foreach($an_array as $key=>$value)
			$new_array[strtoupper($key)] = $value;

	   	return $new_array;
   }

	return $an_array;
}

function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
{
		if (count($fieldArray) == 0) return 0;
		$first = true;
		$uSet = '';
		
		if (!is_array($keyCol)) {
			$keyCol = array($keyCol);
		}
		foreach($fieldArray as $k => $v) {
			if ($autoQuote && !is_numeric($v) and strncmp($v,"'",1) !== 0 and strcasecmp($v,'null')!=0) {
				$v = $zthis->qstr($v);
				$fieldArray[$k] = $v;
			}
			if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
			
			if ($first) {
				$first = false;			
				$uSet = "$k=$v";
			} else
				$uSet .= ",$k=$v";
		}
		 
		$where = false;
		foreach ($keyCol as $v) {
			if (isset($fieldArray[$v])) {
				if ($where) $where .= ' and '.$v.'='.$fieldArray[$v];
				else $where = $v.'='.$fieldArray[$v];
			}
		}
		
		if ($uSet && $where) {
			$update = "UPDATE $table SET $uSet WHERE $where";

			$rs = $zthis->Execute($update);
			
			
			if ($rs) {
				if ($zthis->poorAffectedRows) {
				/*
				 The Select count(*) wipes out any errors that the update would have returned. 
				http://phplens.com/lens/lensforum/msgs.php?id=5696
				*/
					if ($zthis->ErrorNo()<>0) return 0;
					
				# affected_rows == 0 if update field values identical to old values
				# for mysql - which is silly. 
			
					$cnt = $zthis->GetOne("select count(*) from $table where $where");
					if ($cnt > 0) return 1; // record already exists
				} else {
					if (($zthis->Affected_Rows()>0)) return 1;
				}
			} else
				return 0;
		}
		
	//	print "

Error=".$this->ErrorNo().'

'; $first = true; foreach($fieldArray as $k => $v) { if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col if ($first) { $first = false; $iCols = "$k"; $iVals = "$v"; } else { $iCols .= ",$k"; $iVals .= ",$v"; } } $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)"; $rs = $zthis->Execute($insert); return ($rs) ? 2 : 0; } // Requires $ADODB_FETCH_MODE = ADODB_FETCH_NUM function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false, $size=0, $selectAttr='',$compareFields0=true) { $hasvalue = false; if ($multiple or is_array($defstr)) { if ($size==0) $size=5; $attr = ' multiple size="'.$size.'"'; if (!strpos($name,'[]')) $name .= '[]'; } else if ($size) $attr = ' size="'.$size.'"'; else $attr =''; $s = '\n"; } // Requires $ADODB_FETCH_MODE = ADODB_FETCH_NUM function _adodb_getmenu_gp(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false, $size=0, $selectAttr='',$compareFields0=true) { $hasvalue = false; if ($multiple or is_array($defstr)) { if ($size==0) $size=5; $attr = ' multiple size="'.$size.'"'; if (!strpos($name,'[]')) $name .= '[]'; } else if ($size) $attr = ' size="'.$size.'"'; else $attr =''; $s = '\n"; } /* Count the number of records this sql statement will return by using query rewriting heuristics... Does not work with UNIONs, except with postgresql and oracle. Usage: $conn->Connect(...); $cnt = _adodb_getcount($conn, $sql); */ function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0) { $qryRecs = 0; if (!empty($zthis->_nestedSQL) || preg_match("/^\s*SELECT\s+DISTINCT/is", $sql) || preg_match('/\s+GROUP\s+BY\s+/is',$sql) || preg_match('/\s+UNION\s+/is',$sql)) { // ok, has SELECT DISTINCT or GROUP BY so see if we can use a table alias // but this is only supported by oracle and postgresql... if ($zthis->dataProvider == 'oci8') { $rewritesql = preg_replace('/(\sORDER\s+BY\s[^)]*)/is','',$sql); // Allow Oracle hints to be used for query optimization, Chris Wrye if (preg_match('#/\\*+.*?\\*\\/#', $sql, $hint)) { $rewritesql = "SELECT ".$hint[0]." COUNT(*) FROM (".$rewritesql.")"; } else $rewritesql = "SELECT COUNT(*) FROM (".$rewritesql.")"; } else if (strncmp($zthis->databaseType,'postgres',8) == 0) { $rewritesql = preg_replace('/(\sORDER\s+BY\s[^)]*)/is','',$sql); $rewritesql = "SELECT COUNT(*) FROM ($rewritesql) _ADODB_ALIAS_"; } } else { // now replace SELECT ... FROM with SELECT COUNT(*) FROM $rewritesql = preg_replace( '/^\s*SELECT\s.*\s+FROM\s/Uis','SELECT COUNT(*) FROM ',$sql); // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails // with mssql, access and postgresql. Also a good speedup optimization - skips sorting! // also see http://phplens.com/lens/lensforum/msgs.php?id=12752 if (preg_match('/\sORDER\s+BY\s*\(/i',$rewritesql)) $rewritesql = preg_replace('/(\sORDER\s+BY\s.*)/is','',$rewritesql); else $rewritesql = preg_replace('/(\sORDER\s+BY\s[^)]*)/is','',$rewritesql); } if (isset($rewritesql) && $rewritesql != $sql) { if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[1]; if ($secs2cache) { // we only use half the time of secs2cache because the count can quickly // become inaccurate if new records are added $qryRecs = $zthis->CacheGetOne($secs2cache/2,$rewritesql,$inputarr); } else { $qryRecs = $zthis->GetOne($rewritesql,$inputarr); } if ($qryRecs !== false) return $qryRecs; } //-------------------------------------------- // query rewrite failed - so try slower way... // strip off unneeded ORDER BY if no UNION if (preg_match('/\s*UNION\s*/is', $sql)) $rewritesql = $sql; else $rewritesql = preg_replace('/(\sORDER\s+BY\s.*)/is','',$sql); if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[0]; $rstest = &$zthis->Execute($rewritesql,$inputarr); if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr); if ($rstest) { $qryRecs = $rstest->RecordCount(); if ($qryRecs == -1) { global $ADODB_EXTENSION; // some databases will return -1 on MoveLast() - change to MoveNext() if ($ADODB_EXTENSION) { while(!$rstest->EOF) { adodb_movenext($rstest); } } else { while(!$rstest->EOF) { $rstest->MoveNext(); } } $qryRecs = $rstest->_currentRow; } $rstest->Close(); if ($qryRecs == -1) return 0; } return $qryRecs; } /* Code originally from "Cornel G" This code might not work with SQL that has UNION in it Also if you are using CachePageExecute(), there is a strong possibility that data will get out of synch. use CachePageExecute() only with tables that rarely change. */ function &_adodb_pageexecute_all_rows(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0) { $atfirstpage = false; $atlastpage = false; $lastpageno=1; // If an invalid nrows is supplied, // we assume a default value of 10 rows per page if (!isset($nrows) || $nrows <= 0) $nrows = 10; $qryRecs = false; //count records for no offset $qryRecs = _adodb_getcount($zthis,$sql,$inputarr,$secs2cache); $lastpageno = (int) ceil($qryRecs / $nrows); $zthis->_maxRecordCount = $qryRecs; // ***** Here we check whether $page is the last page or // whether we are trying to retrieve // a page number greater than the last page number. if ($page >= $lastpageno) { $page = $lastpageno; $atlastpage = true; } // If page number <= 1, then we are at the first page if (empty($page) || $page <= 1) { $page = 1; $atfirstpage = true; } // We get the data we want $offset = $nrows * ($page-1); if ($secs2cache > 0) $rsreturn = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr); else $rsreturn = &$zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache); // Before returning the RecordSet, we set the pagination properties we need if ($rsreturn) { $rsreturn->_maxRecordCount = $qryRecs; $rsreturn->rowsPerPage = $nrows; $rsreturn->AbsolutePage($page); $rsreturn->AtFirstPage($atfirstpage); $rsreturn->AtLastPage($atlastpage); $rsreturn->LastPageNo($lastpageno); } return $rsreturn; } // Ivn Oliva version function &_adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0) { $atfirstpage = false; $atlastpage = false; if (!isset($page) || $page <= 1) { // If page number <= 1, then we are at the first page $page = 1; $atfirstpage = true; } if ($nrows <= 0) $nrows = 10; // If an invalid nrows is supplied, we assume a default value of 10 rows per page // ***** Here we check whether $page is the last page or whether we are trying to retrieve a page number greater than // the last page number. $pagecounter = $page + 1; $pagecounteroffset = ($pagecounter * $nrows) - $nrows; if ($secs2cache>0) $rstest = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr); else $rstest = &$zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache); if ($rstest) { while ($rstest && $rstest->EOF && $pagecounter>0) { $atlastpage = true; $pagecounter--; $pagecounteroffset = $nrows * ($pagecounter - 1); $rstest->Close(); if ($secs2cache>0) $rstest = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr); else $rstest = &$zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache); } if ($rstest) $rstest->Close(); } if ($atlastpage) { // If we are at the last page or beyond it, we are going to retrieve it $page = $pagecounter; if ($page == 1) $atfirstpage = true; // We have to do this again in case the last page is the same as the first //... page, that is, the recordset has only 1 page. } // We get the data we want $offset = $nrows * ($page-1); if ($secs2cache > 0) $rsreturn = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr); else $rsreturn = &$zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache); // Before returning the RecordSet, we set the pagination properties we need if ($rsreturn) { $rsreturn->rowsPerPage = $nrows; $rsreturn->AbsolutePage($page); $rsreturn->AtFirstPage($atfirstpage); $rsreturn->AtLastPage($atlastpage); } return $rsreturn; } function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=2) { if (!$rs) { printf(ADODB_BAD_RS,'GetUpdateSQL'); return false; } $fieldUpdatedCount = 0; $arrFields = _array_change_key_case($arrFields); $hasnumeric = isset($rs->fields[0]); $setFields = ''; // Loop through all of the fields in the recordset for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { // Get the field from the recordset $field = $rs->FetchField($i); // If the recordset field is one // of the fields passed in then process. $upperfname = strtoupper($field->name); if (adodb_key_exists($upperfname,$arrFields,$force)) { // If the existing field value in the recordset // is different from the value passed in then // go ahead and append the field name and new value to // the update query. if ($hasnumeric) $val = $rs->fields[$i]; else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname]; else if (isset($rs->fields[$field->name])) $val = $rs->fields[$field->name]; else if (isset($rs->fields[strtolower($upperfname)])) $val = $rs->fields[strtolower($upperfname)]; else $val = ''; if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) { // Set the counter for the number of fields that will be updated. $fieldUpdatedCount++; // Based on the datatype of the field // Format the value properly for the database $type = $rs->MetaType($field->type); if ($type == 'null') { $type = 'C'; } if (strpos($upperfname,' ') !== false) $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote; else $fnameq = $upperfname; // is_null requires php 4.0.4 //********************************************************// if (is_null($arrFields[$upperfname]) || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0) || $arrFields[$upperfname] === 'null' ) { switch ($force) { //case 0: // //Ignore empty values. This is allready handled in "adodb_key_exists" function. //break; case 1: //Set null $setFields .= $field->name . " = null, "; break; case 2: //Set empty $arrFields[$upperfname] = ""; $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq); break; default: case 3: //Set the value that was given in array, so you can give both null and empty values if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === 'null') { $setFields .= $field->name . " = null, "; } else { $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq); } break; } //********************************************************// } else { //we do this so each driver can customize the sql for //DB specific column types. //Oracle needs BLOB types to be handled with a returning clause //postgres has special needs as well $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields, $magicq); } } } } // If there were any modified fields then build the rest of the update query. if ($fieldUpdatedCount > 0 || $forceUpdate) { // Get the table name from the existing query. if (!empty($rs->tableName)) $tableName = $rs->tableName; else { preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName); $tableName = $tableName[1]; } // Get the full where clause excluding the word "WHERE" from // the existing query. preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause); $discard = false; // not a good hack, improvements? if ($whereClause) { #var_dump($whereClause); if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard)); else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard)); else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard)); else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see http://sourceforge.net/tracker/index.php?func=detail&aid=1379638&group_id=42718&atid=433976 } else $whereClause = array(false,false); if ($discard) $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1])); $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2); if (strlen($whereClause[1]) > 0) $sql .= ' WHERE '.$whereClause[1]; return $sql; } else { return false; } } function adodb_key_exists($key, &$arr,$force=2) { if ($force<=0) { // the following is the old behaviour where null or empty fields are ignored return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0); } if (isset($arr[$key])) return true; ## null check below if (ADODB_PHPVER >= 0x4010) return array_key_exists($key,$arr); return false; } /** * There is a special case of this function for the oci8 driver. * The proper way to handle an insert w/ a blob in oracle requires * a returning clause with bind variables and a descriptor blob. * * */ function _adodb_getinsertsql(&$zthis,&$rs,$arrFields,$magicq=false,$force=2) { static $cacheRS = false; static $cacheSig = 0; static $cacheCols; $tableName = ''; $values = ''; $fields = ''; $recordSet = null; $arrFields = _array_change_key_case($arrFields); $fieldInsertedCount = 0; if (is_string($rs)) { //ok we have a table name //try and get the column info ourself. $tableName = $rs; //we need an object for the recordSet //because we have to call MetaType. //php can't do a $rsclass::MetaType() $rsclass = $zthis->rsPrefix.$zthis->databaseType; $recordSet = new $rsclass(-1,$zthis->fetchMode); $recordSet->connection = &$zthis; if (is_string($cacheRS) && $cacheRS == $rs) { $columns =& $cacheCols; } else { $columns = $zthis->MetaColumns( $tableName ); $cacheRS = $tableName; $cacheCols = $columns; } } else if (is_subclass_of($rs, 'adorecordset')) { if (isset($rs->insertSig) && is_integer($cacheRS) && $cacheRS == $rs->insertSig) { $columns =& $cacheCols; } else { for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) $columns[] = $rs->FetchField($i); $cacheRS = $cacheSig; $cacheCols = $columns; $rs->insertSig = $cacheSig++; } $recordSet =& $rs; } else { printf(ADODB_BAD_RS,'GetInsertSQL'); return false; } // Loop through all of the fields in the recordset foreach( $columns as $field ) { $upperfname = strtoupper($field->name); if (adodb_key_exists($upperfname,$arrFields,$force)) { $bad = false; if (strpos($upperfname,' ') !== false) $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote; else $fnameq = $upperfname; $type = $recordSet->MetaType($field->type); /********************************************************/ if (is_null($arrFields[$upperfname]) || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0) || $arrFields[$upperfname] === 'null' ) { switch ($force) { case 0: // we must always set null if missing $bad = true; break; case 1: $values .= "null, "; break; case 2: //Set empty $arrFields[$upperfname] = ""; $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,$arrFields, $magicq); break; default: case 3: //Set the value that was given in array, so you can give both null and empty values if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === 'null') { $values .= "null, "; } else { $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq); } break; } // switch /*********************************************************/ } else { //we do this so each driver can customize the sql for //DB specific column types. //Oracle needs BLOB types to be handled with a returning clause //postgres has special needs as well $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq); } if ($bad) continue; // Set the counter for the number of fields that will be inserted. $fieldInsertedCount++; // Get the name of the fields to insert $fields .= $fnameq . ", "; } } // If there were any inserted fields then build the rest of the insert query. if ($fieldInsertedCount <= 0) return false; // Get the table name from the existing query. if (!$tableName) { if (!empty($rs->tableName)) $tableName = $rs->tableName; else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName)) $tableName = $tableName[1]; else return false; } // Strip off the comma and space on the end of both the fields // and their values. $fields = substr($fields, 0, -2); $values = substr($values, 0, -2); // Append the fields and their values to the insert query. return 'INSERT INTO '.$tableName.' ( '.$fields.' ) VALUES ( '.$values.' )'; } /** * This private method is used to help construct * the update/sql which is generated by GetInsertSQL and GetUpdateSQL. * It handles the string construction of 1 column -> sql string based on * the column type. We want to do 'safe' handling of BLOBs * * @param string the type of sql we are trying to create * 'I' or 'U'. * @param string column data type from the db::MetaType() method * @param string the column name * @param array the column value * * @return string * */ function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields, $magicq) { $sql = ''; // Based on the datatype of the field // Format the value properly for the database switch($type) { case 'B': //in order to handle Blobs correctly, we need //to do some magic for Oracle //we need to create a new descriptor to handle //this properly if (!empty($zthis->hasReturningInto)) { if ($action == 'I') { $sql = 'empty_blob(), '; } else { $sql = $fnameq. '=empty_blob(), '; } //add the variable to the returning clause array //so the user can build this later in //case they want to add more to it $zthis->_returningArray[$fname] = ':xx'.$fname.'xx'; } else if (empty($arrFields[$fname])){ if ($action == 'I') { $sql = 'empty_blob(), '; } else { $sql = $fnameq. '=empty_blob(), '; } } else { //this is to maintain compatibility //with older adodb versions. $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false); } break; case "X": //we need to do some more magic here for long variables //to handle these correctly in oracle. //create a safe bind var name //to avoid conflicts w/ dupes. if (!empty($zthis->hasReturningInto)) { if ($action == 'I') { $sql = ':xx'.$fname.'xx, '; } else { $sql = $fnameq.'=:xx'.$fname.'xx, '; } //add the variable to the returning clause array //so the user can build this later in //case they want to add more to it $zthis->_returningArray[$fname] = ':xx'.$fname.'xx'; } else { //this is to maintain compatibility //with older adodb versions. $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false); } break; default: $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false); break; } return $sql; } function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq, $recurse=true) { if ($recurse) { switch($zthis->dataProvider) { case 'postgres': if ($type == 'L') $type = 'C'; break; case 'oci8': return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq); } } switch($type) { case "C": case "X": case 'B': $val = $zthis->qstr($arrFields[$fname],$magicq); break; case "D": $val = $zthis->DBDate($arrFields[$fname]); break; case "T": $val = $zthis->DBTimeStamp($arrFields[$fname]); break; default: $val = $arrFields[$fname]; if (empty($val)) $val = '0'; break; } if ($action == 'I') return $val . ", "; return $fnameq . "=" . $val . ", "; } function _adodb_debug_execute(&$zthis, $sql, $inputarr) { $ss = ''; if ($inputarr) { foreach($inputarr as $kk=>$vv) { if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...'; $ss .= "($kk=>'$vv') "; } $ss = "[ $ss ]"; } $sqlTxt = is_array($sql) ? $sql[0] : $sql; /*str_replace(', ','##1#__^LF',is_array($sql) ? $sql[0] : $sql); $sqlTxt = str_replace(',',', ',$sqlTxt); $sqlTxt = str_replace('##1#__^LF', ', ' ,$sqlTxt); */ // check if running from browser or command-line $inBrowser = isset($_SERVER['HTTP_USER_AGENT']); $dbt = $zthis->databaseType; if (isset($zthis->dsnType)) $dbt .= '-'.$zthis->dsnType; if ($inBrowser) { if ($ss) { $ss = ''.htmlspecialchars($ss).''; } if ($zthis->debug === -1) ADOConnection::outp( "
\n($dbt): ".htmlspecialchars($sqlTxt)."   $ss\n
\n",false); else ADOConnection::outp( "


\n($dbt): ".htmlspecialchars($sqlTxt)."   $ss\n
\n",false); } else { ADOConnection::outp("-----\n($dbt): ".$sqlTxt."\n-----\n",false); } $qID = $zthis->_query($sql,$inputarr); /* Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion */ if ($zthis->databaseType == 'mssql') { // ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6 if($emsg = $zthis->ErrorMsg()) { if ($err = $zthis->ErrorNo()) ADOConnection::outp($err.': '.$emsg); } } else if (!$qID) { ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg()); } if ($zthis->debug === 99) _adodb_backtrace(true,9999,2); return $qID; } # pretty print the debug_backtrace function function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0) { if (!function_exists('debug_backtrace')) return ''; $html = (isset($_SERVER['HTTP_USER_AGENT'])); $fmt = ($html) ? " %% line %4d, file: %s" : "%% line %4d, file: %s"; $MAXSTRLEN = 128; $s = ($html) ? '
' : '';
	
	if (is_array($printOrArr)) $traceArr = $printOrArr;
	else $traceArr = debug_backtrace();
	array_shift($traceArr);
	array_shift($traceArr);
	$tabs = sizeof($traceArr)-2;
	
	foreach ($traceArr as $arr) {
		if ($skippy) {$skippy -= 1; continue;}
		$levels -= 1;
		if ($levels < 0) break;
		
		$args = array();
		for ($i=0; $i < $tabs; $i++) $s .=  ($html) ? '   ' : "\t";
		$tabs -= 1;
		if ($html) $s .= '';
		if (isset($arr['class'])) $s .= $arr['class'].'.';
		if (isset($arr['args']))
		 foreach($arr['args'] as $v) {
			if (is_null($v)) $args[] = 'null';
			else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
			else if (is_object($v)) $args[] = 'Object:'.get_class($v);
			else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
			else {
				$v = (string) @$v;
				$str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
				if (strlen($v) > $MAXSTRLEN) $str .= '...';
				$args[] = $str;
			}
		}
		$s .= $arr['function'].'('.implode(', ',$args).')';
		
		
		$s .= @sprintf($fmt, $arr['line'],$arr['file'],basename($arr['file']));
			
		$s .= "\n";
	}	
	if ($html) $s .= '
'; if ($printOrArr) print $s; return $s; } /* function _adodb_find_from($sql) { $sql = str_replace(array("\n","\r"), ' ', $sql); $charCount = strlen($sql); $inString = false; $quote = ''; $parentheseCount = 0; $prevChars = ''; $nextChars = ''; for($i = 0; $i < $charCount; $i++) { $char = substr($sql,$i,1); $prevChars = substr($sql,0,$i); $nextChars = substr($sql,$i+1); if((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === false) { $quote = $char; $inString = true; } elseif((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === true && $quote == $char) { $quote = ""; $inString = false; } elseif($char == "(" && $inString === false) $parentheseCount++; elseif($char == ")" && $inString === false && $parentheseCount > 0) $parentheseCount--; elseif($parentheseCount <= 0 && $inString === false && $char == " " && strtoupper(substr($prevChars,-5,5)) == " FROM") return $i; } } */ ?>phpgacl-3.3.7/gacl.ini.php0100644025754300001440000000126410270773532014257 0ustar ipsousers; ; ; *WARNING* ; ; DO NOT PUT THIS FILE IN YOUR WEBROOT DIRECTORY. ; ; *WARNING* ; ; Anyone can view your database password if you do! ; debug = FALSE ; ;Database ; db_type = "mysql" db_host = "localhost" db_user = "root" db_password = "" db_name = "gacl" db_table_prefix = "" ; ;Caching ; caching = FALSE force_cache_expire = TRUE cache_dir = "/tmp/phpgacl_cache" cache_expire_time = 600 ; ;Admin interface ; items_per_page = 100 max_select_box_items = 100 max_search_return_items = 200 ;NO Trailing slashes smarty_dir = "smarty/libs" smarty_template_dir = "templates" smarty_compile_dir = "templates_c" phpgacl-3.3.7/phpdoc.phpgacl.ini0100644025754300001440000000755710476664743015503 0ustar ipsousers;; phpDocumentor parse configuration file ;; ;; This file is designed to cut down on repetitive typing on the command-line or web interface ;; You can copy this file to create a number of configuration files that can be used with the ;; command-line switch -c, as in phpdoc -c default.ini or phpdoc -c myini.ini. The web ;; interface will automatically generate a list of .ini files that can be used. ;; ;; default.ini is used to generate the online manual at http://www.phpdoc.org/docs ;; ;; ALL .ini files must be in the user subdirectory of phpDocumentor with an extension of .ini ;; ;; Copyright 2002, Greg Beaver ;; ;; WARNING: do not change the name of any command-line parameters, phpDocumentor will ignore them [Parse Data] ;; title of all the documentation ;; legal values: any string title = phpGACL 3.3.7 Developer's Manual ;; parse files that start with a . like .bash_profile ;; legal values: true, false hidden = false ;; show elements marked @access private in documentation by setting this to on ;; legal values: on, off parseprivate = off ;; parse with javadoc-like description (first sentence is always the short description) ;; legal values: on, off javadocdesc = off ;; add any custom @tags separated by commas here ;; legal values: any legal tagname separated by commas. ;customtags = mytag1,mytag2 ;; This is only used by the XML:DocBook/peardoc2 converter defaultcategoryname = Documentation ;; what is the main package? ;; legal values: alphanumeric string plus - and _ defaultpackagename = phpGACL ;; output any parsing information? set to on for cron jobs ;; legal values: on ;quiet = on ;; parse a PEAR-style repository. Do not turn this on if your project does ;; not have a parent directory named "pear" ;; legal values: on/off ;pear = on ;; where should the documentation be written? ;; legal values: a legal path target = ./docs/phpdoc/ ;; Which files should be parsed out as special documentation files, such as README, ;; INSTALL and CHANGELOG? This overrides the default files found in ;; phpDocumentor.ini (this file is not a user .ini file, but the global file) readmeinstallchangelog = README, INSTALL, CHANGELOG, NEWS, FAQ, LICENSE ;; limit output to the specified packages, even if others are parsed ;; legal values: package names separated by commas ;packageoutput = package1,package2 ;; comma-separated list of files to parse ;; legal values: paths separated by commas ;filename = /path/to/file1,/path/to/file2,fileincurrentdirectory filename = ./gacl.class.php,gacl_api.class.php ;; comma-separated list of directories to parse ;; legal values: directory paths separated by commas ;directory = /path1,/path2,.,..,subdirectory ;directory = /home/jeichorn/cvs/pear ;; template base directory (the equivalent directory of /phpDocumentor) ;templatebase = /path/to/my/templates ;; directory to find any example files in through @example and {@example} tags ;examplesdir = /path/to/my/templates ;; comma-separated list of files, directories or wildcards ? and * (any wildcard) to ignore ;; legal values: any wildcard strings separated by commas ;ignore = /path/to/ignore*,*list.php,myfile.php,subdirectory/ ignore = templates_c/,*HTML/default/*,spec/ ;; comma-separated list of Converters to use in outputformat:Convertername:templatedirectory format ;; legal values: HTML:frames:default,HTML:frames:l0l33t,HTML:frames:phpdoc.de,HTML:frames:phphtmllib, ;; HTML:frames:earthli, ;; HTML:frames:DOM/default,HTML:frames:DOM/l0l33t,HTML:frames:DOM/phpdoc.de, ;; HTML:frames:DOM/phphtmllib,HTML:frames:DOM/earthli ;; HTML:Smarty:default,HTML:Smarty:PHP,HTML:Smarty:HandS ;; PDF:default:default,CHM:default:default,XML:DocBook/peardoc2:default output=HTML:frames:earthli ;; turn this option on if you want highlighted source code for every file ;; legal values: on/off sourcecode = off phpgacl-3.3.7/COPYING.lib0100644025754300001440000005750507542222276013675 0ustar ipsousers GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS