HaCi/0000755000175000000000000000000012475464250011053 5ustar fighterrootHaCi/COPYING0000644000175000000000000004311010527616642012105 0ustar fighterroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. HaCi/LICENSE0000644000175000000000000000017210527616642012060 0ustar fighterrootLICENSE * This program is licensed under the terms of the GNU General Public License. (See the file COPYING.) HaCi/README0000644000175000000000000002702712475464245011747 0ustar fighterroot############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2015 by Lars Wildemann # ## Author: Lars Wildemann # ## # ## HaCi is an IP Address / Network Administration Tool with IPv6 Support. # ## It stores its data efficiently in a relational Database and uses a # ## treelike Strukture to illustrate supernets and subnets. # ## # ## This program is free software; you can redistribute it and#or modify # ## it under the terms of the GNU General Public License as published by # ## the Free Software Foundation; either version 2 of the License, or # ## (at your option) any later version. # ## # ## This program 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 General Public License for more details. # ############################################################################### OVERVIEW HaCi is a web frontend for administrating networks. It consist of a (perl) CGI and a (mySQL/postgreSQL) database as backend. REQUIRERMENTS All you need is a webserver (e.g.: Apache), a database (e.g.: mySQL) and an OS which supports perl. - Perl (check with `perl -M -e 1 2>/dev/null && echo "Ok" || echo "Failed"`) - Cache::Cache - CGI - CGI::Ajax - CGI::Carp - CGI::Cookie - CGI::Session - Class::Accessor - Class::MakeMethods - Config::General - Digest::MD5 - Digest::SHA - Encode - Encode::Guess - File::Temp - HTML::Entities - Locale::gettext - Log::LogLite - Math::BigInt v1.87 - Net::CIDR - Net::IPv6Addr - Net::SNMP - Storable - Template - Time::Local - DBD::mysql (for mysql support) - DBD::Pg (for postgresql support) - Apache::DBI (optional for getting connection persistence when using mod-perl) - DNS::ZoneParse (optional for importing Zonefiles) - File::Basename (optional for XMP-RPC API support) - Frontier::Client (optional for XMP-RPC API support) - IO::Socket::INET6 (optional for IMAP authentication support) - Math::BigInt::GMP (optional, but proposed for more performance!) - Net::DNS (optional for importing Zonefiles) - Net::LDAP (optional for LDAP authentication) - Net::Ping (optional for Ping-Plugin) - Pod::WSDL (optional for SAOP support) - SOAP::Transport::HTTP (optional for SAOP support) - SQL::Translator::Diff (optional for automatic database upgrade support) - SQL::Translator v0.09000 (optional for automatic database upgrade support) - Text::CSV (optional for ex/importing CSV-Files) - Text::CSV_XS (optional for faster ex/importing CSV-Files) - Time::HiRes (optional for XMP-RPC API support) - JSON (optional for RESTWrapper API support) - URI::Query (optional for RESTWrapper API support) - HTTP Server - SQL Database You can use the scripts 'bin/checkPerlDependencies.bash', 'bin/checkPerlOptionals.bash' or the testpage "/cgi-bin/test.cgi" to check for dependencies. INSTALLATION 1) First, extract the archiv into a directory, from which the HTTP server will process it and check dependencies. $ cd /var/www $ wget 'http://downloads.sourceforge.net/project/haci/haci/0.98c/HaCi_0.98c.tar.gz' $ tar xfzv HaCi_0.98c.tar.gz $ HaCi/bin/checkPerlDependencies.bash $ HaCi/bin/checkPerlOptionals.bash 2) If needed, accommodate the file/directory permissions, so the HTTP server will have no problems. /logs : HTTPServer (rwx) /spool : HTTPServer (rwx) 3) Modify the HaCi.conf.sample (/etc) for your needs. It can be stored either in '/etc/HaCi.conf' or '/etc/HaCi.conf'. '/etc/HaCi.conf' has precedence. 4) Configure your database and webserver, for serving your new tool. e.g.: - MySQL ---------------------- 8< --------------------- CREATE USER 'HaCi'@'localhost' IDENTIFIED BY ''; GRANT USAGE ON *.* TO 'HaCi'@'localhost' IDENTIFIED BY ''; CREATE DATABASE `HaCi`; GRANT ALL PRIVILEGES ON `HaCi`.* TO 'HaCi'@'localhost' WITH GRANT OPTION; ---------------------- 8< --------------------- - HaCi will automatically initiate/update the database with the latest schema. If you don't want HaCi to do this by itself, you can import the latest schema dump: $ mysql -u HaCi -p HaCi < docs/HaCi_v0.98c.mysql - postgresql - Execute as postgres super user: $ createuser -PSDR HaCi $ createdb -O HaCi HaCi - Add this line to your 'pg_hba.conf' host HaCi HaCi 127.0.0.1/32 md5 - import the database schema dump with the latest version in order to initialize the database $ psql HaCi < docs/HaCi_v0.98c.pgsql - OR update to a new version (i.e.: from 0.98b to 0.98c): $ psql HaCi < docs/HaCi_v0.98b-v0.98c.pgsql - postgresql v9.0+: Perhaps you need to set the following variable in your 'postgresql.conf', in order to make ipv6 work: bytea_output = 'escape' - Apache (httpd.conf) ---------------------- 8< --------------------- ServerName haci.domain.tld DocumentRoot /var/www/HaCi/html ScriptAlias /cgi-bin/ /var/www/HaCi/cgi-bin/ # If you want to use Mod-Perl --- PerlRequire /var/www/HaCi/etc/startup.pl SetHandler perl-script PerlHandler Apache::Registry # mod-perl1 PerlResponseHandler ModPerl::Registry # mod-perl2 #-------------------------------- Options +ExecCGI AllowOverride All Order allow,deny allow from all # Exclude the soap api interface from the mod-perl handler SetHandler None # Include XML-RPC API PerlSwitches -I/var/www/HaCi/modules SetHandler perl-script PerlHandler HaCi::XMLRPC # disable compressing, because of problems with chunked transfer encodings SetEnv no-gzip # Simple REST-Wrapper which itself uses the XMLRPC-API SetHandler perl-script PerlHandler HaCi::RESTWrapper ---------------------- 8< --------------------- - lighttpd - standalone (/etc/lighttpd/lighttpd.conf): ---------------------- 8< --------------------- server.document-root = "/var/www/HaCi/html" server.errorlog = "/var/www/HaCi/logs/error.log" alias.url += ("/cgi-bin/" => "/var/www/HaCi/cgi-bin/") ---------------------- 8< --------------------- - VHost (/etc/lighttpd/conf-available/20-HaCi.conf): ---------------------- 8< --------------------- server.modules += ( "mod_simple_vhost" ) simple-vhost.server-root = "/var/www" simple-vhost.document-root = "/html/" simple-vhost.default-host = "HaCi" alias.url += ( "/cgi-bin/" => "/var/www/HaCi/cgi-bin/" ) ---------------------- 8< --------------------- - activate CGI - Debian: ln -s /etc/lighttpd/conf-available/10-cgi.conf /etc/lighttpd/conf-enabled/ Edit '/etc/lighttpd/conf-available/10-cgi.conf' and comment the alias-Line out: # alias.url += ( "/cgi-bin/" => "/usr/lib/cgi-bin/" ) # - SuSE: Edit '/etc/lighttpd/modules.conf' and enable following line: include "conf.d/cgi.conf" 5) HaCi Daemon (bin/HaCid.pl) The HaCi daemon is responsible for the recurrent plugins. It runs them in defined intervals. You can start it with the parameter '-c' therewith it exists after one full run and you can start it by crontab. The daemon stores its PID in 'spool' and writes its logfile in 'logs'. So it must have write permissions to this directories. 6) HaCiAPI (documentation can be found in /doc) - SOAP (deprecated) HaCi supports a SOAP-Interface for accessing the data not only from the webfrontend but also from a perl/php/... script. You can find a short description and a demo perl script in the 'docs' directory. - XMLRPC There is also a XML-RPC API which can be easily integrated into several programming languages. Documentation can be found in the 'docs' directory. The following methods can be used: - search - getFreeSubnets - getFreeSubnetsFromSearch - addNet - editNet - delNet - assignFreeSubnet - getNetworkDetails - getSubnets - REST There is a very basic REST Wrapper which itself uses the XML-RPC-API. The main reason for this is to support tools which can only communicate via REST (i.e.: Table-JSON-Plugin in Confluence). The available methods are the same as in the XML-RPC-API. Authentication is done via HTTP-Basic-Auth or via "username"- and "password"-Parameters. 7) Look at 'http://sourceforge.net/projects/haci/' for new releases and 'http://haci.larsux.de' for a wiki and the latest infos about HaCi. For exporting the last CVS version call $ cvs -z3 -d:pserver:anonymous@haci.cvs.sourceforge.net:/cvsroot/haci export -D NOW HaCi 8) FINISH 9) Additional comments Cleanup Database 'bin/cleanupDatabase.pl' This tool checks the database for stale and broken entries. Pass '-c' as parameter in order to cleanup found issues. Please make sure to backup your database before. UPGRADE 1) Make a database backup 2) Stop the web server 3) Rename the HaCi-Document-Root i.e.: $ mv /var/www/HaCi /var/www/HaCi_OLD 4) Get the actual code, extract it and check for new dependencies $ cd /var/www $ wget 'http://downloads.sourceforge.net/project/haci/haci/0.98c/HaCi_0.98c.tar.gz' $ tar xfzv HaCi_0.98c.tar.gz $ cd HaCi $ bin/checkPerlDependencies.bash $ bin/checkPerlOptionals.bash 5) If needed, accommodate the file/directory permissions, so the HTTP server will have no problems. /logs : HTTPServer (rwx) /spool : HTTPServer (rwx) 6) Update your database e.g.: - MySQL - HaCi will automatically update the database with the latest schema. If you don't want HaCi to do this by itself, you can do the changes manually $ less docs/manualDatabaseUpgrades_mysql.txt - postgresql - update to a new version (i.e.: from 0.98b to 0.98c): $ psql HaCi < docs/HaCi_v0.98b-v0.98c.pgsql 7) Check for new entries in the webserver configuration (Apache example in INSTALLATION step 4) 8) Check for new configuration items in HaCi.conf and synchronize it with your old config (if it doesn't resides in /etc). 9) Start the webserver & FINISH The first HaCi request will take some time, because of housekeeping stuff (see in webserver error logfile for more info) AUTHOR Lars Wildemann haci@larsux.de # vim:ts=8 HaCi/Template/0000755000175000000000000000000012475447615012634 5ustar fighterrootHaCi/Template/HaCi.tmpl0000644000175000000000000000214411336301620014313 0ustar fighterroot[% UNLESS noHeader %] [% titlelong %] [% FOREACH style IN layouts %] [% IF style.descr != styleDescr %] [% END %] [% END %] [% END %] [% IF page == 'main' %] [% INCLUDE HaCiMain.tmpl %] [% ELSIF page == 'login' %] [% INCLUDE HaCiLogin.tmpl %] [% ELSIF page == 'showStatus' %] [% INCLUDE HaCiShowStatus.tmpl %] [% ELSIF page == 'treeNetworkTable' %] [% INCLUDE HaCiTreeNetwork.tmpl %] [% ELSIF page == 'treeRootNetworkTable' %] [% INCLUDE HaCiTreeRoot.tmpl %] [% INCLUDE HaCiTreeNetwork.tmpl %] [% ELSIF page == 'showPlugin' %] [% INCLUDE HaCiShowPlugin.tmpl %] [% END %] [% UNLESS noHeader %] [% END %] HaCi/Template/HaCiAddNet.tmpl0000644000175000000000000000660111320500404015367 0ustar fighterroot

[% helpDescrHeader %]

[% INCLUDE HaCiBox.tmpl menu = helpDescrMenu hiddens = helpDescrHiddens parentFormName = helpDescrFormName %]

[% helpStatusHeader %]

[% INCLUDE HaCiBox.tmpl menu = helpStatusMenu %]

[% pluginInfoBoxHeader %]

[% INCLUDE HaCiBox.tmpl menu = pluginInfoBoxMenu %]

[% addNetHeader %]

[% INCLUDE HaCiBox.tmpl menu = addNetMenu hiddens = addNetHiddens parentFormName = addNetFormName %]

[% netGroupRightsHeader %]

[% INCLUDE HaCiBox.tmpl menu = netGroupRightsMenu align = 'left' parentFormName = addNetFormName %]

[% netPluginsHeader %]

[% INCLUDE HaCiBox.tmpl menu = netPluginsMenu parentFormName = addNetFormName %]

[% addNetButtonsMenuHeader %]

[% INCLUDE HaCiBox.tmpl menu = addNetButtonsMenu parentFormName = addNetFormName %]
[% IF checkNet %]

[% addNetDNSInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = addNetDNSInfo parentFormName = addNetFormName %]

[% addNetWHOISInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = addNetWHOISInfo parentFormName = addNetFormName %]
[% END %]
HaCi/Template/HaCiAddRoot.tmpl0000644000175000000000000000202610706272523015600 0ustar fighterroot

[% addRootHeader %]

[% INCLUDE HaCiBox.tmpl menu = addRootMenu hiddens = addRootHiddens parentFormName = addRootFormName %]

[% rootGroupRightsHeader %]

[% INCLUDE HaCiBox.tmpl menu = rootGroupRightsMenu align = 'left' parentFormName = addRootFormName %]

[% addRootButtonsHeader %]

[% INCLUDE HaCiBox.tmpl menu = addRootButtonsMenu parentFormName = addRootFormName %]
HaCi/Template/HaCiBox.tmpl0000644000175000000000000002210312465551740014777 0ustar fighterroot[% IF formName %]
[% formCreated = 1 %] [% ELSE %] [% formName = parentFormName %] [% END %] [% trOpen = 0 %] [% FOREACH entry IN menu %] [% UNLESS trOpen %] [% trOpen = 1 %] [% END %] [% IF entry.value.type == 'hline' %] [% IF entry.value.colspan %] [% trOpen = 0 %] [% ELSE %] [% FOREACH element IN entry.elements %] [% UNLESS trOpen %] [% trOpen = 1 %] [% END %] [% UNLESS element.target == 'key' OR element.target == 'single' %][% trOpen = 0 %][% END %] [% END %] [% IF element.target == 'key' OR element.target == 'single' %][% trOpen = 0 %][% END %] [% END %] [% IF trOpen %][% trOpen = 0 %][% END %] [% END %] [% FOREACH entry IN hiddens %] [% END %]
[% ELSE %] [% END %]
[% IF element.type == 'buttons' %] [% FOREACH button IN element.buttons %] [% IF button.picOnly %] [% button.title %] [% ELSE %]
[% button.value %]
[% END %] [% END %] [% ELSIF element.type == 'vline' %] | [% ELSIF element.type == 'dline' %] / [% ELSIF element.type == 'label' %] [% IF element.hidden %] [% END %] [% UNLESS element.noShow %] [% element.value %] [% END %] [% ELSIF element.type == 'textfield' %] [% ELSIF element.type == 'file' %] [% ELSIF element.type == 'passwordfield' %] [% ELSIF element.type == 'textarea' %] [% ELSIF element.type == 'popupMenu' %] [% ELSIF element.type == 'checkbox' %] [% element.descr %] [% ELSIF element.type == 'radio' %] [% FOREACH radio IN element.values %]  [% radio.label %] [% IF element.cr %]
[% END %] [% END %] [% ELSIF element.type == 'floatEnd' %]
[% END %] [% IF element.focus == 1 %] [% focusName = element.name %] [% END %]
[% IF formCreated %]
[% END %] HaCi/Template/HaCiChanceOwnPW.tmpl0000644000175000000000000000054611031215106016347 0ustar fighterroot

[% chOwnPWHeader %]

[% INCLUDE HaCiBox.tmpl menu = chOwnPW formName = chOwnPWFormName hiddens = chOwnPWHiddens %]

HaCi/Template/HaCiCombineNets.tmpl0000644000175000000000000000141510706272523016453 0ustar fighterroot

[% combineNetsHeader %]

[% INCLUDE HaCiBox.tmpl menu = combineNetsMenu parentFormName = combineNetsFormName hiddens = combineNetsHiddens %]

[% combineNetsButtonsHeader %]

[% INCLUDE HaCiBox.tmpl menu = combineNetsButtonsMenu parentFormName = combineNetsFormName %]
HaCi/Template/HaCiCompare.tmpl0000644000175000000000000000135510706272523015636 0ustar fighterroot

[% compareHeader %]

[% INCLUDE HaCiBox.tmpl menu = compareMenu parentFormName = compareFormName hiddens = compareHiddens %]

[% INCLUDE HaCiBox.tmpl menu = compareButtons parentFormName = compareFormName hiddens = compareHiddens %]
HaCi/Template/HaCiDelGroup.tmpl0000644000175000000000000000042410704777333015774 0ustar fighterroot

[% delGroupHeader %]

[% INCLUDE HaCiBox.tmpl menu = delGroupMenu formName = delGroupFormName hiddens = delGroupHiddens %]
HaCi/Template/HaCiDelNet.tmpl0000644000175000000000000000041410704777333015425 0ustar fighterroot

[% delNetHeader %]

[% INCLUDE HaCiBox.tmpl menu = delNetMenu formName = delNetFormName hiddens = delNetHiddens %]
HaCi/Template/HaCiDelRoot.tmpl0000644000175000000000000000042010704777333015617 0ustar fighterroot

[% delRootHeader %]

[% INCLUDE HaCiBox.tmpl menu = delRootMenu formName = delRootFormName hiddens = delRootHiddens %]
HaCi/Template/HaCiDelTemplate.tmpl0000644000175000000000000000030410526326505016441 0ustar fighterroot

[% delTmplHeader %]

[% INCLUDE HaCiBox.tmpl menu = delTmplMenu formName = delTmplFormName hiddens = delTmplHiddens %]
HaCi/Template/HaCiDelUser.tmpl0000644000175000000000000000042010704777333015612 0ustar fighterroot

[% delUserHeader %]

[% INCLUDE HaCiBox.tmpl menu = delUserMenu formName = delUserFormName hiddens = delUserHiddens %]
HaCi/Template/HaCiEditGroup.tmpl0000644000175000000000000000212110706272523016142 0ustar fighterroot

[% editGroupHeader %]

[% INCLUDE HaCiBox.tmpl menu = editGroupMenu hiddens = editGroupHiddens parentFormName = 'editGroup' %]

[% editGroupPermHeader %]

[% INCLUDE HaCiBox.tmpl menu = editGroupPermMenu hiddens = editGroupPermHiddens align = 'left' parentFormName = 'editGroup' %]

[% INCLUDE HaCiBox.tmpl menu = editGroupButtonsMenu parentFormName = 'editGroup' %]
HaCi/Template/HaCiEditTemplate.tmpl0000644000175000000000000000361711770460633016637 0ustar fighterroot

[% templateHeader %]

[% INCLUDE HaCiBox.tmpl menu = templateMenu parentFormName = editTemplateFormName %]

[% editTemplateHeader %]

[% INCLUDE HaCiBox.tmpl menu = editTemplateMenu hiddens = editTemplateHiddens parentFormName = editTemplateFormName %]

[% editTemplateNameHeader %]

[% INCLUDE HaCiBox.tmpl menu = editTemplateNameMenu parentFormName = editTemplateNameFormName %]

[% editTemplateEntryHeader %]

[% INCLUDE HaCiBox.tmpl menu = editTemplateEntryMenu hiddens = editTemplateEntryHiddens keyWidth = 19 parentFormName = editTemplateFormName %]



[% templatePreviewHeader %]

[% INCLUDE HaCiBox.tmpl menu = templatePreviewMenu hiddens = templatePreviewHiddens parentFormName = editTemplateFormName %]
HaCi/Template/HaCiEditUser.tmpl0000644000175000000000000000214312467504602015772 0ustar fighterroot

[% editUserHeader %]

[% INCLUDE HaCiBox.tmpl menu = editUserMenu hiddens = editUserHiddens parentFormName = 'editUser' %]

[% editUserGroupsHeader %]

[% INCLUDE HaCiBox.tmpl menu = editUserGroupsMenu hiddens = editUserGroupsHiddens align = 'left' parentFormName = 'editUser' %]

[% INCLUDE HaCiBox.tmpl menu = editUserButtonsMenu parentFormName = 'editUser' %]
HaCi/Template/HaCiFooter.tmpl0000644000175000000000000000057111350543632015503 0ustar fighterroot

Status: loading  

[% gettext_contact %]: [% techContact %]   | [% titleshort %]  [% version %]   | [% gettext_support %]     

HaCi/Template/HaCiImportASNRoutes.tmpl0000644000175000000000000000154410706272523017266 0ustar fighterroot
[% importASNRoutesHeader %]
[% INCLUDE HaCiBox.tmpl menu = importASNRoutesMenu parentFormName = importASNRoutesFormName hiddens = importASNRoutesHiddens %]

[% INCLUDE HaCiBox.tmpl menu = importASNRoutesButtons parentFormName = importASNRoutesFormName %]
HaCi/Template/HaCiImportConfig.tmpl0000644000175000000000000000653311410500775016647 0ustar fighterroot
[% IF csvPreview %]

[% csvPreview %]

[% cnter = 0 %]

[% importCSVHeader %]

[% INCLUDE HaCiBox.tmpl menu = importCSVMenu hiddens = importCSVHiddens parentFormName = importCSVFormName %]

[% importCSVTypeHeader %]

[% INCLUDE HaCiBox.tmpl menu = importCSVTypeMenu parentFormName = importCSVFormName %]

[% INCLUDE HaCiBox.tmpl menu = importCSVButtonMenu parentFormName = importCSVFormName %]


[% WHILE cnter < nrOfCols %] [% cnter = cnter + 1 %] [% END %] [% IF noCSVContent %] [% ELSE %] [% FOREACH entry IN csvData %] [% cnter = cnter + 1 %] [% IF cnter mod 2 %] [% bgColor = '#AAFFAA' %] [% ELSE %] [% bgColor = '#DDFFDD' %] [% END %] [% FOREACH col IN entry %] [% END %] [% END %] [% END %]
[% gettext_noContent %]
[% col %]
.
.
.
[% ELSE %]

[% importConfigHeader %]

[% INCLUDE HaCiBox.tmpl menu = importConfigMenu parentFormName = importConfigFormName formType = importConfigFormType hiddens = importConfigHiddens %]

[% INCLUDE HaCiBox.tmpl menu = importConfigButtons parentFormName = importConfigFormName %]
[% END %]
HaCi/Template/HaCiImportDNS.tmpl0000644000175000000000000000376210712127545016073 0ustar fighterroot

[% importDNSHeader %]



[% importDNSTransHeader %]

[% INCLUDE HaCiBox.tmpl menu = importDNSTransMenu parentFormName = importDNSTransFormName hiddens = importDNSTransHiddens %]

[% INCLUDE HaCiBox.tmpl menu = importDNSTransButtons parentFormName = importDNSTransFormName hiddens = importDNSTransHiddens %]


[% importDNSLocalHeader %]

[% INCLUDE HaCiBox.tmpl menu = importDNSLocalMenu parentFormName = importDNSLocalFormName formType = importDNSLocalFormType hiddens = importDNSLocalHiddens %]

[% INCLUDE HaCiBox.tmpl menu = importDNSLocalButtons parentFormName = importDNSLocalFormName hiddens = importDNSLocalHiddens %]
HaCi/Template/HaCiLogin.tmpl0000644000175000000000000000237511410500775015317 0ustar fighterroot

[% loginHeader %]

[% INCLUDE HaCiBox.tmpl menu = loginMenu parentFormName = 'loginForm' hiddens = loginHiddens %]
[% IF authError %]
[% authError %]
[% END %]

[% warnlHeader %]

[% INCLUDE HaCiBox.tmpl menu = warnlMenu %]
HaCi/Template/HaCiMain.tmpl0000644000175000000000000000541212030622510015115 0ustar fighterroot
[% IF mainPage == 'addRoot' %] [% INCLUDE HaCiAddRoot.tmpl %] [% ELSIF mainPage == 'addNet' %] [% INCLUDE HaCiAddNet.tmpl %] [% ELSIF mainPage == 'showAllNets' %] [% INCLUDE HaCiTree.tmpl %] [% ELSIF mainPage == 'importASNRoutes' %] [% INCLUDE HaCiImportASNRoutes.tmpl %] [% ELSIF mainPage == 'importDNS' %] [% INCLUDE HaCiImportDNS.tmpl %] [% ELSIF mainPage == 'importConfig' %] [% INCLUDE HaCiImportConfig.tmpl %] [% ELSIF mainPage == 'showNet' %] [% INCLUDE HaCiShowNet.tmpl %] [% ELSIF mainPage == 'splitNet' %] [% INCLUDE HaCiSplitNet.tmpl %] [% ELSIF mainPage == 'combineNets' %] [% INCLUDE HaCiCombineNets.tmpl %] [% ELSIF mainPage == 'showRoot' %] [% INCLUDE HaCiShowRoot.tmpl %] [% ELSIF mainPage == 'editRoot' %] [% INCLUDE HaCiEditRoot.tmpl %] [% ELSIF mainPage == 'delNet' %] [% INCLUDE HaCiDelNet.tmpl %] [% ELSIF mainPage == 'delRoot' %] [% INCLUDE HaCiDelRoot.tmpl %] [% ELSIF mainPage == 'search' %] [% INCLUDE HaCiSearch.tmpl %] [% ELSIF mainPage == 'getFreeSubnetsFromSearch' %] [% INCLUDE HaCiSearch.tmpl %] [% ELSIF mainPage == 'compare' %] [% INCLUDE HaCiCompare.tmpl %] [% ELSIF mainPage == 'showTemplates' %] [% INCLUDE HaCiShowTemplates.tmpl %] [% ELSIF mainPage == 'editTemplate' %] [% INCLUDE HaCiEditTemplate.tmpl %] [% ELSIF mainPage == 'delTmpl' %] [% INCLUDE HaCiDelTemplate.tmpl %] [% ELSIF mainPage == 'showGroups' %] [% INCLUDE HaCiShowGroups.tmpl %] [% ELSIF mainPage == 'editGroup' %] [% INCLUDE HaCiEditGroup.tmpl %] [% ELSIF mainPage == 'delGroup' %] [% INCLUDE HaCiDelGroup.tmpl %] [% ELSIF mainPage == 'showUsers' %] [% INCLUDE HaCiShowUsers.tmpl %] [% ELSIF mainPage == 'editUser' %] [% INCLUDE HaCiEditUser.tmpl %] [% ELSIF mainPage == 'delUser' %] [% INCLUDE HaCiDelUser.tmpl %] [% ELSIF mainPage == 'showStatus' %] [% INCLUDE HaCiShowStatus.tmpl %] [% ELSIF mainPage == 'showAbout' %] [% INCLUDE HaCiShowAbout.tmpl %] [% ELSIF mainPage == 'showPlugins' %] [% INCLUDE HaCiShowPlugins.tmpl %] [% ELSIF mainPage == 'showPluginConf' %] [% INCLUDE HaCiShowPluginConf.tmpl %] [% ELSIF mainPage == 'showSubnets' %] [% INCLUDE HaCiShowSubnets.tmpl %] [% ELSIF mainPage == 'showSettings' %] [% INCLUDE HaCiShowSettings.tmpl %] [% ELSIF mainPage == 'showAuditLogs' %] [% INCLUDE HaCiShowAuditLogs.tmpl %] [% END %]

[% warnlHeader %]

[% INCLUDE HaCiBox.tmpl menu = warnlMenu %]
HaCi/Template/HaCiMenu.tmpl0000644000175000000000000000115210677351441015153 0ustar fighterroot [% FOREACH submenu IN menu %] [% END %] HaCi/Template/HaCiNetwork.tmpl0000644000175000000000000000307210777271203015701 0ustar fighterroot[% FOREACH network IN networks %] [% IF network.parent %][% UNLESS network.noDiv %]
[% END %][% END %]
[% IF network.parent %] [% ELSE %]
[% IF editTree %] [% IF network.fillNet %]
[% END %] [% END %] [% END %] [% IF editTree %] [% UNLESS network.fillNet %] [% END %] [% END %]
[% IF network.netUrl %] [% network.network %] [% ELSE %]
[% network.network %]
[% END %] [% IFF network.statePic %] [% END %]

[% network.descr %]

[% IF network.parent %][% UNLESS network.expanded %]
[% END %][% END %] [% IF network.parentBroadcast %] [% i = [ 1 .. network.parentBroadcast ] %] [% FOREACH i %] [% END %] [% END %] [% END %] HaCi/Template/HaCiSearch.tmpl0000644000175000000000000000401512451674756015466 0ustar fighterroot

[% searchHeader %]

[% INCLUDE HaCiBox.tmpl menu = searchMenu hiddens = searchHiddens formName = searchFormName buttonFocus = buttonFocus %]

[% IF searchResult %]

[% searchResultHeader %]

[% cnter = 0 %] [% IF !bSearchForFree %] [% IF bShowNrOfFreeSubs %] [% END %] [% FOREACH descrEntry IN tmplDescr %] [% IF noSearchResult %] [% ELSE %] [% FOREACH entry IN searchResult %] [% cnter = cnter + 1 %] [% IF cnter mod 2 %] [% bgColor = '#AAFFAA' %] [% ELSE %] [% bgColor = '#DDFFDD' %] [% END %] [% IF !bSearchForFree %] [% IF bShowNrOfFreeSubs %] [% END %] [% netID = entry.netID %] [% FOREACH descrEntry IN tmplDescr %] [% descrValue = descrEntry.value %] [% END %] [% END %] [% END %] [% END %]
[% gettext_rootName %] [% gettext_network %][% gettext_description %][% gettext_nrOfFreeSubnets %][% gettext_state %] [% gettext_tags %][% descrEntry.value %] [% END %] [% END %]
[% gettext_nothing_found %]
[% entry.rootName %] [% entry.network %][% entry.description %][% entry.nrOfFreeSubs %][% entry.state %] [% entry.tags %][% tmplInfos.$netID.$descrValue %]
[% END %]
HaCi/Template/HaCiShowAbout.tmpl0000644000175000000000000000305412470146636016166 0ustar fighterroot

Version: [% version %]


  HaCi - IP Address Administration                             
    Copyright (C) 2006-2015 by Lars Wildemann                               
    Author: Lars Wildemann <HaCi@larsux.de>                                 
                                                                            
  HaCi is an IP Address / Network Administration Tool with IPv6 Support.
      It stores its data efficiently in a relational Database and uses a        
    treelike Strukture to illustrate supernets and subnets.                 
                                                                            
   This program is free software; you can redistribute it andor modify    
    it under the terms of the GNU General Public License as published by    
    the Free Software Foundation; either version 2 of the License, or       
    (at your option) any later version.                                     
                                                                            
    This program 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 General Public License for more details.                            

HaCi/Template/HaCiShowAuditLogs.tmpl0000644000175000000000000000340412000412522016761 0ustar fighterroot

[% showAuditLogsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showAuditLogsMenu hiddens = showAuditLogsHiddens parentFormName = showAuditLogsFormName %]


[% showAuditLogs %]

[% cnter = 0 %] [% IF noAuditLogs %] [% ELSE %] [% FOREACH entry IN auditLogs %] [% cnter = cnter + 1 %] [% IF cnter mod 2 %] [% bgColor = '#AAFFAA' %] [% ELSE %] [% bgColor = '#DDFFDD' %] [% END %] [% END %] [% END %]
[% gettext_timestamp %] [% gettext_username %] [% gettext_action %] [% gettext_object %] [% gettext_value %] [% gettext_error %]
[% gettext_noContent %]
[% entry.ts %] [% entry.username %] [% entry.action %] [% entry.object %] [% entry.value %] [% entry.error %]
HaCi/Template/HaCiShowGroups.tmpl0000644000175000000000000000125410706272524016367 0ustar fighterroot

[% newGroupHeader %]

[% INCLUDE HaCiBox.tmpl menu = newGroupMenu formName = newGroupFormName hiddens = newGroupHiddens %]

[% showGroupsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showGroupsMenu formName = showGroupsFormName hiddens = showGroupsHiddens %]
HaCi/Template/HaCiShowNet.tmpl0000644000175000000000000000260510761422074015635 0ustar fighterroot

[% netFunctionsHeader %]

[% INCLUDE HaCiBox.tmpl menu = netFunctions formName = netFunctionsFormName hiddens = netFunctionHiddens %]

[% netBasicInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = netBasicInfo %]

[% netInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = netInfo %]

[% FOREACH plugin IN plugins %]

[% plugin.name %]: loading...

[% END %]
HaCi/Template/HaCiShowPlugin.tmpl0000644000175000000000000000026510704777333016355 0ustar fighterroot

[% plugin.HEADER %]

[% INCLUDE HaCiBox.tmpl menu = plugin.BODY %]
HaCi/Template/HaCiShowPluginConf.tmpl0000644000175000000000000000053110732507146017151 0ustar fighterroot

[% pluginConfHeader %]

[% INCLUDE HaCiBox.tmpl menu = pluginConfMenu hiddens = pluginConfHiddens formName = pluginConfFormName %]
HaCi/Template/HaCiShowPlugins.tmpl0000644000175000000000000000255212475447615016545 0ustar fighterroot

[% INCLUDE HaCiBox.tmpl menu = floatingPopupMenu %]

[% showPluginsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showPluginsMenu hiddens = showPluginsHiddens parentFormName = showPluginsFormName %]

[% showPluginsButtonsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showPluginsButtonsMenu parentFormName = showPluginsFormName %]

[% HaCidInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = HaCidInfoMenu %]
HaCi/Template/HaCiShowRoot.tmpl0000644000175000000000000000113410704777333016036 0ustar fighterroot

[% rootInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = rootInfo %]

[% rootFunctionsHeader %]

[% INCLUDE HaCiBox.tmpl menu = rootFunctions formName = rootFunctionFormName hiddens = rootFunctionHiddens %]
HaCi/Template/HaCiShowSettings.tmpl0000644000175000000000000000107311031215110016663 0ustar fighterroot

[% settingsMenuHeader %]

[% INCLUDE HaCiBox.tmpl menu = settingsMenu formName = settingsMenuFormName hiddens = settingsMenuHiddens %]

[% IF settingsSubMenu %]
[% END %] [% IF settingsSubMenu == 'chOwnPW' %] [% INCLUDE HaCiChanceOwnPW.tmpl %] [% ELSIF settingsSubMenu == 'showViewSettings' %] [% INCLUDE HaCiShowViewSettings.tmpl %] [% END %]
HaCi/Template/HaCiShowStatus.tmpl0000644000175000000000000000047310555523334016375 0ustar fighterroot
[% statTitle %]
0%
100%
[% statDetail %]
HaCi/Template/HaCiShowSubnets.tmpl0000644000175000000000000000311611703675123016532 0ustar fighterroot

[% showSubnetsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showSubnetsMenu formName = showSubnetsFormName buttonFocus = buttonFocus hiddens = showSubnetsHiddens %]

[% IF freeSubnets %]

[% freeSubnetsHeader %]

[% cnter = 0 %] [% IF bAddNet %] [% END %] [% IF noResults %] [% ELSE %] [% FOREACH subnet IN freeSubnets %] [% cnter = cnter + 1 %] [% IF cnter mod 2 %] [% bgColor = '#AAFFAA' %] [% ELSE %] [% bgColor = '#DDFFDD' %] [% END %] [% IF bAddNet %] [% END %] [% END %] [% END %]
[% gettext_nr %] [% gettext_subnet %][% gettext_create %]
[% gettext_nothing_found %]
[% cnter %] [% subnet.net %]
[% END %]
HaCi/Template/HaCiShowTemplates.tmpl0000644000175000000000000000132010706272524017040 0ustar fighterroot

[% newTemplateHeader %]

[% INCLUDE HaCiBox.tmpl menu = newTemplateMenu formName = newTemplateFormName hiddens = newTemplateHiddens %]

[% netTypeTemplatesHeader %]

[% INCLUDE HaCiBox.tmpl menu = netTypeTemplatesMenu formName = netTypeTemplatesFormName hiddens = netTypeTemplatesHiddens %]
HaCi/Template/HaCiShowUsers.tmpl0000644000175000000000000000124410706272524016210 0ustar fighterroot

[% newUserHeader %]

[% INCLUDE HaCiBox.tmpl menu = newUserMenu formName = newUserFormName hiddens = newUserHiddens %]

[% showUsersHeader %]

[% INCLUDE HaCiBox.tmpl menu = showUsersMenu formName = showUsersFormName hiddens = showUsersHiddens %]
HaCi/Template/HaCiShowViewSettings.tmpl0000644000175000000000000000057211031215110017521 0ustar fighterroot

[% viewSettingsHeader %]

[% INCLUDE HaCiBox.tmpl menu = viewSettings formName = viewSettingsFormName hiddens = viewSettingsHiddens %]

HaCi/Template/HaCiSplitNet.tmpl0000644000175000000000000000262110767624523016017 0ustar fighterroot

[% helpDescrHeader %]

[% INCLUDE HaCiBox.tmpl menu = helpDescrMenu hiddens = helpDescrHiddens parentFormName = helpDescrFormName %]

[% netBasicInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = netBasicInfo parentFormName = splitNetFormName %]

[% splitNetHeader %]

[% INCLUDE HaCiBox.tmpl menu = splitNetMenu hiddens = splitNetHiddens parentFormName = splitNetFormName %]

[% splitNetButtonsHeader %]

[% INCLUDE HaCiBox.tmpl menu = splitNetButtonsMenu parentFormName = splitNetFormName %]
HaCi/Template/HaCiTree.tmpl0000644000175000000000000000341310706272524015145 0ustar fighterroot

[% treeMenuHeader %]

[% INCLUDE HaCiBox.tmpl menu = treeMenu parentFormName = treeMenuFormName %]
[% IF editTreeMenu %]

[% editTreeMenuHeader %]

[% INCLUDE HaCiBox.tmpl menu = editTreeMenu hiddens = treeMenuHiddens parentFormName = treeMenuFormName %]
[% END %] [% IF editTreeMenu %]
[% END %]
[% FOREACH root IN tree %] [% IF root.parent %]
[% END %] [% INCLUDE HaCiTreeRoot.tmpl root = root %] [% INCLUDE HaCiTreeNetwork.tmpl networks = root.networks %] [% IF root.parent %]
[% END %] [% END %]
HaCi/Template/HaCiTreeNetwork.tmpl0000644000175000000000000000641112115202117016503 0ustar fighterroot[% FOREACH network IN networks %] [% IF network.parent %][% UNLESS network.noDiv %]
[% END %][% END %]
[% IF showTreeStructure %] [% IF network.GUINetCache %] [% FOR entry in network.GUINetCache %] [% name = entry.level %] [% noLine.$name = entry.value %] [% END %] [% END %] [% IF network.lastParent %] [% name = network.lastParent %] [% noLine.$name = 1 %] [% END %] [% level = 1 %] [% WHILE level <= network.level %] [% level = level + 1 %] [% END %] [% ELSE %]
[% END %] [% IF network.parent %] [% ELSE %] [% IF showTreeStructure %] [% ELSE %]
[% END %] [% IF editTree %] [% IF network.fillNet %]
[% END %] [% END %] [% END %] [% IF editTree %] [% UNLESS network.fillNet %] [% END %] [% END %]
[% IF network.netUrl %] [% network.network %] [% ELSE %] [% network.network %] [% END %] [% IF network.statePic %] [% network.stateAlt %] [% END %] [% IF network.defSubnetSize %] [/[% network.defSubnetSize %]] [% END %]

[% IF network.invisible %] [% network.descr %] [% ELSE %] [% network.descr %] [% END %]

[% IF directaccess %] [% UNLESS network.fillNet OR network.invisible %] [% IF network.bEditNet %] [% network.editAlt %] [% END %] [% IF network.bDelNet %] [% network.delAlt %] [% END %] [% END %] [% END %]
[% IF network.parent %][% UNLESS network.expanded %]
[% END %][% END %] [% IF network.parentBroadcast %] [% iE = [ 1 .. network.parentBroadcast ] %] [% FOREACH i IN iE %] [% END %] [% END %] [% END %] HaCi/Template/HaCiTreeRoot.tmpl0000644000175000000000000000151510777271203016013 0ustar fighterroot
[% IF root.space > 0 %]
[% END %] [% IF root.parent %] [% ELSE %]
[% END %]
[% IF root.rootUrl %] [% root.name %] [% ELSE %]
[% root.name %]
[% END %] [% IF root.rootPic %] [% root.rootAlt %] [% END %]

[% root.descr %]

HaCi/bin/0000755000175000000000000000000012475462324011623 5ustar fighterrootHaCi/bin/HaCid.pl0000755000175000000000000002742411606636771013150 0ustar fighterroot#!/usr/bin/perl package HaCi::HaCi; use warnings; use strict; local $| = 1; use FindBin; use lib "$FindBin::Bin/../modules"; chdir "$FindBin::Bin/.."; my $workDir = qx/pwd/; chomp($workDir); $ENV{workdir} = $workDir; use Log::LogLite; use Data::Dumper; use POSIX; use Getopt::Std; use HaCi::Conf; use HaCi::Mathematics qw/dec2net getBroadcastFromNet dec2ip net2dec getV6BroadcastNet getCidrFromDec getCidrFromNetv6Dec/; use HaCi::Utils qw/ getConfig getPlugins getTable getNetworksForPlugin getPluginConfValues updatePluginLastRun getMaintInfosFromNet initTables finalizeTables netID2Stuff getNextDBNetwork fillHoles getPluginLastRun /; $Getopt::Std::STANDARD_HELP_VERSION = 1; $main::VERSION = 0.1; my $log; eval { $log = new Log::LogLite($ENV{workdir} . '/logs/HaCid.log',3); $log->template(' ' . "\n"); }; die "Error while opening logfile. Has the user '$>' write access to the logfile '$ENV{workdir}/logs/HaCid.log'?: $@" if $@; local $SIG{'__WARN__'} = sub {&log($_[0], 2)}; local $SIG{'__DIE__'} = sub {&diel(@_)}; my $progFile = $workDir . '/bin/HaCid.pl'; my @argv = @ARGV; my $running = 1; my $plugins = {}; my $pluginData = {}; my $nrOfChilds = 0; my $maxChilds = 5; my $pluginReload = 300; my $lastReload = 0; my $maxMemCon = 100000; our $opts = {}; getopts('cdr', $opts); our $conf; *conf = \$HaCi::Conf::conf; &HaCi::Conf::init($workDir); &getConfig(); $maxMemCon = $conf->{user}->{misc}->{maxmemconsumption} if exists $conf->{user}->{misc}->{maxmemconsumption} && $conf->{user}->{misc}->{maxmemconsumption}; if (my $nrOfProz = &checkProcessList()) { if ($opts->{r} && $nrOfProz == 1) { &log("Restarting successfull!\n", 3, 1); } else { &log("$nrOfProz Processes allready running!\n", 1, 1); exit 0; } } my $daemon = (exists $opts->{c} && $opts->{c}) ? 0 : 1; my $debug = (exists $opts->{d} && $opts->{d}) ? 1 : 0; push @argv, '-r' unless $opts->{r}; &daemonize() if $daemon; local $SIG{ALRM} = sub {&log("checking Processes", 3); $nrOfChilds = &checkProcessList();alarm 60}; alarm 60; &init(); &wrPID(); if ($daemon) { while ($running) { &checkMemConsumption(); &loadPlugins() if (time - $lastReload) >= $pluginReload; &cleanupPluginData(); &main(); sleep 1; } } else { &loadPlugins(); &main(); } &log("Finished!", 2); &wrPID(1); exit 0; #-------------------------------------------- END { &finalizeTables(); } sub init { &initTables(); } sub main { &log("starting Main...", 3); foreach (keys %{$plugins}) { my $pluginID = $_; my $plugin = $plugins->{$pluginID}->{NAME}; if ($pluginData->{running}->{$pluginID}) { &log("Plugin '$plugin' is allready running!", 3); next; } $pluginData->{lastRuns}->{$pluginID} = &getPluginLastRun($pluginID); if (my $child = &daemonize(1)) { $pluginData->{running}->{$pluginID} = 1; $pluginData->{PID}->{$child} = $pluginID; next; } else { &log("Starting Plugin '$plugin'...", 3); &processPlugin($pluginID); &log("Plugin '$plugin' sucessfully processed", 3); exit 0; } } } sub processPlugin { my $pluginID = shift; my $plugin = $plugins->{$pluginID}->{NAME}; &initTables(); my $pluginGlobConfig = &getPluginConfValues($pluginID, -1); my $interval = $pluginGlobConfig->{def_glob_recurrent_interval}; $interval = 3600 unless defined $interval; unless (defined $interval && $interval > 0) { $interval = 'undef' unless defined $interval; &log(" Interval ($interval) for Plugin '$plugin' is not defined or too small!", 1); $pluginData->{running}->{$pluginID} = 0; return ; } my $timeDiff = time - ($pluginData->{lastRuns}->{$pluginID} || 0); if ($timeDiff < $interval) { &log(" Interval for Plugin '$plugin' is not reached ($timeDiff < $interval)", 3); $pluginData->{running}->{$pluginID} = 0; return; } &log(" loading Plugin $plugin ($pluginID)...", 3); my $package = $plugins->{$pluginID}->{PACKAGE}; (my $packageFile = $package . '.pm') =~ s/::/\//g; eval { require $packageFile; }; if ($@) { &log(" Cannot load Plugin '$plugin': $@", 1); $pluginData->{running}->{$pluginID} = 0; return; } &log(" getting Networks for Plugin $plugin ($pluginID)...", 3); my @networkst = &getNetworksForPlugin($pluginID); my $config = {}; my $networks = []; foreach (@networkst) { my $netID = $_; $config->{$netID} = &getPluginConfValues($pluginID, $netID); my $netInfos = &getMaintInfosFromNet($netID); next unless exists $netInfos->{network}; my $onlyHosts = (exists $config->{$netID}->{def_recurrent_onlyHosts} && $config->{$netID}->{def_recurrent_onlyHosts}) ? 1 : 0; if (!$onlyHosts || $onlyHosts && (($netInfos->{ipv6} && &getCidrFromNetv6Dec($netInfos->{network}) == 128) || (!$netInfos->{ipv6} && &getCidrFromDec($netInfos->{network}) == 32)) ) { $netInfos->{origin} = $netID; push @{$networks}, $netInfos; } if ($config->{$netID}->{def_recurrent_withSubnets}) { my @childs = &getSubnets(0, $netID, $config->{$netID}->{def_recurrent_maxdepth} || 1, $onlyHosts, $netID); foreach (@childs) { push @{$networks}, $_; } } } &log(" " . ($#{$networks} + 1) . ' Networks found for Plugin ' . $plugin . '!', 2); $config->{-1} = $pluginGlobConfig; &log(" initiating Plugin $plugin ($pluginID)...", 3); my $plug; eval { $plug = $package->new($pluginID, $conf->{var}->{TABLES}->{pluginValue}); }; if ($@) { &log(" Error while initiating Plugin '$plugin': $@", 1); $pluginData->{running}->{$pluginID} = 0; return; } &log(" running Plugin $plugin ($pluginID)...", 2); $pluginData->{lastRuns}->{$pluginID} = time; &updatePluginLastRun( $pluginID, $pluginData->{lastRuns}->{$pluginID}, 0, '' ); eval { $plug->run_recurrent($networks, $config); }; if ($@) { &log(" Error while running Plugin '$plugin': $@", 1); } my $error = 0; my $errorStr = ''; if ($plug->can('ERROR')) { $error = $plug->ERROR(); } else { &log(" Plugin has no ERROR Method!", 2); } if ($plug->can('ERRORSTR')) { $errorStr = $plug->ERRORSTR(); } else { &log(" Plugin has no ERRORSTR Method!", 2); } my $runtime = time - $pluginData->{lastRuns}->{$pluginID}; $pluginData->{running}->{$pluginID} = 0; &log(" Plugin $plugin finished ($runtime Seconds Runtime)", 2); &updatePluginLastRun( $pluginID, -1, $runtime, (($error) ? $errorStr : '') ); undef $plug; } sub getSubnets { my $level = shift; my $netID = shift; my $maxDepth = shift; my $onlyHosts = shift; my $origin = shift; my $subnets = []; $level++; my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); my $networkT = undef; while ($networkT = &getNextDBNetwork($rootID, $ipv6, $networkDec)) { last if $networkT->{network} == $networkDec || $networkT->{network} > $broadcast; if (!$onlyHosts || $onlyHosts && (($ipv6 && &getCidrFromNetv6Dec($networkT->{network}) == 128) || (!$ipv6 && &getCidrFromDec($networkT->{network}) == 32)) ) { $networkT->{origin} = $origin; push @{$subnets}, $networkT; } push @{$subnets}, &getSubnets($level, $networkT->{ID}, $maxDepth, $onlyHosts, $origin) if $level < $maxDepth; $networkDec = ($ipv6) ? &getV6BroadcastNet($networkT->{network}, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkT->{network})) . '/32'); } return @{$subnets}; } sub loadPlugins { &initTables(); $plugins = &getPlugins(); $lastReload = time; foreach (keys %{$plugins}) { my $pluginID = $_; my $plugin = $plugins->{$pluginID}->{NAME}; unless ($plugins->{$pluginID}->{RECURRENT} && $plugins->{$pluginID}->{ACTIVE}) { delete $plugins->{$pluginID}; next; } } &log((scalar keys %{$plugins}) . " recurrent Plugin(s) found\n", 2); } sub daemonize { my $return = shift || 0; &log("Maximal number of Childs reached ($maxChilds)", 3) if $nrOfChilds >= $maxChilds; while ($nrOfChilds >= $maxChilds) { sleep 1; } $SIG{'INT'} = $SIG{'HUP'} = $SIG{'TERM'} = \&exitHandler; $SIG{'CHLD'} = \&reaper; my ($sessID, $pid); if ($pid = &fork) { $nrOfChilds++; &log("New Child forked ($pid)! ($nrOfChilds/$maxChilds)", 3); if ($return) { return $pid; } else { print "Daemon started...\n"; exit 0; } } unless ($sessID = POSIX::setsid()) { &log("Cannot detach from controlling terminal", 1); die "Cannot detach from controlling terminal"; } chdir "/"; umask 0; foreach (0 .. POSIX::sysconf(&POSIX::_SC_OPEN_MAX)) { POSIX::close($_); } open(STDIN, "+>/dev/null"); open(STDOUT, "+>&STDIN"); # open(STDERR, "+>&STDIN"); $log = new Log::LogLite($ENV{workdir} . '/logs/HaCid.log',3); $log->template(' ' . "\n"); return 0; } sub fork { my $pid; FORK: { if (defined($pid = fork)) { } elsif ($! =~ /No more process/) { &log("Cannot fork: $!", 1); sleep 5; redo FORK; } else { die "Can't fork: $!"; } } return $pid; } sub diel { my @msg = @_; &log(join(', ', @msg), 1); die $msg[0]; } sub log { my $msg = shift; my $level = shift || 1; my $local = shift || 0; $msg = (($level == 1) ? 'ERROR' : ($level == 2) ? 'INFO' : 'DEBUG') . ': ' . $msg; $msg = $$ . ': ' . $msg; if (defined $log) { chomp($msg); $log->write($msg, $level) if $level < 3 || $debug; print STDERR "$msg\n" if $local; } else { print STDERR "$msg\n"; } } sub reaper { my $child; while (($child = waitpid(-1, WNOHANG)) > 0) { sleep 1; $pluginData->{running}->{$pluginData->{PID}->{$child}} = 0 if exists $pluginData->{PID}->{$child}; $nrOfChilds--; &log(" Child '$child' exited with: $? ($nrOfChilds/$maxChilds)", 3); } $SIG{CHLD} = \&reaper; } sub exitHandler { my $sig = shift; &log("Catching SIG$sig", 2); if ($sig eq 'HUP') { &loadPlugins(); } elsif ($sig eq 'INT' || $sig eq 'TERM') { $running = 0; } } sub getPID { unless (open PID, $conf->{static}->{path}->{hacidpid}) { &log("Cannot open Pidfile '$conf->{static}->{path}->{hacidpid}' for reading: $!", 3); return; } my $pid = ; close PID; return $pid; } sub checkProcessList { my $pid = &getPID(); my $nrOfProz = ($pid) ? qx(ps -p $pid --no-headers | wc -l) : 0; chomp($nrOfProz); return $nrOfProz; } sub cleanupPluginData { my $plugin = {}; foreach (keys %{$pluginData->{PID}}) { my $pid = $_; my $pluginID = $pluginData->{PID}->{$pid}; my $nrOfProcs = qx/ps -p $pid h | wc -l/; if (int($nrOfProcs)) { $plugin->{$pluginID} = 1; } else { $plugin->{$pluginID} = 0 unless exists $plugin->{$pluginID}; delete $pluginData->{PID}->{$pid}; } } foreach (keys %{$plugin}) { my $pluginID = $_; unless ($plugin->{$pluginID}) { &log(" Clear Plugin '$pluginID' from pluginData, because no Child is working on this Plugin", 3); $pluginData->{running}->{$pluginID} = 0; } } } sub main::HELP_MESSAGE { print " NAME HaCid - HaCi Daemon - runns HaCi Plugins periodically SYNOPSIS HaCid [-c] [-d] OPTIONS -c Cronjob mode. Don't run as daemon. Program exits after one full run -d Debug mode. Logging all debug messages "; } sub wrPID { my $del = shift || 0; if ($del) { unless (unlink $conf->{static}->{path}->{hacidpid}) { &log("Cannot delete Pidfile '$conf->{static}->{path}->{hacidpid}': $!", 2); return; } } else { unless (open PID, '>' . $conf->{static}->{path}->{hacidpid}) { &log("Cannot write Pidfile '$conf->{static}->{path}->{hacidpid}': $!", 2); return; } print PID $$; close PID; } } sub checkMemConsumption { my $size = 0; open PIPE, "/bin/ps wwaxo 'pid,rss' |"; while() { next unless /^\s*$$\s/; s/^\s+//g; chomp; $size = (split(' ', $_))[1]; } close PIPE; &log("Checking size: ${size}kB (max ${maxMemCon}kB)", 3); if($size > $maxMemCon) { &log("Max Memory Size reached: ${size}kB/${maxMemCon}kB, restarting ($progFile @ARGV)...", 2); exec $progFile, @argv or die "Restart failed: $!"; } } # vim:ts=2:sts=2:sw=2 HaCi/bin/cleanupDatabase.pl0000755000175000000000000004416312377724507015254 0ustar fighterroot#!/usr/bin/perl # # Check database for inconsistent entries and remove them package HaCi::HaCi; use warnings; use strict; local $| = 1; use FindBin; use lib "$FindBin::Bin/../modules"; chdir "$FindBin::Bin/.."; my $workDir = qx/pwd/; chomp($workDir); $ENV{workdir} = $workDir; use Getopt::Std; use Data::Dumper qw/Dumper/; use HaCi::Conf qw/getConfigValue/; use HaCi::Utils qw/getConfig getPlugins getTable initTables finalizeTables initCache/; $Getopt::Std::STANDARD_HELP_VERSION = 1; $main::VERSION = 0.1; our $opts = {}; getopts('cd', $opts); my $mysqlDumpBin = 'mysqldump'; my $mysqlDumpArgs = q{-u'' -p'' -h'' ''}; my $postgresqlDumpBin = 'pg_dump'; my $postgresqlDumpArgs = q{-U'' -h'' ''}; my $debug = (exists $opts->{d}) ? $opts->{d} : 0; my @busy = ('|', '/', '-', '\\', '-'); my ($bc, $ec, $sc) = (0, 0, 0); my ($userTable, $groupTable, $rootTable, $rootACTable, $networkTable, $networkV6Table, $networkACTable, $networkPluginTable, $templateTable, $templateEntryTable, $templateValueTable, $pluginTable, $pluginConfTable, $pluginValueTable, $settingTable) = (); our $conf; *conf = \$HaCi::Conf::conf; &HaCi::Conf::init($workDir); &getConfig(); &init(); &main(); exit 0; #-------------------------------------------- END { &finalizeTables(); } sub init { &initTables(); $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { die "Cannot get users. DB Error (user)\n"; } $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { die "Cannot get groups. DB Error (group)\n"; } $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { die "Cannot get roots. DB Error (root)\n"; } $rootACTable = $conf->{var}->{TABLES}->{rootAC}; unless (defined $rootACTable) { die "Cannot get rootACs. DB Error (rootAC)\n"; } $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { die "Cannot get Networks. DB Error (network)\n"; } $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { die "Cannot get Networks. DB Error (networkV6)\n"; } $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { die "Cannot get networkACs. DB Error (networkAC)\n"; } $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { die "Cannot get networkPlugins. DB Error (networkPlugin)\n"; } $templateTable = $conf->{var}->{TABLES}->{template}; unless (defined $templateTable) { die "Cannot get templates. DB Error (template)\n"; } $templateEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $templateEntryTable) { die "Cannot get templateEntrys. DB Error (templateEntry)\n"; } $templateValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $templateValueTable) { die "Cannot get templateValues. DB Error (templateValue)\n"; } $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { die "Cannot get plugins. DB Error (plugin)\n"; } $pluginConfTable = $conf->{var}->{TABLES}->{pluginConf}; unless (defined $pluginConfTable) { die "Cannot get pluginConfs. DB Error (pluginConf)\n"; } $pluginValueTable = $conf->{var}->{TABLES}->{pluginValue}; unless (defined $pluginValueTable) { die "Cannot get pluginValues. DB Error (pluginValue)\n"; } $settingTable = $conf->{var}->{TABLES}->{setting}; unless (defined $settingTable) { die "Cannot get settings. DB Error (setting)\n"; } } sub main { if ($opts->{c}) { print "\nPlease make sure to make a database backup before cleaning it up.\n"; my $dbType = &getConfigValue('db', 'dbtype'); my $dumpBin = ''; my $dumpArgs = ''; if ($dbType eq 'mysql') { $dumpBin = $mysqlDumpBin; $dumpArgs = $mysqlDumpArgs; } elsif ($dbType eq 'postgresql') { $dumpBin = $postgresqlDumpBin; $dumpArgs = $postgresqlDumpArgs; } if ($dumpBin && system("which $dumpBin >/dev/null") == 0) { my $md = qx/which $dumpBin/; chomp($md); my $dbConf = $conf->{user}->{db}; my $pwd = qx/pwd/; chomp($pwd); my $dbnameO = "$pwd/$dbConf->{dbname}.sql"; my $dbname = $dbnameO; my $cnter = 1; while ( -f $dbname ) { $dbname = $dbnameO . '.' . $cnter++; } #my $backupT = $md . " -u'$dbConf->{dbuser}' -p'XXX' -h'$dbConf->{dbhost}' '$dbConf->{dbname}' > $dbname"; my $backupT = $md . ' ' . $dumpArgs . "> $dbname"; $backupT =~ s//$dbConf->{dbuser}/g; $backupT =~ s//XXX/g; $backupT =~ s//$dbConf->{dbhost}/g; $backupT =~ s//$dbConf->{dbname}/g; print "Found '$dumpBin':\n\t$backupT\nShould I run this dump? [y|N]: "; my $bb = ; chomp($bb); if (lc($bb) eq 'y') { #my $backupStr = $md . " -u'$dbConf->{dbuser}' -p'$dbConf->{dbpass}' -h'$dbConf->{dbhost}' '$dbConf->{dbname}' > $dbname"; my $backupStr = $md . ' ' . $dumpArgs . "> $dbname"; $backupStr =~ s//$dbConf->{dbuser}/g; $backupStr =~ s//$dbConf->{dbpass}/g; $backupStr =~ s//$dbConf->{dbhost}/g; $backupStr =~ s//$dbConf->{dbname}/g; print qx($backupStr); if ($?) { print "... Dump was NOT successful!!!\n"; } else { print "... Dump was successful\n"; } } } print "Continue cleaning up database? [y|N]: "; my $con = ; print "\n"; chomp($con); exit 0 unless lc($con) eq 'y'; } &checkRootStuff(); &checkBrokenNets(); &checkNetIDStuff(); &checkpluginIDStuff(); &checkTmplIDStuff(); print "\n$bc Items checked!\n"; print '' . ($sc + $ec) . " bad entries found. Deleted [" . $sc . " successful | " . $ec . " unsuccessful]\n" if $opts->{c}; if ($opts->{c} && $sc > 0) { my $error = 0; my ($aclCacheHandle, $netCacheHandle) = &initCache(); if (defined $netCacheHandle) { warn "Cannot set Cache (netCache-DB!)\n" unless $netCacheHandle->set('DB', {}); warn "Cannot set Cache (netCache-FILL!)\n" unless $netCacheHandle->set('FILL', {}); warn "Cannot set Cache (netCache-NET!)\n" unless $netCacheHandle->set('NET', {}); } else { $error = 1; } if (defined $aclCacheHandle) { warn "Cannot set Cache (aclCache!)\n" unless $aclCacheHandle->set('HASH', {}); } else { $error = 1; } warn "Cannot flush the cache. Please press the 'Flush Cache' button in the web gui.\n\n" if $error; } else { print "\nCall this script with '-c' in order to cleanup these issues: $0 -c\n\n"; } } sub checkRootStuff { my $oEC = $ec; my $oSC = $sc; print "\nSearch for entries with bad rootID...\n"; # Get Roots my @rootst = $rootTable->search(['ID'], 0, 0, 0, 1); if ($#rootst == -1) { print "\r No roots available\n"; return; } my $roots = ''; foreach (@rootst) { $roots .= ',' if $roots; $roots .= $_->{ID} } # Get stale V4 my $nriCol = $networkTable->meth2Col('rootID'); my @networksNR = $networkTable->search(['ID', 'network', 'description', 'rootID', 'ipv6ID'], {}, 0, "WHERE $nriCol NOT IN ($roots)"); foreach (@networksNR) { my $ip = $_; print "\r" . $busy[$bc++%5]; print "\r $ip->{network}:$ip->{rootID} ($ip->{ipv6ID}) => $ip->{description}: Stale V4 network entry (root doesn't exists)\n" if $debug; &delEntry($networkTable, $ip->{ID}); } print "\r " . ($#networksNR + 1) . " stale V4 network entrys found without matching root\n"; # Get stale V6 my $nriV6Col = $networkV6Table->meth2Col('rootID'); my @networkV6sNR = $networkV6Table->search(['ID', 'rootID'], {}, 0, "WHERE $nriV6Col NOT IN ($roots)"); foreach (@networkV6sNR) { my $ip = $_; print "\r" . $busy[$bc++%5]; print "\r $ip->{ID} : $ip->{rootID}: Stale V6 network entry (root doesn't exists)\n" if $debug; &delEntry($networkV6Table, {ID => $ip->{ID}, rootID => $ip->{rootID}}); } print "\r " . ($#networkV6sNR + 1) . " stale V6 network entrys found without matching root\n"; # Get stale rootAC my $rriCol = $rootACTable->meth2Col('rootID'); my @rootACsNR = $rootACTable->search(['ID', 'rootID'], {}, 0, "WHERE $rriCol NOT IN ($roots)"); foreach (@rootACsNR) { my $ac = $_; print "\r" . $busy[$bc++%5]; print "\r $ac->{ID} : $ac->{rootID}: Stale root ACL entry (root doesn't exists)\n" if $debug; &delEntry($rootACTable, $ac->{ID}); } print "\r " . ($#rootACsNR + 1) . " stale root ACL entrys found without matching root\n"; print "\n" if $debug; print '' . (($sc - $oSC) + ($ec - $oEC)) . " bad entries found. Deleted [" . ($sc - $oSC) . " successful | " . ($ec - $oEC) . " unsuccessful]\n" if $opts->{c}; } sub checkBrokenNets { my $oEC = $ec; my $oSC = $sc; print "\nSearch for corrupt V6 networks...\n"; # get corrupt V6 networks my $sNError = 0; my $brokenNError = 0; my @networks = $networkTable->search(['ID', 'network', 'description', 'rootID', 'ipv6ID'], {network => 0}); foreach (@networks) { my $ip = $_; my $v6ID = $_->{ipv6ID}; print "\r" . $busy[$bc++%5]; unless ($v6ID) { print "\r $ip->{network}:$ip->{rootID} ($ip->{ipv6ID}) => $ip->{description}: NO network AND no v6ID!\n" if $debug; $brokenNError++; &delEntry($networkTable, $ip->{ID}); next; } my $ip6 = ($networkV6Table->search(['ID'], {ID => $ip->{ipv6ID}, rootID => $ip->{rootID}}))[0]; if (!defined $ip6) { print "\r $ip->{network}:$ip->{rootID} ($ip->{ipv6ID}) => $ip->{description}: Stale network entry (no V6 part)\n" if $debug; &delEntry($networkTable, $ip->{ID}); $sNError++; } } print "\r $sNError stale network entrys found without matching V6 Part\n"; print " $brokenNError broken network entrys found\n"; my $sN6Error = 0; my @v6Networks = $networkV6Table->search(['ID', 'rootID']); foreach (@v6Networks) { my $ip = $_; my $v6ID = $_->{ID}; print "\r" . $busy[$bc++%5]; my $ip4 = ($networkTable->search(['ID'], {ipv6ID => $v6ID, rootID => $ip->{rootID}}))[0]; if (!defined $ip4) { print "\r $v6ID : $ip->{rootID}: Stale V6 network entry (no network part)\n" if $debug; &delEntry($networkV6Table, {ID => $ip->{ID}, rootID => $ip->{rootID}}); $sN6Error++; } } print "\r $sN6Error stale V6 network entrys found without matching network Part\n"; print "\n" if $debug; print '' . (($sc - $oSC) + ($ec - $oEC)) . " bad entries found. Deleted [" . ($sc - $oSC) . " successful | " . ($ec - $oEC) . " unsuccessful]\n" if $opts->{c}; } sub checkNetIDStuff { my $oEC = $ec; my $oSC = $sc; print "\nSearch for entries with bad network ID...\n"; # Get Networks my @netst = $networkTable->search(['ID'], 0, 0, 0, 1); if ($#netst == -1) { print "\r No networks available\n"; return; } my $nets = {}; foreach (@netst) { $nets->{$_->{ID}} = 1; } # Get stale networkAC my @networkACs = $networkACTable->search(['ID', 'netID', 'ACL']); my $cnter = 0; foreach (@networkACs) { my $ac = $_; next if !$ac->{netID} && $ac->{ACL} == 4; print "\r" . $busy[$bc++%5]; if (!exists $nets->{$ac->{netID}}) { $cnter++; &delEntry($networkACTable, $ac->{ID}); print "\r ACL $ac->{ID} : $ac->{netID}: Stale network ACL entry (network doesn't exists)\n" if $debug; } } print "\r $cnter stale network ACL entrys found without matching network\n"; print "\n" if $debug; # Get stale network plugins my @networkPlugins = $networkPluginTable->search(['ID', 'netID']); $cnter = 0; foreach (@networkPlugins) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if ($plugin->{netID} != -1 && !exists $nets->{$plugin->{netID}}) { $cnter++; &delEntry($networkPluginTable, $plugin->{ID}); print "\r ID $plugin->{ID} - netID $plugin->{netID}: Stale network plugin entry (network doesn't exists)\n" if $debug; } } print "\r $cnter stale network Plugin entrys found without matching network\n"; print "\n" if $debug; # Get stale plugin confs my @pluginConfs = $pluginConfTable->search(['ID', 'netID']); $cnter = 0; foreach (@pluginConfs) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if ($plugin->{netID} != -1 && !exists $nets->{$plugin->{netID}}) { $cnter++; &delEntry($pluginConfTable, $plugin->{ID}); print "\r ID $plugin->{ID} - netID $plugin->{netID}: Stale plugin config entry (network doesn't exists)\n" if $debug; } } print "\r $cnter stale plugin config entrys found without matching network\n"; print "\n" if $debug; # Get stale plugin values my @pluginValues = $pluginValueTable->search(['ID', 'netID']); $cnter = 0; foreach (@pluginValues) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if ($plugin->{netID} != -1 && !exists $nets->{$plugin->{netID}}) { $cnter++; &delEntry($pluginValueTable, $plugin->{ID}); print "\r ID $plugin->{ID} - netID $plugin->{netID}: Stale plugin value entry (network doesn't exists)\n" if $debug; } } print "\r $cnter stale plugin value entrys found without matching network\n"; print "\n" if $debug; # Get stale template values my @templateValues = $templateValueTable->search(['ID', 'netID']); $cnter = 0; foreach (@templateValues) { my $template = $_; print "\r" . $busy[$bc++%5]; if ($template->{netID} != -1 && !exists $nets->{$template->{netID}}) { $cnter++; &delEntry($templateValueTable, $template->{ID}); print "\r ID $template->{ID} - netID $template->{netID}: Stale template value entry (network doesn't exists)\n" if $debug; } } print "\r $cnter stale template value entrys found without matching network\n"; print "\n" if $debug; print '' . (($sc - $oSC) + ($ec - $oEC)) . " bad entries found. Deleted [" . ($sc - $oSC) . " successful | " . ($ec - $oEC) . " unsuccessful]\n" if $opts->{c}; } sub checkpluginIDStuff { my $oEC = $ec; my $oSC = $sc; print "\nSearch for entries with bad plugin ID...\n"; # Get Plugins my @pluginst = $pluginTable->search(['ID'], 0, 0, 0, 1); if ($#pluginst == -1) { print "\r No plugins available\n"; return; } my $plugins = {}; foreach (@pluginst) { $plugins->{$_->{ID}} = 1; } # Get stale network plugins my @networkPlugins = $networkPluginTable->search(['ID', 'pluginID']); my $cnter = 0; foreach (@networkPlugins) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if (!exists $plugins->{$plugin->{pluginID}}) { $cnter++; &delEntry($networkPluginTable, $plugin->{ID}); print "\r ID $plugin->{ID} - netID $plugin->{pluginID}: Stale network plugin entry (plugin doesn't exists)\n" if $debug; } } print "\r $cnter stale network Plugin entrys found without matching plugin\n"; print "\n" if $debug; # Get stale plugin confs my @pluginConfs = $pluginConfTable->search(['ID', 'pluginID']); $cnter = 0; foreach (@pluginConfs) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if (!exists $plugins->{$plugin->{pluginID}}) { $cnter++; &delEntry($pluginConfTable, $plugin->{ID}); print "\r ID $plugin->{ID} - pluginID $plugin->{pluginID}: Stale plugin config entry (plugin doesn't exists)\n" if $debug; } } print "\r $cnter stale plugin config entrys found without matching plugin\n"; print "\n" if $debug; # Get stale plugin values my @pluginValues = $pluginValueTable->search(['ID', 'pluginID']); $cnter = 0; foreach (@pluginValues) { my $plugin = $_; print "\r" . $busy[$bc++%5]; if (!exists $plugins->{$plugin->{pluginID}}) { $cnter++; &delEntry($pluginValueTable, $plugin->{ID}); print "\r ID $plugin->{ID} - pluginID $plugin->{pluginID}: Stale plugin value entry (plugin doesn't exists)\n" if $debug; } } print "\r $cnter stale plugin value entrys found without matching plugin\n"; print "\n" if $debug; print '' . (($sc - $oSC) + ($ec - $oEC)) . " bad entries found. Deleted [" . ($sc - $oSC) . " successful | " . ($ec - $oEC) . " unsuccessful]\n" if $opts->{c}; } sub checkTmplIDStuff { my $oEC = $ec; my $oSC = $sc; print "\nSearch for entries with bad template ID...\n"; # Get tmpls my @tmplst = $templateTable->search(['ID'], 0, 0, 0, 1); if ($#tmplst == -1) { print "\r No templates available\n"; return; } my $tmpls = { 0 => 1 }; foreach (@tmplst) { $tmpls->{$_->{ID}} = 1; } # Get stale networks my @networks = $networkTable->search(['ID', 'tmplID']); my $cnter = 0; foreach (@networks) { my $net = $_; print "\r" . $busy[$bc++%5]; if (!exists $tmpls->{$net->{tmplID}}) { $cnter++; if ($opts->{c}) { $networkTable->tmplID(0); unless ($networkTable->update({ID => $net->{ID}})) { warn $networkTable->errorStrs(); $ec++; } else { $sc++; } } print "\r ID $net->{ID} - tmplID $net->{tmplID}: Stale network entry (template doesn't exists)\n" if $debug; } } print "\r $cnter stale network entrys found without matching template\n"; print "\n" if $debug; # Get stale template Entrys my @tmplEntrys = $templateEntryTable->search(['ID', 'tmplID']); $cnter = 0; foreach (@tmplEntrys) { my $tmplEntry = $_; print "\r" . $busy[$bc++%5]; if (!exists $tmpls->{$tmplEntry->{tmplID}}) { $cnter++; &delEntry($templateEntryTable, $tmplEntry->{ID}); print "\r ID $tmplEntry->{ID} - tmplID $tmplEntry->{tmplID}: Stale templateEntry entry (template doesn't exists)\n" if $debug; } } print "\r $cnter stale templateEntry entrys found without matching template\n"; print "\n" if $debug; # Get stale template Values my @tmplValues = $templateValueTable->search(['ID', 'tmplID']); $cnter = 0; foreach (@tmplValues) { my $tmplValue = $_; print "\r" . $busy[$bc++%5]; if (!exists $tmpls->{$tmplValue->{tmplID}}) { $cnter++; &delEntry($templateValueTable, $tmplValue->{ID}); print "\r ID $tmplValue->{ID} - tmplID $tmplValue->{tmplID}: Stale templateValue entry (template doesn't exists)\n" if $debug; } } print "\r $cnter stale templateValue entrys found without matching template\n"; print "\n" if $debug; print '' . (($sc - $oSC) + ($ec - $oEC)) . " bad entries found. Deleted/Modified [" . ($sc - $oSC) . " successful | " . ($ec - $oEC) . " unsuccessful]\n" if $opts->{c}; } sub main::HELP_MESSAGE { print " NAME cleanupDatabase.pl - clean up HaCi database SYNOPSIS $0 [-c] [-d] OPTIONS -c Clean up issues. Without this option issues will only be reported. -d Debug mode. Show every issue. Please make sure to make a database backup before cleaning it up. "; } sub delEntry { my $table = shift; my $ID = shift; my $pk = (ref($ID) eq 'HASH') ? $ID : {ID => $ID}; if ($opts->{c}) { unless($table->delete($pk)) { warn $table->errorStrs(); $ec++; } else { $sc++; } } } # vim:ts=2:sts=2:sw=2 HaCi/bin/checkPerlDependencies.bash0000755000175000000000000000651412475462104016676 0ustar fighterroot#!/usr/bin/env bash # # Checking for Perl module dependencies # It can try to install them automatically on Demand modules="CGI CGI::Ajax CGI::Carp CGI::Cookie CGI::Session Class::Accessor Class::MakeMethods Config::General Digest::MD5 Digest::SHA Encode Encode::Guess File::Temp HTML::Entities Locale::gettext Log::LogLite Math::BigInt#1.87 Net::CIDR Net::IPv6Addr Net::SNMP Storable Template::Toolkit%Template Time::Local Text::CSV" aptget=`which apt-get 2>/dev/null` yum=`which yum 2>/dev/null` yast2=`which yast2 2>/dev/null` zypper=`which zypper 2>/dev/null` cpan=`which cpan 2>/dev/null` checkModules() { index=0 echo "Checking if all Perl Dependencies are available..." for mod in $modules do modOrig=$mod package='' ver='' if echo $mod | grep -q '%'; then package=`echo $mod|cut -d '%' -f 2`; mod=`echo $mod|cut -d '%' -f 1`; fi if echo $mod | grep -q '#'; then ver=`echo $mod|cut -d '#' -f 2`; mod=`echo $mod|cut -d '#' -f 1`; fi echo -n "$mod" test -z $ver || echo -n " v$ver" echo -n ': ' test -z $package && package=$mod if perl -e "use $package $ver" 2>/dev/null; then echo "OK"; else echo "NO"; failed[$index]=$modOrig ((index++)) fi done if [ "$index" -eq "0" ] then return 0 else return 1 fi } showMissing() { echo; echo "The following are missing:" modIndex=0 while [ ${modIndex} -lt ${#failed[@]} ] do mod="${failed[$modIndex]}" if echo $mod | grep -q '%'; then mod=`echo $mod|cut -d '%' -f 1`; fi echo $mod modIndex=$((${modIndex}+1)) done } checkModules if [ "$?" -eq "0" ]; then echo; echo "Everything fine. All Dependencies are installed."; exit 0; fi showMissing if ( test -f /etc/debian_version && [ "$aptget" != "" ] && test -x $aptget); then bDebian=1; else bDebian=0; fi if ( [ "$cpan" != "" ] && test -x $cpan); then bCpan=1; else bCpan=0; fi if ( [ "$yast2" != "" ] && test -x $yast2); then bYast2=1; else bYast2=0; fi if ( [ "$zypper" != "" ] && test -x $zypper); then bZypper=1; else bZypper=0; fi if ( [ "$yum" != "" ] && test -x $yum); then bYum=1; else bYum=0; fi if [ "$bDebian" -eq 0 ] && [ "$bCpan" -eq 0 ] && [ "$bYast2" -eq 0 ] && [ "$bYum" -eq 0 ] then echo; echo "Don't know how to install these modules. Please do it by hand" exit 0; fi echo; echo -n "Should I try to install them automatically? [y|N] " read res if [ "$res" != "y" ] && [ "$res" != "Y" ]; then exit 0; fi; if [ "$bCpan" -eq 1 ]; then echo; echo -n "Should I use CPAN? Please note that you have to have a C-Compiler like gcc installed. [Y|n] " read res if [ "$res" != "y" ] && [ "$res" != "Y" ] && [ "$res" != "" ]; then bCpan=0; fi; fi modIndex=0 while [ ${modIndex} -lt ${#failed[@]} ] do mod=${failed[$modIndex]} if echo "$mod" | grep -q '%'; then mod=`echo "$mod" | cut -d '%' -f 1`; fi echo "$mod" if [ "$bCpan" -eq 1 ]; then $cpan $mod elif [ "$bDebian" -eq 1 ]; then $aptget -y install lib${mod//::/-}-perl elif [ "$bYast2" -eq 1 ]; then $yast2 -i perl-${mod//::/-} elif [ "$bYum" -eq 1 ]; then $yum install -y perl-${mod//::/-} else echo "Sorry, don't know how to get this module..." fi modIndex=$((${modIndex}+1)) done checkModules if [ "$?" -eq "1" ]; then showMissing echo; echo "Sorry, could't install all Dependencies. Try it by Hand." else echo; echo "Everything fine. All Dependencies are installed." fi exit 0 # vim:ts=2:sts=2:sw=2 HaCi/bin/checkPerlOptionals.bash0000755000175000000000000000663212475462106016263 0ustar fighterroot#!/usr/bin/env bash # # Checking for optional Perl module dependencies # It can try to install them automatically on demand modules="Cache::FastMmap Cache::FileCache DNS::ZoneParse IO::Socket::INET6 Math::BigInt::GMP Net::DNS Net::Ping Pod::WSDL SOAP::Transport::HTTP SQL::Translator#0.09000 Apache::DBI DBD::Pg DBD::mysql Frontier-RPC%Frontier::Client LDAP%Net::LDAP Frontier::RPC2 File::Basename Time::HiRes JSON URI::Query Text::CSV_XS" aptget=`which apt-get 2>/dev/null` yast2=`which yast2 2>/dev/null` yum=`which yum 2>/dev/null` zypper=`which zypper 2>/dev/null` cpan=`which cpan 2>/dev/null` checkModules() { index=0 echo "Checking if all optional Perl dependencies are available..." for mod in $modules do modOrig=$mod package='' ver='' if echo $mod | grep -q '%'; then package=`echo $mod|cut -d '%' -f 2`; mod=`echo $mod|cut -d '%' -f 1`; fi if echo $mod | grep -q '#'; then ver=`echo $mod|cut -d '#' -f 2`; mod=`echo $mod|cut -d '#' -f 1`; fi echo -n "$mod" test -z $ver || echo -n " v$ver" echo -n ': ' test -z $package && package=$mod if perl -e "use $package $ver" 2>/dev/null; then echo "OK"; else echo "NO"; failed[$index]=$modOrig ((index++)) fi done if [ "$index" -eq "0" ] then return 0 else return 1 fi } showMissing() { echo; echo "The following are missing:" modIndex=0 while [ ${modIndex} -lt ${#failed[@]} ] do mod=${failed[$modIndex]} if echo $mod | grep -q '%'; then mod=`echo $mod|cut -d '%' -f 1`; fi echo $mod modIndex=$((${modIndex}+1)) done } checkModules if [ "$?" -eq "0" ]; then echo; echo "Everything fine. All Dependencies are installed."; exit 0; fi showMissing if ( test -f /etc/debian_version && [ "$aptget" != "" ] && test -x $aptget); then bDebian=1; else bDebian=0; fi if ( [ "$cpan" != "" ] && test -x $cpan); then bCpan=1; else bCpan=0; fi if ( [ "$yast2" != "" ] && test -x $yast2); then bYast2=1; else bYast2=0; fi if ( [ "$yum" != "" ] && test -x $yum); then bYum=1; else bYum=0; fi if ( [ "$zypper" != "" ] && test -x $zypper); then bZypper=1; else bZypper=0; fi if [ "$bDebian" -eq 0 ] && [ "$bCpan" -eq 0 ] && [ "$bYast2" -eq 0 ] && [ "$bYum" -eq 0 ] then echo; echo "Don't know how to install these modules. Please do it by hand" exit 0; fi echo; echo -n "Should I try to install them automatically? [y|N] " read res if [ "$res" != "y" ] && [ "$res" != "Y" ]; then exit 0; fi; if [ "$bCpan" -eq 1 ]; then echo; echo -n "Should I use CPAN? Please note that you have to have a C-Compiler like gcc installed. [Y|n] " read res if [ "$res" != "y" ] && [ "$res" != "Y" ] && [ "$res" != "" ]; then bCpan=0; fi; fi modIndex=0 while [ ${modIndex} -lt ${#failed[@]} ] do mod=${failed[$modIndex]} if echo $mod | grep -q '%'; then mod=`echo $mod|cut -d '%' -f 1`; fi if echo $mod | grep -q '#'; then mod=`echo $mod|cut -d '#' -f 1`; fi echo $mod if [ "$bCpan" -eq 1 ]; then $cpan $mod elif [ "$bDebian" -eq 1 ]; then $aptget -y install lib${mod//::/-}-perl elif [ "$bYum" -eq 1 ]; then $yum install -y perl-${mod//::/-} elif [ "$bYast2" -eq 1 ]; then $yast2 -i perl-${mod//::/-} else echo "Sorry, don't know how to get this module..." fi modIndex=$((${modIndex}+1)) done checkModules if [ "$?" -eq "1" ]; then showMissing echo; echo "Sorry, could't install all Dependencies. Try it by Hand." else echo; echo "Everything fine. All Dependencies are installed." fi exit 0 # vim:ts=2:sts=2:sw=2 HaCi/bin/testXMLRPCAPI.pl0000755000175000000000000002450012475462153014423 0ustar fighterroot#!/usr/bin/perl # Test all available XMLRPC-API-Calls use strict; use warnings; use Data::Dumper; use Frontier::Client; my $server = 'demo.haci.larsux.de'; my $user = 'admin'; my $pass = 'admin'; my $debug = 1; my $v6 = $ARGV[0] || 0; my $api = new Frontier::Client(url => "http://$server/RPC2"); my $session = $api->call('login', [$user, $pass]); die 'Login failed!' unless $session; unless ($v6) { &test('addRoot', ['_apiTestRoot', 0], 0 ); &test('listRoots', [], {name => '_apiTestRoot', ipv6 => 0}, [['delRoot', ['_apiTestRoot']]] ); &test('addNet', ['_apiTestRoot', '192.168.0.0/24', '1. test network', 'IN USE', 30], 0, [['delRoot', ['_apiTestRoot']]] ); &test('addNet', ['_apiTestRoot', '192.168.0.8/29', '2. test network', 'IN USE', 32], 0, [['delRoot', ['_apiTestRoot']]] ); &test('addNet', ['_apiTestRoot', '192.168.0.10/32', '3. test network', 'IN USE'], 0, [['delRoot', ['_apiTestRoot']]] ); &test('addNet', ['_apiTestRoot', '192.168.0.100/32', '30. test network', 'IN USE'], 0, [['delRoot', ['_apiTestRoot']]] ); &test('getFreeSubnets', ['_apiTestRoot', '192.168.0.0/24', 29, 2], ['192.168.0.0/29', '192.168.0.16/29'], [['delRoot', ['_apiTestRoot']]] ); &test('getFreeSubnetsFromSearch', ['1. test network', '', '', '', '', '_apiTestRoot', 28, 1], {network => '192.168.0.16/28', rootName => '_apiTestRoot'}, [['delRoot', ['_apiTestRoot']]] ); &test('getFreeSubnets', ['_apiTestRoot', '192.168.0.8/29', 32, 2], ['192.168.0.9/32', '192.168.0.11/32'], [['delRoot', ['_apiTestRoot']]] ); &test('search', ['192.168.0.0/24', '', 1, '', '', '_apiTestRoot'], {network => '192.168.0.0/24', description => '1. test network', rootName => '_apiTestRoot', state => 'IN USE'}, [['delRoot', ['_apiTestRoot']]] ); &test('search', ['1. test network', '', 1, '', '', '_apiTestRoot'], {network => '192.168.0.0/24', description => '1. test network', rootName => '_apiTestRoot', state => 'IN USE'}, [['delRoot', ['_apiTestRoot']]] ); &test('editNet', ['_apiTestRoot', '192.168.0.10/32', {description => '4. test network', state => 'FREE'}], 0, [['delRoot', ['_apiTestRoot']]] ); &test('search', ['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], {network => '192.168.0.10/32', description => '4. test network', rootName => '_apiTestRoot', state => 'FREE'}, [['delRoot', ['_apiTestRoot']]] ); &test('delNet', ['_apiTestRoot', '192.168.0.100/32'], 0, [['delRoot', ['_apiTestRoot']]] ); &test('search', ['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], {network => '192.168.0.100/32', description => '30. test network', rootName => '_apiTestRoot', state => 'FREE'}, [['delRoot', ['_apiTestRoot']]], 1 ); &test('addNet', ['_apiTestRoot', '192.168.0.0/28', '6. test network', 'IN USE'], 0, [['delRoot', ['_apiTestRoot']]] ); &test('delNet', ['_apiTestRoot', '192.168.0.0/28', 0, 1], 0, [['delRoot', ['_apiTestRoot']]] ); &test('search', ['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], {network => '192.168.0.10/32', description => '4. test network', rootName => '_apiTestRoot', state => 'FREE'}, [['delRoot', ['_apiTestRoot']]], 1 ); &test('addNet', ['_apiTestRoot', '192.168.0.4/30', '7. test network'], 0, [['delRoot', ['_apiTestRoot']]] ); &test('assignFreeSubnet', ['_apiTestRoot', '192.168.0.4/30', 32, 'assigned net'], {network => '192.168.0.5/32', description => 'assigned net', state => 'UNSPECIFIED'}, [['delRoot', ['_apiTestRoot']]] ); &test('getFreeSubnets', ['_apiTestRoot', '192.168.0.4/30', 32, 2], ['192.168.0.6/32'], [['delRoot', ['_apiTestRoot']]] ); &test('getSubnets', ['_apiTestRoot', '192.168.0.4/30'], {network => '192.168.0.5/32', description => 'assigned net', state => 'UNSPECIFIED'}, [['delRoot', ['_apiTestRoot']]] ); for (1..50) { &test('assignFreeSubnet', ['_apiTestRoot', '192.168.0.0/24', 32, 'assigned net ' . $_], {description => 'assigned net ' . $_, state => 'UNSPECIFIED'}, [['delRoot', ['_apiTestRoot']]] ); if ($_ == 25) { &test('delNet', ['_apiTestRoot', '192.168.0.25/32'], 0, [['delRoot', ['_apiTestRoot']]] ); } } &test('getSubnets', ['_apiTestRoot', '192.168.0.0/24'], {network => '192.168.0.51/32', description => 'assigned net 50', state => 'UNSPECIFIED'}, [['delRoot', ['_apiTestRoot']]] ); &test('getSubnets', ['_apiTestRoot', '192.168.0.0/24'], {network => '192.168.0.25/32', description => 'assigned net 26', state => 'UNSPECIFIED'}, [['delRoot', ['_apiTestRoot']]] ); &test('delRoot', ['_apiTestRoot'], 0 ); } else { &test('addRoot', ['_apiTestRootv6', 1, 1], 0 ); &test('listRoots', [], {name => '_apiTestRootv6', ipv6 => 1}, [['delRoot', ['_apiTestRootv6']]] ); &test('addNet', ['_apiTestRootv6', 'dead:beaf::/64', '1. test network', 'IN USE', 120], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('addNet', ['_apiTestRootv6', 'dead:beaf::100/120', '2. test network', 'IN USE', 128], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('addNet', ['_apiTestRootv6', 'dead:beaf::102/128', '3. test network', 'IN USE'], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('addNet', ['_apiTestRootv6', 'dead:beaf::1000/128', '30. test network', 'IN USE'], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('getFreeSubnets', ['_apiTestRootv6', 'dead:beaf::/64', 120, 2], ['dead:beaf:0000:0000:0000:0000:0000:0000/120', 'dead:beaf:0000:0000:0000:0000:0000:0200/120'], [['delRoot', ['_apiTestRootv6']]] ); &test('getFreeSubnetsFromSearch', ['1. test network', '', '', '', '', '_apiTestRootv6', 124, 1], {network => 'dead:beaf:0000:0000:0000:0000:0000:0000/124', rootName => '_apiTestRootv6'}, [['delRoot', ['_apiTestRootv6']]] ); &test('getFreeSubnets', ['_apiTestRootv6', 'dead:beaf::100/120', 128, 2], ['dead:beaf:0000:0000:0000:0000:0000:0100/128', 'dead:beaf:0000:0000:0000:0000:0000:0101/128'], [['delRoot', ['_apiTestRootv6']]] ); &test('search', ['dead:beaf:0:0:0:0:0:0/64', '', 1, '', '', '_apiTestRootv6'], {network => 'dead:beaf::/64', description => '1. test network', rootName => '_apiTestRootv6', state => 'IN USE'}, [['delRoot', ['_apiTestRootv6']]] ); &test('search', ['1. test network', '', 1, '', '', '_apiTestRootv6'], {network => 'dead:beaf::/64', description => '1. test network', rootName => '_apiTestRootv6', state => 'IN USE'}, [['delRoot', ['_apiTestRootv6']]] ); &test('editNet', ['_apiTestRootv6', 'dead:beaf:0000:0000:0000:0000:0000:0102/128', {description => '4. test network', state => 'FREE'}], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('search', ['4. test network', 'FREE', 1, '', '', '_apiTestRootv6'], {network => 'dead:beaf::102/128', description => '4. test network', rootName => '_apiTestRootv6', state => 'FREE'}, [['delRoot', ['_apiTestRootv6']]] ); &test('delNet', ['_apiTestRootv6', 'dead:beaf:0000:0000:0000:0000:0000:0102/128'], 0, [['delRoot', ['_apiTestRootv6']]] ); &test('search', ['4. test network', 'FREE', 1, '', '', '_apiTestRootv6'], {network => 'dead:beaf::102/128', description => '30. test network', rootName => '_apiTestRootv6', state => 'FREE'}, [['delRoot', ['_apiTestRootv6']]], 1 ); &test('delRoot', ['_apiTestRootv6'], 0 ); } print "Successfully passed all tests\n"; exit 0; #------------------------------------ sub test { my $method = shift; my $args = shift; my $expectedResult = shift; my $rollBackMethods = shift; my $notExpect = shift || 0; my $errorStr = "Error while testing '$method': "; my $result = undef; eval { $result = $api->call($method, $session, @{$args}); }; &rollBack($errorStr . $@, $rollBackMethods) if $@; return unless defined $expectedResult; my $bOK = 0; if (ref($expectedResult) eq 'HASH') { if (ref($result) eq 'ARRAY') { foreach (@{$result}) { my $resultItem = $_; my $nrOfOK = 0; if (ref($resultItem) eq 'HASH') { foreach (keys %{$expectedResult}) { my $key = $_; my $value = $expectedResult->{$key}; $nrOfOK++ if exists $resultItem->{$key} && $value eq $resultItem->{$key}; if ($nrOfOK == scalar keys %{$expectedResult}) { $bOK = 1; last; } } last if $bOK; } } $bOK = abs($bOK - 1) if $notExpect; unless ($bOK) { my $error = $errorStr . (($notExpect) ? 'Not expected result found' : 'Expected result cannot be found'); $error .= Dumper($result) if $debug; &rollBack($error, $rollBackMethods); } else { print "Successfully tested '$method'\n"; } } elsif (ref($result) eq 'HASH') { my $nrOfOK = 0; foreach (keys %{$expectedResult}) { my $key = $_; my $value = $expectedResult->{$key}; $nrOfOK++ if exists $result->{$key} && $value eq $result->{$key}; if ($nrOfOK == scalar keys %{$expectedResult}) { $bOK = 1; last; } } unless ($bOK) { my $error = $errorStr . (($notExpect) ? 'Not expected result found' : 'Expected result cannot be found'); $error .= Dumper($result) if $debug; &rollBack($error, $rollBackMethods); } else { print "Successfully tested '$method'\n"; } } else { &rollBack($errorStr . 'Result is not an array nor a hash: ' . $result, $rollBackMethods); } } elsif (ref($expectedResult) eq 'ARRAY') { if (ref($result) eq 'ARRAY') { my $nrOfOK = 0; foreach (@{$expectedResult}) { my $expectedResultItem = $_; foreach (@{$result}) { my $resultItem = $_; $nrOfOK++ if $resultItem eq $expectedResultItem; if ($nrOfOK == scalar @{$expectedResult}) { $bOK = 1; last; } } last if $bOK; } } unless ($bOK) { my $error = $errorStr . 'Expected result cannot be found'; $error .= Dumper($result) if $debug; &rollBack($error, $rollBackMethods); } else { print "Successfully tested '$method'\n"; } } elsif ($result ne $expectedResult) { &rollBack($errorStr . $result, $rollBackMethods); } else { print "Successfully tested '$method'\n"; } } sub rollBack { my $result = shift; my $rollBackMethods = shift; warn Dumper($result); if (defined $rollBackMethods) { print "Rollbacking with " . join(', ', map {${$_}[0]} @{$rollBackMethods}) . "\n"; foreach (@{$rollBackMethods}) { my $meth = $$_[0]; my $args = $$_[1]; &test($meth, $args); } } exit 1; } # vim:ts=2:sts=2:sw=2 HaCi/cgi-bin/0000755000175000000000000000000012475466174012372 5ustar fighterrootHaCi/cgi-bin/HaCi.cgi0000755000175000000000000000520012470146636013653 0ustar fighterroot#!/usr/bin/perl -w ############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2015 by Lars Wildemann # ## Author: Lars Wildemann # ## # ## HaCi is an IP Address / Network Administration Tool with IPv6 Support. # ## It stores its data efficiently in a relational Database and uses a # ## treelike Strukture to illustrate supernets and subnets. # ## # ## This program is free software; you can redistribute it and#or modify # ## it under the terms of the GNU General Public License as published by # ## the Free Software Foundation; either version 2 of the License, or # ## (at your option) any later version. # ## # ## This program 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 General Public License for more details. # ############################################################################### use strict; use Carp (); local $SIG{__WARN__} = \&Carp::cluck if 0; $| = 1; sub getWorkDir { my $currDir = `pwd`; chomp($currDir); my $scriptFile = $0; $scriptFile = $ENV{SCRIPT_FILENAME} if exists $ENV{SCRIPT_FILENAME}; (my $scriptDir = $scriptFile) =~ s/\/[^\/]+$//; my $destDir = ($scriptDir =~ /^\//) ? $scriptDir : $currDir . '/' . $scriptDir; chdir "$destDir/../" or die "Cannot change into workdir '$destDir/../': $!\n"; my $workDir = `pwd`; chomp($workDir); return $workDir; } BEGIN { my $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; } # Polluting Environment for Config::General $ENV{script_name} = $ENV{SCRIPT_NAME}; my $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; use HaCi::HaCi; use HaCi::Conf; my $startTime = time; &HaCi::Conf::init($workDir); &HaCi::HaCi::run(); my $endTime = time; warn "Processtime (" , localtime($startTime) . " - ". localtime($endTime) . "): " . ($endTime - $startTime) . "\n" if 0; warn "Processtime : " . ($endTime - $startTime) . "\n" if 0; exit 0; # vim:ts=2:sw=2:sws=2 HaCi/cgi-bin/HaCiAPI.cgi0000755000175000000000000000622012470146636014210 0ustar fighterroot#!/usr/bin/perl ############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2015 by Lars Wildemann # ## Author: Lars Wildemann # ## # ## HaCi is an IP Address / Network Administration Tool with IPv6 Support. # ## It stores its data efficiently in a relational Database and uses a # ## treelike Strukture to illustrate supernets and subnets. # ## # ## This program is free software; you can redistribute it and#or modify # ## it under the terms of the GNU General Public License as published by # ## the Free Software Foundation; either version 2 of the License, or # ## (at your option) any later version. # ## # ## This program 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 General Public License for more details. # ############################################################################### sub getWorkDir { my $currDir = `pwd`; chomp($currDir); my $scriptFile = $0; $scriptFile = $ENV{SCRIPT_FILENAME} if exists $ENV{SCRIPT_FILENAME}; (my $scriptDir = $scriptFile) =~ s/\/[^\/]+$//; my $destDir = ($scriptDir =~ /^\//) ? $scriptDir : $currDir . '/' . $scriptDir; chdir "$destDir/../" or die "Cannot change into workdir '$destDir/../': $!\n"; my $workDir = `pwd`; chomp($workDir); return $workDir; } BEGIN { $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; } use strict; use warnings; use Carp (); local $SIG{__WARN__} = \&Carp::cluck if 0; $| = 1; use vars qw/$workDir/; use SOAP::Transport::HTTP; use HaCi::HaCiAPI; map { warn " $_ => $ENV{$_}\n"; } keys %ENV if 0; $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; my $query = $ENV{'QUERY_STRING'}; my $startTime = time; warn "Start: " . localtime() . "\n"; if (defined $query && $query =~ /getWSDL/) { &prWSDL(); } else { SOAP::Transport::HTTP::CGI -> dispatch_to('HaCi::HaCiAPI') -> handle; } warn "End: " . localtime() . "\n"; my $endTime = time; warn "Running: " . ($endTime - $startTime) . "s\n"; #-------------------------------- sub prWSDL { eval { require Pod::WSDL; }; if ($@) { die "Cannot print WSDL: $@\n"; } use CGI; my $q = new CGI; my $pod = new Pod::WSDL( source => 'HaCi::HaCiAPI', location => ((($ENV{'HTTPS'} && $ENV{'HTTPS'} !~ /off/i) ? 'https' : 'http') . '://' . $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'}), pretty => 1, withDocumentation => 1 ); print $q->header(); print $pod->WSDL; } exit 0; # vim:ts=2:sw=2:sws=2 HaCi/cgi-bin/test.cgi0000755000175000000000000000671312475466131014040 0ustar fighterroot#!/usr/bin/perl use strict; use warnings; use CGI; use Data::Dumper; sub getWorkDir { my $currDir = `pwd`; chomp($currDir); my $scriptFile = $0; $scriptFile = $ENV{SCRIPT_FILENAME} if exists $ENV{SCRIPT_FILENAME}; (my $scriptDir = $scriptFile) =~ s/\/[^\/]+$//; my $destDir = ($scriptDir =~ /^\//) ? $scriptDir : $currDir . '/' . $scriptDir; chdir "$destDir/../" or die "Cannot change into workdir '$destDir/../': $!\n"; my $workDir = `pwd`; chomp($workDir); return $workDir; } BEGIN { my $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; } $| = 1; my $workDir = &getWorkDir(); $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; our $conf; *conf = \$HaCi::Conf::conf; eval { require HaCi::Conf; &HaCi::Conf::init($workDir); }; my $q = new CGI(@_); my $title = 'HaCi - IP Address Administration'; my $ver = $conf->{static}->{gui}->{version} || '[unknown]'; my @mands = qw/CGI CGI::Ajax CGI::Carp CGI::Cookie CGI::Session Class::Accessor Class::MakeMethods Config::General DBD::mysql Digest::MD5 Digest::SHA Encode Encode::Guess File::Temp HTML::Entities Locale::gettext Log::LogLite Math::Base85 Math::BigInt%1.87 Net::CIDR Net::IMAP::Simple Net::IPv6Addr Net::SNMP Storable Template Time::Local Text::CSV/; my @opts = qw/Cache::FastMmap Cache::FileCache DNS::ZoneParse IO::Socket::INET6 Math::BigInt::GMP Net::DNS Net::Nslookup Net::Ping Pod::WSDL SOAP::Transport::HTTP SQL::Translator%0.09000 SQL::Translator::Diff Text::CSV_XS Apache::DBI DBD::Pg DBD::mysql Frontier::RPC2 File::Basename Time::HiRes JSON URI::Query Text::CSV_XS/; print $q->header(-charset=>'UTF-8'); print $q->start_html({ title => $title }); print $q->h1($title); print $q->h2("Version: " . $ver . (($ENV{MOD_PERL}) ? ' (running under mod-perl)' : '')); print $q->h6($ENV{SERVER_SOFTWARE}); eval { require HaCi::Utils; &HaCi::Utils::getConfig(); my $dbError = &checkDB(); my $dbHost = $conf->{user}->{db}->{dbhost} || ''; my $dbName = $conf->{user}->{db}->{dbname} || ''; my $dbUser = $conf->{user}->{db}->{dbuser} || ''; print $q->br, $q->h3("Database connection ($dbUser\@$dbHost:$dbName): $dbError"); }; print $q->br, $q->h5("Canot test database connection:
$@
"); print $q->br, $q->h3("Mandatory Modules:"); print $q->start_table({cellpadding=>3, rules=>'all'}); foreach (sort @mands) { s/%/ /g; eval "use $_"; warn $@ if $@; print $q->Tr($q->th({align=>'left'}, $_), $q->td({bgcolor=>(($@) ? '#FFAAAA' : '#AAFFAA')}, (($@) ? 'NOT' : '') . ' available')); } print $q->end_table; print $q->start_table({cellpadding=>3, rules=>'all'}); print $q->br, $q->h3("Recommended Modules:"); foreach (sort @opts) { s/%/ /g; eval "use $_"; warn $@ if $@; print $q->Tr($q->th({align=>'left'}, $_), $q->td({bgcolor=>(($@) ? '#FFAAAA' : '#AAFFAA')}, (($@) ? 'NOT' : '') . ' available')); } exit 0; #----------------------------------- sub checkDB { my $dbType = &HaCi::Utils::getConfigValue('db', 'dbtype'); eval { require "HaCi/Tables/$dbType/network.pm"; }; return "Error while loading Table 'network': $@\n" if $@; $DBIEasy::lastError = ''; my $tableRef = "HaCi::Tables::${dbType}::network"->new($conf->{user}->{db}); return $DBIEasy::lastError if $DBIEasy::lastError; my $dbh = $tableRef->dbh(); if (ref($dbh) && $dbh->can('ping') && $dbh->ping()) { return 'OK'; } else { return $dbh->errstr; } return 'OK'; } # vim:ts=4:sw=4 HaCi/docs/0000755000175000000000000000000012475457053012006 5ustar fighterrootHaCi/docs/HaCiAPI.txt0000644000175000000000000001013512451674756013712 0ustar fighterroot__HaCi_SOAP_API__ Available methods and their parameters, return values and a short description search : _IN username $string Username _IN password $string Password _IN searchString $string Search String _IN state $string (optional) (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN exact $boolean (optional) Search for the Exact search String? _IN fuzzy $boolean (optional) Fuzzy search? _IN template $string (optional) isolate your Search by defining a Template _IN templateQueries $string (optional) Define special Queries for the specified Template. spererated by semicolon. E.g.: value1=foo;value2=bar _IN root $string (optional) Define special root to search in _IN tags $string (optional) return only networks with this tags, seperate with spaces (i.e.: 'OR foo bar', 'AND foo bar') _RETURN @HaCi::SOAP::Type::network _DOC This is a search function getFreeSubnets : _IN username $string Username _IN password $string Password _IN root $string Root Name _IN supernet $string Supernet (e.g. 192.168.0.0/24) _IN size $int Subnet CIDR (e.g. 29) _IN amount $int (optional) Amount of returned Networks (e.g. 1) _RETURN @HaCi::SOAP::Type::network _DOC This Service returns all free Subnets of a certain size from a given Supernet getFreeSubnetsFromSearch : _IN username $string Username _IN password $string Password _IN searchString $string Search String _IN state $string (optional) (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN exact $boolean (optional) Search for the Exact search String? _IN fuzzy $boolean (optional) Fuzzy search? _IN template $string (optional) isolate your Search by defining a Template _IN templateQueries $string (optional) Define special Queries for the specified Template. spererated by semicolon. E.g.: value1=foo;value2=bar _IN size $int Subnet CIDR (e.g. 29) _IN amount $int (optional) Amount of returned Networks (e.g. 1) _RETURN @HaCi::SOAP::Type::network _DOC This Service returns all free Subnets of a certain size from networks, that fit the search criteria addNet : _IN username $string Username _IN password $string Password _IN rootName $string Name of Root _IN ipaddress $string IP Address (E.g.: 192.168.0.1) _IN cidr $int CIDR (E.g.: 24) _IN description $string (optional) Description of the Network _IN state $string (optional) The State of the Network (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN defSubnetSize $int (optional) Default CIDR for Subnets _IN templateName $string (optional) Template, which should be linked to the Network _IN tags $string (optional) Tags (seperate by comma) _RETURN $string Prints if addition has failed or was successfull _DOC This function adds a Network delNet : _IN username $string Username _IN password $string Password _IN root $string Root Name _IN network $string Network (e.g. 192.168.0.0/24) _RETURN $string Prints if deletion has failed or was successfull _DOC This function deletes a Network HaCi/docs/HaCiRESTWrapper.txt0000644000175000000000000000157212470146636015414 0ustar fighterrootREST wrapper for HaCi XML-RPC-API The methods and parameters are equal to the XML-RPC-API. The parameters have to be URL encoded (i.e: '/' => '%2f') Authentication: * via HTTP basic authentication * via username and password parameter USAGE: * http(s):///RESTWrapper/[API-METHOD]?username=&password=&[API-PARAMETER] * http(s)://@@/RESTWrapper/[API-METHOD]?[API-PARAMETER] Examples: * http(s)://foo:bar@/RESTWrapper/search?search=test?rootName=bigRoot * http(s):///RESTWrapper/getFreeSubnets?username=foo&password=bar&rootName=bigRoot&supernet=192.168.0.0%2f24&cidr=32&amount=3 $ curl 'http://demo.haci.larsux.de/RESTWrapper/getFreeSubnets?username=admin&password=admin&rootName=testRoot&supernet=192.168.0.0%2f24&cidr=32&amount=3' ["192.168.0.1/32","192.168.0.2/32","192.168.0.3/32"] Return: JSON-Object HaCi/docs/HaCiSoapClient.pl0000755000175000000000000000234712377724507015144 0ustar fighterroot#!/usr/bin/perl # Demo script using all available functions of the HaCi API use strict; use warnings; use Data::Dumper; use SOAP::Lite on_fault => sub { my($soap, $res) = @_; die (defined $res && ref $res ? $res->faultstring : $soap->transport->status), "\n"; }; my $haciApiUrl = 'http://hacidev.larsux.de/cgi-bin/HaCiAPI.cgi?getWSDL'; #my $user = 'admin'; #my $pass = 'admin'; my $user = 'test'; my $pass = '1'; my $a = $ARGV[0] || 0; my $soap = SOAP::Lite->service($haciApiUrl); die "Cannot initiate Soap" unless defined $soap; if ($a == 1) { print Dumper($soap->search($user, $pass, 'test')); } elsif ($a == 2) { print Dumper($soap->getFreeSubnets($user, $pass, 'testRoot', '192.168.0.0/24', 29, 10)); } elsif ($a == 3) { print Dumper($soap->getFreeSubnetsFromSearch($user, $pass, 'HaCiAPI', 0, 0, 0, 'Pool', 'Pool-Typ=DSL', 29, 10)); } elsif ($a == 4) { print Dumper($soap->addNet($user, $pass, 'testRoot', '192.168.0.100', 32, 'HaCiAPI Test Network', 'ASSIGNED PI')); } elsif ($a == 5) { print Dumper($soap->delNet($user, $pass, 'testRoot', '192.168.0.100/32')); } else { print "USAGE $0 Number Options: Number: 1: search 2: getFreeSubnets 3: getFreeSubnetsFromSearch 4: addNet 5: delNet "; exit 0; } exit 0; HaCi/docs/HaCiXMLRPC-API.txt0000644000175000000000000002460412470236743014712 0ustar fighterrootClass XMLRPC This class provides a XML-RPC API for HaCi. Basic usage: my $api = new Frontier::Client(url => "http://$server/RPC2"); my $session = $api->call('login', [$user, $pass]); die 'Login failed!' unless $session; print Dumper($api->call($method, $session, @args)); $api->call('logout', $session); exit 0; login() Login and return your session Params Returns - session [string] session token you can use for authentication logout() Logout Params Returns search() Search networks Params - search [string] string to search for - state [string] filter the result by that network state (ALLOCATED PA, ASSIGNED, ...) - exact [string] don't do a substring search - template name [string] filter the result by that template name - template query [hash] filter the result by defining special values for template entries (i.e.: {name => 'POOL1'}) - root name [string] limit the search in that root - nrOfFreeSubs [boolean] compute the number of free subnets in each found network - withDetails [boolean] show details for found networks - tags [string] return only networks with this tags, seperate with spaces (i.e.: 'OR foo bar', 'AND foo bar') Returns - networks [array] array of found network blocks (hash) - netID - network - rootName - description - state - nrOfFreeSubs Example my $networks = search('Test', 'ASSIGNED', 0, 'DSL-POOL', {name => 'Pool1'}, 'DSL-Test-Pool-Root', 0); getFreeSubnets() Find free subnets in a specified root and supernet and return the wanted amount of subnets Params - root name [string] search free subnets in that root - supernet [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120) - cidr [integer] specify the target cidr of your subnets (e.g. 30) - amount [integer] how many subnets you want to get Returns - networks [array] array of free subnets found (hash) - network Example my $freeSubnets = getFreeSubnets('Test root', '10.184.120.0/23', 32, 1); getFreeSubnetsFromSearch() Search networks and return free subnets from them Params - search [string] string to search for - state [string] filter the result by that network state (ALLOCATED PA, ASSIGNED, ...) - exact [string] don't do a substring search - template name [string] filter the result by that template name - template query [hash] filter the result by defining special values for template entries (i.e.: {name => 'POOL1'}) - root name [string] limit the search in that root - cidr [integer] specify the target cidr of your subnets (e.g. 30) - amount [integer] how many subnets you want to get Returns - networks [array] array of free subnets found (hash) - network - root name Example my $freeSubnets = getFreeSubnetsFromSearch('search me', '', 1, '', {}, 'Test-Root', 29, 3); listRoots() Add a root to HaCi Params Returns - roots [array] array of found roots (hash) - ID - name - ipv6 (boolean) addRoot() Add a root to HaCi Params - root name [string] add a root with this name - description [string] description of the root - ipv6 [boolean] this root contains IPv6 networks Returns - success [string] 0 on success, error-String at error editRoot() Edit an existing root Params - root name [string] edit this root - new root name [string] change root name - description [string] new description of the root Returns - success [string] 0 on success, error-String at error delRoot() Delete a root from HaCi Params - root name [string] remove the root Returns - success [string] 0 on success, error-String at error addNet() Add a network to HaCi Params - root name [string] add the network to this root - network [network] add this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - description [string] description of the network - state [string] state of the network (e.g. ALLOCATED PA, ASSIGNED, ...) - def subnet Size [integer] define a default subnet cidr size (e.g. 30) - template name [string] assign a template to the network - template values [hash] pass template values (e.g. {hostname => 'abc.de', os => 'redhat'}) - tags [string] add tags (seperate by comma) Returns - success [string] 0 on success, error-String at error editNet() Edit an existing network Params - root name [string] edit the network in this root - network [network] edit this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - changes [hash] changes (valid attributes are: network description state rootName defSubnetSize tmplName tmplValues) e.g.: {description => 'test_8'} {network => '192.168.0.8/29', defSubnetSize=>30, state=>'FREE', rootName=>'larsux.de'} Returns - success [string] 0 on success, error-String at error delNet() Delete a network from HaCi Params - root name [string] remove the netork from this root - network [network] remove this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - network lock [integer] lock network for X seconds - withSubnets [boolean] also remve subnets Returns - success [string] 0 on success, error-String at error assignFreeSubnet() Find the next free subnet and assign it in one step Params - root name [string] search free subnets in that root - supernet [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120) - cidr [integer] specify the target cidr of your subnets (e.g. 30) - description [string] description of the network - state [string] state of the network (e.g. ALLOCATED PA, ASSIGNED, ...) - def subnet Size [integer] define a default subnet cidr size (e.g. 30) - template name [string] assign a template to the network - template values [hash] pass template values (e.g. {hostname => 'abc.de', os => 'redhat'}) Returns - network details [hash] new network assigned: - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from getNetworkDetails() Get details from a network in HaCi Params - root name [string] get details of a network in this root - network [network] get details of this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) Returns - network [hash] network details: - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from getSubnets() Get subnets from a root and optional from a supernet Params - root name [string] get subnets from this root - supernet [network] get subnets from this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) Returns - networks [array] array of found networks (hash) - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from Example my $subnets = getSubnets('Test'); my $subnets = getSubnets('Test', '192.168.0.0/24'); HaCi/docs/HaCiXMLRPCClient.pl0000755000175000000000000000470612470236743015242 0ustar fighterroot#!/usr/bin/perl # Demo script using all available functions of the HaCi XMPRPC-API use strict; use warnings; use Data::Dumper; use Frontier::Client; #my $server = 'demo.haci.larsux.de'; my $server = 'hacidev.larsux.de'; my $user = 'admin'; my $pass = 'admin'; my $a = $ARGV[0] || 0; my $api = new Frontier::Client(url => "http://$server/RPC2"); my $session = $api->call('login', [$user, $pass]); die 'Login failed!' unless $session; $a = 999 if $a && $a !~ /^\d+$/; if (!$a) { print "USAGE $0 Number Options: Number: 1: search 2: getFreeSubnets 3: getFreeSubnetsFromSearch 4: listRoots 5: addRoot 6: editRoot 7: delRoot 8: addNet 9: editNet 10: delNet 11: assignFreeSubnet 12: getNetworkDetails 13: getSubnets "; exit 0; } elsif ($a == 1) { print Dumper($api->call('search', $session, 'test', 0, 0, 'Pool', {'Pool-Typ' => 'DSL'}, 'testRoot', 1, 1)); } elsif ($a == 2) { print Dumper($api->call('getFreeSubnets', $session, 'testRoot', '192.168.0.0/24', 29, 10)); } elsif ($a == 3) { print Dumper($api->call('getFreeSubnetsFromSearch', $session, 'HaCiAPI', 0, 0, 'Pool', {'Pool-Typ' => 'DSL'}, 'testRoot', 29, 10)); } elsif ($a == 4) { print Dumper($api->call('listRoots', $session)); } elsif ($a == 5) { print Dumper($api->call('addRoot', $session, 'testRoot', 'Test Root', 0)); } elsif ($a == 6) { print Dumper($api->call('editRoot', $session, 'testRoot', 'Testroot', 'tEST rOOT')); } elsif ($a == 7) { print Dumper($api->call('delRoot', $session, 'Testroot')); } elsif ($a == 8) { print Dumper($api->call('addNet', $session, 'testRoot', '192.168.0.128/25', 'HaCiAPI Test Network', 'ASSIGNED PI', 29, 'Pool', {'Pool-Typ' => 'DSL', Name => 'TEST'})); } elsif ($a == 9) { print Dumper($api->call('editNet', $session, 'testRoot', '192.168.0.128/25', {description => 'HaCiAPI Test Network modified', state => 'FREE', defSubnetSize => 27})); } elsif ($a == 10) { print Dumper($api->call('delNet', $session, 'testRoot', '192.168.0.128/25')); } elsif ($a == 11) { print Dumper($api->call('assignFreeSubnet', $session, 'testRoot', '192.168.0.128/25', 29, 'test29er under testRoot', '', 32, 'Pool', {'Pool-Typ' => 'DSL', Name => 'TEST'})); } elsif ($a == 12) { print Dumper($api->call('getNetworkDetails', $session, 'testRoot', '192.168.0.128/25')); } elsif ($a == 13) { print Dumper($api->call('getSubnets', $session, 'testRoot', '192.168.0.128/25')); } else { my $method = shift @ARGV; print Dumper($api->call($method, $session, @ARGV)); } exit 0; HaCi/docs/HaCi_v0.98-v0.98b.pgsql0000644000175000000000000000371612475447270015461 0ustar fighterroot ALTER TABLE template_entry ALTER COLUMN entries TYPE character varying(1023) /* TYPE change - table: template_entry original: character varying(255) new: character varying(1023) */; CREATE SEQUENCE audit_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.audit_id_seq OWNER TO "HaCi"; CREATE TABLE audit ( id integer DEFAULT nextval('audit_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, access_groups character varying(255) DEFAULT ''::character varying NOT NULL, action character varying(255) DEFAULT ''::character varying NOT NULL, object character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(1024) DEFAULT ''::character varying NOT NULL, error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE audit ADD CONSTRAINT audit_pkey PRIMARY KEY (id); ALTER TABLE public."audit" OWNER TO "HaCi"; CREATE SEQUENCE network_lock_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_lock_id_seq OWNER TO "HaCi"; CREATE TABLE network_lock ( id integer DEFAULT nextval('network_lock_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, duration integer DEFAULT 0 NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT (0)::smallint NOT NULL, ipv6 smallint DEFAULT (0)::smallint NOT NULL ); ALTER TABLE public.network_lock OWNER TO "HaCi"; ALTER TABLE ONLY network_lock ADD CONSTRAINT network_lock_pkey PRIMARY KEY (id); CREATE UNIQUE INDEX network_lock_uniq_network ON network_lock USING btree (root_id, network_prefix, host_part, cidr, ipv6); HaCi/docs/HaCi_v0.98b.mysql0000644000175000000000000002575512475447270014725 0ustar fighterroot-- -- Table structure for table `audit` -- DROP TABLE IF EXISTS `audit`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `audit` ( `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `username` varchar(255) NOT NULL default '', `accessGroups` varchar(255) NOT NULL default '', `action` varchar(255) NOT NULL default '', `object` varchar(255) NOT NULL default '', `value` text NOT NULL, `error` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=676 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Table structure for table `network` -- DROP TABLE IF EXISTS `network`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `network` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `network` bigint(40) NOT NULL default '0', `description` varchar(255) NOT NULL default '', `state` smallint(6) NOT NULL default '0', `defSubnetSize` tinyint(4) unsigned NOT NULL default '0', `tmplID` int(11) NOT NULL default '0', `ipv6ID` varbinary(22) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`ipv6ID`,`network`), KEY `network` (`network`) ) ENGINE=InnoDB AUTO_INCREMENT=35184 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkAC` -- DROP TABLE IF EXISTS `networkAC`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkAC` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `network` bigint(40) NOT NULL default '0', `netID` int(11) NOT NULL default '0', `groupID` int(11) NOT NULL default '0', `ACL` int(11) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`groupID`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkPlugin` -- DROP TABLE IF EXISTS `networkPlugin`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkPlugin` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL default '0', `pluginID` int(11) NOT NULL default '0', `sequence` int(11) NOT NULL default '0', `newLine` tinyint(4) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`) ) ENGINE=InnoDB AUTO_INCREMENT=83 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkV6` -- DROP TABLE IF EXISTS `networkV6`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkV6` ( `ID` varbinary(22) NOT NULL default '', `rootID` int(11) NOT NULL default '0', `networkPrefix` bigint(20) unsigned NOT NULL default '0', `hostPart` bigint(20) unsigned NOT NULL default '0', `cidr` smallint(6) NOT NULL default '0', PRIMARY KEY (`ID`,`rootID`), UNIQUE KEY `rootID` (`rootID`,`networkPrefix`,`hostPart`,`cidr`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `plugin` -- DROP TABLE IF EXISTS `plugin`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `plugin` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `filename` varchar(255) NOT NULL default '', `active` tinyint(4) NOT NULL default '0', `lastRun` datetime NOT NULL default '0000-00-00 00:00:00', `runTime` int(11) NOT NULL default '0', `lastError` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `pluginConf` -- DROP TABLE IF EXISTS `pluginConf`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `pluginConf` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL, `pluginID` int(11) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`,`name`) ) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `pluginValue` -- DROP TABLE IF EXISTS `pluginValue`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `pluginValue` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL, `pluginID` int(11) NOT NULL, `origin` int(11) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`,`name`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `root` -- DROP TABLE IF EXISTS `root`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `root` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `ipv6` tinyint(1) NOT NULL default '0', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=98 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `rootAC` -- DROP TABLE IF EXISTS `rootAC`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `rootAC` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `groupID` int(11) NOT NULL default '0', `ACL` int(11) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`groupID`) ) ENGINE=InnoDB AUTO_INCREMENT=139 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `setting` -- DROP TABLE IF EXISTS `setting`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `setting` ( `ID` int(11) NOT NULL auto_increment, `userID` int(11) NOT NULL default '0', `param` varchar(255) NOT NULL default '', `value` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `userID` (`userID`,`param`,`value`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `squad` -- DROP TABLE IF EXISTS `squad`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `squad` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `permissions` int(11) NOT NULL default '0', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `template` -- DROP TABLE IF EXISTS `template`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `template` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `type` varchar(64) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `templateEntry` -- DROP TABLE IF EXISTS `templateEntry`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `templateEntry` ( `ID` int(11) NOT NULL auto_increment, `tmplID` int(11) NOT NULL default '0', `type` int(11) NOT NULL default '0', `position` int(11) NOT NULL default '0', `description` varchar(255) NOT NULL default '', `size` int(11) NOT NULL default '1', `entries` text NOT NULL, `rows` int(11) NOT NULL default '1', `cols` int(11) NOT NULL default '1', PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `templateValue` -- DROP TABLE IF EXISTS `templateValue`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `templateValue` ( `ID` int(11) NOT NULL auto_increment, `tmplID` int(11) NOT NULL default '0', `tmplEntryID` int(11) NOT NULL default '0', `netID` int(11) NOT NULL default '0', `value` blob NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`tmplID`,`tmplEntryID`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `user` -- DROP TABLE IF EXISTS `user`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `user` ( `ID` int(11) NOT NULL auto_increment, `username` varchar(100) NOT NULL default '', `password` varchar(255) NOT NULL default '', `groupIDs` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkLock` -- DROP TABLE IF EXISTS `networkLock`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkLock` ( `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `duration` int(11) NOT NULL default '0', `rootID` int(11) NOT NULL default '0', `networkPrefix` bigint(20) unsigned NOT NULL default '0', `hostPart` bigint(20) unsigned NOT NULL default '0', `cidr` smallint(6) NOT NULL default '0', `ipv6` smallint(6) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`networkPrefix`,`hostPart`,`cidr`,`ipv6`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; HaCi/docs/HaCi_v0.98c.mysql0000644000175000000000000003215112475447270014712 0ustar fighterroot-- MySQL dump 10.11 -- -- Host: localhost Database: HaCi -- ------------------------------------------------------ -- Server version 5.0.51a-24+lenny5 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `CIDRmagic` -- DROP TABLE IF EXISTS `CIDRmagic`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `CIDRmagic` ( `cidr` int(11) NOT NULL, `adder` int(10) unsigned NOT NULL, PRIMARY KEY (`cidr`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `audit` -- DROP TABLE IF EXISTS `audit`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `audit` ( `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `username` varchar(255) NOT NULL default '', `accessGroups` varchar(255) NOT NULL default '', `action` varchar(255) NOT NULL default '', `object` varchar(255) NOT NULL default '', `value` text NOT NULL, `error` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=1920 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; -- -- Table structure for table `network` -- DROP TABLE IF EXISTS `network`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `network` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `network` bigint(20) unsigned NOT NULL default '0', `description` varchar(255) NOT NULL default '', `state` smallint(6) NOT NULL default '0', `defSubnetSize` tinyint(4) unsigned NOT NULL default '0', `tmplID` int(11) NOT NULL default '0', `ipv6ID` varbinary(22) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', `searchStr` varchar(255) default '', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`ipv6ID`,`network`), KEY `network` (`network`), KEY `description` (`description`), KEY `searchStr` (`searchStr`) ) ENGINE=InnoDB AUTO_INCREMENT=36230 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkAC` -- DROP TABLE IF EXISTS `networkAC`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkAC` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `network` bigint(40) NOT NULL default '0', `netID` int(11) NOT NULL default '0', `groupID` int(11) NOT NULL default '0', `ACL` int(11) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`groupID`) ) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkLock` -- DROP TABLE IF EXISTS `networkLock`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkLock` ( `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `duration` int(11) NOT NULL default '0', `rootID` int(11) NOT NULL default '0', `networkPrefix` bigint(20) unsigned NOT NULL default '0', `hostPart` bigint(20) unsigned NOT NULL default '0', `cidr` smallint(6) NOT NULL default '0', `ipv6` smallint(6) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`networkPrefix`,`hostPart`,`cidr`,`ipv6`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkPlugin` -- DROP TABLE IF EXISTS `networkPlugin`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkPlugin` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL default '0', `pluginID` int(11) NOT NULL default '0', `sequence` int(11) NOT NULL default '0', `newLine` tinyint(4) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`) ) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkTag` -- DROP TABLE IF EXISTS `networkTag`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkTag` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL default '0', `tag` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`tag`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `networkV6` -- DROP TABLE IF EXISTS `networkV6`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `networkV6` ( `ID` varbinary(22) NOT NULL default '', `rootID` int(11) NOT NULL default '0', `networkPrefix` bigint(20) unsigned NOT NULL default '0', `hostPart` bigint(20) unsigned NOT NULL default '0', `cidr` smallint(6) NOT NULL default '0', PRIMARY KEY (`ID`,`rootID`), UNIQUE KEY `rootID` (`rootID`,`networkPrefix`,`hostPart`,`cidr`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `plugin` -- DROP TABLE IF EXISTS `plugin`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `plugin` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `filename` varchar(255) NOT NULL default '', `active` tinyint(4) NOT NULL default '0', `lastRun` datetime NOT NULL default '0000-00-00 00:00:00', `runTime` int(11) NOT NULL default '0', `lastError` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `pluginConf` -- DROP TABLE IF EXISTS `pluginConf`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `pluginConf` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL, `pluginID` int(11) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`,`name`) ) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `pluginValue` -- DROP TABLE IF EXISTS `pluginValue`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `pluginValue` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL, `pluginID` int(11) NOT NULL, `origin` int(11) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`pluginID`,`name`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `root` -- DROP TABLE IF EXISTS `root`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `root` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `ipv6` tinyint(1) NOT NULL default '0', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `rootAC` -- DROP TABLE IF EXISTS `rootAC`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `rootAC` ( `ID` int(11) NOT NULL auto_increment, `rootID` int(11) NOT NULL default '0', `groupID` int(11) NOT NULL default '0', `ACL` int(11) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`groupID`) ) ENGINE=InnoDB AUTO_INCREMENT=156 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `setting` -- DROP TABLE IF EXISTS `setting`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `setting` ( `ID` int(11) NOT NULL auto_increment, `userID` int(11) NOT NULL default '0', `param` varchar(255) NOT NULL default '', `value` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `userID` (`userID`,`param`,`value`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `squad` -- DROP TABLE IF EXISTS `squad`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `squad` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `permissions` int(11) NOT NULL default '0', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `template` -- DROP TABLE IF EXISTS `template`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `template` ( `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `type` varchar(64) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `templateEntry` -- DROP TABLE IF EXISTS `templateEntry`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `templateEntry` ( `ID` int(11) NOT NULL auto_increment, `tmplID` int(11) NOT NULL default '0', `type` int(11) NOT NULL default '0', `position` int(11) NOT NULL default '0', `description` varchar(255) NOT NULL default '', `size` int(11) NOT NULL default '1', `entries` text NOT NULL, `rows` int(11) NOT NULL default '1', `cols` int(11) NOT NULL default '1', PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `templateValue` -- DROP TABLE IF EXISTS `templateValue`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `templateValue` ( `ID` int(11) NOT NULL auto_increment, `tmplID` int(11) NOT NULL default '0', `tmplEntryID` int(11) NOT NULL default '0', `netID` int(11) NOT NULL default '0', `value` blob NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`tmplID`,`tmplEntryID`) ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -- -- Table structure for table `user` -- DROP TABLE IF EXISTS `user`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `user` ( `ID` int(11) NOT NULL auto_increment, `username` varchar(100) NOT NULL default '', `password` varchar(255) NOT NULL default '', `groupIDs` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2015-01-03 5:02:35 HaCi/docs/HaCi_v0.98c.pgsql0000644000175000000000000005340212475447270014675 0ustar fighterroot-- -- PostgreSQL database dump -- SET statement_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; SET search_path = public, pg_catalog; -- -- Name: audit_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE audit_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.audit_id_seq OWNER TO "HaCi"; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: audit; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE audit ( id integer DEFAULT nextval('audit_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, access_groups character varying(255) DEFAULT ''::character varying NOT NULL, action character varying(255) DEFAULT ''::character varying NOT NULL, object character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(1024) DEFAULT ''::character varying NOT NULL, error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.audit OWNER TO "HaCi"; -- -- Name: network_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE network_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.network_id_seq OWNER TO "HaCi"; -- -- Name: network; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network ( id integer DEFAULT nextval('network_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT (0)::bigint NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, state smallint DEFAULT (0)::smallint NOT NULL, def_subnet_size smallint DEFAULT (0)::smallint NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, ipv6_id bytea DEFAULT '\x'::bytea NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, search_str character varying(255), CONSTRAINT network_def_subnet_size_check CHECK ((def_subnet_size >= 0)) ); ALTER TABLE public.network OWNER TO "HaCi"; -- -- Name: network_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE network_ac_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.network_ac_id_seq OWNER TO "HaCi"; -- -- Name: network_ac; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network_ac ( id integer DEFAULT nextval('network_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT (0)::bigint NOT NULL, net_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.network_ac OWNER TO "HaCi"; -- -- Name: network_lock_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE network_lock_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.network_lock_id_seq OWNER TO "HaCi"; -- -- Name: network_lock; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network_lock ( id integer DEFAULT nextval('network_lock_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, duration integer DEFAULT 0 NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT (0)::smallint NOT NULL, ipv6 smallint DEFAULT (0)::smallint NOT NULL ); ALTER TABLE public.network_lock OWNER TO "HaCi"; -- -- Name: network_plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE network_plugin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.network_plugin_id_seq OWNER TO "HaCi"; -- -- Name: network_plugin; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network_plugin ( id integer DEFAULT nextval('network_plugin_id_seq'::regclass) NOT NULL, net_id integer DEFAULT 0 NOT NULL, plugin_id integer DEFAULT 0 NOT NULL, sequence integer DEFAULT 0 NOT NULL, new_line smallint DEFAULT (0)::smallint NOT NULL ); ALTER TABLE public.network_plugin OWNER TO "HaCi"; -- -- Name: network_tag_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE network_tag_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.network_tag_id_seq OWNER TO "HaCi"; -- -- Name: network_tag; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network_tag ( id integer DEFAULT nextval('network_tag_id_seq'::regclass) NOT NULL, net_id integer DEFAULT 0 NOT NULL, tag character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.network_tag OWNER TO "HaCi"; -- -- Name: network_v6; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE network_v6 ( id bytea DEFAULT '\x'::bytea NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT (0)::smallint NOT NULL, CONSTRAINT network_v6_host_part_check CHECK ((host_part >= (0)::numeric)), CONSTRAINT network_v6_network_prefix_check CHECK ((network_prefix >= (0)::numeric)) ); ALTER TABLE public.network_v6 OWNER TO "HaCi"; -- -- Name: plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE plugin_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.plugin_id_seq OWNER TO "HaCi"; -- -- Name: plugin; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE plugin ( id integer DEFAULT nextval('plugin_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, filename character varying(255) DEFAULT ''::character varying NOT NULL, active smallint DEFAULT (0)::smallint NOT NULL, last_run timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, run_time integer DEFAULT 0 NOT NULL, last_error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.plugin OWNER TO "HaCi"; -- -- Name: plugin_conf_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE plugin_conf_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.plugin_conf_id_seq OWNER TO "HaCi"; -- -- Name: plugin_conf; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE plugin_conf ( id integer DEFAULT nextval('plugin_conf_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_conf OWNER TO "HaCi"; -- -- Name: plugin_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE plugin_value_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.plugin_value_id_seq OWNER TO "HaCi"; -- -- Name: plugin_value; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE plugin_value ( id integer DEFAULT nextval('plugin_value_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, origin integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_value OWNER TO "HaCi"; -- -- Name: root_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE root_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.root_id_seq OWNER TO "HaCi"; -- -- Name: root; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE root ( id integer DEFAULT nextval('root_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, ipv6 smallint DEFAULT (0)::smallint NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.root OWNER TO "HaCi"; -- -- Name: root_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE root_ac_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.root_ac_id_seq OWNER TO "HaCi"; -- -- Name: root_ac; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE root_ac ( id integer DEFAULT nextval('root_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.root_ac OWNER TO "HaCi"; -- -- Name: setting_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE setting_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.setting_id_seq OWNER TO "HaCi"; -- -- Name: setting; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE setting ( id integer DEFAULT nextval('setting_id_seq'::regclass) NOT NULL, user_id integer DEFAULT 0 NOT NULL, param character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.setting OWNER TO "HaCi"; -- -- Name: squad_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE squad_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.squad_id_seq OWNER TO "HaCi"; -- -- Name: squad; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE squad ( id integer DEFAULT nextval('squad_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, permissions integer DEFAULT 0 NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.squad OWNER TO "HaCi"; -- -- Name: template_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE template_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.template_id_seq OWNER TO "HaCi"; -- -- Name: template; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE template ( id integer DEFAULT nextval('template_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, type character varying(64) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.template OWNER TO "HaCi"; -- -- Name: template_entry_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE template_entry_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.template_entry_id_seq OWNER TO "HaCi"; -- -- Name: template_entry; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE template_entry ( id integer DEFAULT nextval('template_entry_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, type integer DEFAULT 0 NOT NULL, "position" integer DEFAULT 0 NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, size integer DEFAULT 1 NOT NULL, entries character varying(1023) DEFAULT ''::character varying NOT NULL, rows integer DEFAULT 1 NOT NULL, cols integer DEFAULT 1 NOT NULL ); ALTER TABLE public.template_entry OWNER TO "HaCi"; -- -- Name: template_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE template_value_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.template_value_id_seq OWNER TO "HaCi"; -- -- Name: template_value; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE template_value ( id integer DEFAULT nextval('template_value_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, tmpl_entry_id integer DEFAULT 0 NOT NULL, net_id integer DEFAULT 0 NOT NULL, value bytea NOT NULL ); ALTER TABLE public.template_value OWNER TO "HaCi"; -- -- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi -- CREATE SEQUENCE user_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE public.user_id_seq OWNER TO "HaCi"; -- -- Name: user; Type: TABLE; Schema: public; Owner: HaCi; Tablespace: -- CREATE TABLE "user" ( id integer DEFAULT nextval('user_id_seq'::regclass) NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, password character varying(255) DEFAULT ''::character varying NOT NULL, group_ids character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public."user" OWNER TO "HaCi"; -- -- Name: audit_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY audit ADD CONSTRAINT audit_pkey PRIMARY KEY (id); -- -- Name: network_ac_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_net_id_key UNIQUE (net_id, group_id); -- -- Name: network_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_pkey PRIMARY KEY (id); -- -- Name: network_lock_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_lock ADD CONSTRAINT network_lock_pkey PRIMARY KEY (id); -- -- Name: network_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_pkey PRIMARY KEY (id); -- -- Name: network_plugin_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_net_id_key UNIQUE (net_id, plugin_id); -- -- Name: network_plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_pkey PRIMARY KEY (id); -- -- Name: network_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_root_id_key UNIQUE (root_id, ipv6_id, network); -- -- Name: network_tag_net_id_tag_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_tag ADD CONSTRAINT network_tag_net_id_tag_key UNIQUE (net_id, tag); -- -- Name: network_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_tag ADD CONSTRAINT network_tag_pkey PRIMARY KEY (id); -- -- Name: network_v6_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_pkey PRIMARY KEY (id, root_id); -- -- Name: network_v6_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_root_id_key UNIQUE (root_id, network_prefix, host_part, cidr); -- -- Name: plugin_conf_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_conf_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_pkey PRIMARY KEY (id); -- -- Name: plugin_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_name_key UNIQUE (name); -- -- Name: plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_pkey PRIMARY KEY (id); -- -- Name: plugin_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_pkey PRIMARY KEY (id); -- -- Name: root_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_pkey PRIMARY KEY (id); -- -- Name: root_ac_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_root_id_key UNIQUE (root_id, group_id); -- -- Name: root_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_name_key UNIQUE (name); -- -- Name: root_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_pkey PRIMARY KEY (id); -- -- Name: setting_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_pkey PRIMARY KEY (id); -- -- Name: setting_user_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_user_id_key UNIQUE (user_id, param, value); -- -- Name: squad_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_name_key UNIQUE (name); -- -- Name: squad_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_pkey PRIMARY KEY (id); -- -- Name: template_entry_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY template_entry ADD CONSTRAINT template_entry_pkey PRIMARY KEY (id); -- -- Name: template_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_name_key UNIQUE (name); -- -- Name: template_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_pkey PRIMARY KEY (id); -- -- Name: template_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_net_id_key UNIQUE (net_id, tmpl_id, tmpl_entry_id); -- -- Name: template_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_pkey PRIMARY KEY (id); -- -- Name: user_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_pkey PRIMARY KEY (id); -- -- Name: user_username_key; Type: CONSTRAINT; Schema: public; Owner: HaCi; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_username_key UNIQUE (username); -- -- Name: network_description_idx; Type: INDEX; Schema: public; Owner: HaCi; Tablespace: -- CREATE INDEX network_description_idx ON network USING btree (description); -- -- Name: network_lock_uniq_network; Type: INDEX; Schema: public; Owner: HaCi; Tablespace: -- CREATE UNIQUE INDEX network_lock_uniq_network ON network_lock USING btree (root_id, network_prefix, host_part, cidr, ipv6); -- -- Name: network_network_idx; Type: INDEX; Schema: public; Owner: HaCi; Tablespace: -- CREATE INDEX network_network_idx ON network USING btree (network); -- -- Name: network_search_str_idx; Type: INDEX; Schema: public; Owner: HaCi; Tablespace: -- CREATE INDEX network_search_str_idx ON network USING btree (search_str); -- -- Name: public; Type: ACL; Schema: -; Owner: postgres -- REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO PUBLIC; -- -- PostgreSQL database dump complete -- HaCi/docs/manualDatabaseUpgrades_mysql.txt0000644000175000000000000000642212475447270020375 0ustar fighterroot# These are the database scheme changes between different HaCi versions. New tables were created by HaCi automatically. # Also those changes where automatically performed by HaCi. # Since version 0.97 there's a config option 'autoUpgradeDatabase', which gives you the opportunity to enable resp. disable the automatic upgrade function. 0.75 -> 0.8: ALTER TABLE network DROP INDEX network; ALTER TABLE network ADD COLUMN ipv6ID varchar(22) NOT NULL DEFAULT ''; ALTER TABLE network ADD UNIQUE (network, rootID, ipv6ID); ALTER TABLE root ADD COLUMN ipv6 tinyint(1) NOT NULL DEFAULT '0'; ALTER TABLE networkAC DROP INDEX rootID; ALTER TABLE networkAC ADD COLUMN netID integer(11) NOT NULL DEFAULT '0'; ALTER TABLE networkAC ADD UNIQUE (netID, groupID); 0.8 -> 0.91 ALTER TABLE plugin DROP COLUMN date; ALTER TABLE plugin ADD COLUMN runTime integer(11) NOT NULL DEFAULT '0'; ALTER TABLE plugin ADD COLUMN lastError varchar(255) NOT NULL DEFAULT ''; ALTER TABLE networkPlugin ADD COLUMN sequence integer(11) NOT NULL DEFAULT '0'; ALTER TABLE networkPlugin ADD COLUMN newLine tinyint(4) NOT NULL DEFAULT '0'; 0.91 -> 0.95 ALTER TABLE plugin ADD COLUMN filename varchar(255) NOT NULL DEFAULT ''; ALTER TABLE network ADD COLUMN defSubnetSize TINYINT(4) unsigned NOT NULL DEFAULT '0'; ALTER TABLE network ADD INDEX (network); 0.95 -> 0.96 ALTER TABLE network CHANGE COLUMN ipv6ID ipv6ID varbinary(22) NOT NULL DEFAULT ''; ALTER TABLE networkV6 CHANGE COLUMN ID ID varbinary(22) NOT NULL DEFAULT ''; 0.96 -> 0.97 ALTER TABLE networkV6 CHANGE COLUMN networkPrefix networkPrefix bigint(20) unsigned NOT NULL DEFAULT '0'; ALTER TABLE networkV6 CHANGE COLUMN hostPart hostPart bigint(20) unsigned NOT NULL DEFAULT '0'; 0.97 -> 0.98 ALTER TABLE templateEntry CHANGE COLUMN entries entries text NOT NULL; CREATE TABLE `audit` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `username` varchar(255) NOT NULL DEFAULT '', `accessGroups` varchar(255) NOT NULL DEFAULT '', `action` varchar(255) NOT NULL DEFAULT '', `object` varchar(255) NOT NULL DEFAULT '', `value` text NOT NULL, `error` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 0.98 -> 0.98b CREATE TABLE `networkLock` ( `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `duration` int(11) NOT NULL default '0', `rootID` int(11) NOT NULL default '0', `networkPrefix` bigint(20) unsigned NOT NULL default '0', `hostPart` bigint(20) unsigned NOT NULL default '0', `cidr` smallint(6) NOT NULL default '0', `ipv6` smallint(6) NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY `rootID` (`rootID`,`networkPrefix`,`hostPart`,`cidr`,`ipv6`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 0.98b -> 0.98c ALTER TABLE network ADD COLUMN searchStr varchar(255) DEFAULT ''; ALTER TABLE network CHANGE COLUMN network network bigint(20) unsigned NOT NULL DEFAULT 0; ALTER TABLE network ADD INDEX (description); ALTER TABLE network ADD INDEX (searchStr); CREATE TABLE `networkTag` ( `ID` int(11) NOT NULL auto_increment, `netID` int(11) NOT NULL default '0', `tag` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY `netID` (`netID`,`tag`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; HaCi/docs/HaCi_v0.98.psql0000644000175000000000000004370612475456643014375 0ustar fighterroot-- -- PostgreSQL database dump -- SET client_encoding = 'UTF8'; SET standard_conforming_strings = off; SET check_function_bodies = false; SET client_min_messages = warning; SET escape_string_warning = off; -- -- Name: HaCi2; Type: COMMENT; Schema: -; Owner: postgres -- COMMENT ON DATABASE "HaCi2" IS 'HaCi2'; SET search_path = public, pg_catalog; -- -- Name: network_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_id_seq OWNER TO "HaCi2"; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: network; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network ( id integer DEFAULT nextval('network_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT 0::bigint NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, state smallint DEFAULT 0::smallint NOT NULL, def_subnet_size smallint DEFAULT 0::smallint NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, ipv6_id bytea DEFAULT ''::bytea NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, CONSTRAINT network_def_subnet_size_check CHECK ((def_subnet_size >= 0)) ); ALTER TABLE public.network OWNER TO "HaCi2"; -- -- Name: network_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_ac_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_ac_id_seq OWNER TO "HaCi2"; -- -- Name: network_ac; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_ac ( id integer DEFAULT nextval('network_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT 0::bigint NOT NULL, net_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.network_ac OWNER TO "HaCi2"; -- -- Name: network_plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_plugin_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_plugin_id_seq OWNER TO "HaCi2"; -- -- Name: network_plugin; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_plugin ( id integer DEFAULT nextval('network_plugin_id_seq'::regclass) NOT NULL, net_id integer DEFAULT 0 NOT NULL, plugin_id integer DEFAULT 0 NOT NULL, sequence integer DEFAULT 0 NOT NULL, new_line smallint DEFAULT 0::smallint NOT NULL ); ALTER TABLE public.network_plugin OWNER TO "HaCi2"; -- -- Name: network_v6; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_v6 ( id bytea DEFAULT ''::bytea NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT 0::smallint NOT NULL, CONSTRAINT network_v6_host_part_check CHECK ((host_part >= (0)::numeric)), CONSTRAINT network_v6_network_prefix_check CHECK ((network_prefix >= (0)::numeric)) ); ALTER TABLE public.network_v6 OWNER TO "HaCi2"; -- -- Name: plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_id_seq OWNER TO "HaCi2"; -- -- Name: plugin; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin ( id integer DEFAULT nextval('plugin_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, filename character varying(255) DEFAULT ''::character varying NOT NULL, active smallint DEFAULT 0::smallint NOT NULL, last_run timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, run_time integer DEFAULT 0 NOT NULL, last_error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.plugin OWNER TO "HaCi2"; -- -- Name: plugin_conf_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_conf_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_conf_id_seq OWNER TO "HaCi2"; -- -- Name: plugin_conf; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin_conf ( id integer DEFAULT nextval('plugin_conf_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_conf OWNER TO "HaCi2"; -- -- Name: plugin_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_value_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_value_id_seq OWNER TO "HaCi2"; -- -- Name: plugin_value; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin_value ( id integer DEFAULT nextval('plugin_value_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, origin integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_value OWNER TO "HaCi2"; -- -- Name: root_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE root_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.root_id_seq OWNER TO "HaCi2"; -- -- Name: root; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE root ( id integer DEFAULT nextval('root_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, ipv6 smallint DEFAULT 0::smallint NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.root OWNER TO "HaCi2"; -- -- Name: root_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE root_ac_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.root_ac_id_seq OWNER TO "HaCi2"; -- -- Name: root_ac; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE root_ac ( id integer DEFAULT nextval('root_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.root_ac OWNER TO "HaCi2"; -- -- Name: setting_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE setting_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.setting_id_seq OWNER TO "HaCi2"; -- -- Name: setting; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE setting ( id integer DEFAULT nextval('setting_id_seq'::regclass) NOT NULL, user_id integer DEFAULT 0 NOT NULL, param character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.setting OWNER TO "HaCi2"; -- -- Name: squad_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE squad_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.squad_id_seq OWNER TO "HaCi2"; -- -- Name: squad; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE squad ( id integer DEFAULT nextval('squad_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, permissions integer DEFAULT 0 NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.squad OWNER TO "HaCi2"; -- -- Name: template_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_id_seq OWNER TO "HaCi2"; -- -- Name: template; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template ( id integer DEFAULT nextval('template_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, type character varying(64) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.template OWNER TO "HaCi2"; -- -- Name: template_entry_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_entry_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_entry_id_seq OWNER TO "HaCi2"; -- -- Name: template_entry; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template_entry ( id integer DEFAULT nextval('template_entry_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, type integer DEFAULT 0 NOT NULL, "position" integer DEFAULT 0 NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, size integer DEFAULT 1 NOT NULL, entries character varying(255) DEFAULT ''::character varying NOT NULL, rows integer DEFAULT 1 NOT NULL, cols integer DEFAULT 1 NOT NULL ); ALTER TABLE public.template_entry OWNER TO "HaCi2"; -- -- Name: template_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_value_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_value_id_seq OWNER TO "HaCi2"; -- -- Name: template_value; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template_value ( id integer DEFAULT nextval('template_value_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, tmpl_entry_id integer DEFAULT 0 NOT NULL, net_id integer DEFAULT 0 NOT NULL, value bytea NOT NULL ); ALTER TABLE public.template_value OWNER TO "HaCi2"; -- -- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE user_id_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.user_id_seq OWNER TO "HaCi2"; -- -- Name: user; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE "user" ( id integer DEFAULT nextval('user_id_seq'::regclass) NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, password character varying(255) DEFAULT ''::character varying NOT NULL, group_ids character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public."user" OWNER TO "HaCi2"; -- -- Name: network_ac_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_net_id_key UNIQUE (net_id, group_id); -- -- Name: network_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_pkey PRIMARY KEY (id); -- -- Name: network_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_pkey PRIMARY KEY (id); -- -- Name: network_plugin_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_net_id_key UNIQUE (net_id, plugin_id); -- -- Name: network_plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_pkey PRIMARY KEY (id); -- -- Name: network_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_root_id_key UNIQUE (root_id, ipv6_id, network); -- -- Name: network_v6_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_pkey PRIMARY KEY (id, root_id); -- -- Name: network_v6_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_root_id_key UNIQUE (root_id, network_prefix, host_part, cidr); -- -- Name: plugin_conf_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_conf_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_pkey PRIMARY KEY (id); -- -- Name: plugin_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_name_key UNIQUE (name); -- -- Name: plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_pkey PRIMARY KEY (id); -- -- Name: plugin_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_pkey PRIMARY KEY (id); -- -- Name: root_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_pkey PRIMARY KEY (id); -- -- Name: root_ac_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_root_id_key UNIQUE (root_id, group_id); -- -- Name: root_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_name_key UNIQUE (name); -- -- Name: root_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_pkey PRIMARY KEY (id); -- -- Name: setting_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_pkey PRIMARY KEY (id); -- -- Name: setting_user_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_user_id_key UNIQUE (user_id, param, value); -- -- Name: squad_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_name_key UNIQUE (name); -- -- Name: squad_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_pkey PRIMARY KEY (id); -- -- Name: template_entry_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_entry ADD CONSTRAINT template_entry_pkey PRIMARY KEY (id); -- -- Name: template_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_name_key UNIQUE (name); -- -- Name: template_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_pkey PRIMARY KEY (id); -- -- Name: template_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_net_id_key UNIQUE (net_id, tmpl_id, tmpl_entry_id); -- -- Name: template_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_pkey PRIMARY KEY (id); -- -- Name: user_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_pkey PRIMARY KEY (id); -- -- Name: user_username_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_username_key UNIQUE (username); -- -- Name: network_network_idx; Type: INDEX; Schema: public; Owner: HaCi2; Tablespace: -- CREATE INDEX network_network_idx ON network USING btree (network); -- -- Name: public; Type: ACL; Schema: -; Owner: postgres -- REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO PUBLIC; -- -- PostgreSQL database dump complete -- HaCi/docs/HaCi_v0.98b-v0.98c.pgsql0000644000175000000000000000154212475456651015622 0ustar fighterrootALTER TABLE network ADD COLUMN search_str character varying(255) DEFAULT ''::character varying; CREATE INDEX network_description_idx ON network USING btree (description); CREATE INDEX network_search_str_idx ON network USING btree (search_str); CREATE SEQUENCE network_tag_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_tag_id_seq OWNER TO "HaCi2"; CREATE TABLE network_tag ( id integer DEFAULT nextval('network_tag_id_seq'::regclass) NOT NULL, net_id integer DEFAULT 0 NOT NULL, tag character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.network_tag OWNER TO "HaCi2"; ALTER TABLE ONLY network_tag ADD CONSTRAINT network_tag_pkey PRIMARY KEY (id); ALTER TABLE ONLY network_tag ADD CONSTRAINT network_tag_net_id_tag_key UNIQUE (net_id, tag); HaCi/docs/HaCi_v0.98b.pgsql0000644000175000000000000005123312475457053014674 0ustar fighterroot-- -- PostgreSQL database dump -- SET statement_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = off; SET check_function_bodies = false; SET client_min_messages = warning; SET escape_string_warning = off; -- -- Name: HaCi2; Type: COMMENT; Schema: -; Owner: postgres -- COMMENT ON DATABASE "HaCi2" IS 'HaCi2'; SET search_path = public, pg_catalog; -- -- Name: network_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_id_seq OWNER TO "HaCi2"; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: network; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network ( id integer DEFAULT nextval('network_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT (0)::bigint NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, state smallint DEFAULT (0)::smallint NOT NULL, def_subnet_size smallint DEFAULT (0)::smallint NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, ipv6_id bytea DEFAULT ''::bytea NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, CONSTRAINT network_def_subnet_size_check CHECK ((def_subnet_size >= 0)) ); ALTER TABLE public.network OWNER TO "HaCi2"; -- -- Name: network_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_ac_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_ac_id_seq OWNER TO "HaCi2"; -- -- Name: network_ac; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_ac ( id integer DEFAULT nextval('network_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, network bigint DEFAULT (0)::bigint NOT NULL, net_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.network_ac OWNER TO "HaCi2"; -- -- Name: network_plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_plugin_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_plugin_id_seq OWNER TO "HaCi2"; -- -- Name: network_plugin; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_plugin ( id integer DEFAULT nextval('network_plugin_id_seq'::regclass) NOT NULL, net_id integer DEFAULT 0 NOT NULL, plugin_id integer DEFAULT 0 NOT NULL, sequence integer DEFAULT 0 NOT NULL, new_line smallint DEFAULT (0)::smallint NOT NULL ); ALTER TABLE public.network_plugin OWNER TO "HaCi2"; -- -- Name: network_v6; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_v6 ( id bytea DEFAULT ''::bytea NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT (0)::smallint NOT NULL, CONSTRAINT network_v6_host_part_check CHECK ((host_part >= (0)::numeric)), CONSTRAINT network_v6_network_prefix_check CHECK ((network_prefix >= (0)::numeric)) ); ALTER TABLE public.network_v6 OWNER TO "HaCi2"; -- -- Name: plugin_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_id_seq OWNER TO "HaCi2"; -- -- Name: plugin; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin ( id integer DEFAULT nextval('plugin_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, filename character varying(255) DEFAULT ''::character varying NOT NULL, active smallint DEFAULT (0)::smallint NOT NULL, last_run timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, run_time integer DEFAULT 0 NOT NULL, last_error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.plugin OWNER TO "HaCi2"; -- -- Name: plugin_conf_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_conf_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_conf_id_seq OWNER TO "HaCi2"; -- -- Name: plugin_conf; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin_conf ( id integer DEFAULT nextval('plugin_conf_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_conf OWNER TO "HaCi2"; -- -- Name: plugin_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE plugin_value_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.plugin_value_id_seq OWNER TO "HaCi2"; -- -- Name: plugin_value; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE plugin_value ( id integer DEFAULT nextval('plugin_value_id_seq'::regclass) NOT NULL, net_id integer NOT NULL, plugin_id integer NOT NULL, origin integer NOT NULL, name character varying(255) NOT NULL, value text NOT NULL ); ALTER TABLE public.plugin_value OWNER TO "HaCi2"; -- -- Name: root_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE root_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.root_id_seq OWNER TO "HaCi2"; -- -- Name: root; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE root ( id integer DEFAULT nextval('root_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, ipv6 smallint DEFAULT (0)::smallint NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.root OWNER TO "HaCi2"; -- -- Name: root_ac_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE root_ac_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.root_ac_id_seq OWNER TO "HaCi2"; -- -- Name: root_ac; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE root_ac ( id integer DEFAULT nextval('root_ac_id_seq'::regclass) NOT NULL, root_id integer DEFAULT 0 NOT NULL, group_id integer DEFAULT 0 NOT NULL, acl integer DEFAULT 0 NOT NULL ); ALTER TABLE public.root_ac OWNER TO "HaCi2"; -- -- Name: setting_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE setting_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.setting_id_seq OWNER TO "HaCi2"; -- -- Name: setting; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE setting ( id integer DEFAULT nextval('setting_id_seq'::regclass) NOT NULL, user_id integer DEFAULT 0 NOT NULL, param character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.setting OWNER TO "HaCi2"; -- -- Name: squad_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE squad_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.squad_id_seq OWNER TO "HaCi2"; -- -- Name: squad; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE squad ( id integer DEFAULT nextval('squad_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, permissions integer DEFAULT 0 NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.squad OWNER TO "HaCi2"; -- -- Name: template_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_id_seq OWNER TO "HaCi2"; -- -- Name: template; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template ( id integer DEFAULT nextval('template_id_seq'::regclass) NOT NULL, name character varying(255) DEFAULT ''::character varying NOT NULL, type character varying(64) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public.template OWNER TO "HaCi2"; -- -- Name: template_entry_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_entry_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_entry_id_seq OWNER TO "HaCi2"; -- -- Name: template_entry; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template_entry ( id integer DEFAULT nextval('template_entry_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, type integer DEFAULT 0 NOT NULL, "position" integer DEFAULT 0 NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, size integer DEFAULT 1 NOT NULL, entries character varying(1023) DEFAULT ''::character varying NOT NULL, rows integer DEFAULT 1 NOT NULL, cols integer DEFAULT 1 NOT NULL ); ALTER TABLE public.template_entry OWNER TO "HaCi2"; -- -- Name: template_value_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE template_value_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.template_value_id_seq OWNER TO "HaCi2"; -- -- Name: template_value; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE template_value ( id integer DEFAULT nextval('template_value_id_seq'::regclass) NOT NULL, tmpl_id integer DEFAULT 0 NOT NULL, tmpl_entry_id integer DEFAULT 0 NOT NULL, net_id integer DEFAULT 0 NOT NULL, value bytea NOT NULL ); ALTER TABLE public.template_value OWNER TO "HaCi2"; -- -- Name: audit_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE audit_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.audit_id_seq OWNER TO "HaCi2"; -- -- Name: audit; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE audit ( id integer DEFAULT nextval('audit_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, access_groups character varying(255) DEFAULT ''::character varying NOT NULL, action character varying(255) DEFAULT ''::character varying NOT NULL, object character varying(255) DEFAULT ''::character varying NOT NULL, value character varying(1024) DEFAULT ''::character varying NOT NULL, error character varying(255) DEFAULT ''::character varying NOT NULL ); ALTER TABLE public.audit OWNER TO "HaCi2"; -- -- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE user_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.user_id_seq OWNER TO "HaCi2"; -- -- Name: user; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE "user" ( id integer DEFAULT nextval('user_id_seq'::regclass) NOT NULL, username character varying(100) DEFAULT ''::character varying NOT NULL, password character varying(255) DEFAULT ''::character varying NOT NULL, group_ids character varying(255) DEFAULT ''::character varying NOT NULL, description character varying(255) DEFAULT ''::character varying NOT NULL, create_from character varying(255) DEFAULT ''::character varying NOT NULL, create_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, modify_from character varying(255) DEFAULT ''::character varying NOT NULL, modify_date timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL ); ALTER TABLE public."user" OWNER TO "HaCi2"; -- -- Name: network_ac_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_net_id_key UNIQUE (net_id, group_id); -- -- Name: network_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_ac ADD CONSTRAINT network_ac_pkey PRIMARY KEY (id); -- -- Name: network_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_pkey PRIMARY KEY (id); -- -- Name: network_plugin_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_net_id_key UNIQUE (net_id, plugin_id); -- -- Name: network_plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_plugin ADD CONSTRAINT network_plugin_pkey PRIMARY KEY (id); -- -- Name: network_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network ADD CONSTRAINT network_root_id_key UNIQUE (root_id, ipv6_id, network); -- -- Name: network_v6_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_pkey PRIMARY KEY (id, root_id); -- -- Name: network_v6_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_v6 ADD CONSTRAINT network_v6_root_id_key UNIQUE (root_id, network_prefix, host_part, cidr); -- -- Name: plugin_conf_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_conf_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_conf ADD CONSTRAINT plugin_conf_pkey PRIMARY KEY (id); -- -- Name: plugin_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_name_key UNIQUE (name); -- -- Name: plugin_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin ADD CONSTRAINT plugin_pkey PRIMARY KEY (id); -- -- Name: plugin_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_net_id_key UNIQUE (net_id, plugin_id, name); -- -- Name: plugin_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY plugin_value ADD CONSTRAINT plugin_value_pkey PRIMARY KEY (id); -- -- Name: root_ac_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_pkey PRIMARY KEY (id); -- -- Name: root_ac_root_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root_ac ADD CONSTRAINT root_ac_root_id_key UNIQUE (root_id, group_id); -- -- Name: root_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_name_key UNIQUE (name); -- -- Name: root_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY root ADD CONSTRAINT root_pkey PRIMARY KEY (id); -- -- Name: setting_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_pkey PRIMARY KEY (id); -- -- Name: setting_user_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY setting ADD CONSTRAINT setting_user_id_key UNIQUE (user_id, param, value); -- -- Name: squad_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_name_key UNIQUE (name); -- -- Name: squad_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY squad ADD CONSTRAINT squad_pkey PRIMARY KEY (id); -- -- Name: template_entry_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_entry ADD CONSTRAINT template_entry_pkey PRIMARY KEY (id); -- -- Name: template_name_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_name_key UNIQUE (name); -- -- Name: template_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template ADD CONSTRAINT template_pkey PRIMARY KEY (id); -- -- Name: template_value_net_id_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_net_id_key UNIQUE (net_id, tmpl_id, tmpl_entry_id); -- -- Name: template_value_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY template_value ADD CONSTRAINT template_value_pkey PRIMARY KEY (id); -- -- Name: audit_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY audit ADD CONSTRAINT audit_pkey PRIMARY KEY (id); -- -- Name: user_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_pkey PRIMARY KEY (id); -- -- Name: user_username_key; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY "user" ADD CONSTRAINT user_username_key UNIQUE (username); -- -- Name: network_network_idx; Type: INDEX; Schema: public; Owner: HaCi2; Tablespace: -- CREATE INDEX network_network_idx ON network USING btree (network); -- -- Name: public; Type: ACL; Schema: -; Owner: postgres -- -- -- Name: network_lock_id_seq; Type: SEQUENCE; Schema: public; Owner: HaCi2 -- CREATE SEQUENCE network_lock_id_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; ALTER TABLE public.network_lock_id_seq OWNER TO "HaCi2"; -- -- Name: network_lock; Type: TABLE; Schema: public; Owner: HaCi2; Tablespace: -- CREATE TABLE network_lock ( id integer DEFAULT nextval('network_lock_id_seq'::regclass) NOT NULL, ts timestamp without time zone DEFAULT '1970-01-01 00:00:00'::timestamp without time zone NOT NULL, duration integer DEFAULT 0 NOT NULL, root_id integer DEFAULT 0 NOT NULL, network_prefix numeric(20,0) DEFAULT (0)::numeric NOT NULL, host_part numeric(20,0) DEFAULT (0)::numeric NOT NULL, cidr smallint DEFAULT (0)::smallint NOT NULL, ipv6 smallint DEFAULT (0)::smallint NOT NULL ); ALTER TABLE public.network_lock OWNER TO "HaCi2"; -- -- Name: network_lock_pkey; Type: CONSTRAINT; Schema: public; Owner: HaCi2; Tablespace: -- ALTER TABLE ONLY network_lock ADD CONSTRAINT network_lock_pkey PRIMARY KEY (id); -- -- Name: network_lock_uniq_network; Type: INDEX; Schema: public; Owner: HaCi2; Tablespace: -- CREATE UNIQUE INDEX network_lock_uniq_network ON network_lock USING btree (root_id, network_prefix, host_part, cidr, ipv6); REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO PUBLIC; -- -- PostgreSQL database dump complete -- HaCi/etc/0000755000175000000000000000000012475462427011632 5ustar fighterrootHaCi/etc/HaCi.conf.sample0000644000175000000000000000525212475447270014570 0ustar fighterroot # database parameters dbHost = localhost dbName = HaCi dbUser = HaCi dbPass = geheim! # database type (mysql [default], postgresql) dbType = mysql # GUI parameters root = FF0000 new = 777777 reserved = FF00F0 locked = 555555 free = 00AA00 assigned = 000000 invisible = FFFFFF netBorder = 5555FF searched = FFAAAA # technical contact techContact = support@domain.tld # preferred style # bright # black style = bright # direct access to network functions by icons directAccess = 1 # show error details instead of a basic error warnings showErrorDetails = 1 # max. number (2^?) of calculated (free) subnets (2^8 => 256) maxSubnetSize = 8 # show structure of the tree (with lines) showTreeStructure = 1 # enable locales support (default 1) enableLocaleSupport = 1 # show number of free subnets with CIDR 'min' showNrOfFreeSubnetsWithMinCIDR = 1 # show first and last address as 'netaddress' respectively 'broadcast' showNetBorders = 1 # remove CIDR (/32 respectively /128) from IP addresses removeCIDRFromIPs = 1 # maximum memory consumption in kB for the HaCi Daemon. If this limit is reached, HaCid will be restarted automatically maxMemConsumption = 50000 # disable caching mechanismen disableCache = 0 # automatically upgrade database if needed autoUpgradeDatabase = 1 # suppress database upgrade error messages if you are sure that everything is okay ignoreUpgradeFailures = 0 # enable debugging debug = 0 # set default network lock time (in seconds) when deleting networks dftNetworkLock = 0 # authentication parameters ## authentication modules # internal: internal auth # imap : IMAP auth # ldap : LDAP auth # # you can specify multiple authentication modules which will be used one after another authModule = ldap, internal # authenticationmodule parameters # host = imap.server.tld # authtype = login # authenticate with bindDN # host = ldap1.server.tld # host = ldap2.server.tld # bindDN = cn=,ou=linux-users,dc=example,dc=com # the variable will be replaced with the authenticating username # search for user and authenticate against that dn (necessary, if the cn isn't equal with the authenticating username) # host = ldap1.server.tld # host = ldap2.server.tld # bindDN = cn=ldapread,ou=User,dc=example,dc=com # bindPW = # baseDN = ou=organisation,dc=example,dc=com # filter = attr= # the variable will be replaced with the authenticating username # vim:ts=8 HaCi/etc/startup.pl0000644000175000000000000000064712115202117013653 0ustar fighterrootuse strict; use FindBin; $ENV{MOD_PERL} or die "not running under mod_perl!"; use lib qw(/var/www/HaCi/modules); use ModPerl::Registry; use Apache::DBI; use CGI (); CGI->compile(); use Digest::SHA; use CGI::Session; use CGI::Cookie; use CGI::Ajax; use Math::BigInt; use Math::BigInt::FastCalc; use Config::General qw(ParseConfig); use Cache::FastMmap; use Net::CIDR; use Net::IPv6Addr; use Locale::gettext; use Storable; HaCi/etc/startup.pl.sample0000644000175000000000000000041311003701764015131 0ustar fighterrootuse strict; $ENV{MOD_PERL} or die "not running under mod_perl!"; use Apache::Registry (); use Apache::DBI (); use DBI (); use CGI (); CGI->compile(); use Digest::SHA; use CGI::Session; use CGI::Cookie; use File::Cache; use Math::BigInt; use Math::BigInt::FastCalc; HaCi/etc/internal.conf0000644000175000000000000001531312475455340014314 0ustar fighterroot# This config is for internal use only. # Only touch if you know what you are doing! dbType = mysql workdir = $workdir templateIncludePath = $workdir/Template spoolPath = $workdir/spool templateCompilePath = ${spoolPath} authModules = $workdir/modules/HaCi/Authentication plugins = $workdir/modules/HaCi/Plugins localePath = $workdir/locale statusFile = ${spoolPath}/status cacheFile = ${spoolPath}/cache tableHashFile = ${spoolPath}/tableHash HaCid = $workdir/bin/HaCid.pl templateInit = HaCi.tmpl imagePathRel = /Images configFile = HaCi.conf whois = /usr/bin/whois imagePath = ${workdir}${imagePathRel} HaCidPID = ${spoolPath}/HaCid.pid lockPath = ${spoolPath} titleShort = HaCi titleLong = HaCi - IP Address Administration version = 0.98c jsSrcDir = / jsSrc = /HaCi.js imagePathRel = /Images logo = ${imagePathRel}/logo.png style = bright maxSubnetSize = 8 enableLocaleSupport = 1 ID = C name = English ID = de_DE name = Deutsch ID = it_IT name = Italiano ID = bright name = bright file = /HaCi.css descr = Bright Layout ID = black name = black file = /HaCi_black.css descr = Black Layout debug = 0 dbiTrace = 0 ripeDB = 193.0.6.135 sessionTimeout = +4h maxMemConsumption = 100000 disableCache = 0 searchLimit = 2000 name = UNSPECIFIED ID = 0 name = ALLOCATED PA ID = 3 minSize = 21 banParents = 1, 2, 16, 5 name = ALLOCATED PI ID = 4 minSize = 21 banParents = 1, 2, 16, 5 name = ALLOCATED UNSPECIFIED ID = 17 banParents = 1, 2, 16, 5 name = SUB-ALLOCATED PA ID = 15 parents = 3 minSize = 24 banParents = 1, 2, 16 name = LIR-PARTITIONED PA ID = 6 banParents = 1, 2, 16 name = LIR-PARTITIONED PI ID = 10 banParents = 1, 2, 16 name = EARLY REGISTRATION ID = 5 banParents = 1, 2, 16 name = NOT-SET ID = 11 name = ASSIGNED PA ID = 1 banish = 1, 2, 16 parents = 3, 17, 15, 6, 5 name = ASSIGNED PI ID = 2 banish = 1, 2, 16, 5 parents = 4, 17, 10 name = ASSIGNED ANYCAST ID = 16 banish = 1, 2, 16 parents = 3, 4, 17, 15, 10, 6 name = ALLOCATED-BY-RIR ID = 12 minSize = 32 banParents = 14 ipv6 = 1 name = ALLOCATED-BY-LIR ID = 13 parents = 12, 13 banParents = 14 ipv6 = 1 name = ASSIGNED ID = 14 minSize = 48 parents = 12, 13 banish = 14 ipv6 = 1 name = IN USE # parents = 1, 2, 16, 14 ID = 18 name = RESERVED ID = 7 name = LOCKED ID = 8 name = FREE ID = 9 <0> long = User Management short = userMgmt order = 0 <1> long = Group Management short = groupMgmt order = 1 <2> long = Template Management short = tmplMgmt order = 2 <3> long = Show Roots short = showRoots order = 4 <4> long = Show Root Details short = showRootDet order = 5 <5> long = Add Root short = addRoot order = 6 <6> long = Edit Root short = editRoot order = 7 <7> long = Show Networks short = showNets order = 8 <8> long = Show Network Details short = showNetDet order = 9 <9> long = Add Network short = addNet order = 10 <10> long = Edit Network short = editNet order = 11 <11> long = Edit Tree short = editTree order = 12 <12> long = Import ASN Routes short = impASNRoutes order = 13 <13> long = Import DNS Zonefile short = impDNS order = 14 <14> long = Import Config short = impConfig order = 15 <15> long = SearchCompare short = search order = 16 <16> long = Plugin Management short = pluginMgmt order = 3 <17> long = Show audit logs short = showAuditLogs order = 17 type = label value = Defaults for mode 'on Demand' type = label value = Defaults for mode 'recurrent' name = def_recurrent_withSubnets descr = With Subnetworks type = checkbox checked = 0 help = Work also with Subnetworks name = def_recurrent_onlyHosts descr = Only IP addresses type = checkbox checked = 0 help = Work only with IP addresses name = def_recurrent_maxdepth descr = Max Depth type = textbox size = 1 maxLength = 10 help = Max subnet depth value = 1 name = def_recurrent_resetLastRun descr = Reset last run type = checkbox checked = 0 help = Reset last run so that the Plugin will be run as soon as possible nodb = 1 value = Defaults for mode 'on Demand' type = label value = Defaults for mode 'recurrent' type = label name = def_glob_recurrent_interval descr = Interval (sec) type = textbox size = 3 maxLength = 10 help = Time between 2 runs value = 300 name = def_glob_recurrent_resetLastRun descr = Reset last run type = checkbox checked = 0 help = Reset last run so that the Plugin will be run as soon as possible nodb = 1 type = hline thisScript = ${SCRIPT_NAME} showSubs = 0 status = # vim:ts=8 HaCi/html/0000755000175000000000000000000012475447615012025 5ustar fighterrootHaCi/html/BigInt.js0000644000175000000000000012475310674173504013543 0ustar fighterroot//////////////////////////////////////////////////////////////////////////////////////// // Big Integer Library v. 5.0 // Created 2000, last modified 2006 // Leemon Baird // www.leemon.com // // This file is public domain. You can use it for any purpose without restriction. // I do not guarantee that it is correct, so use it at your own risk. If you use // it for something interesting, I'd appreciate hearing about it. If you find // any bugs or make any improvements, I'd appreciate hearing about those too. // It would also be nice if my name and address were left in the comments. // But none of that is required. // // This code defines a bigInt library for arbitrary-precision integers. // A bigInt is an array of integers storing the value in chunks of bpe bits, // little endian (buff[0] is the least significant word). // Negative bigInts are stored two's complement. // Some functions assume their parameters have at least one leading zero element. // Functions with an underscore at the end of the name have unpredictable behavior in case of overflow, // so the caller must make sure overflow won't happen. // For each function where a parameter is modified, that same // variable must not be used as another argument too. // So, you cannot square x by doing multMod_(x,x,n). // You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n). // // These functions are designed to avoid frequent dynamic memory allocation in the inner loop. // For most functions, if it needs a BigInt as a local variable it will actually use // a global, and will only allocate to it when it's not the right size. This ensures // that when a function is called repeatedly with same-sized parameters, it only allocates // memory on the first call. // // Note that for cryptographic purposes, the calls to Math.random() must // be replaced with calls to a better pseudorandom number generator. // // In the following, "bigInt" means a bigInt with at least one leading zero element, // and "integer" means a nonnegative integer less than radix. In some cases, integer // can be negative. Negative bigInts are 2s complement. // // The following functions do not modify their inputs, but dynamically allocate memory every time they are called: // // function bigInt2str(x,base) //convert a bigInt into a string in a given base, from base 2 up to base 95 // function dup(x) //returns a copy of bigInt x // function findPrimes(n) //return array of all primes less than integer n // function int2bigInt(t,n,m) //convert integer t to a bigInt with at least n bits and m array elements // function str2bigInt(s,b,n,m) //convert string s in base b to a bigInt with at least n bits and m array elements // function trim(x,k) //return a copy of x with exactly k leading zero elements // // The following functions do not modify their inputs, so there is never a problem with the result being too big: // // function bitSize(x) //returns how many bits long the bigInt x is, not counting leading zeros // function equals(x,y) //is the bigInt x equal to the bigint y? // function equalsInt(x,y) //is bigint x equal to integer y? // function greater(x,y) //is x>y? (x and y are nonnegative bigInts) // function greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y? // function isZero(x) //is the bigInt x equal to zero? // function millerRabin(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime (as opposed to definitely composite)? // function modInt(x,n) //return x mod n for bigInt x and integer n. // function negative(x) //is bigInt x negative? // // The following functions do not modify their inputs, but allocate memory and call functions with underscores // // function add(x,y) //return (x+y) for bigInts x and y. // function addInt(x,n) //return (x+n) where x is a bigInt and n is an integer. // function expand(x,n) //return a copy of x with at least n elements, adding leading zeros if needed // function inverseMod(x,n) //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null // function mod(x,n) //return a new bigInt equal to (x mod n) for bigInts x and n. // function mult(x,y) //return x*y for bigInts x and y. This is faster when y=1. // function randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb. // function squareMod_(x,n) //do x=x*x mod n for bigInts x,n // function sub_(x,y) //do x=x-y for bigInts x and y. Negative answers will be 2s complement. // function subShift_(x,y,ys) //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement. // // The following functions are based on algorithms from the _Handbook of Applied Cryptography_ // powMod_() = algorithm 14.94, Montgomery exponentiation // eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_ // GCD_() = algorothm 14.57, Lehmer's algorithm // mont_() = algorithm 14.36, Montgomery multiplication // divide_() = algorithm 14.20 Multiple-precision division // squareMod_() = algorithm 14.16 Multiple-precision squaring // randTruePrime_() = algorithm 4.62, Maurer's algorithm // millerRabin() = algorithm 4.24, Miller-Rabin algorithm // // Profiling shows: // randTruePrime_() spends: // 10% of its time in calls to powMod_() // 85% of its time in calls to millerRabin() // millerRabin() spends: // 99% of its time in calls to powMod_() (always with a base of 2) // powMod_() spends: // 94% of its time in calls to mont_() (almost always with x==y) // // This suggests there are several ways to speed up this library slightly: // - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window) // -- this should especially focus on being fast when raising 2 to a power mod n // - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test // - tune the parameters in randTruePrime_(), including c, m, and recLimit // - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking // within the loop when all the parameters are the same length. // // There are several ideas that look like they wouldn't help much at all: // - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway) // - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32) // - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square // followed by a Montgomery reduction. The intermediate answer will be twice as long as x, so that // method would be slower. This is unfortunate because the code currently spends almost all of its time // doing mont_(x,x,...), both for randTruePrime_() and powMod_(). A faster method for Montgomery squaring // would have a large impact on the speed of randTruePrime_() and powMod_(). HAC has a couple of poorly-worded // sentences that seem to imply it's faster to do a non-modular square followed by a single // Montgomery reduction, but that's obviously wrong. //////////////////////////////////////////////////////////////////////////////////////// //globals bpe=0; //bits stored per array element mask=0; //AND this with an array element to chop it down to bpe bits radix=mask+1; //equals 2^bpe. A single 1 bit to the left of the last bit of mask. //the digits for converting to different bases digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-'; //initialize the global variables for (bpe=0; (1<<(bpe+1)) > (1<>=1; //bpe=number of bits in one element of the array representing the bigInt mask=(1<0); j--); for (z=0,w=x[j]; w; (w>>=1),z++); z+=bpe*j; return z; } //return a copy of x with at least n elements, adding leading zeros if needed function expand(x,n) { var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0); copy_(ans,x); return ans; } //return a k-bit true random prime using Maurer's algorithm. function randTruePrime(k) { var ans=int2bigInt(0,k,0); randTruePrime_(ans,k); return trim(ans,1); } //return a new bigInt equal to (x mod n) for bigInts x and n. function mod(x,n) { var ans=dup(x); mod_(ans,n); return trim(ans,1); } //return (x+n) where x is a bigInt and n is an integer. function addInt(x,n) { var ans=expand(x,x.length+1); addInt_(ans,n); return trim(ans,1); } //return x*y for bigInts x and y. This is faster when yy.length ? x.length+1 : y.length+1)); sub_(ans,y); return trim(ans,1); } //return (x+y) for bigInts x and y. function add(x,y) { var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); add_(ans,y); return trim(ans,1); } //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null function inverseMod(x,n) { var ans=expand(x,n.length); var s; s=inverseMod_(ans,n); return s ? trim(ans,1) : null; } //return (x*y mod n) for bigInts x,y,n. For greater speed, let y>1))-1; //pm is binary number with all ones, just over sqrt(2^k) copyInt_(ans,0); for (dd=1;dd;) { dd=0; ans[0]= 1 | (1<<(k-1)) | Math.floor(Math.random()*(1<2*m) //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits for (r=1; k-k*r<=m; ) r=pows[Math.floor(Math.random()*512)]; //r=Math.pow(2,Math.random()-1); else r=.5; //simulation suggests the more complex algorithm using r=.333 is only slightly faster. recSize=Math.floor(r*k)+1; randTruePrime_(s_q,recSize); copyInt_(s_i2,0); s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe)); //s_i2=2^(k-2) divide_(s_i2,s_q,s_i,s_rm); //s_i=floor((2^(k-1))/(2q)) z=bitSize(s_i); for (;;) { for (;;) { //generate z-bit numbers until one falls in the range [0,s_i-1] randBigInt_(s_R,z,0); if (greater(s_i,s_R)) break; } //now s_R is in the range [0,s_i-1] addInt_(s_R,1); //now s_R is in the range [1,s_i] add_(s_R,s_i); //now s_R is in the range [s_i+1,2*s_i] copy_(s_n,s_q); mult_(s_n,s_R); multInt_(s_n,2); addInt_(s_n,1); //s_n=2*s_R*s_q+1 copy_(s_r2,s_R); multInt_(s_r2,2); //s_r2=2*s_R //check s_n for divisibility by small primes up to B for (divisible=0,j=0; (j0); j--); //strip leading zeros for (zz=0,w=s_n[j]; w; (w>>=1),zz++); zz+=bpe*j; //zz=number of bits in s_n, ignoring leading zeros for (;;) { //generate z-bit numbers until one falls in the range [0,s_n-1] randBigInt_(s_a,zz,0); if (greater(s_n,s_a)) break; } //now s_a is in the range [0,s_n-1] addInt_(s_n,3); //now s_a is in the range [0,s_n-4] addInt_(s_a,2); //now s_a is in the range [2,s_n-2] copy_(s_b,s_a); copy_(s_n1,s_n); addInt_(s_n1,-1); powMod_(s_b,s_n1,s_n); //s_b=s_a^(s_n-1) modulo s_n addInt_(s_b,-1); if (isZero(s_b)) { copy_(s_b,s_a); powMod_(s_b,s_r2,s_n); addInt_(s_b,-1); copy_(s_aa,s_n); copy_(s_d,s_b); GCD_(s_d,s_n); //if s_b and s_n are relatively prime, then s_n is a prime if (equalsInt(s_d,1)) { copy_(ans,s_aa); return; //if we've made it this far, then s_n is absolutely guaranteed to be prime } } } } } //set b to an n-bit random BigInt. If s=1, then nth bit (most significant bit) is set to 1. //array b must be big enough to hold the result. Must have n>=1 function randBigInt_(b,n,s) { var i,a; for (i=0;i=0;i--); //find most significant element of x xp=x[i]; yp=y[i]; A=1; B=0; C=0; D=1; while ((yp+C) && (yp+D)) { q =Math.floor((xp+A)/(yp+C)); qp=Math.floor((xp+B)/(yp+D)); if (q!=qp) break; t= A-q*C; A=C; C=t; // do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp) t= B-q*D; B=D; D=t; t=xp-q*yp; xp=yp; yp=t; } if (B) { copy_(T,x); linComb_(x,y,A,B); //x=A*x+B*y linComb_(y,T,D,C); //y=D*y+C*T } else { mod_(x,y); copy_(T,x); copy_(x,y); copy_(y,T); } } if (y[0]==0) return; t=modInt(x,y[0]); copyInt_(x,y[0]); y[0]=t; while (y[0]) { x[0]%=y[0]; t=x[0]; x[0]=y[0]; y[0]=t; } } //do x=x**(-1) mod n, for bigInts x and n. //If no inverse exists, it sets x to zero and returns 0, else it returns 1. //The x array must be at least as large as the n array. function inverseMod_(x,n) { var k=1+2*Math.max(x.length,n.length); if(!(x[0]&1) && !(n[0]&1)) { //if both inputs are even, then inverse doesn't exist copyInt_(x,0); return 0; } if (eg_u.length!=k) { eg_u=new Array(k); eg_v=new Array(k); eg_A=new Array(k); eg_B=new Array(k); eg_C=new Array(k); eg_D=new Array(k); } copy_(eg_u,x); copy_(eg_v,n); copyInt_(eg_A,1); copyInt_(eg_B,0); copyInt_(eg_C,0); copyInt_(eg_D,1); for (;;) { while(!(eg_u[0]&1)) { //while eg_u is even halve_(eg_u); if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if eg_A==eg_B==0 mod 2 halve_(eg_A); halve_(eg_B); } else { add_(eg_A,n); halve_(eg_A); sub_(eg_B,x); halve_(eg_B); } } while (!(eg_v[0]&1)) { //while eg_v is even halve_(eg_v); if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if eg_C==eg_D==0 mod 2 halve_(eg_C); halve_(eg_D); } else { add_(eg_C,n); halve_(eg_C); sub_(eg_D,x); halve_(eg_D); } } if (!greater(eg_v,eg_u)) { //eg_v <= eg_u sub_(eg_u,eg_v); sub_(eg_A,eg_C); sub_(eg_B,eg_D); } else { //eg_v > eg_u sub_(eg_v,eg_u); sub_(eg_C,eg_A); sub_(eg_D,eg_B); } if (equalsInt(eg_u,0)) { if (negative(eg_C)) //make sure answer is nonnegative add_(eg_C,n); copy_(x,eg_C); if (!equalsInt(eg_v,1)) { //if GCD_(x,n)!=1, then there is no inverse copyInt_(x,0); return 0; } return 1; } } } //return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse function inverseModInt_(x,n) { var a=1,b=0,t; for (;;) { if (x==1) return a; if (x==0) return 0; b-=a*Math.floor(n/x); n%=x; if (n==1) return b; //to avoid negatives, change this b to n-b, and each -= to += if (n==0) return 0; a-=b*Math.floor(x/n); x%=n; } } //Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that: // v = GCD_(x,y) = a*x-b*y //The bigInts v, a, b, must have exactly as many elements as the larger of x and y. function eGCD_(x,y,v,a,b) { var g=0; var k=Math.max(x.length,y.length); if (eg_u.length!=k) { eg_u=new Array(k); eg_A=new Array(k); eg_B=new Array(k); eg_C=new Array(k); eg_D=new Array(k); } while(!(x[0]&1) && !(y[0]&1)) { //while x and y both even halve_(x); halve_(y); g++; } copy_(eg_u,x); copy_(v,y); copyInt_(eg_A,1); copyInt_(eg_B,0); copyInt_(eg_C,0); copyInt_(eg_D,1); for (;;) { while(!(eg_u[0]&1)) { //while u is even halve_(eg_u); if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if A==B==0 mod 2 halve_(eg_A); halve_(eg_B); } else { add_(eg_A,y); halve_(eg_A); sub_(eg_B,x); halve_(eg_B); } } while (!(v[0]&1)) { //while v is even halve_(v); if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if C==D==0 mod 2 halve_(eg_C); halve_(eg_D); } else { add_(eg_C,y); halve_(eg_C); sub_(eg_D,x); halve_(eg_D); } } if (!greater(v,eg_u)) { //v<=u sub_(eg_u,v); sub_(eg_A,eg_C); sub_(eg_B,eg_D); } else { //v>u sub_(v,eg_u); sub_(eg_C,eg_A); sub_(eg_D,eg_B); } if (equalsInt(eg_u,0)) { if (negative(eg_C)) { //make sure a (C)is nonnegative add_(eg_C,y); sub_(eg_D,x); } multInt_(eg_D,-1); ///make sure b (D) is nonnegative copy_(a,eg_C); copy_(b,eg_D); leftShift_(v,g); return; } } } //is bigInt x negative? function negative(x) { return ((x[x.length-1]>>(bpe-1))&1); } //is (x << (shift*bpe)) > y? //x and y are nonnegative bigInts //shift is a nonnegative integer function greaterShift(x,y,shift) { var kx=x.length, ky=y.length; k=((kx+shift)=0; i++) if (x[i]>0) return 1; //if there are nonzeros in x to the left of the first column of y, then x is bigger for (i=kx-1+shift; i0) return 0; //if there are nonzeros in y to the left of the first column of x, then x is not bigger for (i=k-1; i>=shift; i--) if (x[i-shift]>y[i]) return 1; else if (x[i-shift] y? (x and y both nonnegative) function greater(x,y) { var i; var k=(x.length=0;i--) if (x[i]>y[i]) return 1; else if (x[i]ky;kx--); //normalize: ensure the most significant element of y has its highest bit set b=y[ky-1]; for (a=0; b; a++) b>>=1; a=bpe-a; //a is how many bits to shift so that the high order bit of y is leftmost in its array element leftShift_(y,a); //multiply both by 1<=ky; i--) { if (r[i]==y[ky-1]) q[i-ky]=mask; else q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]); //The following for(;;) loop is equivalent to the commented while loop, //except that the uncommented version avoids overflow. //The commented loop comes from HAC, which assumes r[-1]==y[-1]==0 // while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2]) // q[i-ky]--; for (;;) { y2=(ky>1 ? y[ky-2] : 0)*q[i-ky]; c=y2>>bpe; y2=y2 & mask; y1=c+q[i-ky]*y[ky-1]; c=y1>>bpe; y1=y1 & mask; if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i]) q[i-ky]--; else break; } linCombShift_(r,y,-q[i-ky],i-ky); //r=r-q[i-ky]*leftShift_(y,i-ky) if (negative(r)) { addShift_(r,y,i-ky); //r=r+leftShift_(y,i-ky) q[i-ky]--; } } rightShift_(y,a); //undo the normalization step rightShift_(r,a); //undo the normalization step } //do carries and borrows so each element of the bigInt x fits in bpe bits. function carry_(x) { var i,k,c,b; k=x.length; c=0; for (i=0;i>bpe); c+=b*radix; } x[i]=c & mask; c=(c>>bpe)-b; } } //return x mod n for bigInt x and integer n. function modInt(x,n) { var i,c=0; for (i=x.length-1; i>=0; i--) c=(c*radix+x[i])%n; return c; } //convert the integer t into a bigInt with at least the given number of bits. //the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word) //Pad the array with leading zeros so that it has at least minSize elements. //There will always be at least one leading 0 element. function int2bigInt(t,bits,minSize) { var i,k; k=Math.ceil(bits/bpe)+1; k=minSize>k ? minSize : k; buff=new Array(k); copyInt_(buff,t); return buff; } //return the bigInt given a string representation in a given base. //Pad the array with leading zeros so that it has at least minSize elements. //If base=-1, then it reads in a space-separated list of array elements in decimal. //The array will always have at least one leading zero, unless base=-1. function str2bigInt(s,base,minSize) { var d, i, j, x, y, kk; var k=s.length; if (base==-1) { //comma-separated list of array elements in decimal x=new Array(0); for (;;) { y=new Array(x.length+1); for (i=0;i=36) //convert lowercase to uppercase if base<=36 d-=26; if (d=0) { //ignore illegal characters multInt_(x,base); addInt_(x,d); } } for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros k=minSize>k+1 ? minSize : k+1; y=new Array(k); kk=ky.length) { for (;i0;i--) s+=x[i]+','; s+=x[0]; } else { //return it in the given base while (!isZero(s6)) { t=divInt_(s6,base); //t=s6 % base; s6=floor(s6/base); s=digitsStr.substring(t,t+1)+s; } } if (s.length==0) s="0"; return s; } //returns a duplicate of bigInt x function dup(x) { var i; buff=new Array(x.length); copy_(buff,x); return buff; } //do x=y on bigInts x and y. x must be an array at least as big as y (not counting the leading zeros in y). function copy_(x,y) { var i; var k=x.length>=bpe; } } //do x=x+n where x is a bigInt and n is an integer. //x must be large enough to hold the result. function addInt_(x,n) { var i,k,c,b; x[0]+=n; k=x.length; c=0; for (i=0;i>bpe); c+=b*radix; } x[i]=c & mask; c=(c>>bpe)-b; if (!c) return; //stop carrying as soon as the carry_ is zero } } //right shift bigInt x by n bits. 0 <= n < bpe. function rightShift_(x,n) { var i; var k=Math.floor(n/bpe); if (k) { for (i=0;i>n)); } x[i]>>=n; } //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement function halve_(x) { var i; for (i=0;i>1)); } x[i]=(x[i]>>1) | (x[i] & (radix>>1)); //most significant bit stays the same } //left shift bigInt x by n bits. function leftShift_(x,n) { var i; var k=Math.floor(n/bpe); if (k) { for (i=x.length; i>=k; i--) //left shift x by k elements x[i]=x[i-k]; for (;i>=0;i--) x[i]=0; n%=bpe; } if (!n) return; for (i=x.length-1;i>0;i--) { x[i]=mask & ((x[i]<>(bpe-n))); } x[i]=mask & (x[i]<>bpe); c+=b*radix; } x[i]=c & mask; c=(c>>bpe)-b; } } //do x=floor(x/n) for bigInt x and integer n, and return the remainder function divInt_(x,n) { var i,r=0,s; for (i=x.length-1;i>=0;i--) { s=r*radix+x[i]; x[i]=Math.floor(s/n); r=s%n; } return r; } //do the linear combination x=a*x+b*y for bigInts x and y, and integers a and b. //x must be large enough to hold the answer. function linComb_(x,y,a,b) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;i>=bpe; } } //do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys. //x must be large enough to hold the answer. function linCombShift_(x,y,b,ys) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;c && i>=bpe; } } //do x=x+(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys. //x must be large enough to hold the answer. function addShift_(x,y,ys) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;c && i>=bpe; } } //do x=x-(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys. //x must be large enough to hold the answer. function subShift_(x,y,ys) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;c && i>=bpe; } } //do x=x-y for bigInts x and y. //x must be large enough to hold the answer. //negative answers will be 2s complement function sub_(x,y) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;c && i>=bpe; } } //do x=x+y for bigInts x and y. //x must be large enough to hold the answer. function add_(x,y) { var i,c,k,kk; k=x.length>=bpe; } for (i=k;c && i>=bpe; } } //do x=x*y for bigInts x and y. This is faster when y0 && !x[kx-1]; kx--); //ignore leading zeros in x k=kx>n.length ? 2*kx : 2*n.length; //k=# elements in the product, which is twice the elements in the larger of x and n if (s0.length!=k) s0=new Array(k); copyInt_(s0,0); for (i=0;i>=bpe; for (j=i+1;j>=bpe; } s0[i+kx]=c; } mod_(s0,n); copy_(x,s0); } //return x with exactly k leading zero elements function trim(x,k) { var i,y; for (i=x.length; i>0 && !x[i-1]; i--); y=new Array(i+k); copy_(y,x); return y; } //do x=x**y mod n, where x,y,n are bigInts and ** is exponentiation. 0**0=1. //this is faster when n is odd. x usually needs to have as many elements as n. function powMod_(x,y,n) { var k1,k2,kn,np; if(s7.length!=n.length) s7=dup(n); //for even modulus, use a simple square-and-multiply algorithm, //rather than using the more complex Montgomery algorithm. if ((n[0]&1)==0) { copy_(s7,x); copyInt_(x,1); while(!equalsInt(y,0)) { if (y[0]&1) multMod_(x,s7,n); divInt_(y,2); squareMod_(s7,n); } return; } //calculate np from n for the Montgomery multiplications copyInt_(s7,0); for (kn=n.length;kn>0 && !n[kn-1];kn--); np=radix-inverseModInt_(modInt(n,radix),radix); s7[kn]=1; multMod_(x ,s7,n); // x = x * 2**(kn*bp) mod n if (s3.length!=x.length) s3=dup(x); else copy_(s3,x); for (k1=y.length-1;k1>0 & !y[k1]; k1--); //k1=first nonzero element of y if (y[k1]==0) { //anything to the 0th power is 1 copyInt_(x,1); return; } for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1); //k2=position of first 1 bit in y[k1] for (;;) { if (!(k2>>=1)) { //look at next bit of y k1--; if (k1<0) { mont_(x,one,n,np); return; } k2=1<<(bpe-1); } mont_(x,x,n,np); if (k2 & y[k1]) //if next bit is a 1 mont_(x,s3,n,np); } } //do x=x*y*Ri mod n for bigInts x,y,n, // where Ri = 2**(-kn*bpe) mod n, and kn is the // number of elements in the n array, not // counting leading zeros. //x must be large enough to hold the answer. //It's OK if x and y are the same variable. //must have: // x,y < n // n is odd // np = -(n^(-1)) mod radix function mont_(x,y,n,np) { var i,j,c,ui,t; var kn=n.length; var ky=y.length; if (sa.length!=kn) sa=new Array(kn); for (;kn>0 && n[kn-1]==0;kn--); //ignore leading zeros of n //this function sometimes gives wrong answers when the next line is uncommented //for (;ky>0 && y[ky-1]==0;ky--); //ignore leading zeros of y copyInt_(sa,0); //the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large keys for (i=0; i> bpe; t=x[i]; //do sa=(sa+x[i]*y+ui*n)/b where b=2**bpe for (j=1;j>=bpe; } for (;j>=bpe; } sa[j-1]=c & mask; } if (!greater(n,sa)) sub_(sa,n); copy_(x,sa); } HaCi/html/HaCi.css0000644000175000000000000001713112451674756013350 0ustar fighterroothtml { height: 100%; } body { color: #000000; background-color: #FFFFFF; font-size: 100.01%; font-family: serif; /* Eigentlich Moronic Misfire (BRK) */ margin: 0; padding: 1em 1em 0 1em; text-align: center; /* Zentrierung im Internet Explorer */ line-height:1.1; /* Bad Hack for IE (text unvisible)*/ height: 96% } a:link { color:#5555cc; } a:visited { color:#5555cc; } form { padding:0em; display:inline; } #root{ border:1px solid #000000; height:97%; padding:0; margin:0; } #menu{ float:left; background-color:#DDDDFF; margin: 0; padding-bottom:1em; width:14em; color:#000000; } #main{ margin:0 0 0 14em; padding:1em; background-color:#EEEEFF; text-align:center; height:95%; color:#000000; } #footer{ margin:0; padding:0; background-color:#000000; color:#FFFFFF; font-family:serif; width:100%; font-size:0.9em; text-align:right; vertical-align:bottom; clear:both; } #subFooter{ margin-bottom:0; padding:0; font-size:0.9em; } #subFooter img{ height:0.9em; border:0; margin:0.3em 0 0 0; } #statusField{ margin:0; padding-left:0.5em; background-color:#000000; color:#FFFF00; text-align:left; font-family:serif; float:left; padding:0; } #statusType{ background-color:#000000; font-size:0.8em; color:#000000; } #statusContent{ background-color:#000000; color:#FFFF00; } #floatbox{ border:1px solid #000000; border-right: 5px; border-right-color: #666666; border-right-style: outset; border-bottom: 5px; border-bottom-color: #666666; border-bottom-style: outset; } #box{ border:1px solid #000000; background-color:#DDDDFF; margin:0 auto; padding:0; height:auto; color:#000000; } #boxT{ background-color:#DDDDFF; margin:0 auto; padding:0; height:auto; color:#000000; } #boxHeader{ border-bottom:1px solid #FF0000; font-size:0.8em; margin:0; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; } #boxEntry{ font-size:0.8em; margin:0.2em; min-height:1.6em; border: 0px solid #ff0000; } #boxKey{ width:15em; margin:0; padding:0.2em; background-color:#BBBBFF; text-align:right; font-weight:bold; white-space:nowrap; } #boxValue{ margin:0 0 0 16em; padding:0.2em; background-color:#DDDDFF; text-align:left; overflow:visible; white-space:nowrap; } #boxHLine{ width:100%; height:3px; background-color:#AAAAFF; } #boxVLine{ margin:0; } .boxButton{ cursor:pointer; display:inline; background-color:#BBBBFF; padding:0.3em 0.3em 0.5em 0.3em; border-style:outset; border-width:1px; vertical-align:middle; text-align:center; white-space:nowrap; -moz-border-radius:8px; font-size:0.9em; width:16px; } div.boxButton{ margin:1em; } div.boxButton:active{ border-style:inset; border-width:1px; } .boxButtonDisabled{ cursor:default; display:inline; background-color:#CCCCCC; padding:0.6em 0.3em; margin:0.2em; white-space:nowrap; border-style:outset; border-width:1px; vertical-align:middle; text-align:center; -moz-border-radius:8px; font-size:0.9em; width:16px; } #boxButtonImage{ margin-right:0.2em; border-style:none; height:16px; width:16px; vertical-align:middle; } #box input{ margin:0; padding:0; font-size:1em; height:1.6em; vertical-align:middle; } #box select{ font-size:1em; /*height:1.6em;*/ } #box table{ font-size:0.8em; margin:0.2em auto; } #box th{ background-color:#AAAAFF; padding:0.2em; } #box td{ padding:0.2em 0.6em; white-space:nowrap; } #box a{ white-space:nowrap; color:#0000FF; text-decoration:none; } #menuBox{ padding:1.5em 0 0 0; font-size:0.7em; margin:auto; width:15em; } #menuBoxHeader{ border-bottom:1px solid #FF0000; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; white-space:nowrap; } #menuBoxBody{ padding:0.2em; background-color:#AAAAFF; text-align:left; } #menuBoxPic{ float:left; height:24px; width:24px; padding-right:0.4em; } #menuBoxLink{ margin-top:0.5em; padding-left:0.5em; height:1.6em; vertical-align:middle; } #menuBoxLink a{ color:#0000FF; text-decoration:none; } #menuBoxEntry{ margin-top:0.4em; padding-left:2.5em; } #menuBoxUsername{ color:#FF0000; } #loginRoot{ background-color:#EEEEFF; width:100%; height:100%; } #loginTop{ padding:1em; font-size:3em; font-weight:bold; text-align:center; vertical-align:middle; } #loginTitle{ } #logo { margin-top:1em; height:60px; width:72px; } #loginLogo{ height:100px; width:100px; } #loginBox { float:left; margin:1.5em 1.5em auto; border:1pt solid #000000; font-size:1em; color:#000000; } #loginHeader { border-bottom:1px solid #FF0000; margin:0; background-color:#555599; color:#EEEEFF; text-align:center; font-weight:bold; padding:0.2em; } #loginKey { margin:0; float:left; width:8em; padding:0.4em; } #loginValue { margin:0; padding:0.2em 0; width:8em; float:left; } #loginValue input { background-color:#CCFFCC; height:1.8em; width:8em; font-size:0.8em; } #loginLine{ width:100%; height:2px; background-color:#000000; } #loginSubmit{ background-color:#EEEEFF; margin:0.3em 0 0.3em 0; height:24px; } #authError{ border:1pt solid #FF0000; margin: 2em auto; width:550px; } #authErrorText{ color:#FF5555; text-align:center; font-size:10pt; font-weight:bold; } #treeBox{ margin:0em; padding:1em; background-color:#CCCCFF; color:#000000; } .tE{ font-size:0.7em; text-align:left; white-space:nowrap; } .tdS{ color:#777777; } a.tE{ text-decoration:none; float:left; margin:0.2em; } img.tE{ margin-left:0.2em; padding:0.2em; border:0px; height:10px; width:10px; display:inline; } input.tE{ margin:0.3em 0.4em; border:0px; height:10px; width:10px; float:left; } span.tE{ text-decoration:none; float:left; margin:0em; } img.tES{ padding:0em; border:0px; height:17px; width:17px; display:inline; } a.tE:hover{ cursor:pointer; } img.tE1{ padding:0em; border:0px; height:10px; width:10px; display:inline; } img.tE2{ padding:0em; border:0px; height:10px; display:inline; } img.tE3{ padding:0em; border:0px; height:12px; margin-top:0.2em; display:inline; cursor:pointer; } img.tE3Disabled{ padding:0em; border:0px; height:12px; margin-top:0.2em; display:inline; } #tS{ padding:0.2em 0 0.2em 0; height:1em; float:left; } #tSFW{ width:18px; padding:0.2em 0 0.2em 0; height:1em; float:left; } .tLR{ } a.tLR{ text-decoration:none; font-weight:bold; } .tL{ float:left; width:15em; margin-top:0.3em; } .tL6{ float:left; width:25.2em; margin-top:0.3em; } .tLN{ font-family: sans-serif; } a.tLN{ text-decoration:none; cursor:pointer; } #tD{ float:left; margin:0.2em; margin-right:1em; } #container{ height:100%; overflow:auto; } #pageHeader{ border-bottom:1px solid #FF0000; font-size:1.5em; font-weight:bold; margin:0; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; } #bgbarTable{ border: solid 1px #000000; width:100%; } #pgbar{ height:0.8em; background-color:#000000; } #aboutVersion{ font-size:1.5em; color:#FF0000; } #about{ font-size:1.0em; border:3px ridge #5555cc; margin:0 auto; width:46em; } #descrHelper{ margin:0; padding:0; position:absolute; display:none; } #descrHelper:hover{ cursor:move; } #warnl{ margin:0; padding:0; position:absolute; display:none; top:100px; left:350px; } #warnlContent { max-height: 300px !important; overflow:auto !important; } #warnl:hover{ cursor:move; } #statusHelper{ margin:0; padding:0; position:absolute; display:none; } #statusHelper:hover{ cursor:move; } #pluginInfo{ position:absolute; width:20em; display:none; } #pluginInfo:hover{ cursor:move; } #floatingPopup{ position:absolute; width:20em; display:none; } #floatingPopup:hover{ cursor:move; } #floatingPopupContent{ } #boxBody { } HaCi/html/HaCi.js0000644000175000000000000005413012470146636013164 0ustar fighterrootvar origNetaddress = ''; var maxTmplPositions = 0; var ipv6 = 0; var statID = 0; var objDrag = null; var mouseX = 0; var mouseY = 0; var offX = 0; var offY = 0; var maxSubnetSize = 8; IE = document.all&&!window.opera; DOM = document.getElementById&&!IE; function init(){ document.onmousemove = doDrag; document.onmouseup = stopDrag; } function updateDefSubnetCidr () { var rootID = document.getElementById('rootID').value; var ipv6 = rootID2Ver[rootID]; if (document.getElementById('conf_maxSubnetSize')) maxSubnetSize = parseInt(document.getElementById('conf_maxSubnetSize').value); var cidr = parseInt(document.getElementById('cidr').value); var subnetSizeSel = document.getElementById('defSubnetSize'); var subnetSizeLength = subnetSizeSel.length; var defSubnetSizeSelIndex = subnetSizeSel.selectedIndex; var defSubnetSizeSelVal = subnetSizeSel[defSubnetSizeSelIndex].value; for (i = 1; i < subnetSizeLength; i++) { subnetSizeSel[1] = null; } if (isNaN(cidr)) return; var startVal = (cidr + 1); var endVal = (startVal + (maxSubnetSize - 1)); if (ipv6 == 1) { if (cidr >= 128) return if (endVal > 128) endVal = 128 } else { if (cidr >= 32) return if (endVal > 32) endVal = 32 } var u = 1; for (i = startVal; i <= endVal; i++) { newEntry = new Option(i, i, false, false); subnetSizeSel[u] = newEntry; if (newEntry.value == defSubnetSizeSelVal) subnetSizeSel.selectedIndex = u; u++; } } function checkIfIPv6 (rootID, source) { var rootID = document.getElementById('rootID').value; ipv6 = rootID2Ver[rootID]; if (source == 'TREE') { var jumpToTB = document.getElementById('jumpTo'); if (ipv6 == 1) { jumpToTB.size = 39; jumpToTB.maxlength = 39; } else { jumpToTB.size = 18; jumpToTB.maxlength = 18; } } else if (source == 'ADDNET') { var netmaskBlock = document.getElementsByName("netmaskBlock")[0]; updateDefSubnetCidr(); if (ipv6 == 1) { if (netmaskBlock) { netmaskBlock.style.display = 'none'; } else { document.getElementById('netmask').disabled = true; document.getElementById('netmask').value = ''; } document.getElementsByName('netaddress')[0].size = 43; document.getElementsByName('netaddress')[0].maxLength = 43; document.getElementsByName('cidr')[0].size = 3; document.getElementsByName('cidr')[0].maxLength = 3; } else { if (netmaskBlock) { netmaskBlock.style.display = 'table-row'; } else { document.getElementById('netmask').disabled = false; } document.getElementsByName('netaddress')[0].size = 18; document.getElementsByName('netaddress')[0].maxLength = 18; document.getElementsByName('cidr')[0].size = 2; document.getElementsByName('cidr')[0].maxLength = 2; } } } function setFocus (formElement) { var element = document.getElementById(formElement) if (element) { element.focus(); } } function setCIDR (netmask, target_cidr, target_netaddress, target_netmask) { if (ipv6 == 1) { document.getElementById(target_cidr).value = netmask; // netmask = cidr if ipv6 updateDefSubnetCidr(); return; } var splits = netmask.split("."); var cidr = 0; var newNetmask = ''; for (var i = 0; i < 4;i++) { if (splits[i] > 255) { splits[i] = 255; } if (splits[i] < 0) { splits[i] = 0; } if (newNetmask != '') newNetmask = newNetmask + '.'; newNetmask = newNetmask + splits[i]; } if (netmask != newNetmask) document.getElementById(target_netmask).value = newNetmask; netmask = newNetmask; var splits = netmask.split("."); for (var i = (splits.length - 1); i >= 0; --i) { var a = Math.log(256 - splits[i])/Math.log(2); cidr = (cidr + a); } cidr = 32 - cidr; document.getElementById(target_cidr).value = cidr; document.getElementById(target_netaddress).value = getNetaddress(document.getElementById(target_netaddress).value, netmask); updateDefSubnetCidr(); return cidr; } function setNetmask (cidr, target_netmask, target_netaddress, target_cidr) { if (cidr < 0) { cidr = 0; document.getElementById(target_cidr).value = cidr; } if (ipv6 == 1) { if (cidr > 128) { cidr = 128; document.getElementById(target_cidr).value = cidr; } document.getElementById(target_netaddress).value = getNetaddress(document.getElementById(target_netaddress).value, cidr); updateDefSubnetCidr(); return; } var netmask = ''; if (cidr > 32) { cidr = 32; document.getElementById(target_cidr).value = cidr; } for (var i=0; i<4; i++) { var netmaskt = 0; if (cidr > 8) { netmaskt = 8; cidr = cidr - 8; } else { netmaskt = cidr; cidr = 0; } netmaskt = 256 - Math.pow(2, (8 - netmaskt)); if (netmask != '') netmask = netmask + '.'; netmask = netmask + netmaskt; } document.getElementById(target_netmask).value = netmask; document.getElementById(target_netaddress).value = getNetaddress(document.getElementById(target_netaddress).value, netmask); updateDefSubnetCidr(); return netmask; } function setnetmask_cidr (network, target_netmask, target_cidr, target_netaddress) { var splits = network.split('/'); if (splits[1] == null || splits[1] == '') { if (ipv6 == 1) { splits[1] = 128 } else { splits[1] = 32 } } console.log(splits); if (ipv6 == 1) { splits[0] = adjustIPv6(splits[0]); if (splits[1]) { setCIDR(splits[1], target_cidr); document.getElementById(target_netaddress).value = getNetaddress(splits[0], splits[1], 1); } else { document.getElementById(target_netaddress).value = splits[0]; } return; } var netmask = ''; if (splits[1]) { netmask = setNetmask(splits[1], target_netmask, target_netaddress); setCIDR(netmask, target_cidr, target_netaddress); document.getElementById(target_netaddress).value = getNetaddress(splits[0], netmask, 1); } } function pow (x, y) { if (y < 0) { return x; } if (y == 0) { return int2bigInt(1, 32, 0); } if (y == 1) { return x; } var z = dup(x); for (var i=2; i<=y; i++) { x = mult(x, z); } return x; } function ipv62Dec (ipv6) { var ipSplits = ipv6.split(':'); var ipv6Dec = int2bigInt(0, 128, 0); var base = int2bigInt(65536, 128, 0); for (var i=0; i 0 && i<(ipSplits.length - 1)) { for (var u=ipSplits.length; u<8; u++) { split += ':0'; } } } if (ipv6Adjusted != '') ipv6Adjusted += ':'; ipv6Adjusted += split; } return ipv6Adjusted; } function getNetaddress (ipaddress, netmask, overwrite) { var splits = ipaddress.split('/'); ipaddress = splits[0]; if (origNetaddress == '') origNetaddress = ipaddress; if (overwrite == 1) origNetaddress = ipaddress; ipaddress = origNetaddress; if (ipv6 == 1) { ipaddress = adjustIPv6(ipaddress); var cidr = netmask; // if ipv6 var zero = int2bigInt(0, 32, 0); var two = int2bigInt(2, 32, 0); var netmask = pow(two, (128 - cidr)); var ipv6Dec = ipv62Dec(ipaddress); var tooMuch = mod(ipv6Dec, netmask); ipv6Dec = sub(ipv6Dec, tooMuch); var ipv6Address = ipv6Dec2ip(ipv6Dec); return ipv6Address; } var ipSplits = ipaddress.split('.'); var maskSplits = netmask.split('.'); var netaddress = ''; for (var i=0; i<4; i++) { var ips = 256 - maskSplits[i]; if (netaddress != '') netaddress += '.'; if (ips == 0) { netaddress += ipSplits[i]; } else { netaddress += ips * Math.floor(ipSplits[i] / ips); } } return netaddress; } function dec2ip (dec) { return dec / Math.pow(256, 3) + '.' + dec % Math.pow(256, 3) / Math.pow(256, 2) + '.' + dec % Math.pow(256, 2) / 256 + '.' + dec % 256 } function updTmplParamsFromPreview (ID, type, pos) { var noCheckPos = 0; if (ID == undefined) { noCheckPos = 1; pos = document.getElementById('position').value; if (pos == maxTmplPositions) return; var IDFull = document.getElementById('tmplEntryPos2ID_' + pos).value; type = [document.getElementsByName(IDFull)[0].title]; type = type * 1; ID = IDFull.split('_')[1]; } document.getElementById('TmplEntryParamDescr').disabled = ''; document.getElementById('TmplEntryParamSize').disabled = 'disabled'; document.getElementById('TmplEntryParamEntries').disabled = 'disabled'; document.getElementById('TmplEntryParamRows').disabled = 'disabled'; document.getElementById('TmplEntryParamCols').disabled = 'disabled'; document.getElementById('TmplEntryParamDescr').value = ''; document.getElementById('TmplEntryParamSize').value = ''; document.getElementById('TmplEntryParamEntries').value = ''; document.getElementById('TmplEntryParamRows').value = ''; document.getElementById('TmplEntryParamCols').value = ''; document.getElementById('position').value = pos; if (noCheckPos == 0) chkTmplPosition(); document.getElementById('TmplEntryType').selectedIndex = type; document.getElementById('tmplEntryID').value = ID; switch (type) { case 0: document.getElementById('TmplEntryParamDescr').disabled = 'disabled'; break; case 1: document.getElementById('TmplEntryParamDescr').value = document.getElementById('tmplEntryDescrID_' + ID).value; document.getElementById('TmplEntryParamSize').disabled = ''; document.getElementById('TmplEntryParamSize').value = document.getElementById('tmplEntryID_' + ID).size; break; case 2: document.getElementById('TmplEntryParamDescr').value = document.getElementById('tmplEntryDescrID_' + ID).value; document.getElementById('TmplEntryParamRows').disabled = ''; document.getElementById('TmplEntryParamRows').value = document.getElementById('tmplEntryID_' + ID).rows; document.getElementById('TmplEntryParamCols').disabled = ''; document.getElementById('TmplEntryParamCols').value = document.getElementById('tmplEntryID_' + ID).cols; break; case 3: var optionStr = ''; for (i = 0; i < document.getElementsByTagName("select").length; i++) { var selectName = document.getElementsByTagName("select")[i].name; if (selectName == 'tmplEntryID_' + ID) { var options = document.getElementsByTagName("select")[i].options; for (u = 0; u < options.length; u++) { if (options[u] != undefined) { var optionValue = options[u].value; if (optionStr != '') optionStr += ';'; optionStr += optionValue; } } break; } } document.getElementById('TmplEntryParamDescr').value = document.getElementById('tmplEntryDescrID_' + ID).value; document.getElementById('TmplEntryParamEntries').disabled = ''; document.getElementById('TmplEntryParamEntries').value = optionStr; break; case 4: document.getElementById('TmplEntryParamDescr').value = document.getElementById('tmplEntryDescrID_' + ID).value; break; } } function updTmplParams () { document.getElementById('TmplEntryParamDescr').disabled = ''; document.getElementById('TmplEntryParamSize').disabled = 'disabled'; document.getElementById('TmplEntryParamEntries').disabled = 'disabled'; document.getElementById('TmplEntryParamRows').disabled = 'disabled'; document.getElementById('TmplEntryParamCols').disabled = 'disabled'; var entryType = document.getElementById('TmplEntryType'); switch (entryType.value) { case "0": document.getElementById('TmplEntryParamDescr').disabled = 'disabled'; break; case "1": document.getElementById('TmplEntryParamSize').disabled = ''; break; case "2": document.getElementById('TmplEntryParamRows').disabled = ''; document.getElementById('TmplEntryParamCols').disabled = ''; break; case "3": document.getElementById('TmplEntryParamEntries').disabled = ''; break; } } function setACLs (id, acl) { switch (acl) { case "r": if (document.getElementById('accGroup_r_' + id).checked == false) { document.getElementById('accGroup_w_' + id).checked = false; } break; case "w": if (document.getElementById('accGroup_w_' + id).checked == true) { document.getElementById('accGroup_r_' + id).checked = true; } break; } } function setAllACLs (source, acl) { var sourceChecked = document.getElementById(source).checked; var regex = new RegExp('^accGroup_' + acl + '_'); var i = 0; var inputs = document.getElementsByTagName('input'); for (i = 0; i < inputs.length; ++i) { if (!inputs[i].name.match(regex)) continue; var index = inputs[i].name.replace(regex, ''); inputs[i].checked = sourceChecked; setACLs(index, acl); } switch (acl) { case 'r': if (!sourceChecked) { document.getElementById('checkAll_write').checked = false; } break; case 'w': if (sourceChecked) { document.getElementById('checkAll_read').checked = true; } break; } } function chkTmplPosition (max) { if (max != undefined) { maxTmplPositions = max; } else { max = maxTmplPositions; } var pos = document.getElementById('position').value; if (pos >= max) { document.getElementById('submitEditTmplEntry').className = 'boxButtonDisabled'; document.getElementById('submitEditTmplEntry').disabled = 1; document.getElementById('submitDeleteTmplEntry').className = 'boxButtonDisabled'; document.getElementById('submitDeleteTmplEntry').disabled = 1; } else { document.getElementById('submitEditTmplEntry').className = 'boxButton'; document.getElementById('submitEditTmplEntry').disabled = 0; document.getElementById('submitDeleteTmplEntry').className = 'boxButton'; document.getElementById('submitDeleteTmplEntry').disabled = 0; } } function implButton (id, name, value, formName, buttonID, type) { if (document.getElementById(buttonID)) { if (document.getElementById(buttonID).disabled == 1) { return; } } var el = document.getElementById(id); if (el != undefined) { el.name = name; el.value = value; if (type == 'submit') { document[formName].submit(); } } } function callAjaxFunction (funcName, paramIDs, target) { var attrStr = ''; if (target == undefined) target = 'statusContent'; for (i=0; i modules/HaCi/XMLRPC.pm

Class XMLRPC

This class provides a XML-RPC API for HaCi.

Basic usage:

        my $api         = new Frontier::Client(url => "http://$server/RPC2");
        my $session     = $api->call('login', [$user, $pass]);
        die 'Login failed!' unless $session;
        print Dumper($api->call($method, $session, @args));
        $api->call('logout', $session);
        exit 0;
login()

Login and return your session

 Params
 Returns
    - session        [string]    session token you can use for authentication
logout()

Logout

 Params
 Returns
search()

Search networks

 Params
    - search          [string]  string to search for
    - state           [string]  filter the result by that network state (ALLOCATED PA, ASSIGNED, ...)
    - exact           [string]  don't do a substring search
    - template name   [string]  filter the result by that template name
    - template query  [hash]    filter the result by defining special values for template entries (i.e.: {name => 'POOL1'})
    - root name       [string]  limit the search in that root
    - nrOfFreeSubs    [boolean] compute the number of free subnets in each found network
    - withDetails     [boolean] show details for found networks
    - tags            [string]  return only networks with this tags, seperate with spaces (i.e.: 'OR foo bar', 'AND foo bar')
 Returns
    - networks        [array]    array of found network blocks (hash)
                                 - netID
                                 - network
                                 - rootName
                                 - description
                                 - state
                                 - nrOfFreeSubs
 Example
    my $networks = search('Test', 'ASSIGNED', 0, 'DSL-POOL', {name => 'Pool1'}, 'DSL-Test-Pool-Root', 0);
getFreeSubnets()

Find free subnets in a specified root and supernet and return the wanted amount of subnets

 Params
    - root name       [string]  search free subnets in that root
    - supernet        [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120)
    - cidr            [integer] specify the target cidr of your subnets (e.g. 30)
    - amount          [integer] how many subnets you want to get
 Returns
    - networks        [array]   array of free subnets found (hash)
                                - network
 Example
    my $freeSubnets = getFreeSubnets('Test root', '10.184.120.0/23', 32, 1);
getFreeSubnetsFromSearch()

Search networks and return free subnets from them

 Params
    - search          [string]  string to search for
    - state           [string]  filter the result by that network state (ALLOCATED PA, ASSIGNED, ...)
    - exact           [string]  don't do a substring search
    - template name   [string]  filter the result by that template name
    - template query  [hash]    filter the result by defining special values for template entries (i.e.: {name => 'POOL1'})
    - root name       [string]  limit the search in that root
    - cidr            [integer] specify the target cidr of your subnets (e.g. 30)
    - amount          [integer] how many subnets you want to get
 Returns
    - networks        [array]   array of free subnets found (hash)
                                - network
                                - root name
 Example
    my $freeSubnets = getFreeSubnetsFromSearch('search me', '', 1, '', {}, 'Test-Root', 29, 3);
listRoots()

Add a root to HaCi

 Params
 Returns
    - roots        [array]    array of found roots (hash)
                                 - ID
                                 - name
                                 - ipv6 (boolean)
addRoot()

Add a root to HaCi

 Params
    - root name       [string]  add a root with this name
    - description     [string]  description of the root
    - ipv6            [boolean] this root contains IPv6 networks
 Returns
    - success         [string] 0 on success, error-String at error
editRoot()

Edit an existing root

 Params
    - root name      [string]  edit this root
    - new root name  [string]  change root name
    - description    [string]  new description of the root
 Returns
    - success         [string] 0 on success, error-String at error
delRoot()

Delete a root from HaCi

 Params
    - root name       [string]  remove the root
 Returns
    - success         [string] 0 on success, error-String at error
addNet()

Add a network to HaCi

 Params
    - root name       [string]  add the network to this root
    - network         [network] add this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125)
    - description     [string]  description of the network
    - state           [string]  state of the network (e.g. ALLOCATED PA, ASSIGNED, ...)
    - def subnet Size [integer] define a default subnet cidr size (e.g. 30)
    - template name   [string]  assign a template to the network
    - template values [hash]    pass template values (e.g. {hostname => 'abc.de', os => 'redhat'})
    - tags                                              [string]  add tags (seperate by comma)
 Returns
    - success         [string] 0 on success, error-String at error
editNet()

Edit an existing network

 Params
    - root name   [string]  edit the network in this root
    - network     [network] edit this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125)
    - changes     [hash]    changes (valid attributes are: network description state rootName defSubnetSize tmplName tmplValues)
                            e.g.:
                             {description => 'test_8'}
                             {network => '192.168.0.8/29', defSubnetSize=>30, state=>'FREE', rootName=>'larsux.de'}
 Returns
    - success         [string] 0 on success, error-String at error
delNet()

Delete a network from HaCi

 Params
    - root name       [string]  remove the netork from this root
    - network         [network] remove this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125)
    - network lock    [integer] lock network for X seconds
    - withSubnets     [boolean] also remve subnets
 Returns
    - success         [string] 0 on success, error-String at error
assignFreeSubnet()

Find the next free subnet and assign it in one step

 Params
    - root name       [string]  search free subnets in that root
    - supernet        [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120)
    - cidr            [integer] specify the target cidr of your subnets (e.g. 30)
    - description     [string]  description of the network
    - state           [string]  state of the network (e.g. ALLOCATED PA, ASSIGNED, ...)
    - def subnet Size [integer] define a default subnet cidr size (e.g. 30)
    - template name   [string]  assign a template to the network
    - template values [hash]    pass template values (e.g. {hostname => 'abc.de', os => 'redhat'})
 Returns
    - network details [hash]    new network assigned:
                                - netID
                                - network
                                - modify date
                                - ipv6 (boolean)
                                - description
                                - state
                                - create from
                                - create date
                                - default subnet cidr size
                                - template name
                                - modify from
getNetworkDetails()

Get details from a network in HaCi

 Params
    - root name       [string]  get details of a network in this root
    - network         [network] get details of this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125)
 Returns
    - network         [hash]    network details:
                                - netID
                                - network
                                - modify date
                                - ipv6 (boolean)
                                - description
                                - state
                                - create from
                                - create date
                                - default subnet cidr size
                                - template name
                                - modify from
getSubnets()

Get subnets from a root and optional from a supernet

 Params
    - root name       [string]  get subnets from this root
    - supernet        [network] get subnets from this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125)
 Returns
    - networks        [array]    array of found networks (hash)
                                 - netID
                                 - network
                                 - modify date
                                 - ipv6 (boolean)
                                 - description
                                 - state
                                 - create from
                                 - create date
                                 - default subnet cidr size
                                 - template name
                                 - modify from
 Example
    my $subnets = getSubnets('Test');
    my $subnets = getSubnets('Test', '192.168.0.0/24');
HaCi/html/HaCi_black.css0000644000175000000000000001704312451674756014506 0ustar fighterroothtml { height: 100%; } body { color: #FFFFFF; background-color: #000000; font-size: 100.01%; font-family: serif; /* Eigentlich Moronic Misfire (BRK) */ margin: 0; padding: 1em 1em 0 1em; text-align: center; /* Zentrierung im Internet Explorer */ line-height:1.1; /* Bad Hack for IE (text unvisible)*/ height: 96%; } a:link{ color:#5555cc; } a:visited{ color:#5555cc; } form { padding:0em; display:inline; } #root{ border:1px solid #000000; height:97%; padding:0; margin:0; } #menu{ float:left; background-color:#000000; margin: 0; padding-bottom:1em; width:14em; border-right:1px solid #FF0000; } #main{ margin:0 0 0 14em; padding:1em; background-color:#000000; text-align:center; height:95%; } #footer{ margin:0; padding:0; background-color:#111111; color:#FFFFFF; font-family:serif; width:100%; font-size:0.9em; text-align:right; vertical-align:bottom; clear:both; } #subFooter{ margin-bottom:0; padding:0; font-size:0.9em; } #subFooter img{ height:0.9em; border:0; margin:0.3em 0 0 0; } #statusField{ margin:0; padding-left:0.5em; background-color:#111111; color:#FFFF00; text-align:left; font-family:serif; float:left; padding:0; } #statusType{ color:#111111; font-size:0.8em; } #statusContent{ color:#FFFF00; } #floatbox{ border:1px solid #DDDDFF; border-right: 5px; border-right-color: #666666; border-right-style: outset; border-bottom: 5px; border-bottom-color: #666666; border-bottom-style: outset; } #box{ border:1px solid #DDDDFF; background-color:#DDDDFF; margin:0 auto; padding:0; height:auto; color:#000000; } #boxT{ background-color:#DDDDFF; margin:0 auto; padding:0; height:auto; color:#000000; } #boxHeader{ border-bottom:1px solid #FF0000; font-size:0.8em; margin:0; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; } #boxEntry{ font-size:0.8em; margin:0.2em; min-height:1.6em; border: 0px solid #ff0000; } #boxKey{ width:15em; margin:0; padding:0.2em; background-color:#BBBBFF; text-align:right; font-weight:bold; white-space:nowrap; } #boxValue{ margin:0 0 0 16em; padding:0.2em; background-color:#DDDDFF; text-align:left; overflow:visible; white-space:nowrap; } #boxHLine{ width:100%; height:3px; background-color:#AAAAFF; } #boxVLine{ margin:0; } .boxButton{ cursor:pointer; display:inline; background-color:#BBBBFF; padding:0.3em 0.3em 0.5em 0.3em; border-style:outset; border-width:1px; vertical-align:middle; text-align:center; white-space:nowrap; -moz-border-radius:8px; font-size:0.9em; width:16px; } div.boxButton{ margin:1em; } div.boxButton:active{ border-style:inset; border-width:1px; } .boxButtonDisabled{ cursor:default; display:inline; background-color:#CCCCCC; padding:0.6em 0.3em; margin:0.2em; white-space:nowrap; border-style:outset; border-width:1px; vertical-align:middle; text-align:center; -moz-border-radius:8px; font-size:0.9em; width:16px; } #boxButtonImage{ margin-right:0.2em; border-style:none; height:16px; width:16px; vertical-align:middle; } #box input{ margin:0; padding:0; font-size:1em; height:1.6em; vertical-align:middle; } #box select{ font-size:1em; /*height:1.6em;*/ } #box table{ font-size:0.8em; margin:0.2em auto; } #box th{ background-color:#AAAAFF; padding:0.2em; } #box td{ padding:0.2em 0.6em; white-space:nowrap; } #box a{ white-space:nowrap; color:#0000FF; text-decoration:none; } #menuBox{ padding:1.5em 0 0 0; font-size:0.7em; margin:auto; width:15em; } #menuBoxHeader{ border-bottom:1px solid #FF0000; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; white-space:nowrap; } #menuBoxBody{ padding:0.2em; background-color:#AAAAFF; text-align:left; } #menuBoxPic{ float:left; height:24px; width:24px; padding-right:0.4em; } #menuBoxLink{ margin-top:0.5em; padding-left:0.5em; height:1.6em; vertical-align:middle; } #menuBoxLink a{ color:#0000FF; text-decoration:none; } #menuBoxEntry{ margin-top:0.4em; padding-left:2.5em; } #menuBoxUsername{ color:#FF0000; } #loginRoot{ background-color:#000000; width:100%; height:100%; } #loginTop{ padding:1em; font-size:3em; font-weight:bold; text-align:center; vertical-align:middle; } #loginTitle{ } #logo { margin-top:1em; height:60px; width:72px; } #loginLogo{ height:100px; width:100px; } #loginBox { float:left; margin:1.5em 1.5em auto; border:1pt solid #000000; font-size:1em; color:#000000; } #loginHeader { border-bottom:1px solid #FF0000; margin:0; background-color:#555599; color:#EEEEFF; text-align:center; font-weight:bold; padding:0.2em; } #loginKey { margin:0; float:left; width:8em; padding:0.4em; } #loginValue { margin:0; padding:0.2em 0; width:8em; float:left; } #loginValue input { background-color:#CCFFCC; height:1.8em; width:8em; font-size:0.8em; } #loginLine{ width:100%; height:2px; background-color:#000000; } #loginSubmit{ background-color:#EEEEFF; margin:0.3em 0 0.3em 0; height:24px; } #authError{ border:1pt solid #FF0000; margin: 2em auto; width:550px; } #authErrorText{ color:#FF5555; text-align:center; font-size:10pt; font-weight:bold; } #treeBox{ margin:0em; padding:1em; background-color:#CCCCFF; color:#000000; } .tE{ font-size:0.7em; text-align:left; white-space:nowrap; } .tdS{ color:#777777; } a.tE{ text-decoration:none; float:left; margin:0.2em; } img.tE{ margin-left:0.2em; padding:0.2em; border:0px; height:10px; width:10px; display:inline; } input.tE{ margin:0.3em 0.4em; border:0px; height:10px; width:10px; float:left; } span.tE{ text-decoration:none; float:left; margin:0em; } img.tES{ padding:0em; border:0px; height:17px; width:17px; display:inline; } a.tE:hover{ cursor:pointer; } img.tE1{ padding:0em; border:0px; height:10px; width:10px; display:inline; } img.tE2{ padding:0em; border:0px; height:10px; display:inline; } img.tE3{ padding:0em; border:0px; height:12px; margin-top:0.2em; display:inline; cursor:pointer; } img.tE3Disabled{ padding:0em; border:0px; height:12px; margin-top:0.2em; display:inline; } #tS{ padding:0.2em 0 0.2em 0; height:1em; float:left; } #tSFW{ width:18px; padding:0.2em 0 0.2em 0; height:1em; float:left; } .tLR{ } a.tLR{ text-decoration:none; font-weight:bold; } .tL{ float:left; width:15em; margin-top:0.3em; } .tL6{ float:left; width:25.2em; margin-top:0.3em; } .tLN{ font-family: sans-serif; } a.tLN{ text-decoration:none; cursor:pointer; } #tD{ float:left; margin:0.2em; margin-right:1em; } #container{ height:100%; overflow:auto; } #pageHeader{ border-bottom:1px solid #FF0000; font-size:1.5em; font-weight:bold; margin:0; padding:0.2em; color:#EEEEFF; background-color:#555599; text-align:center; } #bgbarTable{ border: solid 1px #000000; width:100%; } #pgbar{ height:0.8em; background-color:#AAAAFF; } #aboutVersion{ font-size:1.5em; color:#FF0000; } #about{ font-size:1.0em; border:3px ridge #5555cc; margin:0 auto; width:46em; } #descrHelper{ margin:0; padding:0; position:absolute; display:none; } #descrHelper:hover{ cursor:move; } #warnl{ margin:0; padding:0; position:absolute; display:none; top:100px; left:350px; } #warnlContent { max-height: 300px !important; overflow:auto !important; } #warnl:hover{ cursor:move; } #statusHelper{ margin:0; padding:0; position:absolute; display:none; } #statusHelper:hover{ cursor:move; } #pluginInfo{ position:absolute; width:20em; display:none; } #pluginInfo:hover{ cursor:move; } #floatingPopup{ position:absolute; width:20em; display:none; } #floatingPopup:hover{ cursor:move; } #floatingPopupContent{ } #boxBody { } HaCi/html/favicon.ico0000644000175000000000000000526610677351441014150 0ustar fighterroot .  ( \         #   6B(./i5$""s'"": >J@@l<<n==p>>MCCJEEs@@uAAQFFLGGxCC{DDPKK~FFÿSMMTNNZPPvOO_RRPP’QQ”RR•SS—TTb[[˜UU™UUn__Ÿ__mffrjj‘gg¤ggii¨kkzrr|ss¦oo§oo¬ss¤vvŒyy­uu¯uu„{{†}}}}º||•»}}¼}}©š……›††“‰‰É††Ð‹‹«””¬˜˜¯˜˜Þ””ß••§œœ³››Æ™™´œœ¬  æ™™¸ŸŸÃ¢¢¼££Ì¢¢½¤¤Ô££¶ªªÙ¦¦Ã©©õ££Ð©©¹¬¬ÑªªÒ««»¯¯¼¯¯þ©©Ë°°ÿªªÎ³³ô±±ó²²Ú¸¸É¼¼Ì½½Ì¾¾ÿ¹¹ÿººß¿¿ÞÀÀáÂÂÿ¿¿ÿÁÁáÈÈÙËËîÎÎáÑÑáÒÒãÔÔäÕÕîÕÕùØØðÛÛýÜÜÿÝÝ÷ááôääùèèüééûêêþëëþííÿííÿîî2( 0+4<<<3 GG8°¿¿¡$#e´¿½dtŒŒ‘œœœœœ1F``H¯¿¿¿¿£n·¿¿¿«¶¶¶¤œœœœœ¥K``[T¿¿¿¿¿¿¿¿¿¿¼µ¶¶¶ªœœœœœ©&[`\|¿±Ac»¿¿¿¿¿€¶¶­§¦žœœœŸ 6*`\¿is¿¿¿¿¹¶‚œƒ`\•¿Q®¿¿¿¸Ž¶awœm`\•¿JV¿¿¿¸Ž¶Iuœl`\•¿J¨¿¿¸Ž¶Duœl`\•¿Jhº¿¸Ž¶Duœl`\•¿J`–¿¸Ž¶Duœl`\•¿J`p¿¸Ž¶Duœl`\•¿N`b¿¸Ž¶Duœl`\•¿^`f¿¸Ž¶Duœl`\ˆ¿˜ `¿¬Ž¶Duœl`\j¿¿¯¡¡¢²¾¿„޶Dxœl`]-»¿¿¿¿¿¿¿¿U޶D‰œl``.r¿¿¿¿¿¿¿™޶D}œœl``ZOy‹‹‹”—†޶Dgœœœk``````````]޶DqœœœW``````````]޶D;“š~``````````]޶D``? C`]޶D`_`]޶D`\`]޶D,S)`\`]޶D=S7`\`]޶D:S5`\`]޶D!@`\`]޶D`\`]޶D`\`]޶D`\`]޶D`\`]޶E`\`]޶X`\`_…¶P`\``K o¶¶Šzzzzzzzv"B``\```9>¶¶¶¶¶¶¶¶¶¶¶{L``RY``C’¶¶¶¶¶¶¶¶¶¶‡.\Y'/]['%›¶¶¶¶¶¶¶¶³MHaCi/html/index.html0000644000175000000000000000016210702524424014002 0ustar fighterroot HaCi/html/Images/0000755000175000000000000000000012475447615013232 5ustar fighterrootHaCi/html/Images/HaCi.png0000644000175000000000000020160010474157163014534 0ustar fighterroot‰PNG  IHDR—±d@bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ 75y6øtEXtCommentCreated with The GIMPïd%n IDATxÚì½wp]×çù9禗‘FÌY "ER­œ[ÒØc÷ºív‡ò®§¦kzw¦½5Õ==5³Ý]Û3]³[=ì^ÏT[²dK”e‹-R”EQ b&˜ $"¿|Ã9ûÇ{€@!‰ ß·êÖð.î{÷Üßùž_>‚4%”PB „,A %”ðqÀÑ)¡„n 4 4ø£Œ¬1Ég£ã°À4K#VB %Üd´¦Ãø›Ç‹|Là.ÇáËá«m«4b%”PÂ-AZiyû<_ëÉÇ‚¦ÉjÛbe—F¬„J¸%Hi£þVr8—PB  JäSB %”ȧ„J(‘O %”PB‰|J(¡„ù”PB %”ȧ„J(‘O %”PB‰|J(¡„ù”PB %”ȧ„J(‘O %”P"ŸJ(¡„ù”PB · JÃJxÏð´Æg%³…(­h%”ȧ„'|ŸVßÿnÛ )Ù`ÛÄ¥¼¦oK %”Èç}"­5ˆ•úZðZ>ÏÓÙ Ò0h¨©AkÍP*E]&Ã|Ó$$Ämß<§59­)“’’Ô”Èç}Á/N¶”V<ä„JÂ\ÖŠîÚZîܸ‘UË–¡´f×îÝß¹“l‘¨og(àˆçqÀóøl8L•X¥…«D>ï¸<›Íp%¨5L6Yöm.H–,liá÷~ÿ÷Ù´iJ)„eñìÎ%¡z•â•|žÎf¨4 ¶Ú6Õ%ò¹%ßàMV°_äó´ú>Gƒ€ïæsdKÃRÂMð¦ë²Çui þï\–6¥JƒR"ŸÉÃÕšËAÀ ù³·neó#pÌ÷Ùíæé+ S ã,V}J±Ãuñ–-婯~•v!xÓu¹¥*™]“ÃU­yÎuÉÖ×sÿ}÷QS[ËOÓi~rè3T@¹”Ó’¹ «5làãŽ1™\­il!ˆŒaf8B‚ð45A\­yÙsé(/cñwòðã“J&yóí}ÌÌfi4ŒÒ„+‘ÏÄÈhÍißçÇnžÕ[¶°iÓ&f44âo[[9”ÏÓ(%urú”sZsÒóxÕÍ{ÎQ×}£[¹Ok¶å²ÔƘ~±¹†ÁjËž–RºZÓ©Ïçr$Ö­cË=÷°aÃò¹ÿ­£ƒgβ<ði2JS®D> #x;8 ñ'O<Áâ¥K‰Åb<ðÐCüð™gøÍ™34x8Æ´Ó~’Jñ†ëòïS)êëë1Ç"ŠòrÖÖÕ …B „ ‘Hš9“—ƹî@?óyBŽá¦´æmÏãaðû÷ßϦ-[(++ã‘GåÅçŸçÔåËìJ§™6K9P%òßnß¼VVÆçï¿Å‹Ç‘RR[WÇ7ÿàøÞßü ož9Ë–¢\NO·Yuu5Ï=÷3gÎó}˲ˆÇãØvasɯ}ík|ñ‹_÷zÿןÿ9É—^‚drZŽ×9ð=4k¶neíš5ÔÔÔ „À²,¾øÔSüSo/¿zõUrBÔHYšx%ò¹G<n˜Á×¾þuêêêE‚‰F£|ê¾ûعc纻ٞÍò„š–ã`µµµÌ˜1cRçÇb1b±Ø¸ïÇ£Q2Æô\óÏû>{<öPˆß}òI,Z„Q¼W!k×®eÿÚµ¼qì?êïç·CaÊK¡÷éO>ýJqÒ÷9êû„ ‰#&ÖT~“ËÒ>s&k6ofÕªU#+;€iš444°õ¾ûøE{;ϼù&9Àã;Ÿ]­I³¤d•eÑ8E& RŠ¡¡!ÆÓ$ L²|>O.—÷zC©y×÷žÓšS¾ÏßÃ’èM´Ûc®ËñxŒ•wÝÅšu먪ªºæýªª*6nÚÄåS§øñO~@a`Ž“®ª€ß§Z–Z‹L³D>SCZ³ßuùëtšªDœå "‘ȸçŸÍçY³i<ö¡ÐØZÍ=[·rþâE~ÔÞÎÓB•rÌr?èI&9ÕÙÉç‡:)§ ùäóyvíÚEuuõ¸fÙ²eËFÞ?wîGŽ÷z­gÎM§a ¬øy­9ëûüm:Í€ã0«¼œ²DbÜó;\—Š–~ûË_¦n?ÙÊU«¸òÈ#¼¶o?†3ÆX(¥Èäó¼ÓÑÁF!ùª,4Íi™U?íɧFJÖÚ6n*I®¶–Ú X²dɸçßeš¬]»–M›6{Ncc#ŸûÜçhlläêÕ«ãž700ÀÑ#GHmÛÆË¢f  ü•êà_ÿ¯ÿÛ˜çhîÚ¼™?ýÓ?ÉpþÙÏ~Æÿùï`¯ÁÓåS!~âÇ "l; ©XŒÐÊ•¬ß²eÜó¥”455ñ裎í *++¹÷Þ{‘RÒÙÙ‰çyck]¹—ÚÚxóÙg™£4s cÚ–óL{ò Á\ÓäË‘({ÓifϜɗ¿üåsá†É'ÑhtÄÏ3ššš¨««Ã÷ýqÏyååíœ|k/k#6;3¦€sºBJ>³Ü²Æ=çïÒi:ÇÓäÅi4 Bc¬êSDó3… ZJ> óKß'òÔSOaYbŒûB`Û6¦iŽùþÈBXSÃ>ˆçyh=vÜñcÇøÿŽc¡í° h1§ïöä# !ø­p˜‹™ —.qùòeÖ­[÷®‡ ‡Ãã¾åÊ.¶]$uö _ …˜-)Qf Á àf¢|ÉÍÓ;Æû!XeY4™æ˜I†¦Dµ»(ŽÃ¡—|Sœ¤2 ­5{÷ìáÔž=Ô\íáaË&>E„H…läñ[HÌ1ÈGÿ/4ÎÿYS¬ÑØ|Óäá¶6žýáéêîžPÓý 8qü8ûvîÄlkã~)iÓ{zÞä#Šô€iQuî¿~ùe.\¸€û!D_”R$“I^Ú¶ì¡CÜrh0ŒRK…)j¬3MîèëçÕ_äıc$?¤<¥L&Ãî;9þê«<3ß4‰Ns™¹­ KWZk- :;ùû¿ý[º»»oùg¤R)^~ùeN;Fs:ÍöSšÅSóL“»l›9AÀOþùŸ9yâćò9o¿ý6 rµ‡¯8¡Û¢qÝmE>–¬3LîJò /pìèQoÙõ}ß§«³“ï÷»Ì¹ÔÎÒ B–Le@“að/…äðîÝìß¿Ÿ®®®[j¢çóyžûñQï¼ÃVCRw›´¡½ífÆÃ`½ÖXW:Ùþ œjm½e×îììäõ;9þÎ;ܑ˱ҲJ)äÓUBp·aP××Ïþ;Ø»gÏ-»v2™d÷îÝØ»—ÆÞ^î±ll!n‹n™·ùÄ„`–a°Ð4ysÇΞ93nØó½âÊ•+¼ù›ßà¦Ó,4 f¥Âé§\bYœÝ·û÷ß2™I&“¼öë_ÓÓÝÍL ÍÛg¹ºíÈG¡5ºxÜB­5 P¥^ÆÓ ´ÆWŠ@©[&7ZkT ŠrÜF‰ÛŽ|”â¸ç±Ûsyâß`ÝwN˜ö^°`áBžüÒ—pâqöú>­ãd±–0µÕšó¾ÏN7ϺÏ|†Ç?ýé›&¡NÕÕÕüÎ7¾Áì¹s9¼•Ï—ÈgºâX°+ä°pÍ6nÞLCCÃ-»v<gñâÅ<ö¹Ïq¸ª’·}Ÿ¼žú¢PpyÌó¸ä{cv2Rš]ù<íA@NO½¯C)~$æ/`ý¦M,\´èÖ™tŽÃìÙ³yà‘GH¶4óªç‘¹M´æÛŠ|®*Å>ßãTy9O>õÍ--™¾çÁ”’ªêjžxê)¼yó8(§>Ĥ´Štº”âM×å¹Ïø>ÝåÔ76ÇB ¥¤ººšÊ¦y¼fÛ<ãæÙ–ÏsÔ÷§üV:CJqÔ÷ÙŽfó#³råJÊÊÊnÙõ‡K3|øaªW­â˜mqÀó¦Í¢U" ~žýžÇaÓ$ÚÔÄS_úÒ„©îï‘H„Í›7³|õj.WVò²›Ç¢0:•âußã{Zó×e v6Í£þÞ{øÔÖ­444`š&¦i²lÙ2üÂ֮᧠ü÷Pˆg‹7¥5Á•™3AÀ^­IWUñØç?OsKˇòY+V¬`íºuèÙ³y6Ÿ£O+¦û6æíB<®ÖüÂó\¹‚Ç?󪫫o™Ý>–ô…'žào»ºøÕ³Ïòy'ÄLØRû}) ¥—˲Ý0H,XÀ¿ÿַظq#ÕÕÕX¶eY#c¸~ýzV­ZEœ:uŠç~øCvlÛÆ«Ûø‘wXeS,çIiÍo|Ÿ}uµ<ùÄ455ÛfåVh@[¶n¥ãòe¾ûŸÿ3Ÿ·b– 1óĆcÁ …jÞ•–ÅÛ¦a…‰3ZóË\Ž×,“E<À—¾ò***>ÔÏŒ'ôôôpîüy.÷õ³Ü4§LÖª’ZóßÝy’Õ4JA­œžùbÓž|RÅ–˜o¸.åZÓqò$/þàãkIB°þ®»xøá‡©­­óœl6ËØñÊ+¸ƒƒ˜ãd¤¦R)r™ g=—}®ËlÃøÄ“.*Å Z‘Xµšû?ûYÖÝy礉g4êêê¸ÿhokcçÓOSyñ"K 㯺Zs1xÓsétl*»ºøÅÓO{~˜ÕÒB4W;r]—³gÏò£gŸ%ÓߡԘ×|>—¢=Ÿç ,7MjíùLI {8—IIíÀîÛoÓúöÛãžÿN&ÃùóçI$|ö³Ÿs%koogÛóÏóôßÿ=KB!†1nŸ/¨1 Îþ”Øé4§5g”bW8Ì_~ë[¬\½úš>Ö2~÷þ€Ðzág}Ÿ•ïƒÈ>ê18乘@}6‹sò$­'OŽ{þ™\§¹™x<ÎSO=5¦IßÛÛË®×^ã¿þÅ_°8¢Ú4qÆYˆ<¥©ôóZs¥²’;6l ¬¬lÜv³ïRJ–­XÁ±·ÞâàÁøNã\ËÖ«WÙ`[S¦5êdáݾG:fÁ‚ŸÙÀétšl6K:¦««‹®®.Èårø¾?’¥F©¬¬¤¾¾žŠŠ b±áp˜h4JUUÕÕÕø†Éß'nÛXÓdÜ0[Jž‚½oöîeá¢E455ÑÛÛË/·m#t²•û‡ÊR§ùL„Ã`½m³F¼øÌ34462söl¶ýüçd®^e‹¬0­iwßýJ1(LÇ¡¾¾žžžöîÝËž={8xð G%›ÍF)//ѵÖär9GÂÑ ,]ºtd¢eË–‘ˆÅHT”ÓžL±Të)±‡×d‘’U¶ÍæP˜ƒ¿üõ T>ù$Ï=÷ígÏòyßçÞP¨4¹Jäsó•l¦”ü®mó‡³cÇÊËËÙñÓçxR)Z显MudµÆÕšC‡óío›®®.B¡õõõlÙ²…'žx‚ºº:ÊËËoH4 ‚ÏóÈf³\½z•ÎÎN:::8pàÛ¶m£¢¢‚Î+WÈõôÒgYÓ¬vIQ!ø²ãð_ÛÚØ½{7N8Ì?ýÃ?°¶û*k cZïDQ"Ÿ[¼’ÝiZ,N&9ôÊ+dLãÊeGh˜f¹åµær°Íóh-V¡¾ÏªU«hjjbîܹ̜9“ºº:***®Ù&ùŸ‘Ö#Û,÷ôôpåÊ.]ºÄ¹sçhkk£»»›)xÅ÷˜å™Ü) NNŸ±4)lw³2æ7û÷óƒÎNzÏœa“í°Ø¶)\%ò™ôÀTIÉcŽÃ;~‚Ë*à‰XŒ%S¨Lâf(TúŸô}öº.{jª‰ÏXÂcK–p÷–-l¼ë.f̘1éz¦aÇsEEÌŸ?¥Ùl–#Gް{÷nÞÚ³‡Ë.ð‹övú]5¦¦©¨LõQ@\î±mN_¾Ì‹çÎñp4Ê*Ëšp´ù”p àAÇaW>G] øŠmž&Äã½JñªïñtðŽmñ™­[yòÉ'Ù°q#Ñ[´­±”’h4ʆ X¿~=lß¾ú‡àí³çØèy|]+š‘)¶¯×xXgYµlNzè„h,ϸd …(+Ž|5á›Ñ(k,»4:Ê Nû>žÖ,0M¬i°BûÀ)ßçsYöG"4¬\É7þÕ¿bÑ¢EÔÔÖ …>´Šß÷I§Ó\¹|™_<ÿ<Ûö3úÞy‡o†Ã<䄦EA³¦PDy9XnY8Ó„T?RZ³×uùboÉ‚¿O”4Ÿ›­ÜÀ\Ã@Ôj‰1ú•âÏãû¹,Ù%K¸ÿ¾ûøÔƒ²rõjb±Ø-I*œPÕ6MÊÊʈD"|ö‹_¤aölv¾ü2ÏlÛFw>Çöà ӜÒ/€:)©”rÚhÊ%³ëcÂt‰l]UŠ·=—Ÿ)ÅàwpÏãóÀƒ²lùòMÓ–eÑÜÒBEe%uõõüв8ðúëxW¯wLñ],!°JS§D>%ÔÞ}žË ÒàØÜ¹|ëßàS÷ÝGccãÇú½*++Ù¼y3³fÍâÿuönßNæêUj¥¤VJJÆÿô¶*J¸ pØsùç\ŽS-Íüéø|þ _¸¥Íó?°ÔÜÌ¿ù·ÿ–;Ÿ|’W¤äÿÉe¹ªTéÁ•4Ÿ>…v]—Ü$ÿ'$ ^,¿®¢XSØsé€çÑ5IŽÁÜâÆÃfW@¡ÑÖIÏc`’Ira3 ƒfü¡¦ç çÑx“º7A”,0Í÷T4œÃówÙ,öÝwó{_þ2wnØ@$¹e[Ý?‰ÔÔÔð…'Ÿ$ê8ü¿ú+šÝ<Z6sÞ£ªW)Îû>W&ù¬M`µeQu]½YOñ:“¼ŽÌ2 fÆ5-c‡”â¬ïÓ>ÉëØÀ’bxþúgÝ£—‚€ö`ò­6f¿SÕ',êö‰%Wk:‚€ïg29žß78ˆáy¬±íÈ m_Êå8ˆ&™ w%™ÎÎdx,¢RÊòñ´æRð“\–+¶C"6q8:™Ncg²l¶êÃÆ ‚ôól–}ž‹eÛTOкb(•ÆÊdXoÛÌ4ŒI“:µâÝ<îòål}ôQîݺõCo!û~aÛ6---x>ʥ˗yeÛ6ÊÒi*„óžš°u/çs¼­¡ºbüq E6Ÿ#ŸJóÇñ81)¯ñÓt¿Ìå8 ˜ðùäò.ÉÁAîw …®!Ÿ>­x%ŸçM ª|â/²ù<îÐߌƈZÖ Ïº#ØžËñºïQSY‰1Á¸¢»¿{L‹C¡ùLZóÑš>¥ØéæY{ÇÝÌ,:އ¶#Gèõ‰1µÆÕðB!,XÀÿò»¿Ë¸t‰}‡јËs—”“Ž€õ+ÅIggÔ±ðá‡Ç_à\—žóçÙõòË|-Á3ÍkêÍú”â¸Vœ®ŸÁÂG™ð3/_¹ÂÉ7Þ`v:ÍæëžQJiŽ«€Öúz>ýè£^çÜùóìÙ¾ÇU@^›c>ÓÓRÒZSü˜pë§t:ͱ—^¢>fí'Єçùµ¯ñÔSOMxÎÏ~ö3~ñ?ÿ'òµžw÷=÷ðgögÔÕÕ{Οy†Ÿþ—ÿ'[Ç|?‰pïg>Ãÿ»7ág½ôÒK¼ð÷¯¼:æûe–ÍC[·òµ?üCÖO@d/¼ðÏÿã?«¯NzÌ $þʶhZ½šG>ó-^<%|Ñh”+VðäSO±-—ã²RzàL–€Êb16mØÈ_ýÍߌo’æó¼øâ‹ìzùåñÍïPˆ;×®ð:÷ïç¯ÿüÏ ïÙÃXû$Y†Áš;î¸éu~ù⋜~ë-d0>YT”—±eÃþì?ý'ªkjÆ_D¯\áJ{;‰#GÁu?qϹäpž¦¸ìó(‘Ï4Å* }ÖL6Þ? /&O¹{H$l¹ÿ~ŸzCZSŠ•ȧ„O(TÑ/ð–çã67óèc}$™Ë„,_±‚E7ÒQ–à `°~/‘O ŸLøZsÔ÷i‡¨^°€õ6|äÙË·555,\¼˜YK—ò+L:|^B‰|Jøˆá/>Ë—³båÊm{ß³çÎeóã³Ã²èœ¢ûÞ—P"Ÿior¥´æ\Žú•+Y¾bÅ´¸¯ºº:Ö¬[GÎ49øtAéa—ȧ„ORZsÆ÷4 ›š˜5{ö´¸/Çq¨­«£iÁ.…B\R%ò¹­ÉGk}Ó£„ƒJqDTÔ×3cÆ âñø´¸/!áH„ÕkÖÐ]VÆ%õÑÉÖdä¼4>Dò=ªxh ¹TjŒcä½i𦒠 jÍqÍË–QYU5­ÕqV¬^MoYW>D¯Ïõ²>ü7¥J)‚ ÷ÐÅyqý\P%2æÍ¼›°©¯ûyôë5+ï¶Hý3Z¢Š'E8£É´xŒÈÑ«óÇ ÐfY¬nj"žHL+Aµm›¦¦&R–E¯ç8…BÎ[M6ªøõè…Tk|ß¿fNŒþ!Äù( ÙåÁ(ù}áMGË‹¸MŽM>R¢mYèpbQt<ñÄèDb1D$tg'Á;ï .\×E²˜ /ŠêÕhú°¼ßóèÉe9|]#* ¤”bPi'A¶~0èyö=ù>5E’ÉhÍ!Ï¥S)²£6̾ŸÑÇðõRJÓî¹ñÉçzÓgÁ‚üóÌâÙ³‘¾Ï@_½½½ôöõÑ}¹ƒA×Óä7o¾‰”’P(D$¡¢¢‚êêjÊ+*(/+cùŠüÑŸü ;¶mãøîÝœìè ŸÎ \—×¥ÕóX¡žç]#+ב֚ŒÖ¼–ÏÓ¡5Ƶ éI×¥G…Y6 z]›×;ö\Ïãï~—_÷õѧTA->´@)­ ‚`Ä÷ã„ìZ¶Œåõõ„+*ˆš|AA-6‹“øw®×„njjjXºlCCCœsnl¿(Å<ÏcþüùØ£*Õ‡W°áWß÷™=w.¸ÒÙÉÙÁAÞ>r„R©Z–/[F(b`pp(„4 ¤”Eâ1Š¿WVV²pÙ2:{{é …è»Žôš]—Å‹“x&R^ëBßšÀÇ’‰àdà“Vš‹mm8pàÛ:ム94„ö½€«åBP“~5ãùælÛæÜ¹¯ÈJ· IDATs¼úê«ôõõ‘Ëçñ=¯ ëA€.šI µ·2 Ë41- Û¶)/+cÖìÙܱe ó¿þu²Ù,{÷îeÿ¾}mmÌJ§i™?Ÿ|>?"£a2ª¬¬dÃÝw“J§é6Mú¯×â=yee,]±Ëþøû<~–y½y‚Ö$8wµ‡ÞI8ö⦉aÛh¥ð3i²”!0µÀÒ°Fû†Š_VÞbÐÆÙ¸qãû¼#GŽðôÓOsæÌ™"¶á¥ÐEÕZkMee%Õ[·Çɺ.J C`š&¦a`š…íƒ ÃàŽ;î`íÚµãªØ*àùl–ÿchˆšH„°i’ךœRüÅ_þåˆ@O7(¥IÁa7!í©÷Y_DùÜõkדŽRŠT*ʼn'عs'Ge``ß÷ >@þÐåŽZ[[ùõŽD",\È}÷ÝÇãŸþ4Jkü @JI6ŸG i˜†Ä0Í‘EKJÉú;ïäçÏ?ƒÌ|Ržåh²™Èï{½x$P5êÿoðùŒ8ÆÄ$®Z´Å£‘äó¸¹Ï[{ö°ïí·©Ÿ1ƒ­[·²~ýz¢Ñ(žR $BºØÃ –ab˜FQk­}œÓZë‚XJ0 ´è¢68¢Ij ¾5ÄN÷ÑñòÃ2 !p‡H8L08DNƒãŠR„UPˆ ¡Ð ¡ =›†¿ôÇ6 ÅWÃ0°m)å„ä#„  Féw=ryKa›&‚ÂØDˆ!Œ[îãÒÅgU³åž{X»nÝm-ñ<§O“ßµ‹ ¯ÿÝç3cþ|šî\O>Ÿg``€Ã‡³cÇŽ?N.›ýH¾_t´·óÌÓO³}ûvÖ­[Çúõë©®©!Кœ”˜¦‰cZX¦Q$"Ó4'$¡j¾hÃ@Û6D"èª*T}=º®]Y Ñ(Ú4 äžN£{z.µ#ÚÛ½½ˆ\‚`$èäݸ0Ê15Ÿ@©Iµ.R ‡q, ×utŒXœ° ˆx>ï |øJ¢…F«?ßa˜µe‘|¸ÉC•RRV^ŽÒšõ<­ iM„Dˤ,ø´B 9L·Z’XÈaö¬Y´´´Üväãº.---\zçôõVçD‚²E‹¸ãž{0kjÙ³g;vìàè±c$‡†&\X†_MË*˜Ñ¦YxŽB2œt¢”B~^=¯4AïûtuvòÒ/~Áž7ßäŽ5kØ´y3•UUd\¼å°¢öl)u  û‡ýBª$Ú¶Ññ8Ô×£.$hiFÕÖ’w²J‘q]²®‡«´”˜õõ„—-#lÛ˜CIôáÈ·÷b\¼ˆ‘J#µÆÕÿÚñ1np83|LRø£Ñ(Žãô}ú ¤4ˆz.¾›Gç]¤ëb¸.–/°˜"À¤… ù1&] çï†iY7:™¦Iuu5YÏg!¢ÒÀ74:Jah0µÆC©MRˆi·;„i¢P7R¶|9§x{ûËtvwÓÕÕuMNÖõ“×´,"á0±xœx"AEUñŠ B‘¦í€a€(¥ /ïâ¹.ÙtŠÁ¾~RCƒä2™‘Ãó<Ôtôôôðë;8}ú4›·laõêÕ iÏ#çù8–…c𨖉mZJcrDCþÞ·\ ¢ZSY‰ni&Xµš i)éîíåbk+{zèè +¢?›%Úp(De,ƬšZfÍbÁë)[»†àµ˜¯ïÆîî"¯Þ0·æÁu®t!†¦hÓÝ„¤”D#¤a’}å•H;„çæÐ™†™Á6²8Bà¡ þ_¡•¸Æùüqi<#æ‹abÛöM¤eYTWW“Ñš~ÓÄG•¯hnIQØýÀl Òhã]»¸D: Ó$ÞÒBð™Ï9“£Çñ«S§èH¥ðÇñëH) …ÃTVV2£¡‘³f’¨ªB‡#x–…' \!É ðEÑdÐG¨„†(Ð Ó÷!“&70ÀÐÕ« \í¦·«‹d?žç¡¯#>Ïó¸pþ<===œ=s†-wßMÃÌ™d݈!Ë"P [)”eb²Ñþ ÷MBB Ãaô¬Y¨õëñV®¤/âÀoð›¶KœK%ô¼IwÔZ“Ëå¸ÐÑAw?m]Gº££ ï(ÑhÈM<½ØšÉ“O(BKƒp„þjƒP&ƒeY„… ¯®ïáû.Êóо‡ò%Z£Õ¯ÕìB¤a`Ù6â&f—aDc1:-‹>Ûi L£è44ˆ®”øR¢¤,¸Ï’Öóa,555ÔÝw©¡!~¼ýe^îí%5†\‰b~NyEÍÍ-´,^„]YI2¦SHÒ@^ƒ..ÃQFžÞ»‡æúVöy­É Ái98õ „ëêY¸d)ù+—¹rî,]/’$(jcÃQ¸ö¶6¶ ÑÛÓÖ{ï%‹‘Éæð” ¾*p¥Q–º†€†ïMJùÞ´kÓD—•¡–.%ؼ™TyGâÕÖSK‘ ‚Ò¾¦”)RŒâü®óô‹çkù™L†ýG¢¥äÑ»ï&{©l*…N¥ ð¡¢>2f´k8„6YòQ†É@$ÆFY&aÀ |<×Eårf% ´ïɧôá; ÄHÔÈ4MäM¾—išD¢QVÓ†$d¸¦‰g†A JD!(ÄÚKdq«‰ ¥¥…îãÇy}÷ë$;޶ŽD˜=g‹–,¡ªq&™H”6ËfH+\žR…g¦Å»ëq1KßàÝð~Áä\»D 4õ€…Ÿ} CB2ž¿ˆysæÒp©ö“'én¿Djhh„„”R ô÷ó›7Þ```€û|òš’Ù®ài¯*¤qŒX £E¥Ôä È4ѵµë×㮹ƒ+ƒƒ¼úÒKìºz•^ß'Ðï¦Ä†cYDm›²H„²Pˆ°4°¥EÎ÷ÈeéËdèÏåp‹äº.ÇZ[™7g3W¯"캯<êÊcæ5¾—â—V“Ð|„X–E(—‚d,JN  žëág3–‰2L´,8ìâ3 G¤D!Úe˜æ„MmÛØ¡¡0IË!,$!)p '%aH‰–¿R”ª˜?$âQÅR…vì~ƒóƒƒ¸ã,‰²2/YÂÂeË•\¶zä´ÆÓ ‹>Îb9 ÅCc"0Šþ) °‹Øhò¹¶ø8 Xd*À׊Û!Ö<Ÿ…3fPá§»½\&=rOÙl–£GŽày÷?ô•uu ¹n1LíÍÀ¢¶aY×( “& Ã(ÏæÍd–.áÔùól;r„CC#;Ëz"¡6‘ 6§Â´ˆf&ƒH§ 2|Ï/,´ŽÃÌxŒLy9g{û¸˜"[LÒÍd2œoogÞºuøUUˆxr9¨¨‰›#Öëð—-ªœ“±ólÛ&‰GsBø×!°l”i£!‡µÂ!Ɖ:|ÔZz”Jnšš]RJb‰JJÒ–MÞv0_ )PÅiü’âšzþn1ñ¸®ËåË—Ùµkç»»Æ$Ã0¨¬¬dÙŠ•´,]B.žà‚iÓ¸ºàHVR€ÒSB),ÀÒ`#°Ø‚B”‰,” M’(†µaò ´&ЂðøBãShq;Q¶d)Ëkj8ûÎ;\:ÕJzhhDÈçó´ž<‰‚~˜xu5ýù¾Rø¡B ŒÒ¼ëµÑ¡aÜ$ŸLtEjýz2 rèÄ ~ÞÚʉLo”iZ3§¦†Yåå”öÀ t\ÆëÄ ÅOŸàd>GÀ» à 55,š9“zÃÄîî&¸ÔŽ;”ijLŒšZÌX”`hˆL2…'(ø6‘h)pm‹òòrª{zèÎdȆa …<Ÿ¼ã  °,¨¬ˆßØRCNŽ„Ø–…aZxRàIÖKßuÌQÌlæÝcŸ …À‚ˆ”ìÞ¹CJˆÅã˜71‘n.ÄEõ¦I 5©TjÂìf)%áh”ŒЗ͑4¾Và{è ð=rÏPpU+hÂZ¢ ¸Ã~1IMoØfîïíåõ;éjog©”GßÈ9Št6Ë[{öŽF?P}Ò'xlÛ&‰ÏçÙµk;vì ;F¶²”’òòr,^Luc#çr.ç½ú„À¢6§… ‚B²¨ p‹ qŽÖä´.j>à˜&呦e‘J¥È»î»ä3rëÇ(X¾Öí§¨eyy ¯§—ŒÖˆª ¢sæQ9¿, yñ"©³çðÓ”,$`šŽC(žÀtBHãÝ­­mÛ¦¢¼œl&MÆ´ÐÑx!âUQ5Çö‡èIù|L³P“â¾!AHFjÔ 5¥‘ªð7¡u¨G¯Öã¤þZ@•”¬²ö=óCŽl…†–f¢3g‡?P ­ )Z¢ñ8óššh»ÔN>ïÞÔç3ÉpÉë¡Ë´0s9®¦“„“CDŠG8•"”Íb粘®‹øÈ`¸´dòå$AL&é>y’övs9VF¢ 7Êp„ \C"“áÅïŸCo½E¬¶¶ âN3 OŽ P__Okk+û÷ïg ¿Ìs£Ñ(-MÍT×Ö±ëè1ŽƒNm[Ã(È© 0|Ãu1=3ïb¹yLÏÃð=Ì  kiM<fVý b--˜‰8Éîn>ÄÀHÆtÑ÷2ÀsåŠþS)QB¢„ ’À4 ¤¶lª+Ê™ ««%—Ëâçr#÷’Íf9üÎ;”•—Ó¼x1]m—8vñ*›ÇR¶R˜Jaj) ó-—Í ÓÚ/qñÄ f #j£Qôúõô¥Ò¼tâG³üQc»pÞŽmÓ—JsEeè–&$‡}=ˆînèêBtw!zz` ‘LB&n¾àÝ‚ñ¼‡ï)…àK¶Ígm›5åTÊB#@£aðÅp˜Óä7—/ój[‡|ŸÌ4Ô|ÇaÍš5ÔÔÔpêôiNŸ>M~Œ<žábî¬Y,kjbàjÙ¡5 &…߃\‘É@: ™4"†l²YÈ瞇©53ª«¹cÅrj.$9ÐÏ•öKÌoiá˜ìÚ±ƒd*5Ê”¿N”Ũh§4 îÓ@›Ø68V4Æü¹s™SY‡ {ª'®Öš¡¡!8@Yy9s++øõÞ½;s•L#rÙ‚óÖu¾ö}–š÷ZߎÆXiY”I‰=³¿±‘7÷ìá­drÄÇcÛ6M³g³º¹™P{Þ±dóyT}=e--T/˜O¨¬ŒþžΟ?ˆÇ˜;s–i`(…P¦TW”s.^¹ÂP6ƒ_\dãñ8åee\¼ÜIZ È'v̱ü!ZONóNtÊ)Dª"ñ(¡†JˆHëwcúFÿÏX^ˆ A“iReHb^Êf B!,Çyß»pŽ&Ÿxå{„L“Úº:åt\îàr?éâg„ÃašæÎ%Ý×KÒuQ†QÐMG| ³«Pqk N€Ñ$D ƒ»6làÓ¿õ[,Z´èý“Ö<Êf‘RpòèÑqÉGA4CUVQ]=G Êj4õB0CB½!©‚JCP& ¢R’…‰QÆÉω'ø™m3tàÀ5æ Ó$l›Æúz~çw~‡ÙsæL›èVÞuÙ[,CpóùqÒW æÌšEÓ¬Yt¶wpF †Ba|Ã@{.ŽeQWSͺ¥ËH )—’rCRf$ IÜ4‰˜1Û& aIÉàÀûöíヹpáˆs»··—Ýo¼Á¿õ[ÔÔ×cGcX–‰! LCbV±*Ýn—Ê»#ü À ò~@Ö÷Iû)ÏÇS¹þ"–Åѽ{ÉM:­5ƒƒƒœjme^K ›—/££ØŠ£Ê¶©°,Âhòƒƒ¼òÊvÔÐP!;½6L|Ïåh&ÃèÑ‹ÅbÌ9“àêUrÉ$Ö¬YT.]JÎ÷8±÷GÏž¥½¯·P@Zt³DÂaT6‡öó.PQ’Mçh;s‘¤q`µÆhíů„µ¦ß8_X»0 Ffw•­Ç€§øy)%A9¢63ÍÄì,õÓól¬¬pse…Í~›ÿÜìì,‹§O³wç^ÿ@~c df_ |tÒ?iÎûëü›vö`Ò5Tõ\‰$à#¥<€T.ò"_,ôÏA*‡DÒ1 YHæF÷-úäwÞ¡Ùhœøóõz—^|‘ý½}n I·\ÂJ2ù(:Ÿ\…Æ2¢u­Q’‘¼â©éJ¨Qα²²Â/ùK~ûïÿN«Õ:±°Ö²±±Á;o¿ÍùÛ¿Å9G+QT¤ÂîÒ œòA …I¼þ<²)½²Þ;*„L‘JB½ÎÌü<÷ªÕøÜÏêò2q»C­>Æ*‚6P Š33<ø(qþϤ1Ø4LJ‹Ï8Ë2c''‰7·1Ö¢…$ÐÁɃc˜;}šêÈ(a©ÄÎڟܽËÝfcОU«U^½v ÚZ[[$&Cˆi2?ùMHÒT (Ïa&æ#нí¢ír¾§žaÀÇØ|¤OÝí’Rb„À8ñX.™,}Èm ‚-t÷ßÏ6‰ÄŽ·vwyÿƒ¸ÿþ ´ï¸ªgaa©ÉIîîµÙˆÊd¡WÙ+çŒ!HtS²†Ñ@3¢u<U­©D X__ç×ÿò/¼ûî»ìîî>•zH’„;wîðàþ}”µt²Œ¦qì;GÇz‘bâœßÂ+þ‹¤­}úE¨5¥0¤ùÊ«ET*e¦¦¦;DXkÙÛÝe¯Õ¢ŒC} t /Á!&Hµ¸+k¡ßc: E÷z=š­#§ç FGس–XkFGFÑÇ 9:>¤“$ˆRÄÚÚ*ܸÁ§«+ìæíV\8žsSS4=b¿×'3ƃq–Ašxðé÷S}ò4èÙ+C¡^Öó¸¡?Ëý#«x†‰ìgI{¹MFa© œ¿{Éh¤yõ3Ðîs0ƒý¶êyJÓ”û÷pýúuöööN‚Z­Æ‹/ÒîtùB…¤Q„C ²„Àf”’„°£±DÖ0¢55­¨jMYk*QH %;ÛÛ¼ýöÛ¼ûÞ{'¶ZÇ fçç1Jѱ–n~ÁJçÉÔP@„ ´Ž@Y?j/*íü|¦à ;¿¨9>6Æèؘ_â›z½mΟ;‡KEOH‚|÷̆&ó›¤_p•ÖBs—…‘*jè>Øív¹ûà~òæÙ^ß`×XƧ¦˜X]fs¨ê*žû“›7Ùk¶ˆ¤`kk‹«Á‰ø IDATÕöóvXJÉÜܯ_½J²½ÃÎvƒNf0Z5È,¯zâ’$>ÖÃùyÚ.‘ËÕÝá|„ b7ÿÇQþr=ùy¦÷ZT>NO U?ƒ‡¾ê‘_Ï^ׯvËæâºÏn|ÆòòòÝggg™™šææÆÛ¥ˆL(kÐÖf•$!ì÷DÆPÓšªVx‚€P)zíŸ\¿Î»ï¾ËÖÖÖSGkÍ™³gyñÕW™\8OVãQT¦†„Ê AKRP²PÆ/•Ü"†½˜…°›ßžXÆYFj5j##(­I‡Tëq³Ûj!­A ˆ… '¡pÒû/› Å‘9‹rÒ·û­‹cuN逵,Å9GÇܽŸK‹‹\¸|‰=çxØî0]¯³0;ËÞÇô‡*Nc ;­V d™Á8;¸>&''ùε—(;X[]¥™Æ$Å{¶•¦xâ>ÄǀϗyV¯“š9¯| ìþÈÈf?µÏ´Nlæ!Úv7ì"}[ï<ß‘¥)ËËËܺy“öþþ‰?W©T¸´¸Hj ÷¥¢F€@ÙŒÀJqJ9Ž û}´DÎQɧ”û$»Ì°´ôˆ÷÷;VWWOºáJëÚ+¯rùõ×ȦgX-WØ–šX*"%© OÇùWæràq9¸Ñ"¯‚ŠKÂI™ƒTD¹EÑcÕx–eôº]_u I¦$±TĿ„˜0$µÖAX‡´·ßf²ßç•RÄZûÀGh§Ñàí÷Þ£V­òʵ«p÷­]fæÏ²×épwss°*QLî !çõ—^b¦^gcy™nŸ (ëÀøu‘Ä^•Ýëõõ µÀsáŽÃùVÃ:vh‰´Øçj¹ÜãÅølÊã‚¿  ™.ypõàÿxž¯êqÎÑît¸uëKKK'r=B&&&X8·ÀÚ~‡Õ0Â:ËPÖf)å4¦÷ ã¾çX¬¥¢5%­ˆ´B­½]>ùäîܽûÄH!§NâÏ~øCf®^e­>Îý0b _ —¥`Tx_§ áAÇq›ïJº!ÿÕðäb{•ƒA 4NøUžã¨­µ¾²~O2“Þ=3v^AíÂз^YFf4ÖøÔaŒ¡´µÅç¸f<ˆûƒååKKüò_ÿ•Ÿüð‡¼zùw––Y_w,., [[Ä'¬í)«ßyùeÎNϰ¹µÍ£vÇ»I*…¶m,*K àé÷¡ßéë¯á¬ÉëëGé~Ç=Öш/ÕÜ}ÕÏó¤‰ p´Y°_Ê¿=²,ceu•O?ýô‰¤o†œ9}š(Šx¸ße·TÎù;MF”¤”“„jÅ}´ œ;Ø—“e¬¬¬pãÆ ZÍæ‰í–”’ùÓ§ùÁOJõÂEnŽqSìæ>>¥<4ÀrÔ—Ï=õt÷…´·'Ä‹;‹Í2â^ÿÄ×äp =©œ I*¼¬Ã&|•¥X)p6»ý6³ûûüplŒýÆ;i:ð]¾ûàÝn—¼ù&W^¼B<ðíÖS*¬ïýèGÔ/^âúØ8ŸŠ€Va¾Ê.óé“7s(kcöaØ@ãv¤/$JJ²$as}­õµÇÚ@¥Q©„’T ¬òÚ³Ôù¿ët€ScV¨ƒ6Ï:d–mmòÂHÖä)~»³M3=à–×Öøåo~C«µËµ_`´Tb¥Ó¥>3ÃëS§hwºì·;$ÎP©Ö˜žš¢>6Fß:n´vYëÇ$N …"$CZ‹6)Qš¦1QÒ‡b¶Ûíé¯õÌaøîï†íì—¤´¿ÁÊçYAÂ9 ßêy¾ Ñl Íf“Ï?ÿœÖ k.Åg\¯×™š:ŃNͰŒ•’Àx®'JcÊIB%Ž©Ä Aœøl6a¾ú€uìíî±¼¼L§Ó9±Â ‚€Ë/¾Èü¥ËÜãSÑ—g³Igý¿ë¼ùX)÷Š„ Ì­Xn ~?оß;¼P0MSv[-nߺÅúÚÚ¡Ä‹¢â«Ž’ A_*Òœo4x»´W8[er¯Q¬´Ž Ó¥¶¶Æµs $§¦x{‹fpY–±»»Ëƒå%NÏÍšý4a£gÑŠZµÆôØJù¥êD)îµ{le){Y†ÍR´ÉP&C§™ß¡‹ÊqŸ¨ß§Óë#zyåÓÛÿºÀÇBðá’R¸?ZÌ9©›zêV,û¯ö±Öí[üyî{Wše¬¯­ñèÑ£ÕÌÅ833ƒTŠU'é„¡k4†0M)¥)åÄP9‰ Òe´ƒ 7ýwiÊþþÛ[[O¬°*• ç/^ÄŒŒð *Ó’ üjq@ÉAÙ9*@ÅA(9K„7Z×€…ÌçV‰¡¼ÎÁÒ©µ´ÛmnÞ¸Á§×¯³LÛY.—™8uŠžÖtT»úˆ&›{T!%NÈð/f¯P§üf~Z˜³9ÀB“¤)Q’PŽc*ý>Õ¸O­ß'ìõh÷{yåC«×׿·3éOMT÷{–¿úþ÷9W®ðÏqÊ£8!È%éSyËÎR‘‚²”¾âÂO±^ä,Ή¤p 2L’„Õµ5Þ}÷]Þ~ûmÜ¿Oÿ³4¥3ssœ¿|™V©ÌjÐ·Ž¿8Z,r k=äÓ„È5hC$RÊB4¤6#k·;4³Œnš"RáœÍ-g-Êf(cæ`g“|²­œ'Ùcˆ2C)K©¤å,¥–¥ÈÔOE’úÅÒf3Ñ¿¿Óé[àùöxÆ–+כܹs‡ÍÍÍ'þ­5§N‘ Á–Ò©‘Æ{.YJ”ÆDIB)Iˆ’”0ËP™Ém^ ;Ÿ©V*•©¡žxÖZ6Ö×ù§ÿñ?ØØÚâµï}Ÿÿ÷ìY’‘1–²Œf’’Ä1a–Qu–ŠÀû7!Pä ŸøEå?à•Ê|öÙg|øÑGܾ}›f³y,ÿ$„`l|œ×¿û]ÔÔ÷*5¶È'Xdh)ÑΡ¬Ad)"Ë™Av6ÅžeŽ|Ú B%RHØÜ%‹\«E¬ý¾›V~3Þá¹-e3tfÐYŠ®„Üm²²ŽÀYBcˆŒ¥d2Êyì³Ë2¢,ë ÑHõ·—À׃?_gTÎÆ–k¿Ýfiy™î‘}¢ãÀgrr’¶±¬è΢!L½¾§”¦DYB”e™¿[ c¦Mø„Ú‰©S,\¸ÈÇÙ=ÆŒ¾8²,ccu•ÿõÿÀÍ>âìå˜_X`~~ž—§§©ŒP k„J¡¥ôà#¥tv l6÷ÙÙÞaié÷<àÑǬ¬®@ç¤ê«\.óúo0séwGFx ±±(—¡¬@+G€#0’YT{ÆzŸåÜ[k°m À’9çEÎá°ˆ$E†ˆ•ÊÁ'çsŒo©tjl†2ž¼¹y r¹UHþ:Bç|ijóQÏ™1DÆ ¬ñ¼W§“} >ÿ¡ªˆÃÕÄÓ9vñGáÖZv¶·ÙÞÜ|"ÿR$-Ô'&X±–rä‰Oc LæÉάЕd„YFh ÒšAô±÷m²è `|òW^~™ÕÕn|ò ½'Ÿ1†n»Í£»wYyø Š(W*ŒŒŽR­P®” òø™ƒùŠC–úê¨Ûí²ßnÓn·éõz$I‚ÍE{'½ßr¹Ìwß|“K¯½ÎòHÛ*`ßø÷£„"ŽÐfÈ4AÅ µè,ON±Ö¿w—‹#´ž .â~°©_ÈÕšDàã=”uyõƒ¶|¤õü—t œCcÑBµ>÷ÌYRëÿk=gôð¡ù|þDæhƒëŽ´ ϲ•íœ{ „¾)@Ic-ëëëì<Á³§à=Æ&&ˆ*Uvú)}©‘6CåS®0MÚí=”™&œ£\­²pé?Hb¬|þé'tŸ°K6Ð#% i’Ðk·imâ1Ÿ›8á{{–ƒRЉS§øþŸÿ9³/^á~½Îç:d×X„ôÚ¡ÐYJNRrŽ2 Aôû¨$öm˜É|ˆÃ·‰‚$vœcЙ"R)‰0aˆ(Q³•¤9ðøÅ]á¼G¹s(áµO Gë | –p^ó4›e¿Ÿ?1ÀqÇ€ÎpFx1¶}’b÷¤_ @ú&ÁÈ‘/’®¬›Ã5|H)©Œ@ÐH F‚Ì\ÎEøÊ'ÌRÂ4õšë­S=øvR(ÔÄ#cu.¼ô2”+ŒÍLsë£ØZ]%{†ü³C r  |æ³x ‹‹¼òÆèÙ9>¯Ôx 4¼JRÎW%));KUhÊΡ­EÆ1*I|ij±(“9äÜÌ0(:kóŒzÏ)i £Ñ¹yô…EôéyäÈ(ÖÒf“ôî}’»w Ñô;tÑ\(¼-hÇ„¼ÀÒëœ ž¿xúoÁ罕ÂGÒ¢®Z…©)ÿ˜‡Ñ:T*ˆ `Þ9~j2ÎÕjT«U¬µ‡ª‹aP9ØI;ùñMTDÅ©¸ßn³¶¶öD±ßÁ…9J*%;Jc… ÌG¼¡ÉÛ¬,#Ì Aæ[ÏKX(¬òÿõU€E A¥ZåôåËè‰IN]¼Ä£[·xpóÕUÒ¡¿ßçQ€ÎâÅ‹\¼r…ÚÜÍêK:`ËAlŒO[qÎï¨ AG IÍZj?ÝJúè8Fk•W}Æ“ìC¦~ÃV2…Ï–’`fý£üùŸc&Æi÷zìwº)(½ü2c?û郇ô~ñ øàCT{߃~/M€“û)rx¾·vÄ%ý[ðùc!%•Zpv{õ ¼xæfɪU’ ¤/ Îm03kÙ7†²T\ª”Ùi4èözA€ÖÚ'ËæYô7$ƒ?+ÆÍ'ýz´"úz€Hä ŸŽ¶77ŸÚŽ(¥¨ÔH„`3±Ÿ•ƒMe„i†ÎRkÐ΢ÌptÓÐ{È'_*o JJ1:>NR­!Ïžcæ7èmn±³¼ÄÆý{´ÖÖˆ»]l–ùjóK„3Š¡\u)ý$ldt”¹Ó§9½°ÀØÌ,º^g·TæS¥h!ˆ­#“Ö“äΡ£äU%1ŽQ£8jx$'¨4!ta>óU‰<ÒÊ¢Zó'jqñóŸ“\¼ÀÍ7øõç·øxk‹†1îé™3üôÍ7¹üÿüßÄss¸þgD£áE†î`YMüÃçŸæ¿'ðùv ýUZ+­5 ‹‹ü_ÿý¿3·¸HWJ{û¬nï°úùç¬7›lìï³ÓíÒìõè¤)©µˆÜŒ¼R­2V¯3>>Áä©If¦§™›cff†ññq¢ÈûÞ8|Þ¸’>îEæ) CóüQÐpáW Ãì44Ÿà—\<_ÔFFèûA@(gÑš¥ƒý® 3þÎïr›8Ìùà,‹eÐ(á¨`é•ËtfæHÇ'Y\dü{ßGôº˜½]º›Û쮯±·±NÜnc9 ‡C‡ø4‘/²a@½^güÔ)Æ&'©ŒŽÕFpå2ý0b]IZBÒEZŸïn±H+Q¢DHÊÖRŽºÖŒ Á¨€ yªJš¢ÓbüŽžçs¤Úò'baûóŸ±?9Éo~ýkþçýû,ç [|_Ýn—Ͼø‚åÍM~òæ›üè¯ÿŠž€äŸþ‰¨Ùò«NøÉZ8rÈõ¸³Eÿ©\Ÿ´î‰S‚?Íi•W¾Öëu®¾ô·ú}~óÑG,­®qwg›•v›N“rì‘edYFÇ4 îß¿?ø¿‚ `br’Åóç¹xé/¾ø"sssH¥°Ö"•BçÓ%%:ü¸¸ˆ~‘rPý×¢}¹¶Kàœa·Õb(¥ó¤£T.SaÇA&¤÷1ÎÇÉÉ2:ͧ1ÎæZr¦ƒ•)%‹2‚‘ƒ_‘Hœ%µ+ )ÛQ™8Œ£ãD§ÏR³¯3‘‹½Ž(õí1yp‚Ë7ÝsËT!qRøöYJŒ”¤B±§= ¹ÿÞú´X¾Ð”´h' …£,òªÅ(P—‚Q))KA¬ò´Ž,õÕ®(’cÜcƒá{=ÄÔüèG´GGù—wßåÿ[^bㄪ®Èû_gҚ7ücº{{$ÿöo”:=Bç°.däçJQw­ê?†‹NqÕ?ް^+.Òã&:è)Ηý ¬µcè÷ûÜ¿ŸþÇä“?¦·¿OïI€óŒGš¦l¬¯³¹±Áûï¿ÏÄÄ/^äï~—+W® ƒ€žíûl¦0ôœI.Š+€èhEôU¿ðÕJ³Œf«õD#¯‚)W*„¥{Œ¹‚×”u¡C1\²î åâà‚@ˆúW ¿IA&¯JÖWPmëè:Çžs$9H8) È =ïᬷÐ(D}¹±Þàüg†…éŒÍ]ŒJˆÁ&¼ÄXA(,e¡ü™ŒHɈ’Œ*ňR„¹ÆLX¤òtŒAô‘á‚sj5xõúss¼óÉ'üãÊ2›Oi' úí‡21>ÎÂOÊÞÆñ·)§ ™ñ×¥ËGêE,µ=æüøƒ‚ϰ½:JáTže-}C_ˆ¯’t>|ÌZ;˜è2G‰Ó£ ÷ÇTÕcH’„µµ5Þzë-~ýë_³²ºú˜Â×õœiš²±±ÁÖÖŸ|ú)‹‹‹üøÇ?æêµk$ÆÐO‚0$Ô¡V¾)¸#y8kê$‚úYùž^¯ÇÞÞžOXx _EAѶ+<_ãóÂít´É§Ð· ­ “…õ­ÊÕÁa^‘˜<ñ¥üèYJ"k);KÇú8bçH_N-|™ “gÓÉa"× yùõ³ÊF ·iW‚C#r£²( ¨*AU)jZQ j¦(F*ª¹ØO¹ƒh§Ç¾› €…s¤W¯òÅÃübi‰Õ¡V«h³e> 36wD䀧{ïúuNýä'¨þ€ÝÝ]â­-ªÖ`2ƒ5™ß3Ë+AcÌQú:Nˆ"Ÿ#]­Àä$¶>†ÁVÊØ0­ü‡a,.M(%)?h5™™ô¡iú¬¡³ùºR´YY–Ñétøàƒø»¿û;>¿}ûØ* xíÅ ¡òDL1t!Û|2¦òÉX‹É2?R=¦ìµÖ²¿·Ç'ׯsçÎ^zé%þ·ÿú_™š™¡ÛíÒË« rÆgÏkÎÿ½áJè¹* âŽc-Ýn—½½½cGÕG ‘: Zˆ—ª*·êÔ9èhëòÊáÀ]spCª¤È g'N)œ¶8«Nçë–ÐAä,ek©:AÏXbç|kæPBιÁûóŸEîôYÜüÜç;> ó`åZ‘'`HAIIÊJQÑšŠÖTÃJR ÊA€ph5´îà½ßÇν±1Ì•«l÷züë½ûÜMâAà_ŒÕjLŒP“'4:mÖÚm:C ª—–øâÑ#^¾x‰Ö•‡4³Œ´ÓÅd).Mýª‡1c|ŒOalVp›ßøE§5”˸‘˜ŸÇœ?™ŸÇœ:E::ЉB2å#Ð,n C ×üÆ065EEt»ÝAØ_ER\Óœá;ô„ +‚o¸š%IB³Ùä—¿ü%ÿ÷Ïæ…ï !RkÊùIV"ª¥­ •wÊ@Ï9vtƒ€™Ù¬µtÛm:{{t;ºí6½n—,££¯©Ûéð»÷ÞãÞ½{üÍßü ö½ï‘YË^š‘” e‘µ„Zcs]‰ÖúÐÅü4:Ä× A§Û}&¾§¨|¤Ö´Å ‘»újGÛì€òÖ£Èã±JXJéÈ9nºƒì¹ƒøGEz™ /ýÌ8Kb-d (‰sóâ–;Dú뎊©~l­…@ ¥"”‚H*JZQÖÿ5(‡å0¤„ZãœCK5hs†'LÇV=³³ô¦§¹ñà>×÷÷ˆ9PTŸŸŸçåsçítpKË ’…ó|¼¶Ê­’üüét:Üøâ .œ;GùÊ‹ìmn’mna“”D$ÒGU 0™óåø|3•+\²«U¯c1—.“ÍÏÑ/Whu»¬®¯ñð“ë,7¬îíÑè÷é¤)&Ÿþ”ÂZ¹LT«1wú4 œ9{–ññqjÕª¿+yHJ1«!žâÐø8oïþ”$ ÛÛÛüó/~Á?üýß³³³sˆ« ‚€R©ÄdµÊÌè(ã¥2å|oF™ —$dÝYfH4ŒeE ¶êcœyñ*#•2"MI‚íõØo6ØY[§¹±Nkg‡¸×ǘì±×ÕØÙáþÝßÑh4ø‹Ÿþ%2ŠØëtˆÃˆrd)C©¡ ¬t­‚žZåÐëõžè$8 >:@Jö¸<·h¯Š]#•·¾åâ`ó:ï{޶ˆ‡@spáú˜c-‹E?Mé+EœeÄÊCj©µdÎæfñ‹óI GdŠƒroØÑpPíøçó|Õ£$¡Ô„J*ox_ "­ýM ÿ5 ‚ÀÆk%rz ükUÌÅ‹4ú}Þ_[g+“ 8wú4?xåÊ–‰?ý”½~ŸìÔcgOsvf†µv›­¼2ϲŒÍ­-–ÖÖ¸07gÎÐNbD§‹” )ú~š(¼´©óRÂûoß øˆ0DÕFgÏà®^¥?=MËd^Ž1¤Z“IM¦VJVãa&Y‹.IÉhXÂ…%¨:Jã‚ÑÙ9/^âÜî.;±zÿ zΡJÈ9Çþþ>ÿöÖ[´;þúç?§42Â~¿Gl ™+‘9wì­‚žX áéSâj†!H”Âåcti}{äÁÇ íå­Ç1ÍõÑ×uôõJრ‹%ÑH¥”´"É2’Ìd©5¤Æ’Ùƒ‡Eä܈ŠöcüŸœCä¾Bþ¹¤o·¤¼âHoù>Ù4TŠPZ @GU~>Áã À“¿YÜÄñÔ)r³ßóÎŒR21>ÎëW¯RÝoߺÅ~’Ð.WˆµB#©ŽO2ZYc§ßð?N‡Ë˾ú9s†VsëþZ“¾µ ðH“sº››¿ðþ¸e¹L83MxùöÌY6“˜[7oðÞÝ»Üh6Ù5æKs/Y–ÑØÙ¡±³Ãõ?¦^¯sáâE^ºv‹—.Q#ÍRzqL„„QH“¥J)DNn«|âðMMµ:Ÿ|ò o½õÖ ¤®h+NŸ>Í¥^@‡!w?¿ÍøÆzoŸ¶µÄ: 4iùÖ4O'|МCÞ!È[›žsô¤B”*Œ”+Lšbrñ<ë·¿`éömZÛ[‡Ö >íã?Ä9Ç_ýìg”GFØR©sþ.ÈDqÊgÏ@2¯‡i–=UÙ< NÒ¼…‘Λ[yÞ'!ç‹ ‹Mî§H2Žê™ŠåP­¡Ö¤YFfŒ½Yv:ÆxnÍZŒõŸ‰uv•3XÜTWC‰'àgá.ò‹6 ¥˪֞o+&EU?$ƒPCÑÊÇ}îÎ9\açæhKÉõu¶óO†œ?{–¹zþ§ŸÒîté–Kô¢}©)ã«T)WQ¢9¸^’$amcƒÝN‡ÚÔ)ìøí4!PÂ/¾Z*ôLy©ÎÁô´Ô¿¿ t¹ÂÈéy*/Ò¯T¸ÿðïܾÍÇ›[l§ÉcN«>ÃZJ‰.¸™ü*¾à¾1 ÄÁ8/?Œ14 šÍ&7oÞdy°´< IDATaa×^{«×®12:Jœ$¨XE%¢ @k…ÒòÒú› Ù’4emm÷Þ{å¥%Ò4ôÚ/^äå×^#¬Öøèî]Þ¾õ9%&Šè—4©ÒdJúp8qPÊ ë§>YJhÑPFT‘­ftô”flf–³cãŒÍÌpïúǬ?|H<äžWÐõ>BkÍ_ýüç”ÃÝ~LšOvlîì¸À ³äøs< 1tA ²Ôk“žu _Aœóu~¤{˸ávË«z'܉•ícãçáI”èü¼ sð1Æœ¡ßÛüæâ73s‡ÉþAö›m—yìv>LP¹÷Ô”D+ÒêPõ>Ìkš\‰üTíU©Œ™c½Ñàvû¨s!áÂÙ³¸Fƒxs‡~©D/ŠˆÃXIbëaH¹¡„$ŽolmqñìYÂñ1š½>m ²ŽÈzKÀ2û‰¶µP©|½à3¬¡ŒÊ%&NÏ0zî{½×?ü¿ûûôœRJJ¥§FF˜©×C"ç}rÉï°°o ÷㘠\ÆdÝv›ö~›$‰@䜣ÓnsëæMVVV¸sço¾ù&‹.g†^’P.— ƒÀ¡©°ÖýÞ¦_IA>ÝùâÎîܽK7¿ØÃ0äüâ"ß}óMª“§Xî÷xÇìJI'Š(‡!©T˜Ü¼°­, ¤±DYBà*ÐTcD¨à/x;4•1@Ç8LX¢~ù2WGF¢Ë_ܦ?d+1 @µZþä§t¬a/É0øÜzÇAJ¢Þ-]äûÊG%î1«YG’¦¤Iúì•åeƒp67 +6«‹ÑºŒ¸qî©z¤£Ð0móŠÊ*:hŒhÌŒ1ÀsÞ5bèâ<ð¤ôk2¯¸†AHJs5ôûü屃”§ðlv¬N:6ƽ[·XÉ¥J)&'&˜#ùècº.£F$A>ügÍ€›:ú4Ýn—•õu._¸@µ^gswN–Q¶ž#ì™ —¥dZ{ŠÃ˜˜øúÛ.„aÀô§ægÙÙÚäÞç½GKlÄýǪR©Äôø8—OŸfZ”z]ØÝôÛd½¾Ÿ (& BZ^X8ÏìÔÃÎú:kËKlonÒïv d _Ü?dmmïÿà¼ñÆw!Ðì¶;„¥ˆ(ÑÖ ”ÆþžïÅX}·Õâþýû4 ¯,–’©©)¾óÆÔNMŽÒLSºR£ëut­æ× “¬[*kQYvHÛRIb"kÑW¾ŽåÐ"+ÀÇF@ê¼’w_Jêóó¼øç?@xxëÉШßå¹é¿{ï=&OMqù•—Ù‹Zƒ”O" )Jæ¦]ù]¸˜ºœ`SêòÖÙØgß“R`òé¦ÈÅ|E›%) í–x!äÑ*h„Š–Yk=2öÐïðïN6{ ÇIAŽÛ¯{Ø< ðøi^€§+àóN›beV)ÅÔä$‘´wÄ*" C2­qRúÏ3M >óìúc{g‡~’P­"ª5â,£k-å4£œ¦ˆ4%Ó( 2ƒùù¯¿í*šÓ33ÌÏL²³ºÊ»ï¿Ïï=¢‘ć.o¥#µ×.\äÂø¥æ.Ùò:; â,ó Yq·G°!áºÉ—.ŽŒ271ι3g˜»t™ÍGxxçÛëë$ù&rAî®®¬ðË_ü‚f³ÉOÿò/Q¥û½=ëM†¢o„óI¡Ùj±¹±AœGÃDQĥ˗™ž›£[­ÑŽJt¥F–Ë\yñE^;{†kÙÝi°·½MÜÜŶ÷PiŠN¼}gfìÅ1‘3è(¤.“a0ˆo.¸?öŸkb);è9™žå…?ûq¯ÇÊÝ»˜!¸˜‚ýößÿ™ù9&ÇÆyhŒ¿ƒe!³œÀÏAAȪ$È«?†Àè('ò|u¶ËDf9¤ª=–„|†Öî8µ|‘ÒyÜŠÏÑuŸç1sû2N_vÁ×7=M£Ñä^γ\ãäØ8.î{Sý@“é+B€vv0e•ÇÄÒ7¨Ý½=jÕºR¦'ô2C/Mè§12‰I ZCª ª¯5·K+É\}”ócu:›¼ÿÁ‡àþÀÃ0dnjŠW/]bFJ²»÷Ø][£›úB’!FŠÉy~2¥ÆÑw=çØwŽHHTÍÌ0?9ÁÄ™³<øìSÞ¾MgÿP´ÛjñîÛocŒá¯~ö3t°ßí¢ÂR9ÁüƒþŠ””,ËØÛß§Ùja²lÐkŸ_\Ä…!ýj•Ž”¸0`a~ž ç¸8R£&%êEI¯ÍîÚ*{Ñ[]Çlm!âô©ôû„Ö «UFÍXˆL†8ã™s¤Î‘8GßZúΑ!==Ï‹ßý3º{{l¯­jYŒ1,?zÄûo¿Ã_ÿïK”f¹±•A‰42“(©PÒ ­·ß<È$?@…Ä^~„þìðp “´XFY‚¡uÜ :æ¹'´Ç¨ã«hžTå:B²»çs惛çÄëܾ”û2ˉðÈÑétØi¶85>IX*‘”ú&£Ÿ$ÄýÆzðÑ Æì×WùHá˜,—X,—pû»|úÉu>^zHóð”Ëe.;Ç«.PÙÝ¥sû6­æ=%IÊ!™P>_:‘Oáì€ï(LŠœô8©ÍŸæJ½ÎÈøw>¹Nck ›÷µÎ9¯$þÝïÃÿô§B°'Ô²ÌùÅó]ÏL4K¯Kqø4„át‚±±1&&'I£I „d~lœ‘J™öÒüö·t»&&&8;?ÏÔÙ3Ì.,6t>"~pŸäÑ#¶º«„iŠÌ2jAÀh)øgë'U©õ"¹ØZzÖÒµ#4³.°¸¹A{w÷ÿÞôü³O?áÚ+/3{æ Ÿ˜ ¡ý‚2 -óõ%‘Vx>ªØ£*¦CgçA†á—«|œ;ˆév!V'æ~E©È k:Oš/cc{À|Õµ Wˆ¥$î÷yÐé` îPJª• •R™duT€UÊÇ·O5–R–y³2{rÊG£ÕDšJ¥Œí'ÄiJ\Šé—"t/$ /ä” ´V_øT”æ|2jS–ï|Ág÷î²ÝíÚ©V«¼¸¸È«çÏ£7·hÞ¹K«Ó£W.‘(Q›çºËÂÖ1W°ª,#Ê2dÜ'È2J@%' ui££œ{õU‚RÄç|Àöúú!j·Û|ðþûŒOLðÒëßa/Mé¦Ùs ‰'Nåsž<Æ Ä„å2áH [®ø‘j²±ôˆ›}LóÖ vö÷QZ3Z«qnv–‹gΰxú §^y™±.Ó¾u‹Ú¿þ½´„ÄiM9 ½$_Ç8XÃãü˜81†ØÊÆßÙ:ÆÂH ×^bíþVîÝ}L Ýh4øðý÷ùÛ³g©:Ç®óiA®xœóŠc©<7%¥ßïÉ5&ØãHkï9ô¤øš“¾‡èz{<â÷£v(|Ý{ƒ¿·Ä8&}ø…œº¹B{¬R!RŠýý¶7Éù:í¼os”ùŠÀ𧆯Z­ÆZ*¥E$IBE$aD…žó)Hç¯kÚ%…`: ™$ÝËÜýâ{{y+sPñ¼°¸Èë—.#ÖÖØ¾ÿ€fšÒ«”I´ò9Ó€(–óèÛ0K Ò” Ké¤)²×§d2jF•̉TâYµÊé+WqÖ‘¾÷.­­­CÓ°ÝV‹~÷;fç盚¦—sLÏ(Ïpj­¿ð…@‡¡_Èùƒv»ÍþÞ>§ggcXÚXçã·ßfùó›ô÷÷Äl£Õ¢¹»Ë­˜çÂÜ—ΞeúìY*W¯lmCž·¤¤"ÐÎõ#Ånó½¯Y罡ëÑì,‹W®°³¾FïH€Ÿ1†»_|Ask‹™SSlXËžƒÀ:Bë<%enæå—6‹õq¤êq@ Õs¶]Ç ÃíÕ7½>ü§báâú}Äò —Î/P£ô”¢T¯“mo‘î¶°&CJ…ÆKÂÄÆ%SDæØhŸ~¿G5*!£ý$$ŽJÄQD†da:ðm—ùê•‚P)棨Ûfýî=m¬ÑŽAÀâ¹s¼vùÄÎ[KË4Œ¥W®(5(•µhk‰²,Ï^Š}þRš¦>ÌLõº”aTJƃ‹WÛÇ€ÇHœCT*œ½r…~·Ã­> ÝjbçWWVøìúu~ô×M7Mý¾Ê3Ä»a’ô9¶¸]Þ"Tk5Æ&&P‘& FƒwÞy›+½.ÁÈ[; nöoÝ¢wÌΓsŽ~¿Ïòú:ëÛÛ<ØÜäoþâ/PÓSP.a¥·ƒpB ”@*9¦))s›‹âRk<`(5°ͪŠó—/sÿæ V†&ˆÅÑjµ¸wû6/OÏû É"çõE‘µˆœóKž2O蔹¸°PØ9ÐAHT*Z8|VÑT‘È) ‹Tñ8ð|kk7LÍZJ—ÒŒ™‘ûÕ*íÑQúIJº± ÍÝ\‘œøUkˆöÅ)ú •OÁûtº]¢J ©5i‘„1I’¦¨|¤‚(üz*Ÿr ™TŠd{›Õ¥G´ÚíA»UŒ’_»z• ßcse…mcéDeRísäž,ñI‡å8¡÷)Ç}Êñø´³ÕëQ¶–z0^Š#äÌ:R±óÆišrïÞ=^ýþ÷©…$BPÊ@)·úŒ¬#þu ’3‡½›€¨ùu˜¯z¦wÜ· sÒ¡¬e¬½O©ß£ÜlQª4Ù­lÓ.—H¤ò+,™Á Ðø W>îdë“n·K§Ûedl¥Ò $ C’ " ² ÀÚÛËîëá|F‚€rš°»ºÂæÖÉMµZå•+W‹Jl—¤d$ +—ýj1|¬%q^ݳŽÄXƦf¸píÍí-¶VVµ_;ÛÛܾu‹3RÑÞÝ%ËÌ3ÝtÝPÛà†*›§õfJÔ꣜;žÛŸß¢Ýnc²Œ^·Kï˜lî§Uœããã¼zå f§ÁÖÚ:í ÄD= oó­ìœ”Gd±V’ÿ}­&„“Ò •A(H3åÌ·>úð1ðqαµ¹I§µËøÔ4w„ +%]!é8GÙAHœÏ“20Ø·rÃì7P®T¨Žø:=Ž¿³õÜsõÿ„Ç€'˃$!“‘†6ó\!8ßJg)å,£'h“=ñãM’„½ý}ÎjM¤. Hƒ€$ô@d‚¡¶+ýz*ŸŠV¨n—ÆÚûÝÎàõ)¥˜Ÿ›ãÂÙsì=zÄz§Ç^X"Íׄ³èÔÇ«V’„j3ÒïQë÷¨öûT’„ršPÊ ¡Í¨¦2ˈ¤ FŒ”ÊØvqŸxáiÍôÔo¼ô2Óõ:k«ëlôbZ•ª_Á¨”é+IA¤xQ¡œ,OO±‰üýI‰0VcüÔ)V—–s̲ŒÆöÓÎRÐ’žþÄ’| 4s–@x /|”~Fé TŠ­£O¶?™²°H‘±ß˜ ÁØés/ä,GE+\¾Éž©|å£òi—Ö_½ò±Îû¨´›Mv[-Ò¡hzzšs§O³µ¼ÂÐBO„Zë'X9ªVÓ„Z’0=Vçüw¾CyrÙî ··‘[›ˆÍ-ØÞ¢Ôh1º×¢Fu0ÆÎ9cß¶9Ï‘eô”bæôi.]¢¹µuˆ|ît:ôú}¯yk'r“³‚á,ÀåIwîÁг·M¨TkÌž;ÇwpDÕ*7?úˆ­µU’8~ „†bAÀh­Æù³g9ú J)îî´XM2²R™ž±¤Ja£D(AîE\ ºC;X"Á1=ÂXÆço¤ZeâÔZëÇ>c ÍV•"g‰¥ôF[BÐG‰udJ`§¿{–Í­]´¦>6FµZ(ÓŸ>Î:Ê…IûI…øQ3>IAmò¥×ât³'‡¿"k¼}lá…nŒ_•yÖ‡s(á,BeÉ?wôû}Œ±„ÒŸc™.€Ga´ÊGí‚à«U>Î9¬ñ{q{ŸÞPÛ"„àܹsHëý„½ DªsÙ,E9ˆŒ¡’¦Tã„Ñ4¡®T¥ÄŽspj’Êü‘~jÒï3Ýlò®¯qáêJ¥™±ù"ž@ Â!%H+ýÒ¥ô"*a ª>ÆÂ¥Ë<¼s‡îþ¾Gqž-Zø8rÏ#]ÄÁX]ÉÇÐç`×IHAmt”és ˆR™ÉÓ§Y}ð€õ¥G´[-’8!ÍR\–Q C¦&&851A}´Ž #šiÆÃ~Ÿá°aH oÄnÁ#FÈ!{ÏܵÑZÜqÆïÎ!•Dáy¢@)Bår‰‘Ñ‘ÇÌâ‹ÏmoŒ!À‘*E, –’XHb ¹ý†È=€s]‡Ü%õú(µzæÎÎϵ$IÎQ¶æH´Âî?T$ÎÛþú°tŽ:G¦iŠÉ#|, ¶âðˆœîP ÎøïCJI€7ÏwÅ€„"=Cø­ô¡àÁ'5†PK0š,Pþ¡ŒÖ8å$PJ~eð1&Å%ФÓ9TõAÀüÜý$e A/ üÒa–¢2Aè %c¨d)µ4¥’¤”’˜öÒ¿XZf G½ZåT½ÎT½Î©z¨ReñÚ5&§¦°Ö²±±N¥Z¥V« t,ï‹"òtƒÂ9M(Åìü­fƒ½f“3A@Uô5 ý„¦ƒž*7 ”’Šƒçò1>®d? åüœ#Ð…•JõDðé÷z` A*%©$ÂùvKȼåÞ~cP „8ìñ76>ÎØøËâþ9,Mùÿ©{³çº®ôìï·†½÷™1ƒIp”D²5K=ȱ¿ô;qÊ•r}U©Jnrë¿)‚o’‹NÅÏîvÚNêA–,µFŠó3pæ=¬µr±Ö>ç@P¢ÛíS…EаÏÞïzÞç}ÞçqÖ>…|Æô³øO9[k lll°³³ãUà¥q˜ÖA”9Þp ‡ÞúCiÒ4Ÿ¿D\ÍÉûB†µ%žQF0;‡ãkéõY>P;ï]ý¬W𦦠 ©1ʛޥƜ’ß¾øäyqù`¸¯e¨V«LµZ †(¢P •kýÆ:ª¦ –ÔŠœz‘S±†4ÍØÙõ‹oRJ kíU»I‚®×Y\Zâêµk¬œ;Ç0ËQ4v*”cCy-=Éê¬ÁY‰›žæüå—¸õÅþÁù&¼`´sö¼’6YZe"Â$ÊQÓš´^'Uš¬^§ÈsvÖ#î´ÛÜowéÆ1±T ´¦+YÈ€òÒøtÎ,Gg2Ï<ú›póÆ ”_mpÖâŒÅ)‰³ÞyŸ@.ÈòûŒ#MÜB".ð0…ä2!‡G>f¢õsBâÄDfI¸üSSÓÌ/.ø%Ôc2£²,kh:ƒ¤œ6†“Z¿Æ"&¸¹ÿ`#÷RßétøàƒøíoKQÔj5jµÕj•j­F­Z%©T¨$ ³ss(­™™ñrÀAø>îSy©’*°ÖØÂÓûö)˃ ‰´î`Öú‘È§È /U £5&|¶%ߣ^ò±!!ÁùHÑ\Ÿ8ŠÙfdQŒ1Æ›?å9G$ý5ã”Uc¨ -‹ q&D¹Œøímžll ¥dii™ÌZâÁ€J’IM¤­Ô£%%‰X \µÊò™3,¯¬°³±qb½§Z.!N\xDùPOXt*YF¢*RP—2…†Ö/{ntû<ÜÜäTµJ#è%”RT‚ ·tŽØêÖÑžàËâ›Ä4¤$ ¢Ma­OÿkÂièB4L@‹ûQ‰C*}±ã$„X©¼ þCHo®FívâßweÖ ªõ§–—©ÖëôŽ0“wÎyü4e.$VPÊ£ÄL 0æ~þ£)~Œ1ÞçÑ#nݾM¿×›†Mxúh¥¨T*üõ_ÿ5³óót†Y†…¦áˆ„ ˜qŒNyU‚€tåD¸†n3i‰t’+™eYž¡+„4%ƒë¦Æ–¨G*òÅ-–¬‰J)¤òÌyGØ"p/Ò_ÄY*…%)üh<±]©âœ×‡Ýæò,£ßïûÞX ö†)™ƒ$2Tˆ‰ÄB¢”÷ÉÕZµ†Ù¹9Î_~‰{7o²»¹ù “Ä2qtÛ%ƒ'!˜“ %)Œ ’BúË AÓY¢aÙÞ£–f´* &‰©¶¦iµZLµš4ë5QDM)ÏùXË®³t´æíÅšõº_sP -Ò-,…u äÈKè eƒ=q+Ž÷{ËÔÀ/Ÿ"´Z…ð?vÍ-çöËKcø¥åe¦gg->ý^~§Ë|­NTê¬F‡'ÊÝ>þGLX¼þñ£žòþ.Mõ‹¢ØG$|5ѺL7ˤ)V)2¥(å#*Gì¥ôD;V"ÝIÎl"¼oÇßû‰oKTN„¥Â*ßrY1*QR»y«ÉÒE³Å^—Á Š^’$¥èiÍ–RÔ˜‡G:Öyß"!PB"…×+X¡h´šœ»x‘¯?ýŒÇG<\Ï$ŸÃ{lß\Î –$„Õ ç§‰”¼qîWççØ^}Lo}ÛŸ}ÊÝõ5·Û´‡CÒ¢ðÞÇÖîãÙÊ×ÚãÇ#=R~_'‰cªµÓÓÓœ>}šóçÏsñâE–––H’ĉQÏ/QDš¦ôºÝC¥J)¦¦¦È¥¢«ã1c!æþõ“¹àïSI’pzå,­©)vŽh‡‹¢ Ón“KưC@\Rbdhñ¯G߇Ø?øcŒÍž|p‹¢ ÝnÓétž‰|Êž”ì IOHŠz“´Ñ '$ý¢ëÇwÔ IDAT@å¢9òn_4óQ÷jy{Û!®Ñàg°Äç©ô-—+ /ùH‰Ž"ú‘&š™¥ÑhÑn·G•ü믿æÝ·ßæµ™iž¤>±ôÖ-èpèV“ê[oòhu•Oûýc!^É54§¦°QÄNœ°¥#²çTH£H¤_vŒñé² dC‘Ä §Ïžeye…'ÉÓô¹[Í}óËç)@eñ!Dõ*ÞhAi´V$ÎòëOÏï~ñK†›ìöô _lÜIOðp’ÚÀ‘ ú}vwwY{ò„Û·nñá‡2;3Ù³gy饗¸zõ‹‹ ^)Ò†ƒíݽCsÔ•RÌÌÌÒWš=©žn=Å!Çêäh¿$<ƒê6©V9á"ó§NY|Œ1lnl`‡.(Ážõ7·™„÷£ÖË£ Z‡?öÖ˽Y†Ã!;{{ Ÿ!¸hµZ¨$¡El!FfjŠto¾ƒA:$Ư×Äà‡.ÖŽZRq\!ž,PB€ ×Ô¬øŒ4ÞPJ?í”Ò·]B|{y)%m'hÎÎ1{ê›k¤Y†s޵µ5>þäþä½?áõN—/=ÆY+Ö“XIBë­7)¦¦øàÃáñ3TÆBÍ&ó§NÑÓ›q•½ÐKj%I”¦¦4™>f&T[)²œp$qÄÌÜ+.pû«¯ØI7x®ò oäƒ) ãUi) VWWùÉOÊoó6××=üÚ…òß( Ì4MÙÝÙáÞýû|üñÇÌÏÏóÒË/óÎÛosñÒ% cØÜÚb{kóPøES3ÓlE;J✠!ä(9£D8ˆÃ®¤ /µbna•‹¹û6é!yõÆ6×Öhomq¾5ÍÍî ÛžÐô“6 ‘¿Ù­øCøkéloo“¦Ù3ï¥ÙÙYˆ6ã„¡Èö¦È[Sô‹œTŠ‘ÇÕ(Ì0Në&㥕öí¬;>jaß½*…דJ‰•¾}O^@ñQJцͳgÎÒ¸{›t{{Ôz}øÑG¬œ=Ë÷/œÃå9«Ý®÷à‚hºÅÜ¥·¨¿ö*ÿìgür{û™B&¥sóóL-.ò$©ð¸RÁ ‰PQŠT+2%ýMID07w-P©V9{þ‰¤ü6\Ù$ BÐ øüóÏùû¿ÿ{>ûôSºÝî¿)GQþÛEžÓi·é´Û<|ø>ü3gÎ0·°ÀöÎïßêÚ”ÞÓÕFƒŽŽâ'˜¾Ù£µ4óWÌyƒ€é…ġΡ¤¤Ñlpáòe>ûøcÖ><ô{‡l®¯sa~‘Ûå‘JP(I!•ÿ¬|ñ1å$gô ýñ¢I³1†v»ÍæÆ&Yv<¢ˆÙùyò8f»RepøhœÝépàCòSØ¢À…vô¹õme´¸s'&žKí•—AŒ›)óÚÅ Èj—B9ǽ(æ½³gY>»B¿×cÔ–[[[üäÿ‘¿þ«¿â¿{íON-ÒßÚ¦‘ç,O5iMMñûþgþ¯Û·Y?ê©×ëœ9ž¢Þàn­ÁFSªr)(”wE´!ÔßáþïªÒR@5IXX<ÅÒÙ³<¼sç9ÏŽ¥¡-IÞn¯Ço~ýk~üãsûβg´eë9;5ÅB«E3Š18ÚÖÒ×KËKÃAŸöî.{[[tööŽÔ5•…(KS¶67ÙÞÚBi=Šû9ì57?H*ìêç,Bx¿èÑï& Ý}Q*‰2¡üƒâ&F½ÒZ*• §Ïžeqi‰ÍÕÕC¹¦,Ëx²ºÊ•W_å´„U)(„ò k­0RûƒGz.È–mƒ(¹º?NÎdžk½½³ÃÆÆ:Å3ž…F£Akv–^±“Tè9r`ë ŠZÝK²ilæù–ÉŒµRÃs¢;> wœ#/ò“v å ¢ü €¢ˆ¢óÀ$Ë­i.\¸Äìú××GÌ÷ƒø?ô#Þ{÷»¼ñꫜ¾|i íÇyÿïþŽ÷oÞàÉ`ðÌJkÍ©åeN¿ÀýjëIB&¼ Îá¡bߺ%SïIg!G¤SÓ-Î_ºÄן}~"ųs*ÏvÑï÷yÿW¿âG?ú<8òaŸ<ñNŸ:Å•sç™I\@·ßçN¯Çõ¢`-®ð—¯½ÁL£Á”)8gÈÁ¬Ý¦½¹Á“{÷X{ðà)søƒ'±=F€YBþL)6•& †Bâó³Tˆ¡–a0ŽÔ™œŽU´¥ÑÙôÌ ç/]æît'ìOÊWžç¬=~LÚë±R­ðÙ0£P^¤™KM®…ÒÒBJŽï‡ÉÅÌ?¦ä&PÝÆúƉPïôô4õ™Ç {qÂÀ9gq•*6I¼üB+Ï)ÊqüÔ¡D䳈J)}̲À»džÔtoü(>¼äÞ-·†/•¢µ´ÌÊå—é÷úìtÚ£^ýÉ“'üßÿ_ùÇþ'šµÂ:ݽ,=tbs·433Ã¥«Wi7š|Po°!5µòFe_3>Åd$íXL¥¥$rŽj¥Êââ"­é)6¯žÈÉpò­|Ãûx0ðÑ¿þ+?þñ¹ÿþ3§µZ7_}•+gÎÐßÜæÁãu¶Šœ Á°®c6•æ±Pô¤B ‰P²Ú ™§qþW^ƒ«í=vî?àþõ¯Ø|ôh_NûIy¾¹ùy†Z³‹HN ]($®,Bá=à ŠiQzv‡D‰_;©5›¬\¼ÈôÜÝvû©¡Œç}ôàW®\eq»ÍºTäZ‘G^`—KI®$E˜tœxýñ½ÆXÚí6îߣwÄÁ0yýOB7[¬VjìDÖ:TQà‚U©SãçHqàIï])±IL. 0'×û?àÛ!̤µhcé[Çg:áís¸2òõõëlwÛ£›¦( :Ý.gúæ<}ÒNOOóî;ïÂüÿX©sGhêÖOe4x.Ç "<²ñã|?Æ-Ȧ/ÒG‚(åµJ¸7/1–þž¬ÏϲŒÛ·oó“øîܽûÌÂÓjµøOï½Çéf‹»÷pg0¤EØ$Yá•¥Në¡îöM‡Î‘ I=_¡¹pŠ7^övؼ}›‡×¿bwcã™¶%ú𙥫4[Â_;¬¥”B …ðù]2L7å„VÄíG?åȽ’T8³r–—®^ck}ýP¥Á`À×ׯóúw^å\¢YHr­É”&ÓŠL{T(…Vb”ÈàN´ü‡ç{¬säYÊúú:÷îÝ{&Ðl69{éýjÕJ…žÔ$˜rŠäë…+?Ê$7Fn2VèWC)l‘9Gêž#Ûw´æ2F?“Oõ‹I¯p!q¢°¤Æp](^^>Í5ç¸sç6ëí= c¾Ñ¿­”bvv–|÷{LŸ9Ãÿž[Öµ¦i-‘3Þ¸¨8H„ ‘ ‰(Œ³=!FˆÅYï«£…_;8qïëšàCü ïârngw—÷ýk¾¾qã™;fµZþÙŸ±Ülñù½Üv’a½ ±4ÒZœK}:¡-·æ}æ’”ÙÑ[ÞwíHÏ/ÒXXä­7Þ¤÷à>?ÿŒ‡t»GêŸõf‹ÕÈoÛW&÷ÁÀ‹HïÕê”åx=¸Z•c\Q¶j¡ýšž›ãòµ«ÜüêËC ì1l¬¯³±¾ÎùF/Û= ©ÈtD¦#ríÛ¯\J"1æ;ÊöÆ2}üwå{¬e0òàÁƒÑ&ûq¯¹¹9ΜåQµÆ©)„ 2"øËÅb/*”ÁwYX73Äóñ–R‚Ö!ëí9Ú.ÁHò1ª|/ ùذ­Ã¥ŒA›ÂOµ Ëb½Å+/3·¾ÆÃ-:ƒÏ^?A’£RŠJ’pvy™ï¾û.y”ðd†/ã„™Üøì®BQuŽš€šÔ¤ jçˆ/>:Àùò÷¿'NüF8çpÖ…í^ûܹ]“Äî`8äæÍ›|ñùçôž•R¼ýÖ[\X>ÍÇ·îðETaXõŠbé,:+Y†²•e( ê, áíFËâ[>f6¤GçF¦aJ•äÊU^½z·¾Î'?ûyrûö¡SÀ©©)TµÂžŽÂ>ÕxWH1~B’nßc4›|ÏehÕ¢h»rþ¯\asmí)~Ê9G§Óáú×_óŸøC–7¶¹§4i>tä§žZ)†bŸÁ ú Åtgg‡¯¾ü’Ýgs~++DSS<¨ÖØ•2ì ‚¢T4¨"|8ƒ²f"JZ<ª6IÈ!³†“KrÇΙ~Ö0ñU£oÛv•;"!0N[_â<#JSÒAŸMÅ3†ô~ªÑétøòË/Y]]%?fª!„`ii‰ï½ó·ï=àã¤J_iœYX¢ IÙ°Z1~žë{U «5CãWxNú·Ý3ž­‚|¢`aªœ!2†(/¨ä™ÏÜ ÈÒ”"/¨F’—¦¦@*rI°×t#`ijõÕjðõ ã^ÑaPM(òœÈˆÂ÷ZÔjCZ±¦L)IK8šRRÇQCA,D@>aƒ·œ€C%)ä(ÓûDí’ñpzÿZ6—Kfz•ê ‡#MSÖ×Ö¹w÷.½gôöqóƒïŸAoÀï…b¯Yà 2é†èÌ’ä9µtˆíõIú]t3-`Në îŸx%çaœÃ8BѱäÁI 0­ýÒiwsóHåw½^Ç*Å®ÒÞp¾D.Œ—fË_K9ºÖXÿ“ùíBÊÑɬð¼]Eœ¿t™Ë×®±»µE¿ÓyêýØØØà·¿ûÿã_þ¬llò°íQÏ0ŽÉ´&ÕšX)¬ ȧT};ÊþPµ–Ým>ùä6Öן‰zÎ_¾ÌÌÊ 7šÜ FqÚ9b±³ˆ,E§)Q–9Ú>Ćökâ^?iáQŒÕš~¿ÏÀœùŒæ1á+Jq`d£¿yñ)EQ.p.Ú”qhc‰MA’åÔ²”Ê0% È±}Ç@xA˜ ÄDIŒÔF:ÚEÎýá.}Ý%KŠ$‘ *Ë)œEe)Õé­©&3E”L㘒‚&ކT…÷ɉhåa© ðt4‚éóÜ‚Æ"çPX r¢ÐL"Ayä›áŒ%ËsÖ7ÖÙØØx¦–cii‰3gÎrgs›û©ÒHkPiJäI^PKSZý>ôºTº]t­ÂŒRÌ'1±ž£š@¼6dµ› |ÍdÖ9‡’ù8bØéÐÞÚÅÚn›aìÑÏ  XGÁOÆ‹MþOŠíþÐègRX8¹qó&_ýõ±ÜŸ‚Ù¹9®¼ö:{Íד*='¼¹;–Øú‘QiJ’¥ÄyNT_|œs>%úyÆÏí‚ÆÇ%1FJúiÊÀœ¼éÌâÅ#c 6XbDåž–³ÄÆ’˜‚jæÃÿâᙥ~qA¦$™RäyFo ‚É´ÿl£Þ†Ñ:d^x3j! /Ã>Õ¹9¦Ó9faJÁ´L AKIêRR•’DJ,BTpﺰœ"•|®ÂSnUGÁxÛˆqáí[ 9ÎqOOmЩlnn±×në]z`;!¸Åô’ªWìfHŠ‚Z–Ò ™ô¡ß§2èIÁL1[©—¤ºðênC“¶5”Kîip8œŠ"V»=ÒcPYÇX)è)9"’åhÂ%êñŨlwÅ!#ßIOb¯öHã[¯Ø)’$fåÒe^}çv·¶ÙÛzzçk{{›ßüîwüÏÿå¿ðÒL‹[݃8öQD¢µwÑ ]c­›âµHÿž¯¢(X__ç·¿ù_>æ•$ ç/]búÌ~ßhñP(„sDÎ'¶T¬¥b,r˜¢‡âÔmê"G[‹ “aá܉îûzW [¯“+M¯0äÖ<'ºGŽ‚¿ò) ok‘sذ¥®;æ9•4E¥C°Þ„:’L+R‘iMé0¥È)ro‰Zè!V)l€ëÒYd^ û=êgÏ0]äÌãhIh É”’4¤ ®%‰¥$*…nÁýMø'žw-=Àã,˼Òb¹¡09!'¸ OX§iJ¯ß#ÏŸ-&\X}Ä’HMkzš—_ƒwîòU·óT+X·nÝâú×_óÎK—i·»ô:úqÌ0 \E~ß A1Q|ã»?ú™\¥èv»|òûßóå_ËýI)™_Xàå×^g·5Å—qLÏ…vËùÿê|Æ–Ðiêy×<#2fÔv‰ob+«5¶Z!“0°'/=ûšÃh¦oU|Ê(“R[S„›L9|E6†jQPË3t–!rI\ H•$U¾/Ï”/@¹ÒäJa”Æ„>°+¤Eå9zУÑï3k-óJÒPŠf¤iDu­©*EEk"Uz:ËCEVÂMì¸A+òœØZtØr!ÿÅM$qŽsÜåDõ÷_Ìœ‹Â/bóªT*Ô› Îщ«£íȆëše4ÒŒæ0e*MÉÒŒ$ËQÆ'^´jU_|G#X1Zþ´6-Ga ëÃT8géïíQ³[$¥ß•s¡M’"ŒÔq(!ÇãuÜù =v4Ü/—Leàb ±ÖÌ/-ñÚw¿ËÆ“Ç<¹wï©¿···Ç/ߟ¥S§x÷¥‹üK§C¿ß£'DqLk¬Ö¹1¨Ð‚‹ {Û?D*Qž ÛëwïÞå7¿ù [aò(Üh4¸öÆ›ÔΜáÃæT@=–Ø:*ÎRu–Š)¨:‹L‡èÁ€$÷ƒ]”³ó™h»Nø³:­±ÕC½¢8‘ ø ñãþ-çrC:Qå?"DÖÃÁjaHò•å8c(€LàÛ/©É”"ÓŠ\*/‹W28Ó…ÓÉz4E–£}êYʬÌEõ8¦EÔ"M5ŠI´&ÖšH«‘ñ>¯âÂÎÃOš¦ÔC9;B9¾ð”¯£&=êp#g¸“ùT3”Þ|%Q¹PLLN-ÏidÍ<¥žgôòŒ¸ÈQÖPKbjIâ[ )žJŸ(…mÖŒúñDâp˜Òë´]ópΡÃ2¯™P“Äœì÷ .£ƒ­ïAcªÉ?“Â#|¬#‚jµÂ¹—_æê›o±³¹ùÔÎZËÇùù/É_þù_ð—/q½×¥ÛïÇy”P(Edeñq¡T(xãðߺcÈòœÇkküüç?çÖ͛ǚ†ÅqÌåW^áâk¯ò`z–ÏtÌÀZ´s$ÎQÅQs–:þY“Ã!:Ký´(ÐÖs±NÜr=5f¯VÝ,ã¤Ö{#/%3©ñ™x< óíD†yžãœEc°|bŸÊO=ÉR5ÖO«òkÃxW2ém8½^R”¦Pb¬GP΢­éѽ>¢`Zkf’˜jS‰"’("‰4qN|uˆIúXÖãŽ%Ãå|² ÚLÏ8:¹œ¦¸éÎÙQ¾—@‚ôñ4QGsðrÆ›á ¥ByŒ5$…¥ZT‹œZQP/ *Æ ­E:üµˆãÐr^|JäjCû¥ ©ÓaJ¿Û=¶øôû}jR0ca]BÙy Ã$¢ÓòÄçdY T»ÇÒÛ¤4¦Z¼òæ[ܾñ5nÜxêͲŒÏ¿ø‚¹™ÞûÞ÷°iƃ,£7% F{Ç¿aQxW¤Ç¨×Ç÷"¥ü7)@“ˆ§( ¶·¶xÿW¿âƒ>ðÙgÇ ÌSKK¼öîwéÎ-òqµÎzðZJê©9GGÝ9kéh8$¶ÎÚM@>%ÉüÈÅùS›T9cŸ ù8!¼È‹²Sp/®íÊóœ<ÏI¢íÆœ‡¢>¾@ì â,‰±(cqÆ`qr¹ðê¥#ݘ¨xõ«¶Î;³ úTf1U©ëhñiV5‘^ñìÉù "kÑù87vÊ›o‡›Mx¬$Jkê•j±»{¤àr8Òïõ˜[X¤n|®’/>ÎOñ‰!›>¶ã›L+å¯ÅAÔwÈ´Å–1;ZcœCHÁp08ÖbdccƒÄ.›Œ §ƒJ™¾=b|ANp©Å„”A)í±’T¬¢Å,œ^fåêU:[[ìmlìÛÅsÎÑívùí¿ü õz7¿ó* ðÐ:òNÇï}ƒ¼k´·ƒÈ߇xa…§¼ÎEQ°³³Ão~û[~ù‹_°µ¹yäû_îϽõƒœ]áƒf‹»~6Oì|á©;_tΣ!™¦è,ó cÐÖ¾ÇØK<êqµ&‰èôzt‹œçóü –>þVÅ'Í2ò,'®ûP1¤'‡ÏQÂÃÄÈù8?ýñ¨§Z"F¡e“¸Dâ‰ì=cPyFU@#‰iT*ãMëQÑ9Øj•ŸÝ!#Þ“‚çý~ŸÈYb7–Ï—-O‰‚lõR.¬¿>Jû½¨©™6ÖׄÜÃáõÇ«¼|íçÛ}VE(@Ö_ÃØZ"kˆŒÿµ¶þÏøè éÓCä¸|(¤”˜}^Z_…9V…þèÑ#v×Öx·ÑâUŒ54åá:.pÕ‘™ÕßW9Š/S>")‰¥¡ª}®YÜœbáô\@»×ÝW€¬µlmmñO?ÿ9yšòö믓Ä·lÁúΙôò|¤)*}³“£èmyX¢ë·@;%¹œeÛ;;¼ÿ«_ñÓŸþ”û¹Ï§”bvnŽw~ð¯\åë©i>—Š4û³4… )¡n@ çÕîYJ,•'›­Ùgqròºá']®^'‹bÚé6ƒo°"%ä˜XÝ×lkåô¡@à¤Ë‘iJši¶¡Ò 9öìi@¼0/âp‚ ç9SjO'8qŠûTÌ}¬5Õ8¦š$û Î(ßèÂó"zõn§ƒ¶þa߇tÄÄg&Š’À“ÒMHifççY\ZâþÝ» Ž(>Î9îܹûïÄ ·Ò‚ KÖóN:ìš)J!¡GRÊ‘”à8ô'ù,¥òßgðÆpÎ Ï·¶¶øè·¿å¿?{–¿¨TùHGV[[šÈ[Ÿbá„ÿ=!±Øà(ù,ôãßC…G¼±TT”#×¹)Z³<7GÅ9Öû½§ ÐÆÆÿôË_Òëõyï»ïòŸþóQ7oRÔët³|ü F‘ß=RzT,ÞCÛ°£®¥;€Â&Û¬,Ëx²¶Æ/~þs~öOÿÄúÚÚ‘2 ­53ss¼ýýïsö7¸33χ:f×ú÷:qŽj@=MMM'i”üh–¡³œHƒvá‘ωz^Ùlš †R²›åGFZÝDãaGV{„sžç¤iÊŒ”$þß—"Œ˜ÅÈûU2RƒŸŒ‰1 +ÉO‡£ü>K!&”¨†›FKIEÄ;™D:s¨^ÔËZK·ÝöQ“m–Øzܾñ{`û'vjTÑœšæì¹sÜøòË#Ó!ÖÖÖøýÇÿÊÿü/x¯ØãÃÉIõdõ;ÐÖ87FZå{=‚†) eËuÌÿ_Ÿ~ò ‹gÏòúŸþó&÷¤¢ZïÌ oìæ¼» ÑáÞ œÊ³~“Ši+-‘VÄÖP‘i ;¬ÁòÂfݲÙïïO]pŽÝÝ]~ý/0ôyã­·ùoßy›=ç (è•ÅÕYœ‹Báñ@¢|Êkvð~r'ð|*¹´¢(èv»Ü¾s‡_üâ|ðÁl“G¯µfn~×ß}—•W_cufž£˜mÊ¢ð Ô±¡èšRÐx¥È3TQ ¥7ít=WSÞ¿Q„m4é[Ãnšž¸øŒrå=”ôé%’VûçÉ?%üf3aiÑFï7*Ede6Šï˜l_Fî©ÖHI¸:$2DQtè r’Âó¼Å© ­!+'p"Ná­©}ñ”uÁ>õ+©¨5¬\¸Èò¹slooic‘¦)Ÿ|ò çϟ翹tm w;QPèúzd–ÎÄñD‹=0N~Š÷Á›ÌgyÎîÞ_õÛ[[Ç>dÎ9vvvøÿ~òÒ,çÕgfXwðÄ:ò(òÚ-gÉ•#Gy}”Ö£)ØQ¿É©ä(`A8©GÖÞ£ÛîòæåKpj‰/66X?Ђ•˧|ü1«ëë\{ý ®¼ö32k)òŒÕ8ë0…ÁDšØXl¤QÆ<Õ¾?ëÞšÔòÖÖ}ô¿þõ¯¹}ûö‘¦ðB¢(âÔò2¯¾õ6ËW¯òdf–O¢„ S•@.·€Ž))™’¦Ô…#YTQx*"Ð#¾çyîy!pÕ*¦^£›ele'/-Ç­ IDAT>¥¼ž;/TçS¶"ýÁ… îüÞ“ ~­.¤XNZ7 Æ*V5¡zä"Ý1 N—æ`¡Å:éñ¢ˆÃ,Ë0yNÍáF}Šç( ¶åؾALü„Öè8NXX^â¥k×xüèë ÷¶¶øÙÏ~Æ_E1²²ÂiàQ–"z]ŒÞõ™Ü™è. “˜±pï€n²-ÈóÜ;èmnññ'óË_þ’õõõgÆ YkY_[ãgÿõÿáÞí[\}ëmÎ^ºH3®Ði4©–¤áЈ…¦Ó8)lj©Ç¡Ÿ’‚ïË9ÐyNöx•zsþìYÔÒŸ¯­ñ¤Ûyª nß¹ÃæÖ=ä{ï½Çù ¨Tk8k(rÇÐXk°‘ÃZ?Š×Jùxâ‰ìà½Vœ¢(FŸÁþöí;|üñ¿òé§Ÿ²{Ì`AáýÄÏãµ·ß¡uþ«33|UyôX15ëOKÀ´”L h)IS j†R"L1Z$•lj„ln6Éë öº]öBüI^Y–)IlÂN£; r‡÷Ü+iš"q4VrvÂ:ql§¥©‚xJ rüÜILæbB¿ó‡‡M¶épÈTž£ ènŒØÊ¶¶Œ—=ˆz„ðÆN‘V4§§¹|å ëkk z=övv=¬µûè#º»»L×jDKKdY…’ œ¢j­×^•K¾Úoä }ôX{’k)¹ç±Öžã* âaJc}c,gÏ­ ——QOÚmî][kÙÛÛãóO>aíñc^¹z•—¯\ayy™V«…Œœ0d.õÖ«¥L#¬æÈ¯vØÔ°( ¯ZïõØÜÜäÎ;ܸqƒ{÷î±±¹yì¾–”’f«Å¥—_æÊo/Ÿæ^³ÅW*fݬ<)PÃѦL‡ÏSBЂ†RTœ§<„1~ÿÏí×ö<7Ò×;=MÇlöûtŸc§«Ûí Á”ÉÑH„3“ˆ’OÌG>â¤ÖÒëõ0EA? ŒFÄ«8 7Ο˜x°õ’NÅo^tÜ7™¶{'ÆN—é"§RŽÚ'ÐO‰t<ù|ˆÍÆ„‚7Iæ—–¹öö;´Ûm®òÉS›Û“_÷Þ½{t:Þxôˆ«/½ÂâÂ<•Åĵ+ˆímìö6ot;Ì% ‘Ö Ó4¬6¸}…§( ú!Ãkuu•›7orãÆ V?¦ßë»ov\ŠãÄì I_ ¢ Yš¤ðæf‘(+PÒbmص;‚ûyŠ|ëZ´õv"á³¹q–ų+¼¹|š$Šx¸·ûTm‰\?zÄÖæ&_~ö9KËËœ>{†¥¥%æææhÔëT«U¯‘R¥Æˆ§äÑŒ1azÕétØÙÞfccƒ'kka}ÕÕUž;޹~ý+Ï«„Â3 èu»´;z½iš’¦éS#)%³ss\½v3—.áju¶Ûm®ø!oߢ8Äß§Œ3Z:{–n³¡c†x7É>–“ q#î§ÓÂQ ï1Îa!‹JoœUªy¾s3…¡º´ÈÅÙy¦j5înmòh÷i4Ù2EA¿×c{sÓÚÍØA>Ñ–Ú´ žÇsâÃLJI£ÙäÌÊ —^y…¹³+ [-nÇ)Í®ud”þX A_xšB0…wn˜–0­$-¥hjM=ÒTu„p-å~1á7}¶„ pŽíµu¾n·y2èûµ þõ½½=®_¿Î+×^åt¿Çj6Dä¢ÈÁ` 87¡óqg[påâ%þêòeŒŽ|ô†ÖX¥q‘Æ–¿'(I¥V¥—çlä6+ˆ†t–¢ò iBųv »žëqÿ·}Yç(òœ$Ih´ZcÑWYQÄ~˜éˆn¿ï§ Eîûê°;52i’Gç!•j¹8©T•¢Ñh2{î¯VªL-.pý_ÿ•Çwî`Ž˜Ž”öY–ÑîõX=°B!ƒVåàCs˜j¹tl4›œ?žË¯¼Âìò2i½A§R¥¶tšïÌÌÐøxžG_}Iow3‚¤”Ì-,0sꢄ-!цÀÐ o\ï„ å‘J¹”\ŠŸA©C„lºouœvs“LiZó³ü§ó0: ¦l §½³¡Ó¡Œ"zÔh¸'U¶÷öP¿î¡‡)6K!Ï¡(`ÐwzBâÇÉ[Û´’ —€a’ЫÖèÖjtê º:¢%ôjUÒ¤BùoȦ9=cÉÓŒz¯KÜïû/˜f¨"GÚ  âçß ëˆp:` RJ¦gføÞŸü ßyóM ‡·ñPÞzÓ*…• ]…”Ô“„ûRQXã%ì6lv«`]aCLFé!¶c'û”ô09’‚XHêZ“Öd§$gªUT£AÒšâÉ͘n—ì8?)ߟ¼yNrÇIÂT«ÅÙ•V.\`fqêuÚI…=­i[H%Ô—–9ÿÃiæ.^dõóÏÙzð€ÞÞ.Eš¢µféôit½Ác­éGÈdásŽð*v¦œˆ>Åè=[1j#†vbÍþÇ’S( Úƒ7=ÅÌÜ,§Ï¬Ð>1ÝÛ£³»Kž¦Ïœè}“‚£´&Žcjõ: ‹‹,9Ãìâ)ª³³ «5E1[R²‹/ÌÖKjœ@ሠ"5 ÷¨j Á””´´…'¢EÔ¢ˆDKïS&É|“5Чo*T§ÃlQðZ·Íùzƒ4ŠVúI…n­F·^§Óh0ÔýZAµB'Ú»[J ¢í£±¢n¤ßÅúˆtyÝžÕ£“~ä§k©u»œyøˆvµŠ®Õ1½æ,Íhg9»yN§ZFE°¬ˆ³ŒÚ0¥ÖïRítIú=¢t€Î2Tîxa âêÙ?dÒZsêôi¢F“nžÑµÐuŽ=ëèàè9è"è#ȄϽ¬P Ó”ºT$¥wÚóB ÖøtÎð†–B¯Òð[#ˆT¤ .Y’5›¸Ù9¢³+LÃÒp@gw—íN‡N¿Oƺßä%¥$©T¨7ÌÍͱ|ú4 KKÔ§§¡Re't•¢Ìņ8†Vж’(©2ûò+¼tf…•'lݹËî£GD8Î\¸HWGl"0ÆAXü‚°×"É‘i´‚òœ§ÐHyŒO8¬Ã9sp±VMvÓ”v¯ËãZƒ|e…s¯½Âé™iöž¬±½ºJ{{‹A·ëi³ì™‘Ù¥å뤢>Žcâ$!NªµÓ33ÌÎ/И!i41Õ ƒ(fCjv„  wx3:ëüJ„ó–41Ž > ¡!¥ AK+Z‘/:8¦Gû”¢8 ú¶T†iJ¼¹ÍÔn›^Ó®TÕÃFÓLd;YÁ®±ôŒe2¥qR l°ø¨÷ú$6E¯ý SØÝ Åg”ÂY2Ó¾Xè<'NS%© ¯åÉò7 ’ iä£i…ƒ¨È©e~Qï÷¨õ{$ý>q:Dçª0H3^pãÛBÃo.Ø ËXžôlÔÈ$!É tQ`óœ~–c‹‚4/èCÇúÖ‘‡]¥H ƒ(EMIªZ“ˆ„C[´~9Rö‡&ƒò‚ïµ N‹Æúmþš’F`¶õ“^'ÛÛ¬LMóòù äÖÒéuét{ôÓ!ƒá4ËHóœ,Ï1ÆÞå E•ZJR¡Z«Òjµ˜™ŸgzvŽj³‰ªV)¢ˆŽÖô…`ˆ$n“¹±Ø ¤10–GRR­V™:‰¥3+œí÷Ñé¨Õâ‘Rô C,}Ž›B!¥/œ–æÍßULP^l:^$cœ ä)¥µ[ Š‚´Ýåpú4 §Ï0»¸Ä•+Ø~Ÿ´Ó¡·³Í Ý!ô1Yæ7ú*’Rù½Aí…­:ŠÐÝè8¦R©’Ôj¨$AÆ"Š1QÄ@i6¤¤'$!8ïäPNEe˜Ü)ZX'¨„–º4¤ )%ÍQщ¨G µXSÑÁÁA)¢°^4)§xaªcH¤õ«M¾& ©(åw,Áfª×#Žb íÍú¥³Þm3ÍhûT{=Švz]ao;ׇ=”ÒXtQ ITÔZò¼À¥)ª—D©Ò˜`£ mA¥(h¤)Í4¥>RM$ofíÛ/ï¨VXËžµ¬s¢“^¬›Æ00†G««|ñÅ ƒ§þŸZ­ÆÅ‹GêçcO1kÖà Coo»±›ç´aÏZÚÖ1p¾è|HžçÜ¿Ÿ­Na >Ͳ‘¸2—’\R¥êœažÑO#Úñ¡T + [kkqQ™”á9e÷m. Áèª^äT²œ¸ÈÆ­?‘ºÖòUžóÓÁ€<˜|ŸH? `¨5_^¿ÎÐZfggGVÚ{œ9s†¿ù›¿¡Ùl¢µ>R°X.¬*!ȇž½L?нw6ŽBx%„ +!ãÞÇ{‰àÅ\ä·¾øŒá`àorå­3œ”ÞY)œÒˆÈŽVHLÈCÓÖ’XoûQÉsD¿‡üÁX|í5Tµ†ÏUþkž ÿìÖ­[üíßþ-yžsçÎÖwvØ’Š•|—^V „÷¦¶–ÂrcH‹‚u¥èïl³~óʦOâü•«Ð¨Óuà¤çö„pD¢Štã{Ä9¯dÏ­áÁö ‡ÃQʭÛñ[Eˆ&šTõ»‰”m·µØ,ci~ŽÖòifçæ¨HAU jRùb£55­©Çž×©FÕ8&Ñ¿ûì}~ýþûäYF½VMåŒ1>]b0à†Öü~ì¨WšgL[ÇÛQÄŸaåÓz¿°âb-J¢Bx0bƒª;ÏÃq¤½%²òõá‘¶–¤0ÔŒ7쇠ÒVgú°/è­,QáM‹Œµ¸¢@æ‘TT´/U(*›}qϹßûûýÎo± :‚Ç&Çv&{ÏxÓM¬aPNYÿžHPTQNiqñˆ­ÀJ)‰[gΜ¹Â²I%ÍÍœ9“?üÃ?$''ç†î—–| Yñ8—êêxï­·()/'\\‚£ØwS:Æå©—îù°›4eHn ÇmÚíí¥³¹+則dÅâÅÉD¯k«¢õT »tÇ6{€¦úzvlÚÄÅ–¦WΦ8/^Ÿáuß(Ž8–éê…ãuOµ ãr?)Áv—ˆôö2kæLœ6W>IáMû§»J™>¶8¾wf,J~8—œPŽ»š†ŽÔ={*=žô¨^7c£ÛÛB PWSÃÌPˆ»gÏÆ()¹ÜÚ53|+õôéÓüìg?£°°òòrB¥¥XEEÈ(É(%Ý59ºŽ£i„<^úZ[8ÒØ@傅̯¨ 0/L\Bºe*BÓÄå¤ÑŒk°Á…–êêë± ¹¹é8é®}fœO 4i[t¶´ÂÜ9T””•ÐuB†‡ Ç äñ$ÇCÐëuåy<ø“ýª8Àë¿ü%999Ì™3¯×{EÛ¬Y³pfÎäè Âi--„:»0M“Õ~?«_ÎéYj]€W‚OH‚B¸±1ÛÆ0x !ÃÕ[×Éùì:Àm~'4Û&O E£nÀ¹§5qå“êñêwæ7ÂAÓm<¦;"¤k˜É7H”Ɇ_®%lŸ*áv×÷Hw¨™Îå‚ÁÊéÓù³¿ùžüêWoÚ=íêêâç?ÿ9Û·oñɉ®ëè"yì „<^þŸ?þ÷<ð1Ó"j™Ä“±Ké¶Çp‹æt¼€ÇÝ$!ŸžŽîÝËgŸ~BÈïÏ(‘ɦUÚ5dè:†8ÔtJ ¹gå=|çûß'j™ ˜QÇaж‰ÚQ!ÜãkkÉ2YÚ¡#…À²mvíÜe»]#EæøÉåv§’´Ø¤:zo² Š`íƒñЪû)*(tO+u-ý–·[KJ·í-n/Ÿ®ðzÉñzHDúøÉ_ÿ?ÉùnZFYÍsGK–,áOþäOX½z5¥¥¥Wœô¥NûGàç²ÕcZ˜ŽM²ˆÙ6ÇNžâó;é‹E)2tŠ ¸q¦”˜àQ”"½ÿ%©‘àÓ ò™1m: .H¿5®ì®k—G3éòòôV`ÇãlÞ°<à _8”x<=ƒ Çëî'ßeÁñ{½x O²g•AaA>ú(/½ô_ùÊW…BcŠñ|ðþûìY÷<8ti é÷¬›]®»Æ…”„ƒnÛxu@ªi²—ÈØÛ:nó;¯á2M7ÞãØÐÜ›ð ¥Úzªkž#ݬ\MGw<º† KKÊ+š~ÒÝ`^éöíIÿ@rÞéz4 aÎ _á>9gG‚Áàèò0¤D×dzR¦.¡åE…ÄM7n°l¬dJ½ÈhÀe:^Ý ¿×CÐçÇk™ä‡B®˜%ýñ¡Š_3kšRù+^Ãp~M#7à§¢´„XÂ$fYD-‹AÓ$jÛć˜í$ߨ"™?㊓¬]M˜>ÓÂpDò%p9Õ>³Ç²‘ÙîÍ=ú× |º†Ïð G£z fRZT”žø*Áum$îø!q’V‘–œ8ê÷¸=µ„À°l¼RâÕÜiéY^#˜•ºg‡p8LAAÁûåj%–mcÙ®ø$,›ö‚ò<¶C¾¡SâówâšÀ”‰ä!;Ùä.%êBºÙÙþX+‘ I™?®…¼œgª¥çe¾dÝý áMNmÉulrl‹|]£$À¯¼ž+Ç—‚Tß*Ã0äååQTTtc rÃa|¾ë‡;R ý ͼ"“ã®u!04ði:n3@[sÓh¯l(Ón¸%%!Ë"”Ðqq·+ÃõBÓÜÙYšpgi ’o;WxD2g#õv×qçm§ÚfxSµ8É ‘úxd_ŽcNFºn(ù@hRà3tr|>7ØnèX·BÜ—si´dÃ.nà1 ·«ÏGÀðàÑÜÓ¡ËS#®mÍ9dænª’:™€JÍ/3t¼º†_× ZqÛ!®[ć„㸽s„›9l'ãq)ñ `x½äIA8ÓÚHN˜H}¡ƒWsÝ>Ÿ¡';êx½z,FPB¾ÏGQ0˜R’Ó/’”üq’‚¦铼€×ƒÔutËLÖe]ß CALÛÆLÞKËq§xXŽƒ®µ›Ì ŽIAȲ±âqÂŽ Ä›L‘“$GA_”ÎÂNŽòî:zt·ÔÄ/$ r}^òƒ!¼^·ßvº°>´èLd³¼áfô«NÀ´T¯žäÔ[¡¹n¸ 50!óPGKºùÉrfiW—íò-•í.2ë‘2ÓÙå0§H©àej³^;J%»VÏPÖGj¨ ¡ënU³#p’ù"ÃêÉìpç~NC7Ro{ëš\Õo41¨T´ÏëM?4žd>‡ß00=6Vêa±ôÃ⊃-$ƒ¶?Ç+¼{=ø}é¸DúáH&Öôƒáõ¸"êÕ=ÈX ݱ 9á`(y€£%]™>(R£³3ïgÒUôzÓÝô¼ÆµãŒÆS€®N¨¼ÂºM¶ßMrù|„t|Ȳíô)™í¸÷ÒIZxB b„„M¶ÈÓ5JüþäKHKÇ{Róèõ«¬.C70t··v·Â߯ë„|>rƒw‡èA>\¡I}AgPê…1œÈ!ŽˆRŸÉšµŒ½ãöaȰ‚ÒÁ§Ì…a”’«D뚟IRí±˜ì$­¯×‹a„0®ˆ)df_aÞ'7Ša\ÙGz¤ý†´!N~Rņã¤ÅÇöxð'…Ʋ¬ËJòs„ë¤tOuB¿¢T¡pÒ*IYmiJЧ'ʳbW0t ßO0à¿â÷ÈŒ±ˆ«îQú7 ü¯ë†fŒñ™p—á*k(u=ît÷÷ø|ä$ÅÇÛvp¤Ûr$-¨)‹ˆên¢m I®aPœŒµ¤ßðZFí^r&½q…åe`º›™´2}dò«g2åT€ä ´àš ZbqM Çs݇!ùÅ™]åUGßÉÏNÑ.ös… ”ê7,åu»ÐeþFFAçh7ÍÕ-sôOêš<ÉűmÇØŽí6àO¾©!ñâŽÑõJIa0DQNNºGS*^•v 3Œä÷ºÎ Ïç&~ŸŸÏwEo›TÿêÚ2)e:§I×u|I÷DbŽÚD Pæ}M5‡Ox<œäA‚cÛ8Ž@ø. jJ\¥¦áÕܸ›_Óy=䃗î6ŠKÇšôË÷9ã^躎°íä(ékûµ%ð¤xZpu×Q™™z>ŒQbpÍôéé_NE™¯x=“x* ÐÕ.X¦’ÂÐm IDATßH|®v›Æ¼zóeZQ©‡GðxÒGËB8WX "™$êM>0¹Áy¡Ðål]®ŒdƦ2ü>àö]ö$ ™oëOš®¾OWÿÙè>9Üú¤,K×Âu­ÛÔ=M j¦°Êd¬Âïñ¤c‚áPhÈu»ú…võ'‰ KÙ-Ý˜Š¢s#ÎýZñžQ/"·'C-üHGèuâr³×’)8™3¶2Œ¡!Že¹N]#ä÷“ ùp\í:fþ™ùP¤b×feÈëŠtê³ÃÝßl¹×©ßÓãñ\qß®v±3…Uqù`ÁëM§u ee]O¼^ï"Ÿi-ßj/êÑè~•G7Ù@þv¡Ñn†ñÞ8W_ÇÕ"4ÔÆ<³@ @ )>#9žÎŒ} eÑ u}׫²oa¯µM=üCÝÓ¡Õ²¬´Û–*,n®÷çÕ–àTŠN6J|nAî¿úO!Äo×”{1”Õq½%3v£äzíi§â6œ‹3Ôà¿Ô§2Ý¥ëZ×éI=ÙGæJ|b ]ýà\íêdέɃ2Ö˜Õ­ú uO‡ú}2ƒå)“›r‹ŸÛÜ"Jõu¹:È{=·Iqã{¢,%>7$ÕÜÛ²¬+ú_Û¶Ý$¹qè°x¹ÈÑ5RRÍÌ33©oÖ¥HM]pgÔ‹}‡º'©û1š{’b¼Ö&sŒöZ2×'›÷Ãqœq¹%>ÀÀÀdÚ´iøýþ}¦££ƒêêj"‘Èå&òc$Õs¦ººzÄßî”ǃÒÜÜÌâÅ‹Çå^´··§ïÅhŧ³³sÄïGjâÆéÓ§9xð £þ7NžãH àþûïç?ü‡ÿ.‘éFu‡9sæ°zõjæÌ™3æk‡ÃÜ}÷ÝüÑýÀ¨7Hj|ï,]º” Œù|>?þ8¥¥¥îXáŒ#û‘î‘Y³f1gΜ¿\§¬ÁÚkàötý¿B!þïœîóú&ìK/9ŸYoæåñÿþÓ?ñÌóÏ¡P(&õë׳ý—¿¤h÷þ<&<Áô)9`š¼ÔÕI¿ë]hºZ…B‘ ”ø( %> …B‰B¡P(ñQ(J| …B‰B¡Pâ£P(J| ……B¡Pâ£P(¦Y©L‹KI‹eÑÒÓÍk¯ÿŠí»vª•P(&‘3gÎÐvö, '9ýŽ èQ~ûÞ{j'(Y ¬éäø¼8Yúþ¬ˆ(Ð |†AQAÁ¨šn)Šqð>âqñ8¹2{ã°²">źÎ#Ûùüñßþ-?ù¤Ú Å$òÙ§ŸRó›ß°ððüYêGññiš†‚ââbfÏž­vƒB1‰ä‡Ã„4"CÏÚ©SV¾×Ä lÇQ;A¡˜dÌD''¤iYs»²'>hJ|ЬŠO‚wšøhA aY8·É…âV;'x§Y>:àÑ4tMÃã,'…B1v,ÓD˜;M|R_ìÕtDr¨B¡˜DËDzp,Óµ|²tÚ•Õò ¡§§{*ŠÉÃq„ãàËâØçì‰]G*ËG¡˜t¤” $Ùœ8Ÿ5ñÑÐðé®Û¥b> Åäb;B8xá΋ù$Ÿ›O¬P(Æèv pž;ÑíÒ¯²|Šì¹]RdÕúȪåã×u„:jW(²cù×ò¹3Ý.)AJåz)“ŒiÛ8¶C;4æ£P(²ê|eõÛ•ø( %> …b2ѲúíJ| ……B¡Äg’¬>M­€B¡ÄG‰B¡¸“Ü.%@ E;7¹P½wóQ(î@<ºŽfèd³¶@‰Bqbè:ºn`Þé–¦\/…bòÝ.-»]%²*>–è†aj7(wY ÄlÃëÅçó©•P(&ÛÛÐt²9;&{â#%–hÊòQ(&¿×‹Çë%&åó±¤r»ŠlàñxÐ=Ìd[›¬\C6Ý®¸m+·kŠ"¥$ÒÓÓCWWÝÝÝD"LÓÄ4M‰¦ibY†aàM®cê' ‘ŸŸOqq1ÅÅÅäçç«užB†î1°ÈžåãÉ–ð)±ax<µ²Œ‚H$BOO===ôööÒÕÕEKK —.]¢¹¹™öövb±ñxœX,F,#‘H`~¿Ÿ`0H   ’ŸŸOii)3gΤ¢¢‚éÓ§STTD~~>EEEÐu•í‘ ¼^/º×KBr牅;·Km¾ì ŽmÛ˜¦I,£ªªŠ={ö°wï^ª««éééI5Ò’ÃåRi™ÿwê¸V&»Rfþ¤ƒTVVrÏ=÷°fÍzè!fΜINNÇ£R.&Ÿ×‹áó•w˜å#¤LŠ]m¸¬ÑÐÐÀþýûÙ¾};‡¢»»›þþþ´U“)<~¿Ÿââbrss …BWüX–E,cpph4J4¥··—LÓ ‘HP__Okk+;vì 77—… ²jÕ*Ö¬YêU«ƒJ€&ËíJÆ|ì,æùdE| Ž$àóa(ËgR‰D"œ={–={öP]]ÍÙ³gill¤­­ ˲…BL›6¹sç2}út¦M›Æ´iÓ(--%77ŸÏ‡ÇãÁëõ¦ÿLYP–e¥âñ8}}}´··ÓÚÚJkk+MMM466ÒÒÒ@{{;çÏŸgÏž=,Y²„ßû½ßãþûïgîܹÊŸè?éve3Ã9kâ“àóyÕI×$ÑÙÙImm-ÇŽãðáÃìÛ·ºº:âñ8yyy,[¶Œòòrf͚Ŝ9s˜3gÓ¦M£¬¬Œ²²2 G%RJb±ÝÝÝ´¶¶ÒÖÖFss3 \¸p¦¦&šššhnn¦¶¶–ƒRSSÃñãǹ÷Þ{Y¾|9³gÏ& ªÅ›¨˜ÏGâN³|D2æãõx0”™=aH)I$tvvràÀ>üðC¶nÝJss3>Ÿüü|æÎËâÅ‹yä‘GxàX¼x1ÅÅÅ7ýÝš¦¥Ý²™3g¦ÿwÓ4éììäàÁƒì۷ÇsöìYzzzؾ};»wïfÁ‚¼øâ‹<ýôÓÌŸ?Ÿüü|¼^¯ZÐñ¿ÃïÏjžOÖb>6àñxUÀyI$œ;wŽù—aÆ 455aÛ6†a0kÖ,¾þõ¯óÜsÏqÿý÷ãóùÐõ‰?ðù|̘1ƒgŸ}–¯~õ«´µµ±oß>Ö­[Çž={èèè ¦¦†ÚÚZ~÷»ßñGôG|ík_»BÀãóIº]öèvÅ€@ÀG¹]Â… øì³Ïxï½÷8uê†Á²eËøú׿Îc=ÆìÙ³)--% Mj WÓ´ô ×ôéÓyâ‰'Xºt)Çç³Ï>K[g'Ožä§?ý)à…^àÉ'Ÿ$¨ÅËÇçC÷ûIL’åã ‰_õ])ñ1q³=“*>¡†2§ÇÓ49rä6l`Ó¦MTWW“H$X°`>ø =ö?ü0óçÏŸI>Ÿ’’JJJ(//§¢¢‚Å‹óÅ_PUUEmm-‘H„îîn:::xâ‰'˜9s¦ Hß$þ@O0DTÓ&E|„th(Ë'‘üo3 f˜-%ƒ@(V¾ü8Æwz{{9qâëׯgÆ \¼x‘œœ–/_ΓO>É3Ï<ý÷ÞK(š’¿Cqq1kÖ¬aÉ’%,\¸ÒÒR8@cc#7n¤¹¹™Ö®]ËüùóñûýjáÇH Ä““C¿>9â#Gív9€.SÊkþÒ¸¿> W¥Ü›ðD£Qªªªø_ÿë±{÷nb±¹¹¹Ü}÷ÝüÅ_ü>ú(EEESþwÑ4iÓ¦ñÍo~“|W_}•õë×S__ÏÁƒinn¦µµ•þð‡Ìž=[–ŽU|¼9!z cr,ÀÆò±HNs$^fŸ²|r ñª·×McYŸ~ú)¯½öZZxJJJøÚ×¾ÆøC–/_>e­ë‰ÐŒ3xå•WX¸p!¯¾ú*¤­­uëÖÑÕÕÅßüÍßPVV¦¬ç1 ñ„BôkR› ñ‘×äeÆ|4`R¢ß0 ëJ|ÆÁâ±,‹wÞy‡õë×sðàAâñ8sæÌá[ßúÏ=÷Ë—/'77÷–üý¼^/Ó§OgíÚµäååñÆo°k×.ZZZزe š¦ñÊ+¯°hÑ"•4JB¡ÞPˆˆÉ„/•Òû gùxÜš+‰˜`ù±4>Ã`zAr»n‚¾¾>vìØÁÛo¿Íþýû‰F£Ì›7—_~™o~ó›,[¶ì¦­Çqèë룳³“žžúúú cš&ƒƒƒ!ƒ„ÃaùùùéŠö’’ü~ÿ˜OÓ Ã ¢¢‚'Ÿ|2]1¿cÇxï½÷(,,äþàøÒ—¾¤öÒ(-o(D¿“tÚ–Úí2/@BJì ¾KÓè÷xX—§6̤¦¦†þçæÐ¡CÄb1æÎË3Ï<ÃþÏÿ™3fŒ)"¥Ä4Múúúèë룧§‡ÆÆFêêêhmmM‹ÐÀÀýýýTWWPYYÉ‚ ÈÉÉ¡¬¬,]Ñ>þ|ÊÊÊÈÏÏ'//ÜÜÜ1ååææòì³Ï¦]²M›6ÑÙÙÉ/ùKB¡Ì›7OÕ†"æã pœIéf˜*©Êíê4)%1)¯ {Ì4 rÔiט©©©á—¿ü%;vìÀ4MæÍ›ÇóÏ?Ï_ÿõ_ …Æœ,hÛ6lݺ•Í›7SUUE?¥¥¥,Z´ˆ¹sç²dÉŠ‹‹‰ÇãÔÖÖ’ŸŸŸÎîééáܹs|þùçÔ××ÓÛÛËÒ¥Kyì±Çxê©§xðÁÉÉÉóïýôÓO“““ƒa¼ñÆ´··óÆo`ñ¡öÓh,Ÿ@€>Ër»L°hÛÉ<†ú„ÀœhËè×Pâ3FŽ?Î|ÀÇŒeY̘1ƒ—_~™ï}ï{c!ñxœ-[¶ðùçŸSSSƒmÛ,[¶Œ?ýÓ?eîܹ”—— …øý~¼^/­­­–/_ÎSO=Åc=†mÛéªø”ÕtîÜ9Nœ8ÁßÿýßSZZÊš5kxöÙg©¨¨õúëºÎ=÷ÜÃw¾óZZZÒGñ6l ¤¤„ï~÷»*q„–×ï'nÛØÉ俉”SBŸÚòéüƒRbNtÌGJú¥$¬Ü®Q»D‘H„7ò»ßýŽŽŽB¡ðÀóÏ?ÏüùóG-<­­­=z”;wRWW‡¦i¬X±‚E‹±páB*++)++#_»Ž–…®ë„ÃaÊÊʘ1cÆ•.é¾]ºt‰åË—sæÌ.\¸ÀæÍ›9wî«V­bÕªUÌŸ?TלŸŸÏ}÷ÝÇ+¯¼‚eYTWWsòäI~ûÛß²|ùr–/_N^^žÚ07q¯ÇƒÇë%Šëíx'P~L)é—bHñ‰¹k¢-)é‚%>£"‘H°{÷n6nÜÈÉ“' ‡Ã<òÈ#|ó›ßdåÊ•#¶"R§çÏŸçÀìÝ»—ÚÚZæÍ›Ç#<ÂÃ?Ì¢E‹nºÎ+3{yåÊ•´µµqäÈ6mÚÄ™3ghoo§¥¥…Ç{Œ%K– GüÅÅÅ<óÌ3\ºt‰X,Fuu5‡býúõäææ²téReU߯ÇCN0È `KðN éc"¼\^a]mù¤ÄÇœà2{[l›œÜ\µAF*Ø–EGG¯¾ú*‡`Þ¼yüå_þ%+V¬ñQsÊŪ««ãõ×_gÇŽƒA~ðƒðä“ORVV6aAÛiÓ¦ñÕ¯~•'Ÿ|’­[·òÆo°nÝ:Ž=ÊŸüÉŸ°hÑ"ÂáðˆHÓ4ßÿþ÷Ó½‚Z[[yíµ×X¶lYº‘â:/¯—ÜPýñ8–”'0îcIW[’Ê2xuÌ'*!H LÜh K m›œœ%>#¤¥¥…ßüæ79r„ÞÞ^–-[ÆË/¿<êr‰H$BUUÿóþOÇá™gžá¥—^¢²²rÒ L Ã`õêÕÌ;—Ï?ÿœ·ß~›ýèGüÙŸýO<ñĈ3±5M#óâ‹/Ò××Ç¿üË¿088È;ï¼Cqq1/½ô’Ú87Ÿp.}ñ¸kŠLhÌGÒ9æÍŸn Üìæ˜p#Ó¡ ؈ )1Ñø|n75ÕRã†Äb1N:źuëèèèHm_|ñÅQ˜/^¼ÈçŸλï¾KYYk×®å‘GaΜ9“^' ™={6_ýêW)))aýúõ¼ýöÛtttðo|ãšøÑõb‹/fíÚµœ:uŠ]»vQ]]Í®]»X¾|9‹/Vh8·Ëç#'/ŽÎkrpÆý¹G"Õ×{ S|Úôj÷'c2¡ ¨›‰IIÂÐÉ …T ÕR[[˶mÛ8~ü8Žã°jÕ*ž|òI,X0bᩯ¯gãÆìܹ“¼¼<^xá}ôÑ?äòæõù¨¬¬¤°°Ã0øè£Ø»w/º®óòË/“——7¢\¥¼¼<î½÷^^zé%NŸ>MGG{öìaáÂ…ÌŸ?Ã0TþÏP÷ßï'\XH_*3Ä¥¤ûrÀ9B†gÕš´~ÜÿD&H £R’0<åç+ñÉ¢Åãìß¿Ÿ7bÛ6%%%üþïÿ>?üðˆ„Çqzzzؼy3›7oFÁ+¯¼Â×¾öµ¬ O¦ë”——Ç7¾ñ ¾õ­oá÷ûùè£Ø¹s'½½½1²¸™3gòÔSOqß}÷‘——GMM Ÿ}öÍÍÍØ¶­6ÒøòKKéÕ˜ðÎËÙÔÝ™âÓ’)>½B‘“÷8 %q¯—’’U‘<ššš8tèGÅçóñÔSO±jÕ*ÊÊÊFv¿ؽ{7?ÿùÏ),,ä¿ý·ÿÆc=6%k¡~ÿ÷Ÿo}ë[ñçþçœ>}:=ýb$q¤éÓ§óÊ+¯0wî\9sæ ü1}}}j# A ¢`Æ º=xÌ.’FG—©lêîL·+%>Ðz…¤WȉŸÒiÓ”øŒ€ 6pøða<¥¥¥|ë[ßbÑ¢E#úl$áÀüÝßý<òÏ?ÿøàf̘ÁSO=ÅÊ•+oª´a2ÈÏÏçž{îá{ßû­­­ìÙ³‡3gÎŒØú)((`íÚµ|éK_"rúôiöïßO[[›ÚTCˆOAq ݺAb¿§O¸z’aÎ\!>nÝtvDLœø hñ`’éÓ•ø\‡X,Æ8}ú4ýýýÌž=›_|‘âââÅzêêêØ»w/uuu|ûÛßæ—©“Aqq1O?ý4+W®¤¶¶–;v‹Å®˜€z=V­ZÅý÷ßOAA}}}lÙ²…úúzµ©†´|ŠéFNèȵaœ+ÄÜVª­€Ý#½óÑ5â¡%¥¥Ê †TKÔ>úˆ¶¶6òòòXºt)_ùÊWnh¹¤ªÒ·nÝÊ©S§¸çž{xñÅ)--½e~ÿTÉÆw¿û]¼^oºô#5AõF̘1ƒ{ï½—•+Wâ8Û·oçܹs#ŽÝQâS\L·ãçvEäaÉUgp{º7V—tM”åƒF, T‰Ï ]®Í›7ÓÕÕÅ]wÝÅš5k7Œ}8ŽÃÙ³gÙµk¡Pˆüà·d"§¦iÌ;—5kÖ …ø÷ÿwâñøˆ?¿|ùržxâ „´µµqâÄ êêêÔæÊÀï÷“_XHIJH8××§S8t ÜØs|(Ëg8˜}RÒ-Ä„ ‘ïs¢ÓTÀyXš››Ù·oÝÝÝH)¹ë®»x衇FtÅb¼ýöÛ„ÃaV¯^MEEÅ-›ãâñxx衇X¼x1û÷ïçÌ™3 Žè³ååå¬\¹’ÊÊJ ÃàðáÃé²Åe Óïó‘ Ó5'Èõêpí®1c$“ 3Å' \LKJz… ÝqÆU|ÐoÛ$t²²2%>ÃÐØØÈÎ;1M“éÓ§³xñb*++oø¹Ôñò¡C‡X¸p!¿÷{¿wËOx˜9s&Ë–-£  €-[¶Œ8p …¨¬¬äÁ$ qúôiNœ8A"‘P,úôx<”õxèŸ o§]\!>õI­¹ÆíªÃíjH¿\gñ‰KI ^¯:ú¼ŽËÕÐÐ@UUŽãp×]w1þüåå´··³mÛ6t]géÒ¥Ì;÷–¿^¯— °zõj6nÜH}}=–5²|ÜââbÖ®]K8¦¥¥…sçÎÑÒÒ2âÄÅ;a0cÆ ¢>}`ùØI·«Û×™I':”哟ˆ48b/¨K¤?@a8¬„g¸{ÔÕE]]õõõ!¸ï¾û˜7oÞˆ>{éÒ%>ùäÖ¬Yüyón›º¹Y³f±fÍÎ;ÇÙ³géîîÑçŠŠŠøò—¿L8FJISSPÏ™âãõR1kƒ ½ã,Ê©Bõn!èwuÄLjÌ5âcãV·wñ>!]ñOßOŒp.Å·Àü¨lqîÜ9Î;—öÉ—-[6¢9åÝÝÝ\¸pÆÆFzè!***n›{âóù(++cÍš5œ?žS§NêsóçÏ'??Ÿ¶¶6><âS³;Ããaúœ9DsBôshP—‡¾Ë']&n\ùñ‘¡¾¾”å3ŽÓ)ôü|Šo¡cßÉæÂ… ÔÕÕ¥‹.ËËËG4ú¦±±‘‹/2kÖ,fΜyËÍéºQl"³fͨ­­QΦiøý~–-[FII œ8qÓ4Gœ3tÛ[>å æäЧ¯¥,‹ŽCßåcüÄpân²á9 7"$õ¶ÅøÍñê” R¬š< KCC øý~–,Y’®øÉçZZZX±bŘ'DLe‚Á >ø Ñh4ݘ~DovÃH7¼ ±±‘þþ~eýdˆÏŒ3ˆ†BDÆÛò‘’ó¶MëÎ%€ÜR®Äpâsè‚&Ç¡WÆËCn×5´¢bŠGXy§!„ ¥¥…¶¶¶´øŒ´¢­­žž–.]z[Î0÷ù|,]º”üü|:::¸páˆ>§ë:óçϧ°°Çq¤­­Mzeˆsyy9Q¯—¾q®ï@­mÓíŠO?p7¥ÇJ|DR|º%Erʶ§@T›¦£ÞRÙ¶“…”’X,–Èçóù˜;wÏ{{{éïïgæÌ™xnòMÓðz½é¼ÚÚÚnöìÙéz8Û¶immUâsµåcôZÖ¸†YଓŸnàLJx†³|Î'ÿ¢—’Ëb`œüãÛBÏÏW½u‡!c%ç(y<ÊÊÊFlÅ$ lÛ&??ÿ¶î9wî\4Mãüùó#Ÿ¢¢¢´ˆ !èëëS'^–a("œ“ƒíõŒ[eCBJZ‡vÇIÍëêI6ÊLþ¥V 7.%',››¬ûÉ‹éµm|áÜ[¦Àq²-Ó4Ó9(†aEJ‚H¶¨ôù|·u×¾ŠŠ t]§±±qÄâ ÓÖ ‚h4ªb>W¹^%EE›KÛ8Ý—¨”œ³m„L©MÊòI‹‰gkéÐ’²ø¤e¥?uKÛ©FB^/¡ÜÜ)ßÖ![ضMÓðù|£¶bn$<ñxœîîn(**"???«µ_)ÑmjjBÓ4òóó)((ö÷N•å477ã8ΈÄÙ0Œô¿'¥Ä²,•hxõ}>žÂBZúX>ÿÞ€”ÔØ–›TœÌ5ÄÍñÖòIÑ4Æ¥äŒm‘ò¦|ÁPçØ–”P0Äð9Åäà8ÍÍÍ|ôÑGüó?ÿ3»ví¢§§'«×$„ ³³“W_}•û·cß¾}×­ß*..ÆëõÒÓÓC"‘P"2^â3s&ZI)-ãt?¤à„e¥\®>  ·e¼žån ô JÁÛâK^Óõ±e%›RRo;VT_X¨V:Kœø€7ÞxƒH$‚‘L­/Ëâéc4åüùólذH$¾}û¨­­åOÿôO‡üûáp˜`0ˆeYôööRZZª& ŒÓËË9WVF‹g|*"BrÐ4‰ºâS—ü¹‚áħ·Ìà;mÛ<à8cŸDò+¬¬¤@Å{²*>Ÿþ9õõõø|>rrrF”À8‘ø|>Âá0ýýý´¶¶g÷ùÊ IDATF™>}:RÊ!]È@ €ÏçÃqTüp¼,ŸiÓ0Š i‡Ã¥)iv'5’çîAֈħhÂMšuʲôKŽÃ½c ˜À]cå¬Y(ËgÂb'}}};vŒh4:ä)Ùþýû©­­Å¶m‚Á YµzRâSXX˜XØßßÏÅ‹9xð Žã\“‰œr…é“AÅ8‰O^­–ãó£3öo»p8çØ©2åIT|ܯ â¬mëMŽƒŒÅö‰'e •øL˜øttt°e˪««‡Ìõ9v샃ƒH)BŸŸ?¢~Љ¦i†ANN†a „ §§‡?üh4:dLçĉjÁÇ™¢¢"üá0á0 ¹ºÎX°&Ûá”Ûy@âv˸ˆ{ˆ5"ñwà1à+MŽã¹d;ô AÁ(ýk Ä… vp’Š e&OàCœš:sæÌ!O°.]ºtÅß·m{J9K)±m;mÅøý~*++‡ &×××ÓÚÚª}ñx<æå‘_XÈ…hŒÅ^ï˜'79§-’IÎ@sÒ±ø´GÇ. ‡ZÛæŸoT2(%@nn.ùyy·eêÿTŸââb}ôQ–-[6ä}îì줪ªŠ¾¾>4M£««‹îî¦Ü§Tî¦iòÌ3Ï û%>CqI %³gsöÄ æx-[FH%f•/}éK<ýôÓ466ÒÚÚŠ®ëYw»ÇÁ4MJJJƒ¬\¹’/ùËj±²!>¥¥”Ì›ÇnM'>Æã„mSçØ8®ðD’¡›ŽÑЏ¨Û»›§$•pX i#FõmÁ ‹V¬ ”åcÝ;Y³fñôÓO Ù»w/>ú(³gÏÎê5ƒAæÍ›ÇW¾òB¡«W¯æ‘GQ‹•ŠŠŠ(™5‹³RŒI|$pÔ2¹àÖÍ âÖr¥*ÙG->ÀÀ@»ã°ÇLðe€ðýÁM£%äñÅ‹o«W·*eee<ûì³<þøãäææf}f»a”••ñ£ýMÓÈÉɹ%GýÜR2}:MÑ(^/Î(N¼nöˢѵ¦»“Ú1lºúÄÇÄͬ“°¤SˆÂ “U>aÍÑõØ6íšÆ"%>SÇC~~þ”Š›x’£”ÙÅëõ‡).*¢CˆQn'¤ä¨eÑ|¹Š½;é5 ÷}ú1€›ïÓÜ#%û-“ÈåJÕëÒ/ýš† 1}Æ |£ V+ŠÉCÓ4rsrX¸hÍ/Ý£HàŒJÉîD‚!n›Ô&ÜxO|¬â“¢ ¸0 „<4«¢#¸°Vá )Ÿ6`  NºŠ)NN8Ìò{ïåRÀO×[é _ v›fªqXp 7Ðl߬øj%ˆ˜”ì64à”¤ÁÄ‹ Y¸d š…bê‹On.wÝ{/Má0Ý#l(o&‡´ÌÔøsÀ}n¤ŠN§,àóD‚FçÆà.é:ñéÓY¸lÙmÝàJ¡¸]ÈÍÍeùòå4]#LÃhrv&L„Àq-³Iƒe\ÄÇÆíBVeKÉIËâÜå®ôÃÒ(‰¢".^¬ÄG¡¸ðûýÌœ5 Ð ©a×72‡í‰„;mÔ= :Í0‰…cŸ”)uHB¬[YcÙÔ ÓWâ»uòò¨¬¬Tâ£P܆A87—Ò²2þ¶U’ ŽMµeb»Bu8)>æxŠO p7ŠmU[&Ç,kÈ™^7ØœðxÈÍϧ¸¸X‰Bq‹ é:‹-"^XH£¸¾ø4Ø6'-›6÷”Ëö'Ý®2ñ‘¸QìÀàIËæ¸m•ò²¥äŒe“;£œò3Ôj*·’øh ﺋÄôé4Þ è|IJ¨²Lp§7á¦å´Œä{F{Õüˆ H!O[6ûLóšþÎpVƒð¼yÌÌrú¾B¡ƒø,^L¢¨˜Æaâºè‚£–™êÝ3lMyF!>}¸‰Cgè;ïØlIı®²~làŒ„ðœÙ”_§:Y¡PLMñ™={62/váf,_íÝ Ú²8iÛtº—z’†IÛH¿g´âc]À6àÒ%Çaw¤ír¯V$n¤é¼pWTP^^®VS¡¸Åħ´´”`~QÃC§ϹÂê1¥dS"Î9ÛFºVO°')B">$µå ..%—›Ï/g6bKIŸ´›Ó¦e½G°B¡›UL›Fî´iœ¶,œŒ#wGJ"BðE"Aƒ{âÝlN ψGÁŽE|œ¤Ê{„àX”¶¤øôIÉi)¨œ;‡¢ë S(S›™sç^0ŸSW)J§|šˆ»ëÕŸ1‚ãõ›‰[8¶¨ŽIÉÓä¸eÑ!)9¥ëÌÿÒ—(*)QGì Å-Jåœ9äÍ›ÇIdZ| Ùqx/£[¤Ûþnà¨:ÓÝŒYr¨²a°KùE"A­mNz<,X±‚â’µ‚ Å-JÅÌ™TVrÞqˆ%§w ÁqËfo"‘s 8ˆÛýbTsŒnF|“_|[q[&íRrÖ0˜×]©T(nQJJJ((+£è’‚D²´jKÓdW"N—Ìž;W5úV(nat]§¸°yóçsRHÚ…Cµe²ÍL¤<°ÃÀQÜ“*>)ëg;PgCüi²Ï0˜;{6¹¹¹†¡VP¡¸…).-eÁ=÷pÒc°Å49dYt¹‡K6ðp’Qœp§ø àÛt5 AG8Ìò+ðjåŠ[œ¢’¬XIáá“D‚jËÂ’2Š[¿µ7£yLxÆáúz€õÀ}–®—z }KV¬À§†*·<………Ì[º„óºFƒe1àæõtpã½Ñ1»uãp}ƒ¸™Gu]ï …B̘1cÈYá …âÖ"3köl"RÒç8˜îLë&à—¸MâÇÌxeJà!Ä‘K—.ñÉ'Ÿ088¨VN¡¸ÅÑ4 ŸÏǬ9sRC?Ï;p³šÍ›ù·Ç3ýø°bOOOè©©!‰¨ÕS(na„Äãqúûû±mÛÆ`ü; ˆ›ù·ÇS|Ú„{÷ÔÕÕ‰>úˆºº:„jŠ[”ÎÎNª««ikkÃ4Íz`îûM3Þ…WUŽãü.‰DÞxã §ªªŠµ‚ Å-ˆmÛœ·{ýºD"ѲgÏkçÎttt ¥T«©PÜ"8ŽÃgŸ}Æûï¿OŸb”òn¬gJŠ@Ÿ”òˆã8›[ZZšwïÞÍÆUìG¡¸EBpäÈvìØÁ©S§Ç9-¥ü÷ˆ}ܬˆ‰‘t¿ÞqçèÉ“'£ï¾û. $ µ² Å·xøè£8zô¨ˆÅbRʸ§\}ãù]ÕéËv !vôõõÕ:uŠwß}—öövµº Å&‹qöìY>ùäN:eš¦yø7‘É<Ùâ“âÛ¶wéÒ%ó¿øG¥¿¿_­°B1EÝ­ÚÚZ~üãS__b'nËän2§'âsQ±#‘Hl»xñ¢xçw¨ªªRÁg…b röìY6lØÀ‘#GäÀÀ@³”rîѺ5ß7ÑâŽJ)ß·m»mÛ¶mÖ_|Acc£Zi…b ÑÛÛËÞ½{Ù°aƒŒD"¶ã8Û¥”»pË(&„ÉèîÞ(¥Ü*„ØÕÕÕÕ»k×.¹mÛ6,ËRB1EÜ­ãdzmÛ6ªªª,Ó4›…ëp‡DLØC:¥ç©*ØŸ$‰{÷î-ÈËËó®^½šY³fáõzÕê+YžÁÁA^ýu6lØ€išMÀÏqO·&´8s²æÚ$€óRÊ·âñøáÇóÿøtvv*ëG¡È"‘H„W_}•d)T›”r7n®Î‰´z&S|îQÝ&!ÄööööÆ­[·²qãFš››ÕP(²@OOUUU|ôÑG444$lÛ®ÞíR˜ð¤¼ÉžèwNJ¹Å4Í---‰·ÞzKVWW«Þ? Å$“H$8sæ ¿ýío9qâ‘Hä¼ã8›-mñ¤ÈF»Á]¶mçô÷÷?´sçΙ˗/÷–——³råJ5`P¡˜¤”477³uëV^ýuÇm)åop§ŽNZ"^6ÄǪ¤”?I$?y÷Ýwg½¢¢BÍuW(&ÁÁAÞyç^ýuLÓR~Œ;¢n2¯#ƒÔÐlB¼ÝÖÖV¿qãF~ýë_ÇUZ¡˜@lÛæwÞaãÆ\ºt)â8Îàuà“çɶø¤¬ŸKÀ;¶mï>{ölûûï¿Ï–-[èííU;D¡˜úûû©ªªâý÷ß§¦¦&aYÖÜ“­½À¤?xzï…B|ÇÖÖÖ&þéŸþ‰“'O‹ÅÔNQ(Æóa³,êëëyíµ×8vì˜ìíím¶m{+ðîšIw9¦Â|› ¶mçöôôÌ?tèÐâõë×k>Ÿx@í…bœhjjbÛ¶m¬_¿žÇqœ€Çm“‘•XÇTŸ˜”ò ÇqÂýé§Ÿ†ÃaO0dùòåj×(7I{{;Ÿ|ò ¿øÅ/èïïw„oJ)?ÁÍçÉZu*ˆ𤔛Ç©¸xñâ~öÙg³B¡Q\\LYY™šù®PŒ!}}}|øá‡¼÷Þ{œ>}º×qœÀ»¸½˜³ߘ*cEͤ ÿ«mÛÅÇŽ{Ê0Œ™3fÌО~úiJJJÔT…b ÂóùçŸóæ›oràÀ~Û¶á6ÛËMNô)t¿Iúÿ,ËÚzäÈ‘øÿøÿƒÐÛÛ«ŽàŠ‘ºRF9}ú4û·Ë¡C‡Ó4OI)ßMZ=ÝSá:õ)xïÎëÇùMss³üñ̦M›Ô¼B1R7Â4Ù½{7õW•ê~NñðöTºÎ©èËÄqG/MÓÌ?wîÜÚ×^{-dš&/¾ø"yyyjw)Ãà8[¶laݺu;v̼ä8ο'³˜»”øÜ˜.`Ÿ›H$röíÛw¯Ïç+ |ík_# ª ´BqBöíÛÇûï¿Ï_|‘èëëkvç䨛3dñdëVpK0~'¥ Æb1}ÇŽD"‘ܲ²2V¬XAAA 1¾‰‰D‚@ €ßïG×õ¬_Sj¬vꚣ#‹ÑÔÔÄk¯½Æ¶mÛ쎎Ž&˲¶?Zgª]³>Õï)ðŽâX,v¸ººš?þã?fÿþýª Çéïïçøñãlذ³gÏf=›\JÉàà [·nå‹/¾ ®®N.ŒÁâ¹páù—ɦM›hjjê2Ms#ð·@ `OÅëžêç×· ãwRJ‰íííkþáþþð‡<ÿüó©Ý7Bؼy3¿þõ¯éííåë_ÿ:/¼ð÷ÜsOÖ®ippššþÏÿù?D£Q–-[ÆÓO?ÍË/¿¬l„VãÎ;yýõ×Ù¿?ÝÝݶm¿ ¼‰ÛpÊŽ ¾U’gZ¤”Ÿ !´D"a9räîuëÖ…mÛæ…^ °°0ë®Ã­À‰'ذaÀ0 ¢ÑhÖ{(†aÔÕÕÑÖÖFKK Á`P‰Ï…gëÖ­üö·¿eûöíV{{{·ã8ë„¿N2A#oî4ñ¨—R~,¥Ôb±Øܵk׊x<^ …xâ‰'(..¾ã›ÑÇb1.]ºDnnîq“ƒräÈLÓ$S^^NiiiV¯Ùï÷S\\L @A{{;gΜ¡®®!Ä.XWW×½ÎRJb±GeݺulÛ¶-ÑÚÚšŠñü 7¸<åg“ßjiÃíÀ/¤”f<ÿÞþýûijjò~øaJKKïØ ´”’ÎÎN¶nÝÊÉ“'‡⪪*"‘RJ„SXX˜ÕëÖu¯×KNN†a ¥¤»»›>øÓ4‡Ÿ'NÜñÂsþüyþþïÿžªª*§«««Å²¬MÀ_áv"´o…ßåV­Yø@J³m{ ©©éùÿþßÿ;ÿå¿üžþyæÎ{ÇnJÛ¶‰F£ô÷÷YދŰíËûRÓ´)ᮦ®CÓ4¤”8ŽC?¦i"ĵ!‹D"qÇŠO<gçÎüïÿý¿9~ü8½½½mÛ^¼†[¡.n•ßåVŸ>`§Â6M3vñâÅgýë_çöôôðo|ƒ•+WÞq›RÓ4JKKyâ‰'X´hÑn—eYÔÔÔ000€®ëôõõÑ××G Èzì"bÛ6š¦QXXÈsÏ=7üâ÷õqðàÁ;n{zzøøãY¿~=GޱúûûkmÛ~#Y¡ÞÈà%n:lÛNÔÔÔ<ÙÓÓ3£§§ÇóÝï~—»îº‹¼¼<5#ÉòåËyâ‰'¨««£··—H$’Nî˦i‰Dðù|°téRxàuz™!Î---ìÚµ‹Ÿþô§œ9s&bšfu²Hô ²Ø ìNHNÃþÁ¶í–¦¦¦WÞzë­¢ÖÖV^yåÖ®]«2¡“,]º”o|ãtuu±gÏ,Xõ‰!~¿Ÿ²²2*++™?>_ÿú×y饗Ôb%innæÃ?äïþîï¶mïþ øøVs³nGñËóàßB´ þèÀå¦iú/^¼Èw¾ó‚ÁàoéºÎüùóù¯ÿõ¿òýïŸÒÒRŠ‹‹³zM^¯—Ù³góÿøhšFqq±*N²k×.Þÿ}6lØ ûûû-!įp'ŠºÕ…çvp«áÏK)ãŽãX/TUUÝ‹ÅJ#‘/½ôåååY®f›P(ļyó¦ÌõhšF(R-s“!ˆD"lÞ¼™M›6±gϞĹsç.:Žó1ð!pè¹~×Û­=  \~eYVOOO÷¡C‡jll¬´m[{üñÇY¼xqÖs[Š¡ˆÅb466²gÏÞ|óMjjj"§mÛÞŒ{”ÞÌ-M¥R‘¾¾>|øá‡8|øð×¾¬O†x<Ž>ø÷î݃¢(Ô¶í€?À-£êé>êÕÄ’~G)=©iÚÛ©Têµ÷ß_îííÅ[o½…cÇŽ‰YÂ?ŽÛÍFGGÑ×ׇ+W® ›ÍBÓ´,!d®hpOÉ.-A>?M¦HPJ Již2ÇO†Ñ>;;Û8>>Ž'N »»@@¼Aß–e­®ýꫯ011Áâñ¸Êûçü&çü܉ƒj=ÞO½'7(ÜŽàç<î8Nqzzúh>Ÿß‹Åž_\\”>ŒW^yííí‚„¾3é,,, ‹all _|ñ¦§§+–eMBÆj!Ö¿,Öó=‰Ìª ®l=nÛö Û¶N§O}òÉ'‘Ë—/7œ={V>sæ ººº áõz…@Qàx4²Z­"ŸÏc``çÏŸç‰D‚˜¦©RJ'k¤sÀ ÖAo– Ÿ?ð€1Ö¯ëúo³Ùìk}ôÑÆ?~ï½÷ÚÛÛE>Hà „ T*áüùóèïïG<‡ªª¶iš³œó?¸àaÍÐ1qc‚|¾ &€yÎù?8çsŽãTåÔÔÔÔ¡åååçÆÇÇqìØ1ôööbÿþýuߨZïpñx7oÞÄÍ›7‘Éd0;;k­®®Æ9烌±¡éP'%tA>?ÐÁT¶Ì9Ÿ¡”æTUëº~(“ÉìÏçóÍÉdÒÛÓÓƒ}ûöa÷îÝhnnsƒêÅ=f š¦!•Ja||ccc¸{÷.w8çIJécì_FLÕB,± QÏ÷ åkë2clŒ1v À¯âñx÷ƒ^ Ÿ??oÛ¶­B–8ç1ýàætlq{‚|žÆLƒ›ºÃ9ï ”¾ªiÚ/5Mûù•+WÂCCCž––"=7©¹ `sžçœ'c·l5 £Í¶ím«««]„.ŸÏ×ñàÁƒðÄÄ„·½½‘H©T ¶mc``KKKhkkÃæÍ›‰Džé!hš¦AQ‹E‹Eär9ŒŒŒ T*áÓO?…ªª( ¼X,ꪪdYNSJ³”ÒiÆØ,ÜÞ¼bíY„#ÈGàƒ×,³ Sû­‰RºÕ0Œv8ŽÓ©ëú%ÏÖ0„IDATóÅb1’L&#”Ò°,Ë‘–––Ð¥K—6Äb1´µµaË–-ˆD"hjjBSSB¡B¡ZZZÐØØˆ††444`Æ ODkD)…mÛ0M†aÀ0 ¨ªúÈs¦iPUu|æçç‘ÍfÙÜÜœV.—Ëׯ__¢”–Ç)SJKpW$¥à&Œs–EGÀtj/Tºö]¢”vjš¶ À./éºÞµ¸¸ŽF£~¿ßïà#„ø½^¯ëÖ­¾ÎÎNyÇŽRgg'Z[[‰D‡ …àñxÖŽ×ë]Ë'ɲ¼¶ÙU–eH’I’P­V×HD×uT*pÎ×clíù(?C¥ô¿ååe(Š‚R©„\.‡d2É3™ ) 6!Ä–eÙ–$É1MÓ&„èœóùZ¾lªöLÁÿ‰A>?Ažh®Ä €§v8çÇqº!/rÎ_ÐeYÖ®l6»mff¦yddÄóHU-Ic Ôí… …Bظq#Z[[×¼¦@ €@ €ÆÆFȲ¼¶×Ìï÷Ã0 H’„J¥‚h4 MÓ`,Ë‚mÛ°, †aÀ4M”Ëe,--¡X,¢T*auu–eA’¤µVÇq`š&LÓ´cKŒ±4€¤$IiYÎyîtµªÒÚ]Páá<{õÚõ€@à¿N€FÍ'ìõz7I’áœGcÆX˜s¾Ñçóµø|¾ ßïøýþFŸÏç÷x<Ò#OèëÇãñ€1†l6‹`0ˆP(„††PJAã8kžcŒB,Û¶MÛ¶uÛ¶UÇqVcË’$-{<E–e…s®BBHnJ¯y}_ÿlBtޝ üõc§ÒcdEIEND®B`‚HaCi/html/Images/right.png0000644000175000000000000000424312000412522015025 0ustar fighterroot‰PNG  IHDR00Wù‡sBIT|dˆ pHYs11·í(RtEXtSoftwarewww.inkscape.org›î< IDATxÚíZklW=÷ÎξmÇۛ؎åºÄ®e7H%jI›Ð$¸! ¡¤¤Š°Í¯¢Jù“ĤUAùD‹TET¡Q«R©H•x•¤„”´q*!uœPb§k×YÇÞµ»;»³÷ÎåÎß}Ù1Æ!2w¾ÙãÝsîw¿ofvM„ø_Þ¨Ä-· ,‡²Üÿà“þ"üHøË óðWÝÙM^Ùq÷އcÑØkd/ùyš(îÍø{È³Š¿°°!Žb˧¶ø@p çñ[ò5R=¿6V‹­[} 8âð÷“5«c b‡³ÎâÏ=€Þí½A7¦ñŽÌF×\ü·.¿…={÷àñ}»| çdv6¯Nû€D*37°±c#úô#äµàŒœÙ^TØIÉOßÀ¦ÏnÂñÇŽ#·Á‡7d}ô­ŠK#k MÃó£ÿP?šÖ5Å`á”ÌÄ3¥ëœ †¬™ÅŒ1ƒ@]ÇGk}k À ª.VÊ€p‘Íeaä ÇH†fл¿÷tܣÇ£x¯¸.g.ß4#=ƒÃ_?Œm]ÛôºX1 îŒJ³qڜƶû¶aßÖ}6§ ÀB'4ŸÏJ®¹ŸdI<ô…‡ÐÓÝST+³„¨åˆPpg7i$ÑØÒˆ¾Ý}F‚íáç6Ÿ #oØ<7š?—DKG ŽõC(Ru±¼ðjÀT‚¼èàãìÇ U=_ìA}]}Ìæs#˲%pMÌäf€jàhßQ4×5/Y]T¼%_"ëÑŒøæÚÍh¨mõQÕdÔ "¥Î~P âí¿¼H4‚ζNB@ u# ¢Ñ–ãÌŸÎàüßÏ¿ƒ†GůEri ´"ÞUÓ…u‘u®x߬xe†*-€L6ƒ`8¨  Üu†kzðâ Nœ,\pHšxoé ´#~Wô.Ôè5ʀʂ2D•‰ygŸŠâ¨S‰)¼þæë0MsGůÄËKcàÓˆw†:AP–˜ð"­`‚¸PÂK ˆ"#ÌŒ‰3ïœÁTj*†Ø„~ñ”°þ;]$~§l0š©¹ô"*R'*¥³/ÊÄ«h)0sOÄ.N#‡ƒ © ß¼ý‰Yžµ…)–• Ó ©YWÇË…[^*nn¿Û¾\Á•±+»@1 [í!ññçÅÐA nØ3\ü¢‚µLTÏ-^E^[nGÐÀàG—62ÊÞ _%ÇÄkâ¥Åe@ó2ÀÝeâ PˆiiÇQC™G8§Eûk«×â3zû[­‘7N’ƒZ:¬#ª.R÷’x»¿ 4KAüĽ´  †7„&@õ’ó)ï:sÌ:÷"ó^gîsk³Ç-fáòÔçòE¾þ‡¦ªÆÝCÏ å˜‚áé© à‡Ðû&'‰Â§û |Â>Û‰@áw‚8GñÒa–°çaÙûEÙ°¸åˆƒƒöh"µQL‹™ÏŽ}À÷$Ä Àæ2<˜ÔÄ$ŸÄ¤1 0¨cš*zg, .Á<ä]hy sà̼•“F Ó‚0Äìsä€j½»vìBÃm vKxŸÄon€Ài›Ð–‚îÁ_b@«`€Û(7Ç-ÎyG.œ@=¬ƒ¯xsü,šÒ߸÷±§mö‚»¡ÄŽMxP¯Q(™}^bÌRpÍyÐl¨çáX¬ÆB𧙑0~z¢ï¹#:˜ ïBpŠS½ñœFTÊ 0åâ¹ÇWPY’qÍ'k‘Ö ä™™B†|“¿˜á?o£ÿ.L…yLxÇ”Xµt*‹§ .ô€Ž¼Ÿ#¥Ï@˜Ö?1i¯ˆww"Se&J³BñÊP¹pw? Á¯‘ÊMÛ…üGLXûÅ«bjñ74 „ªõKÊ?Z‰€ÅúP ÅX© êHå§2â$nãÛ Å/.vß&ZÅ¥¥ De©eTb¼l«¯^©T 93;¤xB<ÏO.Í-%ñº€Ú„ÊÜ  !ßø•à²Mý­s„Åx2\>;Œëtç|âWÄ …¨öKPŪº…µV¸¸ƒr®ðøZ‚f‰© »XÏb˜ï¯²©%ÿz Z(Dís…:³ô}?®”p.•0½3/s øZ=ÈŒŽDr‚#-N!Èï/\ïK›A˃y'¹#®ÃËBÎÍDˆK>—ük’Ÿ•üq²sÁâß…¼ 0Õûm±p aR…F¯yqÿàݸŽÚ(TÔ™8Fb¥¢ø`Bò™äòÝs­÷¥¯‹ªe ±¡º„øñaj”#c½ƒÝ'NŠ”GW<4‰ ã~Ä'Ç8rÖK’_°Þ—7ÂË@Þ½¬nk½×ø¨ýMu“ü ñ}þE.6@ähw`d,ŽtNò§ø·Åù Å\Ä1Pí¢±­ƒÙ˰ÀF1LÏòŠ|&ùVM¡ÛñþÈË'_?æç`å ä `w|¢þ*Š Ó—j½‹‹l§Z2ø5’Ÿ”ükƒ€&ùC¬[œ“+ÿ M3&à##bqçàüe$Ù%~þ¤ä'%ßâ?ƒ!ùË*^ÝÔW>𔾽a}Õ#‰ëéßç¾›ûåMßèIɯ“ü„äÿ@òWh»õ¿· ü¿ø®ƒ;l™ùêIEND®B`‚HaCi/html/Images/HaCi_Logo.jpg0000644000175000000000000011024410513214352015477 0ustar fighterrootÿØÿàJFIFHHÿáExifMM*ÿÛCÿÛCÿÀ@@"ÿÄ ÿÄ]     !19Wˆ—¸Ø"Ax#26BQUXav”–µ·Õ×38Riq‘§¨è$%'bcr4CSVfs‚¡¥ÁÖÿÄ   ÿÄ^ ! 1A"Qa#2v 3478BRVXqrs”–±²´¶ÁÔ$Ub“—ÑÓÖðGHd†‡’ÆÇÕ×h‘¡£§ãçÿÚ ?÷ßÃVéeóÏÿªÎ:ýê±ðÕºY|Áóÿ곎¿z¬€SÕ7çó€Z|Šûªç¶úý[kܯláÕùT:‰3©n;ó7B‰Šƒc^´Í)¡PCÀAxéª aQdÉÜàÓV8øjÝ,¾`ùÿõYÇ_½V>·K/˜>ýVq×ïU”båCŽ«¼>Œ}bÝœ(Ûê ²¯îÚá¼ê§Ç1D⒩Ș]¯ìaôÅ*IšÝ‡ÝKNâ—¢CÙ&^æQjÛ©qȦEvDZjõ_ [¥—Ì?þ«8ë÷ªÇÃVéeóÏÿªÎ:ýê²·ÔÿCw«™£GV!kÅœ¦C¬Âá¹no]°1¾4]ž¨ï,N¡?n,½Hh©ó°þOTߟÎ}iò+î«5}ΗýP4Vmoägê‚—I¥î ý))ºà)uËS‹Ur—¯ïO¤#ØÑvLjV¾¬FǃnÕӉƲ'‘k*ŠÑH6A›ÇÒ?Cèôô¿ß½&x_³¸çÈË~Ÿº]®œŸºn¸¹M)?t±Õ[Õlz§JÑXÇÈ>½kýq.•)}q8áÓVðn£‰ê)deWr»Ælg{iŒciŒci•Á篥À^¼±ÚÜ:Ýz‹—ö›§ýâûæÕ´/7D}ø@ÖÔí¥ î³Òl}Z¿vŠg'î…b3ɘo ݯ®²I´ƒ»eúÌú/Üúê%ÔŸ’ÅÒ›wˆ}e¸?þö`¶•ûtB^Øþô.®Õ³>îÆTôÚ¾ÛÖl™W‘žçÙäüèwîz“Õ\Ç´i¯_ü5n–_0|ÿú¬ã¯Þ« [¥—Ì?þ«8ë÷ªÊüºô+ú©7AE‘Þ<|¡¹ZµÚ»ü‹ª?ì¦gÜ_fÔÿÍr‘÷³ËÛwÑ2ë9¬(þ»§uNðAW)ê-ßJõÔR'‰´WÖ/Î "6‹jýÚž *mÎ~äšµ?ÃVéeóÏÿªÎ:ýê±ðÕºY|Áóÿ곎¿z¬Î\RäÏ-#Kä¶„ÛZ.ÊeVM¤vÏ¢Xª–*Øî L0mb0v:2pO$cœ& ª”LÀaóþ4ÖŸ [¥—Ì?þ«8ë÷ªÇÃVéeóÏÿªÎ:ýê³0LcMi÷ðÕºY|Áóÿ곎¿z¬|5n–_0|ÿú¬ã¯Þ«3Æ4ÖŸ [¥—Ì?þ«8ë÷ªÇÃVéeóÏÿªÎ:ýê³0LcMi÷ðÕºY|Áóÿ곎¿z¬|5n–_0|ÿú¬ã¯Þ«3Æ4ÖŸ [¥—Ì?þ«8ë÷ªÇÃVéeóÏÿªÎ:ýê³4½Y§væó·2 iM]°öõêK·¹ôÝcK±ß-/NTüM jñ²’‹rʨ‚`ñ;äëhÿEŸ­éjÊQï 4¬$‚ ¸k)¼6µ¨è¡@#Ú|Å·aD®P1J£yz{ˆq€b(i«o|5n–_0|ÿú¬ã¯Þ«;ƒ^—_M¾JoÝÇ:.’æüMÛn i¥)Ò–Ím¡˜Õcm[Ré E¯HYŸCòVz]}œ¼ë7.¢àæd[Ç&åf1R.H“5«!è[uV~Ø«ºÜ¼…T{wg'¶7¢®KÜ;ˆÐÜi–h=¾!ðº7·âîÜõ‡½N¤œkæ‡9zݼ –¤èOè-×q‹©ìòúÕ%UÕ{Z§z°ÇÖXÌqª!åäDÆðÍe'!£œH¨Ùұ͎«ÄZkEÌcÓL„®¤Ýúwõ(‹³X/®7Mòa³•£y#¦£XÕ¯C:p‘¿Â3+Z¶ØlªåA)0ºÇ:²)šŒ m•¥Ž›ô&×ÓXžuIéMÉ®“ûäuùŒBr¡g )M5»+m]…·ªñË7MÓØ“82«ÁÚ Åó÷*<šÇ–­=xÍb/-]–®Øæã+6¤ë=Óî£Ô‹§ÞõÑPM$6l5Z[eñötY ¼½guÒbÞÊÔIéBŠŒšÜÅ7ZöÌd¼FV¯j–òÒ3ÄÙ¨–+b =„Ø !ñ€‡äÆšcÆšcÆšd˜ô´é[É.«üƒKKh¦-àj•´ãf·>è°´]zNŸ§?rºJJ¦‚­×³M™›ö´ºDs”e- Ýœî¡ëÑ6+$gfÎ ºx×zoôëÒ:HL7-þ½¸98 ʌĶֽFµ–y$äÄ*ë³Ö± Æë¨d„oêÕÅ¥Aª22j¸i®ÓÑýéÓÓr2µ?KÕ»§é7pÿ‘»®6>Ûx$â~‘ Á¼IzžªAÅdâÍL‹kfJ5B2¶XÖ"–›œciŒciŒciŒciŒüé‰xÊüL¤ôÛö±PБÏeåäß,F죣#[*ñû÷‹¨ D³hŠ®,q&’g9„¢9ðbúkX¦¢+ñ<¦¬/+;(Â1Ý£‚ÒnÒdÉ%ä$¨Í#˜¢£•Ó"ºlɱ˺pŠQBüË ºj“a»[zÊÇ$÷z°XO‰äv(‰ø%ÖÉÞÂ0BðлTÅ…$ç9Ä6Ãr÷=¤Ì o3œÞ%2·R± Jÿ%N’A÷c­£ôÕóZ¯GÛ‹%Æ}l™ñB;æÂËŠ2cŒc>ž°m1ŒcMuæÔÔz³yѧ5–è×}¯®¬­ýZ~±*з¬º àÐSìŸÇ.¢&0¨Ùc·›,Yº‰*Rœ)ÕçÑ ×–*í«|ô±‹Ä[7“RÜG³O:”¦]E!;§Héû½žIyZe‘R‚Þ¥P·ÊÊÔe\ªÝŒTå›Tš¼½ž1¦°$´V,”›-‚›q˜ªÛªsr•»EbÃî~»aƒ|¼dÌÜKô}-"ÙË÷ˆ"é›´ná$ÕLåÂËÊzfý<êÃfhî¡úÒ¤ {âUî—ߨF²A“ M¥^ZÅ®o ùX²\(ñ¨+犫}sóÂáûéG'£^4ÓÆ4ÓÆ4Ó.{ÐËÑd—æ*—ËΠ/­š×v¤clº¯D×U4ÇÝ5Gh¦ú:ßn±ˆ™öºÖÖ  ÚE´-æç åY¸É:TR•éË6z?\„ê)ÔçJj;äJšk[¡)¿÷„;¤Ë)Ýw¬žD œ‹~ÀE¢/W¹ªMx†Y¹ÂÇ(«u}i$Sd†Í›2lÝ›6è4hÑ›5jÙ$ÐlÙ² •$·A"‘$PE"4’L¥M4ÊR PšóßøÆ^kæÚ·‹š?]hú:œÄÐëÍ"ÝN:l˜¢”¥®xཊå;åSR~Ù-36¹?wꢱŒi¦1Œi¦1Œi¦1Œi¦a‘Ô+V6Ñüõæ®bØÆkVò “ÞÖµÅÁ(‚e7^³%ÛU>Àno˜Èú@Õ¤ê}fz‚ŤŸ–W[Ñ{(—Ãáî¥Ò§X¸¬§oþ*³§WÅûoˆ}£5¸Æ1¦˜Æ1¦½qÀ-N×{󧆚VA©EínSh-2ÝTüÔMlÚuhYøL@@íQˆxõgEíÓSØ?î˜Éz?5”í½fz}E*O0­7š6`/‡ÅÙJUJÑrDý‡âò–"ž/Úøð¶CW×ä½O`rjAÖ¶" ùoëÆH7´e_xÚº‰sHvA*ïæÈ‰‰BŸ‹âÉrë_ÊïÑCÎKãH)/^×:(ªiŠG¯‹·U§Î{Ÿ@&n±¥îËK´k$ŽYô=y@Pé¤#r¾7‡*ùU›X¸Ãž%uBû»…ä ¸fi&@ñóI$Í)ˆœ”UU]„z8öì Òö Ϊæ;„)¹Š8ßd¦&dqc-Kèiâ²tØÓ5åD2QbÛÚ®HqH”üªýœ%Ô›VGÝýVn¹Äëïa¾×ΧsüV¾©âY$¤Ï°Qü8ÁeOÉ“ìø¢‰ÀÇ ºjã`UHÕš–5Uu4Æ1›PËLcÓU¾ô¯õSmÑ[XNÔ?ÒûCíX‚‚`¢¨¹>Ô¯k 7Hˆ€ŠbÚµ³'–]Bˆ4+’ˆ‰L%’3hN½u”íú†E*ŸšV¼u³Ù€½¼]”¥¿Š¸¤§oþ°$W¿í|/ɘ½ãM1ŒcM1ŒcM_ÐyÕ_lŽ Û±ËbÚµ#AêÈg†Oº‡k}žÙVÛ3dUíø¤M]oRUÊ}ÃÆeZ›°ù~Í ò’ž„]m6¼BæÀ¬ï$j³©áö:¦±Ž”Lž/Ê šæ©€¿µóm—kÆšcÆšcÆšcÆšcÆšf=^“sÇuÉçkr€)7¢ßvö¥8¿¤¤Ì?ñ;ãÍ…s ?J, NºÜæüBn4ùÏÃî?ßó1„q¦ Æ4ÓÆ4Ôòz2 K#×'‚MÌ`Nk{>î(¾.îé"·¿´§hS!€;wÍ…3 ?EÌ nºÜö€’æþrpû&/üŒP×óiŒciŒciŒciž êcÊ”øwÃ=¿·X¾#;»˜xš°¢r•u¶EÕ5â«îÚýŠàõ”Fèé°˜¢¼meñ o”Þ¹J¯Hߕ߄^@Ñø±Y’óªú –[ºMÖ¥ylí³G% %T¤(,SV¯mÖ& ¦U×»£•|‘ÂílZsß%¿fUª/ëóDÀoü¨Œ#óSŸ%õnß>Qaô!°‹ÔGS:­âT’×8ÏÃÄŽ¸ž.ôyR JNXɪpAíŸT|5$­úª¨ºŠ,²‡YeŽuUUS™EQC ÔQE"cœæ1ÎaÂ""""9üc]:ìÑ„DO$DDòDD÷&¹¾´ØVK±(ÛF’üÑ–ýwm¯Ý+OÃÄ%m7Z•k/uHSVn.Z&GMÄÀG-̪ wMCéÿÇÍÑWäV՛ʚr{hR`­ÌÛyÅ]X·LÓ<¬ ÅH È×¥Êú L¥ä#œ¦­Ì²rÝ>¿9éôÍ µ´í¸±©Aë /ZÊj²ȳy_º,°[*¾]¼…´6÷Ê”SœÉ;”³¤Ñ;xõŒŒéªÞIJ÷ðøq&Ø®KN81Ÿ˜úÙ׺ ŽpüEÙ¨&èEoÍ{STÍé¥Új]€¤ßÉsê)&ìÍËq/m-§B«Œî˜I‹_(™1ÖuʬºY‘›7bÁ•});^%µ~21ïW¸ÑYó©5¼ìwEñ CÀ„:†/ôÏmN"e!‡õ§F ÐþQ ´|Ÿjëoq摤ëq¼@‹›U®NÅãÿeEDFÕü¾þÁ2E~¯oˆ,æ¦ýè¿v>>½ƒáUÛÉ0)}Êq,¥G°ý”„IñMqß–õÇÒÞã±æî½E͹D‰Â·ÊÁÒxµeE_6›ö Ë&Á~ºžŒehæz®r¦SÇêA­k¾.ý½Æ§»_Ëïñx=ðON÷íù<~?ãïœÇRîd¯ßÊÚ ÷ø½__ëÓvÿ‡­ÕÝ|_ÇßøûæÂÑží¾(NÎÃ!*ûÂMÅ‘’~Êã–_Q¯Õδ´ÿJNÜ Uº6¢žç`cm¶_µK<®¹ß?òš¯KïX&%‘éGÔ‘¹€àß(ßoãFi›Œ‘þ v…ýÁć5ÛœÑä–õÔ[KFm-†›[n]uvÕ[´ú<+™zN­ÉT­1åëõ¸¹HµßA˾lŒŒc¶¯Ù(©\³p‹„“Pµß°ôIálɳíÊ‘ÎåžôÉØ"aýh”¶ŠÝ‹ÆPŒªÂ!Ü<@=ŒÙ]nÔpRfv9Q†âÜY¯Ô‹6Žr¿Y¢}*šWúPúpšà„šÑ©TEzâq°å|Ô’«,³uP}ëÚÑ/äUòÕB±–Q½ô ¯*’ëk>D̰X¾!m{¤1—Iošóuù˜C¶ì=¼K'_wß°€ ÀCÀ»W£o4µÊk½¯×jr1:†[]Ú÷I6åïá2VöµY7.;šC#0 {&*¢pÖ¹NûÉ»7³™KÇ£8—È Ÿ|~D‰rÀGÍWƌڢ"’¢ s­é†u­ÓrëQª÷f‚²c¤ ‘2ƬqG ŽÖ’^I¶µ× UR<×ÍP”×·QQŒæws°5ŒÑ뛑l¡Ï&0ÃÜ+ÒµÉ&Sx|äšK4h²ÍÌ"›„Jt)ŠtÔ1 S Í7"<ˆ»SFÉ«oGѲûF>ðu§\lÓâ&(©ñMIèsaØÅbu|¸Ó¡Jl^2íJ‹!¢û×XÁ›O6_¡6Ì…~ ºÓ‹Ð¢bTúirBHÅ3ÄaíñŽ\[*èT”?JÏ~›ò?¶‘Gþã·Ä?ýÌ9oìüuìéŒg—9—º_h^<^ïR(Æ[…5úS…Pdìå³O=I›g2‘AË‹D±Ñjñ³–Ê£°.Ýt¼Iëãô“ò[Ê|z¬ìnì¡UBŤ“:Cqš7ŒÂm†ÉÄ7ÜFÏÃhMÎÕATÖ7™eu&%’æ—î8Õ&)EkÚ(ÙÈX5ž!¸­ºë ½-æÙVb0O6É6™C4]zUŸÓ"æÏ7ý;Õÿ…ËSfÉÝ}”ʶ{Ø)¬1ùß(½«ê^•c'ÂöG³½gÖ½¡UYÙßí8þƒã÷v=âx]¡âh¾º©ÛÞ¦~X|ƒ¦Ìê~DüŸö¯ÊêúH±ò“Û~£ìÿcdWþ/…ì ž·ë>©áø‘¼½ßŒc4þ¤Æ˜Æ1¦™/¥òëó›èÍö<ãîkõ™úQß.¿9¾ŒßcÎ>ãM@1Œi¦1Œi©þô\~]~ ý&~Çœ‚Í~³ _EÇå×àÏÒgìyÈ,×ëiŒciŒciŒck¨wöæªñãJí áuT ZÖ¹Ût‚ ©ZIH¶j)ÉC‰î•‚PYAÅÀ ¬ŒƒT»~>f´öM«qì«î×¼>~Ç·Ø.¶GŸŽ ©/c”s*ô“9Î(2AW&nÅ©Må´f’ Q¤‰ ×ô’ù]ïk\jþVd¼Û!â;Wg Ý^ʧH¬H,Îñ0*,W&²¾ª"æ„Àà>[‘UWThkÖÙpE¢šUp‹ o-i¨Häüð¬“ô«Âv0y QM@):f)Æ+n…~a»{OµûyAk•ÛÀoæÔÓF9O;e4Z9/È!ሰ«¢Q“>c¬C¯We”§ØlL“¡GBlÏE$gdõ-žâ[G‹çsP[Ì3{j¢ÂÂ1§¦Ä¨¯­mÔ++Ì· ¹Øõ$I×+P±ñ©a ã¶½.BS‘4ÈePÅ"i¢sœç)B”Æ1Œ R” "aÏPëž&쫨7}8‰(ðjøOë3hœó $nÃâkC¤ä†ì=À$ÕŒ(”|iŠ¡Øßú¿@kÝZšc#‚^ÆB-f˜"n$A/cŒz^mˆ˜ Vd+HA7.Ý |cÝÙb›è©©ˆÔ,ƒ¨K÷-&’4ÿØÿšäZÆ9D%}“¶-Ïœj$­È¥kl<Ú”{Éì’.¨Òø­Œ¾Þ]ÞÝz96ö>)HÚÉ€½Dïk\¦ÁQI¡³Ûí«ìŠ ÆÒE}¦âO&|)8Ö”SÎTn-jZ_’åÄ9í²©xL/íMúP;ŠM(’ d…ÃGNìL=Ì>ˆA"›vȤÝH ¤‚ ‘$R!}…"i¦!ì” ù>\úr±¬\IÌH2ŠŽhAQËùH³hÜûeœ8:i&Š•"Q®f^tu·M.*ëôVN£%²%Ñð˜Ò—Ù‡/‘üg)`bºt oÖ&ö-ò¤  áAMU“ug³M)Tä œ8ªà¶¸jG¶¯2mÃh”.ÊDzwÛ@ÁÃ6a"¶¤«¼qOF—VVÖnÖg;+edÅH¥:¿w˜²Ä²Xa.$yì ¬0ëäå•òƒ*4¨ìÜÔU Í>Ñ‹¨Ù)¥^IUÔ"(&¢Ë*`"i$C(¢‡0ö)B˜æ0û¥`v$V›ÛÓ¤`õ^È™L@Š£ÙäH ?ÙŬQü‡Û—«kÚ6¥Ò*5ä $Dk5Èx$ʘ‡À‹fÔ<=½‚ßËß9~i>¸ÝWiöí±i{³Èˆœ4çÉJ4Z×z$·|þ>^r~‹Ñ1É·­ó|‘Ó£°Ä›[Ë7áÒÑÀ°µ›+º91UÕþµTl×RH1hª!¡‹Ü‡œv÷*¥î÷-¨‹Õ UÔ`ER\Ñ‘~Ù÷m™#îºmÃâ¦sœjB-¢#Jnáàa‚ˆ@†ppUâå)}eÊÆp5QeYeªªœê*ª‡1ÔQC˜Lu9„LsœÂ&9Ì"cDDDDG?Œæã©±÷k¨ë9Q®,ÞÆp¥U·´rßn¡¶[qJ3×Ï‚îIh€g.x F_Cr®º´6×ý6ýž…Žý8ÅM–‹ÃÝ¡œ®²Î£3ʈ2s)$ÆF¬ààYöËxÉö™¨Çž;‰ÐI˜ùVI“=™!5ý9Uú9¹@ž’õ=ÉVj©’,·–͵Ü\ö®–9­ITA¨ø¬0DSyU}kŒc$æ¨Ë^êÅòYu,üÀ9‘öuØÙˆ6móÕ‹ä²êYù€s#ìë±³li¦1Œi¦zû‹|âä&¶³µíAàs7­,ââZ2'0 ê–3Ö^M`)|SUçQrJŠH¦ñwŒÈfŠyõ©/nq»(×sj-"|yÐ$9C~îàïmS½§;]eÄ6^VÝQ\w*ÄqŒæŠv3˜PÕd´Møsjn!³:Èœö9á<%áHd—Ä)•nLgP^ŽëN€š_¿…ÛÉç3¸¼Ÿ)jÚòÑZ¥Fly}7rYè!%^ÚúÍ:Û'œÃ!^9Ìî¸úREŒ@ʃ§MhWÑ’ Ðôw—¡—]‚¶ô–äfÏ=_›çžÙ–‡•j“ÈùN8çÅRªÝËeŠdÔ!ƒ±Š""¥Q3BÁêÞupbGŽ’‡¿ëä_Ìi‰§€—uL£É ›¥D…˜p>%\º9()Ç* ‚X‰e #êOf¬«`z™ž;Î 5~^hVÙ¶!¿#1DdšEFàܹÂÇm$âîcÕnÑGXÝÌÚÖ™´ã>ëmZ"‘{Bù¹6ëmÃó”ª+ÖØÃJH1å;*–û Éɱ…û6ãƒÆL V†™xŒ£¾^#+ÿ®ù-ÿ®ÿøGW)è‘ÿœý”ÿæN˜Æ2¿õršcÆšf@¾”w˯Îo£7Øó¹¯Öd éG|ºüæú3}8û58Æ1¦˜Æ1¦§ûÑqùuø3ô™ûr 5úÌ}—_ƒ?IŸ±ç ³_¬i¦1Œi¦1‘ÅÔK–Êh ~‘"T6ÆÄdå6.PP=jŸU0¨ÒBÎQñ¡$íPV2´sx;a9É3¤' ‘ÆvvšÕ6MÛ³*:¨˜ ½ªQ6bíDÌ£h˜äHwróOJA)…œÈÚ¼¶ ?Z@ÈT¹7;@”Mç‰,¼uÅ€ `:‹A„1LS‰Lëü…Ù¦c#Äì§ÊºC»ÆmÙD'ª² ÓŸMdÐ’ª ;ÇgãJmQQÚDUED÷.½Á¨Nà–$ŸH´„ŸüÑåMb_ŒÛCñ?ûÿ†/ÿ4ÿü›^^Æÿ¤ÿöýº‚¯B§ä²ßŸŸþÓû:ñW-Ëa¯B[ ¥ë6X¶sPÑ"d*ìä#Þ¤dµp‘½†MTŽb÷)È=Ž™ˆr”ÁøÏ6½8n°ëo3Ó;Œ¼Ë€ëNµ½dÛ­:Ù!¶ãnÓ¡ƒ€h„*„$ˆB¨¨‹¯ÊF<Ħ‹(Ú“KNG‘DPy‡Øx ·™y—&ÝiÖÈÆÌH „…EU5 ÜÍâÌÇözÑ—úîÒg’ºòyÇQhä•OÖà$•ŒÝtÎ[¶xp•óE˜J4=xíy,ÁŸŸ)/ÑI ©8è† ¥{(õ´{D½‚?ª9vªH“Ø?ŒpöäÉÙ‡~*'$JêÖþz]:³Ýè˜E„¿DÇQ8„ á¿:ɲ.7*Ê%ðY ºpd»ÓMC&0Bbp£œ´1žÁ²ž½½º&éÃ؇÷N6'Ô!¼RrÂÆâ>;–ø©R®\ÆÍªÜß*qåm&$d”5Ê+Q%õ¥ñ}[ÛØÆ2qênéŒci™úQß.¿9¾ŒßcÎ>æ¿Y/¥òëó›èÍö<ãî4ÔãÆšcÆšŸïEÇå×àÏÒgìyÈ,×ë2ô\~]~ ý&~Çœ‚Í~±¦˜Æ1¦¸6ÌØu½OAµìks¯T¯TaÝK¿8 <çˆG3)ÌR«!(õFÑ±È ‹ç¾vÝ÷ €nm³iÞ*Õ³nIk,‰Ü&Њí!âÑB& 8ØHÂ%‚h3@D ¢â™Ý88]U%C«?!)`¯ñÚ¹ >ç×JÒÛ°Áº¿Šâyó2³çÀ%0–*)sN¹n1Ö™ˆ[±\Ƈ†òкGÚÐűÎí#¢_f,‰WøÃ°1¡48  ©È•à6.¨ª‹‘³ÈLE ?IPNîä†ÑcóH± ²”à\øóã;6•«A^×aÇ)#‰ˆ›._r¦Û¬±Œd¾Õié–é5Çô«Ô™î@O²í5x;ªÅ(WOñ™Ô"ž³2H €§ž°4b"_דQJh3×i}›~§kØx¥îV8Šë#‰ ¢mÔ”z“S½\¥jÁzìýÊTš·YC¥ ˆ\º—Q„ Ôk4ŠÛPgS‚‹¯D7Âb1‰f“6æTåù®" «•Ì7ªê ”PÆkÖ>à¹CˆVàÕï«s²÷ŽE¢¶\8Õc²\*%•0Ú*‹±áN`ÑAÅE´F&Ì5—îUîí]Dª6Ú3p¨æûš1¼eà M÷!6áQS§ˆ Ø›kS1’ENM™ÌurâŸèIçÔ¨ÄFûŸ¯v  ÛÚÀ©%å3F«yvùÓØVD(xkT¶6±Õ™¡ã:¡Á¢衚3å|½!þ*~¸©ÈJäo¬\øÛ8gs*7KÄéî«»8ˆ²¦b¦_5Ͻû5‰ôÌ ¸t퀩•w e3oŽ-ò‹ “1†ûì1Ò+hê)É”@Û6Q|×±b§­*"rNChSÞºì7Ñg¿_a®§èñÛY¾­ˆo# míÀ¸çlf/åHGð‹"Q|/”hAÇ <<’ÁåEQMQÓÆ@­u§¦1Œi«Äú;ܪü.qR69/X¹qºxB¦á_§š²ðâBb¸rQó\ûß±%hP©Ñ‹‡-] ™2®Ý<°~g5Ò/•_¡/œZªÛ1%î~½Øn‡PlÓª¯”͵åã¬fžœÃå¤Ö«mm[´=_ÀuKýº@äÚ2äõØü§å#îwØc¦5—“( U*y¯bÅâ*¯$ä7I}úä³Ò›°ßa¾§ï2*¨^­ˆï$w7 œšo²3òd,|Þ´ID¤ ÷uñ¶€ÄL’Hª¢ZðV/’Ë©gæ̳®ÆÌA³ož¬_%—RÏÌ™g]˜ƒfãÕmkælåÃ7 Þ3p³WmVIËWM•Q œ ¡UAÃuÒ1EdU)TITÌUP¥9 ³wI¯H³N.I²O#äX;@ÇAÓ'­EËW(œé.‚¤U3†ÐÈmè¸$w7I®'JÍ=QôÝ¿lÔOΪ‚©‘c«ovZ…A Æ1ü-¨QÕDÊSx|°(å†4Ég›£ƒÊÛ-ÊÜ ¹šÿ­JÀóLŸz_‡á$ÂÇn¦Ô¤Ðo¸˜1Kh„Iáº<' ¹ƒFŽ6Û‰ä†|}‹ÇïsƘÆ3מ¹î¿£;»Ê¸(-c‚jɼîr&¡Œ ·nS:qàP‡–‘¢Çø ’±ÕÕoB¸§ËPvµuYE)’ÍÆ›-áû×±ðfhªö=ã4œ¬ªlËg‹E׬¦Ü©¦gÐK¹SºÏÀhôœaÒ»ð€+.ùëÓ˜Cñ„ˆª1éGãðY™BÄ©Œ¯üþFjýí¥ïÚÅéõ™øEÏéou¦8BF·!æ “I vÍJóËxõµ‚nß°oC–Êà].â;A¾sñúùÛ…¹”poò̪Æìê0¼Ö?]O:ûfõ,HTk¬ì ±&âÐ ÂQ!Å>­¶­ÝóÙ\ëˆN%ºBöÆ,ºm‹™5 ,úØÏv’³hãNÔ¼¯  Ã3ÎCmøì´cNL¼FRó‘^FÈ6Y›ø÷N¾hà‚ší^4Xè9lºfìb,‚É%>Òœ¦(ûC.ý—£×1 ŽÖ˜æ¤$*„$$˜‚‰ '(¢¨¨¨¨ªŠ‹Êyj¸}€mPm¸Û–Õƒ˜¨.䉉"’(’"Š¢¢¢*iŒc ®KLcÓLÈÒŽùuùÍôfûq÷5úÌ}(ï—_œßFo±çq¦ Æ4ÓÆ4Ôÿz.?.¿~“?cÎAf¿Y/¢ãòëðgé3ö<äkõ4Î ³¯ðº¯^ܶ5„àXŠmzNyÚ`r¦£±bØê6Žnc—×$Ýù̈ >c·H¦"lçY=]6áà5­NF;Þßæ•²XÑHÿŽ5š™‘öŽÓïÿÑäìnÚ¾nnÃÝzÒ¡Ü QgÛ]†9¸þ1‰z1idß´P™©ˆ6ÕÑ$òyÊ’¢+êÐsÉ&´ïP ÆÍlæ}¸Î+K/¢{ج»Ú¡'"²qª¬z9‚ù¸Ë—a,‘"‚ûª-—=w¸Îì…šófvg³öÉÉ)ùg&ð™ì›¥*š%1å5@T7(ùm›$Š SL…-ŒeÞGŽÄHìEŒÓlFŒËQã°Ð 4Ë €¶ÓM€¢6Ûb (ˆ"(ˆœ&¹B›2]ŒÉvä=.té/Ì™.C„ì‰Rå:oÈû¦ªN<ûÆnºá*‘™ªªéŒc?mzÚ•n’ú´¶½çdÙOš‚ÑÚ¶¬rǬbwm7!qr‰€J"Jû[H øé¨«s€—Ùâ±–F§J½|.1’ÔºGÛ.ã?aŒO ƈ‡Q:¤ks{ÂBK>oâïâ,‘Ô(Šj$¯)ç©|¨²­âÊL\ñ"P<Ö/{»‘ ¦jp"û¸+‡,áJ*‰Û'I{ᨎŸvßt…ÖNâÞªü¹†{!æTdµ93JÀ𱙑gë( ’!-\è.§p:Lc‡êFéš;tœåWè¹á¦½ËI{£°)lGSí¨¯œñK¥«&A.üâ=ÎòÙZq]¸º0‰•Ìúè&P8œ±£­Ê¯ÁW'íum7 ø[3ÚtF¯åbJª`E¯¾:¢–hÅÌA*ÒRÌjÌP­È]˱¹OÉìÚ4'Üì¯ÈÀj_B^f÷Ö<©äŠ~´¾¨*«À„× ÏV§¥KaþÌ]0ÜäõP½g.Ù‰.nQ6ßt—±Æ˜õlâ¸I”c­Bè÷½'„Ò(¡6yêÅòYu,üÀ9‘öuØÙˆ6móÕ‹ä²êYù€s#ìë±³lžzäËLcÓZ=z*3ŠKtÄ`s‰ËXåÖƒD£ñ$›Š–¯²‰ø…[ ª{;{Tøå•²®¾‰B†?N ÈQì5¶Beî?GGñÍnÁûâTÃÛ÷DGò墳~ºc^¯º‚i´AÜky*ˆœ|ù[«û$ãæJ¿UUó]eп*GýÌúyiŒc"~½­{Û^" èõrvC´[ؽ®Ië?Î*ˆˆþQù̳ŒÒƒµ:¨þƒùÆ5ÿ¼äÙÜ~ÌÃj¿gö¦(‚̶Á¡´(œ µ«e´Dø"""|X[«Ë®ªûÕÃUýòUÕ]z”j$õw',r‘Í}^h±o°ãÁ2xQNRMw,íMü`S.¥…‹Ù…SI–  b˜ÖŠÏs׈–TÕè„£IU¡®”©Ù¦öÚêU„cŠÔãYŸ¬ÃBμ3ÂÉFA®Í%ú¸$Wâ+¢¡ÊU½÷“#uws¶›'猇–S_FU"À0æ9šSÄHž'´k«Åås¸ˆŸnJ©qC.ž6ZÓi7Ûª»&ê\……n ÆÝdØ|áÆ·e3›Ùåñ[Ue).®Š20 „M" Æh L1ŒcM3 _J;å×ç7Ñ›ìyÇÜ×ë2ô£¾]~s}¾Çœ}Æš€cÓLcÓ^ÿéqÎÒ×çfŒæ·à»ðÑøü&ÿìÓß·àçß/áNìMû2÷£{÷Ü~ïþÅ%}Ð÷+ܯý×½Ògo߇9þë¯ûÙÿÄ| 1¦¯ùðç?Ýuÿ{?øž¡²ón×Ô./_ršÕ¬GKŽÂ×5wµý[ïÔû *5•’q#A·ž¥E÷`óÄ‘VÒsR$šsÜÀ Ä$]çB©?¿Þ©tH®ã'u¶W*QÀRøÍëöI†pÌü$iÇÖ§Ø¿¶gåÍ ` ã+0•¨VÅe ^ˆƒ‰fOÖ4Œ‰f‹ ‰Ø<(5n’Eìì {'D˜ÈÊÈó ¹æÑR¢®$"$å=bæAÊ”ãKðqˆõM4Eä¨Üåå µS>•¬õÊü#löÞ+Ê+’ä™U 6\©ãÚ¯¯eô嘕7 ~@ ò*ýPšðM?¯Œc,wT{¦1Y¼í㯴žà¾¢éz¾ýkMP7€ÄV¿U••HÄ0)æ4 &üaPJîaÏZl¶ C—9õíbg弿CQÚ7œ_?/ _={õUÒ-ìëªa|»Ið뢇 ½Ò&Èn3Ây¯s®Špž~~ZëŠo¦‰¡êðÚbµÓsß„¯fxë‡èÃ,ÛÑ®¨¤q­†‚'¦Ë {2È)8x’ÌË„qß™˜JH>¶·&øsŸîºÿ½ŸüGÊã(VÊ{ö¶6r—ºMŒÙSä*½ÏÌ}ɯ+漸á/+æºìŠ¢&?IOA{ ÑÕWÔB;bVÄfaá<“µ–A8O$ã„òÕÿ>çû®¿ïgÿñðç?Ýuÿ{?ø”ÆzZúº»o-½/úg1øù±¸éz颥jbF5hÖàÛ—éØžÒgã$™ÌWn  Å¨A™V¿0Á›õáSœƒ4ëÞBb9]'M¶~ѳæk¦å›Öè»håÒpÙÂeYÒ8{ š©ŠÁì1LVŸ&ƒ…›'ß¶¥BºõÇ›5¯\rœþ%TƒX‡^ºàCö©&ØŽaÑ/Çá‡ñ뻌eê7õšÚ¼º3|»ZâUÙ§šÁ”âœ'~i 檧<<¸Rñý ÛõìL×:éâêoe~kó¼%§\à)¢ˆÜlš¾0rªr.1¦bY’p‚Û”…ç¹ÔBö1Œˆ:èÇLæZêûgÕwúNÌ¥H*߯­uûbDž!sµ©V³k˜¥1Dˆñš"²"`*ÉxÒ?rÀ<7æÛŽ2ão4dÛ­8Û€ª&Û€H@`I‰ "ªy¢¢*kÖ™%Œ9uóã32 èÏÙKbôyQ%4lHŒûFвû&m:Ù¢‰’**¦¥œþ—Ô®âм¶á¬ßNRÒä÷.“ݼxq}O–çF¸]©¯,t÷–õE8Ç y¦¬[X‘²µ€=–$òmÈ”y¦Øq~•²E9õ­ýFrµ´cÛø[Î"ZÍ„ä/b„¬z*/ éSv2¯bÈ嘈ˆ‰C7(ˆâ#ye.JÞ[ŠS^¡ ¿*(·8„F¬c*Çœ©÷‚²7EDUdÚ$N x–ê³d¥ôó¿Û“µN¶úUÑ_=+’ÿqì:èÛ’¯)!ᨙ$÷ˆBÎ,æQÆLEŒc2ÝGh…è’üœ[«óÚØÿÜO²ÑÙWD—äâÝ_žÖÇþâxÝ–ŽÎ8:ôüø]@~ËþYt/Ê‘ÿsOíÓÆDm{Z:gì>§üš‚þËkœ—8Õ3öSþMAeµÎKÍmOæ]¶ß€8wõv»XSŸ’9ûrþréŒc3íxiŒciŒci™úQß.¿9¾ŒßcÎ>æ¿Y/¥òëó›èÍö<ãî4ÔãÆšcÆšõÿ¸S´ú‰rÇTðëJOëú¾ÍÜþ½ìÎí)[%àÿ[\v”Ï»²u:¥ÚÀÛÖkô™VqžçÖ$üé‡íÝz“%\È4±÷À©ê›óùÀ­>E}ÕsÀ‹Ë¯ÁŸ¤ÏØóY¯Ö4ÖeúÿÑ_ç/ö&¿åO vÿ,ú¿Q\a,õýc}Ý—¹9S¸5R×â휤×Ý(ÊÐæ"RDd,ñž¯ÆAÓQxõÌJFXó«„ò‘¼o«Ã¢§„Ö-¯ÝÉ;öóF×-rJ`øü/ÐŒ7aîÛ¿ÆÊáå¦tiT0¶¦e‚Šx·9]¤Ÿœ¬D‰]\Ój¿£J1úÓ×>ž”„ízˆ«¦C_Ûº><ˆÌ²²»º}äO»tËÝÈÇoËâ¬c-5\Zg ØÜVÚÜÝ ]x¥¤f©UÝ»*Óµ*ÔÖÅ“ž†¥0!c\ÌN«`’¬V®ÍZ¹.’fŽ­Ê¨£¥¢¢)7Qg(s<ž—ñÅ}ËÊ{“Ä0õ‹Ì‰ÿ³2µ·‘âîISÿ×ÛòæºóV»l7hª¡±…äÊÒ¢ñÃÇO1¶WŸ©Ó_ï=nNªÆë6Z±ÁBjVéài U9BŠÖMXü¡ããÌvÝO?/§ËVàTõMùüàÖŸ"¾ê¸ø=S~8õ§È¯º®i÷Œ£íu‰¬Á>OTߟÎ}iò+î«SÕ7çó€Z|ŠûªæŸxÆšÌàTõMùüàÖŸ"¾ê¹ÎéÞŠßS®Dì]íiÚ<=¾PéºòÏ?v¦ë‡»f/’ðµÈõg޵RÍÇŠŒ¤Ôx0Qd™½±FÃÈ7d¢ï—nÍΘñ.‚Y³”RpÙÂJ áºé‘dAb j¢²J“U%S1ˆ¢g)ˆrJ`ˆ†|‹úh¹ -$Ôæ5œ'âp„­€¨Óà‹Âx‘Ý@}¥_sŠü5±6“rov{sp]ÐÆÍRçɪ²­x„Ós›ƒ$ mT“RHvðU\áDU8sûídÙŒ÷/Qþ--ÃÞcn=4Ùš­©ÈO߬U8Rs­®"yª²(,§c:4+¸©H:ìR«1^’” žÊ̳®•Qc:®hxrë¥È…$<ø£:M9Úª‰Ü @ªÇ*„žJšî3Ìh· Ås¼bRMdzz£&¥“óP­º€Å„Et‰|Y!!•%6> Œc=eêÝÓ¯QÚ:ÊÙM1²Çq ªžƒyèñ± yƒíI3½E&ÎNQk¸L{”毲è,Ùe›¸ID7UDER‰EdŽ)ª’„0ˆ¢g)ˆr˜J` ²ÞB25¿¼=Á#*É¿•|HÖ†"BøRNMUE+ @À¨Iw“9JTÑ–l@“Ý8å>ë\FKœ78Ú°I|’\p§² ñ7â‹/¢"" ÂuUy/:,ôÑì7µql¨ŠX]Ó1Y·Ùí7É–=q!ùØŒ‚DDõWÎÙU„¤n¿“×´(€Òªy?ÆK­sµ­½_“‹u~{[û‰ãvZ;*ãè’üœ[«óÚØÿÜO²ÑÙÇ^ŸŸ ¨ÃÙÐ k.…ùR?îiýºcȯkRLý‡Ôÿ“P_Ùms’ç¦~Ãêɨ/ì¶¹És¹­©ü˶Ûðþ®×k sòG?n_Î]1Œf}¯ 1ŒcM1ŒcM3 _J;å×ç7Ñ›ìyÇÜ×ë2ô£¾]~s}¾Çœ}Æš€cÓLcÓSýè¸üºüúLý9šýf@¾‹Ë¯ÁŸ¤ÏØóY¯Ö4Ô1u‘:T½ú›Û=ÊDáߨ'‹Š„l˜öþ"Ì*ÿ'qýÜLšn±÷8v}¯ZÉ7VÏs¹MÔÂ.˜AZßÁBÖ$V/nÀ„«úmµ³qïÜOã¸v…œ·~”Ŵؼ=ÆÔKÆ““‘*+ewQMQW‚á’/ $ *ˆ¨ºæËÒ&’Ûêãr£Liö5vÞ« ¾Ù´^©;n1KX®€š «™°L8‰ØóOƒ­©‰+ÆHBM2NºL5+ŽPÊ,`î,u=±Ñ=ûó•B?Åøÿ·åÈÅÉNé@?%îÝÒÒvc—¿îß]'ìþ? †þnù¨÷äÕ½œÜBEãœn`~ó¤ÛjŸ¾†©ûú’=²/õ9²€HŠƒœÖ=Âþš8>ø¯ì¡6ŠŸZ&¬uŒc)o]KéŒciŒcj´>’ýûéMuË*Üo›?¦%S¡ì'%ÝeõµÚD…¯Èµ X N97#ÉqUUWŽš=õòÛfrMº›âßm¢ØãÀóœ½#ÊäɘÛM!ª¸òQä‰jËçχÅ$@^¶Æ1‘ãW!¦y7™:ßßÞ .É¿›9BTövBBø•R)4ü«P —¹À‡)CÄ¢±-È#ž²Ï‰tt‚Íœ¤šíÜ$¢  ©@é,ŠÄ2j¤¡ S¦¡ b¦D>Ö9w'½ª¼‰ÏY5‰H½¨ó`\?—Þ%‚v;œyö8\kYo6×ÒoNÕgÛW ¥^sŒÙQ‚m*é²®TÜ2Ù|Ò•KlÔ+hh\ŠJ„ʪ*"¢Ö“Ú;Ÿ_-«öe²˜r( 㤎¼2ªw0¸‚)_C«æ±U ÅtPrbˆ^"á!fêì³*ùÑ­ B²†â;|Xó"¸Ÿ£bK@óEÇ+¨ª§>KÊ/šk‡l»»Á²¬— Éa•~C‰^Ûcwžè¶´³Ÿ®žÇ*ƒÜ-ÉŽè¢"8&?4“Z!z$¿'êüö¶?÷Æì´vUÇÑ%ù8·Wçµ±ÿ¸ž7e£³ŽÎ½?>P‡²ÿ @×¹ ò¤ÜÓûtÆ1‘^Ö¤™û©ÿ& ¿²Úç%Î5Lý‡Ôÿ“P_Ùms’çs[Sù—m·àý]®ÖçäŽ~Ü¿œºcÌû^cÆšcÆšf@¾”w˯Îo£7Øó¹¯Öd éG|ºüæú3}8û58Æ1¦˜Æ1¦§ûÑqùuø3ô™ûr 5òxí¬{GOß9A“M×vñã¥SnÕ£VÉg\®©Š’( ‰ªÊ¨b¦šd1Î`)DC ßEÇå×àÏÒgìyÈ,Ñ“®w+¿Cg-UH/Rؼ‹p¾¡¬•|ZV$Y™ÆÌL€$SÕ›Ô…ZȹDåY”µ¾É;ø>6Cu£´¼˜©êõž”CÊ º`<1|‘É/«qÚçÉ\pS[+gvÊóy·KÚÌpKÚÙÎMYBËèÚºñd¾…go!±T"‡KVÜËi½¿9"B|…Qk™cåC®asC•›œŽœ-V—“®@ëfËy„:Öš¼ä@ÙNÂÍiXò{ä”j^å$ää¡ÀÆÆìŒ~ÈWÛtXo]¨ƒàÈcFÌG£ÿ0,™„÷|ÙsYV]‘%÷–[7 Em­Œ‰ŸÓËéiü(èŸ"ÊL~ž_KOáCÿDùþReü¼ÁÿVx§ñŠ£ûæ»ûSú¦ým[ÿþ†÷ýÜÔ³c"gôòúZ ú'ÈŸò“§—ÒÓøPÿÑ>Dÿ”˜ùyƒþ¬ñOãG÷Í>Ôþ©¿[Vÿÿ¡½Åÿw5,ØÈ™ý<¾–ŸÂ‡þ‰ò'ü¤Çéåô´þ?ôO‘?å&>^`ÿ«ÕÆ$ãÒî—Ž¿þ„\‘ÕÎ'cŽW¸½^JIexäZh¢‡’(¦¹„ôÃl7Èô¥Þ:h^;¼•Kíse¾Ö#gx³+í;Ñ´ðØ[Š3¤±o¹ÉÖ ^Êû¡ƒæšz$¿'êüö¶?÷Æì´vUÇÑ%ù8·Wçµ±ÿ¸ž7e£³–½?>P‡²ÿ @ÕXBü©÷4þÝ1ŒdF×µ©¦~Ãêɨ/ì¶¹ÉsS?aõ?äÔö[\ä¹ÜÖÔþeÛmø‡Wkµ…9ù#Ÿ·/ç.˜Æ3>׆˜Æ1¦˜Æ1¦™/¥òëó›èÍö<ãîkõ™úQß.¿9¾ŒßcÎ>ãM@1Œi¦1Œi©þô\~]~ ý&~Çœ‚ËuÕåwèæí–›%ëºïŽ \j:éWÆÉÕµ£ÏYÙód ä+¥mD÷¨¢ÈœRwL‰rP(¨nõHèÉÈ +õÓü‚‰d³É}qCä¹àE6þ²ƒ+mÓ‹Û—[Ñd$R˜¦Šiy¹VÔ’)»Úy©Šc‚HÞ½y$õÜŒ‹§¤$8zùëµ”péãÇJw.œ®©Œªî.¡ÖYe ePæ9Ì&0ˆÆ£²¯V!ΚImh‚¾iƒ6 0içȽ(^B¼(”6 9CÕêúvÚ™6{Ô]Ü.èxÃmî ãÍò%k<ܶÒ9* ƒõtOVÔ6è)¶ë9-›$ˆã^â4¹#7\+c›ÀYÈ™èŽâ=Š'•L¦ýõ"ÈBåPÄöˆK¶AV¯ ÎÆ£Ï@IÛD*îÔðX™ò)?îoˆÌ”\¢#ìãÜ;†N®ZW¢c+ -•ϱpNN-¸efÊw5[”QV¤QQç”°¢·1/$%"ûÅ×;?ŠýÚ'±Ž¸z{ÞV#›U{³Ó‹x»¯(/‡+'Úœó%+gEÎÛÇsÜ5‡NU±i·x}Æ2ÕuÉF™Ò‘]#°ž3#ÖH¸”"%£åNpýÏ lÎ&ý¯ˆØ#ߟ‰e%¦¹?YQfíÉb…”ƒ3‡~oª· f+°óÜ ¸\GÏóUY`!L)$¡À¤tq³Ì¶Ïq1ÃÄs*ÁrÜq°á½Ëº ÀÉTŠR"sñ]oΕ75”ꇦíä’÷«FÚ]ûÙýÌ‘#•b> ¸XîPóª£ó‘j¨ÉxåxEáPŒ°²~_9Õ!Kmq)DÔ!TMB_7ÈrÄ9 ]%1LQÀ" 9ýüŽu|ëñ3úõ¸ÈŒä‰v§qx\JÙ<•¶‘Q~…û¯—×ôyý ¯õåN¿:6TEN¡výQQ&NTT^Ô8TTTóÿRê¼ØË üŽu|ëñ3úõ¸ÈŒ|Žu|ëñ3úõ¸ÈŒþ}Š·õ%mþm¯ö¿_òý ¯ïÛ÷Ñ¿ë„Ûÿá“¿¸}Ëô.«ÍŒ°ÏÁ¨çWο?¯[‡üˆÇÁ¨çWο?¯[‡üˆÇØ«q?RVßæÚÿkõÿ/к}¿}þ¸M¿þ;û‡×ü¿Bê¼ØË üŽu|ëñ3úõ¸ÈŒ|Žu|ëñ3úõ¸ÈŒ}Š·õ%mþm¯ö¿_òý §Û÷Ñ¿ë„Ûÿá“¿¸}Ëô.«ÍŒ˜®XtAåïô}§ßm:çK¥¹„NÈÃYY¶Í–9”ì»H$&OeÖ*E3’b”’ÉJËD\•ص;D,Þsº ¹Ç%„Êé5’Ü`$¶Ä A3`ÍÆÁÑáIÚp9Eò $^5¾6Çw6Óyè$å;Y™Ògü;GéeÚQÈ'ãÆ¶$Çà>Ž6Ó­In$øR ±å™,¸*¢hºcÏ­¦D':µ¿½­‹|`ßÁ|eÙñ“/dÒ²C&‹W~ (xõøãG:'‹±Ü:NIoÆ1T—¼è.LëÂv ²Ä5oçÎC¤jéJ_¦•†MeLÕîed㔑{|׉œÃØ™²6£)ù'›ULuϾyû&ÍUx‡8Àã_rICY¯ ½Œ§ßjz@ví‚éƒ>Ækázæ]‹FMÀÁÄñ$žE‹1&CµÑE¸ä_Ñ=sÇáY´Žé¯ ¦¬¿è’üœ[«óÚØÿÜO²ÑÙWD—äâÝ_žÖÇþâxÝ–ŽÎ|:ôüø]@~Ëþ\„Bü©÷4þÝ1ŒdF×µ©¦~Ãêɨ/ì¶¹ÉsS?aõ?äÔö[\ä¹ÜÖÔþeÛmø‡Wkµ…9ù#Ÿ·/ç.˜Æ3>׆˜Æ1¦˜Æ1¦™/¥òëó›èÍö<ãîkõ™úQß.¿9¾ŒßcÎ>ãM@1Œi¦1Ÿ½V®I[ìUht¼Ù;«–EW|àˆUD| œ*?Š’ ¨¡Ä AüÞy¨ì»!÷¦XlÞyÓTm¦…MÇ —ÉT‰WÉuîÖ×N¸±SWùöv“b×WAŠÙ;&l鯷$Hí$ãò$:Û-6(¤nŠy®¤ç×ܺÄþÏoáyf\ð9{°QnÒnR7åNFe2¶8´Šl„ç¨V#iux œA<µè–Q-DJRR3@‰ÊÀ_`¸t¡NåÉý¢¢ê¨ ˆ‰„G‘åjæÙ#¹nSq|â’72Q$6Ë”V`0ˆÄ”}ÂC¶ÕÞÞž'ŽMuÛwKÛ)§­‡Û¨ˆ,,Ür‡2Ilv¨YeÖ¤v¹Dôu¹Ö]º™1¸*á³ZÔ(¨jÜpD÷OM¾,­Ì>céÝ8钮醜-Ëh(BŸÊo­©¦Nf̃…‰ÜÍFÂTšT:ð˜¨ËØã„ňäÏr{\¥äÚ $ßÕXB\åU…nðUÉ£–z´@ì‚N8;”¢ "PˆzïÑÀâ—¼=°y_eò¬[¶UJMw vY¶³¤Hª”ËæjåFÕ{EÛWˆ˜S’‰éWýÁÕçQ{“v×Ûª5¯…º-ZU•TÉØ…Ÿ¯ßAºp~ߌæR ˦I`oY(xJ>ÓÛÇ£ ÐÁ-ä@°_ÝÖ«)†½Ÿw¥ñ&ãÍš¯¢u¥vû¢'m[lW•A^)?1˜¹ÔFBÂcn$ú.‘.Se|fÎ^Zq`n„Æy0z>HÎG`‰Ã`Îô‚NϺ¤6ã˶×úcÆš¸oÉìÞ7i«€/ë]ÑaâåVxŒ¤ía#V'Îoh‰Dó0ïŽ0‰€¦(›õÃèlˆÛD“ZÇ`jW®_Ò,ˆÚ"PÿŒjýµ%Ê ÉßÚ”|äK·.Lgýyr_ò’7ƒ<7ssJmZb5ì¹0S„ö]™%g î.Ø2ãyw‰'¨¢Zôϸ î~ÃmndŒ‰sñ*Ø…Êû~„†û¹TƒºÚ¶c€'ÉxN6\’1Œf¶ÖóÓÆ4ÓÆ4×ÚšÞ­¸µ¥ûT]Ù…GcÓì4«NÄóˆ²Eº‰zvÇ9N=AFpÅÑKæ4x’Qª‘ ~o}?iãöæÙÚNè§fÖYúlš ‘ÑEøÃ?Y³I†D8‰†6qZÌE«â0/ùªÅ1Š ê”Ôô’8­ïCok.[Vã|¸M·ž¸Ø®K²Il*dx«U“z¯`ñ;³QQM (ÄDDQd™Ä{˜rÁù ½ÿÕbþqÖïîËPdÕeõac*Û¨½Ø°š~,§òeGÝ_¾tÙ¯‚ÇŠ|óËŽ£Hã‹äŠá¢"*"q“Õ¾E·ýLon#ŒÅH45;ƒzµpµÁô´DP!Á)¥jŠMÄe3pÄœ&1Œ:Žº:gì>§üš‚þËkœ—8Õ3öSþMAeµÎKÍmOæ]¶ß€8wõv»XSŸ’9ûrþréŒc3íxiŒciŒci™úQß.¿9¾ŒßcÎ>æ¿Y/¥òëó›èÍö<ãî4ÔãÆšg¿ø­}Ù·ÎlÉþ&懃9ËÜŠXeÛ‰^,‘„ñ°§Q%H=„i²…äðiS‘$ˆuPåM4Ó(œêâ!B€˜Ç1„ R”L""9?Ú\“VjÊ­Lé9Dل剕°Jvw$S¾ÅA‘ÎHÆêö;F-û€i-øÊ½†¹WÎÉù+…\ßipa^Ú –n§Ò&Ñ5Óô³USÌyKBôOlÙo©H¹Ý¼/XĶF”£u¾øÏæ2v& —…í~<ægäñÉÆ ÉÔî í '©m[çnëm1Hoë­s¯Ó!¼ID¹‘A‘äÞùŒœlCu””p"R6Žfé†"iÁÕùfoFïŠ^ý7&Èåµ–7ÌÓѪëÝtát»¤¾ÇºFˆÙ¤™+ØCÖjôGé# {§°Z,˜˜È‚á8Û¹nQOBÚ‡6X,ÇÉY€Â+óE÷ fÜðùTBy[^M5ÑßSûÕ§½ˆÜØ–LÌjñÇb?Â…Ž[jaU‹À&ùïu—îæB)¨Ø™³\ÜÉ**Û©n­;«*º?Uk½=Gkêu-iM¯ÒàR1H ¨Â¿Þ97 @(-# dþMÑ€Tw åË¥LuV9‡«y¤K¿ô â„Õ«cI¡l´³›Â%¶¾U^E·!Íø©ÂBÆòZK¸T¡â)D=5Œ´ nÒV'kIqH£^?2ÚÄá¦\®u·#4­Š¢ F×=¦Ê“eóIS\6n<]ϦÌi3C~æ6y òJü—<‘n¥x[No·xV^]o8f: x[EÚ…DÕ¶ÂJšB«ç$žb@*I‹9c4lS„cƒ:rñ𬲷8Åhòº“B…u™bHeG ÜÈ/*"'`? þ“íäUy,Ý=º½ÚmÂË6ï#h‚Ó¸“\o+jÛsáòÖ[FUT‰oZôK(œª—«ÊmƒBDcÌ£X½™ÀÌW’´‰Gî­f䡵í¨êŠq¶w S|¹Ì>Qб7…’t¹€DŒZ» AC-y”wà=„= !ñ€þîZÿ‚¼€O ·+$üÞiÉ£M¾EI+XBU0¹_Ežó²!—ì]Ä´×s|,^qÅå¨ðòš¨êjŸ=±jºâ4V»”o%¨Š4鯲qŒeêäôÆ14Æ14ÏuâêÀáÞæÒíÙ¤æÜò¼¥§Z* k²iâ3µDÑ\þÆ„›vÕJ´‹8iù ˜MØ}¿Œô¬«âÛWN«šß‹Â$ˆRCË’fKDËˆŠ¨½¤‚j¢\r$ˆI扬£ Ì/6û1ų¬fRÂÈpì‚£&¥•ó”Y³¥žÅ„5pD…\džŽû*¨/2N4|’.M‹ »UÖjé[9lªˆ8nºgEtDæMTVIB•D•IB˜Š&r”ä9LS ñd¼õ¶â·èeçEòB7ÔµîùLÛ¢š(¥àdÕý•ë¤ïÐi€Vé)wo1 „z!càg Ê™Ò1â+.þšV=uiI1?Âk&¿ÂáEFQ·ÁÏÃ×cí/Å·~:î3i7"x6ËÝ pÑióœf«!Œ×ˆŽ¹ÉÑ€æÕÈ1D™Q=$ÕÍD@™ðý˜Æ3äkbjØÝÿÕbþqÖïîËPdÕd*ô#ÿT]‹ùÇ[¿»-A“Uœûõ=ù¿nŸáDèѵǟ]¿ž÷%ÿAƒ¦1ŒÐú‰z:gì>§üš‚þËkœ—8Õ3öSþMAeµÎKÍmOæ]¶ß€8wõv»XSŸ’9ûrþréŸY'¬×ré’.Ú¬ñˆ /Z$á%³D2EÒ8ªÜ¦CT„ˆC?J"É^AU8Û«fvŒé;)MQ®yà‹»EÂJ„Z#í:mðì»Òþ£ÝÊå"Î=]³Šßh­‡É=µÊ¨»¾¼KÆí“fóg'[ø–‡B‚dÆnaZ9íF¹ ÔÆoñÛ Î9›6 •Ùö–»a±;Œå9sÖðñz ~—"ÚÛ0ñA° LÆÆ—Ä_ ¸Sl[jR²ûŒ´ÌY&2Eˆƒ¿½]âû!íöÛÅÇ,÷1Ìí`±?Ç%F jZ›7–d†Ù*ÄÛ«k'nª•éTVeH“>gåÚÿÆhK½1ŒcM3 /J(åS®·9ŒQî~4ù“áÿÓ81ˆ!šþæ<“šrÝpùàé1*V]1"ßõH^6i¨eCþ «€‡äü˜ÓPMŒckÔü@Ö¿„¿ùë:ŽRZ¤Ääñ"«Ö«°,Œ"A:Ò¾SÃ$r‰gõ1nMÎyK‡º¼uæ¥c&ý¿“a¾4ŸŒžŽU z<ýÀ F9A~dÔ((ƒ¹Wˆ›õÛÕ¹_ÛË•¦SšÎõw|JÚTZx +Ëf±Œ½v@ñóKǘ¯ 8<ø‘ÚŽ¼ª"q×Ç£W§òØn˜qe·ƒê™®çî>XŽ·Û*(ÝE`qšg»Ñijñ¦«ŽL'PVÌëìqÅ/³g/\·fͺÎÝ»]&ÍZ¶Hë¸råu ’ ÐE"™E–YS•4’L¦:‡1HB‰„t¶éÕÅÆÜ=áö›Ò«4A k ñ,û-t€†;Í•oÎ[J£„ý…xä•xÇB21€%/ƒÂ0ébÊeêcÂí¡$˜Ü6ü«Øèã1"ÓZÛZ^öÄî‘P‡#˜ägèѽnrŠN í&ªw+ ´ªÍ¿Ó†-áE¶Ëä·÷ID´õŠIæ‘Ù&Þ°|9å^}#ÇNJ,€^Pµ\¾š]ø)÷û}Ó­,ÅX”,ãg ²ç w6MJ¬ÄkAQPvº¤­íždÐÛu«ê—ÓµÆtÆ1’‹TC¯:ò£AÅò?LÙµÓ±lÚlÄ,Ý*]ÉGà oŒMcE99ÊE›7¥UÄ<©“ME*Iè¢ApTDµ²W'* ª­–5Ì=‚»&öf-áŽXIG¸;gm•L’ɘ tÌt”/…DŽtÌS Ûrú ñk]$ǹ[?𠌀{qc$‹¶fãÜÈ÷È{Ôn0‘-ª²¸íðÕ«K[`Bœ'¯B莚üND.YOpWû‘W•èóл¾g·ùîÀÜLW'`6 šáí:ç..+“IðoàÆož-6OáX:Jˆ¥#/TE$@æøÆ24êï5ln„ꋱ8ëw÷e¨2j²zÿª.Åüã­ßÝ– ÉªÎ}úžüß·Oð¢GôhÚãÏ®ßÏ{¿¿‡’ÿ ÁÓÆh}D½H3öSþMAeµÏ†ñw«ëz”õâé.Ö ±ZVJ^MÙ„Aû‰¤™@ʹvés¤Ñ‹&äQÓ章ѪJ¸Y4ÍôáfbkºâzzE”<,52.JVVEÂMGG³…n»§ŽÜ¬b$ƒvèê*ª†”¥ÊÙó¯šR\”´Nž³ÈÝ5U~¡áZ( ¶sp•GÌCß\ËcxNš ™”N¿à¾kk*éÑS|³f}ÿtƒ³×;¹‰íœèXåv „½‘Ý |È‘‹®Q‡·m& “qZT4h{å¼Ã' z¨êcé» “o0¢Úf×C*6ŠªŽÙÏ쬢G˜ ª'zÆB+dù+UÑ]r›6ús–|›²òg;µÈzÌmB\ÅÐ*Ê*œª.]3™ÏM$žÍº!”î¡[Ç¢²Œ#X&w¦_©uú›Žå(l ÜaÚ:KÀê³BTé=bÜÅ0ÐhU6³2)˜DÉ1oÔäné)=<8†¦û¾aÝ£ŒmI¯ä›ªí)«Ý--ü§Œ«)Ëå¯È „…˜§h£8‘'þ·;†–f€ PìÀ`{2dõ;¸Ôø­ ~Å`(ÌêèÑ[ÉR|ØÑ™ì‘ ‰]åMÙ2\Q³ºx‰^tɆßu×$Ïm!A!“îasÕÎñ”›k»ÉÖà«hu>O‰Ï.Hê"Üx0c¡ÐâÑÛ–ôHì1òÿqŒdÕ¼iŒci˜¸u߸ñÖ¨|Òkr²ä­Öž'!€ÀSëÐeAQö›©Z2/ÆC¤b±Š!›D¹rÝ›uÝ»]­Z¢«—.\(DPnÝU—]U©¤ŠI”Ê(¡ÌR…1Œ PÌ!yO¶}òw‘»Ì ¡ƒto½¶Ëx¼ãÅØ6€_üÁ ŽçñÅ߸÷ i®‡Îùã~ª>ÛÚPŽ›™ZäQ‚~Ô§„|¡‡U3zÍì4»³6ŒR¢åÔÀA±ût0ˆ€ˆ`öˆˆü@ùDroø¡¦Í©µÂ.%Ú‚7 ˆ¶š°„¯‡”or Ž>ã›,¢Îˆ!Ý9'Ï’ñ4’6jíÛÍG Äå9ä ›arº D¸q·\$Î<Ñ 0JèŸ )(â¶^Nêwz<ºc©n ¨¡Û×”¶Ûç!æ{ˆó­)•žú\YÒTFÍܮфônðx¨ãßKaTàªkÓå)HR…)BHB€¥)@¥)@ R€lþ±Œ¯¿]„¢"""""""""pˆ‰äˆˆžHˆžH‰î×ÉÓ§p^ÎP„sᎧíØÚš…)ÿÝý«Tž€uØCñDTg=ÌIí1UMdÄ@ÆYLÃÛŠá-qÔ³|’z ã(œÐÓ»>EÊŠ‚i%Y¬îZìÛÖË*#á+ ¯2U’Âcx}SÄ7n㛄å˜át¡Žâxý0 B¬Œ2„)އ¬Ns‡‰1×Üá|Ó»…U_=pùÔÖçIÞN 7wr¤>RÉó›·ªHÉMZÇ+ä­F/¹Wç$,r¾®"p$ŒwŠ* 1ŒfO­¦ ¤’é(ŠÉ¦²+&t•IRD•IB‰MDÎC¦r”ä0 LQ˜C?¼gõQQQU”TòTT÷*/ÁS_ÅDTTTEEEEENQQ|•ÉQSÉQ}ú­gP>8Ñ;K\G*¶³Hˆ»bÙ3)ø;|¨‰bW€‰+2 œK^znÄf©‚ኸE¯+Yw)è[D,¥rÇÆnm‹˜Éx™&é»a"ÁÚfEËWMÕ)“US0”Å0{=†(€+SÍÞظó&þý@lúÃ¥dxʸy¯%5úî•J"ÂnÆUxc,r·‡±LSˆ£0t¤ŒÕ̽—tÛÔSDh8s3^nVÙc§¹E’F^öÔÞ«µŽ€64óP}f¤$Š €ÊŠêyMˆ‹¡ÞÛ.µº¶+~³îŸs&rìpøo£Q² zi:t™-`Ÿ©YÆlÁQÆ”Ü8 (M¯xÌã¹á=%‰‡)Šr”ä1NC S@Å1LÊb˜;€”@@@@D¸{3ýÊÉqC¨®ÈÐiÆRï »Ù¥¯–Ù¼k‡%÷×Sf¬K:?ÓɈŠUÉu=H šM£Á$*™Kéž@êMûîGÏ)&yHS˜XÙ Ž§bùSP.¼¹Aæø‘Iß”¤sÃæzí FªwObóª–ë–pŽÓW{a嬸åk€EÃAIõ”/[iWàŽ*ýK:½[‘'mºÇÚù5_šZJÛ{†P”FdlÒ)ÖÕ°jžñg(J è*Š„ä .QRe1Œe{k°í[¡ qb‡ë¹oþ0üêïÿ0þlšœ†Î†¬LÓ‡6W”Þ÷WÄøŒTêî4D¿º8ã—ÿ˜¦É“'œúu4๿»¨B¨¨™\Æ×Ó4Ó ~Ê*/Öšã»®wAî®÷øÁQDwÉ¥Tý:k×±v]Ûmpá-#UMú!O¯¼Æòæ½Zò’Y&/äe¬[¶šêH+uît¼ä]´g[GmMÓQ™ãÆ‹+JbF÷µ¡ä+Ò3±$!¼Â^R׳l*Pd›šëõŽgR¬ZºÆÛ%G©çS~Rõ‡ä¹ö¾ÈŒ|Ö½^Aô‘ÐôŸu§k:¢˜íáQ£ÓnUç­³§E“»ÍåÓol¯Ú2E&pÕ¸zår¨ôﮬ¦6 ûÓ®¦t×R /e•L¢ꙹÕkÝ`ö*ª«-&BÑ-”9\£Œä™†9‰Å9W¶±a¨‚›q{…Ø*Œ´È\"t»Šô¡´u»Hë6ÙçFë=Ëì—'ymçZåÖ)«¯¢€Eþ ²’òŒë Æ3Ô¤Õpmõ÷µ;U޳"™Ò}7'ä‡î"*1v«0 ?éT¤RT;•TÎE"S€ŽÌ}ú‡Ôz‘ôöÒ;}­›ý½J«ÂjžE׌õç 6ý*%¤LÌÄ›2›Îk±›5m±+JÍHÑ2G™ÒÒqR©7ËÛ”üZ[g*{õ6éÞl’2ðꨋVö–ÍSòÛ¬“•< 7œl‰H؇t¡=h’(³e&eúç·Q.aô†äb[kL.þY"²ˆÚÚvôÊM*ਰtªÄ„µDøš«ë åÛš­Âd&ëO9Z-ú‘’s1’Ö3·ùí>mMDiL³qÛ J²p\i@˜2ªŽ;ÃåÈòU²@5ÖƒŒ®úIÜ~—·+!¨º ´‘·“-æHÀó¶!¾õÕ™.9[E‹m”X9 (äÜKjy.5-©MˆíÈ­“l²ñÓCÒ7éáÔ^6±SW`Gñ£’RɶhÿAn©¶0ªÉΫáHYk Ží(ºvÌAãŸa㙯 |v‰ »Ú$a<>)÷ÌÿQLcÓLúØ0•bò2Q“I(Ù«²~ÙŒ_2t‘‘rÑãGQ-œ"s¤º &t•HæMB¦·Œò !0"BQ!!^D„‘QD…Q9Eç^6´è¸Û¸(`à(˜(ª‰ "¡"ª**. ‹˜=0ßÇ-)±¸ÔÁI³ù例@*e$£ÍíUeè«.cI˜‡Œþö\ª2MÌQJi®Ú)œ.;h郧,_6pÉë5Öjñ›´Tl飦ê%Û9n±H²  ©’ȪB(’…1R˜¢x ñß$¸E¥¹&ƒ™)¸±©lGÀÓaV[ „²‡L€D°±-•™©I!åɤÝ0o/CFnlçW蚉Žîhʹ«ib.QUû˜M#lª³ o3T³'BÍÓʤêwÑ»Q—H±Í¶뱋ùì» ¿˜C´|¹qÃÇ% ¨c’Þ>ô×Åh\qÆÅ‡h£´~-NñžÚäwþ…Qäšµõ6 QB])-]I Õ©;›Í°B‡˜¯™4À¦rátÃ$sH̹0x—,Êq̾µ»|bê¾î¹ÔN$À#f©ÝàÉi‰ SòHÒ›fCkäãb¨©ªfÎ6û7Û[Çñ¼÷¹Å.˜RUƒq تûb]¾³ õE‹c ×ò)Г äà™|ÅQtÏÜ­Ùìté–v*œôÅfz9O5ŒÌ “È™6‡ø„Íß1Y)xƒñNP rˆàbˆ€þ3í¼ËRq‡ÚmæiÖ^q§[1Q6ÜlЀÀÅTH HUQQQuŠF“"†%ÄôYQmøÒc:ã#¾Ñ!´ó4BãN¶b†Û˜! ¢¢.¥{KubÜÄÙÃíºôVÕ‡@‰¦Û:½Ñ4ƒ±<ÅÝ4l¼ ¹‘L $Mhx÷ŽŽ3¹s( ¬Y@Ö]G8«²J‚ Þ”×’Ë{Åì†#]M1žÀ’Ò2¦ö­<І/cùE†¬8Èçšt­´ÙkK‹Y+±uHÊF2óqbм¢¹S%©5 Ú/=Á<4UåÄ^&ö×zBºŒÛvcWX_@ÜzXè „<ò+Ö6M²(‚HÎG L ×^QD@vÚe³mªrŒ*) ݶÍ[µ°$¥ZÁdŒS·‚FY„Ãøƒ¹|ãœ9no{C²ƒÜ=¡ìÏÛÊEDÍÌÀ;+ø)y8WÄöìK÷qÎÊ=À 嚨¬^Â>Ç·ÛñçwFr¿“0ìUŽa¾vºlÕn«_-kÄûÓ ŠÉ%Þ½páÈC ³[¬Õ@*Í”IR”ámú¸l̨3ÚÙMªªƒvôò 'ÀMèR¬…ÅO‰¤v‘}èÚ{µ3q¯K2óMŽa³÷µïˆ¢:þ5’×Ü4é~ˆÛ‹i_Dl ùª4S$(û•â生LOŸuÎDsCXð÷\O5›§ðÒ½c.ÃyäUb®úÙªÃ/h®,¢&;GËkÚj¥²¤PëÃZ'nõ×I¶}õ3V³„O%wÌ4š œÍ«–)gʇp"i;Šq_D¦ˆÆQÌÊ^ý¦0ê R0–ؘ<¶KJÏX´Z2³s’e¦eÜìM´R’’.Ty!$ýêWÄÜ:|ùÚË:vée»‡ ¨²§:‡1‡˜ëŽŸÜBÔ…‘.¿ÓŒ FXÍ"ª–Ý2áÐ4ó}Y5ÏÛ%ŠÊ˜­È©ñ¨sŠbs ³Xæ= ïÖ5yMQ’í¸Jµ¯‘\ÛÖ™;,6ÔÐõy.GÄå9Ü1œyZBuH€TŒeoN˜Î“öË|6·rwê^9€fùœºüOÛ« ‰“ñ™sI([î…$eëÈuá9çe6lÀY.²Óï‹l[gÛdÁô“‚4Ždíû¥²m™6YÓƒpÄE(¡‡¸€~)GÚ 5±Ú+Nاk®j§1{xEübR½„= =¥=p<@>Ð7oaîýªõ<ÚÇI×µÖMÜ¿P¦X±Ìać!\ÊIª)¤Ù‹2 ¢†‡2m›‘WK ‚‘±ÿE¬¬'¼Í÷Ÿ¨,±:™ÙS¢têÊzJ¸ç.Æ|ÛKéø¼X1£ÆiÇ”óN‹h(¾ ª¨ rÙ¿âÁv²Òd\w§n†wwq2«¹lUãð÷q±|¤ÙMqˆW‡Qî„« Ó!16>ª¡ë±Óî©#&µü¶½àö°k?öjÉ-yµ¿Œ‘l³7͉!o–c.[.DÖDî¡bã^Чœ'⛹BH³T«liÕzõR7¸°®BÆÂµ9€ ¢©G4I¨.¨pœ BºÆî"eT9„DDDyp½»ÙF_ºÛ•”ãïM‘dÞYo½bб`åëÙÒ) s ˆ ËöYÄI- ¶ò""&´¦o¸6ëæ6ææp+jrÌþîÇ.Èêiž~M=EÍü—,¬*jdJ"’ý]d™@¯zI‡aÇdÞ"p‰UŒc5Þ±mzçAERbéN}è2…hýäü£Û¸Æ¹ä·<2oœ-b:gQÐJ/î%Ûd_ª¥ê$Z¦œqÙ€÷~VÇ–W¾RðÓhGsOŒÌUØe #« ÑnÅÚðöªÕmg® 6fírö.n 9ûèwÖØ„•{Á¤+™ɪ±,I!¼@êóÂþ^ÇE0‰Ø±º£h¼Mº/5>ב«ÏMP™¥VmâèÖï (¹VõÀH)8£BÌ~ êz±zùèw©\3y6l#üpÌq|;²86Ee9sŠÓ¢qÑuóBS°j Vâ8©!"Él™£ >[NË¥¬â>×ÒnîÙÔ&{¶2cdr0¸ *ÃmrfTŠòƒ8Ç+ÅéÔ,Œµr¦üØ*[šiÕ³Êlió®(8À€€€‡pö€€üBù@q“QÓY‚|5n©¿0|ú¬äWÞ«8ŇÓ7êÅ4Ùt#uÏ êJªA"okÚ‡k¹rÔÃñ(-{òÌÌÇÈZ8O÷S±/À©éeóùÏÿ­>:ýÕqð*zY|þsÿëOŽ¿u\iª_ò#Ò3ëÉHù+W3îÚú±"Š•¯èˆZžð7X<. Vº„ƒØpAÖIýÁ቎„QRëjíª¬ÕvÆ·ÖMu¼]*ÕËe¢Çs˜œ´¾šžž†e)) ü“RO¸YÓ×+*¡Õnu'ýPçï—Iø=,¾9ÿõ§Ç_º®zÑSámv&¿Ë޳ˆ‚ŒaÓß_œz¬lcT™1më¸f»¥ü†¨$—œåe—WÃãYU1Ž:Çsñ|»*­¯‡‰Þ7Hë2žrÀœ²²®QÍ”ÙS­$ÞAs’VÝm<‰KËS—¡}÷éã`ó\Ã#ê keîtøx“ð¼+3~†æ=—¬Ê±mŒÖêš5kŽÄA`&׺ìµóh„U%¦TD%}¡X@CÅA±'o (ˆö‘­ Ø;…³$PD;°;;³?W.gð[8}ü0yÿýeáßÜ»³‡ßßÿÖ^ý˲<¹Óžx铎Übî¸j¤n9arfd¾ò#*5"UøªªªêãaúgzN¯ŠÄ(o¾ÐaElY¶±¢Çh|…¦#³º@ÓMŠ}è6)ðDÕ31—3ø-œ>þ<ÿþ²ðïî]‚ÙÃïáƒÏÿë/þåÙáö·çã\Søu¿þ…¯gñêzYý@ïÿñ[n¿÷WTÌÆ\Ïà¶pûø`óÿúËÿ¹v> g¿†?ÿ¬¼;û—cíoÎ?Ƹ§ðëý OÇ©égõ¿ÿÅmºÿÝ]S3?:N"&m©™LÅÇK²?ëÚI²lý©û‡añ7t’©¸‡´ƒìÙtO‚ÙÃïáƒÏÿë/þ娸-œ>þ<ÿþ²ðïî]žAÓ–vÙ‹Üb͘*X\‰‰'¸„†U> ŠŠšü$úh:P›è“6ç~%ÅÙ5"4œCmŸŽûFœo2îé›n¶Iä@bBI䨺£§4l¹Ìwzº¢™Ž=Ì1±…†Ê Xs0)D~1€´}¹!œdæß/8yÞ¿ÇÎHnUŠ$oI°Þfö•Ëà*04²êóMLKÛÆHh6%PJC(1%´‡Álá÷ðÁçÿõ—‡rì|Î ÿYxw÷.Ì… ÞhÂ?SÉŒ«*hQ܈-Ö '±­;iéôh]¼r.ºF¶·á)8ý¦Áì÷Œ•UTÙyæDªªªªJªª«ª’S}1þ®u†MÁñc,Ù2g÷-3obî@ÅýrÎɯvʼnSöàÁ“$ƒÿ«H™Ø¿ [ªoÌ>«9÷ªÉþø=,¾9ÿõ§Ç_º®>OK/ŸÎýiñ×î«’Ó\öêGýž¨÷«7 öw#9PÓô»µ/“÷M)¥ .•Ê«Š­sTé[Ó 7­±åÕ°+/±çºtÞq¬qãšÅ"ŒRPxñôïdpt¿é ºLè+øçoÜJMÓpOî¹IM×?K±Ú›Ú¬t½E}ú‹¯õÄBUô¢5Ć­\Aº‘$‹©U–•]²ìÙ±‘üi¦1Œi¦yrðwÛ½G’6jh;ßÕ·ÑÔ-^yEÔî'vô­ReÙ„C»™èyE»¥C¶zãö¨r;ü^pÙã—VtsÁU\Ùž EBð&’Tù캆ӉȘª¢âÙv‡gõ.Qfø½YNâ©-}ý\;HÀ⊊>ÀKiÕ$~å*:µ!¢àšt RvH Ìh¹{¨¶d ¥¨ ÕFêÉÍj\‰‡deãK/$ä}Ÿ«9i_n=Ä ø@O{oj>?oûçw.Õ×ózkeë­í´;=Î)ãCÛjPºØ°’xõYÏuªVˆ)”I # «t߃G©µ‘jõ›kåepyëè¿p¨—,v·1w^Ýåý_fîx¾ù µmûKÂQþõµ;VÃ{…lÐkoY¯ÒbžIû¡g“ó¦H8kêL•mÒKcbn•(6Åã4Ydpá Ùð–ºÈ„S„—RqbsÇ‘8õl‡ QIIJ gÞŒžŸr——ŠIËvê[ŠDéíîŒ\?2#®ÈšŸcÛÝæ-E»†ËhªØ‚ŒSÆÊFL³FFE„¬{‚ø›¾vÝó5ËþÒ.š¨ª —øÈs}ìõ}oÐÛéÃNrgµSõ#ª¼0”Lî·»ôD“ Z&^3‹íU/íDOÜ?'lô={ÑãÅ]2#ÔW«8¢Ÿ`M^Hi; )½€šV0Ê&š`ìH…*aù íÜÕpÑ8"—8 ´3áʲæªüHF\J¥DUóAS%wyqÊÅëïDæZƸ¾ñc–m*ª¶7ؽˆ+æ‚g_e‰(û”űCã»Ã{R2q“%èþjØÖÉ5mÔ ¨¢‰"4„ß %œˆ „Ãæ¼”àëÇkqYu¥ì@!JPýtÖÆ/„yûÔ ?Ù?Joç1x çÉ­³QUs΀ø^ Ð8*¿H²&•~*€¼} ¬%ïENú‰‚Gζ™Ö×Þ³Ì1òNîÆÃ 'Âò‰Üë}Ȉ«Ú«Ú‘e¨4ý—uÙ_Uj¯ £ä#àœØZÀåûVffÖB29D’R:2Us:2ò­ÎB¹ˆ±Œ±NROI£Óÿn˜à ÚuºiþØèÉÙÖ8Á3ÔÛ”Rä q¤¦¼ãÁ—Ø,ùOËmÆ´½:Bœj¾àãHÕØ'#7^›4óõ7uDÿ»ÍM^${S=°=†ùI@q áé£ßG{ÃðOýò²ÿL‹ÿÊ“ë¬OIäíã±>‹×ié¶Hñê ®grj1¹9kyGq/ßpß‘4=P¤+E ].AyãRçh=ûAY‡Ecz’g>k’çaùÅ} ד‚°#6̈0¤«Í4„’ðšS%ù«äа'Véì§œUn»ž®S‰Z(Þr¤ö‰e%”ð `ö€aÜ€÷ ƒ·„}Û®5= TE*“Þ0*A!" ‹©‰S¥âð)%&·‰Ë Ž¡ÒnM›c* 4l܇g¿ÿtÿß+/ôÈ¿ðl~éÿ¾V_é‘àÙN}Píצ_¬ZÔÇwçvh²,,d³,p ,¯ÂpwŽm»ë,o£¥…? öÒDY ÜË‚ú›‘eKSWhºiéçce­¦Üíõ}]ò´l®Ibí…þB º*[[ÉsäV6ûd­Èf©`±!´Ay§Ÿ#c=sø§þùY¦EÿƒcðOýò²ÿL‹ÿÈ1øÒ}a‰°ãÌî¿ñÂý\ÈojÄý3Ÿ÷ýñÂý\ùëŸÀ?÷ÊËý2/ü€:ï•—úd_ø6?O¬/ñ6üyƒý×þ8_«—µb~™Ïû‹þ¿øá~®|Œ À}‚ñ~àäarK¤Ç¹ùýš."CMÞߪ³§v nVM!¥^¬&1œÎÓ ¬ ƒS¨áÊðe®É¿ps,úIÁ„{Ï·àŸûåeþ™þ À?÷ÊËý2/ü2Ü;џצßZ%Î+Æì{Dz³pâ2ܦ…{…‰ÑV)ÄŸ T½ZkïNï¹u°ö×{³ýž¾L›l3|“ ºPŸ•G1ØíN` LbÚÁ%r¾Þš«‰Î,¸¾"#žz jªÕnœ}Ht‘ŒãWP9XzÃ.àÆNÙ´i0)}‰”¨ólJ°‰J·WÀÊ@ðˆ†zïFÂu±§mY!µù§î:~aR¤6a¬L<¥’Í®Ù#Ýà"$e8åü³5”¥#£œž tž9EDæâŽP|„÷~éÿ¾V_é‘àØüÓÿ|¬¿Ó"ÿÁ²ZQm7¥³j3Pò=»4ŒM+™-\pyZ!$ãW3ñÚãc!ä{x^}dÝtî6qt|ï ØŒÖÆÊ$˜²òLaöæfJE-—rsVŒÒFXÖ!â“ÌLŒËN1 Eæ»LQWÿÙHaCi/html/Images/reserved_small.png0000644000175000000000000000025210665316126016735 0ustar fighterroot‰PNG  IHDRíÝâRPLTEÿÿÿÿÿÿÀÿ@€/ˆPtRNS@æØfFIDATxœc`À @„1³ˆ0f0R‰°*)9€)©@T2+@FP³ «2„áÀ‘jT1L€Xn<€þ"šC¤²IEND®B`‚HaCi/html/Images/HaCi_Logo.png0000644000175000000000000010126510515547760015524 0ustar fighterroot‰PNG  IHDR@@Í¥ª pHYs  šœtIMEÖ +M‡o IDATxÚì½y\å™îùûÎ’{-Y‹ªJ¥µ´ -€Ä&–n00xŒpÛ×î;=¸Ó㎹îeLÄLϘ¹1m{ú¶»û:Ú ŽqÓ}ûÒ ±ƒ1 v±K± „T*©Jµ¯¹žå›?NfUÖžµgV}OÄA…”•yòœ÷{Îó.ßû‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚‚Â"„¤º 9ö°X¡ì\a”½kê(((,U(TPPP8-ý(„º‚ó©×ÕõVP˜Õµ£åó‹ãý²”*¬2ŸÈçz+’T…ü׎–Ï/΄è”I*LíZŽeÃÊ®—˜ <Ñ Ÿèß&2"õ)Ö‡ŠzП}ù cË„Êp Ü3©Û«0Š’ò}åÙ³geOO===twwsþüyÞ{ï=Þÿ}Ž=J<W$8ÐuM›6±cÇ.ºè"6nÜHyy9eeeÙ?𤋮¬¼_]©é¡µµµ«§§'šµù®®.>þøcŽ9‘#GhnnVižQ^^ÎÎ;¹ð ¹ð ©­­¥´´”h4JYYõõõ ÷ÿè£ä< ¿ñoÈ`0(3O\uÌҡ뺼æškäßüÍßÈwß}W=A € Ÿ}öYùñ²¡¡AÙèeeeò;ßùŽÜ·oŸlll,.›ðÁåm·Ý&À¨/&„P78Ãï÷˯|å+ò—¿ü¥lkk;¤h§pqèÐ!yï½÷Ê 6(ÛÁQ]]-¿ÿýïËgžyfñ<äúӟʺº:uƒó<*++å_þå_ÊÖÖÖ.E-ŇƒÊ[o½5¯ýÈ×,Uq°eËù«_ýjq{6¿øÅ/†¹ cÝ쥬äßÿýß+÷v‘àƒ>ßþö·¥®ëêÁ>αgÏùÄO,-›ÿ×ýWY[[« GöÿÓ?ý“"¾EŠÆÆFùÝï~W=ìsŽmÛ¶É—^ziéÚ|ggçÝßúÖ·–4ñ !äýÑÉÎÎλM,~<ÿüóK>a …äücõ°Ïâ±Ç“åååKî©X^^._xáeKög¶$ÉoûöíòÔ©SÊæÇŠ•,_¾|ÉBmm­ŸoIÅúººº®Ww~œ>}ZnÞ¼yQ»»d'OžTä§À+¯¼"+++}lð¦›nRöžÎ;'kjj- ùÁ(cPÄ»ï¾;f­ìb9Ö¯_¯ì}*xíµ×¤iš‹ÒþñÿQƒÂ(<øàƒ‹ÒÞMÓ”ï¼ó޲ù©â¯ÿú¯1ÜqÇÊÆÅøÃEgó?ùÉO”ÍOk×®]TOÂÓ§O+cP˜UUU‹ÆæW¬X¡ì}&øçþçEc ßûÞ÷”1(LŠŸýìg‹Ææ3^œÂL°råÊE‘ýýä“O”1(ä…ŠŠŠEÑÅ¥X®wAO…»ýöÛ‹Þ o¼ñF¶nݪÚ:ϲ«óîaGwwÃbý®ßøÆ7Šþ;|ï{ßSF;xñÅ‹þixÿý÷+õ7]â;òžùÑÓ¿¡"™Àçæ÷ Bé4'žþ-´µß·X.ÅêÕ«‹îœ—/_®p®`¡ª¾‘$¨pêpž|³·¿#Ñåðä‡Þ1ÖÃ'`ÛTwuÂ/škQè6¯à<¢ªªªðÝ·®¹r§xýšÏÉ#?L‰•Æp'N~Œ¼î~ס"™ ñ‘Gmí‡Ãõ(›éý£Í+œ#TVV*V› ^~ýó„lgbÂC êÒ+ŒŽŸ>o¾¹k1\Žb!“Ü1Ú|Q`1^Øb$í[Dww=üWøìØSV º >×¥4æÔþ}ÈŽ¶¢WEwÎåå劕1(œ2OÞwö7(O§0ÉDÞ¯Œé Aˡ套àä©¢WÅhóJÎ"‘HAŸßXYàB?ç‚QÝÝ û¡$•&`;èÒe*Ís]° cSÓß(ÚK • ¬plc¢0g åªAŸÏ§nBõ×vèÔ¿=H‰e£ß›,æ—«GÖ†l›å1œ}m2$Ì`IO°¬³­ fëýrPŒ6_8òÂzC„bR­ †ÏOì:ÿêk„-‡Ùî›c¸’h*Å'¿ûtte2dúdâ2õàÁBŸ³"ÀÉȤ» ø°b¹ ððCãv}É*ü|xq,¥¨KŸëàˆa=ùdQ^žÙ²ù™‡ü&&Ô\o,üDözMEv(Œw#O!?Z³Vöš¦LkÚ°RˆIûæs8Ù Éwö\!esÑNæS6¯ Â¢ÃÓOaôtt¼ÂçÜÞl…7„„å`?¯½¦®ùl)ÅÁæT¨ %¸–fyäᇻ¾Ìe87èØÔ$bô>üpQ6K•dß–½,%jX!<ˆ<ö)ÛASJ‚Œ×p,˜Ž— 9ýúAhj*ºf©ÓRÃ# Ojô \æ"?vÐÙ]¸dËlÂ(Æ“.†,°Â÷­³óîž»ïaY"Fбq¥œSq¢K݆ÒDŠžGEöt7ˆòhãl~F—”×;p·„ DÝ Gdÿ$çOw—äòR®*±€‘åÞoH¼È&®÷B×ɼ¹í½Ëðèç(bÊ6ì•Rzï%† ‰ì‡bB—”×Çáy ¯ZX1‰Z*&)Ul¬§dêÈøÞ{òèM7³±«sŒ®Ïs…󡧸âÉß ÖÎè¾´Iy( »@ H=@·„ž$tÇ¡7 = 觉¥,RŽ‹íHÒÒ!e9خIJÒŽãºhš†iøtSÓ1uOÞ;ôÉÞ’}ÝÐÓ ½ÝÐ×½]ÐÖêýì¦À¶ÀI‚k“òþÌgŽ1ÊEbó]R^ÿ¶ËóõÈ{tûˈc—œÕÇÛyð΋&$AC-K…y!¿îŸþ”Òd‚À$MOg!Û¢¤¹ž}&¯×·K¹Ï†;ÀÎ}@Kžh‘œëà\oŒæÞ8±$iÍÄÖ ÒšŽ¥Þÿ [Ó±„+L\„·½OhHMCú4Üw>»OsA³]té¢m¿†°´)s iãs ™ÆïZø§¯‹¾Ö³ô4Ÿe ¥‰tË9h9 ­Í•h{DèZH×õþNÚC*±H©$<ÿf+œ.©§ÃW>.f^»ø\`…"DgçÉÏŒÛôÂä‰6øoNªçÔfµ®Zö‡a.±.ÀAÃÕ¼×k.IéGà¢IÏ¥R"ðvÏèáôÚÍøvº,s]téeÕ^ÚO~Jüä§pòSh<æ©E;V œD惋3†—ÞoŽÓc–`é3£0E€ óƒßüš²ö6¶=Hzó#ЄÀçº|öé –Ÿ<ó#àÞ¬+›šlx¼>këåX[Í ›#È€ aHéØüf¹ç=œìÉü³#ôÌÿëû‚aÙt_¨šPåJ¢_EØNILj·œ¡íØQº>|Ž}ñ>pÓ¥˜ùSZàz±Æñººã­]$Bu8bfy\E€ sïþvtüø‹[o¡v >ªëË|À:½¾0þR|þ >R>Øï´¤ø´¥ƒ^[`é:iÍÀQì°-<³5ÃSwÓ¡ë,ä’ž‡¼²/qåÐë¥Ì«#ÄHž²…I\×Hi&}fÃ_Ž®#°n7«nùCÌtœŽãŸÐûñøà]8ý¤ûÀê;‰#)"Æ>ýyG«”]OµC\˜å=³Ç¨"@…¹ÇÛoý(öÙç,·¬9M|8BÃÖtb†I/HŸ/HŸÏO—¿„ÞªZÚÖmå„VÁ?ÿۚ•´J‰ù–‘H®‡]†˜Ì¿MÍ4È !rH1ÏÏqN®€´ÐÇU޾€Cè¢jê/¸„èíßAtœãÌ»¯Ñûö«pâ¤ÈQ†¸Rºˆp™; zàØ9zhÆä§PaîÕ_{Û¡/þìωØéL +?§QŒâ…¬K–uyÜaÿæ"±4>_/"ÖqfÍ&:7î$¶v3}Uu´#tÂôúÄ )ÍÄÆ ºÓ$£Ip$ åAJ"›Ü˜hƒX¶Ï-­òò?}¾ ݾ eÕÄ6ì }Û.έÜÀ±ò:ΖTÐé ‘ÐÍq}:m}»¼CgsèOŽŒ æÂEàj:–ÐIè A\¦tð¯*gyÝzÊoü:òìç4¾ö©×@o'Øq¤3¤ ‘ó«ûa×sÇl:ý¥¤t] Bàá},ïë'88îR\$^rBŽ"¶ˆŒTòT  BÇ’Î@OËëø¸¢žsë¶Ó·u=k7r>RA¯/HoÆ Ž&i͘|ÜÙÎÈÈþû ß貊aïŽÌsÅP!¥ûé:Fÿº(U ºë¡éðë$_ú-|ü¤{ ÷jç RÞ}È…ƒ-ô„–a‹Ùi½¥PaîÜß3Mò£k¯ce2…Xò#C‚cH”Q(Þ-GÓIè=þ0þíÁ0-•ËéÝ~í\ÂéÚµ/«áL¤œ^_pT|HÌS%ð(šËm›ÃvuLQ}ާ …Ž¥{ÊÓkqD)»êVÖ\z5œù”Ï_~çàèëðHÐIcev¦Ì Úá¾ß>O¯!¥Í^¯ME€ s‡ß>ÑÕIȶ0Fldbwl‡óÖ·‹£i¤4ƒæp9G*WplÍÚ.¾†ž;8© 3PB¯?H¿á'n˜^NŒçÊœ-`ùH¬áo4JIMärN‘üF¾Çxnì¨Ì1ÃÎÃGfÎwúDl¡Óã 7|Ö—Qß°sïàä«ÏÁï…óêknð¬”ò±8Øe ”ÏêƒE Â܈“óçåÑoÿ•VÃuÇHrêÑp4AÌðeÔ^ ­ÁrÚVo m×Uœßt1‡ª×ðEi%º´¦"¨ñ]Âìçή2ùU4$Bº™9Ç.ºë;#³±P7Ó†@ åÈÒ mp‡ˆ›óÿ¯ìÃÑ´LÁ5HôÙwßGÀG˜¤4“~#„.ü¾(ËoùU¿=MŸ¥û·pê³×eºk„˜µ©îíRîûø—7ÎÑé/'­™³ªª*Ì Þ|û£Y“Ôý‰ŒÒ“C‹ßÑ ÝÇ™H”·–5иõ:w]Íù•ëh –шÐí ÓMlMË[éxYÞ|ÏØ¯Ë‡l4$†ëbH ¿“Æ/m Ç”¦kîØÐ¥‹&]´*X"pD†à„–ùYÇÞv»”îÃÒ¼¸¦%4O'ëÏL…Žõ÷Rh¤tÝ"BB÷ºñßQyímü$ùÚö@ô)e-ÜP!Ä™ÆýŽÃ?{µ•&"®sî·"@…BU]w÷ýÏ̲8~ÇÎ+L&ш&Ýþ0mÁRšBe´®ßFë•7pªaïUÔÓ®$¥OaGÆÄà4¼&†kc¸CÚ莅_H„ë KoßnÄЩ- ˆ}DCP€hJüàÃ;ü€™Yˆ>¼ VæÏ›·}|~ðùÀ‚hD+¡rh5áÊeTF«ðEʰ5m°pÛAÇÕ4,a` [Ó2ÝS¯KÌ_îß»™+ ÝOB÷ÓOÝ_NOóGO·pÙ²0·]´ìù“Rê¦Ñ¿®MÊC§`×ÏßëçH ú}¡aJ_ BáâìÙûN½ô2ëÓI|®ôBrr‰Öñê†‹éø½¯rfývN”,£-¡ßxY¾¹(SÉvYɉ]iÒM!+Ųd©>.¨+åš-ËÙ]%ž»6×*ofþ…ÌG2º™zš$¤,HÇàt'œ9JóÃ÷Á–T_õeV^z =¥Ëè1#$Œ ®Ð‡’T’I“JÃ^;ê ¶0ðEH¸!ºüÎ`c p…¥y™îáÉš©+?E€ ƒC‡~Ô{ì5é¡®/Hê&0Í¡r˪8¿ëZ®¾…÷êÖs¢¤Šn_hîÏMóþšì4åé©8ÕÉ~Êú:ð·å/ÿã]”EÀ7˵l…¬¨çÜ•7qîÒëxmùK+ŽÎ‚ŽEI*NU*Am¢—•çϰì½W©9ú»[©õàh.=üÙÙy·¨¬¼¿Ø.ÿt»7‹LüPÚ6Ø ï/Óðêo8þÎk°ûZ–}u/+Ö]@Ÿé#[º1ªé°ŸÄUÖò,Iš•¨Pav°ÿjz{ ;¶®‘Ð}/¯å¹—ÐuÃ_{'ʪi„±Æ(ÊÍ[ÉÔ0“¼^CRn%ÙÖÝÂ…MŸ±òõg©>v˜úNj}T$c €“o¼NùéÆûðJPŠ î„ 1O!™y+ ¬¯ÉÁÁÇi{û9Ú.¾’­ð}V]@—¯Œ”nâ }|æšæýSôÍòS¨0{êãì9yìºk©J[Ätƒö`„ƲZ·_Aë{y§~#§#UtûsÛ[=£~ný6뿺—x(Ê€&©û±ÅÜïGV P¡pñì3ÐÖŠ‚¦p‡k7ðÉ {9uáU|^VÍù`1Ó"¾1\¤YY@™÷ 8iêã=ìì<Ë…ï¿JÅëϱæü¬ŠuQè'b§1ÝÑ]hŽÅgO>ÁÿÓ÷O²äç:çt|Î*¾ìû9'^y’šoÿV\~=Í ú0šôºr#Šë²)T˜™X8^¾õ?üøJh ùdÍ´}ù6Žl¹’£õÄL?imêmå§¼\%„œ•É8kú;Øxþå/=ź²£ë,ËýDÒÉAë ‚¶MÉùVxú·êÆŽ¥Ý”×ÿlŒÖŸÿß´¾ö"+þð„–¯£ßôÔ ƒ6+³:*Þ}—ÖOOÒZ¹’ó[/ãÔ—nçýšµœ —3Mì‰ÈnŒ&¥Ó‰ W¢IIM|€+[O°áÈ«¬|å)¶´¡6ÑGyÊ‹óe÷$g?6·#µ°êb1Îíß?îüà%­3³Šq’o‡wŸãìñ#Tÿá³ìê¯Ó¨ eHç±ÇX Â¢Àûϼ@c¤†ŽK®§åòxµn§K*òS3\$ºôÚNEÓ1V÷w³õüIV¼øë>|‹+ZYëöôˆdÔždÁèÌ©!]"–EËÇQôýK˜ÝAòóFäÜ3i{õ„v ì8íÿðcÚ?ýœ÷ü9çí ýf83pJ/øo©©%¬0í%r®]þ×4ÞüÞ»î^®ÛDk <>ùåQÓàŠñÔßèk†ëRšN±½«…¯½ù{~õWÜðÖo¹²õ$ÕÉDÎ:!ÇïH#„@Ëì 1]—°eÓ¼o?²³ãÇKüã.pƦéz$˜è†çäon^Áž`ššd!×¥ìµT…Ц¿}é=â×߯g;¯áHå*N•D‰™ñI/§=ÕL`º’+Åê.®l=Á¶ƒ¿eÕoþ¿wê(;;ϲr ‹ÊÛ¸efx–$ÖũgŸsÍ?Rwy‚Ѓž´ìf'üä¿¦Ž¯®_Ʋx'åé8>×Q.°ÂâB«”]黎þç‡Ó»i]¾=¾àµ6bZÏÔ„ £gr ½O6Ë{YËI.|濳á½WÙÔÛʲDÁLMßL²mjº{áñÇÔÍž@ÝG/>X%ĽÀ½ŸK)ëÃËù×C§h Õ`›¡‚-“Q¨0%œ•RŒÃ_=wœ³Õ ôú"ÃçíÎAô;»#Àç:”X)Öôw°óÜ êžÝÏÎ#/qa÷YBé$>×™ñ§ !0]‡ªd‚=Žlk?$–UïVw>wÙÃ!Ä )eyd-??x–fl o»"@…¢E“”òÙü×—¿à\pýþ#Ýc?꧞ݽͪÄJ±®¯KN¾ÏæÇ~ÉîÓ³¶¿‹P:‰éÊ >}bÂÌg~ßt]°-Ògšà•Wv©;?u¬Bœ‘RVݰ‚¿?p‚OÜ*:e^|XcÔÆ•…‚Š*äM~¯öÃß8Ãi_%ݾImöóJ‰Ïu¨LÅÙÐÛÊ¥'ß§îÉÿÆžïsQGÕ‰|®ãíiAlù½ýfÌ–Å >Ç¥ÄJóù¾}ȶ¶CʦŽUBˆË|˜Qgý IDAT𣯬g³‘¤2Ù‹ßµ †ü*äM~OÅàÿyù ;rbZWÓ§äíŽÌîf³‚“f… ÄJ±¾·KN¼Ï¶‡~ÁW>{›5±îÌnwØiÈ1ˆmò%à-wÄïl‡Ž×ÂñÏ—Œ y©èüic…b'ðŸ¾TË…n7‘ôZ *T˜g¤”zàç/7qÖ_I¿ÆÒy©ö÷¹Ñtœõ½í\zâ5Oþ+W4}¶îÊSÉAÕ'&uÀ§êt{:6µ°²»»AYÃôP/„¸øÓ›×²ž¢é~|®Uç¦PaBò{r~zð ç}e$Ì@þsYÇ“™¿wGc)Á°e±¦¿ƒ‹²uÿ?rëñwX1ЉžQ~£ÅâtèÏ%ë“ü}¿ãP™Hðño~ý'—Â=]m9Öís§*©Blî½a%S­„³­·*"ÎJ)_é…xù çUô™aÒš>/ÊO—’ˆe±*ÖÅegŽQóô¿qé™clí®ü&s{§Þsst¡uÄrðuuÁï~§Œb‚0E¾îðn þäK¨·ú(·¼NP Â(´H)_uῼþm¾ro“»n*“Æî²]_&“™×äÆýŽMur€mm§¹àɸ鳷XëBw%ÚàvŽ¡ØÈÓ¥ž[ì´Ó|¸ï!äùó¼ÒtÙŽRˆpó¿,+…WáO¯YÁšX!'1§g(ó¸š ƒh“òÐÇÀß=ßD“¥ß zýÞæJùå¨ /ã[›ècwÛ)ê<Áöc‡ÙÞuŽh*î%<¤œ÷zÈrH~ðRÉàÙÀ*!ÄïEà;»¨Hõ²Sè³ÖúyjvªPaíRîûvýä…sœ&B¿ô¦taSãïÙ¦1 Ð¥KÈ¶ØØÛÊ¥æ÷ß|ŠuýíøìÆÜwŠÝÍ9ºõ±‰‡,€dÈü}ï¹Ä:!Ä×êáÚ•Q*Ò=øôxsaÉ<²È"o T¨@—”ן…;ÿö`[úÌiÍœ·¹H(K'¸ »…õïdíϱ«³‰ªdÿà¥|\š¹€a»”¥R|þ Ðvþ¤²–ÙA5ÜðýA¶ûmJì8¦tæ*ð<ÿ³w»9Ò/éö•’Ö|#0ç=’ëb}\÷é[l~öa6÷µRj%0å0Ž«…à<,¿# %“¤Ù³ð%1óp;æüD*„8°öÿðÚz–»1‚v*3N5ó†cÆÅèÞfŠf‚RÊþÔâ`[Š_) =£üfKýMò6¦t(±’¬Šuyù7ìn;E]¢Ïs}sö÷ Æïæ<§k\zîy8eñÁ#A¿R³¥…Ø»UÀ¿ß³‚ÚD—G‚ƒ~îï´Ú ¼ÄqFJùt<}¢•¶`)Í7Ú7Í>ygàÞ×p ް-j“|kû|}Í,uNj#f#.¤ê :þSðâË ~ß f/Å,œÈ !ÄI)eÇÆ*þ©±Ÿ˜DC 1g~žíè§"À%Œ)åÛ.<ðæ :Ë0óºR4)Ñ¥K‰cO•ŸÛv– qíÙqüð`N)v©ç6Kð;6Õ‰û÷ÍÙü`)ÛAÿ.èÃ]TTödÇß’Ó³ ƒñï6•ñaÛ|–öR¹˜}þŽnj›Ó²êi'Ì—‘ò-9ÞëŠi„“,2~)øk{XJùç/·òå'fFH c(ö2U÷CLQ)J‰‰KÐN±Ééæïn¬ç Á{ïÊ7oÿ뻺©J.äná FÀÒŸG£ìxúÄ¥—Ìê=öÈïØ®SO¢ûŽÑ¹$ÄR€# RšI!…9 Ë'@ŠÊt'5Š8³fªàÅgRÊ¿;ÔÉgIƒž@Ùpƒ›Ì¯øm‹êdwí©g €´ ¡á𲫯ٕøíSÈÔømìç}‘J‰ŽD·³óƒEvw7ˆèlÎNí‚4ß)¼n6çEÔÄ)J;̦< À”ÎXuˆŒZÊŽol5"G<æ–¾R>ÜÏœOÒo„‘#ã-Y8ÕÇøXêoŒ÷ÉòkØN°' 7W@ºáÐîuߺ‹Ç‹6› ¿<Ÿ6biŽq Y6Ÿ=ùkè蜳dÈxûš³—sèìÜQä7Hr¬ïµpDçæqÕÇ#´üÝD9®û˜{ pi¢MÊCï%áß}A{°’¤aŽ]Ð<‹óƒý‡¡j—›^ "‰ÔWŒOÖÂE׋› ‰…ÐÓ R Rh"Âa1l›úÖ?áÌÁ&ˆƒ/µ¦!1ñcM÷–T)\Âh‘R¾ƒ'[èÖÒ}r£ÔšÌ©<žt–Ç`=ßdL)Ñ Söç»W®f t‡àÀ(m`PS{φ¯Üz_ü¾w\n9Ž ã±-Ov´UËf<8Iˆe»¥l;´¶rÅØe0™K\V%¥P…åËá‚íelݱœmÛWQSÂhÅ5Zb=Ç$8~ÊH:—SŒáHtlÂÄ©¦—zXC›SÇ©ŽÍ~bv„´FÌÒ‰§}TôtðÀM_a“~Nq„Š sm;¥¼ûÜ÷Ÿž=Å!¥Ã_>¶… û ïùàäçás-JìW”Jþö²(+@ø2o!¥ ü;.¤-8tH~pÛ×ÙØÓƒáÊÌ6©…W€Y´ƒ_VÅUO>‰Øy‘˜›Ï­#×r4¶í„o…[¾º™5 a„Öúš-5.ñÜ Ì|¹4!âT£–^ê8Ñádg Ÿw–Ð<°Œm9¢†åX"ˆ-‚X„YÛßÁÓ·n`Ýr•\èƒûøÔáC!f'!¬á¶âŠ)/Ñ bgË’ÝÜyíZBã93´¦4ð™°uËOjv]ô£Þƒ¯S–J£;£§/„ü>¶MmO/<:óƒsË-ÝÌÿ{1n>x>8ÿïÿõ)—^÷ü` _ºa®ÿs4ÚIÐ+‘È)ûwS!¶|ÉT›Ö×Ô@šÄÅJÎp5oµ®â…ãQÎ[+H‰rÒ2‚e„‡\`aâbàb:Žè›Ô<.r´JÙõò8ÑBW¨†´6ƒbçéj„L˜1d§¸¬:Ä%†Wö¢OÄÁº>ß½5wîýÑG‡ßÃïØø§ \¿ëP‘Lqüñ'­m]¢fYżûCYBtF3ÎÛ¯Á¡·Ž±iÛ1~ðÜúí`´‚Ñ…&’€=â-³£EÅ»[H×)GÚuè=¼ú^„—Újiô]E—¾É8sir<—Éh* ¼ˆÑ%åõ' úÿ½y†n#LZ3† žç˜ür;F $†ôÜßoíª¡nä³À ¾üeúW®$n˜óX˜L×%l[XMgᥗ£ur.à€›‚cïßüIþpï»4Ÿ^Ž´êqe€\/z¬7ór¹ZRš¸NÒÚJMànjø;7ÿ¯8é ¸nùøä—}£Áiƒ“Ó›"ÀÅíú>¿ÿ„˧¶¸Âà žç«NÌ6¥é—¯ªf³ ¥pÀÌë (+[·óŽ;ˆ™6¢ H0;?¸Ô¶¼ùÁ­]…F‚ÒñÅn^~nºî-Þ|9ŽL¯ÆuKAšcpÈü׺:re×®kuÁ¯áç*Äf±#TËæÊõÎì:­Š±úûÜ…ç>;G‡¿ÜËúŽŠkÍâƒá£ƒ¶ÅòD'ßÜá'‡µOeÜòAÕ†C·ßN<ÂÒ5¡œ Ùo¼Ç?]P8,Ž*†»ƒ{[áß«‰§mF¦pex\{˜Ï>:€ôƒS öjƒßÅä˱UÔÂ=·_XKØN KwҮОýM®`.R´ÃóûÞm¥Ë,!®æ¼³óx-ò5 'ÅåËËÙ `wÞg¢i`êPWÆk¯£×ïÇÖ ¯pÁïØÔÆc°ïám™?a県)-Hõßþ ›Co$N•GóiŒ^=€•‰ý‰ùiø2LøeÉO—.;ÉUëýD3 O§Ò€s¢dæ r4t¸ýÚ£åÄ cbÒœ7ï_žŸáJÊÒ)ZÞ}NÿQ1¸Ám­€ëæób€ÆÔË™Ng>?Üsu Ô¤û“!^—·üï»"À"F¶îï¹ãÍÄu?ŽÐpHWNé)8óÅïý©K— `sm”•@`––Ó`±ñàÌjj÷¯»ùb¦é¹ÆæŸI:'²ð‰aè¶MyG'üæ×oK½=€4‘B$¾¹.ˆv1øÑM›Š*„8°-uƒ€DÇQ¸Ð,¥|£ ÚmAJ3‘¹c%'yÚϦDCC=0]‹ Ñõ€öާŒfôyƒ*VÓ¸¿vï^ÎG"T8¤r½ùÁ1ÎìÛìè(èdH,A&"æ¯!ªÔÀ ̈ÁK†ìY·Œ2«¨M–Ê/ntŽ572±¿Ò=Y‘ Ið¹6—¬*~žSd»ÄÜxñºz Yþ3¼ùÁÇ?‡Cïn2DÂ@?xq¸ù¤¯Œ†FºÝ‡/[ ÑtfNat>ÏyE€Eˆ6)uᣋ”63㙕HŽôtƒß±ØµÒ¤ÜsR‡+ÎYVž€— )/[·õöÛI ½5n´ÂÒÛv,šzÙÞ±¯Pm*g~K`¯™Ž˜á^¡eBìÞ¬ iø›#HoTž"ÀŇ8ì:p,MW Š¥€ò‘.~'Íò°5Æìe‡+Í1ØS 6–Üq;} ICÇÚ¼–¿Lªq$-›¦^€¦/î,T›J§˜ç"èì5Ðsö!OåÀ¥«k3[ã¼"®"ÀbT€ÀÛ§[é3#Cc.s]Ãy^ÿ~iQjpѪªÙßœó™áy&5×k ›P_OÃu×ÒébébÊ_ÆÏr‡l›šÞ^xôÑ‚µ)Ûδ¥—¹É¤96$éõ »\~jX%„¸tM¿c¡ËüéM`‘¡YJùjcŠ^-@J3ç|ÖG^ž¨t(³ã\´|Ð=Ÿg$5¦ïþ’;÷Ò L—˜\˜®Ce*Ág?Žln–…hW©$1<8÷¡µŒÛmÌ ­÷A]HÇç$Ñ¥«š!,Ft/~~Ž”fŽÝð ßXÛ,*Eõ)³lô÷ξªrÇ1ÝŒ»k˜pÅ´-[I«‰ÜÆÕ³JÄ㼯Óu ÚNK+¼ôRAÚ•ã0Iô\uŠ™½÷õ—®­¡ÌÀçÚªÖ¢s}¥<ô¡§Òú˜c. >×ace Þ³üþÙ#¿<¡ë‹ ÷î¥ß4°µÂ)¥œ\b¥ùtß>dk[W¡Ù–\0]ªÍÚ“8û/[ u‰N*S=”Z±I˫ҰëSÐå+%5Ó²YÌÊÒfKM”¦[Ï?þéÉ|‚®A ·Þ‚]^FJ8bìßyÇ§Š‘ï;–" ÙÝo¿Ç>ŽšmyÍT´<Ôø¬ò¬½Sµ{/Öá"úXß×D}¼LØaÚP´R<ˆïžn¡ßŒb‹Â¸uºŸã°µ.@InùËlª“É4‚¦©Ayɺ n¹åä™y€ÊD Ã-¬û—ììÛìînÑÑ“ÏFþe p¡0{r½îù?¿qé}}xµ¨Ë„Ø­à"@—”ׇö´ôv~äi4šœ;ËDsüÂf}‚ÌF>/{ªZæ˜ø´eV™@7¹ë.ÎFJˆ›Æ¦-¸¹ûIU*ɱ§Ÿ‚îî‚j–ºp¹´Ù½'•BÜ¿F±C±9,ˆ"À"A/<ÿƉ^ºGÓçµiǸêOºœk+K‰dÈoAQg0 ؼ‰ª=W’0 cgHv fç‡Ó6þÞܧŸ.(ûŠ..Ìç*,õ×¼ÓÔ†­û¼:Ï<ãXÃ2År»SøûÜø‰kSb%ØZWº`ä7lÆ…®C8"6~ë. . — ÉÆ³™H94uœ mstÿþ™ÍÆdø"‡"À"€ûÇà¼ô“Â))„͆+ Û1ÖUémO^`IªÏ×üÆŠz|…×2_!ËÁùäSxãõY½ Š9Ò}û´M·YJZ×AˆÙ+[wÇä‹LÇ&l§h œu |u¿ùMZƒARƒ-óGg1gãN§®0`[,O 0°o?²³ëî©8…B‘ "À¥†ðqs1#ˆ- e««áºTú` ÏZýß å•ÐÀg4rÛít”—MØ$a¡.£Ï…òd’“¯¼ -çî›Î×TPXè3>„!1Ârã¶ëèL»¤toæÇœ­æ[Ò$®ÃñCorqÙj*ÌÈóB9úÐ3‡˜·£,‘ìØÎkíÄç¸Oàtê 5\|.„qî½è"Öƒ,ãþË1–é\4ÕQ¨P wG3€ûŤtsáæýNàwŸ> v ° æ¼R@;ðV,FŸÏOJ×Ç-Œ^¡*½ zز¹¢²’z¼m\…ÔÅF BÜ|%Ôlß5ºðy®²€yJ Wx‹xà‹à$&¨¤«ãÃú€F 1™ ÏçÃ58iáëC¶Å:Ëâ¼ʱšx·ÙUkAàU€‘ *6m#3ÿVˆ…÷ƒ4$†ëÀ™“à¤r3é(8ؼ‹Ó ’Ò ÏÔ®¤:‘àòP€RÀ'U|O º%C·E3`óNbþ2ìLß?-·p9G“Œ;aË#?7Þ­-àX 'V)óU¶–û9qà] Ñç#n˜#\ÌùW¦£BŽKi2ņ`˜5à‘àˆe9$È5µ\.1¶í¦Ï c sÐõZó¨ºr>K—.>×¢óìiYò+ 7-׃Oç€7;;‰™¶Ó%&{ý™¹!—BTãÅ.adÈD Ôn»˜~3Œ“Ûú>+s´¹[Í®A¶9êI“.'I¬­yRå·¦Z·-‹>ÓÀÒ2ƒã é‡7?øÒhÙà0ù±&…óQ¨0Ç«BóÔ_i¥u+Iiþ¡ëB*˜Œ Ô‘ø] ·µe¡ÛˆLè[À@F~Ú?@ßOZhg Ø6+’)®"¨]nŠ‚€µ›HKI¶üEÎ픵IOK h®×–¦¼à¼îà¹ÈŒfН¥Ò4‡#“vŒ^Ì̾¼¤”0^S µ0.aÔAlØ„•3ôh¡ëÄd&£I› cAÛ¹‚ÊþŽÇ‡1à(pAÜÔ îMץIJXaèlÊP:.UîÂk+j©X»GÓcqÒ- ²Ñ¥KÀNÃù|b€ Ÿ½Lgƒí$ cŽ!ÿçÐáÉ¿ÿPatš+KJ¨' TP¸Ä ¥ô’FʆMà x d§€.]Ò½iü5M=À{®¤Ã00uìBë#!âØ\Ñ„ÔRP¸tnÁˆÛ ¨Y(¯¬ÿ+¤] º” ´7ƒ›Êãœæ+{9¶Òx…Ñ   8ÜÕE‡?@JŸí‡Éðï9´GxâïŸÛM&hÛ¬Š'Ø“!@1ò‹¨Ìˆ"À¥áëа‰L÷—ÂpÍs×¢Kßùæ¼   6^FøMZÃRz¡\ס ëw$•©$»ËˉâmÓÕjP¸¸1†BÐ<LèÁœÁç +s«Œu¶ƒkŽ­´rus8M¥ˆfaèi7“ÙGCw!hÙÔ@9…Ñ9_D5MPX8bm.ŒQè”­ZG\âˆÂËjÒ%ÙÓé)À"*ÎMmÀ›Ý=ô›>,½0æç>âL)‰¤Ó\QQA-Ó!Y[“R5ÄRX ˜}côŠ #µ+IfAµqÊ* =Ý^¤ˆÖ¢tç‘$Œ©´ÌŸël¶§`¥”‹í¦ÉÖŒ.¦kQô-ŠÅ(ª*ï]~×Ð ‘2Í1–ÆÂ-) èØ, p¥Ï$”9¡–­"ÀÅN€­ý±‚ÊþfùWH›¿±x çæ›è««óñ™ÓéH¢i‹-¥^ƒ„^«,U­pQ¢SÊ»@oÊÉqçÙ!› &ÕMsñ¸c•ë¶ÜvÛÔ¶³ÅíHJ-›ËL“¼dˆ”ŽZ,Š\Ý¡¨ IDAT¸¾p4Y€1@Mº^+,cñ(-kŒ~ó›ô‚$›¬¹Îàüàå¨6YŠ1$4ôJHknÞ]º„|‹, ¿rå=k¯¾šn¿kD—˜Ün- …íж¸Õ&Kàb'ÀØš^pµiž ,=\d£ËDeåý¥{¿Ik0\0]bráwìÌüà0‘Œ¬ºÄ(\Œ.p´/–ð!çýÖLRý&² Ð! /Bùúj܈úð¤ø`_¿y¾9¥ WRšN±. (a¢–ùg¯U[AE€ èI¥é¸h¦Þö8— oñ-"±¼^\¸÷› øü8šV`óƒÁï8”¦S\ Îx~°*¤VX¨.0}‰Ì>à‘Áø‚xt{I  t/¶ë¯õë¤JJIé Ÿ Ky‡ËÊKYË£sýˆ‰:P¡ê* ö§ll¡ä>`‘qƒ¾Ez*+Ö]póÍ™dH!- жͪDj0¢Í„Ãù),4¸@_"­^ 0wÈœoPDbÊL!´t иخ¿(6jwÝÉÙH˜„®/ÀRkªœ;xێòDŒËK½ùÁÆ xR‡¡pFÌ(TBÒ¶‡6çXÅ¿Fv7È"ƶ ˆ^vyAu‰Éš‚麔¤-Vê›RÔü`E€‹Ì¶]Yr Fgq*@±¬Vl¾k/1ÓkG6$8ÞT¹ŒmÈìü`ˆØi®.)p~°xŠ‹–#½F¨#]àrÔÍÅ~¾ô%D]±I´.ŠDX*ŒV¸È`»rè¶ä¸ÀcvhžOe$†”É¢‡ß¿nÓwÒ /´êh›\‰š¬pÁ,Ç)È pÖ°è (6rûí´•–ŽÑ&káá³¢©G£j~°"ÀÅæÛ^üf ŽÕ¡yA Ff,ê±råOV}ùF’ã´ÉZ¸–T.†„m± É…Œ??xôRWË]`#m9Ù &»øÄlÒ[XUyïÊ;ï¤Ïô“ÒµÁX`!Ìæ¸®K‰mqEe”:f¶3D Ba,:À²ìLd¤Â#Ž…Ã’)½ØuñOʶn¡ßgb 6èžßk?ѧùÓÛ ƒ­Œ• ÑBöð"³ÁR…¦û;à5õ”FŒ\:F#ªªî]¹÷›´ƒ¤¨c´˜Àõ:µñWhbÌùÁj‚œ"À¢ìén8òØ“øm mÌ1 #ç|/œ¼¤pëWé©­£côÙÉ ahøIe"Á®ÊJª2*PŸDñ©ú@E€…‰îž“Gy”`*…ÈÄÚ ®{)Ý—òò{¶~ýë &ŽFÁu‰ ÙÑTš]šF𩕤°(ñì³”6!˜ˆa¸öPÍßB5¤æv ¹ämIi@QQyùß$æ÷a ­€J”†vŽ›=UU,gt,P)>E€…ïþvtü¸ùá‡X–ˆJ§0¤áÌN¡Éœ^ÿ{©Ý 5 ÷¬Ús%½~?öˆlp! `;l’‹øÿÛ{ó0¹ÎúÎ÷ó¾g«½»zWkßñ†±¼È¶„°M°Á€mÉ$Ãd›ÄÎ6$™ cf’ç2“É$r“;I`ÆÉä^ças€À Æ`0ly“-cY{KjI½wum§ÎòÞ?NUwuwõª^Jêó{žzÚeUWŸóžßû}¿¿=È ƒ!^X²ÿC}û_£±d)Ù¾>)–m(ÅT•'¾·úpÛŠb¾ŸîX‚¢n”…úáU1×eu.ËnÓ$Á,Ûd…áàë‚]õ÷?pú+_!U²‰¸.†SB÷}DšJæÔŠÊxó-x[·ÔÝü`!º¯h°K\šJÒÉØüàPB¬9yêácß~œ˜ë¬ËóЕ°/µt'¶Dc|µ*O„(!Wž ˆÎNqÅž=ä ½®!J^YÝW$J×L—]® ƒ!.»ò nâ«Ñ>œ!âºA•…ã ûÒ«?íTJá#qVèó²>ðŠ©òº†[';GQi“ås]nhn™¶MV(!.,38b–/9øÕ¯ÒšËõ¼`¶˜Àrbäw‘Ol1c„Ó'˜Uâ—²iÅ=즦͗¼ç½ôE¢”¤VÉêÕ—s6”l®eº6YÓÏ 0”¥“§Ÿ†î3$Ü Õ¼¦|¤ëaúÞ2æN´J€'$¹ÒÊ@‘NÕ÷îál,NQ×ë¦]cå2,ÏæÇc¤ç‡¸¬p1Ãï=«ÞøÒ—ˆ¹.?` t·„黹vל¶q…FÞ f¯Èþö+S×]WWÁY¦¢Áüà›"Q6¶Ì°žåÕWÉîÛGÌuFóœ¥òÑŠy,ÇE¯Cµ/$®”äK+ØåÑÞÖtéÞ½Œl)ê" RIÉÑ”?:?øúx|Üü`!œ—ë…5 n*}á tæFˆ¸îè&2ñвÃ$<ݯ3?M=¡‘/­ðâm·!;Ú(:ެ¿-s=®mH²±`HuÞ¢"L på\Ï‘7Ÿz’tÑÆðüQ¥Ô<+—!éØèªþ’MOH²öÊv£‹U«ÄÛ~+Š#ë//0⺬.¹ž 'P#ì`‰ýõ¯Íå1}…ŽB¨@)uåbdH9…ѶX“Ø2ÏñÐCp|ih  Õ——M)…åm²®K¥FÛdÉ„>„ÁÐGºô ÚuRýôƒd­SBSþ¸>ºï£  Я/¸$0GlÖŠˆPý}ŸZ‘»¨Tzˆ‘,»w^Gÿ¿<^w—gø>ZÉam,Î¥ÀY`˜ZÁ`ÉSß!ÚuŒ˜ëMjò¢û 3;LÌ.`¨ÚFæX®Þh»˜%bA­ô‡¾ý>wéNµû¶úêµDbúŠ˜["î8$Jõçª ¤ðI¸%v¥8˜Æìp÷…¸¬ì¯¿ïSç~þôç DÜÉÇP.‰R‘ba$0•ª«ÖО¸B'ilÌŒpEßÀŠxnBˆ ÆoV¹VQž¼#䇙aΆۯ¦„>À¥”oö­os½ÑSzbrª®\RN33˜ÀK¡¤S„kE›}€`Ij”ä G†©¼:P®èïÛ8wéæó ž;«ÿÓWi/ˆzÞ(ñ‚±†ÏJáù´Gˆ œÅòœe=¥kõ¬ˆ§IìdŠŒaáÊ0Ž6f8õûÅ—˜ëp©”\NU›¬00À%“}÷T‰’ƒVNo©…-¦ïÒP*`ö÷õJøËšô<•”¤ÀN4’1#”B¸îÅò\VòÜ(õšóƒC eþfÆ Ê¤Îõ ¼ù…/w\Dü*­å+fJÅg¤ûФcc)Œq´©Xàbwˆ®òNtE–¤ŽneЌㄠð@EºçéÚ˜8?8ÀPS¼–üéóAËû v•‹Ñ'Êh7×ÁìTÀ¨_àD .I»©Áh‚R€u/•`HÚq¸Zæ[ᲄ¸ àÀÔ®54¸ÉýâY“!â–F5ë|Ç¢ƒ¦["~®›ŽÂ–ïÌý/€Lç´¥N!ÝBÁÖ´ yoc÷Q»“¼hõ Þ%ê¹ìjne-a0$ÀE”QNÔÛwä'ž m0k$>O­¨ñÞnÚ Y¢nýEƒMc$•&—lÁžÐ  ú.WR×Qç („ æzlöÝp~p€‹»¤ó¢ôÏ_':’ÅòÔhâs%ê;™E-ß!Úw––Â0–W/%ìjtÇØšN¿§ÐÜŠ#µ ›Í(ˆXÑÅÛH«žE)…å8tæòÜhY5Ûd­h²Êš@öôiõ‡>ÄêòœßÙšQ°¡óº»BÇcN'›6>¸z×n†-·+e*óƒw&Áü`´:ä%ÀóîÎQŽÞª ø±ØÙ"ê9£)«ÙŸ¨aòN4ƒMß#YÊc Ÿ£YWX¾3:(}ºü¼Åb„ÕOúúŽÐ8Ú=Jí õ¨ê75?Ò¸gݱE]¯™¹x¤yæZã â¨ÄFÓb3òAW+w÷‡r¾ èÀN`]»ãólÅA®Ý¶·måÒõ«‰;ÅÚSâ–Yi}¡s¤o˜\jÁ¹õ]¸›6’×µšùË™#Y©8J86×'Áüà:0$ÀÑs®}ÛäèKC £H»èȈxÞœ:W3‚¢®q&ãºûîcÛú&RnÃ÷j#ï2Š#%Gú3äB%šühVwŠ·ß·‡¼®×íØ”˜ëqm2Á æŒ×ÙKÍåE!.€è(Ë€5R#Ur0昡ÔXbLA3(¬^ ï¹-rrAMpUV¸Bãø`ž! _©BM/Ö?@>‘  ixBÖy¼ˆ{A„Xø+˸âp줛kß6ô:Ý ÄÜRø˜àû›iV„¢0€+uF´(‡Kà§BÈ› -Ín»í6"QlMÔyÚ÷h/ØÙ)ÀðÜ™M©ùÒ÷5 °N%lÞ‰w‚q—sUp¥ž€‚¦‘ÅHÜs_ ¤À«Ò~iŒÖ t…ƈçµnp£Á“µ¦æG"÷íál,:©fºÄð}%‡N —Ä Urê¦z%À%’ù7U‘£'y¸Æu}™´ÔL¹€>¶&ˆDÙ|ë­°ºãA€UBˆ·¯Ša¨ªæ¨B,y4x*˜1â¼Ö=¼¢« ¦•k¯Åºâ ò†VWæoE'%>1nh‡H>‡å¹+ªQtÈÏCt‚ÄçVàªæ&šŠ,oþêck:=±(‰ŸÿyDSó#•ÿ¿5 I\ 5MƒÔeOHŠºÁÑþ!Bu¨ 2âí{ö’ÓM\¸8ê)&"lLŽ ã 0”J”KŒ¾&*®U¿RÐèzXžš”ø<Éúå—Ãu×ûÿqàòUÍ$JùÚé0Ë$>OHÍâÀô*õ•PojÈ{·© [Óð¥V—š®—Kù¡ Ê,¥ü¸¡µ„Ìû˜kݧ"ޏR’ÓM®Ü³ÑÑ1kM`ç¦îHít˜eA[38p:Cö„ZQC6_ö¾÷1`E±Eým91P«Ï4!.ÓÖz›T^“}7Q‚y«Û=wR×—Y+_Ù”,h^s3Üuç¤ÏDàöëS°Ê/ÔO{¬QW¸BãÕî>†B¥©ýŒÓG¹o§’ñ)+C–ýaEN‰ pžÊbIà†ˆ5®ë‹šÂ”žNJRc åÒ÷¿R©Íÿ½Iˆ§ZëZ1ÝR}ÒBP’&§lÁaàœR¡;°–lß>Ørã £•!áDÊ/X1‚ë€K Ë]_FOÒLr:Éë:Ý©$ìݰ…²Y±{s#UB ê•‘fŠŸt…é0SžímMÛö–+CÊ~å \ -€Bœ‡Œ?¢QR%‡ˆë!•_ƒͬ">P04VÝt3lÝòâtŸ½$–FԳѨ_ AB´åÅã}Bõ˜Zn¾ÙÙɈi”»Ä„Û/À P¢À&àÆÆFb®ƒ?Åi8SÁ»àKIÖÐÙøáû­m×L÷ù8pævR¥ †ªŸ`ˆ'$=B×H‘£n¨SJ$²yë½÷ЋRÐ$r%Å"% °^i¿V6wëŠA×—ùú´)15"7à 7¾8 æùâ¡ÉÁôJÔ‹/ÝGà IQ³Øw¼ÀÉ•4n.úÓ˜>ʇ>DO*EQ{‡xù4‚™ îjh #t}™Mꋪñ¾¨iôDãlÚs¢­õ𙾣MˆkÞlOê¾3¾"®Æ ,)@*p4“çŸc0T§©eíºÇVßö.ŠºV¥U²¦u€ m‡XG¢ À¥@§ÔH̡님L(ê:ý-MðÁÍúš€Ý›VõJhjú`È’ð05vO¶08”W$l’:¥´¶îÝpß^2¦IIjÓš‘!‘°®|1`5°+Õ@¢Üõe>o*z]Ð4¶¾ïƒÐÖþàl·SñÎõ:Ê%RÕ)zÜ ,åÆcÒÖ ú­F¾wì°IêÔríu%·n#kš8RPí T"x7síx(!.±D€mÀŽxŒ¨ãÍ[A=!±5aË¢mï}ãê~g#­Àî­«I:#˜~¨Ã29]¡3¢Çxîðé°6xZزwý=÷б°5Y//ÀP¦#:’8°èÌŽLšÖ6ýËq)1® ›×í„­Û?=×k‰Ácwn…UÅA"ª4ÆÄ–yã(¶f2¤tž;gBnj¹ûôµ´×ÇׇÌ/Àº“ ðáÓ ìhNÓd0çØ5£ lÃà\,JëÞû-ÍŸ˜ëõ´ ±w;°km3Q§ˆD-_NBÕ}ùå‰q9ÍäûC8ýClypûûßGÁÐñÂ]`=/‘48µš=EÔUhjúàÇXèÉ• y)±7n‚wß:ï«Ú*„¸ëò4Q¯€¦ü™Ap±¢ÓW¸Œ--^*ña0dÊekn~¤å¾½Œ˜&®u;7$ÀP°€UÀ®Ö6RnÐ5w^Áøÿ²¦É•{î…dróù\×6ÞÞž&áäÐ}wÙ|GXùY){ú "C¦–·m°ãªdLcƈp(ó—^¥¾òÍ^_}ñtISJU·m p&‚$|bÀ`;Qgv¥}9 ð‘Øš a~èž)ë~g+1àÎËâ´±|'Ø@S¹Ý9u¿ºCuIš YIž=rš³¡jMËÛîßËÙx¹KÌ8b-¦°(B™‹ô+õÀ›°ç3?=ʽØÍÞ7Ì °çTÙ'à  hª ëËõ“ör×—ù´3€­ #o»ãhmÝ|¾×·J±3 —&$Ï®l’.§xBP&CZ„ユ§+ †L-·ßA~õ š6U‡K¶Ò î§yKoâD¬ƒgú]>ù­£|ý4¼ª” p‚dÔß®.K&IÛA×—©•súå,ê:§âIÌßHŸû«H; ÞuÙZbîXbtÍË[â(±/ / ?xŠã„c3§<×o—Ý{qatxîr\)õÇàG¹ Y‡­™dÌ$GÍþ¯—Ïð»/”BÈ4fG¥ëËu–E£=u×—Y¬€¼n¼ú*xÇU vÍíB4ÝÐ#‚¤S@_Î& · ™œÑR<~òðp¨aµ%ùÁ{ÉD¢Ø†>i~p(ó“3J©À7œ¢×jÄ–žä¥ÉˆgØJrpݺs"³‹ôüÛÝ”&æ>¶éI”?%øyJó~ïÿyD{Ç‚r±FàþkÛè(öbùöì)îbù Êwçj&ýV#OOžäM¿­ n¿}Á¯·SqS®i²ˆ»4¥¾ZúùÁ´#4òºÅYÏäÉ./ì3Õ²55?’Ús/=Ñ8M£Áç)}°ç០rD&Èé d9] EÔ+°Ýr¸·5 ‚P+Oo\×—dŠU¹\Ðõe–¦îÄh­iôFã\rß½‹n^Œ»è€áºRNÓw‘¢<ÓntàÍR™Á㙦 §[|íµ"ôN)»wãmÝBQ×-D]Ωw9¤”úÂ}6ÃF‚’4Gfáƒî»$ÿöÚõ\B€5¥Òõå`®“tfßõ¥–4žTî½÷¼S_¦’f!¹Ô€›7u’.e0—cxÒÏ–'e’¯¾å‘ }µY`çj±ã¾ûÈ›žø¨0xÙ.¢p`µŒ-EŒ ëËšl–Ø ÁŠ™2ú³ÊèIÓ ¹ýmpõ5-Å]lBܵÉdµéó èeÓ½ÚšZËJòZ”g\žÉ…ã3k.Q:}TîÝÙDŠ|Ø2öÿÕK#¼èÆÈQü ÞzÝ÷‰»Ö[>ÜbÒ "2iׇ‚˜‘´×45ÑT,`úóÏ«+JIo4Êú½÷!Z[ö.Õ}´÷]ÓI³=„å9u³¾¶fÐg5òÅŸžãL8>³¶\ùb︒‚f„k1 9¦”zìˆÍÏd鳃 G5­Qñl:ŠýüÒîNÚÊGp¢ø£‰Ï;€ß'êy3v}eUeæWí,ê:mmðþ»—ôNV !ÞÛ»R‚˜W(G—gŽWuÚ‚œå¸ _x#Ï¡ÐË?™vtˆËöÜOÁÐÃÚßä´Rê‰<úÆY2VWê¨*ö'U0V%鿏{Cš[,HLð…X%Ðìnm!锿ÕõEQÕõE7¸üC÷B:ýàRßK<øËï\M»—%îå1”¿,¶oµ¶)!)IŒçÛ‡Îð\ºCœ,wÜÓÜL^7pe¸EkIRûöûðÈNq6ÚL^ Lßꀟ®’¥,› ‡û¯HÑ Âšx@‡K9&1‚ÞMoó!æzsÚàæLx¸R³L÷̽åýBH³lþ͵kYUè š%,CÐßc|Z*(h§b­üíºÃn1µ$ßü¶Ÿ{/½Ñ(ö„& SM‘»pí®¹K¯R_y®þ³ï¦[ÆÉkQ¼‰ë$ÓwU¡—_½±“‚Ê.-À ˜@ ¸Ñ2é(æ±¼ÙU~Ôâ/•–÷ëwí† \®ûZ'„¸«nh‰’trÊ[Ú¤è)Ä#ÉQÇà ûG8²Àñúؘ>*ïz/gcAeH(ãåìù«gû8èFÈ1\¡áUiP>†ï‘ts¼gc 7FÓWgr)k€]_Ôèú‚˜×É›×uN'â4üü‡—…ýUK'Üþk×·ÒA‘¨k£-0Ö̵Ҥ’—¨dŒ8ßèä»ùО$›·²¿T¤¨k|c•K‰9>öW”Rÿcß/(Í ÒcÔÂ(—¡ §ÀÃç#W$éJ/„E¸m1¿¿ü¸1¥¡ÜõEG æÙ?ª¨¸›6ÃM7-ûÚ5 ñÔfà#ׯ¥½8˜Â iÂÌ3ÑÚPÔ,ÎEšøÛïwñFÙ´ ‘¯,‹É:¾Ô.Ú€H?;»›;¬”úºüà\ž!39VéQå’("žM[¡_¾qý<õ =Ï[´ÊAú²ØÕØHÌ ÒF|åMðPø³òXm¯t¶ànĪκPÛN!Äûá¶öI7Wf0¢¢£[&ù‹gºy ö (u¡3 ^¥m“Ugº’‚ć”R_? ß<ÔCO¤ [3Æé±TA³¦|RÎ÷lná]QHÎ÷A mÛ{ ü*]_vk‹öh×1Ï ]Ð4 †[o­«5Ü&„ø¥k[XK‘”›=¯üÆI‹8Ïè#(!Ñcì/j|þ•=ðdÐo—^eÑܧ IDATdвpåÊ͉9®”zzþß}Gè·Éê¼Iá °<—F{„+’:¿pY‚öQß s¹Ü¢|¯V6ÓÀéf:ò9¢ÞüÁÖ‘(¯ @,^wë¸ø•WÓ™ï]8SxŠô¹ø‹Ò`Àläû§‡ùê  Íç(Ï£8 †¬´ÎÐ]J©dᯟ=ÅÉØ*òºJMr½øb^m¥^>º»™6´˜õ¨®æóùEù^h®°Jy${´tl>¾ [Óé‰EyÁW}¯îÖqâÖܵµƒt)CÔ[ø È¼˜ ”äu‹>+Å—^9ÂSVü‘l6Kx8ìûuí"¾ÛÚú”Rê¹|ö™Ãœ‰¤É± Æw‚Q«)E̵i²3üÊ®­\XuMú,Œ“ÍcŸomm#8Y§þVš (¥(êo!xöܺ\ËíBˆ_¼$ÊŽFƒ'»hm³æ)èQNÆÛùëžä‡yVtÕ\.‡ œö ô¯¸t˜n¥Ô‹.üÙÓ'y+ÒFV‹TéÓxµ0]›V{ˆ;6·sK4McúŽzlÊTpE`µïo'°]y£ÁjP›ÕÙ¥Tà˜F’×M^èïã\Ù‡S¯ÒýæÍl%OÜ-ùç™T±W¡1d$è6øë§ó¼°€• €ƒÀ~Ï'kØšD­€Ìµn¥Ô«À_ùäiö©•åìééÓ/àeà\4нH¾@1Ãó]Ì ä&ƒ¤xÁ‡ÿò“ìךȘñÀºª8ìÔXc_ÂRéR†_¿r-w% ±\é>Wn|A`__ßÂ<ä²o \\&$ wrÇ—¹.¢­éœ³L^-+«FFF.ˆ¶Uñkïhdgs„{Ë«ߥ- ú# RIþì[‡y®¸r¢Ã£:/6ÐqJ5š#,´shéÄ‚‚f1D‚Ÿ*ŸÏ<~˜Ã²³[èU.1n°‘åÛ´Úܱ¡•{ÖIÚÊ NEÝßñ23ÀjßßíÉ$k²Y¢Ž3‰èÔúïéPã@6Ïi PþKýýÎ$Üvxìã×5°ÓÌ‘ròHT _àxаèÓæTùÔW‚#Ê›fÿýé.ž#+G ¯(ýÀ™L™.¼³"ø>Iï±$M†?ÅåOž|‘ŸY-dõh|5üœRì/YÊrSÒå·/5HÃÖyÙÃÂñkÍÀ;€MºAÚ¶G+"T ö7Û1˜E]çÀH†>ÀY`ÖºÒ*ÄÞðØï½k[dŽæRÓ]æI/$¬€à6à¡ÛÖ°­ØCÂ˲¼Z|c©¦ÍùU+§G9ë௞?ÅçÞòا”ºX'Ì={v怓À ò±5 ow®Ð èÞXµî3¸“Î÷ñ{HòÂbØ×q¤œ4Ëc"ûK¸¶{øø­¬ðÈù¸ ðøñãç‹}h*0}wWA¢<ímâ¡7˜u¤¤ kœÌåéŠU*t¡ ‘á«üûwoeµ=Hƒ“ÁPËúî­\Iô0k€ð\Uá‚ÀêÓp>G’F0ë·¸£±‘Õ##DˉÏ£½sQ+G ­(o Øå³òUgΜ» 7ÞZ!Ä®|ì–õl*œ#æÚå“ylÇRš^q«õÚÖ z­&žÑxèÉÓ<áó øuww«b±X^k1Ž ö=†"QJ ˆXJ”‘ ŠÕW¢¾S1Á…L ¨¾G©O¡1æcŽy6äÏò{7¯ãÆ4ƒ0fü.ÂëÀƒªóÕm hvK @cÉÅðÏßá[’:ƒ–ÅaÆ¢¿éêêº`7àZ!ÄM øè­[é,õÓèŒï³xP3»/v„FVÐg¥x“$ŸùÎq¾Òåó¦Rêb·yäÈ‘šŒÆŽÖ…³Ðé0²Ì–8"PÈ*¢”1]y4:YVøÍ[¶pKÒåˆïœbáÓä6Ö=ÎÚü=’&g,ÅmÀ-ͤì¿lþž•q4I¿apœŠïoì;û/èM¸Aqk>vóz6åÏwƒérKå'Ÿ)Ê,xB'§G鎴ð7zyè¹A^€ô± œ 9v´PH èCQZàöXSÅw‰ÄE—!@É`°‘Wf~ksçø­×qG5‡MMIËW>üÆ>æ—ÿ~ËáÇçý»Õí®n¶”JD]gÔä=ß}âHÁ©¢ÍcÑßêï¢0”GÊÉÓVàW¯ßÌ{› Dœ©;;ÏþoÂ5óå—_žãÙü:Ðlnni¡=w~ýþ&Š+$' 2eód¢ñÍ7ß¼àýQ„·%á?Þ¼ž-…s$ÜÂ"•ÌWÍ™¢ÌÕÿî#ð„$cÆ9]Åÿ|ý,¶ÀÕ"|iß‹UÖI™É… d®@= hKÝÁÈGÃŘPumÖä{ùwW­ãƒíÐ1ŽùMáÛ›*0el X׺ï±óꫯN½e„˜–ÅÅš€Þ•n¤­X$QNzV,L;qWJN²d«`µüìg?ãbuBˆ.¥Tü½[ù³ïähÑ'kD±5sÚÔ…¥£‚ _li`›yÝ"›ËÑõ/Ǹ{K/*¥ÚWÈíMB ÃPh^í»Ú‹üêuëù@G`ö.hká ¿uÏ_ýõiô^M²í«ã• eÓw·ÐH• …X°-ëJIŒÖÿNÔ—p±È:!Ä ü·÷¬å1HC)¦üógƒ P&&øÃ\¡“1ãtYíüïãy~ý‰“üÃY8OÖ{¤ëø 50®ŠhŒéøÁ¶>À[ÀsÇ`ÄÍÃKñÑp°t”CÜͱ)Žÿ°k÷v@[MŸŸÿšk8z‚¾Õ5¾òÊ+ª0¶R&Ь~.ÝDg.;:룚jAŸd°¬”µ ë矞‹I:…WÂà¾c#WÇ]Z ýÄ\m™ç ]´Æ.À+³Á!3Æ™H+‡Œfþ÷‹ÝüŸOà;ƒ¤)¥º•Rõ8€é¹çžgáT‹Gk:£C’†ÀJ²„ªŒ)‹ÿ@%>:E’h±‡Øàñû·oæö¦üR~|µ¨×PÇòä“sŸ£Dº½Üi™¬ñ]⮃á«Qº?ßÊIŠ#ƒ ýR•7¢¢´8zä==g/ª$Ýv!š¶Ã‹ÿõ­¼7 Íö–ï æ«¦3Ì™uÍñáéÊøÍžHû¼>ùã³üÎùZºê>þÄ·ËË!Q"~Y׊0‹`Åøømõ4¹Z5ÝÅŸao,DÈØwyèäiY.wÏòÉÛÖqsl|´wÆ¿wž¤_L(Ÿ4A·—[ÓM¤‡1½Å£õî4f9À3Ï<ÃÅ&mB\3 Ômÿáú¶'›Á×t1`5’Õ£¸BŸ\¥ Ô’×™V‹#4]#¯[ ©$ƒùÇþµ‹ïDï»l}ú€R*ÄàÁf!–uýO<1g;_ñ$DS€îàË¥3uJDfC4Ξ÷ÝDcy~K%uÍøÃÎéóAÎß`O:ͦ¡aâÎxuYÈ©ZB&Z…¬›¯}ퟹ¥Iˆ§.BüÖFøÃ]ëØT UÊ¡+w ô”*×O_ã9i¾«_¨òëüjŽ«q·-ÎkQÎÆZù‰×À¿|†÷ÔYþö ¼ ^ÆúâçŸ^;W® þ¸îÌ•b ˆ2%Nï}ÑV§;ã|€ÕÞò ÑZÈ C9¤½~“í(ÒÓTx¬8|ôÑGçäÿUà÷þDŒÍž7®ÛËbˆ¦‚@K­ñ{ùÖ·¾ÅÅ,„·§áïÚÀ;,›¶B?I7)¦f}KI'& øJR#£Eé‹6r:ÞÁë"Å?¼t†‡¾ñÿ÷Ë}<=ÂÃo)¥N-q.áßýÝßM¯oþ톲î-È!.®±Ž³„W€‹í”8DT†$CÄaÎ3~þ󟟵_itšÞü\$BÚ¶J-ê UMù´–WRåÿ«ÚáÙl–O~ò“uë¦N!Ä ðâ§oiç®v‹Õù>"Nq“«½jPcx·{¿€Þ¦)R¥” ÑÖ,ú¢iÇ:ø§Á'~ÐÍ/=}Ž¿ì‚çàêýJ©¥èFýÿø“Mˆª·20Ói.ëÞl¶ïäj'9á@PxŒŽ8%³„Òk›À‹Ñ\â"Dߦ:Œ¨”ZšäÔ©ðõ×_WW\qŬ?_©ö¸ ¸·µ™5™,QÇ-›^‹·Š†ç²-a_±H(V"ÌöÊ_üÅ_ÐÛÛû•ÖÖÖ½+¶ q À¥ÔöMiýéiN{69=Š­sçÄ8¸d)Ê3PNGh8šF^‹”M3^×áÄý|÷å!¶¦L®^ÓÊãE¥¶X ª¯ÇZ…Xgú™Ï|F}üãŸÑÅÓl0L ovcŠÆ§ŠI<)p…AA×Éëδe¹´½‘’ìÁWŠ%KíÈNq¢Pî<Û_døû¿ÿûcn›$\ürsšÕ»õe ‘U>’ó“¨çquº‘ïŸ9KA™R-všᡇÚà ËÊIÓWýÜjþîGÝüp(O_¤[3ð…VvD‰ÉàW)U‹{ˆ)8 “ÿM0k½©ÉJTbRÔ \™ ¯[œ-¹üäÈ‘·hH®Xӯ޵’Kbìy])熧NR—\rÉŒŸ‹ÀަF¬‘ìœúA°Ã–:CV”3±݉('‚†wz 'L|á&BÏl4.D°ÄGŠ"Êv_A½êûåÊÀÇ{LíÙ3;¬0Åc-ðáXŒ·»e¿ßŒQß…‰tE\—µŽÃ5À1`ˆ "¤ÖÞûû¿ÿ{>ûÙϪßþíß;®Bô+õÀ'vu>¼é <öbCzœ#JQ3p„¾§ü<Ù½R)Fs«ÿ{ „Å(+,h þ’æ{œõ\ŽœÈðÌá,ÉR– 1¶·5°­µaÏ”R«ÊLM{ JxJƒG¦ªByà·?ÊH¶°U[O+-Ý.:H]Ã:¾ƲºBÇ•Wj”¤Ž­½öŠlÝ¢`˜Œ¥ÖFÚ×â¬Mã®Óˆìèe(ò<ºð—XSQyžý‡¿d÷}›V»@‹,YƒÂºÚŒ/¿ü²ºé¦›f=P¨‰ Îw¯apW<Îê\Žˆï£yÞ$îPÉZHŸ`I“ätƒW"ŸÎp€r^ ˜LT Iï>õ$ïz×».z¬ÈI¥Ô›>üÃÏðÂÃÙH ÙÊ¬× é1BTíiSg*‘èé"Î’À§¬+Ã÷0|Ýw1”‡®\4¯D“e°¦¹µ QV7Àª¬1]µÊŒÃ,ƒ¤ƒŸü/ŸJÿ¯ÿþÇ üà…?æPA$X¨àw7ÿ)ÙÀN©ñ\P’¼n‘× Š†IF·1" ›QŒù†fìt¹tùÖNì¶NŠ-íŒDâ4©÷’’/ñÞëαkõã¤ÅK³Œ…ô^4màà_fH¨›Ùüüˆ´ t´ÅÁºa€]]]êú믟øU˜ß:àCRã¶ÆÚ3#Ä' 8Zìícø>©’ÃFËâ}±8™|Ž“€£ÆMW×+{¾ÇÝwßÍã?®î¼ó΂k…JÝÖ±{Õ“OÀ—öusº#§G)h.ÆhY—WËXËãd {›]QÅ"5¥èV.‡¢½ƒD<›¸_ÂòŠ4JAkCœtÔ 1bÑ5ÙÿÂOÓ_üþ~xû;¡X€’ ®N J¸%pK(!)J¿µ…®æüb¡é‰áDc8‘^,N!ÇM5QjhÂN·2M0hEé³’ôDSôEb XqŠš/ Yylñ²$Ö(<ìÑg°ÔQzWnó¥?ÿ:¾“bëG?¥H%–€ X< ¬ -;yò¤zÏ{ÞÃo¼1« N—™ßÝÀššXËuq¦¯Z¢›S2¦EW"Áúûù:p‚ YuªMÓøÜß|–ó7V 8¥”:|õþ¿#½œµÒdŒÄƒpT=)3²HBâ#•¦‰pS"Îú‘, ¶½¬÷P1…%“<Ò×Ç¿½esx*Ÿ ~ãÁßà>ü¿VSJ½|ñ'ìïÏ“1âe-vÇ ÇQu€B€ò—©Ò¥LׯEÔkÙ¨h†ƒNŽõÞÓüÖOpSëˤ8L„áòWˆ%©®HÜŽÒ8ÜLß—NÑýç`å †6]ÆÎÿú'°s秉YŸ@¯bƒâ"Ào|ãê#ùÈŒfo„ ÷©¸¸«±‘M¾OK©DÄ)-j©ÛlY #%æũXœêïã›À)‚9ÁÓ_»v-ŸûÜçxÿûß¿¢€°G©}¸ú… |í哼2"è³ÉéQJR[öë›Op^β™lLåÏÚä›o*‘Åþ vÄÿ•]÷6ð=trHå ÅÂûøfZ&Í׉” º;8ðà1¬WAs4FŒ4Ûï½ëwV­-³Á…ÁeÙtýýýüÞïýÞÃ>úèäÅ pøšåŸ‚Î.—·¦¹ZHVr¤J.–çÕÕ¦.i’‚nЕHðÏ™,ßvlz ÒcŠe œÊ4¾ÿþûù£?ú#¶oß¾¢€ðœR=þ~¾öbÇFú¨úØÇ>ÆhÍc%ÏGøè*H2mÚ‚×;’IÖé)§HÂñˆ¸º_Ɇ¯©0Á‚n0‰qDy|/“áyà4ÐOÐÐÒUæñ ]¥”ìÙ³‡?øƒ?àíoûŠÂ3J©aàG½ðÍW󳢤¿Ü`aºaÙ³Þ±•j“¥èù®¦/×¾Kˆó‹<ÌÈ(Ëÿ.ÀTRþ)6Êgùõ¯²#ù ¼E”%ʺ›^b%‹t¶ö)^ýÃŽA¬Ê2bX¸¼íÿ-Ü}7´w|šhô˜Æyù—l“}þóŸWŸùÓ?áØ‘£•,zÌòÏrJQ ¸DÀ• \Ò™ÏÑT ’›§3u—Óe^9³GŠ W`Æ4èŽ'8¢k¼44Ìþ’Íq‚ª6Xa„•Ÿ•¹\wÝy'ýÁâê]ï\q@Øü(ßyc„×Ï3bD±¥…]vä{hc̬pC§ Q1£Q Ì^u”ur?÷_3À®Ø·iç%E$þ²  è×¥û’¶áfØßÌÁ?}^‡tb6ØR£7’¤¿¡µï¾uï{\w-Äb›‘ò(š)F(õ±l‚ŠÉ,ª ×àÅ‹/¾¨¾üå/ó…/|Ó§Na2âe–—*Þf` °)™¤Ó4‰¹–çq=¢^%çÊ_ÔºÞ…dq¤¤¨ä ‰-5r†Î çs<›ã¸çqœ X2D0ë¡b&ÛÁ“°yãîÙ{?÷Þ?ÛvìX1`xN©Hqá;¯òÃ}ô)­Fòº…¯Î¯KÌØd¬Yš«1³Ø_Œ'P @ç ªUŒrÜ Hp†V?›µóo®îæÒø¤9L„A$n]=w¡$1×$‘M 40ðØÞú"tvCC”Ò(I“¢®‘×MüT‚õ;®!~ãupõµ°i#¯Ðz]MM/PD`]„ ú8Ïž=«ž{î9ž}öY¾ùÍorìСQÀK•A¯X#`C,ÁºX”Õ†Aƒm“¶m’ŽMÔõ–=¨±X ¬ƒR$¬n0‰5 òºÁŠî|AÇ!ã:dJ¥(0D)ëZZøà=b÷M·°jÇÄ¥—¬@<¦”: |ï<{¤—“Y‡¢fak%aRÒ´Ú}§wˆ•[v‰éÙ‹ € ƃ´©|tlL•ÅbËï¥Ee÷Æ>n]š-ë…@÷¦ïbúAn”á) åcx.ºR Ó›/8úH\)(I‰+%žÞWþ[‰`ÞCå³> %´ñ}Ëÿq´!Å7o¦ñ–[ؾ};¤ÓiillütKKË'.–µëWê<\Þ²áÙÃY~|ì ÝDÔãŒX a,C[\Ûocý‡šà8µ³'HoÉS=¤ýc´úû¹~u»·X¥$EqÎa)óŸå'Ó™Þ†2°l“h1‚YŒÃ)Ÿ®¯ŸâÄî…¦,¤ò »²œó(ËùWûÉ%´òÏ`õ#GS üÒw¿»˜íR$-ølÚ°wôõ±*—J^í;«5ÝíÂKƒÎ#æ<ƒx6p¯57ó‘þ~^[øíxA°Â^à…xîh‘WN÷1‚Ž# J20“\${BÖÇôº©V\HPÉÏ3È£ûY4á ÑR”÷L¥4Î/÷Öqƒ–RxhÊÁ`„„6Äúf›mÍ#\Ör–ÍÆ~š8Hœ ²uwû³ñ= @*I¢#1ܽIøIÏžáÌ  g‚‰ž¦ÒÍ‹âO$SJA 5Z|àɧ³.P9FÙ^5ø‰*ïn-p¸˜l»Ù‚_5è‰óÆ• Ë%vë“|å®+#éì•k80 /Ÿ楓}œ±5õ8#NAš()QBÔßêUõ½ÓÉ‘ôÓ ºH©.,†³{´k¶B ˆqÓ'j”ˆ› ½@£Ud]³bMCž8ƒ&p” 2hØuùå,—È>y«ˆ—îÅJdˆ¬1húÀ:šòÿødž: ƒ'À7¥xÅ«=N'lhÚ?3·½Tƒ˜£ã«™J…»xÂúŠóÆ•(åî*M•÷JÝv]CÓC—7ð–‚WNÂë=9Žöœ#_îí Wjxèe·ƒ6ê~Xn–h§AuñkïZEÝıÑx¨Q xhÑ)–Ùb‹ †ˆÐGŒsè–V…a).€ù·³AWóp´ÙH¥@"Ñ}I´Ã áªÒÅØQpÀáîëÓVU'NûAÚŒ…Á¯Í €ó¿™@ó¢bwbÌiËý*ÕÁäà‰Ǥ紾çqh]€( ¨4ÙµŽ«ÝuqrĹìê÷bm»Œ–m—ѱõrüd9=tSÑ,ŠZWh“}ˆS¸à¦J”ž8Án.>ICåH©¬â4y‚!Ë%Ç]’‹ÀGÃdù¥QB£8N/tà«.Ë«.Ï"˜HíJŸ¼åPÒGÐ#4_ ùrt?ÊÑ})G]¾Ôp=}¡Ám"h®tÆ2ÉZ ئlß^õùÙ®ÿJBëP=†.qeÿìNëqN X»Öo†µ›ˆ­Ý@ûº-˜Ét™ Рɀ¢'A¡lJÈ2ø€'åx@D–39./%ðEUÄêæ*˜‘ó ¤ü>qhâi~6#(LåS›™ØÔeÃz¯¦½f%|\ó˜Ó)$IDATáãI¢aÏ`lûc{©ü]sÀ™6ÔÄ ÖLÛoª´‘Åèß·('”š^õÄ\¾gÜÚúçÍšUèf×ß§Å!x³þ¤$/tòBƒT¬Ûˆ¾j ±öâ-$š;ˆ·¶¢Åq¤NI8RLg¡ã"PBbøAgWÊÑ”O”;¶ýaÃŒ¤ò1”‹TšÝ÷Ñ”O“*ÐàÙÄ”@˜¢·Á4»j¶Œ¯•3ìß¹–ÆïïOO¾¦†zotšòT2~Âåû_Ô~€ÁûS‰8†ïq.Œlê"åˆKpÇRäúûCp›ÕŽwÁs•¾wµ #zâj&Í # èF08·­[ Õñ$R‹§Ø´n=B3ðu_7ð¥†Òt”&QR­ÜàÁóÁóÐÜB9h®®‡î{Ä݃ç {=šÖ‚’¹ò¾Uøbq6&ÛMû¹6…8ßß_èû™ `/M0 ÊÛå—nÕp®üsÁ\.·7b¦í[.£ª”OI@+¯Š^¦‡zÐ*…ø£> Y³äjüÒV:æúUQ`D?è.oùs,ý ºìI …ìè*þ Ê竪yÞÎô~¦óúB{?ßû ƒŠ¡„RO(­N¨ @²o†£­:è6Sr”ªÁu­®B(¡Ô%ö?¥‡§î)^‹ØÎC%”PB %”PB %”PB %”$ÿ?Øûí~ôµIEND®B`‚HaCi/html/Images/search.png0000644000175000000000000000744710670113276015205 0ustar fighterroot‰PNG  IHDR00Wù‡gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ‹IDATxÚÕ™yXÓWÖÇÅ:®cm§vZk§ãûªÕö±¯PmŸ·}ZµNí¨Aö­ "Ê"²Èb@Ve „–@Ø k vÙBK¢\jE8sn,:ZÁ:3õ>Ï繿äwùï½çžsïÍXð*óø‡(Ë—/ýý÷ßWþä“OT¿þúkË;wºìÚµëüŽ;¼¾øâ çmÛ¶™oذaÿ;ï¼³ñ5, ^By)Ö¬Yóguuu7Ÿž¤¤ÄÙY,(ÈgC~ndgf#=R’àJTøùúÀ©S§îiii•ôÑG¨ãÿ5ØÛ›ššÆÑh4ImupËJ +;Ò˜–šÉàŸÞñlðOÈJ (a‘L@?pwsMMM¾²²ò%%¥¥ÿ1‹-z _z8&&¦OÐØüú: fBDZ>\aó!©Rù-£PÑ35}·±ž€¼öR=ìVW£©@ …ó^çpDì`ïÞ½8’ûÐÔÒºÀãñ »óÐYlðK)z]? üø3LáÃÓÈýÉI¿ÝÒ[Ð)¹ í£· ùÆ8Ô·s ˜5BÉäÃ…ˆdð>΀Á?üИôÑ¿K€’……ER“@ ØëQ4¤W^“NŒº11UÑ6ð3»®÷^zEç”ÒŽ‰¤’öq¹Nå^»QÝy/³¦ëANm'°¸-U AÉðö¿ŽgìÁÄÄä!|ײ—*}tªªª[ss3´µ4C`Ø1255ÜÖþÉE-ÉÅm7RË;¯Ó+»¥ôÊž¬‘^Ñ=BãvI©¥ש%m´Ò¦©´¼Jˆa”CPZx†ÂIÛ ¯¯?ùÁ¿TUªª*±çyp™Æn—Df¼XzóafönYÛ(³º[Ââõf×õ d׋ÄÙõ}b¼~DO(ƒUÛ;€mé•q'‰U QÔ\¡—œ8n ”¬\¹ò Òw¿YÀÚµk×GFFvöötC,=ò½2ã{¥“´²¦›•C9õ½ýìQ_^£H˜'èG°Ækö#ú„ì9¹|‚PˆÏYµÝ¢ôò–ñœéˆdS‹ÁÙý˜›™æ’r ëЄ…¿I€¡¡aB+º ™ ®@æï⑱‡´²†QVM»˜Ó(ì)lu6÷?F~÷ˆ¦ú:9‚Fag^CO³²EGgO_Šg@`L:ØÙÚ‚žž`¨>ùkóá¹0kþN¿É««ƒ0:¤ãwaâÎÝ)·a4»¦MXØ$¼VÜ*j+iío+~‚’Y6„¢¬[HÝ'§¹¯­°YØÊô´1¹£QI@IÊ÷€035o¾ù¦yáÂ…[ž™ž+3æÅ:^-в9UÛ! ’U-Ý´rºHo[aKSQë¯CÚ¶ˆdÌÔÍ}28„&aG lb7t·%ç–ÿOŸ¨4°¶¶c::°nÝ:W4eż¼þúëïP(”ñòÒˆHÉ„‘[·¡whd2¡ ¶ŸÅëlÍkêçç·ˆë9Í¿N~s?¢¨›ää"lAß uU­ÝQTÖ´ß•tpp?Ɔðù矗¡9“É@™À©ïV•ành6Ÿ—RÝŸŸZ7ÄNå ²ÓŸëþ•4‚¢Mêc °©µ¿R#–S;›Àí)Nçò$怉­èÓÍ›7³yÎÙØØÐhiàÍzy#\-æÝ fñ«*ûs’k†³’k†§ö<ÖnPV'aT-'±j+`Å”õægÕ¶LR¨ù`dí8# íýëœàNIé„•UæÕäd8™1yÕéºȨçÆpÅ™ñUÃŒ„YÄW#UÏ@voHÞëøÊ!FB®ã*±râä5=²¨›SØÔùðbjX†cÚG#"í]7¯I|äÈ‘¤øXp½ti%p™]þÐ7WY*¦_©Ð®T?"ºR‚ ?‰ YÛŠ_¸\1$‡;à -ª|(%,¿½‚ß;^ñ¹ gaZš$™EÏK)Ÿ}ö™]ôåH8.W²á£¼Òªkƒ Eiáå’Bw6#Ï@~æ™_N +# ="¸d0!¾¼³µkx ¬.¦‚®±9®LÕ¦Þ|óM—y¹)üñ!?__ ë Tðc‚{bi«OVW¥D’\:òbgÊlЇ ¾yâøòkÒÁ&Ñ(è¹…ƒž¾>üýïßÝ@—ÖW"åí·ßÞròäÉÉK¸ ?u!(‰àž9æLmdúqãŠGbŠ%±rFžƒ¼ÑʹP8ëW(¹ì•Ó›Ú%¿Ï¨¹ÇN8ƒ.ú?&Ô4gϼ)¨zÙáÇKýý|À- Ì}âÀÞ?ì.ñ=²ú¢½ $Q¿0òäíÎφ3Ã0©#=r#¢ËºÚE7ï€ã&™€¦ÆÒÑœ-O[V?w5ºaÆ=ŽÓ¾@Ç3,=CÁêBÒ¸m² Å-w(Ü#_ê‘76Ü ùŠZ†$Ì•- :›#¤ßjU'²ñ]£°oßÞ[¸Œ0'§8OÛØk_<çS‰åË—o:xð`û©“Öààæ :îQ ï fnÁ`x!½×$VpÕœ6ämÉó8ž9扜#X)j̱sxÿ¬yƘ«iê@ÀÙ\Q_tãöðÄ}ðcÖÂá“>²eƒúáC°sçÎÛ‹/>…¯þY,~¡90»¬Zµj+fçk«ãàää :.a ã –çÂáø…„i³à¡qŒ Ñ”*ö5¦{§HÜS¥®FÔ}¼6ÄïÌSýèì‚Öá±[÷O4á,в8-3×þðÝwßÁîÝ»IølÀ5ÙvE[I¦äo>Z\½zõ®½{÷Öš›™­-X:{ƒ¦KxŃÝÅpˆdM»QËoyå6Š|8mm~×CJ{©¼áŽ‚Vé€@|ón†ÉŠ)œM)= hY€¶–¨©ªÂ¡C‡d`Gººúƒ•+Wj+Fì–<9 /t6Šîôùþýû»Œ ÁÊÒlO]‡@Ðt‹ ÿ«p&‚ÞŒ Hä]ƒb¡ø#?‚`t¸½×É‚cb1¨9E‚†¹=èëé‚–†:¨¨ 'Õäh‘œ‘’Ó9ÙÄÈÄ&£ðå—_ÍìÎ~Of QšõçŠ.çïš››„ÙíÛ·óq.üq½ß£€)¸#sÑÖÖö¾£™ÕÌû•€9æ-Œ@i8s1ܑܡˆf ^ ŠB–›‘õŠë…¯š%E¾øÃœÙ«È?yIG†ï{èIEND®B`‚HaCi/html/Images/HaCi_Logo2.png0000644000175000000000000002236010677351441015602 0ustar fighterroot‰PNG  IHDRh,î,)¯bKGDÿÿÿ ½§“ pHYs  šœtIME× 8dÄM= IDATxÚíÝwxTUþð7S2%“I&½ÒH!¡„‘š"ÒDPY eEem`] î³"²€º (‹¬ ¢ô²$bBè$!•ô6ée~DÝý© É̽sgæýüL¦6QÚsvVaÆŒ'0oÞópuÕsP8~HÇ”)ÐÒÒlù•Ë,yÀ¨¨QèÚõ6IÐÇ' uue¨¬Ìãh²0o ú4ôz?‰—'ššêP^ž%x[ÁÁaØ´é&N¼*•ŠƒÂÁøú@¡P 5u¿ÅmÑk0™LÈÈ’,bŸ>ÂÛ;Уɂ”J ’“@§ó–äùEEÝ''¹ m$&ÆÖ­ièÖ­;„›5k1<=}¤Ð¡P«¥yy'“ÉѯßCËùMº¥ôî}Ÿ`·MZ‚Fメ‹`ÇŸ<ùa|üñ~ ž N¡P`„¤Оža’.¢Vë¨(~«n™Ëº„„$Iþ<…“2™K–¼‰7ÞøJ¥’ƒ#FŒ“v@«Õ®’/bTÔ(¨ÕnMf O¶‰ó´ôL&Ç»ïnÆìÙ‹9èÿ ‘v@ÛÂòB¡BLÌ]MfP©\áïßÃF.=-û¥ÝâůcôèÉô+>>ÒhkÜwÚaaƒ¡Ñ8¢:ÉÝ=X°G·-­±ÑrÜsÏCœ9Ó UU•K; jl¢2™ÜjO8Ú­ÖÃfε©É2˜8¯¾º–O7”ŸMÚ]Zzņ.ÓuQ$æ{UÌU^žmö1‚‚B±zõV~!H7uäÈnitee.êë+ÙSvÎV–7ššjQQaÞ¬F©tÆÚµÛáááÅŽ§›Úºuƒ´0ñ‘j’ŒK—öÃd2ïM3f<¨¨8“njË–‘››)õ€²²Ž¢¨è{Œ¬ªºú:.]ÚcÖ1 /<ñÄó,&ÝTQQ^{m‘0W«–> ÉdBJÊû((8Íž#«¨¬ÌÅ¡Co£µÕ¼—×,X°®®¼gžn¬¦¦sæLDM0K»‚lyÕÚÚŒ””÷:={NRÉÝ#Hx­­Í¸pák\¸°Ûì÷AGDÄbÚ´9,*ÝPm­<2gά A÷$ÌÊJÁõëHH¸=Ø£$àeæyœ<¹Éb{SþùÏ+ —ËYXúMW®œÇãOÆ•+Â.ç ¾il}}%RRVÁÛ; ññ%ÿ¾²-eeYÈÈØnÑï=úö‚ää;Y\ú•¶¶6lÞ¼¯¿¾HÐTD 蟔”\ÄoÀß?qqàîÌÞ¦N«¬ÌÅÙ³_¢°ðŒÅ=}úY`úÅD³{÷~‰•+_BfæÑÚUˆýA Ï °ð,‚ƒû {÷qpuõcïÓ-3‹qöì—ÈÍ=!¶Ótw÷ÄwÞ#©Ï\]]‰ÒÒ"TUUüøSŽººZ³o!¤›kh¨ÃõëùÈ̼ˆÔÔýhl¬ýÖùè&俦#/ï$BB’УÇ=|²nª¹¹gÎlEfæQ˜L­‚µ3vì}Vß}»  ©©ž~ééG‘•u ÒÙ©œ  ŒiS²³SQXøzõº]ºôeЯ_ÀñãëQWW.x[C‡ZïM‡§O§aÍš7±wï6´µµ²ãɺý“ÆF#¾ûî俦¡_¿Gx[ýüüôéãòåƒ¢Ì UHJ*úçÌÉÉÄÒ¥3qìØ!Δéÿ‘Ô¾ð?àðáwlæµ¥$l8;ö._> Zhõî}4­¨ŸóàÁ]˜0!ÇŽÙ$ÐPQ‘ƒS§¶°gÜ… »‘—wBÔ6cc{‰ÚÞ¬À¬YãP]]Á'Ûh¸ví;TVæ²wTSS-.^Ü-z»áá1¢µuàÀNüå/ÏÀdjc‡“m4`B^Þ÷앟ÿ=š›Å¿¥)22V”vòò²±pá †3Ùj@ÅÅçÙ;ª¨è‚UÚõöö¥… ä²Ùv@óÅÿŽ«ª*Ï*íº¸¿+ýO÷6Ùt@76Ö°w”%7zíWW½àm|ðÁ v0Ù~@›û._²]MMuViW¥R züœœLìß¿ƒL¶Ð丄|”ÛšÚÅ'‰M$E'O¦²Ä€&’¢'RXb@IMQQ øð1 ‰$'?ÿø® b@IPu5ïë'4‘DšOšH’ªªÐÄ€&’$£±šE 4‘µµñÍuÄ€&"b@šˆˆMDD h""b@1 ‰ˆˆMDÄ€&""41 ‰ˆÐDDÄ€&"b@šˆˆMDD h""b@1 ‰ˆˆMDÄ€&""41 ‰ˆÐDDdK@Dæ2uðßwbÉ8ƒ&"áƒÙÔÉÿŽÐD$‘Y³¥ÂMDÄ0šˆ;œö h"’p˜2¤ÐDD h"â왳h4šˆˆÐDDÄ€&"b@šˆˆMDD h""41 ‰ˆˆMDfã.( h"âb@C”MD i?šˆø h"rðPu²ÐqˆMDšýZ*àí‚% "K…´‰³e4Ù׌š~—8ˆˆÐDDÄ€¾J¥†½OD h)ruõeïw’Z­gˆÐÂÑëÁ¯4:ÇÍ-ˆE b@ G£qC``/Ž€×Íww4Z`11£9:(.nd2ÞIÄ€˜Á‚ˆˆ¡·ÈÛ; !!I,Z={Þ OÏpބߡÓù`À€9prâº=Z¬Èä0àüâë&´ZO ü$œ]X "´¸Ôj=† [ ŸhãÜÜ‚0|øètÞ,Ú:”J5~11c “ÉY]»Þ†aÃC£qc1ˆ¬€_Çÿï_+™qq㔀“'?AYY¦CÖA«õ@ïÞ÷!  'ZZÜÝÛ/ë¯_?‹ŒŒ(/ÏrˆÏ­Ñ¸#:z4ÂÂó*‚ˆ-m~~qðó‹CMÍu俦£¨èêêÊP__ “©Íæ?ŸR©‹‹' †®N„Oœœ¸êEÄ€¶!®®~ˆ‹ØØ±,‰†Ó%""Π‰HJœµˆ‹†^½FÃß?îî~psó…^ï ™ìÆs·––¦ŸŒÆ ÔÔ”¢¦¦%%×PXx —põêqÔÔ”²È h"êˆî݇aܸEˆ‹gguÇCCá …Â ÓyÀÏï·ŸÄÍÏ¿€“'¿ÂÞ½kpýúežMD7ÞÓ¦½Š=n¥½ÀÀhFcܸ…HOß>š‡ÒÒvDp šÈî9aòäñúëi¢…ó/%&ŽÃ;ïd`äÈ9à{ØÐD„öåˆyóÖãÞ{—Yý\ÔjfÏþ;þð‡wx;'šÈÁçÍN2,Z´C†<(©ó3fæÍÛ¹\ÉNb@9¦ñã#!á.IžÛàÁ÷ã‘GV‚Ë h"‡£Ñè1iÒŸ%}Ž·ß>cÇ>ÍÎb@9–Aƒ¦C«•þîë>øâ↳ÃÐDŽ£{÷¡6qžNNN¸ÿþ7À¥4‘à ëc3瞈ÄÄqì44‘cpuõ²©ó:õeÞzÇ€&r «MoHHôí;Ç€&²uuU6wÎ'.eÇ1 ‰ì_uu‰ÍsDD_x{weç1 ‰ì[NÎY›,F577૯þŠyó°mÛ_ÐÖÖÊ¢0 ¥¥²2‡­@CCµYÇé×oöíû‘‘Q,ªÈzõêƒ-[vA§se1:¡¡¡›6-ÅŸÿÜ×®ýÀ‚0 ¥¡¾¾G¾‡¦¦Z³gΛ7ï„ÁàÁ¢Z1¤7nÜ gg‹ÑI™™'°ti_|öÙË,ÚºÚÚZ‘’ò>êë+Í:Ž^ïŽÍ›wÂÝÝÀQeeÉÉ#°zõÈdr£“ZZš°eË X»ö,Úz22v ¢âšYÇËøðÃÍ\ÖI“îÅ“O.f!Ì´oß|óÍ{,Z|9¸xq·ÙÇyå•1bG“Ä,]úbc{°fÚ²åE¾mÙóv³ß˜6yòý˜3çIŽ$ R*•øûß7@©tf1Ì`4–#=ýK‚-žšš"ž1ë..íO´‘tÅÅõÄâÅϳf:y’0 ETZzÅìcÌŸ¿¾¾~E÷øã áíÍ'9Í‘——Á"0 ÅÓÐ`Þ®ÇAA]0oÞ"Ž  Ñhðøã Y3Øâ&´ höüó¯A­V³6âÑGçÂÃ’v–ÑXÎ"0 mCBB?L™r? aC\\\0{ö,D'ñño´Íàå²mºë® ,Ù¾ºƒ<<¼0vì$I“ÉdBMM5jkka2™oO©TC¡¸ùòŽ“ T* ÑHg³Û¸¸žîŠÜÜldb@Û£{ï}J¥uvò¨ªªDZÚ·HKKÅñãß"''••¨®®õ4.nbb×É P«áâ¢BP'BC}ꃰ0_(•â?Š=~ü=Xµj21 y™l©©GðÞ{+°{÷N³¬vÔ×7¡¾¾ ¥¥58uª}öªÕªpÛm‘<8þþ⽿düøÉ hb@Û#­Öýû­½ôôïðÌ3ópêTºÝÕ²®®û÷ŸÅþýgÑ»wWÜwß@¸»»Þnß¾Ið÷Daa>4I¿$쀆ˆ¶¼±nÝ{3fˆ]†ó/}ÿ}6–-û7>'J{#GŽæ`&´½‰ï-J;O>9K–<ææ&‡©m}}6m:Šÿû[4ºã¢¢bocÕªwðñÇ:l÷í;ƒçŸö Íää˹ºG h»Ò­›°’rË–-qø:¯ZµŸ~ºQ°ãëõnHHèËM h{âåå-èñ-š‹ÖÖÀK/-E]]€³hnèK h»âêªìØß|³/žc‘TX˜¿ýí ÁŽ?hÐP™ÐöDÈÝ¢ß}÷Mø„\‹÷÷d‰mOäraž|ËËËÁ±c),ðo̢Ϟ=ÍBš¬çر£L,ÄoØ·ïkÐd퀦ߒ–ö-‹@ hbIQii1‹@ h²žüü\á†Í­–ˆMVTUUÉ"Ü@YšÐd%ÕÕÕÜNè&Xb@“µfÏ,ÂMèõn,1 ‰3D)2~Š-´Læ8ëu}ûÞÆpv#GN€Áàe“çîãÓU”vºt‰G—.ñ,Rh•Êq¾,HLL’ÔùÈå †³ cZ…©SgÙä¹ÇÇ­­#fq°H? õÓ IIƒ Ñh%ÎëÖm²ûpV*•¿úÿjk‚·ûÀsmîn''úô+b@Ï„^ïÃt–r@{y9΃z½wß}ÃYD¾¾þ¿º3¡¦¦JðvýýƒðÈ#OÙT­ šŽ  XÑÚsvÖ`âÄ?1¥Ð~~Ò»%©ªªR°cÏš5€õnÒw¤pFƒž=~ѿ墴ýôÓ¯ØÌ-w^^]pÿýoˆÞî]w=‰àà88:I´ÁÐE’““-ر{ôè!C†[ås©Tj|ðÁ¿nÍù—W-W®œi†èŒ+6Jþþw_ßp,[vâß›,“ÉñØcB¡P1 ¥D.wFïÞÓ%ùÈç‰i‚ÿo[ÎUÔÏäãã‡;9ä}Î3fÌ‚^ÿßû{/\øA´¶cc{aõê/ Vk%W''î¸c.Þzë´hwoü–ˆˆ~xä‘•V½²´zX&•“Q*58ðqx{GÞViée_ìÐãæfÀر“;'wwÜÝ=°gÏNQêßÛ·Dtt¬C~•JggØ Ðéô?~ºh퇆vCRÒ0ìÙ³ õ’¨‰Ÿ_$-ú£FÍ…Baý~XX¸¸¸ãôé=LœA[K@@OŒµ >>Q’-Öþs@ð6~xfÍzBðËÇ3fáë¯"00È¡/!çÌyññ½ß}wMMM¢¶ß§Ï|öY* ºÃª3EWW/<ðÀ[X±â bc‡Hªîºk>–,Ù/¯ΠŦV»!1ñˆ‹›¥R-Z»™A53f||ü=·Ûo “ HI9bñc÷ë76|‡šó›·š9Ü E&ÃÀÉØ´éŸ¨««Eß¾ƒ.ê9 ^˜4éA$% EVÖ%æŠÖ¶F£ÇĉKðÔSŸ".n˜doô÷ÄÈ‘³¡Vë`0øÃÃ#îîþhjj@cc­ÝŽO'k]7¨ÕnˆŽ…°°!ËÅŠóçwáìÙ/;üß=ûì+X¸ðYQÎñ³Ïþ…åË— ?ßü_Ø€€ ¼ðÂë¸÷Þ@¿¶uëÌœ9 Ó§ÿ/½´ÊªçréRŽÝ‹ÔÔ}HK;‚ÚÚ‹·¡TªqûíÄÝw? ½Þ˦ûîâÅT>¼GŽlDSS½]KÑZ£qGtô d•`67 ãã{ãðá“¢gcc#Ö®]‰•+ßBiiq‡»·oß$Ìš5&LáŒùw¬Yó.^{íE;VµZ#‰sjmmEQQ*+ËPQQŠŠŠ2ÔÔTÁd2uúW^¯ï/¯D(•öõ´ns³%%ÇPSsÕìc555¢¤¤¹8vì Š‹ ì; µZƒy $Þ³ÑÙ€œpüøE„‡GŠz¾&“ ßŸŽƒ÷àðáýÈͽ†²²ÆŸ»P.W ,,qq=1p`2Fçðk̵eËÇ0Û0iÒ »ûlõõ@v6PWgß}èå–ܧ÷ìٓظq¶mÛˆ––fû h¹Üqq1 2™\2Øù€,ø^xáu‰ÌšQYYµZWWW%.™/Áǧ›Ý|“ ¸~(,lÿߎÀÝ ,}·nNN&^}öìÙjûíãÄÄáâ"½5.sÚ`ðDFFÔj5È>ÕÖ"ßÐ!ˆ–àÊ•öÏãh|} . 7o^‡W^y õõÂV ÛìœÐ«×½HN~J’ál®ŠŠ2lÞ¼)fÇ4Ûÿ ­­À¥KŽÎPT$Üg¿ï¾Yøì³Txz ûR'AºOŸéˆŒ´ïo¯]û.SÌŽÉd€­_ ´¯;;²\ïXŒŽîO>9//án»µx@ûùuGXØ»ïø 2pàÀ&™S«-¿†)–¦& ¤„}X[+ì—¢‘‘±X³f›`÷[< ãâ&:Lç¿ðÂb´¶¶ò·ÀN99Z­mž{U•ã|!ø{**„=~¯^ý1{ö3ÒhÆC‡éøsç~ÀÚµ+ù`Ç”Êö[S];ûßY´Ðžx⸹yH; }|¢®óßxcŠŠ®ó·ÀŽgÑ..–½§V ޾öü¿„oC¥RaܸiÒh­ÖÃf:­®Î2/g¯®®ÂsÏ=Íß;iζ֣¹òö_--â´sÇ–Ó¥EW¶]l¦ÓÊ˯YìXŸþ/$% £Îuè_„ÖÖ6?~§O_Cff1jjÚ§q:>>nˆ‰ D¿~ðö¶½ý&åòö™´Ñhç+V(ÙÊX1‡J; ¥ô¤àÍÃ9••9=æÒ¥ …ääùKpâD&>ûìÊË`UUu¨ªªÃå˅عóú÷ÄäÉIÐélë>6¥²ýþh[X>P(Òÿ8ŠÓޝo ´—8êë+m¢Ã22v0ciÆÃß‹ÌÌ+5ø››[°aÃa¬]»ï7Ãù—ÚÚLøöÛKxõÕ/•UlsŸW¥jÿa(1 =±üž– h£Qú7^ü€ë×Ï rìÊÊrÜ}÷¸|ù¢C ü’’ ¼ùæv¤¤tüó–—ñöÛ;pìØ%›»\Öj¥ÿ¤¡­Þ(ƒAœv„x·Eúúõ ´¶6K¶£ª‘ž.ì#Ú99Y5jRRÛõ ?thF‚ìì"3®:ZñÂçŸgÆë3­C­n_“–*ÐPÝÝÅiëÛo-¿ã’Eº¥¥ùù§$ÙQmm­HKû5‚·UYYŽ{î…O?Ýh‡KÍX¶l &O¾ÙÙgqþü.³¹gÏi¬\ù ªªlë=˜Î΀«+PW'½ou:ÛTÝ|}Û×ãÅðùçë-~L‹oyUYy ¡¡ƒ%÷…aZÚ?QP ÞÖÖV|õÕ6\½z IIƒáââbóƒýüù Lž|'vîüâçoiée !puõ5s¹¤))áî®EP§ÍÔ¤¾¾S¦ŒDBÂxzzKjæ¨ÕeeŽÎ*•0¯ý-Û·ÿ ›7¯‘~@77×£¥¥þþñ’é¨S§>EVÖQ«´}òòÒqIDATîÜ|üñG0<ѳg‚MôŠŠr,_þ'ÌŸ?EE¿ÞY¢°ð {A¥Ò™9vZqêT6®]+AX˜/´Zé7þ,ìß¿ Ÿ~úѧÏ@(ÒØ×ï§/ÇlåÖ@‹›ˆˆç ÂââBÌœ9VÙÙ4¶¼<2™ÞÞ‘Ví¤¶¶V?þOdfµêy44Ôã›ovàÀ=ðó }7–Ϊ­­Åºuïᡇ¦ %åÚÚÚnPçä矂¿³Cº}ÀWáðás¨®®C—.^P«¥ù¬õ›o¾„5kþö?cí?عs3ÂÃcÐ¥K¸$ÎQ§k'‡#…´\të&Î¥¥¥ÅxøáÑÈËËæJ¾°?&f ºw'+<‚ÕÔT‹o¿]Óá»Å× ü 'NL‚Ï]ÇÚµ+ñÑG«QUuëošQ«õ2ä)¸¹Xp¨@ÿþ‘>¼;¤ñÍW^^.-z {ö|uÃ_«ñã§áÙgÿ //‰ô)Ÿoÿ/PR«Û—5ĸË&//øÃ(dg w'’à[^y{G¡ÿ‡¡ÑDë¤ââ HKû‡äïË ÃÔ©âž{¦!22ʪçb4±k×6|þù¿pðàÞNﻦP¨0!!I?Ǩ¨ôëÞ½»ÂÅEüoÀ²³3ñ׿¾ŽÍ›7 ¹ù÷·[Ñéô˜2åQ<üð|†X}¼ÕÕµïIh¯ïéðôlß‹P.Â×_›7¯Ãk¯-dÇuQ”J ""†£[·‚>ÞØhĹs_áÊ•ƒy³r³»!>¾î¹gÆŒ™(ÊHII1Î;ƒÔÔ#HM=‚ôôchl´Ü[e‚ƒû">~\\,ÿ…Ÿ\.Cd¤?¢¢á‹/¨TÂ,6šL&?~ ~ø>¾øb3Z[[:q¾ Œ1£GOÁ°acàêj½GÝM& ¼¼}B1^"$½ç¶Ç#GvcÕªWž~T¤d1É üüâàïƒ!jµÞì5ËÖÖfTVæ  à ®\9€––F›p~~0`È?ÉˆŽŽ½…:´¢¶¶µµFÔÕý÷ŸF£ùù¹ÈξŠÌÌ+ÈʺŠìì«0…¿ÝÐÉI†ààD÷…^ï/³—»š›ëQ__…êê|œAaáhn®G@@ ºv Ghh8ÂÂ"еk8|}ý ÕºüüãâÒþOçßù模¡‡íÅ®]_b÷î())²XM”Jgôé3qq}Û ‘‘Ýáîî WW7èt®¢-þ´.]] ÔÔ´¿à¿¥EúK 2Yû#÷Î΀›[ûP·666¢´ô:rr2qôèìß¿—/gˆ÷Þ¶´¥¡ÜÒ–Z°U¤PE!¨0 PÔÍcÌâǶdÑÄÅÁa\Œš~ÌÌøÅ0ÛÄq˜,&H™?œ b@eŽ9QÛ`Ah7 •Ú–Ò¯Û»…Kùl…*véóëí{oÏ=ÏyÏ9ï9ï B!„B!ÌjD£1SS?ЉYJ§3IÒ=8hìè¸ÛÖöIºý”Å`°““7K¥+P”v»ùùó†Ç•ÞÑÎÅ›Äb)†a¯«®Ñh¸xñLYÙ7AxfFD$&*ò1Œ‚Ðé, z{U~YA³²ŠÄâA‡-&Èúû_˜Í=ž™ŒŒ¼ÊÊúŒŒ<.GQtöf2Ù+Vd«T«Õmž™)(:=âãs<æô‰ðp~TTÜøyJlRÒÒsçªqœ?s· cȧFju}W×ý1ƒ¢‚ þÏŸ œju½N÷`vìX9›>CÕÝnwmí•ÚÚ+#žOkSÓO<^ýòåŸp¹â~I¯ÖÒRi±è=?““Ó““Ó¨§Vë@EÅ‰ÆÆ:“ÉøZb-“VÛI’äÃq1Ølfæá­[çm6K ¼Y,X°¼¤¤NgR3YYŸffn+)Éõ‡Ã4³‚â ~ŠZ´(Ë[ûá^<]¡øü¬@b¢¸  eÁ“IŸ¼¡!Y¯T^=}úxooÏÔÕêf—ËA£1ÆÌK¥)ÈBàp 46~_SS¬TX´èUaáú¤¤¹Sh(ŠH$óöìù²¡áÏÔÔ´©å?}zçðáU­­7¦×åø$@Þ»wA§kµÛÍÛ·ïØ·ïÀk煢˗«y¼¨©_{þ¼¥´tíÙ³»ÝD€³ËåÐëŸ@d$~èÐ1ïGf³©§ç—Ë5lu‡#ƒÆãqPt(D"ñÎ{ŒF›¯s²¡áòÆ%’%M£¤gWÉÎÎÇqÞpOH=Z\QqÎnQ+"bNAÁמøær™ …07w­çц ›++ñG»Ýú¦š¹s¥Ôøúõkåå'½µmK0Ì»vmÒF&“3™¬Yîȼ]¿¹¹ÑçûFc¿FÓ>\`”G½-¥Õ:àa>_Ä=q^Þ:¡P¬p<üøñ²`mêq<¼°p½H„%ç½Í ÊcSXøVµŸ&:}‚B(&&¶¨è¡0Âçß»' •Ê>rvBÂÂ13ɼªª_'Ó~Lu-“%±XlŸ» “É™&>?~ü¤VÛE·lÙ.“É©Ÿii+•ÊÛ‰tŠ=Á{#çp"öî=:ÞL\®pΜ¯V6'2Òw"%…ÍæÉåëââ2©›F—ËVSSLΨ(~kkuÉe2½R*¯êõ=ÉÉ©ÙÙù>/L-«ÝΤ L’äýû wïÖ[,&Ï™€H”’˜¸ÅÂGoçÍïƒÚéªJ ýÍ›ÕT‘2ŠŸ/[½ú 9º"¥¥'wïÞ7mg‡c‚ÚÛÁ2yóÈ`€\TܩշnÍîïïªS¼\3×s1ì ›Í¬RÕy ÒææÆÌÌ옘¹“}I§{Y^~£¦æÄÅ Ç8 N'ŒoZ èíŠ9AƒáÜQQÂîîŽGŒ%À`°cc—z¯ Iº[Z.™LºáÖÌqõjAry›=* ûúzËÊNœ:Uévóm6g[[·FÓÍårÙ ­¶]¥R‹D±£mF_W•|>°Xâüùï´ÚÎñ1€H$éIºç²ž \ju]OÏ“ñâ–-{O(¹Ýn«u ««³½]år¹MLÌ z”v:mË“ÈH”ËÅ1 ëëÓ75Ý!IR¡Ø¤Plâ󣇹¡4šE£'9>#ÝnË¥òxÁÀ€¹ªê‡Û·¯Oijs8Tf³ÁhüB!„BáŠÿtüaÈý9%`IEND®B`‚HaCi/html/Images/tLine.png0000644000175000000000000000023111030471134014762 0ustar fighterroot‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœ0IDAT(Ïc`@ÿј0”0¬"¬„ah+a$.,XÂE[6% aC9\‡ UÍ g¾ÿIEND®B`‚HaCi/html/Images/HaCi_Logo_String.png0000644000175000000000000000377110677351441017053 0ustar fighterroot‰PNG  IHDRübàƒ- pHYs  šœtIME× &5[°*ýtEXtCommentCreated with The GIMPïd%noIDATxÚíœk¬]CÇçÞ£Ú†"õª¢Õjè“ËhKõAUQ¯bqûAø >ˆñU‘| ‘(¡Š²HHEÅ£n[QUEUŸ^õ*-IëÕöÚ1wÛûtϹçöÜûŸ4÷œÙk¯™ù¯5³æ±NKÖº¥ÀXòÁ× Þ2  ˜!b6äQj­ë<œ4%t-ZEÌWBÁk$ÊÀ«Àf¯l0@?¿üà={Ø| ŒñÊâ÷KÀ‘Àxà ý›O“ϵê%`0˜ L+ÌŒ‚×í¼ˆ™™ˆ ý¯ÅÀxSI‰ß5JLòe¬uÍÀr`\@;N>FŠ˜ž®²N.S SÕ„‚×íД,1ßèÇŸÒ»Êü¡Iʨc­ÕHÒŽ/|§T]«= SÕlß‚×Ù¾¨–(x-ÐE|© ¶@.¸‡ïàû}­u¥Ä¾ 8ع»;c­›<¢ÑëlóZ=dÿïè,®¬uw狘¡Ôc­»˜.bÆí.Sëª=àOÞ³Ö}ï• :é_¨k¿rJ@ß=ˆn6ÔQ¶;cwó‚eÀž]@Ï`ôn²GV]õâ¢S<€I|ß ¼\¨g‚µ® Øâ•P£!&Š˜í ÛÑ(^w #€4Rµ®0([ë¦/eV×!ºgÖU/.:{À Œ1›êÔžq9H;˜CtŸAlîöUò¶× ;A'ª2pð=pp¾WÞfÍÀ<þÍEˆ±˜%bîóÚÛOeÇW9ÛX&bŽ‘m0¯“ù‘V;€Áº øX ì­+½^ªË—ˆ˜uªc%0 xNÄ\¬e“€ZÿL`+0K'(´ž×‰’º¾ËÒ»®t⼓ùÚö‘5øÃãºâM&8µ%8mVdÔ•ÆÅÀ*:­ÀÀP¾::à?¬ã`_ Œ1?{FÛx8Ê“›«Nù9°èÜ•¢3Dö+5€QÃÆX®ÆLR#4ë`ÿøN ?SÆÀ}Þû€5:8ҢǺdÉëràm¢{üXÇŸDwþÀS=Ù¥DY™†(Ñçt-_ ¬'»øH¹½3Qß&`Ö98¿Šž.·7ꤰx¸è¡“j2Ái°N8³Õ?æeÔ•Ö‡¹:ÔI`Žú ¾Êt¬ôR#ÊVkݪ„cNЙ²EÄìT­Ë¡Ã“ËÙ¼²"f½µn<°Ý3øE"æ=oß;88ø8A£ÜCúÎö”­Ì™IÆèýw5„È6ŒW³ÙZwQòÀ7ÀYÀ'ÀsÞ`œ.b^PîVeýÅ:n°Ö ×>Çe5ÊÇ6Ø\(b>ð„Wáe¦é åÒZwœŒ³­hQsçµ÷C¢ÔfDÌMu¥õᤠÍÊåɵðÕ•îáó¢lðIP#œ×AYDÌïþAUl\}V1Ót9ݤÑèoY·Z#鼄Ú߀þºtÛß!²äÕO¾ú 8CĬПk¼­Ç 1w¥’}ú³Š *Ày±óê³:Ê»ÒÈe<ð¶Õè%`Ïׇd`M­+¥©:•ã5ñ6%”¯2Ý•GYn­[¢3c­²1–ˆ·‹6œ¢Ë©¾¯1¿¦È=¨û«e:ëúízCß[_ƒl£yñ¦ˆYéÉ«íîèr–ˆ˜:ð~=¹Ìãi[†YÀ¨zÚ£Jy.¾ºë€Ï:„ù±ƒ²yžÅè­{·ë€û«œìÞ®{Åsž^y?ÝëÎáß…È6š×v«ðSßæ2™S襄®™À-U¶¥·¥€ò\|•SÒ_?î—Õ(k]¯zYÕZ·WN¹>A+À Œ¶í£²½uÉ"‹µ®gÀÌ[¦Š˜×« ê²jŽþkgp=¹[‹lƒyÅZw`µ[_3ê;ÌûIn-ÛWÄl ±µnoó[–ž@.ãåtK¼"¨ÁëÖ¡Üš8èÃZ×Ko9RëJëƒÚ£9ñç&=Œ£¾ÊÖº›êâ´Yë~ñž-žÕƒ™z öQD\y°ÈZw9ÑÏqO$JøYç-oúè©)À«Öº+eÿžÉÙ–w•³»­u²–ºÇ?:ãy=üsÊŽye»¯}ˆþO„,ŒÕ“ì4,Õú¦Sµ¬ÍZ7W}뙀v·—¦èÙ?Ë—ô„|‰æ,¼<Ж…D·+”·)2O%0¥ÕUJéÃRµÇ'Öºµž=ÃõýA¡|•µ-BÉ}Û!D×1G%ؼØÁ1ÑÕO5] tY6T—c—]YœÄ“~b5lˆì_j¨<ýjžÒSú¬;ñ!úwT•¨°¸Z¿O m4¯Ûˆn'vO§¬jV¥¼û¼Ö·¿~Ÿêâ ¦ÝYÈÙîaêizzrù¸¶ëj]î^ð‡«t‚¬œ¼MtýVîWÞ†W©kkJ. ºý9‹ö·)q0iÕþ‡òU @ (P @î‹9ź½·3IEND®B`‚HaCi/html/Images/tmplMgmt.png0000644000175000000000000000770310512363354015533 0ustar fighterroot‰PNG  IHDR@@ªiqÞgAMAÙܲÚzIDATxœí›]pWvÇ÷v÷ŒBŸHH2{í5Ø€Áe>ŒÁìuâµuv_SIUR©<æ1yHU*yÊË&®$©JUª¶²xm/kc{ùXcÖˆl`mÀ` 2` +,„,4ú˜é¾'÷vOϨIH¸¨ÊžR×´î½ÝsϹçüÏÿ~ ü^þ‹ªQ¦ï[îË|ŠÆ]·”jÄŠ×M@]F›»]˜†ݪ±ŸQVÜ_*•Ž)¥î:/D¤â^DPJ%uèСŸ>ÿüóÿœưFÉ”,äåžçù©wÞ5+_¾D*ïû¿þúà~à Ö5%Ë>°h¶»ôU—._ícó$ÿÂõáÉþì/þúO:»–n­_ØPHšsðoÿ毦ŸÌ2€žmê¹x©W_ÞA> ¥@)å.{ŸtY@ĸOƒ1Vc¬*ÆUŠ€$J bÜÿ¸‘Ê÷Àw–/]üÇôâ3A.ÿ¤ÖڀаpÁ¢úçÿ`F˜µ´µ6³cû&ÞØ½Ÿ|ÿY‚À4ÚFkÖ‚R*‰Û(2ˆX…1öS*ïÅÄ€1ÆÇØrg@#€k¡ÿÚõR), /Z¸èf.ÂèîlËì÷¬ pöìeFÇÆkBŠRŠöÖÅüd×{üàåøžöÚyöˆr5DbÀ)g/0Ldc=ÄDƒk›xˆ5F>ŸO¼+ö¥”ÍáÎ=j§²Yà«+ר¶õÑ©ÆÀž=ðÒK¬^u?Wû¯óþ¾xõ•øž‡Öqh”"qñx%eÁ$ž!Æ:º‰œ!’7`b|‰ Úר eIŒPxJ%š§Á3-³Šu­ù|0õÊùä5äó¹œÏòeìÜñoîÞG†©7¸wÊu8îŠí¯Ã (§V A+Ö€Ö¶ãΡ¤üvìëm{R„ÊöÛyÀjYÚÕÁÎg7ó³Ýûøá+Ï>J—]Ñz±ucý‚¶Ö⹑/çyGïEðɱé‰Ghm±´Å˜ˆ={$®oÝͳ€8PW—cËæUS+Œþ^Ȫsråêýì=Ðë/í@ë˜a§¼ËbØ´q×Y¹â>@%\!Íl™pæóKD¥¨œEDÀµW Ð* ÿ™&˜5áÉ”2›©)K»:xîÙͼñö~¢(ÄCd &tŸ.÷+ ¥¹‰SŸ'Š"¢È†!ah Ã(²™Â8±Á†RìUI*¬èä2À eiwß{v o½s‘ˆ(!@öG|”Ö,^ÜÆ™³+ÒW¢hŠô¤ ;ênô•¶—Ô0À¬BÀ¡PŸêKF,ªƧ}GSSOm~œ]oíãÕWvZÅ$èçz ´´´pú̾s_·c‹Ž";|(‹Ž*—/1”ù@<Äj †!_\è›M"0¡ «.CXóðC¼ñóý|ÿ…m”‰ƒ8ì3€ÂÓšÆÆFü-K»–”ÑIÿ­UÓcë–(41óÌ^™•r¹€µk˜ZEpþ$dÕ¥¯„eËÚÙ{à0¯¼ø Å9ægìž`Ø´a5×Yñà½.ïyºìƸ “”Ç@¥bÜÜIœ¥Ä1ßÕÙÎŽí›xkÏä‚ÀMŒRüßDˆXº¸­•3ç.%mâÉ ˆûPÓAë™ð­ ý4cèîjgçöüüíär–#1ˆ1„Q¬h­iokålï¥T¬K¢tÜ0==vP"ó@„Ld¸60”¡‚Œ:©ºI4¡º†À˱níÃìzk/¯¾¼ƒÉ„õ„RD]>çh¼` ´4·ðÙç_²¤£•‘Â8­­ ¶o.â\P¦Æ:v‚¹ 2†Ã…©o!0\Èxª¼˜QŽÏx¦Wɺ|ž¯f÷»yñù§ÄÌ<Ï*v[K+'?íE»%Ë4ˆ³‚D‘ø¸Rfî >ßýîÒ©Q§ŽAU]–ÛߪLDhkmä—óÊ>ÃÐð("BËW~_NX·îaþ÷è©d0Lp| ΃ÉL²=àŽ`ÀtŠÆe1ùIßw.icûSëٽ皚bŒar²˜~;žÖ<ºæ!®]J05óSå…ªHi¹£ 8ÝÈgµ3ÆÐµ¤íÛçíwÒÜÜÀäd‰ÈD¤€­ªi¯ͦÆzº»ZÙ÷«^xn 7nXÔ°íb\Eq8xtuurñ«¾d•KÿnMi~Ö´§ioožª/°¸qÚ˜e¦ÀØÜÜ@>—ã—ó½[)ŒŽ³¨±D]v@§4˺»M¾Ã.)´Öxž¾óÓá4MiÌO–"BG{ [Ÿ\Çûû>¢®.ÇðÐH‚eeí=¶öa7­¶(¨ãõÃ2¯ 8u+MfXvëgE„%‹[ØöäZöp„º|ÀÀÀ0Æ’…ŸÄÝq[¼v= ^<­µQ<«˜œ(qô·g+,Ç1…ýÜ­Èijo‡Ì”c;YƒLŒï“çqÇ}iÃÂz~ñþ!žÞ²ž‹_~Ͳ{Úin^D’ûEaÇÜ$TØ­,ÏryŸu>˜t,™‹G.FÖ>€DQ¦b%Ê+¾qYÚíSË^©gâ-3ûür‡9ü?'ØøøZ®^âæèíÍ,X//¢g@ƒÒÙݳ2€RŠ ð¦Æ­r&ñ½dÖ9%¾õÌÊ´ ʸ~J°¤½…­›×rðן°å‰uŒÜãÂÅ>êëëh_܈R‚öüò~‚Öɺ@µÌR9>´ªÀ«f»[”¥ïk•‰ímM<òÐr}tÌf#£c|q¡÷r¶÷+ú‡™˜,±Sê,™Ý¾@êZkDŒÛ“SvF)DY†f]ÝLm—”Ù\‡DÜξ[%e1¤ËâvAðèª9rô[ÃÐQ(–BFû¿aèÆ¾ö|]nî›ä£žS)0+Ç)#ùèDy–çHM²#RA‚’º*LïIì)qü›ò=®s“ëWÐÚÚÀáßœäñu«.${ ´&Œ„h¬H–ÌÈî ù¼Ý¨\„LAÿÈ“k·8ÕÍËW¾O¯õg…Cj!4#Îõ^!2†¶Ö&6oXEÏÑÓ¬[½…Û‚Ÿn·4À”ã(5˜±›#"³Žù™â@ö³Æm¡Ûiôæ pä7§éîlg²89õg‚é‰ã¯‚áݦ¢sQÞu')·6ñä¦Õ|þÅÅ ±qa²r[Ȧ²å]X{âæF VRUFF»ZÏ–Ëì}V»ê2@ÅÄ©Üçî®vú¯žï=u¦wa}}ž ÒY!5C z¥F)ÅØÄ8~x¼|D%1cà¦BwÀe)ƒ®Š_ÂÓ3?Eôù|,¨K”öîÝ;JFÔ’iC ¢³ÉÁçô´Ô–eYaŒ-c’)­qsrc$UæÚIu;íBl™u{ rJi&&J ~3 ø©WpãÆèøñããNùÓüXbZT‚ ˜ÐpøÈ©ä+©©¯óHϧe¢b¤<ŽA3þ×1<*¾£üšA¢ =»ÑU__GSS#‹›xj뚊>øá‡ì¨±¿)£éí òd·°}ûÚ Ã¸kÃʲÇà«A-¥4‡p£¢ÍT·¯«ËáyAœï§ôùàÁƒc)”˜k¤?§‹[ɈÛjÒTýÞä=íª•·îïáû~¦òÆÙµk×(Öí'™!Îh=`¾^-z›–jå}߯@û´\¹r¥ôÚk¯ õõõ•°(ö€Û7@&Þ¦Aæ‚öÕFˆß×××WÚ½{÷È›o¾yóÀñÉŒ"ö7Bgˆi%Ë(fÅú·¥|Llâ{¥” –Ž;v³§§gdÏž=Ã'Nœˆ_ä>C`¸ üŽi~-v+þwÞyçÝB¡ÐdŒñªŠ¥'¦kW bå=Ïs™¥RÉ Ož<9ÚÓÓ3200¦¸E§ìdÕ5\Ãþ\n„€`Ö' ¸hæîø©PéØ1Ð…©+N1˜µâ²Èqw(‹@òsØô•®O_3’Z+fwß/&+eÆ þ^¦‘ÿÞ*JFÑò¹:IEND®B`‚HaCi/html/Images/HaCi_Logo_small.png0000644000175000000000000000777310665316126016721 0ustar fighterroot‰PNG  IHDR@@ªiqÞbKGDÿÿÿ ½§“ pHYs  šœtIMEדF*2ˆIDATxÚí[yxU¶ÿݪê={:{ Á˜’ (²JÀÁ7( #€ úÄ!êuD£*Äm‘Å7£â$¼€ìB !B !}ë®î®å΄®Þ–÷}ï~_}_wÕ¹çÜû»çœ{Ω[Àÿ7Ð[t tcL‡oј³Â0`!‚Åbñ“e¹[ˆúùùÁÇÇçHdddQ]]ÝNGü  ÉEFgÃÃÃËÃÂÂÌ*•Ê·µµUÝÝ• 2‡‡‡ŸÐjµZZZò¸hÚ´iQ±±±Ù:Îê.¢%Æ ››››ÐÓjúÄOŒKLLÌ#„¸½ÒÑÑÑy©©©ãܘ‘‘1"((Èäªäää¼]»véo¶½>ôÐCiÆåÉßwß}¯{,lðàÁ3\âççgZ³fÑc'T_ÇÑú:Wé###³]WHHH^·ÐÎÍÍÕ{yy9»¹;rjGŽÌkññ¡5Ï?ÿ'Wè§L™2ÊE­Ìì¶ÊŸw&(...ËSþæuë™||¨ ÐÆÁƒºÒ§  ÀȲ¬S&L˜àÔîg¦Ö©Ë6k=àïßÌ×57ƒ@}úÔà†µkïsÖgèСµF£s‹KII©í6NÅÅÅyfû¨‹NOBÉ ºiS†+}}}}ÒŒ?ÝÀd2Õ8£inn®ö€ºì÷Òt•—t×þ\aádz Àéòò<ït\}úô©rFÃ9#(--½àŒfË–-Ý^ý†ÒüÀ¨ –ÒN÷õ—.éª?Xõ €w«(¸hCôÑË̓k©*®Â̰͢ †ü¸äE¢ó?ë¥së¯Tl³|\ç׫W¯K]å¾õ[ËŸŽ›Ø!ýtâÏKâ|¶p¸M­ñ½•Sµ……]ï_ñòÇ^mxæÄc–ÿbI*6³°dPB@AÚ´eÁG`(Ge°u—€“ûAn¿ÿ'ЦêN@\kÕ”j§³.ßÕ¬ x¸} w÷(†ÅÉèD¬$ÛGLB™1éÉ@hJûNÀp8^c~˜2ö¥;ÍHZ×¢é‰OãâŠg¿·×—P züøSÞÀé–¶×gö¨àìþƒaþg{:l/‹ß ü&éuK(/AîwÓíUlàÕ’<¬â*ˆö‹¡•~Y.g$öÈÀ{Ü´ñƒÀ«”ÓOîb  —@5€o¡q²Œ9 é±÷¼ˆìð±téœSŸI,,«øü™¢ÃÜQ ºjddØ‚W*>²pÑšk´J×IY&BÿþŠÕh/IÂLÝ6 ¨¡4°Y# è†(¢tÄ%^–ÄÄ”òŠ™¼î4l@Tµ¨ìW ¥ÒÈÔ^Á/,XgŽˆà•žÇˆ"ÆqÜi”W˜ÐBhq…2´VHNÚJäs {' É€ÍáSPQnv•ytR¶Õ œæŽ”eôï¦3ìYùòPµÍæPÜié釭ÉI‡óIÂìn:à üðô³ƒõ'Çúº¥U÷Y%8Xå©‚ïšß\6^ÿëños‹€fIåíßÀÅY›-}ãßó‡ ¦¨T»ÇŠ¢âÿìxÞËjAH]ŠÃcì@ÿë)­ÑÑ]åkº÷èxÞáëò9ÖÝN2&NŒW¤r²„ˆê‹M Ifúóç{4øL–$ gYÔÞ.[}ež®®,¥èWVè¶$2Ã@tó’ˆãü`.øíd{D4b‹Š†“« ΀Òc PÊõ€£c&š*þšEyÞÒ•¨½i;2C;l^••IþŸ}–Á)œbO)6æÖ0’ã×rÝñ%Å…ÖúÊVc#£±K3ª.:;Lm¿(¢h>•—ô-{÷Ìð*)µ[gôEø¹Œµ0ÖW½s¤¯¤x°á¢¨Æ†2sšG†÷À¨¬=f—µ¦.;kzÅc¦¨T„öOHNÚJ†o?/8%œ[îûZ ‚½ Ì3§( uW®ÿ_–m³õï_ÙÉç›äد¯°›ÿ~…yÇ#ÔÒ:$7ƒdò”÷;Ò<¬Þ4Ñ(TrYGyzÍó›EññŸ8z™ê¨•Qªÿ ”ÿçq³J?9˜~â#Y)ÒõS¶¤¤Céé7dmOöbçÄ©,V%-ØRÃÍþ¶Ö:Î]ùÏe¬i ó€•gølkÐôík+&F÷’S’““ã»:g×aP•Øï{}òÕœ|:‚þEOí—°/Jj|pVúr¿F¹åF ¯Þâöä—œãsÖU©é­‘ŦÇbv €$I*ØÎ[ß –ÅjÁ¦˜/ŽÖ¿=3DØÍ(œ)àµAËO›÷R÷@Xßܛ˵‚¯«}³³/°i„³#é[SšŸ]Ú._¾Üþ›ðÃBÛAý·âPi©C‹tMö·œ³P|ß ‹zñ¿w{³-ÉeGv¥Å]B_%¯pÖÆ £˜Œ0QéáÂO¯Fë^syÔjµ ×~ß˲˜Üaò"à —uuõÓÕB÷ÓïÍ—ÛìhÕEýç)éÀ‡üŸéÓµkÖ·¶bGH8j7 i%X$`V°-a¬nªËqÀŠ+¢ªªªô ðÃ@ÓAýEBP,Š`âô›ÀA*RþÎïô#÷狺ˆº–ZµúE¥Ü_û•/ÞTc™UG©Ý ÿóõë‚¢.Çßo ”Ü€kBz˜9q¢.µ!f—í,%%%%ÍR©¨DH§£+<ËÒÞ„Pƒ^_“““ãï Ïó”ê3Ϙ7ìµÙ=:síòÚc£ãŽò/·¬Ì³Ò‰5”¶gW}ûöÝ€¾Æqíc±2,­œçM+혺(‡P‰èV2++kÄêÕ«·754è§s,ÖP $ßPâZ©Ra± à®»ïÎ;flêêœO\BøÃ ó7VaÕþVQtƾ²}½YôR‹ÕBSã…þml1a¶F£Áå!Ñ(9c³‘óx°’ Öåcó£Æ,™H†ÜÛè€/¾øÂøþ»ï>ÝtþüÒ¾‹~&I2Ô y¸È0XϲX+Ih5k{õ‰~ëÙç2¾š4k–ÓÍJ6”ñ¯ý\GÒ¶pãYˆK@1Ôû'|šò|Xûb5»õ–FÖ²?6N›öOæþ‘ÇI` HÐv ýû÷Ohjj2J _‰RD ~¢âb†%2 œ õ’ÎË ŸmÖhªŠ‹‹»Jò6€r¥èìû ËÓûêéÌ33¤˜g 2d50ÍˆÓ‚Š±"H]ûwbRèfH½ãâÍe-È—Ö_T€: BLDÝ Å[õ:øØ„n%oØ*v €¡íb‡­‚Ò^‰w'ÿl ‹I`¢û ïÑ?j/?*5Y¿Bl˜ö.XX@ˆÒµo(ì—¢ºÜg(†¶}}Bð°QZ 4N¼~LÐóöo—³OÌ™s­IEND®B`‚HaCi/html/Images/userMgmt.png0000644000175000000000000000372310474157163015541 0ustar fighterroot‰PNG  IHDR szzô pHYs  šœ/iCCPPhotoshop ICC profilexÚ­Ž½JÃPGÏmEÁ!ˆ7áâ .âÇÖ1iKjI²5É¥Š6¹Ü\?:ù>„ƒ‹£ oPqœ|7A"dpÁ3ÿþü ±âuüncF¹5AÏ—aË™G¦iÀ -µ×ïoäE®øÁû3àiÕëø]þÆlª>ÍL•)ˆu ;³Ú‚¸ÜäH[W€kö‚6ˆ;ÀV>œ¤òÀ1aƒxÜaÅÐp“Ê]ÀµêÜ´ =6‡Ã+7Z­–ô²"Qrw\Z5*åVžFf`UTûªÝžÖÇJ=Ÿÿ%ŒbYÙÛ “ºÕ¤'æô[…xøý®ŒïÁ `ê¶nûp½‹Íº-/ÁüÜè/ñÀP=ú‹æægAMAØëõª cHRMz%€ƒô%„Ñm_èl<‹XƒçxIDATxÚÄ–mˆ\gÇÏëÙ™$ãnšt³›t·Ù´l 1©ú!ꋦb´(’?d©´øV Òø¥XS‚í)*‚’Q)¦¾¶J±Õj„Ø$CÚ˜¬›dwɾÎܹo3÷Åwf³Ê®ÝÅ-}àð<3Ìœó»ÿsÎs®È²Œ·s‰…Bˆ7ýÃ÷î/öY=æú¬8ŽÁ9"Rÿ÷n_nàvÜÙ۱ߎY ÖdÅ"¥utt”‘Y8V/ïþfã—«pàžRű\±–ŠÕ`t†5àAiÍZÊëº(¯íÄXE}üÌñÝkÃËË•lû;ã‡ûº“Šà‡?ø¡À 2j5·VÇu=¢¦¦¸aÇþgØO-Çï²N|¥°?ƒÇ*åŒÁ¾IV ~^(p½˜jÕÇ­yÔ]˜¢pë·W Àùdå¶ïáO²©o?´¿¯¹!Õª‡ëz¸nìÆ¾ã_,l[€HTü(V®ô²y`ˆá‡sçû>‚‚Pàù)Õª76$Aƒ¯ @Ø#A~$Ø2ð^ˆŠÅöÞÿ{îû©,ãG·Þ¤Zõ¨U}¢¦ÂDåÍ|ëå¡8§Üñž»°R@ÃiPRóÁ»?Á»†vpêäO9óÊ $Y@šV ƒ «s}ýã¥>!¸²gß—øÀÎí MËô‚]3=5Åì«<÷󧘞§\Ìú¿öã`äÿnÃoýÆ âø†î>ˆêÐh›Ͷùt½c-[o§êF4b1¼Tð×@ÔäàÅóç M r¡Ñ¶…@.¸×è\?øÛÇOzÇW­ ¾ÿb}$ŽÂˆ¤‘‹j9HÔ‚¦`æ HcšÍ4Y®_½’ÉúÞ$…®^ââh‚ ÚIH‡j­>¹\Ÿr%ÓS7^OB7ÄéU„4…$†$ °PÎÙzݯ¾uãøÁ'†ï»÷sÅÒ:HD  $ªÀä«#ßýá/~ðø3¯yKÆq{ýþ©}ÙPÿŠ¥NP† ¨3:6Î¥Ñék{>»y%ï+JÀÑï¹c²ãcs;yñbçÏOóçK[ví'íù4ŸÿꡞU}#êï?´ký-Ň֬1½›z:z»{:zúòGéÝ´€KW&è¿€šðì¯ÎR­zãs¯þktæì™Ó“³.<úäŠRpôé*==vÈuƒ{×ßâônèv0JcŒ¡P°ÃC·“¥cãÓÜvÛFŠËõ±)J ƒã„€¹ªÇ_¾üÆ…‹ã§:7&ßxâàsÿàèÓÏo;ý—ñW„ ŠÝ=c°Ú`ŒÆMѱ ë¬5X­ÐF¡•Bi‰R-“cåRm4qìG/Õýhfó‡œ[²^~iìg[ ÅîM­à&äX‹ãäf›¶-c0VctnZ)”R!ˆ¢˜,ÍRòÙÏÜU¾>ÚøÉ’ѱ¿«\u½ Šµ«sçí@Æh¬c)X“+`4Z+´V(%R"¥ÈM仂8NRðÚ?FèßZÞ½$€”r€VӒݶŸ²eÇä*Ø<%Z+´’H%‘B $ùÞ>Ë\ä¿û'×®O3;ðèác}ÀÈ"b[’$8N.£1Û‚h«àØ…Š´Ÿ^"¤@ @„Èoe! M3^ýû&nÌ’$)B”TóÿQ¿>yéîžÞò|ðvNÍAX'7ÇY£ÑZct« •df¶Îù×F¨{aK)EOo™ÓšydѬYë ¦iŠRùµ^²  kB«<ï´®4MñýˆÉ©u/@J‰ÑŠ8QóÅY,ªÞE¬£»šq<ßJyk)ŒQójXs³0Ñ(•‹˜$)aÔÄóB&BŒÑ$I‚ÒÝLPJµÛ³sQ-EQJI–±B抴@nV½"ˠѸ9ú¥XkȲ”$Uıj©™û’J"¥dcw±²(À®my÷s§.Ÿl4E©$F)ô|-äg­F«%W’¤ÄqL3Nˆã„¸™Ÿ›qB³Ù$šÓ;ßßµoÑYðv¬ ‚š½Ëç,¢IEND®B`‚HaCi/html/Images/add-group_small.png0000644000175000000000000000242610704777333017012 0ustar fighterroot‰PNG  IHDRóÿa pHYs  šœ/iCCPPhotoshop ICC profilexÚ­Ž½JÃPGÏmEÁ!ˆ7áâ .âÇÖ1iKjI²5É¥Š6¹Ü\?:ù>„ƒ‹£ oPqœ|7A"dpÁ3ÿþü ±âuüncF¹5AÏ—aË™G¦iÀ -µ×ïoäE®øÁû3àiÕëø]þÆlª>ÍL•)ˆu ;³Ú‚¸ÜäH[W€kö‚6ˆ;ÀV>œ¤òÀ1aƒxÜaÅÐp“Ê]ÀµêÜ´ =6‡Ã+7Z­–ô²"Qrw\Z5*åVžFf`UTûªÝžÖÇJ=Ÿÿ%ŒbYÙÛ “ºÕ¤'æô[…xøý®ŒïÁ `ê¶nûp½‹Íº-/ÁüÜè/ñÀP=ú‹æægAMAØëõª cHRMz%€ƒô%„Ñm_èl<‹XƒçxQIDATxÚl“]h[e‡ÿ÷¼çœ$'mj>F“¶©›âºéª›ÚÑMpŠh‡›2½Ù•xá2óÆá®ÝAÄ!“é¼t¢à¼°n±£Ek;ÒØ¤i’&=éi’¶9_ïëEÅ)ì¹ÿ=WÏ®¼£¬¶XÊ´¼/“ e8ÔÑÓ*”éæ@*ÿÀ¤/KއQF†8¤¤Ó¸Fò®År[å*ŒT:=ð{VR™uYW•o|qWÀ+¦!BžScáÐ}øáëóØp— dða«)ÑeîŸ+…®ëúÝÉDóíw_CôþÝH¦wàÛ‹ŸÁó̶n‚®!!%šDhÿOpìØËFdßÉ.dF¯_>2"°ë8zz{ÅâìÊšŠSŽ'2áZª®ž%¢Rþ#˜a/L"¨ 10˜ù´˜›xJL.ÿ…Z¹4™Š™¥µ¦ÿy¸#ˆ@Ç.ÔÌü#]Æú VåÀÓ¯Âö$–W]ÜÎÇ7ŒÎx÷Bv|ÿílÔóì)aM<ÞjéOcƒyï"ZË“álÖäºê|ïù΀1™¬Uš^1ßþ¥€Hÿg΃‚ çG/ìQRü•ãûHï…ªЕz$oív@`¶-’ù…µ`¹jU·,?´ý`AÏÌLÀYoâùáºá¹uiq耥 Z¥v -AЦøzCðÂüæôÞC\š½ .:çûñÍ_atügg1vëW¼=‚üü$Ænfฆ’«´üLN‚1}cMQ=Ýq<„#„“§£$´pƒ‡ß‚oôaj® _¸Ü1C¹™•öÉ+ã öãü'åCÅìu1mõ6V7]ˆ¨…g>Ù§.­u£Ê ¸*¡Úpðó|¸Ã]sÛÖUM“ñE”˜op ßDˆj›{N¦£â·U,OUP¯FPž+at,‡ìô¤¦ªôFRIEND®B`‚HaCi/html/Images/vertLine.gif0000644000175000000000000000147511030471134015473 0ustar fighterrootGIF87a çÿ€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, "í ´` À Úûw0áBƒtÈ¢D‹^4x1 ;HaCi/html/Images/add-template_small.png0000644000175000000000000000133110704777333017463 0ustar fighterroot‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIME× #ü™…¸fIDAT8Ë¥“;ha…¿ÿŸq&»Ù<\wWŒqQDФ4Eìlì¬D!*ŠöŠÆÊÂÂÂÚJŒFÑBˆDA±PKE,±\Ž!îHv2;¯ÿa‘8±±ow‹sî9‡sÅÍ›-ÿ1.@6}‚ »ÆZ¢ˆCœ¢¾"J4qbXK5Êvý–±‚aÎU^¯\œvú8î ZòB#¥ƒÖm ÚXò2LcÐá@[22äÓétÈó)eé9Š¢ß`ÇZ›¸Íf“F£ë:¼_fË€b¼Qgbb¢Öj5îÞ}ñlÄ¿¾óììŒøsÿ7 -2,ÆfçIEND®B`‚HaCi/html/Images/vertLine.png0000644000175000000000000000020111030471134015474 0ustar fighterroot‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`@ÿј‚Q%£Jàf` òk•KIEND®B`‚HaCi/html/Images/add-user_small.png0000644000175000000000000000240510704777333016631 0ustar fighterroot‰PNG  IHDRóÿa pHYs  šœ/iCCPPhotoshop ICC profilexÚ­Ž½JÃPGÏmEÁ!ˆ7áâ .âÇÖ1iKjI²5É¥Š6¹Ü\?:ù>„ƒ‹£ oPqœ|7A"dpÁ3ÿþü ±âuüncF¹5AÏ—aË™G¦iÀ -µ×ïoäE®øÁû3àiÕëø]þÆlª>ÍL•)ˆu ;³Ú‚¸ÜäH[W€kö‚6ˆ;ÀV>œ¤òÀ1aƒxÜaÅÐp“Ê]ÀµêÜ´ =6‡Ã+7Z­–ô²"Qrw\Z5*åVžFf`UTûªÝžÖÇJ=Ÿÿ%ŒbYÙÛ “ºÕ¤'æô[…xøý®ŒïÁ `ê¶nûp½‹Íº-/ÁüÜè/ñÀP=ú‹æægAMAØëõª cHRMz%€ƒô%„Ñm_èl<‹Xƒçx@IDATxÚd“Ëk\eÆßù¾33gÎÌdf2™d¥±¦M ©$ÕT]U¤FJ­»(¢€€¸²nD7âÊ…ŠÁº1/ ¶Bz‹Vm›˜„L2÷ÌÌɹ|Ÿ‹Y¨øÀ»{.ðð¼âô±ÿ†6r Åþágòîú[Ù¾ìt2•ó¥J®ØÁÝWýÀ»†>BÄÿ 4¤’Æ>>}ZiZó¯ËÈP–Ç©62·n^¿q²ÕÚ^³,€%€ÿœ€´[ø@XbþÔé7xpâ$«wjüöë]”ò'´qÞî1{PIëŸôð!DË#áÁ©§Õ‘Çæùjp„ëW.¬mÐ ´ÜØUHÙÊèQ‡M×b-eÙ0j ÍvuÑIxñ‰Ù©bÌ6ŒO?ÎØ‘£¬,/߬T믇Ag7f+”¨ûéž“Ö", q*3ìýUëÔu·Ql—B ¢Èþ#ãèÕŽï*¯_0‘ÈÙ2axssÇðÝ;¥Mo£¿fyÅ­BwÎOËbêPm0FãûÆð£’Z„Étp"ðo ®dàÍ·V»Ò_êŸêþöd¥±¾RµYR&@€0‰ýnoÐëD"aØnöâ~7†H*"‰ÉÒÿgÇN"òºfÙsì±÷|llîÀ;}¿Põ=e˜ÙúA‘´ff&0!‚ "bÀZ+63”f)ŒsìœS6Ë6KtE\s{ea3lÖ›·eéÒæ­«+q¿‹ˆ±Y–9gÍ_šÀôžÕ‡žüÀ/L8òðž¹ÙéÇŽß»k~÷äØìdµê{ZA ”ï1+b‘b­s™qÖd™Ë’Ä:ç¤;Hâ^Åqb5f£õÖkí~¿?pÆd6“4MÓ$Ã^欵ă°eñ t[ÖæÚÙå«ç¾›%ñàMPžÏ?úwñWö>þîÇï?àø©Ç™ž’ÌõYš+‘v’¥ý N„I1+&­‚ϼ«àO{ r¬XªZˆ\œd‰ÇqKnc³ÖìE½nÖObÓî ’°?paœfõn˜¶ÃÒ$¡4ì¬,]>ûâÆ­«+çLš$oHàø;Þûþ‡ß÷ôÏÿµcóêŸ}â'ž>yaqãöV+ÒžÇDJX)Öž&O{¬µf¥)ÍÄ fbb"Ê@ h&f4£ÀäûJ) rZ±ì«%"B˜Ùt̃ò™(N³ôÚÒÆú3ß9uùì…kV^EkUt[«W_~þ/ýS“¥ÑkÊ•âþþ§>ËÊwO½ó­³‡Ý{x½¢X,hÏóH+Ížï±Rš”RJiÎ-¯@¬ˆÑðÀ"’ȳã£Dœ@à$¿[WTð4+máÒýãåJœ$É©óWo\]XZ]]ß굺ýŠöôD·^»ñ½¯þþjm®ž×£Çgö-–ª36K¶ÛQu¼Ÿ&•J©¨XA{i­Yë<+MJƒ•"ò1+"&Òbʃ#7;´"ñ5ADgBi&00NÄÀZ'ÖAõ,ŒsY&"X­'­@ŽÜwäð±£÷î'1ƒ—Î\\üús/^Už÷VfuÀ­cÓ»îa¥bÙV*• T(úâ”R¤3³bVŠtžµ&b&Rš†±b&"ùšh¢Ô·RõêB¢Öš†’Ô"É€$s”f"ÎÄDHD'R_Ã1¬Û5¦Š÷ïÎì¥W/l¹,*776­­µE¢€•¢ÙƒÇŽfi’TŠEorrjŒ™Ø™Y1³b¥4±R¬±ÒDZå •ÎÿgRL¤¡à-× _Y‰ùüíßÚrªì3M•5vOevÌ£‰K© àk0`$2‚^â¤Ziõ¬kt2®w ]\ÍÔ‰Ó5/kŽí?0¦ÔÊÂÉo:k¶$9V4¹{Ït–$©ž›™™špVHåøI)ECã“Êÿ&ÖšIéÜò¾"„ž=ª¯žîysc;:Áÿà=E:8çÓ8\èXH; bHß b ”EdÒNcœô#q­ŽuÍN–}³½¾±R¼'¸ų†Ü°¬ÉÜʵ‹‹÷½ý]÷õÂp; ûýje¬J„‘ëbÎÁçÀóK±Rò‘8Ðo>ÛjmáO~pŽß1_ÂDi¥ ¹¸(®Y„)$ŒEb#’ÀXsÈ+2Ù‰r!ŠJ1ä½÷Ξ»¾~õ䕦ÛzuÀ€Ó "ˆº­ºRÊïõbS«m5§§göc0ŒÐÜ·sç× °&‚¯ˆ¾p²å7ûà÷“ûøÐ”Gõð‡ç­ÝhˆX år4Ê›D€¯  ˆÍ sgĈ8 8ç܇ß½ëÜ­5t6Va’Þðd'ˆ»ÍÚª5YDºÝj´XfÖ¹“)"b&p.Ä RL(hEW×õ¿ê_|xšòè…eçÎ/[É2ˆVDžÊ(¬Lä†2"šŸ™$N‘¤ê³J’ÔÜ®÷” k ˆm0;Åt¶7’(Ê<ߟªo×[ž†Ë¬"&&æ\Ö™†ÖÏ¥’)Šh¡–ð™Gg |½#8»dE|¢»+·žxHDD(vT‚À$H­HÙw¨ÅÆl´ûlÃí[Â?WNÂN}éÊ+Ïú~aßêúfX[_^+— (ÏEÌ VfåÖ‡""•g* ŽÆËl[àû·­e&(/GEw_{Ñ+Ö¹ÿh˜ (ϹEI¬ 5ŽX.l[[@+1ók˜(x"€sR|71ΟçϘdvbÂS¯Ü®ÇI§¶†Í‹WÞ@Ôkך›«[A±4S¯7:N»­´âÜb ÑýNm“Ç£¯™*F’ º='Ä;€F?9S dÇídÐp§Ñþ£ÏÂAài'¼°Ùu¶vùlÜxݎ̶këçX©ÙAœp¿]oŠ>cˆxx Cÿ¤"‰P €ÄX¤™ç$Ê ˜ ÆÅ8qþ³ü¹W?Èÿ²vH=¿ökLˆœu7[6‰z5ôjËwûÿhÕÖ/D½NŠÓKË+›a«¹MÕ©=‹µXÕc««WÚmörÌTÖLU_áþC3² —ÝÐAsúÊõ2ü–Ò #!‡H8óÜpxeTœ¬æ.'€µ"UMT“la³çìæÕWa’úèhŽúweâ»× ×m¬\<õµ‡žø‘´"ÓúÜ©M©®e©›ð±w¢È©ðß÷iï˜G»Šš&‡Rž½û§qäS¿ŠBìæ©Jy¹¡ ”(ˆ¬¶à2ƒÇ€A¶ ¡<['©u6n±¥›Í^Ttn¦Jǽ‡Ò­ê[+¿¬öèÔþ£ö?Í ~ìN€²Ú[XŒÔM+s?·X™9üŠS?~¤ŒffÔñÙ¢š/y©ýD¤Ñ‡[1ÍLО½OP£õ"Ù]Š€†ÎACº2‚° ˜Ю/"ŽäH¡ß 0îñžùíñÝS·ÆÃŸúU9‰Ò~¬¸¯ÙÛó–òŸk8òaBj>ßÿÄ;öó[~áñ»~êølµÌJ×Cç.vÝ d.¯aò™(¡³W^¤ç^ü—šf>û¹» ̉õ—pâ © Yk ¼œàÊßF²«‰º÷ LµŽdºÁœU ¾ðû‚s¢“ÂÕðU}GWìýÒüÑ™úµ'÷>øôÁÊáÄÏÜno-tÅf^iª—åÒåi"Å E"D̼'yôwá;/ÍC5#¤“&Ϻwž© †ÓåBá#!®ÿ¯ÀŒƒ_(‚…Bw»¶«¿>ÝÓˆ’ýí>’Bø˜Í ä›i°þÙ‡ÿðï¼ÿž‡Ÿ˜*Ì·an~ù•¥â¥ÍÁÖdµÂ*Mh°ç€(÷Y’—l∈˜åر»k7~“yo&0ðáïÄÀ¸@0’g5­0±wUª¢²^wY?ƒK$„&Dìb°fhÖ€‡ñ!´÷€/¼ï7~øÀñ'¦ 󿾄g¿¾BgUþ§¬ìžÌ&Mc²çûe'$;àÉQ>Ú`uÀ½çSîò•Ï“ê eÕ €…wb€@;± $çqÌ®ÍBE ‘º©K!8r¹`a ˜ðä;+€_8¸k¢¾m¦8û|—¿±‰s¥@yþÔä1Á8‹z·×R*7šÄAÄ$°ÝŒRçr±‘”'wÃu\ìàîdŒJ©Q¡¤HÁƒNÖAMÕÐÕ]X¶yé" ƒ\n1¡ádÖç'P,ÎU¼b1¾j±þ?xI3+([©6\¸)q¥­0lMOïRŠ}ÀX#7/ŸâοìÁ"Ìä D“K©6ônõwŠÐ- US€Xñ‘Ñ ŒÆÂV,„$*'î`' !#°ãüHÓ0‘Å=@y¨EÔÿbÍ<9Ïj q1 (t)êîÏ2 —nžNMZ/.¶ŸãEó§ŠgÚDïLÑÝíÀ“>J~%·~?€j*èH#Pú¶TÒÜ÷E÷0;âú£@‡;`Û!hÙ"å¦hèJ©!~ðƒ~’îæ…Ž­ÍW½Dw*C[[¢¨=/LüÒéß ÷ôËï/£2Q̨O(¶‹¨.WD\â¸g0lPÇC‹ÞÑݹ܀Ø2¬X6Hý)§H8AJ)2d°Öð•ôdH ‰gj0ƒÊí†Ù˜«JUyÄãc.,o(“UwO~hmù…cØ̼Á¼7ΰ€!ƒ`•ÍÉ$Šv¼S:ŒúÊ‘kÀ™ËVBD…)R1ÈL—ºá[=@w23\ÊùTZÎ,aÐ^i%Wî™)ÖÇ<5mܰë)øFÆ&ÖÑØ|Ëø‘G·÷,¾·×Xøn5é'ØžÝÆDiN¹Ðd‡] ݱ0pÊÁ°Î†éÇ Ÿ[€,Á‡^ÒƒëZPGÀ‚xa •Á ‚ddÊpIu;ŽÆO4¯ßüŒv$€“Mô;ëÑ ž^ ÍÖC3j&‡@’R±…ºó”¨‡Ÿøäú÷¾u鈄=Þ®l#ÐJ~)PÉëq£ 2室P …ÝÙî\›û¼² …¸€b§ó²ÃT÷þpÌ?ø¹ªw`½ìŠ ryÛ¹±¥çþÃG¿ýÚjT@âõ¾ˆ~ëgW:c+û'üvUÓ”ÍE›Q)µ\à·XLEÍÎ÷Î}0¾Vÿï%i Zºò 'H¼‰Ÿß6È8w *ËeL´&‘îJQð ù ¸;$8eø~€4èaWðÖ¯œûüï~òM½#Ã(CZwÆÇ{ýéÝ×ÛÁÚ#»ü²b8´±33WQÛ|45©<òÁOm¬þ·&5Õë íÚH³6µà ) 1 cF5½×¹öD¢Ã5¥ç”Ÿ–Så`˜L$¿†$´Ö@)CÔÛxüÍ¿¥Ìgx€f‹vó ZÛ,uéZä¶”µ‡`š߀¯bf zÖ5ì#ÿ¥“&)²^†lÁÀ?_Bõô4Ê/Ì`Ï™'åžÅŸOçnýR:wý#›åçJ¿_‘ODí&4Hã4oïº(! \EqïèÇþã×÷¿É¸KLú%l­ß#Õã× úªÖ%µwŒö@À(ú©-=Žú3Ý”º•Ù{êÿøgþ¤T;󜮮ó•:ÝÌV|GùÜ|õì`ñÒ·—.:‡í5avíÙNa"õSø¾¿ãBâä¢TTèñ&Ýð}~ïà»ô™8E¿÷¿°¹ö¯ÃriãR[¯” Ai¢ˆ‰ܧjy­”ô÷½en|ï_Ÿ˜y úîBôà£x~e3¹ýòÅpùæ:g­FBᣡ©sñ[î.<ð£Uyß¡ƒ»웟Ü?îk,wB÷™ïßè¾tcƒVV6Yߺ´êýö×ÂÕ›/nõ:7tFÀG£ù™Ÿúµúçžö+Ee •àlžè´ÕÐIÐÅvçâƒoÒ…þì»KÎ>ƒÕ•ódÓÍŸÌŠüd…ߵœ[ÍåïßZ›¹ºV;K+‘]¹U+Þ¾ðý­WN~%6vehñîh†÷Ö×ÿý>áÿÌô5ÓKŽ%¾UP£’â¾øPFCŠ@}ýü“Oýâ—ùäg>êþb¯Þ óY,--"Kúl2ÝùèÅÎïÕ7›H·Zc¸tº1µr¹ãÕVnt–¯ß¨ ¶4ò|yÇ⯷JcoùR?üΧu)€aÀ: k-2—1ÀBßݘڬ/¼ À™ÿ{£Ø uëµîÊÚý]Ïï"jøý»ŒÅKµ&ਠ'ɸýAÇ>5õÞ奯s¨DMn"–™Ë`¬Ék –š¸}ý…wÿå ì^riv¥rÞɯ^=I‡>n¼éï?˜ú¡¯,ëù_oEk•ÄØœ²` È`"&È`Û߸ù¿jðÿcÍþÜÿ½Zçk_@:Ã* àeÒ™D'žó"̃.:/\Ä?´›rò/Úçÿj˜3Ím.DëIEND®B`‚HaCi/html/Images/addRoot.png0000644000175000000000000001271010670113276015321 0ustar fighterroot‰PNG  IHDR00Wù‡bKGDÿÿÿ ½§“ pHYs  šœtIME× +9©æEUIDAThÞíziŒ\וÞwνo©}íìn’¢¸j³-˲,Kò2r4ñØJƱaeÅÆ ƒÀ˜%@‚d2“ÄÉØŒãq~ã8 ¢I&Šâ(Žm)¶µK–D™¤dR$E±Ù{wuuíË«·Ü%?ª¸h<Š— ?¹ï^Õ»ç;ç;Ë=·€ÿ?þßúi_pSét±:7ëø©Êá;îÉ6·Ös:‰R$  € `m8öú£~7 :­~8ì…ä¦cuç¯ßbžüÒ)X­ÿoïô`fÿ¡¹#w|è3ùÙ…÷»®Ÿs¡˜Yï;rIÉÌL`–ÖÖZ«…ÖÚêDQœÄPư1Fè$a¡ÃÑЛÂsνß´YÙéD®ç±ã8p‡}ßc×qÙqá8‚¥#Ø‘ÄÒav³ã0K‡É‘DŽÃìH&ÇaÊÒ×^óýbZòbÁàÍ­!ú݆=T£‘pF­Ø­0u‚X7evÃ$šŸ®LÝ|ø†…J¹$Gô“8niˆÂÂÑ[>²päÖ;w7WÖÂA¯~Õ…êÜáT:WÕI´´Û r…ae³é”`é8$¥d)% ! IB‚¥IÁÌ‚ˆ$ƒD "&@YÏÚoœ–ÞžüÜ]@Õ÷ðý“%¬lÄ —[nyªÁÍÁõ†”1Ib-°ÑˆÚž`ºñðÁÇo˜g«F/Ÿ<»òÄS?¼ ç]Ìâ0€ËWä+SûXˆ‘Õ¬³Ù¬—öS®5B‚‰™‰Y0 AB‚S.SÆg,Xb",ÁsQ¤é©U×9P"Ü}èv$йú¹ ¾÷VÓmÔ×9=í']´L€±‚,[ë0Œ ¥«yéß8¥ô™×Ïì˜$È´¶·›íÍV A3ûNâ(ʦRN©TÎ303 f,„$+Ÿ}—èbmàžX¹åœk >K1ò)Ršm>ÅBÐZ-áç.kq©=/?~+pCx¡Ì– Øí–1膩¾xï-’D!k‡ÆC½¯ívW™F_Q+0¢ù^w¹+¨³¥Sa¾8#Ö—žùžÑj@4À‚JÓs•$Šbë9¦Z-¶$Æò“‚&Ê'!@ÌÄ‹×|åÉ ÷ô¦/‘KÖ@°…C€#¬…6p”B”ž¢Ån_†Ø €ù)íV ë"žey¹QâÝÛC‹ qX–%Ȫ{ ¥î³7î¶P̜ﻕVÓ.hÆh›Î•n¬Îï?…£Ýn˜óýTž™XÁR:,…`)‰'œç|ZÒá*™ÇÏÖ]ϯRµ8 ×/@º9ÀÉ‘v {JÈÅ=‡= s@¥¸X›D @:@¥ ” @Ú2Pr€T 4·»8»¶3jà¦L¨½lÉLÏÎØ$OvCCzм  Eî«Fƒncõüéﺮ¿wc«6¨o­mfÒ¾Ì 4Žó‚ˆ ÌÄÒ£;Ιb½½µŠz'ÄhX ¸H¹@6øÎ˜VDKÀs‡Ç¥@V:VÖ°T¯áXªc¦K9ø•=fna­ 5ºÃ>:'Ć^—Nýð›á°×w7ÿâ+§Îj¥BÁrÌ"M‚ˆ€‚ÈIcª˜³7U†vmg»­ýÁ˜ÿÀ˜÷WN¢ñ¤t…6ÈJ ìo-oáüÆ:ntêvoÑ3™édªÓjn~šÏlö¨SßDíÌùwô;õVmcÇK¥«F³Ûív:B ¦ñÄtå:N²ãb¬EÊ•T,äí] Æsb,wG(øÀ{*@ÆŸÌ4Ñ>]w lÖ,N¾ÙÇ–7 k8š™T¡ŒLyÆJESÌú´R]?w:lMà'ÐúÖk,ÄÌ(ŒxØi´ý”˘H<±_A@2c*Ë8VH—â=Çrø½{{n;¦Ó•a̵½!ÐZ¼Õ$œjÖ)‡‘,¢®SÈK™‚Þ3“åDÓè `š+k"æ`ãâÙo»­–2þâÒŠ„IÆUÍõª»V‹AN*M/÷¦ùð´ÀïÜE.ð­u`4h²^³v¬„xñůâ‘?†¯o-  7ÍgQ.MCåæqº—Pؘ…²ë¶ú¡êt[ЃÆÚ•èóŽºZ}ùÌ«Ogò…Ùso.mn¯¯®§}Ç}û"‚aIEÆe±Æ¿?ŸÕt ï.à—*À7W€­:àó8q©(ÂÙ ßÁÃÏýe|ùGU<·ÿóØ|àû0‡;8ñÆ?Ä‹ßz7nôB¸ù)Ä©½xy©ÅQoÓ¾ö–wÛª×ïíeÉO]R¶¶×ÎZ­?i¬õ~øÊ«g?³oá3±#€”$ëH²±²è‡šÎwþÚëÒyz%Ï¿yðÑàáu`µäá‰g>ŠÌÞÛqqêOÜÒ“v b¹ÆT–*àc3Ú@WƒŸ,㦹÷âtØÇj­C'Ï-»Ÿ¸eʾ¾V3qЯ£__»žÿï ]ß:ô»±ç§*«këµA»µK¹òÜJ=P‹æHËNBÍ‘+mâ3­9¾ÿ°À}{ þÁ¿ýx’¾ 3wít ÎÇ]ï 2Aó;7ÁxP‚U ÄN ·ä!Ùjb·qÇ?ˆN«Q€s»—äwO¼‘,¯¤7Ïž…ŠZÖâÏ âh”Nù…½ûÝÒ‹Ð{ÃL||‹ö¼¸1»±ÂsÅmó⦙,ÿéù`4K‚ˆè»+¸¤íìçV²ÕƒÇ‘*ÿÊ¡ ŽW«âØLJìK;¤ü‹'ènב„5,z°ØDúå ¦Ã4h– !‹À™øAXk-´Ôà ƒó€Ú]GcóÂØ5I–¡ýYîvnÚ˜dXHÝ!'Ëù;rŸsò7:t~û'|áB¬>ÞqÝ¿sdžs_yßÂÔ§Íä2,„pq`Ì;Z}ëG;üøKk|„Ûpü\9DŠBäcÜÚO¦½Iz @ c¡'·“ƒ@°lðŠ¨·†*=“OAlt<èÊüêêËéË÷¶¿èÍ+ƒ'”Ì#$^òž··U¾ðƒ½ß\\,|ðK÷ì¹éÁýÙCˆ¿½ÜÙYêY8ér?‡.•$lÃ.ÅíDí:ÅÁ“p\\êlð٭ߥôÍe JpÙ÷¾&³; ãv|oc‹V½‰Á¹dz!šj¡áœ†Ê5U†ÍPŽàZîЃ÷š‡àÙƒÁë×ü“ïKø™_»ù`ùÁ¯}lß-(û‹/4Õ¥ÿvz5õFm´SÊeùæs‡Œ×¿dI«ˆ¢A—T8"f†ˆÿî[Ÿ”é=⃠i7 Kömº2ÖLrƒ…Ña+ÄŽªŽ jàú)P$ {r»9ÚȾDîÑÝ颻֭]£ÇEÁÿÈ—?ºpìeñVñÝ'Öé”hêe„žKtÔR*9®›±°Ö]~© ²Æ^YƒÆYûÈÑ¿nÞºôæ=)(OÁ…{ÕÆ ˆÇV™àâžr”Cv+ î1’aØÄb M– ÉpP¸–È\ÿT1=xw55ótç¾WÃkiO8n¹´JLPF£Ñë·… , `-‘e),{.Èu,¹ìŒÏ_þèß×Ze,õ‰N@LàÉA¸vÏ`01 \À͘¹8Ú" zô“> O}D°eX¶`fÀw/ë¤Rá­ÿÞÄË’!X@ ›kW‚Mzg0hY;¢µ°ÊÀîv‰”…±†(!Sž†é˜ÐÀ\+_p¥¤É!H@)…nÒE]ÔÑ“=hÖãÚËØ2Ø2ÈИv4i-;àkÒ1ê ÿS]=GK qÊÁó{ôæ•–ƒQ s™Œ«¶Pš–Þ<ÁÏŸyÅõ¼¨Ò[J«°åä´€x(Ù–u(€_B k²€¶–ìXPK0Ö\ØX%!°áñ3"¤ë2qØFñ4/uu}1çTLÂ1t.¿JAgQ›„—/P>õùrû)^UOJ®vˆÞ£7mÀ%i7ƒ´™ƒ;ô Z2ð„‡¡"¶ñ˜ûÖÂZ{Õ°Ù‰°°€Á5a¹ P³FÌ1TJÁ“„×Ö¡FÙå¦ÚžÍÙœpˆ-`A(ä·Í ³-U’;ñÒWÒÁñW¼ÌÇ2È}@ACBª“Bn-/ð`"ƒÈDH8b…ˆ"žhôʰ×fÀ¬Új(VˆÝ1Lj8BL1$ÐZ p…ôíuŒZŨWYoGç÷US¼#*ÊLV=¾«l¾¸…fí¶=S¥þúsOW± ¨E…Egœ0 E #A =¶ÞD« €Í$êàZËâ 5`€Ä$dü1b(«¨&6“m’ñJXö9` d˜˜¼Î³jv·‚QXÙ¨›«¢5žŠdÓ©6Æ™:ú¾öÔò=îÒËÅhawfÅtF˜«B“;çÕÞŠ m„b™L¦5“ç M0Ê õazÔµà.A<8ƒ4²£*¼¨R˜(·…Ç[/ýÑÛ3ñ?þ¯wàÆ£¿–[Üwò®}Ù¹œ¤²¶0 X“(ÆÒ›÷³Õî°¾Ÿ|þ7nB±ÏjVa¾0?NVlÇiZ–-”PHœdL/ÆÈA4¦GÓ`žø‚sÞhƒÀ Јv¡^1(÷Žòîþ?Î9 [YoÄ)Èì“_}ê_}ê™?¿œÖúºÍÏö‡•é‹oóÖ)7#ž {RéjõêµÛý¹ÅÞléÃݵþ£%Û±hË6È%D!r"Dîøª¤BÂc:˜Ð"»–A±]B<Ãwü± ®‚à˜áºb¯)ï]½öõoüÎOÛbz{9ýâ£ïÿd¾wo×ÏœÏú®-§©` ‚#GÔï,«ïlÖÎý`*à  n£išèÄŒ‚!¢V„x;­ÎùÊÐ-û¦³*Ü´ðS&màJÐcá­[B[ %ÂN™á¬ß[Ì{Ó—¤L‹=yšƒ#åÆÚ÷ú «p¶ï|à7ž|ü¯îOú lpƒ4dèC‚I˜´šª¥;ï/ÖFµÚwr/¹?øWN#1\v¯Zzâô0Y‹d·ø3øÄü#¿ýÀÆÏnxþQ<ØÙÏÆ¹ÜfÎv5%³)~(h`£(å…Ã}ÇfËé_=~¸òé›ô*»³ åݸ4oÔÖ>ë:÷uªÁ7f;½-líÕ§ÿcíì ã‹ÝKÑÑÑoeR9Ÿ\‚KîØÊ^½†"Bj:˜uî}ýâ3¼þóo³~á3gñ…G¿ßÿÔ@àÄkÍÂåÛ§ýã÷çìGìŸZØ»Xš/¸kÝù_»Ô{Å»ƒÖg¸<|ãRÿÔ3ßI.œ>™¹aqíä+/nèLÖ±fÜ‚©<wFŠ´€†¾N¯ä&$a ëöZG~á}büþ_û6þå7`Õ=-³øl’*ò=Y¾«„ÍgwZk/]Þ¬^Øl„ÝÕõ@¯_®§–ϼ´súÙÇB¥×ôÎÔW{Wzø×¿v±úé¯m6þøA7›‚ RÈ«Qˆ Aj ™uy=ìvÏÞôÞ©Ç?ûÓ_Æž¹¿‘ß[éfG]¿Qk!ÞiçñÆ«Íòú¹®S_«µvñ­Äb@sÒx¯o>ýÄîÿß®\(éHª’EÚO韉M°›ßŰÑÇÌÒ‡Z‡+ŸŸzö>e~~ \ðÙÇñÏÿs½·¾y´ç¸=ÄAq +oÔ[€ ;éDÁê_ÒùÛžÿ]™ö XÁÂB ­5“!À>ahÞ*×Kïpòÿô¡“ïð’xÂqÀþ¬(—?¼¶Ú| Ù@£Å-„6Db(­ÆµÎ 4§[X¾øÜÝqï<ôÏ¢ñ?;Ê|lM.þA;ØÌFJß h( k ”—@w¾wéÿô®ÿ ÚÓð÷|ðIEND®B`‚HaCi/html/Images/back_small.png0000644000175000000000000000162611031215111016000 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœúIDATxÚmSk,–a¾?„¯4•¯9µ¶Ic4YŸŽË)Ea-”„¢–HrXÖcZ³RM«­í3irÎ1LJF–e%”ôubåœSŒ\ÝïKÉrýyŸç½Ÿëºî÷¹¯—ˆÔHÅÚŸ¤nWˆÌˆ•v;È%)nOV*ùææ<$Š雈5-}"=s"Mmš*‘•/Iˆtw:›Þv¯öTöÀö<À2ÝÈ'ã9® #"u­?¢užD##“z€:1€W)à^ 8æˈJÀæÎ¸fÌ"üŠgܶ…+ÄOÀúÛì\8…ÜSøƒ–6%\s•k³€{!`}‹Ê….Ù_¼àU œxd¶ ”Y20‰ãÙOQÝ?½kê›DfÊ÷#=ÛÒ”í#€»i•í˜C#ÃØ_ðzÞš^X HÛ$Œ4<)[Rð¼.4¼ëæÒø?Ç~!²ºcsÉc#CÐ ,c5¡DòàH‹Lžu ›bA_P§äybB<Üð¹±õßPóê=U͈É~ ÇKmÐN[NÙÒÂ5qà­ˆu'%ñ2`—²Ã­ïqžÉ€,§§óƒÄG«•ƒR𩺚«œû†ãâxØúÙàIÆI‰3¾4ò ð|H<~ÍÑäd¾Ñë9”À{j5SGʹ•¬ P^×y~-,Lù„*SÄ&¾~(SÖ ü¥÷Pþ¥oÚ(4 IEND®B`‚HaCi/html/Images/change_small.png0000644000175000000000000000165610704777334016362 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœIDATxÚm“}LÕeÇÏä%-QØi®dèÄI2µœaÊM&"êAc2s¶ô’4À6—/4ÐúÃVZs.H@DE™ÈîædB(ÊŠ¨,zÔñ2æ•k÷~<÷w *ýnÏžçùýÎ÷¼<ç{DÄK<ÞL¿Ä¯DÂ׊)‹c$þ@Θ åGM©'+d¾%[ÆO3þ½,4KÄw¼ ÃS$2ULæ\‘IïÆ…êˆ·B\-,91§!êI)½¡_•€Pïqÿ80‰ÌH‰ÊÊúâ>üäµ@l%$VÃÚó¤ûгðÆqu’݆øøG‹ÉKF[؇Okûß6Û0WýÍêKvE—f”R‘EêdK=Ê©Aæ?z¢ä=­jP›k]·§tô>ag£d«ƒ4-ëƒ:X§%MþFÌÙxn8zìBKì퀙…°HS_Tí ò®Íšºm|vÃÆú+N£œùê`S+‹—DïÏK×´Þ‚ª'Cð{ÿcÖ×>Äît2 ·ØÙß åà—ù3â¸B$©¬è¯ÛpòQÍ®?â…èì~̼}7‘WB·‹,ÿ¾ÀÚãŽ0û Ée-ØþG¼÷['‡ª[X÷];!™ˆÿÔt‘¹[³ÂÐVüIî…N.ÝîbÏé&ßì!¶Ý¹Ëî3 $•µ“rN[ª=~›ñô]*ª°ÐרXŠ!ZEÚ´evƒØÛó9§®±¦â>lp’Ýmë;ûñH8àzD£cÓ*Zœ„¥¥°JÓÚ3DU}#+ó[ÙPg'«v\ƒŒË|ÌAÈ­?hvÁ¨<¼'çt±P|¢mÊ(þsÁ›U@éõð±Ó/Âû%NÂ?ï@ÞÚÖ7Ý= A:¢3›™«=^RèÌF]›tROÀ²#ƒ¼¶³yÛòHSå?Ð{`˜òœ·¥2 ã*a¹]D|ÙÍ̼{LÎlÀ+ñkM;2_m|å9¸ÈÁÆÑû½]®ÍOE²L¦[dbèV1y›oÓÌn{/WöÆñ×51/:I<ûIEND®B`‚HaCi/html/Images/check_small.png0000644000175000000000000000177710704777334016216 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<‘IDATxÚb,-ícäaâàæ øÂ!Zü™CD•Ÿáó7¶/o¶0|zÛýãû?ýf`bbd@ÄÂÄøŸùË–ª‡ZõÌòŠÌü"ì ì¬ ßÿ3ñüýõ-‰ñÖe·¿w¯$311ìbdÄ4 €X~þeô~# Û(¤¯Ã¨,÷›¨ùÃû¿Øø¸Ø%Íe^ý8íÃå‹vÿ™Ÿ10 @LŸ˜xóØUÔ¤ÿ3¨‰31è 01ð030ÈK½ÁûŸAT‚™AÒJGù7“ß÷¯ß~üø‰‚ˆå› ±¸7Ç?! Ùýg`eedæbdøÁÃÈÀÆøŸOCšá+‹ûÇŸg0³±¡¸ €Xþ°ñ²s±ýcøðñ/ùÿ¸¸˜¤ø˜€PÛf&†_?~ò|üô…•Õ€bùýþùFVfο¿þ2üêââºè& Fa@üëï?†/>~øôù+ËÏ? Œ@WÁ@1½½uéè·wŸ~303üüÍÀðU?À°dàbY ·®>ÇðèÞÓÍßšÿ|ÿÀðëÛG†Ÿ_?2üøò‘ €˜y˜…12±Fj:°r²3£‡…¨Y¨ùõûO s§,gøñý÷$'©[ì¹O0ˆýÄÀûóû× Ä,.®þøý½Û/¾øé(£¥Ì.LÀ¨âj¾qõÃÌÞ ÌL :2e ¾mVøö›ñÚ÷Ÿÿ¿}ö_ÿA¾ÿþûùѬ©ÒãÌÈs#7 öëîðäÚß®Êôc#ÿÄR"!CöÖï4ûþ^ÿÿðÿˆŒÌÌ@æ¿ÓŸ=‹`üŒ ^n†Ï^2|z÷‘AKõ?óõÊ¡òA:ŽÑ l«—ú¾:ú~É‹¬ @­oäD”£ˆGP€ƒ“ƒá÷ ¿~10(ñbày}âÖ­Ûï¸Å•d-4,x>ÜR»ÿð«Æ«¯Ì{a0f˜XX8ùxy‰Â_÷‹AN”•çËÍß/>üÛyêÈ-~iy }ws‰ïwÔ¯Üùª@L Dv6æÿ¯Þÿ)ën\ÛwñÊ^[&ay€ba °²2ÿ~÷éoeWÍ*!·[Œæç ÛÑCaP2ú…IEND®B`‚HaCi/html/Images/close_small.png0000644000175000000000000000157210704777334016237 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœÞIDATxÚuS{,p>÷*\±è!Ü4ÓS[y7Õ¼vo5Ô Õ˜¨©K*mJºµÙú«šÈKÊ•QŠj¹¬š¸¨1ŒZwË#ܯãºXMgûþ:ç;¿ï;çüˆh Ý(ø*ÑÚÒ…­¯7Iäç(,?‹ö( É=1™LlVérfÖDV눌ÍIDÎQ$¸À éêë?5Òç€÷#`€†áU˜fž÷ž\J —šM59„¹'Ëä½ÀK&Ä¿W`:.)»á‘ 8æsÙg‘ùÌ¥™Ký³¾OË>n\˜Ù8ÓàÁÛNøŒ"è>à’ÃMâªÀ¬ùÓ|œÑ‘ß\¨hÁÆÜ18ÝT3 Z:»àW¬ÁÙw@ºr–r-È5ºDÿº¿gJ ïÎWâKÏ/DWãtÍ]ý¨lnÅQE= ÚGÑÓÛ‡ýYØr›UÄ('T˜PrùâAöú˜¥ßÑB=0ÌojQØ:ˆ„ýȨë†zpRI†¢Qå@H`žÒ- "Qd~¶„½mS~÷€˜§C\:†ãMóG„=üYU ˆÓZ@öÇÈ8ôÆ-OÚÖB`G )Õ¢¬}èú0 뤒yMûø1ÛS¬ÀÜ.‘„›ãdî<4 ïz+ÙÍU ÿ¥BÙ BdEÎ4ñ–ªÙ¯ÔâÈ3±”ÈÔÚÞá&À v²·p%Èó(ïÑÓG›×€$ÞÀÉ: ±Œ,ó„ò‰!éa]Tãɾ‚x{Ÿ|‰Çë'éên„•!•ïâD çØ®8U²r¾6sH†f6âtµÖ‹×È!l%˜Ñ;\Q¶!‚Ï:–_ˇCZ#hSÂWfÍ›¢Û10\×,ƒð?9=O4°`|b42jÍ<Ð’õ K'†#hÁJ˜ºÑ+}mÛÄÒ¬ah*"±ËvZá›@ö^ñüûüI` š­ô£Öë÷pÿIEND®B`‚HaCi/html/Images/combine_small.png0000644000175000000000000000051710704777334016544 0ustar fighterroot‰PNG  IHDRÄ´l;bKGD½½½iBÕ¨mIDATxÚÕÕ1€0 CÑÿSåffŠAÇ Ok©Ð?BR„d°RÔW¨Ðüí¯¨’ù¼£:á¦XF¨V+7Ñ{h jÈ1´6z¬lËôy>·¾…ß—?âcågEùp—¿ÎùÅ¡ËzÑ ÊkFƒf³òúLzTXtSoftwarexÚsÐPÖôÌMLOõMLÏLÎV0Ñ3Ò3R°´Ô70Õ70TH.Ê,.©tÈ­,.ÉLÖK-ÖK)-ÈÏ+ÑKÎÏôK€ôÝÄ^3zTXtSignaturexÚ3541I4K571µ4KI6·05O232²44°0MIµ03‚|ì\d^IEND®B`‚HaCi/html/Images/compare.png0000644000175000000000000000610310670113276015352 0ustar fighterroot‰PNG  IHDR00Wù‡gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ §IDATxÚíY L•Ù~¼§ˆŠ" "*¸ Џ î ŽUƦj©ZªH›::iÄ¥.M4êT4mT ÖĺTtÀNÜâ¸0îûZpCQAqATáöûnî%ÿ<ÏNÇÄ?9ùyïýËùÎùÎwν˜„¦ÙL|ðÀ V­Z&ooo“½£f͚Φéprr’¯tø{[ÅÄÄ|¹wïÞÃ+W®üç°aÃþ€kÛý/wqqiçêê:ÙÝÝýë† ~W»ví¥øºyµ N)))ß"M¢¤¤Däåå‰Ë—/‹¤¤¤üÐÐÐ鸤Îáx5œ<<ÊQ(ÐO‘9ѱcGù6mÚHóóó“@}XŒË'Ã|*€ê7#e¶^n‰ŠŠÚ}çÎaë ¥Þ¼y#NŸ>-""")Åp( (Ö¿ÓÙnݺÉè·mÛVž ‚gOÒŽžÆåaîF™ý\q®!© ®qqqÛálRæg|Ñ Aƒ":d³À¿{÷®ˆŽŽÎ2›Íý\üo‚#wÀ€2ú,ZšjC€Åpr®¯ (U{Èìdð<>¶a2ؤ\ׯ_Ÿùøñc½0~üø?5mÚ´“æê’%KN¼zõÊ& ¼&&&¾D´~‰[:àgt¶OŸ>RuÚ·o/ÏZXÈÍ›7'}Òqíg°pØÎÿßeSjñÝk•™ºLG½øøøt* ÒQÏ‹ŒŒ\V§N¤9üÔ©Så•ш5’œœ\„ZŠÀ½ðk:,Ð(+LAä2T†è' (½Q «àt˜"XÙ'ð[žó &EHHHHgAæççKÍçQPP@½¿9cÆŒ­èÂÏi[Øä¶nÝúωĽð+òŸXĺi,`€å xi°l3³Â{x&½@Wñ=lPtòåË—2 Z> $55µìܹsïH*?ÄÆ_‚n­õº’c0å‘òÉŽKëÔ©“4v_‚ÏËHö]è¤ï#  "õ1Üa—%è0ßFG>|(³•ÔIÒ:Bõƒ÷žàómöF›N²pé8£“¬-«¼ŽÑg×€õwÏž=·oßW¯^.\Žyóf1pà@eRE;οuª ‹˜g  sçÎòLÐPª; _g9ïT÷Àª+–vñâÅ:l@pâßÈAN©Š?htšõгgÏ ý×ôtÊŒôuÜÛ·J…Œî:ÎÑyJä“'OdâãÌ ©’-•hÓ¦Mñ‚!JæœTéÎGƒËœéÿ˵4´€˜oŠum°.(³Ì€îÄ*s‰rîqDBñ€¶˜‹6\N>xð@:ÌE<3ÀÏT6.N¯”MDm‘r\˯E£Ý[-ÊµŽ³±¤ú°xÉ.!QrÒi-­z•Æï8A vhÇóPùME¡ÑѬ¬,Ù¨ÈýíÛ·ËÝþ–™™)Á;v§¥­#D ÎÖkeèüŽ ”OFœ“©žDùÙ€#6³ :²ýfn78öÅÊ]86&ª 4¢Ó+V¬»wï–Y¸té’¤ÕÒ¥KÿÛ;+gß»«‚¢>ÅÈPV ‚FÇI!ã™ß@²0¬ÒZ@ZæÍ›÷r]wTt×®]“Ÿù=øž?f̘£˜yd·…tÊZضmÛSÜj ‘½i4"QÌâÕüGäßâþK(øŽÆ j¤Çn€X¡éÝZÀÍÎááá+W­Zõf×®]âäÉ“ÒIF™]–UNa¶™Wxøða.-3ÁëúxL#ö„é51‹–+- xeøi:Ôj»îÎ@ú°á©u@1$*Å«´/˜1Ɔ¡9} lJ¬ hˆÄ\\ÓcÁ‚—ø·\XÌTDDÄAë4›Ë1OÏmÀ&ß]8ù„;\Õq|æ(MZ‘ûõ Ñÿ¿ÍÂ5ýÔÆÙÞG{¡¨FŒ5ê›åË—?Á¼/ÂÂÂvá{Îûn“&Múš4:pà€Ø³gÌ2÷êbO)à„ œË E€Ý™†L~‡Ÿ{©5IŠ*Úg°4|¥ÞÛV±Ã]™zC1>ñ€>W1áóÂuëÖq9)U‰tâZ>…í,%ƒÀñ=ÿ4iÒDÎ<ˆ0ÿ¨.ã†Õrw9>Q[îÕÙ@6fÄCÍäÒ9´úñññ%©©©rd *q€± NíçÛÖëõw^¯÷Ál6ãÎÏÏÉé9ét:JÿI(â”Jå‰D"I †?h~ĪÕê{>òd2á¾ý~ÿ÷rúé&– ‹¬V+P)H@A’}+“ÉÁ’t»ÝoR©Ô¶¿¿ÏˆÅâÇØlðûýàp8@.—ø)gÄúX¡P`Èx{™d>ŸC2™|£V«¿ƒAL&;C˜t¥Ã¨dårù9n4'Ô‡V«;;;€ø‰%ü‡Ã\±­l¡,GO$Ϩñxü¿¯ñ¨±î‰Ïç:èþi… Ùl~ àt:…áp½^ïû²ËÜÅ)^¯ç(+Tß¹rH§ÓoOOO¡R©œår¹ãñx ròvVhðÚ2 R©èž¾"ÈçóÔé[x D"‘e‹ÅyûÞÞÞ/\oYñÜ'Õb¼—4Úu\zʾd³ÙÏüo€« B¡ðåÚnü#(‹”&·u8cN‡{à^øÒÈv1Cö?‚¿­fÙF¹šõIEND®B`‚HaCi/html/Images/config_small.png0000644000175000000000000000167510732507147016375 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ!IDATxÚ]SgLSQ>•"¶*…’j­£NŒb«Ò(DZK" «j1ÒŠj0Dq¤(Ô‰# â$80F+®„¸â÷ DD1Šqæó¼÷BÏwoÞý¾ïž{Îwˆ¨3 —IRøR{èâòHmv’̘8kˆçU}¯õ­ ½vu ‰Ð72)&ç)0î, K(õ ÿ4ÑY¤0%Û_(û¤]&ò9ÍØ}ÿñ»ò~ßfàš·ÀôZ)ú;&íi¤±›ê^^âÿ[ß¹€loù Î<œüuU. XNŸ€ÊŸ@þP¯¾¡™ú¯¨c¿ä=JX¤¤0ïlÉ”ñ¤Ò3S&Õ shJö”À¼s@ˆ½Œo >ÊÉËÉ',cõ”ÀÉé{X$ï.Ð;¹\8WI/èä'.þæ%Â2:!%!u@AºñâÙÀÄÍÏ›? ¤  êEß„„;Q©“´Ì¹4Û:þ\fÚLðË¡ Qj¢Àîn—ýŲìE è&ëIÞõß.tâ:I[“ѵ·Ñ½ ½,ˆâ´ÃŽ»wÁ•> *ÿ×ÃÀÓ±™ ûžÝ`,ìW›eú 2Àp0íh±ŠbXŒzè¬K˜ßòžÚÒ ûK^Xùøæ/&§pûF”†¬ÃˆpW#þ20gïuTîÜ­ašç Ò¹•½·ý)µR䣓¦Ö1¹¨ˆªúTÉ}É-å–å·…'káqØ VúbÞ™XzHbŒoº·MP§Ø’|“ÍÃ>˜{pq›œl–xvœù`t샶 AiÃà­o0ù`+\WØLžwBAG µ­fãÀüóœÅþO0mkFìH®ùç ½ôn¾yÝÀ} È`¯híG­˜Ew[ñÅð*.Þi Õ ,âL–ß’µÙ@üÎÅìéi‡rHÞÍÞ± &º´o›iûgDí¬@L9Yü¡…¯¡Ï<®Ñ«ž“ÆX$Ìôûo&Cg·o“Ÿ:†=xÎ&Ÿ DÃso•Pš`aèö•¯›+&IãIEND®B`‚HaCi/html/Images/config_small_disabled.png0000644000175000000000000000106210732507147020212 0ustar fighterroot‰PNG  IHDRµú7êsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœtIME×  #iºáâ¶IDAT(Ï5ÎÏK“aÀñïó¾Ú¶7Ûë‚zÁ†¤&à ÃÀ¡Dm!QY¢ƒ.­¢ºÝ»u‹ êP£º(á¡a`–‘.V[$òå~40·õö>ªÏ_ðÑâdù+FÏØé{C—TWþ ÿíŽÏª;jßcˆÒw²®òjJÝU{t€d¦O¸è OÎδ—T 5mä%@F3 °† ©º^ÒF6¢C  [µb+Üf~Ü©~0ú‡A§Ì#Ò:0ýÇU—µ 8”½ª®¨x‘E°‹ýÏN(q±Ý'6^È%”WÑ) ±Ùº)¬·œ§@sÜ/þ0ý4±‚67æROG­|e™UšyÍâûZoÐàÖ«33¼'È,¼ü$C”ƒæå…õA=£Gw¾£@˜a t~ñAáÀtÒ#»÷¤XÅO+rÔyN’iŠ ¹‰È7³ñsHSÆÄÇ1ãE§F™OðM¯¤GÃAƒÍlÁdl*,1ŃSÎŒ°#×¶¹ $5*|!›[œ,]oÍW½,B¨9¤¹»Qb‰eœ­|ÆG‰?£û˜æÕ*IEND®B`‚HaCi/html/Images/configure.png0000644000175000000000000001260111031215111015664 0ustar fighterroot‰PNG  IHDR@@ªiqÞgAMAÙܲÚ8IDATxœí[kpוþú1Ý==I£Ñh„„x Iãà“¼Eü('kŒÍz)ÛbâÄì+Äö.Ve³Á Uvj]Ž{+ÞZ;Ø„-;%×"lo±y I ¤A ÄH‰Íh4žîžéÇÝ EOäüØ|ÿzúÜsÏùúöísÏ9ü?õ§6`ª8~üx%€å¦i–Bæ†Á†]×ÛÖ¯_¿oªúnJÀþð‡N§³~íÚµú-Y<hhh(#„ìdYöq§ÓY"Š"A°,‹l6‹ÖÖV]Å‚;î¸#>ÝàóùØx<þ2Ã0/š¦Y“ÍfÛ´i“4M_¦„úúz7EQûÇS………¬Ãá@&“A6›…¦i0 ,Ë‚çyœ>}zÿý÷ßÿSƒžè†(Н1 ó"Ð4½žçùºC‡UNÇ¡© ®®nÅbi^°`ÁÖE‹±E!£¿¿©T ªª‚‹Å‚ÆÆÆCGýñ­Ì3á „,¯­­=Ù××' ûY²Z­›×­[÷ñ­L6YÔÕÕm´ÙlæÏŸÏÒ4†1Ü6°, QÑÜÜ|èÓO?Ý\UUuK¯è„+€¢¨Æ%K–|/??ð‡Ã^VVVÉd^¾•É&ƒ†††;yžÿ­Çãa³Ù,¢Ñèó¦iÂ4M°, «ÕŠÆÆÆw§ã<0‰M0‘Hü{CCÃ÷i𯂠PTT‹Å‚d2¹/''ç––ÝD¸råŠÇóòò*DQD6›…iš#d8ŽƒÅbA}}ýkßúÖ·vOwÎ WÀ œNçîåË—Ÿ*++Cqq1, €a˜çô£ÍŸ®ÃÑßß¿Ób±TP…L&3ÂyÓ4a±X`±Xpúôé)9ßÔÔTÙÔÔÔxðàÁ_Œ¾wS(ŠÒ9ŽûN"‘ 7Èf³ kÖ¬ùÙd˜ hšþ!Ã0 i„¡%?¸ìsssQ[[ûÙ† ¦ä|NNŽoÉ’%_'„<1fÎÉ(±Ûí¡ãÇßU]]}, (++{â™gžY5io€úúú4MÏçy~ÈiCDX­ÖÁ/@t²:ý~¿—çùrrr¼,ËÂf³•Œ–™ðÝï~7ØÔÔ´öÈ‘#ûNŸ> Y–QVV†òòò·žzê©ÜÉꙦi®` ÃŒxúƒd›¦ A@Óôú÷Þ{Ï}3}~¿ßNQÔçv»½"•JÁ4M0 3FnÒ@UU•þôÓOÿ¸±±qÃ'Ÿ|íììĶmÛ*ËËË«Ÿ}öYûTt†iš%4Mƒ¢¨1Oß4MH’»ÝŽÛn»-WÅ<8á|~¿ßnšfMnnn…$IPUš¦AQ”1²S"`;wî¬éëëûæ‰'Býýýؾ}û*§Óyx:$˜¦)P5áÓ×4 áp¥¥¥X¶lÙ*«ÕúåÇì­Çç󱺮¿ïr¹îK§ÓCŸÐë´–¿%`ÇŽm©Tê1¿ß/E£QìÚµkUNNÎïW¯^ÍÞŠ>BH¸o±BQôõõÁãñ ¢¢b9Ã0¾Ñ$8ŽwÝn÷ƒ²,Cׯ…E!NC’¤¶ÑóÞ2°cÇŽc‰DbCkk«.I¶oß¾~ùòån…Ã0Út]ñ c‰Pápn·••••V«µî³Ï>Ûùå—_®©¯¯Óëõ>®ªêó@Ó4Âá0LÓ<5zÞi[·n=–J¥6_¼xQ—$ »víÚx+$†ñ¹ªªc6Ac®5MC$ÍfCEEE‰Ëåú•Ífóy<žƒ‡¥á „ ··4M6zÞiO?ýô!I’¾ i¶lÙ²qéÒ¥c‚ŽaíÚµQEQjcö‚á0 ±X †aÀëõ¢°°Ùl™Lf„EQÐ4 Á`0°cÇŽÚÑzf„xòÉ'ßVUuwGGAÀ¶mÛžß¾}û«SÑašæ¯ãñ8l6Û˜xy¤ÓiÄãq$‰1O¸FT €ªªoާcÆ€M›6½¦iÚþ@ Aðøã?¿}ûöŸLv|*•Z£ªêÐF8]†T*…ŽŽŽ¨®ë¿Ofld0M|ðÁŸnÚ´É=00p÷¢E‹PTTô—¢(&Æl@ƒðù|ìO<ñ»òòò­EEEˆD"Ó¶C×u¤Ói\¸pétúÙÝ»wYþÀ,;vìøT×õ¥ápxéâÅ‹!Šâz—Ë5. >ŸUåýÅ‹?æñx …¦=¿ªªˆÇãèèèÀÀÀÀ»»wïÞ;‘쌾ƒX»v­n³Ù6 ‚Pã÷ûqÏ=÷`åÊ•¯nÛ¶mãp9¿ßoWåý¥K—>êv»ÑÛÛ;¡NMÓÆÝ GËD£Q„B!\ºt ±XìP<æFcf5+ìóùìÙlöp,[uï½÷âÈ‘#z]]Ýæ·ß~ûÏç³ëº^³xñâûœNç„Ož‚¾¾¾¡ˆŽã8‚–e‡î«ª EQ ( ‰ôL&ór*•zåfÉ’YO‹û|>»au¡P¨rõêÕøàƒô®®®-<ðÀ÷+**î³Ûí¾ó4M#‹Áï÷ƒòÇq·qw§ÅbÁðsC6›…¢(eY5 ã]EQ^yá…Æ„½ãá+© ø|>¯a¾`0X¹nÝ:TWWcÍš5ÈËËCÿ¸chš†,ËhjjB0Ü÷ÒK/ý^ýu¯iš+(ŠúEQóu]!¤@c<?^UU5¥ÌõWV9~üx¥¢(¾ÎÎNïC=„+W®€çyˆ¢M3 Ë2ìvñšQ׃—óçÏãÊ•+‡^xá…ÇfË®YÙÇ˲ñÜÜÜDYYª««Q^^UU‘Œ§J¥àvç-khooGWWש`0xÃMlº˜•Ïàh´µµUò<ÿyqqñ"Qaš&¾üòK¬\¹¡pyy9àyÙl ÃàòåËhoo„Ãáu¿üå/c³iÛ¬PSSS©ëºÏãñ”pUUÁóü`rkÖ¬AWW!à8===hkk“úûûÿrß¾}³m߬îÕÕÕ·Y,–O ¼¦iÂn·C×L\½†§0==AtwwcÓ¦Mhii!ÍÍÍzP,û×’{ÖÝ%›øü¾FíŸMg€úúúU‡ãp~~¾½··’$¡¯¯¢(Â[XÖ‚eY´¶¶"‹á‘GAmm-ŽcÜmëVŒ…å9 /í^Být¶ìœNž<¹Êét.--µ ‚€Ôþi9Åe‹P4§h¨´•N§qîÜ9d2P‹Öàô€™âAq<ì"«ƒC`v¿°"ïµÙ°uÆ¿µµµëA8\\\l§( Ù¬†Ö– Ð4 .W.b1ƒAˆ¢Y–aš&æÎ‹úT!Ž\ÖÑUKªPd’œ…ƈèhñ¿úÓ“ýcrú3]_|ñÅFžç,X°€eY„çý` 9yNttt@UUØl6èºǃhlÏôóóÒi°Q@eãí6—Àa1ëhÔç-®Øü/÷¹ͤÍ3FÀñãÇ72 s`Þ¼y,!4Íà\c3ìvy®ke]× ¡(ÊP–÷d$íæ$4€xˆ¢›‡ËnAäJ .AAÉ<ØX Ô…q÷º¾S<6SvÏÈ+P[[»“eÙsçÎe3™ 4Í@CýY8ö!çÀjµbÙ²eX´h‰€tì >kïI %][öédi)ƒ¤¬Ã³`ú.5#ÚÛ Å° oñBçù³‡R/ÏH5 ˜N:õ<Ã0¿*))aE¡hn:‚‚|ää:‡ä†ÃáÀ`Æ6‘Hè™LæÛÁ¬°%VÚ”´ 9™@âJRÑ«H$UHª‰‚…·#|Éh(Åäa+^b¿|öÔï÷ÌH³Æ´8qâÄó üZXXEQ@à÷·Á[T»ÃöÇIhyyyÐuªª¢¹¹š¦í|ã7þ¿ßÒ2äátÏù€f+A2x R¼±¸‚t–‚«t)®¶œF4t:-"§ôvoïåvß¿5²?uuu¯ ‚ðª×ë…¦iÐ4­-1wnDÑ:$GQ\.X–…¦iðûý…Bû_|ñÅÿªÞÔ®Ç;ÖÊÝ!UJC·#ÑÕi qºÉÀ5ïv\m9ƒh4Ýbç^àmoA`ˆI¡·»×/ˆü›²¬ÔvÞf³!›Í‚‚ööv\¾|ù£`083}„µ¿Ø­)éwäà9d);²‡ø•sÓ ‚‘4r\…gC¯¿‘¤Š‚Ò È yy營¿™êq ¸páÂK‹åQŽã iX–G´?úÚ±“_ÜñÍoÞ÷½»ï¾ë.]×k†;o Ã@ww7ZZZê{zz¶L§ƒs ¬l×Ò‰CrïydY'²&‡d° é´ŠÞ¨ —»8ôúO#’ÌÀ³ðkHÆ“¯>ó»³{#µcNƒ‘HäÎh4z’¦iÖ0 ðœ€x"Õøo|ýŽárÍÍÍÛX–}Ëf»òÖã‚¡Pè®IïøSÁj ù>µ8\kÄ9K`Ñb° œ¥K`·[1'_D´·¼(À»xò\½tNÏs»7ÿúÑòqÑcV€ªªû)Šb ÃEQ×vyS´Ã0wÚl¶kñÀõ²ÔÙ³g¥D"1ùÏÝTqt­qÞÃZ²ï˜ºsAV5${.A’ôÆÒp͇’J#táb’†¢ò¯³‰ØÀgÞk÷9‚BÈ EQV6'°, Aà!ÚÅñv[[[¥ÃáxÊ4M(ŠÓ4qæÌ=‰lܳgÏùYq~G¿/Á¶hƒ–Œ¶ÉáËй|È©4R½R*"qž’ES)D툥4,XÊf2Æá¿;IÆ0â$iWW×ß^3 ¯×‹x<®&“Éç(ŠúœeÙ5v»ýg™LÆ=˜Ç?sæ ÀÎ={öŒÛ„0+Xýs/Ò=>‹k~¥X8¬†-/ï8V8yDº/ÀéñÂ31¬V‚ÈEо÷Õ•ÔPÝpÄ 0 cù¨kÄb1X­V!''ç-»Ý~Én·¿%˲;“É€ã8\¸pÝÝÝû¿Rçàè?…`›¿A‹÷†ähtÁƒô@ R_ɤrm#,-G2Ò)AZ΀Öu7ãÉájFÀóü˜0RQÄb±ëG^ Édº®ƒã8\¼xñÐÇ<£mó“ÆÑÝXs×j}—Br/tÞ)zR_7’I}ñ,xÏb¨² ÞР)ª4  W1š€z«ÕŠÑ0M×R]Ú ¢Ñ(ZZZ%“ÉÍžî¾ }± ö¢ïhýINôCç½"½HÇcH¦(r"¥C’ÕxW\]ÿÛ¿âÏ >ú+ð¦ÇãQGGyƒ ( v»ÑhÍÍÍïD£Ñiý[cÆptÏ)Xs6h¡INÅ Y‹ d ²jÆÀ@B #ÒŠê' :& „l”eù@0d‡ÿO¸vÄ …B‰K—.íijjú/°X,B&“, ¥ëº¢¨!ö à†!üTü¡(Jã8.=¼íM×uÂ0ŒD1Ng&‰¨V«•¤R)b³ÙˆÓé$¯|Áýµª“ÿ´V°9N+æys Sl›ªcÃ…Wî Œ;×(ç©]»v>øàƒÝ~ûí{hš^¨ª*uíø›L\¼x1ÐÔÔt.“Édhšv\OB¨ëãÇœ-¨kõî©¦ß §!ˆ¢(ãúMÐ4mB@‘®ÿž¡i:}²¿pɉXÉ# ‹rÀ5x‡µís¨H½Ãá詪ªÓ|8°çž{Žç8îaÓ4o£iºˆã¸<—ËeK§Óz*•2!4õÇ&¾¯¬·àF „Š¢À`¶¦òÜ*8f©-r™§ÍÃ0.ò<ÿÑ+¯¼’=~̓ٻw/ €M&“Œiš<˲9š¦9†±BDŠ¢Bc±Xì`š¦Há(Š¢ Ãp#EQ4MÛ Ã˜RþºC)Bˆ9ê·4¢s—ÖuÝ$„¨„,Ã0YiÃ0$š¦%UU“š¦)]]]FMMÀw®©6!ÔË/¿|£Uq«+e¼^Yªªª&ºÿgüSÄÿ;{ÚÒIEND®B`‚HaCi/html/Images/copy_small.png0000644000175000000000000000166310704777334016105 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœIDATxÚmSkHSaþ2•uC"¢þÉ[SAqDŠ¥QËËÄJ°(¢HQ+P± YÑŸL¡Ë ™V‹ls‰¨s¶Ù¼l^¦2µMÛæ¥Žk¦9•/Oç;hIöÀÃ9ß9ïû¼Ïû~ßG G<==ÉÜ’“Eg“ŽMO9q,õdº8Ã{o\&!>{èO¿ý—¸ Þî@âæ¹“lß×7þç¯lÄ„s ¢ëõàg¨oAÚiç±Ë—lñØNˆP(Ì A‹‹‹˜›saúÇw<‘i‘PÔ„çÚ¹5‰)˜ôµ8|UŽèÛÀÁ|Ãgâ¶Íƒ­„DDD¤ãÌ8ì¸[ú $®ÑEãx«ÅXO nÞ{ rDa¡ .Êœœådš´ººÊ‘ÂÁ|E¯N‰ô+¥ á—=Š¢‡­–W¢ðÖ#|¨z…îîn‡ .“ààà”uuŒá“J‰w$Ø‘ Á¡ ‘–ÿ$o0jî‚Õb3nƒ`_Höæ´5r9ïß¡³¹]mj¼|!…ÃÁ°œÀÈÈØÜ,”²¼¼Œ••¿“ŸŸŸgì°Z‡0jµÀúÅ £± åål²ƒK6›Í Í¢RéP.--ý¡Ž099 ›ÍƒÁ©TŠááaô÷÷£·· ‡1Õåraff†ÝƹMN†ÁÀÀ´Z-$ :;;ÑÖÖ†ööv„‡‡çqN§“ ¤ö¨Ðìì,¨(ý644ÄU¯¯¯GYYÑÐЀ¦¦&DFF^£â1vê4’öG×v»ƒƒƒèèè€Z­†œhII ÷¬®®F]]¢¢¢ HXX˜Øb± ¯¯===0™Liz½ž«¤T*!“É8µµµP©TÐétˆ-&l§èDF#W’Z¦}j4.¡ªª …ÓìÉ-މ‰¹Ï&WxyyÅÓ»pžZ¥ÕZZZÐÚÚʱ¹¹™ë— PÛÔrnnn=½‰‰‰D$BÜÝݽÙýÌ 8çïïŸÉ>9n|?ÃÎ*‡Ïç'­ßZ±XLØ5ù \ ‘åc,K³IEND®B`‚HaCi/html/Images/curveLine.png0000644000175000000000000000020511030471134015644 0ustar fighterroot‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`@ÿј‚á§„‘p¸Œìƒ3™¿ë0IEND®B`‚HaCi/html/Images/del_small.png0000644000175000000000000000165510704777334015700 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœIDATxÚe’]H“aÇæHк("“£Â ºéª’ $ˆ@ˆºNèãÊ›@»‰#!g*’kìÃùn:ÛæœK_MÅÍ}¸©sŸºé؇s›kót^MgõÀÿ}Ÿç<ïûûŸsž†‡‡all,O«ÕÞ!=|300ðN£Ñ4²¢ùRƒZ­~A{U½½½Åb±Øw:®qbbÂnµZ-N§Ó¦"‘ˆ.ªHJV´–ú|>Æn·‹M&“A¡P´ï¸\nÙäädšæ1Rém:nÚÚÚâf2vÝ ô®'M…BaÉ> µµÚÛÛo[,ôx<¿ßï&ÇÕX,æ ‡Ãr¶z½Þ ËåòP ¥Rió_%°€ŽŽaðÏH¥R …`Éx<ž¢L¶ Ž333›äÝÝÝY5T*ôôôÜ£z‘ÜÐáp¬ó¹®R6H%àüü<’smWW0 “°‘H<£×ë‘Ò6‘£•z°’L&7m6 Ø6Hß‘Éd¬Ù?ª‰ÏçÆ"Åf—––ÂÑè(Í’¶ ˆJ¥’+•2@Çr¹< Hº "@ÀöA“H$vR¦˜~¥RvÊ"äv»±õKKÕ7^'ˆ| YÀF?Ô P÷É@«Ñ2ñø&®üès»p3Ýmj"޼޶§r‰4} )”`žÕƒA? ãZ%˜ z‘ÏãD³Ù\Zv-LæVÌKÈåXÀ~™àkq àL@IU°7î?{SV·Ñãõ¡nÎ…³ËëéiÇzFgõã°-„ÚÑi¼ô’¹¥•Wÿ€›Ÿ5-:/.&=D?ÉKZÛÞ½> ¹ „ ª¡¼ú à<@îÉBxÅï“XѲŒußWQbÜÀAKûkØ6ÆÊqÄknÄCkåœ' %y§Ù àF]ÔØ2PŸ@ÎÇ-<ÚŒ˜ÃG,&ñâ›ÎqΩ³öÎŽ ÿÀá\€»ŸvwsŽ—BQy%]­NþeŠäî8”]‡ƒ€ßaªI Ï|iIEND®B`‚HaCi/html/Images/editClose_small.png0000644000175000000000000000165310704777334017045 0ustar fighterroot‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIME× +9àâ$c8IDAT8Ë]’]lSuÆÿÿ9=]×µ‡n+clŽ™™éŒ1qñ#f0ŠDfâ…_hü@BH ñˆ‘8§D 1b¸PLGoS¥\(ÆH6ª¸1·…¬ÊØF×v]{ºÓó÷i2ŸäMž¼yŸ÷É“÷mmmhšöRéòÊAMzÚK“5Í­«]y·O9™ Bä.À^@Ù‹…® ‘¼‘T›÷>¤Çãq|>ߣ[Þ|£åÁ‡ÆNÜBŸ9/0‹;@%s¤FcwòœlO¥DV»ÑŠÜ-ºã8(¥,¿ßÏ2 øKÈùËXù‘¾†Q¬‘»…åºó±§°o'ÈŒt!¼óAN !°m›»0L½ê Ư×36šÆl RVó7rê/¼+ê0Lptþ‡…¦ˆ róÚï\þã&ÁöWÈLœ¥aùŸ¸Ó=qÏBÀ@ÏJ‰GÓØºm»·l¥…aE1d\ÐØºžGžlâùõ;X³¢˜o¯¤¼qòE¤³:R€¬µ²çe¦©ªš›_%µØƒÓÛÍí˜Å}O?C±D"?Ó?:ËŹ‰ç*Ðkòè @~:8ÄëƒÑªç-x««Ú÷?þ@‰éÅÐB_ —ûÖF"'…º°só(‹Î?Y²ä@èÑKÄÆ¯£iw¶à˜®ë†mÛë‚Á`Ô0\É;ã!%©Tª217·À4Í¿ß?í8Ž333²,ë›o¡YV*áIEND®B`‚HaCi/html/Images/edit_small.png0000644000175000000000000000216710704777334016060 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe< IDATxÚA¾ÿAAA\t Tò,8 ý ÿþ÷÷úüöÿüûûòåòÿa¿A¾ÿïïï :iec/024üñêìÅ íÿòúóÜâý÷þøõ /ˆåß¿ ÿÿÿÿÉÇÇÇ ÀÇÃÀÄ¿ø„¾ÝÞËÀøõ3Ã÷»¯~²ê2ð[»2üy÷‘áûí% ŒÜ_€®cø@LŒ@þüùÃlüü ,R¶ ï+1ܽó•ßQ”AXöÓË[ Ü’Š`y† ŒL Ä€¾=Éðþö5†7.1œ½ü‚A4$ŽáûãC *bWØ¿î`øxà;×[A6°z€bùÁÄÄÀÉÌÌÀÄÁÍðëÑS†Wû¦1r†ïo?2üÿý‹Î@âßw~~E†cUÖO›ûí΋ϷˆEØG¢D$ø'Û«µç²”0ün`xÏ óô>ÓÙKÀhågà`ýÏpÿ-Ãw6%am†ï@ó–{±žé߯|€A¾ÿÂå´JóæU=€&`zøøÄÞÿööû +2ÜõÒòò+ùÜüÿ øöõúðˆiÁ½?éûŶþ7÷dàbýËÀûÔ7†¿ØN~c8ý”áÞGvé¿ \ß¾ý&<°  Î@,¼rr Üâÿ|óýþåÃçïø¹þ0 ½exóé/·Ãÿ÷^ùÅÀõûH34ý1‹ ˆƒ 77/;777Ãß¿ÿ˜YÙ8…ÄxŸ?c`}r“á Ã/ á‚B <üÜ l,lð„@,Œ@çüÆõ;·@y‚á0 @ü”Xyyþÿúä1#0áüeørþ ã‡÷9b @±°°°2¼zõjEqq‘*0[ 1#b6#0­31Ø8(õÿ?30þZ´$`’‰Z!ÇoœIEND®B`‚HaCi/html/Images/exportSubnets_small.png0000644000175000000000000000127212405424257020005 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<LIDAT8Ë¥SÏkA}Ù$$$U!$$˜ÚÐÄ‚i±èIA z° ‚rÑ“ý„ ^¤§žï{ï!¥SQXX(ÊÊÊDEE…(//¢´´tây^^ž¨ªªZtõêÕ‡x÷ýšššÿ¤} DÓ§ÙlÞUVVÇÂíõŒ÷{Á.únåû‹‹‹ßã~x‘9¾—‘‘±<''gYVVÖ\ RSSEJJЏtéÒD«pöìY1Ý^q3ªåãQ´ËÁwy¶º¾¾>¦¡¡!6¾®®î´Åb9r¿›ûµµµ;!¶£ººz'dwA`7Øöƒ<; bè âèÇxõ8î3Ïo°áyHï ˜¼0Œ"p•g…ê=åÒ'_B~~þ!"µÃjµnjooßÒÒÒ²'œ%ê;óýæÝ<A˜ŸÁ››”— t”pÆ]¼x1¯˜._¾œ›››Æ„ ¼N@4–ÉŽ3&OˆMMMûš››÷)CZ[[§bÏ~Çû]ôÝϸc%%%ñ|/íÊ•+—333‹˜/Ïd2&ª[è¿Õf³íÅ®í8æ±Y `Ì«’ƒF“ñrì³ðx.dÒùXRgggBoooÒÀÀ@²Ëåº022’166–çóùJü~e ¨…Bµ NÓ4Ë$t]oªUïŠé—ɘ„ÑÑÑCCCCÛø^4ßÝ€÷·3W¤âÑ£äɶ¶¶ªîîî&ò©Íg†²…wÉD¦ÃC8KR@‚ ñä`$²j}¸%Êx¤4Ñ¢;UÛ¯S T>8™4‘1&ªa¢Œþþþ ª#Á­hÖŠa»Ý^ƒôªÔ5ýëy×@¿<¯®-xùã‹IP+Ä3¸¶ÐßÇx?2¸ÖãââìÌÿ‚ò¾2^µ³`ÐHš pýpQÛñ¢‰X¯]»æ¦RI¢"ñ ~ýúu!¿N$\$¢jûHÆN¼ØŒWk1¨œÄ¼JÅʧïUî{éçã[=òQù¤r Ñë…˜ƒ±’ÒêÇ™1¬ó€˜Ä¬”Ñ* “P‘àÙ©ù£^ÆDV¼=ÈdcLFnër¦²Ð”¡†Œ½H­ •Dˆ^‰y‘¨‡k+ëé B’¤µ±H~‘uAP>[ŽÀT(,. HÉ‹Œ˜hdš¡’‰%†Jå=¢!ñüÔµ^—ä€Ttªš׃D¤yR(lôiScX}Ch¯’"0³ igÂØÃH¼>®lž4^=i¬2rô304ˆL|ä‹gppp­c³ÛaE`„>ø¢ï÷#÷~. ¼ß÷¹-ÓLý=È$#$¤SMŠÇ]šô.òPÖ‘ˆŸ\E.VÏëª 'ù2ˆüìéRĉ”θQà”êë!Ÿ”öCØ7“³˜ÜºN‡ªÁhð¡6ð¦ªß­L®)Ã!d`´Z/ú‰R/ùâ½ÞNòÛT…ÂÀ>*ËžUQc¼ •ªú¨ï²}Ñ‘Ž›õç[çý["  ½ fG:Æ„]L<€ÇU"zÙ”°©BË=”Í6ý%Þ÷€V¥ª‰]J=—ìk$û%©%*"êà¢lVµ_•Qõw ­É #+·Ú…b|3çï…wQN£Äþ~SyWr€³ÁVcµû¬£œŽbH?Ú6ØŒI©mò8yàH€­C€d%†ˆ€[mŸ‰Ž—ÖOôƉØ(FŽBàkÓgqï¾û®X±b…xöÙg'0+ÅöfP$L&Óvõ l†Ä0Òñb¨f6W¸ýãã-þq_]0à7÷õÚ«+Ím5ÕU]µ5ÕöK½½¡ÁÒ×`±8¨VŽJ³yÀdÊä›*Ñ/&uwZ$Qq¢££ÅüùóÅÔßmh&˨"Aå8a!ó®ÇÛîÒ²2gw½Öér—w÷9ëZ{lmvgK«ÝÙjíèí°´vu–×6ö•TÔ8ÌU5õ m=öÞz6y•%ÅE]äAI\Üé…‰gâÑz¢ˆ‰9"–.]*¦ÿn+‰U(Ù߈+922Ò#¨gêëjv{Oõ¨×ï-1×øÓs¯ -]þüºk!Sm‡aªé¦švÐ&ói jÛåÕúkFICW¨ÔÚò&»¯ÂÖ3t¹¸j(9ùœçÄÉ“?Ø{F|óƒ,ñÀsëÅÇýf%°hÑ¢ÿ…… ŠOà‰Ÿüä§â|j–ÒdiqQzEU­»ÌÒ1ä—¹Åf™W\!‹êZåÕšfYXm“…•M²Àl•ù•6Ð,M°IÏL² Ò* ª¬}/TË”´,ùæîóqQ/g ñ’IˆŒ¾=3þîY$ÿ,KüÍÆ’{SóÊÚRM² ¦EzƼ2¿ºU^ª0RêÜFF“GšÚ}Fa—ß(´Œ¢¾ …½!ÃÔ2r:Ff«ßH³yÄÚ=®Â©ŸÈµêÉ©YrkìE=ê•ü+beÞ—Ä“ëï2¨¥sÄ‹Yß\¼º$ñ|^¥LÉ2IKË5Ù7¢ÉÔ–Q9( ;KÚ°~ãoëÙ~ÐÏq§¸Oñ–¶)½=t$¥Ðxj“IŠWÊÊÄSÑËï./?³B¬,4–GWI" we4¶€á—rk¨òªÀKM¿PAÍ0ÆC†¼î5dÇ!놴 I9ÔeˆuÂ14&3+Ûä›û²õÈ_µI±æj¡˜µ„™çÜ9Ï=ù°x­¹KüGüÒÛ—´ƒifýýô¾Ð ¾·2 ÏÚtyÃäYÎ_É-Ržk5d2H‰-†Œo6äiÛ œå¾ Ç N]:FCl|rØ5 ÍU•rõ¡|}ɯ[4q"nŸÀ½Ÿÿ”øYS½xÇ%Å«6=ò¥ýé µ×ç‡^³„V&;´_d ë;K}Æ)KH&7ëòR›!ÓÚ ™ÞaÈœNCš0¸¬O—Í.M{ƒxÜ/µÀ˜vÉö¶y&³X~wk‘¹¡ ãuã“9ÒxÈ$åœSš_|ùñç±"ò|áñZ{›XÇ˵.M¬n×ÄJ³1ÿ¥KÚãï'kk¥ék⫵W’:C¯¥ †Ö纵s ^£kÈ/ÇÆÒ«àóË‘QŸìôÈÆîë²´¥‹JT'cÎ_1þ}[¶¾,ºE{BzT’4–Jãà°”?äH$6×uŠ?ûêËÊŠ;“Ðe¹Xq"U|à×ÅcR¼Ó§‰7Zuñr¹ö©U9ÚÓ.é?ý]¶þÚ‘"mõÉ*í­‹¶>ŦíÈhÑödÚ´}éú®”zã· åÆšã%Æ÷On/Ö?³±I;FŒ¹±†ñh®”o5KyÐ%å¿–ûƒâÕ¸r±ðÞÿföGÀÒ;Ob!>)ü—UâçâC:n—âýAM¬±‡Ä›­ZÄëeÚ²ÕyÚÿL×ÿ):]nk¦þƒmYú3›²õ¿ßpYÿëËôeë,ºXßmˆ-£†òø}xü[ùRnåm¦z½Ô$傃Ý.ñàãÛ™ïiU:À²©‰|'ÔG¢Ä’û_û¯ÄÏ›»Å&l‘2â#¿z4±Î ů‚ëûµˆ!ø‘[›½ZÄ®¾ä¨a|6IÊ'ˤ\Ý!åÛ]R>ß(åwòý¡¥oœªÿÛ~1©Òü7À}jõ™šÀwJ`ò§>¸DD.|T|ce´xá|¡XÛÜ+¶xƒûI¼}|d—!Åس_ b¤\vBÊÏ`øWr¤ü;Œ¢p<ôÐ鮡¨÷²­â‡;3Äç[Ç7ÿ|ÜL7ün˜$1¨­âçÅœÈ'Äý__%¾ýÎIñÂÉ\±®¥SlìqElqŒÌÙÒ7.O"ÃXOÈ퇟E…£w[¿Y üÃÿ ÁëÂiUBæIEND®B`‚HaCi/html/Images/free_small.png0000644000175000000000000000156310665316126016045 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<IDATxÚbüÿÿ?% ƒqp @ðÈ•~µ-“:,Ýׯ\Nf‡Ø6ÔZ:¦ÄxÕ±Õ˜ÒÿÇâ0ìÚü rˆ‰$ë@Žýý;A]YjW¤µ…•+ß?€"Ú WEÿ11Ìv·Ð›_ëé(sñÚóî¼J –ÿ ÿÁð ¨æ% ÿþü3ã㜟좥 ¡Ìбû<ñÓ2¹™6‹ïÕ_¿ÿÝüüøÃS½ÿþ³þøû'UNM°§ÂǘÓF\€¡|çy†]Ô3±³Ìb`üÇ@L|ÌÜŸ} ¸ÙVþþóO¬“Nÿküå`Xææ¨4uj˜§•¸$Cãžk »ö>ïgûÏÒÄÀ‰=€bzvçûL 1†ì@+kF.†uÿþ10þùýÏ•WœeWj°AHƒ«;ƒ8CïÁK Û¶¿\Åþ›¹œ)äA¾ÿ0æîôûdqIJ þÇ×âe"* avW:J"3øþóŸÉЮ­é¸úý]ü÷üúýùÿüA¾ÿ0!ðíî÷3W0<÷S6.*#îüé±ÔÓ±ªìÑòYìýýû ù A¾ÿ/"ÿýôûòï[/'0$ÿïýê¿ÝÖ®¥ñàÿíMàüüù ö ôˆYHC›õÇã/ŸÿYñ«~UâagaøôÿÃé'÷¶.ýþæ÷S–&ŽßWþ3ÿƒxšñ ÌôŒˆ‰ã;ç?¶ŸŒÏø¶ž>ýžá9ㆋ¯î3ì\úë˯Û\1ÌÌ §ñ%0€bbþËÎÂÿؽ>ÃýðÄíç GW1ÿú~Y ¨y'¡ @ÌjFÀ(ef`bdüÎôCìÍm‹×yë˜ÿLûÏö‡áóo†ÿ,À ö#†ÄHiv0P­!3•Žð#IEND®B`‚HaCi/html/Images/genRandNet.png0000644000175000000000000001306410512363354015754 0ustar fighterroot‰PNG  IHDR@@ªiqÞgAMA¯È7ŠéëIDATxœí›i”Õuï5ß{{¸=¨»ÕR M‹š…!€ ƒmŒež’å8ÄqâÄñržãøCží—¼88ŽWX+„EÞsÂ`cb ›É Ì µæÖÔ“z¼cÕ­áœSïCµÔ“ÔjÉÂùð¼×:«êVuWÿïÚgïsö_Ëÿߢý·¼õúeYôÐÀÒc¶-ò¿£+ï=›Öµã¨ÕèúEhÚZ4š@«…XŸÔ2Jõ£èFÊíÃüÇÞ½@ø^wï½!àãKÐÒ7cèDg=†Þ€(*†8žúzM]›8†²H¬ºä¿3ìmáÇÝ]ïI?9×ܳæ|,ã> í£XF3Rˆ ݲiÉ6P_SM:mkŠH $•0¤äV(»¾+’né@$sò ÆÊ°¥ûÍsÚ_η­­£Qÿ†ö{XFaRRŸmäÒe\~þtžwÕ5º£f@Y•)ˆ™gÔÏ3äŽ2RÎ10’çXO™|Ÿ¢2¬A¬¥%|ù ïŽý-ïôö“~s.¸{Ío²îÇÖW ‚޶vîÞx¿µæ2æ7Õã©2¹(G.Ê“ pQ¤$‹äE’,R”Éo—2¡HAq0&¿Ë ¼Ï"5°5ð£.Fý/ñÔž-çÿ/IÀ§Ö}Cÿ&¦žÂhn˜Ï~s3÷\y#UÕ6#Á(£áQ (Ó J²D… ÂS M‰²B—¸…—3{S`è …G.ø Oìú úÕð~LV¬ÿ–ñ§(BpÛ%×óõÞÇò–ù …#äÂ<Y˜´(‹”Diâº(RÊ2Ѻ0àÂHC"ä\9+Ü_¤©<ÓHìš +(úßâÑ®¿ÊgK€qVÿuýúoá˜_ ’˜†ÅWoý ÿû®?$]mÒ_9Æh4FNä?æ¢Ü”ãX4vâ¼— a‰¤™i%À…9~M—+‚.t”P‡S·¡Ê¼‚euUt ÿ¿>¹î/°Í/#$¦iñ›¿È}טÑhŒá`˜11nLŒp’–Wy*ZeääãI¯I"] ›=â5yèKñdÌ ,Îzìy Pï-ŸX¶ñ]TŒeØÜÿ±?fóÕ7Œk}t ÀÉà§QŠKz0äI˜n RS–°ºûj`ÌŒq% ™.ævŸ ôñëúxˆÙ¼ºÇ|C«AJ¾ö¡ßå×ÞLŸ70ŤgM ¢€§y 0[ SéD;B8ɹœLÂ4Rb=žè¥£ £o×Ad›TYëñ¢ç­Œœ NðžõW`òI í24Ò@ h(5JLèËqô‹ñ>´ñþæžß'åÉ‹©m²—ŸüÛ]"=B9©K|OQÕ Bˆ  !Ì ˆÛ‹e¿],]‡Q÷Ÿy|×ç€Ê™ð‰uÃÒÄ6ÒDr<\% MO„©@$X:¿‡?÷礫 Fñ‰!ídÛL†·€ÄÜ¥%p4ÆÞ1ñŽȲrRJ €´D¯ÐV–P—·O?VýÛyð_- ‹ {Gïäå£O+ð´brûÊFLýk˜zšPÑÚ\OCC†åŠ„”Ý·!¥ÉgoºLAåØ)Ç÷ãšw•Kh„ÄŽ$C/YvšÄÂH>.)CD8DLˆ†¥ÏC)5’†RðJœ_†ŽÁ…ÅS£øÀìÈB>fQíÛ€±¹Pm¯ÅÐÛ k:—²éö H+ħB!,Q Ëüy¯DÅ• ·ìçÅ\´–&Ta2®‹y™Ÿ°UÄÇOÆð”Ä‹Øêà÷˜`ž¿¡òÓô–^cÄïCÉ€”©S—ªcaíj2×’1¯D7³tÕÁž¸$·õAK0E6‚k‡á±E¶¯eÂ+y­ÿGs±;èİpAZJRò˸ÊÅ“<„ ›ZµÇÓÅ-h´“&kf©¦S™„QH)*ÐúñƃÞ'ÓˆQEŽ”㧇ÿ¾Ø”HRÞä#ï+it ¿<Ī–5t6n¦Æù¦YÏk wôÃÕÃ3‘l›`4í°¨îN^ëÿ s V·´“27#c-©§eQšR4A€«\\éâJŸ€ØŠ¦À5\†Ì!zìúR} f)XB+D eJ|7fà©4Ñ  6Ð_ü!ÏøŸµ(’„±j\SÇ›* »GØ5ü<šö"ÕV)c%‘ oÕ¨+K`MR°­ dÁžZ°h&WÙJ!<=ÔÕc™›+Sk±øüJ¢|¼'=<åhÁÔñØ’(S!-Ih…xŽG©¶D¾>O¾>WãRì6ðßÊ@h$ެÚî sÞ<,ãuÊ…Óuˆ(eçÐVæWõ±.Æ1ªé®‚Õp~ ª&M$ÕDðZ#`Ô`ê]tçÞä4Á‘AK­¤>}'ºÞ ´˜¶Ž**‰Ö•;üôv<`1åD8k B+D,-&ß®­`Ðß2¨2×Иº i¼ÄPit$øì{]ÛF]j£A'q|+\¨χjì®…Ñ4hªD×ð³€?;‡ò.kZ®Å6.*‚¦¥T…”åÀO" ²¢ä»Ÿ>fWIè,ÂEypMèIƒc-$kµ°cèÇÀI¼Ú)d ÜÃPáYdÛÈ«(ZðnúÐ$V6ìÀ¾ZЕÞÁ'‘äf'`US Žuk*¬lL¦UàÊ9€'à”à§qQ„{«ÁÔ#Ä1äõÌ™€²(°cè9–×WQm^Žg&–ÐVIFØ64TüÜý³=.‰BòâE"1‚¡ÓßíQôÝ$VŸxdE„v87ð“å²±$Œqð©áì2Òˆ©-HÙUÜqÑu|û#Ê×~û3\¾ôB²éêà:[—bÏï|K'cÕŒÇîÉ^13Ó^¹ ‘+šŸÃ±Ö+œ»ûaã"V§Ÿ·á…&x¹ Š6X þÛìz€w†žIÌ~ö,mÓºv2Ú_¢k›‰–•fÓ¥¿Åç¯ÛÄEç­œñç11*V(hàEnùû/ðÊ®í`YÐS¸§<ÀIHŸ9FîéeUö›Ôë ézðø|ôÕ9Kè~,¯Îƒ×“9:3ôs¸ø¯¼pè@7ÉÏìòéu¿ƒn|]5¡bn^s ÿãæO±qùZ©Œ%"DqDGɹŠÐMß½áf^ïÞAKhªºEµ?¦§Ø=ýu§šk1¹wÍ÷©vn'ˆ¡>„?8‹½‰¿õ$!ùyC2-U²“Œ/G9æþ¯}”±`'‰¹ûœNë[]OÚü¦¾™(dÅ‚e|õÖßáÃ_‡¡k¸¢B¤¢`'žAB¡ð×=ÎsÛ¶CÊ‚¾Ò_ðÔÞo2múüTQ’ ’[€Û1âD«-…߀ýÕIÒÑ—i€¡@øý zϲcð Ž–Þ!â*Ìå[¿÷âåØñ¿`jW"c>zõM|ñ–»YPßHoÐC¨¦‚‹â¡ÄÌkãçB bCqé%‹yñÝ„BB}ê^æÕ<ÆHiJh|ê016ÉMÓУìýç'e ÒH¼º¯²›áòV~1´•QwP>·‚‡Í+[±ã'0´µUVŠ/Üy'wl¸šHyì-øl÷ht¬lbÇ/ú eµsIË-l-}‡IVpj¼ jsD3ô¦Å­îK&05 ‚ʆ‚Ÿq¬ô*o ¼ô̾œi`“Jß©­Í¦«øòæ°¾}9‡¼Ã„*œ¡Õšž|T3ï)MÒÒ©ÓÕ¥£Ð )³ x è?=‡ó½4g†c%š~ÿš°­ûžüùVH¥A¨$5v’Å*C3¾Òµ½l„êgÏèOhvöŒåXÛr=z¼L³4®º°“\X@·c6vv2XÊÓÝÓ–‘¥Ê¾öƒá·¨Ìyž/fÇà+,Ë8Æåql^NqŸ‰Wg‰ ÒàRª)áf\üŒŸMù„NHh'uÇM>Öbb->yxgmÂ¥Bv = ¸§›ŽŠYÕTOÚºq°0ƥ혢"‘²nå2 Ë`סb¥,ªRײ¬érUïg_®wŽ$Dt¼D­½ZûR£1v5ê­ZÖ/^D]Ål”¦¬iÊd•øLk[ oÕ'ùŠŠ#öäžDÊÑÓÏÇ-Ê©²7ù¾Ÿ®­Ë°dq#ŰDY–ñb—e‹›hŸåHÿ0^у”µ”Œ}'ÍóiNá`a.ËÕ’‹®Ã1oAêéê7X§B¬ŠEu±šº|/ƒ! b=>¡õ9‰Ãëó`Ì%]Žäž¤"Žž€ýc#¬i^‡m®ÌçXÕ¹€H÷)ËdŨ$Êdç9´Ÿß„† –’ Ï´µÚÔGXÕ´œö† ÍV‰#eoʳoí¨aCÛF.iý6)ëOzºjbé1ÔùxšO`øŽO`'+ÌZ¬aEéJ't0¤ZŒŠã$?1OAˆgÂóÍP±@Ê»Æ%ˆçfH·u\CsÕVTìÜrÓz6nXF% HÂ8L¢.$ÉÁC#üâµ{¼$Ô0uRÇû‰µ}Äê1†ÞŠÆ ­F×u☺ #Z®ŠÐj$‚Ä)]M iÓÎc-±•ŠðŸ™Gôv Ì÷“ä­6‚ºñø¬lBWvÕ€eÀˆû"w} 8<×/IçÞµ“±>T±¹ëî5djMB95ò± Ö%~1p¤ÌÑ]c½á‘|³Æøëb’ 2F³ÎAÝ¥™ŽeH¤6ð©PºBÙÕ“Bým;­$TÐ⤨ñúðEÀ;ƒÌ›ý¹¹»’Wl ­æYÐj–vÖ±áÆ–)¡è”F„Ò%"–¸EAiXáŽ@%üzJaµD8KB춤rDp 'ù $ß¿Ðà;íÉt»¥ ”94MC#®9hZR›,ã <Ì‘y½ÿ‡@™/Ý´úëÔ¥¾L¤¸àšj®3 £q(BS:ŠØR(M!´i-‰ÔJÅS"¸é€O o'“´ÁÓ­ÉäwÙ?Äs‡þŒj»@cª‘”U¦ÙH%ÈW†Ø3¼Ÿˆ>’Œ5·Ã3Öêyܸ⇤¬«uËn€švPs0ÕSÜ“†$Ö&8vNÞý'ÍIyŒ¡!ï }‰7úŽ/ºêÓ0Êñvâá³çÓe <‘Ò±¤ö)e™ ¾ iMG¤/ð‘2ž3è)ŽLQÚY¬¼:¾ß6OÁáü?òFßS$1s.š<óu¹C¹æWï£Ê¾)VFÚ=h }ÔR—Ðg6gâ<²#";BXI"ësÔôtÙÒ /¥'úK²µû~’y‰¹¯6s¶¥²ÆöÑR½jë†86Òþn!%¢£@XëŽ/œU…9ø—*Ï.Xð½%ð\KR¹¦}¥ÇùѾÿ¼Ù0SÎŽ€c{hÎì!c^…eÖʽÄá²¹‚lö&¢´s±#AjIåÇCK“`G)%G ÿÄ3ûÿ8ÀYLŸ=ݹ½ÆÏÈÚdŒÅ ;ðF}2M^%í—!À7’çG΃ç[ bBJ7<Æ®á¯óÒ‘ŽpŠE¹È¹Ú23Ÿ_ðGÔ:¿‡ce [Š2¬)Àª"4I]Àé¤d&îÌ&«É½é¤›ʘ1+Û{ §´ÄÛÿR«Îåž!›‹Z/§½á³TÛ·b›UDZRušÐâCSÍ>ÔŠ¤¶Ïˆ-—L±“ò–aFˆôdŒ²€P*JÁvºÇágI‚—s°Õî\ïÓ€*.o½‚EuwRí\‡¥/Ã4’ˆMÁÄ<©÷Ä&2-!Å ‰Þ|1B1ÜΑüòfÿ«@/É·~ζӽWû5 ÍüêÅt4\LcÕU¤ÍuXFºVƒF]7±t ”(T\"RC¸ánÆü·88ö6½Å}L,²œó}„ïõÆIãd@-ó2ói«m%eÖ¡ë,­šHùD²B•® 1P>F¢e—dL¼‡»JÕ[g2y/ÂñêéÕ¢g!ýZ~-g*ÿ‡ží dnÅþIEND®B`‚HaCi/html/Images/groupMgmt.png0000644000175000000000000000433110474157163015713 0ustar fighterroot‰PNG  IHDR szzô pHYs  šœ/iCCPPhotoshop ICC profilexÚ­Ž½JÃPGÏmEÁ!ˆ7áâ .âÇÖ1iKjI²5É¥Š6¹Ü\?:ù>„ƒ‹£ oPqœ|7A"dpÁ3ÿþü ±âuüncF¹5AÏ—aË™G¦iÀ -µ×ïoäE®øÁû3àiÕëø]þÆlª>ÍL•)ˆu ;³Ú‚¸ÜäH[W€kö‚6ˆ;ÀV>œ¤òÀ1aƒxÜaÅÐp“Ê]ÀµêÜ´ =6‡Ã+7Z­–ô²"Qrw\Z5*åVžFf`UTûªÝžÖÇJ=Ÿÿ%ŒbYÙÛ “ºÕ¤'æô[…xøý®ŒïÁ `ê¶nûp½‹Íº-/ÁüÜè/ñÀP=ú‹æægAMAØëõª cHRMz%€ƒô%„Ñm_èl<‹XƒçxIDATxÚÄ—klÅdz;3»×¾vnBóÈ ´$-L\R”P0*IZR„[Uj)'UU>ð¦©TÚO ¥¨! Â4¨”´R#“|HR àHÈÛbßGöúÞ»Ùé‡{C1¯BÅHG«ÑžùÍΜ9+¬µ|–M|Ô௯Îôû«}móžçÒÚÚ‚ç©aª÷,]Uü¿¬úfK¿§Åãž¶hž²´f[™6cÙöØðí‘jqïÅË•Ž@~ØÀIéG\*5N N 1u¬°N†¶észdb†a×¹Çà~PçÚ¼ÕÙ ßí:!åíđÁX0)˜Ä€¡ðZ;».÷æþÇ6›‘O à|Pg¥&ú+5j?ƒënÿùy ™¬ *5A9ˆ)J¥€ÉZ ^÷Àñ(ðÕºÈMÖ-ÓO£³;ÏÀ wrÑò«¨T•KUŠMˆÐø}Ÿ:Àd]ŒLÖ'Ïé…¸‚†e—+¯ù9†,A%e¢P, B>õ ¬ÖÅ“‘Kßés{! ÀÑ8Ž¢wá"¾0ÿ~ñ_ìÛñÛ^ZvÑO]z$žèš}¤êG <Ò‰Z´àüE_áŠïÿ€8ŠJÖ²ò½ß?}«î9.Vÿ£2zÿÙ¹aꥋ K =p4¸Ms\j…×éì>ûÏ·üaÓÀŸnñz\‡Õ®cûL Oݪq£BðÐ×ÝqL HWïF(ˆ*P+A½i“cPÜMFk*“á.€?^ïõ$†áÄÐA’’CÞ¤¬úûmzø˜.ûÒï7nz2ZˆªP/7¶Ä 6l~¹pûÃ/Þ±:1"'$°Ö!6‚Ä@bè{úV}Ç1ˆÅ·ìÚ¶õ/&¬‚j! ²…H´ÔwìØù$À½?ô{’„¾ÆŠEÓ gÉ,¼ôg¤iSÃÀÇ΄GÛ›ö=zÞo`fÖži›I-¶•2ë7>¿ïŠ;ÿv À²sÔ×@ô½S-0ûs 8wù*ÚO]D¶­ƒýÛ‡‘ûÎb¹õÑæµ¥ÀšG†ó‡³ßPãÙ3Y¿+`˘$Í/ag¸äó¿¸ëáå ùÉOËZH‡Fª.»ââ §ô^E[[qùGÞ†®ÊU&«}Q_´å¹·¿íi÷ä«W|™¾ ΢XªP*O2gv'…€ØÄdµÎ¬ÂªÒÜ®bnñÒŸR;¼ƒñb‰yç ô4Ðí ÛÙúìý¼üÏ{pF¯¼'œó¾cxÿພz®Ú²e´ßZRÒ{^_³ÿO5„#Æ—©×B.¸ð2¾fã}“¹ñÃU2Ó:Ɉ3ºÏ€0h®­ai‘ÁÌlšè:?7ðÛzé=yÀ>þÊ+óÝ'·â)çi´Rø¾Æ÷5iš¢¥BkI&h­p]‡4µ©K½â›ÒØæäÖ›R›%N JiJðì;®Z±mÛ¡ü‰Ý-èæäž§ñ´"ã{d|ßÓh-QJ"¥‹ë:TËo±å©»)-¾aì­×™sÁˆ&q ŽÔ?°‡ÄÊAb¦–€Òqœ¼ã8(å •Âó4þQó5¾çáû ­Jºíbã_ï th;ž“’ÁÞ¯0û¢K‰ŠL„xŒŽî&Š¢¸Q]M9Ž#Ήã%%Z+¼&DÄû/D³ï¤ÓÎâ‚þk)¾±aÊÓH>Cëždû«/5‚—‰‰"aí#kys\R(;¤Ö–LÊ”ËËq''R­$JK<ÝT—yÏk(‘?s‹.»éÝ_|'ÛÅ6lXGáÐ>Pm™aÝ3CìÜséZ¦µ™RÖ57_ÿ`mJ éGô¥&EJ)%Z)´’xZây O¿{;4žÖ8ŽË™_½ÙÒÙœ¼‘½–6²3(÷ÓÖÑÁ×ü˜ŽOà@)ìòãs4 ¾?[p×u‘Òi€(‰Rª£J+ÇÅZˆã”8J)•Êx§_Î~æsPöPœ¶˜øh­™ÑÑEÆsáÈ^žÙÝJyÆÒ÷íè^°`Áêöööþ)A(¥»òìùÝ7nÞt°GÊÆÊµ×ˆ¥j¸îÔ„¹{÷f††Éd|2™ ~ª¨ —Z¡„#„« ®2z°È«{ÆéêròRÊ›æÍ›wS¡P(ApóØØØ øELŸïûÃÙlßo4Ÿ•J…á»–2«³„`ð©øÉo†PJ½Ï‰µ¶T­VçÈO0†!q£”š`­åù—_gyGÁúöP,?ÌO¸É=ÆÉsBˆ{ßZ‹1†0 Ã(аÖrêtÉ…½sËÊ;£%å¯Ç9Öå[kGÞóNÇA@¡Pà­ñ"¤)#¯ \©ÿ/w#â3ÿ;þ¬þ3ç\ÒŠ†NIEND®B`‚HaCi/html/Images/horizLine.gif0000644000175000000000000000146611030471134015646 0ustar fighterrootGIF87a çÿ€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, ÿ H° A‚öÚ[È0!€‡!œH±¢À€;HaCi/html/Images/horizLine.png0000644000175000000000000000017711030471134015663 0ustar fighterroot‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`Ø#ð±± jÿÄ?‘Í ’<0mÍ# å‚~üøö¹,Ag#(„€ø!}E}W ÿþýç aFf[VVFY.E^>QnÖÿ ?Þ``úúA€á3///ƒ€€ ÃÁƒ/»¹¹%ºÄ‘Í B–ÏYÿÀñÏ¿ÿÀÂÊ‘A—ƒƒ•‘—¡´sGYw¹Ç, ’Ï螨q[àÏïßA dñÑþ?$”x„ĸøËÇÏ Œ@³¿yÌÀÅÌv4¯¨0P·0×/_^½zÅ , ´”›{C`ñÖ‡†þþwb`d´gddñýûï?ƒ”(ƒ¬'ƒˆ;Ð"V†ë÷¾0üù«ÑUزU´¿Æ» 䉆Y7õþýûÄÀÀäL¤zœœl J@‹ˆq2HÈð0ü:æû§Ï ¬?ž3ü¿÷™AŒƒÊœœ² ÿþeøüù3ØÑ bhcæñ‡ÿÿg üû÷·ðÙ¥Ž-ĸþÀKß¿~ÝéðŸ’&ù¸YD…ØDÙ$Ä8þÍúÌ>€.±ÿéû¾0œ:û†áû÷¯7™YØ%ÿýcä“zRBœƒARŠ›—áÛ×o >d`ùþõßO°ƒ¹¹¹€éšáçÏŸ _¿~‡2ÌÁ ŒìÅÀÂ5'ÍÛúú ;¯¬#«+3PïÏÏgÛ(Þóòp0ó³2ˆ°113ܱ?þ@ Ãÿ Xª¬ì, /_W—Æ0'Ã/ ÄOþ~~Îðãå`(ƒÒ0¿¸4333г߀% ˜†9x÷ù?çÿ.ÝÿËp{Áu†¯?øä4 ¤åTÔäUŽÆpjÿÕG@¥RÄ4䣞*?¿„0;Ü‘¿ÿ¡:ŽÑÄ…9x¹ÿ1üùþ…áó“§ Ì¿¿1‚2 ˆ;»02‚Cäh|Éž½ýÇpùþ†Ë÷þ0¨® –ß¾yõŽ×ó?Žÿú˜4Þ>§e~^~9IpQr4(#‚Ž7”ßü:øÃé?ž¼þÃ*N•¸ì$¥9Þ~ûÉðâÓ†c^3ü¦ßß@Ì%%N Ç¿}ñ–áÝã×ÀüÄÂðùÅYP | –ßíçæbø tå_&œŽÿöTŠ|`PWSy°Ð9œPƒðìÍ »Naxóñ/¸pPQæcpu–ÿœŒ Ï?~gxúáÃýû_ÀŽþÌ|¿þA’1Ìá¿øë— Îß`øûý/ó¿ÿß>>9ü ‘ÄR”d{féŽ'ŸÞ¼ÿÅÇ+À3Ù|üú‹áÿŸ$×¼ÏßþaXµÿƒ‹µ0ƒ‚Ão¦ /?ÿd¸óñ Ã××…Ä?†Ÿ@—þ†:ìð¿°Ù?ýaxtæ*ïßX~±0üúöö1´Fþ@ ÆÜŸŸ?~{ùö0ÓáΰÉhuƒÒùÌït5xŒõž}ûÁpëõW ~1|…4Ðq@·AB†‘=Ä?€1ÿð `3üþñ›é/°¤{td<ÿ þÿøþíð‹·?˜q;„Ií:,ÚñŽ—•ÁÙZ”áÁÛï ~ü&`Ðý…8ìØÿˆd‚ò@!¸sòÃ×÷Ÿ~ÿüÍðûÐÿ˜~|€d` þ @àšøó§÷ûß}føÊÿ˜°:Töëþ37¾2œºöXþaˆð—fø tÍ“O?!‡†2rˆÃذ9þû_ O®Üføùèp ¯™~3ü)fü//!ä€{ <Ýþ̼M>½æ^pÛ'Xñ8úÞÓ §®a¸|ç¨ù*]"­ÄØåùõW_á!Žâ ä‡bP6ûøæ#ÃÓwÁižXþ†<ã/`éÃÊðé×·OO<ƒ&¡?k ýV6Ç^¼ùé!ÏÏN—ÿþ£â¿Xbàý§ß ‡Î`¸|÷+Û¿TŽv±ePUá§óW_~1|z ¬È GN:°´Ž\â|ÿþ‹áù½' ž¿&™_ ~ÂøìF ÍÁÌÁðûû;x…3@Á<ðØz¼ôâõU~†/ÿp'¡@/^xL"ž¼üÁªMõ´Ty¾õ½ýö›áÆëï ß.úLà?ÿ¡9©hdebbPfePæ~ K88,”>|}Ëððù;†ƒgï2\|ø›á;°~y„ Øäùùá.<ƒÜ @ðÖèׯŸ¼ýÈWÆ*ÛÿcÏ ,Úú èv5`K3Ä]Š…‰á#ЕϾë  ƒ¿Kl!fÿ‡8ÔÄPäa`0ãÖ¨\ ¿03ü¯üÿ#°Íô‹AMZŽAQ\›‰ù=úÝ'¶zÃÀÂÄÂð )ƒÜ @ȶëý´5aøÃʼnáø×ï¿1|ºÏ°í8+C‚¿ ƒ(°•úáÇ?†¯À ýL¸ß@4P!$äÿAè?ÿ åü0ýåëO`9þƒÁJZ€A˜ØaaüÏ #ÎÈÀÎl…; bffVŽÀdó”¿aÆÃÛOæmxȰga|êÏOŽÝ òÈ€Bî€êƒã>þ²àáçô$ôë#¸Öååaeø 4ÿ¨H„¶X€’ ´ˆD/Àdõöýga–ÿ ö ’ ¿?³2p~aàbÿËðñ#0F˜€½26†ÿÿ@͇¯ ÿÿ‚¢ â(`eÍÀìjþý%ÎÄóO‰5Rwr_çhÿ˜ €=ðï篇Ÿ½úi)!,Zc&¡÷_™Àù€èç_ÿøýÂÁ?Ó;(S‰¾1|üü›é/ƒ¥„Ûǿ¤„?2|ýü˜ i€Ü„ÿöå38äAI =y€ê¨ß_¹˜¼ƒ‹SE¯7Õ–<:@1#—*6®±¬¿ÿqFëi 0¼ÿêø¯ß °BùÏÀΠ¯)Àðú(Y@0$Ýÿ‡„:Tìó·_ €­ÉÏÀdóØ25åcxûè/70M `É•ƒƒ[è^f6.†O¿‚ñ`¨üå) þÃÀhþ÷ë+ó—ÿYå•UÔxyO^8{ê5@¡Œ Õg[íÕ¶®ÇV ½ÿÂÄÀÆ ÌdÐdð’”„@Žÿl7}úìq‹Åo@¬ÈÏÎðéé_†çÓ÷†¿Œ| |"²À:BØÇç–ý¬  ‚æý{`m Ä?þdxlàèOÀâDºé+Ðܯž3<|ôOÍÌÆ=èdA€BØúóëçã/_ý`öÚ0ÚEŸ0‚ûÅèé†A±ð˜Þ?Cc_4#°Z•ådg¸zá¨Nb”Pv꥕%М_ ÈR†7¯?3¼yõX€ìf\Pzdæšñ“_ÀÉð°©ýû÷O†û·_2°s©ø…Ç¥ëz§þß/P>xñÃR˜>þBm}z€• œ?À9ýÿdVPÒùòÀ7HèKr‹Ù€±úŽA@߈ƒ Üd†5¬þÿûÃðþí;†'÷ï‹Ï¯ LÀºÔªdö›?~Íð èCxžÒÿ€Iáû— /^ŠÈš9:ƨÄ÷o_<}É_fÉtôgÔ$ô*R\ˆPÿ‡ìø_àôŠæo@|ò¿cAØW¾}ñ ƒ”´°œ†:0Äÿ@ìbfö?g¸váØq`ÇÃjÖ_Á¹æx˜þCôçÏï /Ÿ`ÐÔ–µ ´*Û¼±ùŸÄ ûX§q¨£äƒ?@×2±0‚c–Œàއ<$ÙÀÿˆeyEžÿûÀ § î+ƒëCh¥È´ýРOŸ¾Cœ¨ñÏŸÀf¨(ÿú°Ò“W „—00f8ÏÀðÊèEÐ9æÏb ,+PèÿüúLóëüoà%Î`2ù uPÆý y £ÉÂK“‹¡'X‚•…Ø®ùÂpõÒI†c‡N0¼|ú‚  î$°Va‘“`PPT–óÀî((j€u#ÔьȎ:š••• ØZÀðåó[`‘Ë"@, {€=Éù@ÀÀ ®¤#¾ßÿlÿ@ⳑ¾¼ ¼8ýóç?ØRpïì"Ã"Bþ7°ÒùËà¯ÏÅ`.'ïröc?¾{fßüoŸ?f066dPÒPǃ¾‰.0yqƒkÞÏ?2¼~þ# @jÿüýÃÀ ljèêë1<{üXéýe  Ã±t°…!ƒgÂË×2¥<ùüæ\>Hè#;þ3°JVâýÏ`©ÁÉ $Èw8(½‚0¨EÉÄô<°óÿ¿}ûÂpþü9 £þ2¨k©ÕügxÂužAÏÐ ¬þÁý‡ o^¾€è‡EƒÌc–d††F ·oÞfxúðƒ„¤?@ëp ãA!àNSý}KÞ±=fø,“ᙘ–8¹˜ÀMˆ/ÀŠÔCRãûÇPhÊÆ`ÈÉ * w4rÆcåfcæU`£ ’iA£jß¾e¸ví:°ìÿô#ÃÜŸ¹ {ÿMgøÏö•ABBج`«g6ðþüË_ôŒõí/~v`ãQMK…AßH‹““ó?@±€[ìÐÐGÄ‚]¿™É/^ ö\© ê¿>û²²¢ÌÀ¤ô—Á\øƒ–2#+Á+P 4xMÐÿ‰XšÛ˜0póq{ÿÁ­šcß×0\ÿv„Á—£èÁoàöÑoа#0„„äm¨÷o?0üüñ•AE]‘‡áùÇŸˆ…áÃ-W5¸ã™9βá8ã6`ò”µ0gpä°cP“rcPý§ÃÀñ\&c”H±Âì¬À¤'È,i¾€ÓÃ`r’–‘dPV–†dP }ÿÿB†ßþ}ưà[)ƒ0i ]7öY¥гV BBÀÊí+#ÃÓG^=Œ]†oÌ_ ÃZ†`÷ ‡ÁAŠAší™’??ɰˆˆÿvƒ¹åÙääþÉ1Xÿ²fàûÃÇ ó_Zÿ,p±12ð‹ 1|zÿ\T›ë3¨¨Ê3°³0€Ó?¸/òó+8ÅÿÌ8|â=Ãð@çƒúC` PôePzöó‡OÀäÃÎOÿÿK,`Ëöþí«çˆ…á Æg Ñ ŠÌ|t€†È£y‹‡2=dxøï!ÃaÆÃœöÚÅ@ŠoM>“¯>#Ã/…¯ ¬ÀÚUÔh!?ÃßwÐY ¾Ï??»”¿!øÍ§O‰ËÎç æ,} ·®ÝaÐ7ÖbÐ7TÖï€`üvùÜ¡« o. Vuhô±`ÅÊ ËàLT¼@šAÚèFö vÃñ/¬]2eɳW®¬)²òhü4æLN0lVvq|«fà6‰Oð5íGׄU_1Üüu“áÜ/`9ìƒó (yñ#MØ2!9þ´ gƒÆÌÑ?¡˜â1Ðh3Ãì1ðªöƒ·Ã™Ïÿ¬[¿rý…#ëÎE_®I>PÌ€œWºþÖ`ÊDƒ—ÁÆRÃ~6‚\}ưçɆ÷,O~¿b;óìóŸ{Ï~)~;á?33pãTkèCù<ÀTªÂ©ÆpáýYè(š'þChU~5† ÇnNãKV­_µenÅ  Ì] ~@¸&ùþB“Õ{QÅã@[ÊÉó{PÌ »O~{öñÂýS›þøòú t ôtÒï;–9]PÜp›y¤„ÚÚ9EÛ)ð33ïõ›O.|K†Î Cñ¨j ;Í$ÁO-áä•ç?Ž^ºiïò¦• ‡1¨Kù €ÍR{ƒ?32s[Þ}Áʰùø†_?X€Í™ëNl®ß uèg$ÌŒäpfh7 dá©sV¿~|ëÞÝ^QšªÊFÂ\|„ ‹Nˆbi9†«JÖ¿ K6Ÿ»{ùܾ•W®;”z ÅàÙQ€"8Íúóû—Œì\eç®Ü¿víÄò—Ï9 éW@üjШõD-o¸õÐ`ÀìxõÊÆVAUßèep~à†$#v¿Ê¿]?nß|ÿÞ›§[½xxýì»wAsbÏ¡ö~Mí1ÝlàŒÍÀ åƒýÉá¤O@ªK6èܯ C9°5ð <…ýؤ_Æl¯AÍ~ µ–°±± jÿÄ?‘Í ’<0mÍ# å‚~üøö¹,Ag#(„€ø!}E}W ÿþýç aFf[VVFY.E^>QnÖÿ ?Þ``úúA€á3///ƒ€€ ÃÁƒ/»¹¹%ºÄ‘Í B–ÏYÿÀñÏ¿ÿÀÂÊ‘A—ƒƒ•‘—¡´sGYw¹Ç, ’Ï螨q[àÏïßA dñÑþ?$”x„ĸøËÇÏ Œ@³¿yÌÀÅÌv4¯¨0P·0×/_^½zÅ , ´”›{C`ñÖ‡†þþwb`d´gddñýûï?ƒ”(ƒ¬'ƒˆ;Ð"V†ë÷¾0üù«ÑUزU´¿Æ» 䉆Y7õþýûÄÀÀäL¤zœœl J@‹ˆq2HÈð0ü:æû§Ï ¬?ž3ü¿÷™AŒƒÊœœ² ÿþeøüù3ØÑ bhcæñ‡ÿÿg üû÷·ðÙ¥Ž-ĸþÀKß¿~ÝéðŸ’&ù¸YD…ØDÙ$Ä8þÍúÌ>€.±ÿéû¾0œ:û†áû÷¯7™YØ%ÿýcä“zRBœƒARŠ›—áÛ×o >d`ùþõßO°ƒ¹¹¹€éšáçÏŸ _¿~‡2ÌÁ ŒìÅÀÂ5'ÍÛúú ;¯¬#«+3PïÏÏgÛ(Þóòp0ó³2ˆ°113ܱ?þ@ Ãÿ Xª¬ì, /_W—Æ0'Ã/ ÄOþ~~Îðãå`(ƒÒ0¿¸4333г߀% ˜†9x÷ù?çÿ.ÝÿËp{Áu†¯?øä4 ¤åTÔäUŽÆpjÿÕG@¥RÄ4䣞*?¿„0;Ü‘¿ÿ¡:ŽÑÄ…9x¹ÿ1üùþ…áó“§ Ì¿¿1‚2 ˆ;»02‚Cäh|Éž½ýÇpùþ†Ë÷þ0¨® –ß¾yõŽ×ó?Žÿú˜4Þ>§e~^~9IpQr4(#‚Ž7”ßü:øÃé?ž¼þÃ*N•¸ì$¥9Þ~ûÉðâÓ†c^3ü¦ßß@Ì%%N Ç¿}ñ–áÝã×ÀüÄÂðùÅYP | –ßíçæbø tå_&œŽÿöTŠ|`PWSy°Ð9œPƒðìÍ »Naxóñ/¸pPQæcpu–ÿœŒ Ï?~gxúáÃýû_ÀŽþÌ|¿þA’1Ìá¿øë— Îß`øûý/ó¿ÿß>>9ü ‘ÄR”d{féŽ'ŸÞ¼ÿÅÇ+À3Ù|üú‹áÿŸ$×¼ÏßþaXµÿƒ‹µ0ƒ‚Ão¦ /?ÿd¸óñ Ã××…Ä?†Ÿ@—þ†:ìð¿°Ù?ýaxtæ*ïßX~±0üúöö1´Fþ@ ÆÜŸŸ?~{ùö0ÓáΰÉhuƒÒùÌït5xŒõž}ûÁpëõW ~1|…4Ðq@·AB†‘=Ä?€1ÿð `3üþñ›é/°¤{td<ÿ þÿøþíð‹·?˜q;„Ií:,ÚñŽ—•ÁÙZ”áÁÛï ~ü&`Ðý…8ìØÿˆd‚ò@!¸sòÃ×÷Ÿ~ÿüÍðûÐÿ˜~|€d` þ @àšøó§÷ûß}føÊÿ˜°:Töëþ37¾2œºöXþaˆð—fø tÍ“O?!‡†2rˆÃذ9þû_ O®Üføùèp ¯™~3ü)fü//!ä€{ <Ýþ̼M>½æ^pÛ'Xñ8úÞÓ §®a¸|ç¨ù*]"­ÄØåùõW_á!Žâ ä‡bP6ûøæ#ÃÓwÁižXþ†<ã/`éÃÊðé×·OO<ƒ&¡?k ýV6Ç^¼ùé!ÏÏN—ÿþ£â¿Xbàý§ß ‡Î`¸|÷+Û¿TŽv±ePUá§óW_~1|z ¬È GN:°´Ž\â|ÿþ‹áù½' ž¿&™_ ~ÂøìF ÍÁÌÁðûû;x…3@Á<ðØz¼ôâõU~†/ÿp'¡@/^xL"ž¼üÁªMõ´Ty¾õ½ýö›áÆëï ß.úLà?ÿ¡9©hdebbPfePæ~ K88,”>|}Ëððù;†ƒgï2\|ø›á;°~y„ Øäùùá.<ƒÜ @ðÖèׯŸ¼ýÈWÆ*ÛÿcÏ ,Úú èv5`K3Ä]Š…‰á#ЕϾë  ƒ¿Kl!fÿ‡8ÔÄPäa`0ãÖ¨\ ¿03ü¯üÿ#°Íô‹AMZŽAQ\›‰ù=úÝ'¶zÃÀÂÄÂð )ƒÜ @ȶëý´5aøÃʼnáø×ï¿1|ºÏ°í8+C‚¿ ƒ(°•úáÇ?†¯À ýL¸ß@4P!$äÿAè?ÿ åü0ýåëO`9þƒÁJZ€A˜ØaaüÏ #ÎÈÀÎl…; bffVŽÀdó”¿aÆÃÛOæmxȰga|êÏOŽÝ òÈ€Bî€êƒã>þ²àáçô$ôë#¸Öååaeø 4ÿ¨H„¶X€’ ´ˆD/Àdõöýga–ÿ ö ’ ¿?³2p~aàbÿËðñ#0F˜€½26†ÿÿ@͇¯ ÿÿ‚¢ â(`eÍÀìjþý%ÎÄóO‰5Rwr_çhÿ˜ €=ðï篇Ÿ½úi)!,Zc&¡÷_™Àù€èç_ÿøýÂÁ?Ó;(S‰¾1|üü›é/ƒ¥„Ûǿ¤„?2|ýü˜ i€Ü„ÿöå38äAI =y€ê¨ß_¹˜¼ƒ‹SE¯7Õ–<:@1#—*6®±¬¿ÿqFëi 0¼ÿêø¯ß °BùÏÀΠ¯)Àðú(Y@0$Ýÿ‡„:Tìó·_ €­ÉÏÀdóØ25åcxûè/70M `É•ƒƒ[è^f6.†O¿‚ñ`¨üå) þÃÀhþ÷ë+ó—ÿYå•UÔxyO^8{ê5@¡Œ Õg[íÕ¶®ÇV ½ÿÂÄÀÆ ÌdÐdð’”„@Žÿl7}úìq‹Åo@¬ÈÏÎðéé_†çÓ÷†¿Œ| |"²À:BØÇç–ý¬  ‚æý{`m Ä?þdxlàèOÀâDºé+Ðܯž3<|ôOÍÌÆ=èdA€BØúóëçã/_ý`öÚ0ÚEŸ0‚ûÅèé†A±ð˜Þ?Cc_4#°Z•ådg¸zá¨Nb”Pv꥕%М_ ÈR†7¯?3¼yõX€ìf\Pzdæšñ“_ÀÉð°©ýû÷O†û·_2°s©ø…Ç¥ëz§þß/P>xñÃR˜>þBm}z€• œ?À9ýÿdVPÒùòÀ7HèKr‹Ù€±úŽA@߈ƒ Üd†5¬þÿûÃðþí;†'÷ï‹Ï¯ LÀºÔªdö›?~Íð èCxžÒÿ€Iáû— /^ŠÈš9:ƨÄ÷o_<}É_fÉtôgÔ$ô*R\ˆPÿ‡ìø_àôŠæo@|ò¿cAØW¾}ñ ƒ”´°œ†:0Äÿ@ìbfö?g¸váØq`ÇÃjÖ_Á¹æx˜þCôçÏï /Ÿ`ÐÔ–µ ´*Û¼±ùŸÄ ûX§q¨£äƒ?@×2±0‚c–Œàއ<$ÙÀÿˆeyEžÿûÀ § î+ƒëCh¥È´ýРOŸ¾Cœ¨ñÏŸÀf¨(ÿú°Ò“W „—00f8ÏÀðÊèEÐ9æÏb ,+PèÿüúLóëüoà%Î`2ù uPÆý y £ÉÂK“‹¡'X‚•…Ø®ùÂpõÒI†c‡N0¼|ú‚  î$°Va‘“`PPT–óÀî((j€u#ÔьȎ:š••• ØZÀðåó[`‘Ë"@, {€=Éù@ÀÀ ®¤#¾ßÿlÿ@ⳑ¾¼ ¼8ýóç?ØRpïì"Ã"Bþ7°ÒùËà¯ÏÅ`.'ïröc?¾{fßüoŸ?f066dPÒPǃ¾‰.0yqƒkÞÏ?2¼~þ# @jÿüýÃÀ ljèêë1<{üXéýe  Ã±t°…!ƒgÂË×2¥<ùüæ\>Hè#;þ3°JVâýÏ`©ÁÉ $Èw8(½‚0¨EÉÄô<°óÿ¿}ûÂpþü9 £þ2¨k©ÕügxÂužAÏÐ ¬þÁý‡ o^¾€è‡EƒÌc–d††F ·oÞfxúðƒ„¤?@ëp ãA!àNSý}KÞ±=fø,“ᙘ–8¹˜ÀMˆ/ÀŠÔCRãûÇPhÊÆ`ÈÉ * w4rÆcåfcæU`£ ’iA£jß¾e¸ví:°ìÿô#ÃÜŸ¹ {ÿMgøÏö•ABBج`«g6ðþüË_ôŒõí/~v`ãQMK…AßH‹““ó?@±€[ìÐÐGÄ‚]¿™É/^ ö\© ê¿>û²²¢ÌÀ¤ô—Á\øƒ–2#+Á+P 4xMÐÿ‰XšÛ˜0póq{ÿÁ­šcß×0\ÿv„Á—£èÁoàöÑoа#0„„äm¨÷o?0üüñ•AE]‘‡áùÇŸˆ…áÃ-W5¸ã™9βá8ã6`ò”µ0gpä°cP“rcPý§ÃÀñ\&c”H±Âì¬À¤'È,i¾€ÓÃ`r’–‘dPV–†dP }ÿÿB†ßþ}ưà[)ƒ0i ]7öY¥гV BBÀÊí+#ÃÓG^=Œ]†oÌ_ ÃZ†`÷ ‡ÁAŠAší™’??ɰˆˆÿvƒ¹åÙääþÉ1Xÿ²fàûÃÇ ó_Zÿ,p±12ð‹ 1|zÿ\T›ë3¨¨Ê3°³0€Ó?¸/òó+8ÅÿÌ8|â=Ãð@çƒúC` PôePzöó‡OÀäÃÎOÿÿK,`Ëöþí«çˆ…á Æg Ñ ŠÌ|t€†È£y‹‡2=dxøï!ÃaÆÃœöÚÅ@ŠoM>“¯>#Ã/…¯ ¬ÀÚUÔh!?ÃßwÐY ¾Ï??»”¿!øÍ§O‰ËÎç æ,} ·®ÝaÐ7ÖbÐ7TÖï€`üvùÜ¡« o. Vuhô±`ÅÊ ËàLT¼@šAÚèFö vÃñ/¬]2eɳW®¬)²òhü4æLN0lVvq|«fà6‰Oð5íGׄU_1Üüu“áÜ/`9ìƒó (yñ#MØ2!9þ´ gƒÆÌÑ?¡˜â1Ðh3Ãì1ðªöƒ·Ã™Ïÿ¬[¿rý…#ëÎE_®I>PÌ€œWºþÖ`ÊDƒ—ÁÆRÃ~6‚\}ưçɆ÷,O~¿b;óìóŸ{Ï~)~;á?33pãTkèCù<ÀTªÂ©ÆpáýYè(š'þChU~5† ÇnNãKV­_µenÅ  Ì] ~@¸&ùþB“Õ{QÅã@[ÊÉó{PÌ »O~{öñÂýS›þøòú t ôtÒï;–9]PÜp›y¤„ÚÚ9EÛ)ð33ïõ›O.|K†Î Cñ¨j ;Í$ÁO-áä•ç?Ž^ºiïò¦• ‡1¨Kù €ÍR{ƒ?32s[Þ}Áʰùø†_?X€Í™ëNl®ß uèg$ÌŒäpfh7 dá©sV¿~|ëÞÝ^QšªÊFÂ\|„ ‹Nˆbi9†«JÖ¿ K6Ÿ»{ùܾ•W®;”z ÅàÙQ€"8Íúóû—Œì\eç®Ü¿víÄò—Ï9 éW@üjШõD-o¸õÐ`ÀìxõÊÆVAUßèep~à†$#v¿Ê¿]?nß|ÿÞ›§[½xxýì»wAsbÏ¡ö~Mí1ÝlàŒÍÀ åƒýÉá¤O@ªK6èܯ C9°5ð <…ýؤ_Æl¯AÍ~ µ–°±± jÿÄ?‘Í ’<0mÍ# å‚~üøö¹,Ag#(„€ø!}E}W ÿþýç aFf[VVFY.E^>QnÖÿ ?Þ``úúA€á3///ƒ€€ ÃÁƒ/»¹¹%ºÄ‘Í B–ÏYÿÀñÏ¿ÿÀÂÊ‘A—ƒƒ•‘—¡´sGYw¹Ç, ’Ï螨q[àÏïßA dñÑþ?$”x„ĸøËÇÏ Œ@³¿yÌÀÅÌv4¯¨0P·0×/_^½zÅ , ´”›{C`ñÖ‡†þþwb`d´gddñýûï?ƒ”(ƒ¬'ƒˆ;Ð"V†ë÷¾0üù«ÑUزU´¿Æ» 䉆Y7õþýûÄÀÀäL¤zœœl J@‹ˆq2HÈð0ü:æû§Ï ¬?ž3ü¿÷™AŒƒÊœœ² ÿþeøüù3ØÑ bhcæñ‡ÿÿg üû÷·ðÙ¥Ž-ĸþÀKß¿~ÝéðŸ’&ù¸YD…ØDÙ$Ä8þÍúÌ>€.±ÿéû¾0œ:û†áû÷¯7™YØ%ÿýcä“zRBœƒARŠ›—áÛ×o >d`ùþõßO°ƒ¹¹¹€éšáçÏŸ _¿~‡2ÌÁ ŒìÅÀÂ5'ÍÛúú ;¯¬#«+3PïÏÏgÛ(Þóòp0ó³2ˆ°113ܱ?þ@ Ãÿ Xª¬ì, /_W—Æ0'Ã/ ÄOþ~~Îðãå`(ƒÒ0¿¸4333г߀% ˜†9x÷ù?çÿ.ÝÿËp{Áu†¯?øä4 ¤åTÔäUŽÆpjÿÕG@¥RÄ4䣞*?¿„0;Ü‘¿ÿ¡:ŽÑÄ…9x¹ÿ1üùþ…áó“§ Ì¿¿1‚2 ˆ;»02‚Cäh|Éž½ýÇpùþ†Ë÷þ0¨® –ß¾yõŽ×ó?Žÿú˜4Þ>§e~^~9IpQr4(#‚Ž7”ßü:øÃé?ž¼þÃ*N•¸ì$¥9Þ~ûÉðâÓ†c^3ü¦ßß@Ì%%N Ç¿}ñ–áÝã×ÀüÄÂðùÅYP | –ßíçæbø tå_&œŽÿöTŠ|`PWSy°Ð9œPƒðìÍ »Naxóñ/¸pPQæcpu–ÿœŒ Ï?~gxúáÃýû_ÀŽþÌ|¿þA’1Ìá¿øë— Îß`øûý/ó¿ÿß>>9ü ‘ÄR”d{féŽ'ŸÞ¼ÿÅÇ+À3Ù|üú‹áÿŸ$×¼ÏßþaXµÿƒ‹µ0ƒ‚Ão¦ /?ÿd¸óñ Ã××…Ä?†Ÿ@—þ†:ìð¿°Ù?ýaxtæ*ïßX~±0üúöö1´Fþ@ ÆÜŸŸ?~{ùö0ÓáΰÉhuƒÒùÌït5xŒõž}ûÁpëõW ~1|…4Ðq@·AB†‘=Ä?€1ÿð `3üþñ›é/°¤{td<ÿ þÿøþíð‹·?˜q;„Ií:,ÚñŽ—•ÁÙZ”áÁÛï ~ü&`Ðý…8ìØÿˆd‚ò@!¸sòÃ×÷Ÿ~ÿüÍðûÐÿ˜~|€d` þ @àšøó§÷ûß}føÊÿ˜°:Töëþ37¾2œºöXþaˆð—fø tÍ“O?!‡†2rˆÃذ9þû_ O®Üføùèp ¯™~3ü)fü//!ä€{ <Ýþ̼M>½æ^pÛ'Xñ8úÞÓ §®a¸|ç¨ù*]"­ÄØåùõW_á!Žâ ä‡bP6ûøæ#ÃÓwÁižXþ†<ã/`éÃÊðé×·OO<ƒ&¡?k ýV6Ç^¼ùé!ÏÏN—ÿþ£â¿Xbàý§ß ‡Î`¸|÷+Û¿TŽv±ePUá§óW_~1|z ¬È GN:°´Ž\â|ÿþ‹áù½' ž¿&™_ ~ÂøìF ÍÁÌÁðûû;x…3@Á<ðØz¼ôâõU~†/ÿp'¡@/^xL"ž¼üÁªMõ´Ty¾õ½ýö›áÆëï ß.úLà?ÿ¡9©hdebbPfePæ~ K88,”>|}Ëððù;†ƒgï2\|ø›á;°~y„ Øäùùá.<ƒÜ @ðÖèׯŸ¼ýÈWÆ*ÛÿcÏ ,Úú èv5`K3Ä]Š…‰á#ЕϾë  ƒ¿Kl!fÿ‡8ÔÄPäa`0ãÖ¨\ ¿03ü¯üÿ#°Íô‹AMZŽAQ\›‰ù=úÝ'¶zÃÀÂÄÂð )ƒÜ @ȶëý´5aøÃʼnáø×ï¿1|ºÏ°í8+C‚¿ ƒ(°•úáÇ?†¯À ýL¸ß@4P!$äÿAè?ÿ åü0ýåëO`9þƒÁJZ€A˜ØaaüÏ #ÎÈÀÎl…; bffVŽÀdó”¿aÆÃÛOæmxȰga|êÏOŽÝ òÈ€Bî€êƒã>þ²àáçô$ôë#¸Öååaeø 4ÿ¨H„¶X€’ ´ˆD/Àdõöýga–ÿ ö ’ ¿?³2p~aàbÿËðñ#0F˜€½26†ÿÿ@͇¯ ÿÿ‚¢ â(`eÍÀìjþý%ÎÄóO‰5Rwr_çhÿ˜ €=ðï篇Ÿ½úi)!,Zc&¡÷_™Àù€èç_ÿøýÂÁ?Ó;(S‰¾1|üü›é/ƒ¥„Ûǿ¤„?2|ýü˜ i€Ü„ÿöå38äAI =y€ê¨ß_¹˜¼ƒ‹SE¯7Õ–<:@1#—*6®±¬¿ÿqFëi 0¼ÿêø¯ß °BùÏÀΠ¯)Àðú(Y@0$Ýÿ‡„:Tìó·_ €­ÉÏÀdóØ25åcxûè/70M `É•ƒƒ[è^f6.†O¿‚ñ`¨üå) þÃÀhþ÷ë+ó—ÿYå•UÔxyO^8{ê5@¡Œ Õg[íÕ¶®ÇV ½ÿÂÄÀÆ ÌdÐdð’”„@Žÿl7}úìq‹Åo@¬ÈÏÎðéé_†çÓ÷†¿Œ| |"²À:BØÇç–ý¬  ‚æý{`m Ä?þdxlàèOÀâDºé+Ðܯž3<|ôOÍÌÆ=èdA€BØúóëçã/_ý`öÚ0ÚEŸ0‚ûÅèé†A±ð˜Þ?Cc_4#°Z•ådg¸zá¨Nb”Pv꥕%М_ ÈR†7¯?3¼yõX€ìf\Pzdæšñ“_ÀÉð°©ýû÷O†û·_2°s©ø…Ç¥ëz§þß/P>xñÃR˜>þBm}z€• œ?À9ýÿdVPÒùòÀ7HèKr‹Ù€±úŽA@߈ƒ Üd†5¬þÿûÃðþí;†'÷ï‹Ï¯ LÀºÔªdö›?~Íð èCxžÒÿ€Iáû— /^ŠÈš9:ƨÄ÷o_<}É_fÉtôgÔ$ô*R\ˆPÿ‡ìø_àôŠæo@|ò¿cAØW¾}ñ ƒ”´°œ†:0Äÿ@ìbfö?g¸váØq`ÇÃjÖ_Á¹æx˜þCôçÏï /Ÿ`ÐÔ–µ ´*Û¼±ùŸÄ ûX§q¨£äƒ?@×2±0‚c–Œàއ<$ÙÀÿˆeyEžÿûÀ § î+ƒëCh¥È´ýРOŸ¾Cœ¨ñÏŸÀf¨(ÿú°Ò“W „—00f8ÏÀðÊèEÐ9æÏb ,+PèÿüúLóëüoà%Î`2ù uPÆý y £ÉÂK“‹¡'X‚•…Ø®ùÂpõÒI†c‡N0¼|ú‚  î$°Va‘“`PPT–óÀî((j€u#ÔьȎ:š••• ØZÀðåó[`‘Ë"@, {€=Éù@ÀÀ ®¤#¾ßÿlÿ@ⳑ¾¼ ¼8ýóç?ØRpïì"Ã"Bþ7°ÒùËà¯ÏÅ`.'ïröc?¾{fßüoŸ?f066dPÒPǃ¾‰.0yqƒkÞÏ?2¼~þ# @jÿüýÃÀ ljèêë1<{üXéýe  Ã±t°…!ƒgÂË×2¥<ùüæ\>Hè#;þ3°JVâýÏ`©ÁÉ $Èw8(½‚0¨EÉÄô<°óÿ¿}ûÂpþü9 £þ2¨k©ÕügxÂužAÏÐ ¬þÁý‡ o^¾€è‡EƒÌc–d††F ·oÞfxúðƒ„¤?@ëp ãA!àNSý}KÞ±=fø,“ᙘ–8¹˜ÀMˆ/ÀŠÔCRãûÇPhÊÆ`ÈÉ * w4rÆcåfcæU`£ ’iA£jß¾e¸ví:°ìÿô#ÃÜŸ¹ {ÿMgøÏö•ABBج`«g6ðþüË_ôŒõí/~v`ãQMK…AßH‹““ó?@±€[ìÐÐGÄ‚]¿™É/^ ö\© ê¿>û²²¢ÌÀ¤ô—Á\øƒ–2#+Á+P 4xMÐÿ‰XšÛ˜0póq{ÿÁ­šcß×0\ÿv„Á—£èÁoàöÑoа#0„„äm¨÷o?0üüñ•AE]‘‡áùÇŸˆ…áÃ-W5¸ã™9βá8ã6`ò”µ0gpä°cP“rcPý§ÃÀñ\&c”H±Âì¬À¤'È,i¾€ÓÃ`r’–‘dPV–†dP }ÿÿB†ßþ}ưà[)ƒ0i ]7öY¥гV BBÀÊí+#ÃÓG^=Œ]†oÌ_ ÃZ†`÷ ‡ÁAŠAší™’??ɰˆˆÿvƒ¹åÙääþÉ1Xÿ²fàûÃÇ ó_Zÿ,p±12ð‹ 1|zÿ\T›ë3¨¨Ê3°³0€Ó?¸/òó+8ÅÿÌ8|â=Ãð@çƒúC` PôePzöó‡OÀäÃÎOÿÿK,`Ëöþí«çˆ…á Æg Ñ ŠÌ|t€†È£y‹‡2=dxøï!ÃaÆÃœöÚÅ@ŠoM>“¯>#Ã/…¯ ¬ÀÚUÔh!?ÃßwÐY ¾Ï??»”¿!øÍ§O‰ËÎç æ,} ·®ÝaÐ7ÖbÐ7TÖï€`üvùÜ¡« o. Vuhô±`ÅÊ ËàLT¼@šAÚèFö vÃñ/¬]2eɳW®¬)²òhü4æLN0lVvq|«fà6‰Oð5íGׄU_1Üüu“áÜ/`9ìƒó (yñ#MØ2!9þ´ gƒÆÌÑ?¡˜â1Ðh3Ãì1ðªöƒ·Ã™Ïÿ¬[¿rý…#ëÎE_®I>PÌ€œWºþÖ`ÊDƒ—ÁÆRÃ~6‚\}ưçɆ÷,O~¿b;óìóŸ{Ï~)~;á?33pãTkèCù<ÀTªÂ©ÆpáýYè(š'þChU~5† ÇnNãKV­_µenÅ  Ì] ~@¸&ùþB“Õ{QÅã@[ÊÉó{PÌ »O~{öñÂýS›þøòú t ôtÒï;–9]PÜp›y¤„ÚÚ9EÛ)ð33ïõ›O.|K†Î Cñ¨j ;Í$ÁO-áä•ç?Ž^ºiïò¦• ‡1¨Kù €ÍR{ƒ?32s[Þ}Áʰùø†_?X€Í™ëNl®ß uèg$ÌŒäpfh7 dá©sV¿~|ëÞÝ^QšªÊFÂ\|„ ‹Nˆbi9†«JÖ¿ K6Ÿ»{ùܾ•W®;”z ÅàÙQ€"8Íúóû—Œì\eç®Ü¿víÄò—Ï9 éW@üjШõD-o¸õÐ`ÀìxõÊÆVAUßèep~à†$#v¿Ê¿]?nß|ÿÞ›§[½xxýì»wAsbÏ¡ö~Mí1ÝlàŒÍÀ åƒýÉá¤O@ªK6èܯ C9°5ð <…ýؤ_Æl¯AÍ~ µ–®ÜKW~mêk-T¢ “¬ ë[u–Ö¶HäʘI\iq{nžj¥B†øž§¸¡ùŽÊFΪǻršëãjë[uìÍu€Y¯“Íæ ‚\®Äf¦@f#+ãy ¨h\Zéœ9tÝý Â!=†ã’Ó£žÛß„[-ðÇôU¯Lxø/Ú:w£(’¡G’$“}ŠÎŸüì ן}¯å)šqžŒŽÐ/ú™«uÓ$¢{mâÄð\¼{Ž]­q&ÛNÒÜpsa!¥Qža¬íé½oXÔáLç B àe †m@Ô²ÊGc_09}fG¦—ß¼pdÿ¾žíìòÊWÓÿ´»¼Æý€€H—ÿðàäCG_zßÔê“÷½àu”~vþ÷ßÝyݱSßì:ôü™^ÀøµêÈÑ׆8ÉR×øÐ½û?Dþ|‰t¯Å¯IEND®B`‚HaCi/html/Images/info_left_small.png0000644000175000000000000000154110761422075017063 0ustar fighterroot‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ+µ ƒáIDAT8Ëu“Ol“u‡Ÿ÷íÛ¾íÛ®ckkšlÀD S rer ËÈ¢ËPáäÁC‚Ó+zXBà`¢ÀÄLH؆$Ydz蜛šÉJel2J»µv²u¼¥ïŸ¾ïûó@àsû$Ÿo¾‡O‰ç±fG¯vµSÓ¸Ÿ&sõO® ópþöÓUiEJ¶wn8xáüKÍÑšˆ ëðX±ä— 8òÝ5Î~ð8¹g¿n=pàè?BŒ !ö ÑŸðtÑÞ7/ÞþAˆ×Î Á§iºªåñ™€†ÎŽÁñÓûcÐw&3à—µå;ü2q‹´"‘¬§^€Sˆµîcbð+Àòlû&>Ò"û¾¸ c·!¿*‘×mrÔáO®F Êd¨ñ`1Ø„^Ìl"?yN¦¡c{Û6%0P€Ñ)ÐTa{nKr¨5ŒWžG‹@XƒÚض¼·ÐdyóŽ·Š ðó$ø$ˆ„ €²^¢§§-Z‡cé¨*¨*„Ð`U¼BñEM45æ àwA €ì¤ìãüñÏp«UdEFQ@òO á€J)}QvÙ©˜ñk<àÌîžc¯¨¿ëþ‡cŠ›Hò‡høƒQÞ=™bêÒ·L¥j™ìNÓ´±WëdæàâØe–ŽwuÊ^jøû²à:I6³iÏç|yi–îînGªx¸&Xï—X¾—×¼"SÎÿýu(eHP­‚mmBü…$½½½Øú®)áTḋ°pßcznïÓý€%”ßsæV–ж†ü"d³Y®þ5¦†Y†‹šenäÔ2…ÉVº¬k‰òÓï/o~˜~¬å’çÆÐ “™î^9±Èè×[;Ïʵ´îˆ½±wW]|-A5Œç˜”–æ)Ný†3ފµóù6>!D(ÑŽV¿áx,å¦Ö`<]ü¬8*6WèQIEND®B`‚HaCi/html/Images/info_right_small.png0000644000175000000000000000152610761422075017251 0ustar fighterroot‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEØ ~º?ÖIDAT8Ëm“MhwÆóŸÙ™ÝÍf“ìn  bQÚƒIl(!&„*~Pz‘RJôˆè¥=xPJ¡ôPKAÒòa¤M/‚-b4®-©‰ˆ“˜v“ÝDØvÌÎÎÎÌßC¢¢És{àyÞÞ÷yÖÆÖf¶ík¡´fªVÂÌà<¼2À˹ñ÷¥Ê;¬ªeï–Ó^þ`k¸4$àÃÈ.ÃÝ ¤aaèÒ.|}œÔÚ­u'Nü4/e\JÙyOÊÞ„\—“­¿¦å¿¤ü袔œLHŒòú×6€ê½mý÷úÎwF¡û!ŒÌ€O‘”™SÜ#‘÷SY!"A ÇÈDwu0Üÿ3PP»‰³õBýá1܇@|†B:g3«DЪjúº€R²þMäf¶“¹(¨nkjnÔô¾ ÄŸ@P¿MÕðmsg‚xæÁ”¡¬¶„í‚BìhݳðnŽ€ª@(ºž"X~™£˜_Â0àÇ…;¿#dg(Õ@ Ö¦•›jRð¹`è «PTA®É•U×д>:ÐÅ£«½êÚA¨ š´MgÙ‚ BžÝ+Ÿý8Ìúpí= ÂøBx£˜9ð×Ç·ž£¬&MÁqlkżóÐ)¿§©¯pM`¦Ÿ&o_Í+P,‚]ÛßjFËC:®¥P4á›$µ¾c:›Åû÷|/PfÿWŸMŽ%YVÀ΃´!…d2Éàÿ“`±Lx>/žfv¨g‰ÌÈÑw»à¯¨ÿû~íŽD5ð (,½@ñ\Âá(¹¼Å£™IžÝø-Kü\0µ¶LPÆ®Ž¾è'íŸWÄ6â7Jð‹‹sÌÅqï÷õyp °Öoã[T¶ŒlF:‹© daÈ¿/| ˜.4èUü£IEND®B`‚HaCi/html/Images/info_small.png0000644000175000000000000000165210761422075016054 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœIDATxÚe“},•qÇËpK‹¨¼T³c½Yea™.½¸HÒMV–Fw$6¥ikÚüa*ï/ò’¢w%ÓL3C”v¥;¥ˆ­Ö›oç¹Xuþ¹Ï}Îï|Î÷|Ïï!"’lQ4à‘œ4±ÊÝ•d™iXYHÁ5Uä{Šš[kr‹ÌˆL×é/¦ÙÐ&²?HZžéœpó¶Éþ<åó?’:c-ÀÆëÀ²€BË»¸á 2¶"Ò5øÐ"² "Úš9<= ú ÆÌ4Ü/«áWÏ r†œêé:–͇…·GѤx>é°ãÐ:ƒŽÞ!\kl†oþ ÍÀþ›€}1CŽ6ƒ«æf §’ß¾sñ¹^À“‹w6‘­@J³Êû£ˆlû‰˜ J€ÔY Ù|¨a¶»‡KÒ+àŠðæd@°ï!PòZTôejwTP2<^€ÜÜ Ñ$¨X@YÆù#rÅjú+Œù¿ÉZÒ\Ïm‘µÆqmX“>‚u?Á.ã̕ϡŲíóøŒ>ýB±ÙÍ£®W²ð#%éR_2¶‰¡%VÇIKÏSóÎÚK<¯#¨—ht("Š‹4µIEND®B`‚HaCi/html/Images/info_small_disabled.png0000644000175000000000000000076510765147574017723 0ustar fighterroot‰PNG  IHDRµú7êsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœŒIDAT(ÏMÑ=oŒqðßóÜk{šžêP4ªRR,ÔD¤Ò D|‹E &ŸÁb4{™¤K#±ˆÅp‰¦!E ÕÓ·(¹¾¤­;½»ç¹¿Áâ÷~' X¶àÀÅ‘ñÒ¡LéÇ»ùjI—M'ô©^½7y´'«¬î›M3^Þ(¯kÈS÷çÂNxfCixž…GávÈEˆØ?1ýzHEÕAƒV,R·æ­É²­ æ&23æuÉÙ’êW‹äÔFkÏã çòUsòò†]sI]I·=ú¿®;{ìʺe±.9P×T*(éWœÈîü%ÈÉÊ€Ž¬ŒŒŒœ‚ÂpÜJv¥"tDþt„Nöç\tt "‰D"Õ´­^¿Lý¤ÚZRhjÙµfUú&n,Î| ÚZšbôhihX·êó͘©ËUÁ®Ä†Uïµ5¬Y4½U»CDI2vkfTAlGG¯†%•Ù³–ˆ(‹löž~zæf¿¢Ä†×îÚõïb¯¬šó*]Åñâ‘ÐÙþ*‡ÿ¬ÈHñשùÙøIEND®B`‚HaCi/html/Images/insert_small.png0000644000175000000000000000711310761422075016423 0ustar fighterroot‰PNG  IHDRóÿa pHYs  šœ MiCCPPhotoshop ICC profilexÚSwX“÷>ß÷eVBØð±—l"#¬ÈY¢’a„@Å…ˆ VœHUÄ‚Õ Hˆâ (¸gAŠˆZ‹U\8îܧµ}zïííû×û¼çœçüÎyÏ€&‘æ¢j9R…<:ØOHÄɽ€Hà æËÂgÅðyx~t°?ü¯opÕ.$ÇáÿƒºP&W ‘à"ç RÈ.TÈȰS³d ”ly|B"ª ìôI>Ø©“ÜØ¢©™(G$@»`UR,À ¬@".À®€Y¶2G€½vŽX@`€™B,Ì 8CÍ L 0Ò¿à©_p…¸HÀ˕͗KÒ3¸•Ðwòðàâ!âÂl±Ba)f ä"œ—›#HçLÎ ùÑÁþ8?çæäáæfçlïôÅ¢þkðo">!ñßþ¼ŒNÏïÚ_ååÖpǰu¿k©[ÚVhßù]3Û  Z Ðzù‹y8ü@ž¡PÈ< í%b¡½0ã‹>ÿ3áoà‹~öü@þÛzðqš@™­À£ƒýqanv®RŽçËB1n÷ç#þÇ…ýŽ)Ñâ4±\,ŠñX‰¸P"MÇy¹R‘D!É•âé2ñ–ý “w ¬†OÀN¶µËlÀ~î‹XÒv@~ó-Œ ‘g42y÷“¿ù@+Í—¤ã¼è\¨”LÆD *°A Á¬ÀœÁ¼ÀaD@ $À<Bä€ ¡–ATÀ:ص° šá´Á18 çà\ëp`žÂ¼† AÈa!:ˆbŽØ"ΙŽ"aH4’€¤ éˆQ"ÅÈr¤©Bj‘]H#ò-r9\@úÛÈ 2ŠüмG1”²QÔu@¹¨ŠÆ sÑt4]€–¢kÑ´=€¶¢§ÑKèut}ŠŽc€Ñ1fŒÙa\Œ‡E`‰X&ÇcåX5V5cX7vÀžaï$‹€ì^„Âl‚GXLXC¨%ì#´ºW ƒ„1Â'"“¨O´%zùÄxb:±XF¬&î!!ž%^'_“H$É’äN !%2I IkHÛH-¤S¤>ÒiœL&ëmÉÞä²€¬ —‘·O’ûÉÃä·:ňâL ¢$R¤”J5e?奟2B™ ªQÍ©žÔªˆ:ŸZIm vP/S‡©4uš%Í›Cˤ-£ÕКigi÷h/étº ݃E—ЗÒkèéçéƒôw † ƒÇHb(k{§·/™L¦Ó—™ÈT0×2™g˜˜oUX*ö*|‘Ê•:•V•~•çªTUsU?Õyª T«U«^V}¦FU³Pã© Ô«Õ©U»©6®ÎRwRPÏQ_£¾_ý‚úc ²†…F †H£Tc·Æ!Æ2eñXBÖrVë,k˜Mb[²ùìLvûv/{LSCsªf¬f‘fæqÍƱàð9ÙœJÎ!Î Î{--?-±Öj­f­~­7ÚzÚ¾ÚbírííëÚïup@,õ:m:÷u º6ºQº…ºÛuÏê>Ócëyé õÊõéÝÑGõmô£õêïÖïÑ7046l18cðÌcèk˜i¸Ñð„á¨Ëhº‘Äh£ÑI£'¸&î‡gã5x>f¬ob¬4ÞeÜkVyVõV׬IÖ\ë,ëmÖWlPW› ›:›Ë¶¨­›­Äv›mßâ)Ò)õSnÚ1ìüì ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌã§›§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖކÏß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûg½Žñ©Œ¹;Ûj¶rvg¬jlRlc웸€¸ª¸x‡øEñ—t$ í‰äÄØÄ=‰ãsçlš3œäšT–tc®åÜ¢¹æéÎËžwç|þü/÷„óû%ÒŸ3gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFhIDATxÚœ“KlTU†ÿsï™{g:v¦™¾†;¥¤´5”Òˆ*QÔÁ"j(Æ ²‘’nŒ‘…Aãk!± Ú&>H’,†*‰­4Õ´¥-qÚ™Ö¹ÎóνsçDbXú-ÿüËï#á^8ê"õ¬ƒáóƒ E60=ìCý†#P…nd«‰¾¾–ž¿l9•ypîsÆgBÜÝã(X÷ú¶—[·‚^…D@þ}r½àép0<©|¡*ãBpTm”È»žÛÑ?öî‰ýýO œ$4®jÛí Êß`Nf¤éÐ×GO½&ùˆdsãf¯z+gº#o?õlïà¶>Íc&ƒ^ܾþörîÜØÏÈnÍãp›÷B ,¢³;ðrëõᢙB‰'ðÓį«ùÈî¡£{÷•(¥·V+0ŽÛ9žï‹Oæã¢ N;Pµ`]Ï729ÃRàÊkÈ%ÌåT±÷Ïß46nî¬õQž-pk.íþò—œêI­¥J´Ë6Îtâʱ³+ó—9´†¼—DjI¿lŽ¿µÝ_ZôÇ4 . 9C%ñÛhù›é½B‚áÝ_ÝqpS{3“ƒ›?ì™Ú¨˜ž@Ñ2FÚwñ-=Z‡¦‚» rü™ž•£Ú 3–Aïë8¬õnFªœÁ<øÕï®]4fm/Z£º ‰–澡'÷mmë‰y›àüw3NféâWÌÌ‚[Z y ï¡­ƒ“ãolë5 ­õä·×oÈ»4_M§•“©nÙ°‚*¬êÊïž¾0aA“³ÓãùKØÇ›ï¯¯«Hõ¡hÛZÁïšT–²VæÓµìYmä³™9Ì.Ö&³S7Ï""5ÝQÇڈОâ'ÞyúØŠu±`4ì~%T Àf:gÌó®gΉ?I •®5Ÿrô"$*¨÷ ÝÔõèw騺 $íHHm‚Ä›%SJ*+n ‹±Á‰ªÒÍ©ÚQ˜Š¹iRŸFXÑ@H+¸T Ýa(TÊÐKYèÕf§¬jåg\’TD*ßm„Ê‹Ff!§~žžô¢@@D` ð\Àu1ðe1.±po~ä¿ÅýþB³˜óø«MIEND®B`‚HaCi/html/Images/ipv6.png0000644000175000000000000001653310675407754014634 0ustar fighterroot‰PNG  IHDRj0ù4ïbKGDÿÿÿ ½§“ pHYs  šœtIME×  ú°ÄèIDATxÚí|[ŒdÛyÖ·nû¾«ºº{zºçv.sær®öq|ÃØ±%ä(2Iå1BBAˆxåxEN AD¼B ‡ $Y±ÇÇ>ÇÇ>3çêñ\»{º««jßו‡½ª§f¦§çjG,i©ª[µwí½¾õÿÿ÷ÿ¿‹¼}媀¶i°só&v¶·1›L¥)²4Ežfð»~À—?÷¹ŸÜ…oüùk/øy_”R~l¼·‡½½1&Ó)q°@ÿê,œs ”‚R Æ!p®ÿ?PÂÀ(c«Ë+XYYÆêò c_ðe¿söìÙ+øK6vwor€øÚÿ~íÌîxò¥õœÃ'càœƒ1fç÷àË¿ô‹¿0[<Åÿ)·Ö¬µÐZCI )%T@k cÌ=ž;o´†T ìJ@((àààÀ[µ„Xk÷­Š K(Œ±hÛM]£ BPB"cÌJSÕ'þÇû²P¨æ~^þ¸ìÆý¾H(ca'.HRƉÐøkkسçÜü˜·ß¹€`‡¡V͆RíRÛÉÐ9 âÀ9(­5@J‰¦®1+ L&À9pJŠàžÏ_75ŠÙ “½ xÀÁ„8¥éA›ƒ:ÇcLºë$ °Z£k[”³(¥GÈËÔB€·|à};›;pÀ³ÖX€p ÀüëþâÌŠbà,€³F·çÚföüd:I¥‘ç9ò<‡Ùá@)u ¨º®Qx ¥…@Å ä`´•R €#J¡®ÌŠ{“=Dq„0Ž!å ”QPÂz€Ðc²oÅJÁh kl¢±èše1Ã^"Ž¢#I”¼’¥é1Áņ÷÷7ÿ:àÓÎ{@´·¦?и²”1zÉõçÌ RÖËÓéîrYUpnA@¥¡_2÷=wº>³o]ס®*”E$ŠÐ¥´ÒÀ=¬rÑ-j¥úãëeQ„€ !P4œS°skÒÆ@))%´RÄY'œµÂY+ŒR¨« Àp0¹¡…"8C@W¼þ£DÃݸ@0¬¸v–76i³7fº*Ò <J?XëZ8טùÍóúõ¯cŸ@L‹Ý kÍ9çÜGµVÏk-Ñ4ŠÙžK’@+•¡ÐÐ-7çŒÎ߀³ùäà\@p ÷´¨ÅÿJ@)¥ \¤Y†••U¬Y…±f[Jy¹éÚËZëæ@Ó"ÊÓôTŧ!NÔe…ª*Q—º¶AIœ1Œû/ü±XÎ’ŸƒùlÇÛO“(}p1b"ÔdÌEx3LÙ¦·¤©w‡kN8e-^,ŠòlÛµƒª.¡d‹@PdY¤£m‡ÛNâh ½[ß^´Æýõaœù7 œ30–à}°ÃE((%`Œ‚qŽÌuüøqc¶ê¦~cûæöŸÍŠbrй²4ž:qòãƒ|†ApbkkÛ›uQ k[8mÑ5-(ù±Õ À†_ìcþý1«ä†5ØpŒ/.5!tODÉeBØ{ÞÒú€ÃátÛv{“½¼ª HÙ"8Tó­8W–=P7}̻Ӣz ›ƒÄÁ¶œóqéŒ3fÙèÚ8ÚÍØžˆò^ð@Í|°Y𠀟vŽ“Ra2›¢,§àÜ!B„:OÅöh^ø‰Ÿû?¤ƒšÇc Œ60F/01s[BzhŒ2ÆXOŒ1ÐJCIËh…;Ï–þÔ9g´Ö”õ,Ë6º¦á²SPZB±´¼²òüsgÎÌÞú»¿|À ›/¼ò¡Ç"î¿Ë½Õ¬ØèÞ{ç¤rî¤;ÙåqU##e@Aˆ(AEhx‚Ó-?ô‹\p“Y±î¢,Q7 d'çD†y„, mEM…óø6óqÊ”†1Zëýüi¾è÷²(½”1Æöàö@i(­!•³ìAú!眻Ñuí«øHž¥G(À­6è¬cáR>ÈÏ–†)€ã¾ã©ñã2@à€xնͲjš%ÙµK²“ÕÉUJ&„Â,G§†0^0Á¶ýµ—*v:›ÝT'%’8Âêò–G¹ãœ·aM=P­ŸöP‹Ò~Ôb¾s EYsÛû,{ë\žz/&Æ`Q×Êböz1™hJÉ‘,Ë_±Æ mX£Á]Šã( Ãð9?€ËÞ¿?Î`Nø€¿î´ MÛP9›2©5Æ1cL dyŽ8 ûeXrê˜ûÒoü»ýWUÕ[””DVF#__± ¤%„ÌrÜòÒÙ»-ÊùEtÖÁùÆXŸÏ¸Ã-ÊZ²Ÿ98‡[y’›“3Bú¿#jO?ý¬ Èÿðïÿ­Pœ+p¸.= „`œsF)‰7{kxxw÷çX°j:¦ªÙ+N«§1K]]rY×P] cÌÌ:²ãÙPB@ãl à"€­ßüã?Ñþ\+¿ñÿëj9¿"»î¸5&P²@Ç1¢€6Œ‹JÙŽâ € ¶——ÎêCÍ}n-Î98ë`…³Ö:¿øî;½Ý™öÖ·ée$B(ÈCÐé@ˆÛþ(…!‚@@0Ö‹‡]ÛƒØ+ /äE­ô³²*鶦²k!»JJXkÇŽÐïƒð7ýÎß7¯l{Ýô8€—¼ly®íä3mSGpœ1 ²aÈ*FÅ{Ú¸7¼åß¼¿_¶þf=(sœ³~º9 ¸sµ×is½®B@È<·z8JbßR „a€(  ÎçÀ»[;æñ€ú€ÏXkÙÔq=­T·µ†sØ!ßµ_ñòÕ¾ÇŸëŽ @}ÀO9`$µNfUŒ`˜%ÈóI”Œ³÷ªÆü±W2æúà}è~I<Å¦Þ è-·w€I,Z›ó@ö‰/¥ý±óy¿qsó‡@ ªÊ2ûî믺¦Ñ¤kh¥úMdt£¤šm¦€{À5ÄÌݽý'±O^‡ºjž1Uû¼Õæ9«Õ©¶*Ñ55ºº‚£TÆ ]¿ë/Ÿýå|i~®w67…?ׯ'GWßøî[/³âl'åÓm×F’½Ìæhm›rΧA¾ àBÝØ÷_þ‰Ÿ}à’ Ÿ/b¿ûûH1¯‘xkèS^Gî­ž;¯e·û @ùM3pDÁºsîdYêªhÛ4hë]ׂ“Ùtr±©«‹ÀêÛ>F< ãxîœ#î|Û–çeQQMY—èêR+ˆ8éxžÏÂ|PÎo(P BÏ8'„8ÅáùÝñøøx2am]AJ ÀÁ:7‘Z_TÊ\ô×û6€Ý‡¢¤óE¤„ì'¼‚ó»û~eçæAöb¨S„ÓNTU9ÜÝÙ¡ª“0JC+£ô[[â$þꉧžúžÏ=¦ Ô9Ÿð!­ä žM†ÍÞZK¥ µÏó6 &ùÉS7yœìx«½¨ÀÓ>Eù4ç| îM'LuœVsñy¯i»·Šºùêh˜½å¯wúP@-ª ‹1…RÒ« ¾Ðþ³ý±ÆtR¢ªk –J®ììîžüWÿæK‘Ñ Z+XköëSÿý÷~/yúäÉÓYšœ¡”ž™³ãeU‹bF‰8eˆÂ„Z¶Íæ{.¼ý7ÿÞ?¼ð@în÷µh^CÒ;³Ó®U眵/«ºyAµTS «¦c£àA ž$•äÛÉÚÚ‚|pÀyio›Ï]t]·Ç»N¶Ö>¯µy¥“-ZÙ¡j*X¥AsÖÂ4uÛÞ(ªbMÄysqä_ü£r$Ë3äy†,KÁ…h¢$iž=¾=²±aæt}tì¨ã”ÜΜ›Óó¾„~룔-ˆ²½‹tÎAkƒªª0ÞÝ¥ÖÙ5©ä‡›¶fÞ…Ü]®ƒ²˜­Œ­SBŽîíî)‹"×ZÓ,N0Ìæù]ŒðÇŠ¯!6ÐçÛb÷y]VKªªÐ¬iÁ ž¦~fq2 ’ø’O¨ß÷ Hë?à„±æôîÞ¬—›¶ÅÖö6ʪìÓÏžu€³KÔºh¯¼poË>»ìI†òVìnÅ(ÿY»CÝNîæ‹@ÍßÏ‹U]ƒŽÇè”eô(¡„QJû\éîÚ–Ö¬.ËTwm¢¤Lµìb-Ul´¦a`yy Ç×7Gñ£µêió§ìÙ–«õÎæPN§pÆÀYPDÃÑÊD+« œO).Š×½†Wx V|¢ýaÀ½XUÕS›ÛÛË»ã=E²,úà§5Ôºœ}Ö!Õ!@}Ë—F¦ TÖô1 ¤nž°úœêNUâNãZ¤Ý„ø*®ëcWÛ4°Ö¢éZDQ¸ÆÑr…gïp–·m­ê¦AS•`„€Žs†8ŽÕÒp¨Ž®­é<ËJ¿`æPw·ó-1¯9¥N8¸áðW¬–Oë®F3ÙA7Þˆ D'ˆòĤ+#›Û°„Ò±/[\$G^ØÏÞÛ¹y±ö­Í«MÛwÇãÁÕkס”‚RÎPÏ×)%` 9s$gÄ=ÃЗ‚@|‹‚s^ͱÔ8¥õl<™\—€à¨£4©¯_¿®)£Ç}³º_£"¾4Ñ}«~IÈ<‚S„Œa&pf‚QË)³‚1›gÙfš¦W)¥W}&ÿ¶'‡y‰â¤®ŠWL]¶²KÛéj:†U5@-DÈ¥1¢<·a,à3p6¡×ýξmCŒ§ól//ËjPTu\רëÎôʬEÀ9#¡` ÎZ h U®‘P„ ±Æ¥y©°WȰ,g+«+òHuìcµ¨ 9ÿ…—Ž(aûd¤·Î¹tA@ˆë[Ç(…`Aš ä¡àE`£@˜04§üFÀùë\ð×|ÂyãXÓ€W|Âiõ”žM6Ôt/é¦{P³=ÙÔ‚‡Qž ]Ê-Kã‚…ââ†jv'P“Y9Ï÷²º®‡UÕˆºnxS5·¬@EÈÓKYŽqPPX¥Ñ5º¦W=:m¡šÔa°8ÍèZœ¥éÙ–yl Ü ¹}ô(¡ûE<ѸӢ¥œ!Ã<Ã0Ë0Ì3$QdÓ8Ñi+Jè _zÿŸGž}ñÚ^ÞºW¿àŒéºB·}Ýd %XÙ‚p2DYŒti`‘Ä3DÁ „á» ôÚA5«ê}‹jêfP5 êºA]Õƒ`}M.äƒ4Åêò±Aq€j%f´€Ñ­4PÊÂJ k-Is·æ8[c¡‚1f_7õô|A†!½xÔ!ˆ’K£%,–¸-cõ¦6ú†1º}À3j¿Hº­ë­Ùdo°}ýêOþö¯ÿËF%dQ ¦§ÖÖprí(ÖWW€,Ò¨•‡3§+Ìl 9ÛCUL º`a'1Â¥ØÒf<œBì»×Ëž’ß–;mÞØºE€¤DWµÃr–»Pˆ2 ‚*ƒ2K’" ÂZxövC ç” JȺ&µ”3…“QAJ ~»¤=È=X_‚»Çh¯F#¬o¬ÃZ³Ý¶íëÓÙÞwêF?h¢gçs6Ý[ïl¯µMóœw;÷R‚ªOAw™í¡+&¨‹ Œ–³a– Œ†`Ãä”MÁØ÷–W&wu}kA1ÐMA(FYîÒ8.Yº569ã×¥×9g×<ù9(¿Éh¼ Æ (Y7FAI Ó(ð€#Š#Äû@Ý%lº¢ wÙ?‚[@mll€²eŒyc¼·ûûß~kûaíôúåKUvÝsÖÚÏúØsÿa¬@Û ÔSa¦{è¼Eá0F˜ÅH—‡‹2ÈU@½°Î[ômÅ´­‹¢ˆEDYî–‡Ãb}íÈçž:õcüm­õÅI1»¸}sçÀÜ‘¦é2 #€’c¬ 4DS`œ!ŠcDq„®ëðxdâ>*żǜ1®…@}lãøì Ÿÿ©ÉÞë+ÿé7»(€L;·Ä¤í΂S—è¦ÔÆ`.% ¦˜Âc˜b YLá´4  FËË+àƒh\Ìs Ôí¥à\2ïBºPNºlySwüõo}çé÷ßy÷Ùéd2PJ÷ý‹Iˆ<Ž]„³‹kY’^ŒÂð׎,/ÿÚOþÀÜé×~ë¿°…ÄÓ1…± MSÁX ©$š¦àîjÑ™Ý/L¹C\£s}1ÑBì} ‡‡v™¤é­¸@ ¸V`J‚ËDK¨r‚Æ´:C`[PdГ1ºÉäô&d[Xƒ(ŽÁ)¢Ñ2‚ÕUðåÐ8 C€°{©ÏyÀbñh4ZËóüôl2))A¢!颯 ÆfV›kÆØ ^mß»W|€£ÇÝ®•R ©TåŽÚô½–…G]Ð9ë#‹EÄ>™ƒy¼&¼,Ëö½°"LJ°®±TIhÙ © À¶`D÷@wP·a`@†(Ž ‡=P+GÀWVA¨aX¬™õßµìÅÛOø70£0‹ã8¡„$º“ Î!âY;JèÔ(}µ,Ë iÏ$ ƒ:¶qPʃ´·+B µA×upÎâúÜ㻾G-Þ9祗qÞrŒ6œb(€"&JÁ¨ÚáúŽ'f`Ê)d9CS– !‡HB„Y†p°ä‚làx’9¥ÖÊ~’‰§ãOµ'œ3Ä!c„Œç¡”Œ!IÁËcÕÕË×&ûì§î[ ŒÓÄxÒòçܔҡuvàà†ÎYf­…6T“'£©ˆóHæk[ŒñÇEþ&€ïVÉóNÉsĚ󔸘8 g$\G€ÚTÃVTÛ SA$†1ÂÁ¢Áȉ8³”GÆÑ9G&dbÙòîj^„£oOîœqSâ`9¡Ë!çyÈ9"Î Xˆƒq¯ìû,ñ=ˆô"ìŸ:ç&Ålr®,gç•Q©à„Yw«¹èÉ’‰…ë#´¯o‰ cìqÏ|s~S¦*6][ƒZsŒ%NFÂê®spLè`뺭{Q˜d`a„h°„h°g†òHƒˆÆ‚ì‡ëä €-/¾:¿Ó;ל²’Y,SÊΆŒ!d=P±ˆƒ¾E ‰¢G¹¯9P£õÛׯ]þÜt2Nµ–§`¡»7Pî Y—¯cùÂá㺾3Ÿý™b^yí×þiÌ€ç# ±”X8§`U ÝhtPV@· ŒÑp„€ˆ,N ²D’KÑLÌa;äªqäšrä2€ës ž¡Iå›W¶¾wes‰Ï€g‰sŒIˆµ BÒ³&Xcú¾.ÿð5;w—RJ²7Þ¬š¦’J+gmç…oÆ8¹]¥{L„ÈmÁÑ{èƒpÔ‡ 5at€†6:©Ga …t Ïf9xšƒÄ9D3ö¬{Çïæ=ïææ}³vì.€ï M]_íínÊâ´îºõ¦®P̦³GÚŒ€§œæœŸ^^½,¥±§Y\ßðrXç#•%·ÄfÑ †Zh«`]߆-• °@@ä9Â4ÓIÖGnn¿í÷ ¹”Žt±™ Á$B(x!䈗F`q²C)y“ýMO¹7½àúÐãÔÉ[j‚VE1C1+P55‹h|œ“€‚ã^ðMô]´‹@¥^íøœýîÔ†Tj]j -[J¥ ƒ ÃAŽ¥áY–ÿ-Šþh,*Z°(rœ‚%H0¯;P‚¨Z¼…Á £]ס“d'ˆy–!1†ƒÁü†ë«Ù5Ÿ»çÀu‰³–àžŽ }Û™õÞÀ¹'”¡íu@Õ]cμ%©èÑÕy;6¥|i |¸’vh‹ó“zõü®ÒÆ@½ ÕZ_×F¿J9ÿHœ&G-€N)´ZA³&•ü”r´èúêª8£O‚/3ßÿ¨µ†5Ñ(F–fVGñÍ oÂÙoØ]°(Xk‰u.xôö–[ÌÑYÛÓs­€'oQ€÷°{õr`•@–ŸB–£Œ0Ê­€¤Ë É`ü0ßMýÆd ËÃFé-ê;“ñ»–”üxÄ“—›®CÛvhºÆš5 ;²Î½rëœJkª‹NXAüºÌ›VFy–býè„w(%oj£þ€+Ü<Î!ó¬äÎæ¹ïù†’;%ûÊ'ß>0ÿpû-gßõÇ?ö/­üåù®oåþêÞcß!Š2Â(c=8Œ÷ݯ”½áEÝæ€eú"ìýä™Ïñçæß_ýçÿlÀû„o3Æ‚4IW“4]u «Ú®æÆ˜ØÁ‚×O”8PâúŸ=Qj¢”šh¥¦BÍ9×B-ÿ€w­ÕÛ§OŸžp_]p¡ŸwZÕy7 ½;º×¸äéj÷„ë“5€÷üâ^:äsWÑw»Ö÷(ùëÅÒÿC|çï‹RJoŽVV^Š“ôå GRIÚI‰®ë`­öqº':‚3NA)QRv›mS¿W–ÅûI’6Bˆ §õïÏ7ðµÅêð¼sAꀚ¡ï±»â¾×˜gýíjþ Ù ÞKÑ.ÜÃþ8²¸›»rnEæÄÎÖoí,Ëß:þù6N’â…¶é{ýªº†VÖYXk@)A†ˆ¢Ú:{Ãý¦”Ý7c3!‚ct¶p½5ðãøYÿGÆ—~Âÿ$€/Öu”e‰ªê¿±^NcŒ"ŽCÄQ„( §Œ±¯Aðµµµµ¯ûø;0Y½M{û¿~Üù6+xIEND®B`‚HaCi/html/Images/jumpTo_small.png0000644000175000000000000000113310704777334016401 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ¿IDATxÚ…ÓËJaðñŠ*¸P7.D\ *‚ ‚5EÑ'hi^Z–D"¸Ä—²tSé=’¤ h=À¿ïˆŸØ0æÀ¬~gþsÎÌùAv„Í—ŒÝYaËvŸnñãªR©> íK¹F£ÁÒw%´ZíÜf³aù¦²ØõzýÅbá^—ê` a4yQMô‚orNÇýDœñÇ‘N§ÁŠyÑ!G¥Rù•J¥L&×Cªb±*•Š8äˆÍÿ–H$P.—‘ÉdÀFâ~° ˆF£‹€jµŠ\.³Ù¼êD.—Ï)€{¡P€Édâ^Y„Ãa”J%´Z-L&4 Øív*¸f¼ÒˆÅbÍfÓéõzV«•|°…B¨ÕjÇèv»ðz½„wË^x‡£ÑÃán·›üj5}ez°×ëÁårÞrd#|F"ôû} þðÅúGœÑŒív‡ðrYÀw6›E§Óßï'ø»ì2Ù3Â3ñ?V«Õ?ù|>ŸüFjßN'á¹Ô¦ 8òŽä*³E!|ÜtVXä÷ÿƽ-§uÿ?ü¥ƒËÅ•sô^IEND®B`‚HaCi/html/Images/key_small.png0000644000175000000000000000107210665316126015707 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ÌIDATxÚbüÿÿ?% €˜(D±D±D±Ä"Y´xy=5Ìäýõ¤užÝ}~ûþ•'žÞzÑÌËðÿ-3Íó@`ü#´¶†´º)ðÈJ00°ò10üûiþúøNóR›w~0üÛ 3_ÿ Ôÿl oAb6@^þ§ðë«§30³ð(±020±1k¼e(]®2ùçO¦?Œÿþ3üÿÃÈðïH3ãÿû'?¿Ú¹êePëv€b¥ƒÍé⿳Ø—îmRôÿ?Ïÿ¸ÿ½Éýÿï=žÿÿ_ñîÿÿŸñC(}‘ëÿ ;® ½v Ô¿ß%KyBEd^03¼üÂÀø‡‹Ÿìà·gîŸ`øËÉÅÀüâÃÛ?¿~üÿÅÀqâCd:P @ øóó§¢P×Ïï /¬@×6Ïc¸xz/Ãâ·?v²30ðÿe`xÄï€a"4ûH/@1‚œ‘ÃÏœ!¯Îì¨aüÏ’‹û/ï³{ nŸc¸tý!C6'ÃPb$#R üâ¥@½ÄHi^ Š@Ql@Ql@€ý¶¯—þ çIEND®B`‚HaCi/html/Images/left.png0000644000175000000000000000417012000412522014641 0ustar fighterroot‰PNG  IHDR00Wù‡sBIT|dˆ pHYs11·í(RtEXtSoftwarewww.inkscape.org›î<õIDATxÚíš[l×ÇÿçÌe=»{ר^bˆcƒí@¡&âR¢pMQÃ-jÚDˆ´U¤r jÕ‡V¥Rš¼µi‚ªF‰P"Ò´IÞúÒ<¥/•Ò„Ò£4äVnÀ`﮽·Ù9çôtfÄ'|°Ò-ØRT¼úôíìüGûÿïf­Õ2¥¾È\×€;_¶mŸ·sÞk]Ot Ý.½=+ÆŸfÇñpì`ÕN[SÛVö-6_½¥ªÓêÿ_ƒá€¡Ÿí °¬Y›ù38~¼iå&gdžxûÄÛiHdnªßÅæ†z†Ÿ¬¿w½³uÝVÒÏöôª ÀÆQ0,Ù³mÖÞ¿'>=áÓÃ@ÌÐoe+Àð´~÷†ÝÀð©aÒÏ å÷1Øx>™Hf>v--/ãØÉcÓMj7y®—Ù·}šÛ›1^Ç»ÿ|7r)f|˜y_”]äìß¹"%0Qž€T n–÷gÁðäÂÖ…Îã[‡pŠ•"„³@yx›×/_í›·£ÄJP¾ Í+©ˆàƼOàMØØ¼ªw†Ö ¡$JU­ RH&@uÆÌ¼?ºùQ,]¾9‘ƒ”Jźú Ðm)Jø-ônûê6ôöô¢à ‚JAh}€ ¼{ /³÷;{‘jK!WÉA(Aæã®_‹ÞÕÁë ‰†ÌÃ#Õ¤õU ÈXÄî\jým0óÞÙÞéìÚ¶ ·æ]A…ù%€èXXQ$²-ÙŒ¾E"°LV'ÃU'óa€˜œ PÞßÇ7ï뿃÷¢ Âd×Í —è[Ô‡µ+Ö¢(Š5ZucõÚ·€Ì8 K6­Þ„ÞÞ^ŒûãdœE† $ê îZ€dCùZRÆæ)óÔƒŠ{0ʻ뺙-ë¶ ÝšF®œƒb¡é¨#îJšÓp A!ÞÜFlÌÎo=B”÷:ïö§›ÒÎàÊA¸IÅj‘ŒS§`èšjž¦`Nã–(ï ¼‰¶©c^Öô­†²ʵ2$Msœumtwš )F€Ũ:è¹)vw·u£¯«UY 7ŸâêFn€{A(AÇF7§ápàÿãØwÙ÷ÐÈž³];ÝÛºm¨ø•È”¥ ­Ø´5euù”è0:¿f7§FÈæ6`Õçý$?„$ÛãÙžóåöeð¼È¼š§,Ûd"<'ãc®Èxô0ïL,ÒÓëñ±¢»S#k¬o‹,N4Lxo‰F±q®==MÝàŠC› KÄ«?ż=%2*î,Î:íTq—Œ1xŽÏõ` ¨ª¾O]`áÿbyÏŠ–FáJU¿Ý¯"e^*ÀgaY¶eD ásÅTôº+\Š ˜ÍÔL–'q©t ~Å ›7' $þûM̶-ôº²÷@NHüíýw÷ó@àOp0…=>W^ØÂa‡ À‰ËŽËBb?R—ÐÄ%bCç5\]’;W?ò̫ǿîÂäÅ5A„sÖAMÖ®çQ±1+6ÅA†PÙ¤3È4™$8]œs€Õñ­Ä¾Íû ?ûöO¿>::úbÑ*¬ÇB²5IoÂÉ“yÂUT.usjNÀªók•‡úòÅ‘Ú>”Ø^ŸùùZ»DsGš 32nÔt £È¼aœ*œX]4É#ÁË“CµÀ?“ONÇu‚Á˜Œ AFë2Ï£âV}2!þ¨ŽcT®TUõWÌaHfçÀKz±y„` #>Ó›§bÓ½>â¨G‹Ø€’:œ¯Þè ¹1 €¦™ÂØäæb0Äù-ÄSJªß‰"§öýb¡Ì}dÓwÝLj±@ Pt8i¨¦d Ù-ÈKâ0®ð¡j­rîrqhâHØ @‘ÆüÌ! ± ž0ÌÒµaÑeœ}þ¨âÿï8#¾¢|ùrs­4FúŒ û8•ˆJŽ(4 gÐXÓzaèŒë9øm }Ñ ¾†¢ze´pU°ªƒv7 PqÅÿ pÁqed|ØE{5TìÜ~ÚÏO  Ÿ,” …ñ"¾”è ˜i¾J`@¾Ÿ(| õ¾ÖטÖ0ÍÌLçÄK¸Ì†ÊÕÒùs#pï†'=À¿Ñ<ʈà ë#õHÙ×úi}ë âús¦BýŒÄ þ1|(–«š|ïôÈYdJ­È¨Llž¸äQƯˆa|"–)_ë/žEK­ +¸â€ÄŒоPÁ:Tå«Ç?lÌÅBvwl>8é«<ÊZ_’G.äF \Ü=§“ j10f€öÅÓÁ÷1.~t­x­8v©€>ç^°2‹7õƒ`L¸VÖWÔòèßï‹xjöÈØ¯Ä ¸Œok“#Ÿ?…~oRA Yw ˜©F¼ˆsüþ“âiôu/EÊÒúég €LòßçÁ2É‚ãœÿYkú3HÙ.P…2ôÏë}t2è—ŽŸœøÙ­Ïj½EúY =5¦s¾RüáÔÕ3âýÏ>ÄüæÔ:qÕTÇû"§õB¼vª¢õ“Zß4½~VjÀ~n?˜nImô'ý?M>UþËçê©õ­ZŸÓúƒ7×ßù­Ä€ÿw€ä“ x¤ãIEND®B`‚HaCi/html/Images/login_small.png0000644000175000000000000000134010706272524016224 0ustar fighterroot‰PNG  IHDRóÿa pHYs  ÒÝ~ügAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFVIDATxÚbøÿÿ?. * l›xUm_³ðHlò5ÑÕ>Í€Âe³§–Û“î'ÿÿ¯úõÿÄš«ÿ{·þÙóð¿D@HL-@á DC1églÀp{òAÒJ‹AÄZ‹á+0ø˜D•@ò¶@| Ä \üâd¸ ô?ßÎM\ÅðåÀêß?½¾ÀðäÜA üN˜B€Âÿ>½ûÈðèó'ߨMþ33ýc¸pÕÿ_ŸJNS@Øü¯Ë(ixž{Ú½ÿ‚{þÿç_ýó¿Ðþÿÿù¶þÿÏäRú (…¬ €42%0pŠ/dÔO|ÇÜüø?ÛŠÿÿ™³þgs|þà?ë|`Jœù÷?£N (FLaúa3÷†ÈÓÿ[þÿgì ÄŸýÏÀ)w¨jŒ<—þgŠ3f»èI˜>€Bvº#ƒqç†äoÿ¬çSÄ ˜TމUdƒÝÊÿ ϘØr`úÕÿ¬Â«¸55$€’2ZØHq;(‹#‹liº5ž›IEND®B`‚HaCi/html/Images/logo.png0000644000175000000000000020160010474157163014670 0ustar fighterroot‰PNG  IHDR—±d@bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ 75y6øtEXtCommentCreated with The GIMPïd%n IDATxÚì½wp]×çù9禗‘FÌY "ER­œ[ÒØc÷ºív‡ò®§¦kzw¦½5Õ==5³Ý]Û3]³[=ì^ÏT[²dK”e‹-R”EQ b&˜ $"¿|Ã9ûÇ{€@!‰ ß·êÖð.î{÷Üßùž_>‚4%”PB „,A %”ðqÀÑ)¡„n 4 4ø£Œ¬1Ég£ã°À4K#VB %Üd´¦Ãø›Ç‹|Là.ÇáËá«m«4b%”PÂ-AZiyû<_ëÉÇ‚¦ÉjÛbe—F¬„J¸%Hi£þVr8—PB  JäSB %”ȧ„J(‘O %”PB‰|J(¡„ù”PB %”ȧ„J(‘O %”PB‰|J(¡„ù”PB %”ȧ„J(‘O %”P"ŸJ(¡„ù”PB · JÃJxÏð´Æg%³…(­h%”ȧ„'|ŸVßÿnÛ )Ù`ÛÄ¥¼¦oK %”Èç}"­5ˆ•úZðZ>ÏÓÙ Ò0h¨©AkÍP*E]&Ã|Ó$$Ämß<§59­)“’’Ô”Èç}Á/N¶”V<ä„JÂ\ÖŠîÚZîܸ‘UË–¡´f×îÝß¹“l‘¨og(àˆçqÀóøl8L•X¥…«D>ï¸<›Íp%¨5L6Yöm.H–,liá÷~ÿ÷Ù´iJ)„eñìÎ%¡z•â•|žÎf¨4 ¶Ú6Õ%ò¹%ßàMV°_äó´ú>Gƒ€ïæsdKÃRÂMð¦ë²Çui þï\–6¥JƒR"ŸÉÃÕšËAÀ ù³·neó#pÌ÷Ùíæé+ S ã,V}J±Ãuñ–-婯~•v!xÓu¹¥*™]“ÃU­yÎuÉÖ×sÿ}÷QS[ËOÓi~rè3T@¹”Ó’¹ «5làãŽ1™\­il!ˆŒaf8B‚ð45A\­yÙsé(/cñwòðã“J&yóí}ÌÌfi4ŒÒ„+‘ÏÄÈhÍißçÇnžÕ[¶°iÓ&f44âo[[9”ÏÓ(%urú”sZsÒóxÕÍ{ÎQ×}£[¹Ok¶å²ÔƘ~±¹†ÁjËž–RºZÓ©Ïçr$Ö­cË=÷°aÃò¹ÿ­£ƒgβ<ði2JS®D> #x;8 ñ'O<Áâ¥K‰Åb<ðÐCüð™gøÍ™34x8Æ´Ó~’Jñ†ëòïS)êëë1Ç"ŠòrÖÖÕ …B „ ‘Hš9“—ƹî@?óyBŽá¦´æmÏãaðû÷ßϦ-[(++ã‘GåÅçŸçÔåËìJ§™6K9P%òßnß¼VVÆçï¿Å‹Ç‘RR[WÇ7ÿàøÞßü ož9Ë–¢\NO·Yuu5Ï=÷3gÎó}˲ˆÇãØvasɯ}ík|ñ‹_÷zÿןÿ9É—^‚drZŽ×9ð=4k¶neíš5ÔÔÔ „À²,¾øÔSüSo/¿zõUrBÔHYšx%ò¹G<n˜Á×¾þuêêêE‚‰F£|ê¾ûعc纻ٞÍò„š–ã`µµµÌ˜1cRçÇb1b±Ø¸ïÇ£Q2Æô\óÏû>{<öPˆß}òI,Z„Q¼W!k×®eÿÚµ¼qì?êïç·CaÊK¡÷éO>ýJqÒ÷9êû„ ‰#&ÖT~“ËÒ>s&k6ofÕªU#+;€iš444°õ¾ûøE{;ϼù&9Àã;Ÿ]­I³¤d•eÑ8E& RŠ¡¡!ÆÓ$ L²|>O.—÷zC©y×÷žÓšS¾ÏßÃ’èM´Ûc®ËñxŒ•wÝÅšu먪ªºæýªª*6nÚÄåS§øñO~@a`Ž“®ª€ß§Z–Z‹L³D>SCZ³ßuùëtšªDœå "‘ȸçŸÍçY³i<ö¡ÐØZÍ=[·rþâE~ÔÞÎÓB•rÌr?èI&9ÕÙÉç‡:)§ ùäóyvíÚEuuõ¸fÙ²eËFÞ?wîGŽ÷z­gÎM§a ¬øy­9ëûüm:Í€ã0«¼œ²DbÜó;\—Š–~ûË_¦n?ÙÊU«¸òÈ#¼¶o?†3ÆX(¥Èäó¼ÓÑÁF!ùª,4Íi™U?íɧFJÖÚ6n*I®¶–Ú X²dɸçßeš¬]»–M›6{Ncc#ŸûÜçhlläêÕ«ãž700ÀÑ#GHmÛÆË¢f  ü•êà_ÿ¯ÿÛ˜çhîÚ¼™?ýÓ?ÉpþÙÏ~Æÿùï`¯ÁÓåS!~âÇ "l; ©XŒÐÊ•¬ß²eÜó¥”455ñ裎í *++¹÷Þ{‘RÒÙÙ‰çyck]¹—ÚÚxóÙg™£4s cÚ–óL{ò Á\ÓäË‘({ÓifϜɗ¿üåsá†É'ÑhtÄÏ3ššš¨««Ã÷ýqÏyååíœ|k/k#6;3¦€sºBJ>³Ü²Æ=çïÒi:ÇÓäÅi4 Bc¬êSDó3… ZJ> óKß'òÔSOaYbŒûB`Û6¦iŽùþÈBXSÃ>ˆçyh=vÜñcÇøÿŽc¡í° h1§ïöä# !ø­p˜‹™ —.qùòeÖ­[÷®‡ ‡Ãã¾åÊ.¶]$uö _ …˜-)Qf Á àf¢|ÉÍÓ;Æû!XeY4™æ˜I†¦Dµ»(ŽÃ¡—|Sœ¤2 ­5{÷ìáÔž=Ô\íáaË&>E„H…läñ[HÌ1ÈGÿ/4ÎÿYS¬ÑØ|Óäá¶6žýáéêîžPÓý 8qü8ûvîÄlkã~)iÓ{zÞä#Šô€iQuî¿~ùe.\¸€û!D_”R$“I^Ú¶ì¡CÜrh0ŒRK…)j¬3MîèëçÕ_äıc$?¤<¥L&Ãî;9þê«<3ß4‰Ns™¹­ KWZk- :;ùû¿ý[º»»oùg¤R)^~ùeN;Fs:ÍöSšÅSóL“»l›9AÀOþùŸ9yâćò9o¿ý6 rµ‡¯8¡Û¢qÝmE>–¬3LîJò /pìèQoÙõ}ß§«³“ï÷»Ì¹ÔÎÒ B–Le@“að/…äðîÝìß¿Ÿ®®®[j¢çóyžûñQï¼ÃVCRw›´¡½ífÆÃ`½ÖXW:Ùþ œjm½e×îììäõ;9þÎ;ܑ˱ҲJ)äÓUBp·aP××Ïþ;Ø»gÏ-»v2™d÷îÝØ»—ÆÞ^î±ll!n‹n™·ùÄ„`–a°Ð4ysÇΞ93nØó½âÊ•+¼ù›ßà¦Ó,4 f¥Âé§\bYœÝ·û÷ß2™I&“¼öë_ÓÓÝÍL ÍÛg¹ºíÈG¡5ºxÜB­5 P¥^ÆÓ ´ÆWŠ@©[&7ZkT ŠrÜF‰ÛŽ|”â¸ç±Ûsyâß`ÝwN˜ö^°`áBžüÒ—pâqöú>­ãd±–0µÕšó¾ÏN7ϺÏ|†Ç?ýé›&¡NÕÕÕüÎ7¾Áì¹s9¼•Ï—ÈgºâX°+ä°pÍ6nÞLCCÃ-»v<gñâÅ<ö¹Ïq¸ª’·}Ÿ¼žú¢PpyÌó¸ä{cv2Rš]ù<íA@NO½¯C)~$æ/`ý¦M,\´èÖ™tŽÃìÙ³yà‘GH¶4óªç‘¹M´æÛŠ|®*Å>ßãTy9O>õÍ--™¾çÁ”’ªêjžxê)¼yó8(§>Ĥ´Štº”âM×å¹Ïø>ÝåÔ76ÇB ¥¤ººšÊ¦y¼fÛ<ãæÙ–ÏsÔ÷§üV:CJqÔ÷ÙŽfó#³råJÊÊÊnÙõ‡K3|øaªW­â˜mqÀó¦Í¢U" ~žýžÇaÓ$ÚÔÄS_úÒ„©îï‘H„Í›7³|õj.WVò²›Ç¢0:•âußã{Zó×e v6Í£þÞ{øÔÖ­444`š&¦i²lÙ2üÂ֮᧠ü÷Pˆg‹7¥5Á•™3AÀ^­IWUñØç?OsKˇòY+V¬`íºuèÙ³y6Ÿ£O+¦û6æíB<®ÖüÂó\¹‚Ç?󪫫o™Ý>–ô…'žào»ºøÕ³Ïòy'ÄLØRû}) ¥—˲Ý0H,XÀ¿ÿַظq#ÕÕÕX¶eY#c¸~ýzV­ZEœ:uŠç~øCvlÛÆ«Ûø‘wXeS,çIiÍo|Ÿ}uµ<ùÄ455ÛfåVh@[¶n¥ãòe¾ûŸÿ3Ÿ·b– 1óĆcÁ …jÞ•–ÅÛ¦a…‰3ZóË\Ž×,“E<À—¾ò***>ÔÏŒ'ôôôpîüy.÷õ³Ü4§LÖª’ZóßÝy’Õ4JA­œžùbÓž|RÅ–˜o¸.åZÓqò$/þàãkIB°þ®»xøá‡©­­óœl6ËØñÊ+¸ƒƒ˜ãd¤¦R)r™ g=—}®ËlÃøÄ“.*Å Z‘Xµšû?ûYÖÝy礉g4êêê¸ÿhokcçÓOSyñ"K 㯺Zs1xÓsétl*»ºøÅÓO{~˜ÕÒB4W;r]—³gÏò£gŸ%ÓߡԘ×|>—¢=Ÿç ,7MjíùLI {8—IIíÀîÛoÓúöÛãžÿN&ÃùóçI$|ö³Ÿs%koogÛóÏóôßÿ=KB!†1nŸ/¨1 Îþ”Øé4§5g”bW8Ì_~ë[¬\½úš>Ö2~÷þ€Ðzág}Ÿ•ïƒÈ>ê18乘@}6‹sò$­'OŽ{þ™\§¹™x<ÎSO=5¦IßÛÛË®×^ã¿þÅ_°8¢Ú4qÆYˆ<¥©ôóZs¥²’;6l ¬¬lÜv³ïRJ–­XÁ±·ÞâàÁøNã\ËÖ«WÙ`[S¦5êdáݾG:fÁ‚ŸÙÀétšl6K:¦««‹®®.Èårø¾?’¥F©¬¬¤¾¾žŠŠ b±áp˜h4JUUÕÕÕø†Éß'nÛXÓdÜ0[Jž‚½oöîeá¢E455ÑÛÛË/·m#t²•û‡ÊR§ùL„Ã`½m³F¼øÌ34462söl¶ýüçd®^e‹¬0­iwßýJ1(LÇ¡¾¾žžžöîÝËž={8xð G%›ÍF)//ѵÖär9GÂÑ ,]ºtd¢eË–‘ˆÅHT”ÓžL±Të)±‡×d‘’U¶ÍæP˜ƒ¿üõ T>ù$Ï=÷ígÏòyßçÞP¨4¹Jäsó•l¦”ü®mó‡³cÇÊËËÙñÓçxR)Z显MudµÆÕšC‡óío›®®.B¡õõõlÙ²…'žx‚ºº:ÊËËoH4 ‚ÏóÈf³\½z•ÎÎN:::8pàÛ¶m£¢¢‚Î+WÈõôÒgYÓ¬vIQ!ø²ãð_ÛÚØ½{7N8Ì?ýÃ?°¶û*k cZïDQ"Ÿ[¼’ÝiZ,N&9ôÊ+dLãÊeGh˜f¹åµær°Íóh-V¡¾ÏªU«hjjbîܹ̜9“ºº:***®Ù&ùŸ‘Ö#Û,÷ôôpåÊ.]ºÄ¹sçhkk£»»›)xÅ÷˜å™Ü) NNŸ±4)lw³2æ7û÷óƒÎNzÏœa“í°Ø¶)\%ò™ôÀTIÉcŽÃ;~‚Ë*à‰XŒ%S¨Lâf(TúŸô}öº.{jª‰ÏXÂcK–p÷–-l¼ë.f̘1éz¦aÇsEEÌŸ?¥Ùl–#Gް{÷nÞÚ³‡Ë.ð‹övú]5¦¦©¨LõQ@\î±mN_¾Ì‹çÎñp4Ê*Ëšp´ù”p àAÇaW>G] øŠmž&Äã½JñªïñtðŽmñ™­[yòÉ'Ù°q#Ñ[´­±”’h4ʆ X¿~=lß¾ú‡àí³çØèy|]+š‘)¶¯×xXgYµlNzè„h,ϸd …(+Ž|5á›Ñ(k,»4:Ê Nû>žÖ,0M¬i°BûÀ)ßçsYöG"4¬\É7þÕ¿bÑ¢EÔÔÖ …>´Šß÷I§Ó\¹|™_<ÿ<Ûö3úÞy‡o†Ã<䄦EA³¦PDy9XnY8Ó„T?RZ³×uùboÉ‚¿O”4Ÿ›­ÜÀ\Ã@Ôj‰1ú•âÏãû¹,Ù%K¸ÿ¾ûøÔƒ²rõjb±Ø-I*œPÕ6MÊÊʈD"|ö‹_¤aölv¾ü2ÏlÛFw>Çöà ӜÒ/€:)©”rÚhÊ%³ëcÂt‰l]UŠ·=—Ÿ)ÅàwpÏãóÀƒ²lùòMÓ–eÑÜÒBEe%uõõüв8ðúëxW¯wLñ],!°JS§D>%ÔÞ}žË ÒàØÜ¹|ëßàS÷ÝGccãÇú½*++Ù¼y3³fÍâÿuönßNæêUj¥¤VJJÆÿô¶*J¸ pØsùç\ŽS-Íüéø|þ _¸¥Íó?°ÔÜÌ¿ù·ÿ–;Ÿ|’W¤äÿÉe¹ªTéÁ•4Ÿ>…v]—Ü$ÿ'$ ^,¿®¢XSØsé€çÑ5IŽÁÜâÆÃfW@¡ÑÖIÏc`’Ira3 ƒfü¡¦ç çÑx“º7A”,0Í÷T4œÃówÙ,öÝwó{_þ2wnØ@$¹e[Ý?‰ÔÔÔð…'Ÿ$ê8ü¿ú+šÝ<Z6sÞ£ªW)Îû>W&ù¬M`µeQu]½YOñ:“¼ŽÌ2 fÆ5-c‡”â¬ïÓ>ÉëØÀ’bxþúgÝ£—‚€ö`ò­6f¿SÕ',êö‰%Wk:‚€ïg29žß78ˆáy¬±íÈ m_Êå8ˆ&™ w%™ÎÎdx,¢RÊòñ´æRð“\–+¶C"6q8:™Ncg²l¶êÃÆ ‚ôól–}ž‹eÛTOкb(•ÆÊdXoÛÌ4ŒI“:µâÝ<îòål}ôQîݺõCo!û~aÛ6---x>ʥ˗yeÛ6ÊÒi*„óžš°u/çs¼­¡ºbüq E6Ÿ#ŸJóÇñ81)¯ñÓt¿Ìå8 ˜ðùäò.ÉÁAîw …®!Ÿ>­x%ŸçM ª|â/²ù<îÐߌƈZÖ Ïº#ØžËñºïQSY‰1Á¸¢»¿{L‹C¡ùLZóÑš>¥ØéæY{ÇÝÌ,:އ¶#Gèõ‰1µÆÕðB!,XÀÿò»¿Ë¸t‰}‡јËs—”“Ž€õ+ÅIggÔ±ðá‡Ç_à\—žóçÙõòË|-Á3ÍkêÍú”â¸Vœ®ŸÁÂG™ð3/_¹ÂÉ7Þ`v:ÍæëžQJiŽ«€Öúz>ýè£^çÜùóìÙ¾ÇU@^›c>ÓÓRÒZSü˜pë§t:ͱ—^¢>fí'Єçùµ¯ñÔSOMxÎÏ~ö3~ñ?ÿ'òµžw÷=÷ðgögÔÕÕ{Οy†Ÿþ—ÿ'[Ç|?‰pïg>Ãÿ»7ág½ôÒK¼ð÷¯¼:æûe–ÍC[·òµ?üCÖO@d/¼ðÏÿã?«¯NzÌ $þʶhZ½šG>ó-^<%|Ñh”+VðäSO±-—ã²RzàL–€Êb16mØÈ_ýÍߌo’æó¼øâ‹ìzùåñÍïPˆ;×®ð:÷ïç¯ÿüÏ ïÙÃXû$Y†Áš;î¸éu~ù⋜~ë-d0>YT”—±eÃþì?ý'ªkjÆ_D¯\áJ{;‰#GÁu?qϹäpž¦¸ìó(‘Ï4Å* }ÖL6Þ? /&O¹{H$l¹ÿ~ŸzCZSŠ•ȧ„O(TÑ/ð–çã67óèc}$™Ë„,_±‚E7ÒQ–à `°~/‘O ŸLøZsÔ÷i‡¨^°€õ6|äÙË·555,\¼˜YK—ò+L:|^B‰|Jøˆá/>Ë—³båÊm{ß³çÎeóã³Ã²èœ¢ûÞ—P"Ÿior¥´æ\Žú•+Y¾bÅ´¸¯ºº:Ö¬[GÎ49øtAéa—ȧ„ORZsÆ÷4 ›š˜5{ö´¸/Çq¨­«£iÁ.…B\R%ò¹­ÉGk}Ó£„ƒJqDTÔ×3cÆ âñø´¸/!áH„ÕkÖÐ]VÆ%õÑÉÖdä¼4>Dò=ªxh ¹TjŒcä½i𦒠 jÍqÍË–QYU5­ÕqV¬^MoYW>D¯Ïõ²>ü7¥J)‚ ÷ÐÅyqý\P%2æÍ¼›°©¯ûyôë5+ï¶Hý3Z¢Š'E8£É´xŒÈÑ«óÇ ÐfY¬nj"žHL+Aµm›¦¦&R–E¯ç8…BÎ[M6ªøõè…Tk|ß¿fNŒþ!Äù( ÙåÁ(ù}áMGË‹¸MŽM>R¢mYèpbQt<ñÄèDb1D$tg'Á;ï .\×E²˜ /ŠêÕhú°¼ßóèÉe9|]#* ¤”bPi'A¶~0èyö=ù>5E’ÉhÍ!Ï¥S)²£6̾ŸÑÇðõRJÓî¹ñÉçzÓgÁ‚üóÌâÙ³‘¾Ï@_½½½ôöõÑ}¹ƒA×Óä7o¾‰”’P(D$¡¢¢‚êêjÊ+*(/+cùŠüÑŸü ;¶mãøîÝœìè ŸÎ \—×¥ÕóX¡žç]#+ב֚ŒÖ¼–ÏÓ¡5Ƶ éI×¥G…Y6 z]›×;ö\Ïãï~—_÷õѧTA->´@)­ ‚`Ä÷ã„ìZ¶Œåõõ„+*ˆš|AA-6‹“øw®×„njjjXºlCCCœsnl¿(Å<ÏcþüùØ£*Õ‡W°áWß÷™=w.¸ÒÙÉÙÁAÞ>r„R©Z–/[F(b`pp(„4 ¤”Eâ1Š¿WVV²pÙ2:{{é …è»Žôš]—Å‹“x&R^ëBßšÀÇ’‰àdà“Vš‹mm8pàÛ:ム94„ö½€«åBP“~5ãùælÛæÜ¹¯ÈJ· IDATs¼úê«ôõõ‘Ëçñ=¯ ëA€.šI µ·2 Ë41- Û¶)/+cÖìÙܱe ó¿þu²Ù,{÷îeÿ¾}mmÌJ§i™?Ÿ|>?"£a2ª¬¬dÃÝw“J§é6Mú¯×â=yee,]±Ëþøû<~–y½y‚Ö$8wµ‡ÞI8ö⦉aÛh¥ð3i²”!0µÀÒ°Fû†Š_VÞbÐÆÙ¸qãû¼#GŽðôÓOsæÌ™"¶á¥ÐEÕZkMee%Õ[·Çɺ.J C`š&¦a`š…íƒ ÃàŽ;î`íÚµãªØ*àùl–ÿchˆšH„°i’ךœRüÅ_þåˆ@O7(¥IÁa7!í©÷Y_DùÜõkדŽRŠT*ʼn'عs'Ge``ß÷ >@þÐåŽZ[[ùõŽD",\È}÷ÝÇãŸþ4Jkü @JI6ŸG i˜†Ä0Í‘EKJÉú;ïäçÏ?ƒÌ|Ržåh²™Èï{½x$P5êÿoðùŒ8ÆÄ$®Z´Å£‘äó¸¹Ï[{ö°ïí·©Ÿ1ƒ­[·²~ýz¢Ñ(žR $BºØÃ –ab˜FQk­}œÓZë‚XJ0 ´è¢68¢Ij ¾5ÄN÷ÑñòÃ2 !p‡H8L08DNƒãŠR„UPˆ ¡Ð ¡ =›†¿ôÇ6 ÅWÃ0°m)å„ä#„  Féw=ryKa›&‚ÂØDˆ!Œ[îãÒÅgU³åž{X»nÝm-ñ<§O“ßµ‹ ¯ÿÝç3cþ|šî\O>Ÿg``€Ã‡³cÇŽ?N.›ýH¾_t´·óÌÓO³}ûvÖ­[Çúõë©®©!Кœ”˜¦‰cZX¦Q$"Ó4'$¡j¾hÃ@Û6D"èª*T}=º®]Y Ñ(Ú4 äžN£{z.µ#ÚÛ½½ˆ\‚`$èäݸ0Ê15Ÿ@©Iµ.R ‡q, ×utŒXœ° ˆx>ï |øJ¢…F«?ßa˜µe‘|¸ÉC•RRV^ŽÒšõ<­ iM„Dˤ,ø´B 9L·Z’XÈaö¬Y´´´Üväãº.---\zçôõVçD‚²E‹¸ãž{0kjÙ³g;vìàè±c$‡†&\X†_MË*˜Ñ¦YxŽB2œt¢”B~^=¯4AïûtuvòÒ/~Áž7ßäŽ5kØ´y3•UUd\¼å°¢öl)u  û‡ýBª$Ú¶Ññ8Ô×£.$hiFÕÖ’w²J‘q]²®‡«´”˜õõ„—-#lÛ˜CIôáÈ·÷b\¼ˆ‘J#µÆÕÿÚñ1np83|LRø£Ñ(Žãô}ú ¤4ˆz.¾›Gç]¤ëb¸.–/°˜"À¤… ù1&] çï†iY7:™¦Iuu5YÏg!¢ÒÀ74:Jah0µÆC©MRˆi·;„i¢P7R¶|9§x{ûËtvwÓÕÕuMNÖõ“×´,"á0±xœx"AEUñŠ B‘¦í€a€(¥ /ïâ¹.ÙtŠÁ¾~RCƒä2™‘Ãó<Ôtôôôðë;8}ú4›·laõêÕ iÏ#çù8–…c𨖉mZJcrDCþÞ·\ ¢ZSY‰ni&Xµš i)éîíåbk+{zèè +¢?›%Úp(De,ƬšZfÍbÁë)[»†àµ˜¯ïÆîî"¯Þ0·æÁu®t!†¦hÓÝ„¤”D#¤a’}å•H;„çæÐ™†™Á6²8Bà¡ þ_¡•¸Æùüqi<#æ‹abÛöM¤eYTWW“Ñš~ÓÄG•¯hnIQØýÀl Òhã]»¸D: Ó$ÞÒBð™Ï9“£Çñ«S§èH¥ðÇñëH) …ÃTVV2£¡‘³f’¨ªB‡#x–…' \!É ðEÑdÐG¨„†(Ð Ó÷!“&70ÀÐÕ« \í¦·«‹d?žç¡¯#>Ïó¸pþ<===œ=s†-wßMÃÌ™d݈!Ë"P [)”eb²Ñþ ÷MBB Ãaô¬Y¨õëñV®¤/âÀoð›¶KœK%ô¼IwÔZ“Ëå¸ÐÑAw?m]Gº££ ï(ÑhÈM<½ØšÉ“O(BKƒp„þjƒP&ƒeY„… ¯®ïáû.Êóо‡ò%Z£Õ¯ÕìB¤a`Ù6â&f—aDc1:-‹>Ûi L£è44ˆ®”øR¢¤,¸Ï’Öóa,555ÔÝw©¡!~¼ýe^îí%5†\‰b~NyEÍÍ-´,^„]YI2¦SHÒ@^ƒ..ÃQFžÞ»‡æúVöy­É Ái98õ „ëêY¸d)ù+—¹rî,]/’$(jcÃQ¸ö¶6¶ ÑÛÓÖ{ï%‹‘Éæð” ¾*p¥Q–º†€†ïMJùÞ´kÓD—•¡–.%ؼ™TyGâÕÖSK‘ ‚Ò¾¦”)RŒâü®óô‹çkù™L†ýG¢¥äÑ»ï&{©l*…N¥ ð¡¢>2f´k8„6YòQ†É@$ÆFY&aÀ |<×Eårf% ´ïɧôá; ÄHÔÈ4MäM¾—išD¢QVÓ†$d¸¦‰g†A JD!(ÄÚKdq«‰ ¥¥…îãÇy}÷ë$;޶ŽD˜=g‹–,¡ªq&™H”6ËfH+\žR…g¦Å»ëq1KßàÝð~Áä\»D 4õ€…Ÿ} CB2ž¿ˆysæÒp©ö“'én¿Djhh„„”R ô÷ó›7Þ```€û|òš’Ù®ài¯*¤qŒX £E¥Ôä È4ѵµë×㮹ƒ+ƒƒ¼úÒKìºz•^ß'Ðï¦Ä†cYDm›²H„²Pˆ°4°¥EÎ÷ÈeéËdèÏåp‹äº.ÇZ[™7g3W¯"캯<êÊcæ5¾—â—V“Ð|„X–E(—‚d,JN  žëág3–‰2L´,8ìâ3 G¤D!Úe˜æ„MmÛØ¡¡0IË!,$!)p '%aH‰–¿R”ª˜?$âQÅR…vì~ƒóƒƒ¸ã,‰²2/YÂÂeË•\¶zä´ÆÓ ‹>Îb9 ÅCc"0Šþ) °‹Øhò¹¶ø8 Xd*À׊Û!Ö<Ÿ…3fPá§»½\&=rOÙl–£GŽày÷?ô•uu ¹n1LíÍÀ¢¶aY×( “& Ã(ÏæÍd–.áÔùól;r„CC#;Ëz"¡6‘ 6§Â´ˆf&ƒH§ 2|Ï/,´ŽÃÌxŒLy9g{û¸˜"[LÒÍd2œoogÞºuøUUˆxr9¨¨‰›#Öëð—-ªœ“±ólÛ&‰GsBø×!°l”i£!‡µÂ!Ɖ:|ÔZz”Jnšš]RJb‰JJÒ–MÞv0_ )PÅiü’âšzþn1ñ¸®ËåË—Ùµkç»»Æ$Ã0¨¬¬dÙŠ•´,]B.žà‚iÓ¸ºàHVR€ÒSB),ÀÒ`#°Ø‚B”‰,” M’(†µaò ´&ЂðøBãShq;Q¶d)Ëkj8ûÎ;\:ÕJzhhDÈçó´ž<‰‚~˜xu5ýù¾Rø¡B ŒÒ¼ëµÑ¡aÜ$ŸLtEjýz2 rèÄ ~ÞÚʉLo”iZ3§¦†Yåå”öÀ t\ÆëÄ ÅOŸàd>GÀ» à 55,š9“zÃÄîî&¸ÔŽ;”ijLŒšZÌX”`hˆL2…'(ø6‘h)pm‹òòrª{zèÎdȆa …<Ÿ¼ã  °,¨¬ˆßØRCNŽ„Ø–…aZxRàIÖKßuÌQÌlæÝcŸ …À‚ˆ”ìÞ¹CJˆÅã˜71‘n.ÄEõ¦I 5©TjÂìf)%áh”ŒЗ͑4¾Và{è ð=rÏPpU+hÂZ¢ ¸Ã~1IMoØfîïíåõ;éjog©”GßÈ9Št6Ë[{öŽF?P}Ò'xlÛ&‰ÏçÙµk;vì ;F¶²”’òòr,^Luc#çr.ç½ú„À¢6§… ‚B²¨ p‹ qŽÖä´.j>à˜&呦e‘J¥È»î»ä3rëÇ(X¾Öí§¨eyy ¯§—ŒÖˆª ¢sæQ9¿, yñ"©³çðÓ”,$`šŽC(žÀtBHãÝ­­mÛ¦¢¼œl&MÆ´ÐÑx!âUQ5Çö‡èIù|L³P“â¾!AHFjÔ 5¥‘ªð7¡u¨G¯Öã¤þZ@•”¬²ö=óCŽl…†–f¢3g‡?P ­ )Z¢ñ8óššh»ÔN>ïÞÔç3ÉpÉë¡Ë´0s9®¦“„“CDŠG8•"”Íb粘®‹øÈ`¸´dòå$AL&é>y’övs9VF¢ 7Êp„ \C"“áÅïŸCo½E¬¶¶ âN3 OŽ P__Okk+û÷ïg ¿Ìs£Ñ(-MÍT×Ö±ëè1ŽƒNm[Ã(È© 0|Ãu1=3ïb¹yLÏÃð=Ì  kiM<fVý b--˜‰8Éîn>ÄÀHÆtÑ÷2ÀsåŠþS)QB¢„ ’À4 ¤¶lª+Ê™ ««%—Ëâçr#÷’Íf9üÎ;”•—Ó¼x1]m—8vñ*›ÇR¶R˜Jaj) ó-—Í ÓÚ/qñÄ f #j£Qôúõô¥Ò¼tâG³üQc»pÞŽmÓ—JsEeè–&$‡}=ˆînèêBtw!zz` ‘LB&n¾àÝ‚ñ¼‡ï)…àK¶Ígm›5åTÊB#@£aðÅp˜Óä7—/ój[‡|ŸÌ4Ô|ÇaÍš5ÔÔÔpêôiNŸ>M~Œ<žábî¬Y,kjbàjÙ¡5 &…߃\‘É@: ™4"†l²YÈ瞇©53ª«¹cÅrj.$9ÐÏ•öKÌoiá˜ìÚ±ƒd*5Ê”¿N”Ũh§4 îÓ@›Ø68V4Æü¹s™SY‡ {ª'®Öš¡¡!8@Yy9s++øõÞ½;s•L#rÙ‚óÖu¾ö}–š÷ZߎÆXiY”I‰=³¿±‘7÷ìá­drÄÇcÛ6M³g³º¹™P{Þ±dóyT}=e--T/˜O¨¬ŒþžΟ?ˆÇ˜;s–i`(…P¦TW”s.^¹ÂP6ƒ_\dãñ8åee\¼ÜIZ È'v̱ü!ZONóNtÊ)Dª"ñ(¡†JˆHëwcúFÿÏX^ˆ A“iReHb^Êf B!,Çyß»pŽ&Ÿxå{„L“Úº:åt\îàr?éâg„ÃašæÎ%Ý×KÒuQ†QÐMG| ³«Pqk N€Ñ$D ƒ»6làÓ¿õ[,Z´èý“Ö<Êf‘RpòèÑqÉGA4CUVQ]=G Êj4õB0CB½!©‚JCP& ¢R’…‰QÆÉω'ø™m3tàÀ5æ Ó$l›Æúz~çw~‡ÙsæL›èVÞuÙ[,CpóùqÒW æÌšEÓ¬Yt¶wpF †Ba|Ã@{.ŽeQWSͺ¥ËH )—’rCRf$ IÜ4‰˜1Û& aIÉàÀûöíヹpáˆs»··—Ýo¼Á¿õ[ÔÔ×cGcX–‰! LCbV±*Ýn—Ê»#ü À ò~@Ö÷Iû)ÏÇS¹þ"–Åѽ{ÉM:­5ƒƒƒœjme^K ›—/££ØŠ£Ê¶©°,Âhòƒƒ¼òÊvÔÐP!;½6L|Ïåh&ÃèÑ‹ÅbÌ9“àêUrÉ$Ö¬YT.]JÎ÷8±÷GÏž¥½¯·P@Zt³DÂaT6‡öó.PQ’Mçh;s‘¤q`µÆhíů„µ¦ß8_X»0 Ffw•­Ç€§øy)%A9¢63ÍÄì,õÓól¬¬pse…Í~›ÿÜìì,‹§O³wç^ÿ@~c df_ |tÒ?iÎûëü›vö`Ò5Tõ\‰$à#¥<€T.ò"_,ôÏA*‡DÒ1 YHæF÷-úäwÞ¡Ùhœøóõz—^|‘ý½}n I·\ÂJ2ù(:Ÿ\…Æ2¢u­Q’‘¼â©éJ¨Qα²²Â/ùK~ûïÿN«Õ:±°Ö²±±Á;o¿ÍùÛ¿Å9G+QT¤ÂîÒ œòA …I¼þ<²)½²Þ;*„L‘JB½ÎÌü<÷ªÕøÜÏêò2q»C­>Æ*‚6P Š33<ø(qþϤ1Ø4LJ‹Ï8Ë2c''‰7·1Ö¢…$ÐÁɃc˜;}šêÈ(a©ÄÎڟܽËÝfcОU«U^½v ÚZ[[$&Cˆi2?ùMHÒT (Ïa&æ#нí¢ír¾§žaÀÇØ|¤OÝí’Rb„À8ñX.™,}Èm ‚-t÷ßÏ6‰ÄŽ·vwyÿƒ¸ÿþ ´ï¸ªgaa©ÉIîîµÙˆÊd¡WÙ+çŒ!HtS²†Ñ@3¢u<U­©D X__ç×ÿò/¼ûî»ìîî>•zH’„;wîðàþ}”µt²Œ¦qì;GÇz‘bâœßÂ+þ‹¤­}úE¨5¥0¤ùÊ«ET*e¦¦¦;DXkÙÛÝe¯Õ¢ŒC} t /Á!&Hµ¸+k¡ßc: E÷z=š­#§ç FGس–XkFGFÑÇ 9:>¤“$ˆRÄÚÚ*ܸÁ§«+ìæíV\8žsSS4=b¿×'3ƃq–Ašxðé÷S}ò4èÙ+C¡^Öó¸¡?Ëý#«x†‰ìgI{¹MFa© œ¿{Éh¤yõ3Ðîs0ƒý¶êyJÓ”û÷pýúuöööN‚Z­Æ‹/ÒîtùB…¤Q„C ²„Àf”’„°£±DÖ0¢55­¨jMYk*QH %;ÛÛ¼ýöÛ¼ûÞ{'¶ZÇ fçç1Jѱ–n~ÁJçÉÔP@„ ´Ž@Y?j/*íü|¦à ;¿¨9>6Æèؘ_â›z½mΟ;‡KEOH‚|÷̆&ó›¤_p•ÖBs—…‘*jè>Øív¹ûà~òæÙ^ß`×XƧ¦˜X]fs¨ê*žû“›7Ùk¶ˆ¤`kk‹«Á‰ø IDATÕöóvXJÉÜܯ_½J²½ÃÎvƒNf0Z5È,¯zâ’$>ÖÃùyÚ.‘ËÕÝá|„ b7ÿÇQþr=ùy¦÷ZT>NO U?ƒ‡¾ê‘_Ï^ׯvËæâºÏn|ÆòòòÝggg™™šææÆÛ¥ˆL(kÐÖf•$!ì÷DÆPÓšªVx‚€P)zíŸ\¿Î»ï¾ËÖÖÖSGkÍ™³gyñÕW™\8OVãQT¦†„Ê AKRP²PÆ/•Ü"†½˜…°›ßžXÆYFj5j##(­I‡Tëq³Ûj!­A ˆ… '¡pÒû/› Å‘9‹rÒ·û­‹cuN逵,Å9GÇܽŸK‹‹\¸|‰=çxØî0]¯³0;ËÞÇô‡*Nc ;­V d™Á8;¸>&''ùε—(;X[]¥™Æ$Å{¶•¦xâ>ÄǀϗyV¯“š9¯| ìþÈÈf?µÏ´Nlæ!Úv7ì"}[ï<ß‘¥)ËËËܺy“öþþ‰?W©T¸´¸Hj ÷¥¢F€@ÙŒÀJqJ9Ž û}´DÎQɧ”û$»Ì°´ôˆ÷÷;VWWOºáJëÚ+¯rùõ×ȦgX-WØ–šX*"%© OÇùWæràq9¸Ñ"¯‚ŠKÂI™ƒTD¹EÑcÕx–eôº]_u I¦$±TĿ„˜0$µÖAX‡´·ßf²ßç•RÄZûÀGh§Ñàí÷Þ£V­òʵ«p÷­]fæÏ²×épwss°*QLî !çõ—^b¦^gcy™nŸ (ëÀøu‘Ä^•Ýëõõ µÀsáŽÃùVÃ:vh‰´Øçj¹ÜãÅølÊã‚¿  ™.ypõàÿxž¯êqÎÑît¸uëKKK'r=B&&&X8·ÀÚ~‡Õ0Â:ËPÖf)å4¦÷ ã¾çX¬¥¢5%­ˆ´B­½]>ùäîܽûÄH!§NâÏ~øCf®^e­>Îý0b _ —¥`Tx_§ áAÇq›ïJº!ÿÕðäb{•ƒA 4NøUžã¨­µ¾²~O2“Þ=3v^AíÂз^YFf4ÖøÔaŒ¡´µÅç¸f<ˆûƒååKKüò_ÿ•Ÿüð‡¼zùw––Y_w,., [[Ä'¬í)«ßyùeÎNϰ¹µÍ£vÇ»I*…¶m,*K àé÷¡ßéë¯á¬ÉëëGé~Ç=Öш/ÕÜ}ÕÏó¤‰ p´Y°_Ê¿=²,ceu•O?ýô‰¤o†œ9}š(Šx¸ße·TÎù;MF”¤”“„jÅ}´ œ;Ø—“e¬¬¬pãÆ ZÍæ‰í–”’ùÓ§ùÁOJõÂEnŽqSìæ>>¥<4ÀrÔ—Ï=õt÷…´·'Ä‹;‹Í2â^ÿÄ×äp =©œ I*¼¬Ã&|•¥X)p6»ý6³ûûüplŒýÆ;i:ð]¾ûàÝn—¼ù&W^¼B<ðíÖS*¬ïýèGÔ/^âúØ8ŸŠ€Va¾Ê.óé“7s(kcöaØ@ãv¤/$JJ²$as}­õµÇÚ@¥Q©„’T ¬òÚ³Ôù¿ët€ScV¨ƒ6Ï:d–mmòÂHÖä)~»³M3=à–×Öøåo~C«µËµ_`´Tb¥Ó¥>3ÃëS§hwºì·;$ÎP©Ö˜žš¢>6Fß:n´vYëÇ$N …"$CZ‹6)Qš¦1QÒ‡b¶Ûíé¯õÌaøîï†íì—¤´¿ÁÊçYAÂ9 ßêy¾ Ñl Íf“Ï?ÿœÖ k.Åg\¯×™š:ŃNͰŒ•’Àx®'JcÊIB%Ž©Ä Aœøl6a¾ú€uìíî±¼¼L§Ó9±Â ‚€Ë/¾Èü¥ËÜãSÑ—g³Igý¿ë¼ùX)÷Š„ Ì­Xn ~?оß;¼P0MSv[-nߺÅúÚÚ¡Ä‹¢â«Ž’ A_*Òœo4x»´W8[er¯Q¬´Ž Ó¥¶¶Æµs $§¦x{‹fpY–±»»Ëƒå%NÏÍšý4a£gÑŠZµÆôØJù¥êD)îµ{le){Y†ÍR´ÉP&C§™ß¡‹ÊqŸ¨ß§Óë#zyåÓÛÿºÀÇBðá’R¸?ZÌ9©›zêV,û¯ö±Öí[üyî{Wše¬¯­ñèÑ£ÕÌÅ833ƒTŠU'é„¡k4†0M)¥)åÄP9‰ Òe´ƒ 7ýwiÊþþÛ[[O¬°*• ç/^ÄŒŒð *Ó’ üjq@ÉAÙ9*@ÅA(9K„7Z×€…ÌçV‰¡¼ÎÁÒ©µ´ÛmnÞ¸Á§×¯³LÛY.—™8uŠžÖtT»úˆ&›{T!%NÈð/f¯P§üf~Z˜³9ÀB“¤)Q’PŽc*ý>Õ¸O­ß'ìõh÷{yåC«×׿·3éOMT÷{–¿úþ÷9W®ðÏqÊ£8!È%éSyËÎR‘‚²”¾âÂO±^ä,Ή¤p 2L’„Õµ5Þ}÷]Þ~ûmÜ¿Oÿ³4¥3ssœ¿|™V©ÌjÐ·Ž¿8Z,r k=äÓ„È5hC$RÊB4¤6#k·;4³Œnš"RáœÍ-g-Êf(cæ`g“|²­œ'Ùcˆ2C)K©¤å,¥–¥ÈÔOE’úÅÒf3Ñ¿¿Óé[àùöxÆ–+כܹs‡ÍÍÍ'þ­5§N‘ Á–Ò©‘Æ{.YJ”ÆDIB)Iˆ’”0ËP™Ém^ ;Ÿ©V*•©¡žxÖZ6Ö×ù§ÿñ?ØØÚâµï}Ÿÿ÷ìY’‘1–²Œf’’Ä1a–Qu–ŠÀû7!Pä ŸøEå?à•Ê|öÙg|øÑGܾ}›f³y,ÿ$„`l|œ×¿û]ÔÔ÷*5¶È'Xdh)ÑΡ¬Ad)"Ë™Av6ÅžeŽ|Ú B%RHØÜ%‹\«E¬ý¾›V~3Þá¹-e3tfÐYŠ®„Üm²²ŽÀYBcˆŒ¥d2Êyì³Ë2¢,ë ÑHõ·—À׃?_gTÎÆ–k¿Ýfiy™î‘}¢ãÀgrr’¶±¬è΢!L½¾§”¦DYB”e™¿[ c¦Mø„Ú‰©S,\¸ÈÇÙ=ÆŒ¾8²,ccu•ÿõÿÀÍ>âìå˜_X`~~ž—§§©ŒP k„J¡¥ôà#¥tv l6÷ÙÙÞaié÷<àÑǬ¬®@ç¤ê«\.óúo0séwGFx ±±(—¡¬@+G€#0’YT{ÆzŸåÜ[k°m À’9çEÎá°ˆ$E†ˆ•ÊÁ'çsŒo©tjl†2ž¼¹y r¹UHþ:Bç|ijóQÏ™1DÆ ¬ñ¼W§“} >ÿ¡ªˆÃÕÄÓ9vñGáÖZv¶·ÙÞÜ|"ÿR$-Ô'&X±–rä‰Oc LæÉάЕd„YFh ÒšAô±÷m²è `|òW^~™ÕÕn|ò ½'Ÿ1†n»Í£»wYyø Š(W*ŒŒŽR­P®” òø™ƒùŠC–úê¨Ûí²ßnÓn·éõz$I‚ÍE{'½ßr¹Ìwß|“K¯½ÎòHÛ*`ßø÷£„"ŽÐfÈ4AÅ µè,ON±Ö¿w—‹#´ž .â~°©_ÈÕšDàã=”uyõƒ¶|¤õü—t œCcÑBµ>÷ÌYRëÿk=gôð¡ù|þDæhƒëŽ´ ϲ•íœ{ „¾)@Ic-ëëëì<Á³§à=Æ&&ˆ*Uvú)}©‘6CåS®0MÚí=”™&œ£\­²pé?Hb¬|þé'tŸ°K6Ð#% i’Ðk·imâ1Ÿ›8á{{–ƒRЉS§øþŸÿ9³/^á~½Îç:d×X„ôÚ¡ÐYJNRrŽ2 Aôû¨$öm˜É|ˆÃ·‰‚$vœcЙ"R)‰0aˆ(Q³•¤9ðøÅ]á¼G¹s(áµO Gë | –p^ó4›e¿Ÿ?1ÀqÇ€ÎpFx1¶}’b÷¤_ @ú&ÁÈ‘/’®¬›Ã5|H)©Œ@ÐH F‚Ì\ÎEøÊ'ÌRÂ4õšë­S=øvR(ÔÄ#cu.¼ô2”+ŒÍLsë£ØZ]%{†ü³C r  |æ³x ‹‹¼òÆèÙ9>¯Ôx 4¼JRÎW%));KUhÊΡ­EÆ1*I|ij±(“9äÜÌ0(:kóŒzÏ)i £Ñ¹yô…EôéyäÈ(ÖÒf“ôî}’»w Ñô;tÑ\(¼-hÇ„¼ÀÒëœ ž¿xúoÁ罕ÂGÒ¢®Z…©)ÿ˜‡Ñ:T*ˆ `Þ9~j2ÎÕjT«U¬µ‡ª‹aP9ØI;ùñMTDÅ©¸ßn³¶¶öD±ßÁ…9J*%;Jc… ÌG¼¡ÉÛ¬,#Ì Aæ[ÏKX(¬òÿõU€E A¥ZåôåËè‰IN]¼Ä£[·xpóÕUÒ¡¿ßçQ€ÎâÅ‹\¼r…ÚÜÍêK:`ËAlŒO[qÎï¨ AG IÍZj?ÝJúè8Fk•W}Æ“ìC¦~ÃV2…Ï–’`fý£üùŸc&Æi÷zìwº)(½ü2c?û郇ô~ñ øàCT{߃~/M€“û)rx¾·vÄ%ý[ðùc!%•Zpv{õ ¼xæfɪU’ ¤/ Îm03kÙ7†²T\ª”Ùi4èözA€ÖÚ'ËæYô7$ƒ?+ÆÍ'ýz´"úz€Hä ŸŽ¶77ŸÚŽ(¥¨ÔH„`3±Ÿ•ƒMe„i†ÎRkÐ΢ÌptÓÐ{È'_*o JJ1:>NR­!Ïžcæ7èmn±³¼ÄÆý{´ÖÖˆ»]l–ùjóK„3Š¡\u)ý$ldt”¹Ó§9½°ÀØÌ,º^g·TæS¥h!ˆ­#“Ö“äΡ£äU%1ŽQ£8jx$'¨4!ta>óU‰<ÒÊ¢Zó'jqñóŸ“\¼ÀÍ7øõç·øxk‹†1îé™3üôÍ7¹üÿüßÄss¸þgD£áE†î`YMüÃçŸæ¿'ðùv ýUZ+­5 ‹‹ü_ÿý¿3·¸HWJ{û¬nï°úùç¬7›lìï³ÓíÒìõè¤)©µˆÜŒ¼R­2V¯3>>Áä©If¦§™›cff†ññq¢ÈûÞ8|Þ¸’>îEæ) CóüQÐpáW Ãì44Ÿà—\<_ÔFFèûA@(gÑš¥ƒý® 3þÎïr›8Ìùà,‹eÐ(á¨`é•ËtfæHÇ'Y\dü{ßGôº˜½]º›Û쮯±·±NÜnc9 ‡C‡ø4‘/²a@½^güÔ)Æ&'©ŒŽÕFpå2ý0b]IZBÒEZŸïn±H+Q¢DHÊÖRŽºÖŒ Á¨€ yªJš¢ÓbüŽžçs¤Úò'baûóŸ±?9Éo~ýkþçýû,ç [|_Ýn—Ͼø‚åÍM~òæ›üè¯ÿŠž€äŸþ‰¨Ùò«NøÉZ8rÈõ¸³Eÿ©\Ÿ´î‰S‚?Íi•W¾Öëu®¾ô·ú}~óÑG,­®qwg›•v›N“rì‘edYFÇ4 îß¿?ø¿‚ `br’Åóç¹xé/¾ø"sssH¥°Ö"•BçÓ%%:ü¸¸ˆ~‘rPý×¢}¹¶Kàœa·Õb(¥ó¤£T.SaÇA&¤÷1ÎÇÉÉ2:ͧ1ÎæZr¦ƒ•)%‹2‚‘ƒ_‘Hœ%µ+ )ÛQ™8Œ£ãD§ÏR³¯3‘‹½Ž(õí1yp‚Ë7ÝsËT!qRøöYJŒ”¤B±§= ¹ÿÞú´X¾Ð”´h' …£,òªÅ(P—‚Q))KA¬ò´Ž,õÕ®(’cÜcƒá{=ÄÔüèG´GGù—wßåÿ[^bㄪ®Èû_gҚ7ücº{{$ÿöo”:=Bç°.däçJQw­ê?†‹NqÕ?ް^+.Òã&:è)Ηý ¬µcè÷ûÜ¿ŸþÇä“?¦·¿OïI€óŒGš¦l¬¯³¹±Áûï¿ÏÄÄ/^äï~—+W® ƒ€žíûl¦0ôœI.Š+€èhEôU¿ðÕJ³Œf«õD#¯‚)W*„¥{Œ¹‚×”u¡C1\²î åâà‚@ˆúW ¿IA&¯JÖWPmëè:Çžs$9H8) È =ïᬷÐ(D}¹±Þàüg†…éŒÍ]ŒJˆÁ&¼ÄXA(,e¡ü™ŒHɈ’Œ*ňR„¹ÆLX¤òtŒAô‘á‚sj5xõúss¼óÉ'üãÊ2›Oi' úí‡21>ÎÂOÊÞÆñ·)§ ™ñ×¥ËGêE,µ=æüøƒ‚ϰ½:JáTže-}C_ˆ¯’t>|ÌZ;˜è2G‰Ó£ ÷ÇTÕcH’„µµ5Þzë-~ýë_³²ºú˜Â×õœiš²±±ÁÖÖŸ|ú)‹‹‹üøÇ?æêµk$ÆÐO‚0$Ô¡V¾)¸#y8kê$‚úYùž^¯ÇÞÞžOXx _EAѶ+<_ãóÂít´É§Ð· ­ “…õ­ÊÕÁa^‘˜<ñ¥üèYJ"k);KÇú8bçH_N-|™ “gÓÉa"× yùõ³ÊF ·iW‚C#r£²( ¨*AU)jZQ j¦(F*ª¹ØO¹ƒh§Ç¾› €…s¤W¯òÅÃübi‰Õ¡V«h³e> 36wD䀧{ïúuNýä'¨þ€ÝÝ]â­-ªÖ`2ƒ5™ß3Ë+AcÌQú:Nˆ"Ÿ#]­Àä$¶>†ÁVÊØ0­ü‡a,.M(%)?h5™™ô¡iú¬¡³ùºR´YY–Ñétøàƒø»¿û;>¿}ûØ* xíÅ ¡òDL1t!Û|2¦òÉX‹É2?R=¦ìµÖ²¿·Ç'ׯsçÎ^zé%þ·ÿú_™š™¡ÛíÒË« rÆgÏkÎÿ½áJè¹* âŽc-Ýn—½½½cGÕG ‘: Zˆ—ª*·êÔ9èhëòÊáÀ]spCª¤È g'N)œ¶8«Nçë–ÐAä,ek©:AÏXbç|kæPBιÁûóŸEîôYÜüÜç;> ó`åZ‘'`HAIIÊJQÑšŠÖTÃJR ÊA€ph5´îà½ßÇν±1Ì•«l÷züë½ûÜMâAà_ŒÕjLŒP“'4:mÖÚm:C ª—–øâÑ#^¾x‰Ö•‡4³Œ´ÓÅd).Mýª‡1c|ŒOalVp›ßøE§5”˸‘˜ŸÇœ?™ŸÇœ:E::ЉB2å#Ð,n C ×üÆ065EEt»ÝAØ_ER\Óœá;ô„ +‚o¸š%IB³Ùä—¿ü%ÿ÷Ïæ…ï !RkÊùIV"ª¥­ •wÊ@Ï9vtƒ€™Ù¬µtÛm:{{t;ºí6½n—,££¯©Ûéð»÷ÞãÞ½{üÍßü ö½ï‘YË^š‘” e‘µ„Zcs]‰ÖúÐÅü4:Ä× A§Û}&¾§¨|¤Ö´Å ‘»újGÛì€òÖ£Èã±JXJéÈ9nºƒì¹ƒøGEz™ /ýÌ8Kb-d (‰sóâ–;Dú뎊©~l­…@ ¥"”‚H*JZQÖÿ5(‡å0¤„ZãœCK5hs†'LÇV=³³ô¦§¹ñà>×÷÷ˆ9PTŸŸŸçåsçítpKË ’…ó|¼¶Ê­’üüét:Üøâ .œ;GùÊ‹ìmn’mna“”D$ÒGU 0™óåø|3•+\²«U¯c1—.“ÍÏÑ/Whu»¬®¯ñð“ë,7¬îíÑè÷é¤)&Ÿþ”ÂZ¹LT«1wú4 œ9{–ññqjÕª¿+yHJ1«!žâÐø8oïþ”$ ÛÛÛüó/~Á?üýß³³³sˆ« ‚€R©ÄdµÊÌè(ã¥2å|oF™ —$dÝYfH4ŒeE ¶êcœyñ*#•2"MI‚íõØo6ØY[§¹±Nkg‡¸×ǘì±×ÕØÙáþÝßÑh4ø‹Ÿþ%2ŠØëtˆÃˆrd)C©¡ ¬t­‚žZåÐëõžè$8 >:@Jö¸<·h¯Š]#•·¾åâ`ó:ï{޶ˆ‡@spáú˜c-‹E?Mé+EœeÄÊCj©µdÎæfñ‹óI GdŠƒroØÑpPíøçó|Õ£$¡Ô„J*ox_ "­ýM ÿ5 ‚ÀÆk%rz ükUÌÅ‹4ú}Þ_[g+“ 8wú4?xåÊ–‰?ý”½~ŸìÔcgOsvf†µv›­¼2ϲŒÍ­-–ÖÖ¸07gÎÐNbD§‹” )ú~š(¼´©óRÂûoß øˆ0DÕFgÏà®^¥?=MËd^Ž1¤Z“IM¦VJVãa&Y‹.IÉhXÂ…%¨:Jã‚ÑÙ9/^âÜî.;±zÿ zΡJÈ9Çþþ>ÿöÖ[´;þúç?§42Â~¿Gl ™+‘9wì­‚žX áéSâj†!H”Âåcti}{äÁÇ íå­Ç1ÍõÑ×uôõJრ‹%ÑH¥”´"É2’Ìd©5¤Æ’Ùƒ‡Eä܈ŠöcüŸœCä¾Bþ¹¤o·¤¼âHoù>Ù4TŠPZ @GU~>Áã À“¿YÜÄñÔ)r³ßóÎŒR21>ÎëW¯RÝoߺÅ~’Ð.WˆµB#©ŽO2ZYc§ßð?N‡Ë˾ú9s†VsëþZ“¾µ ðH“sº››¿ðþ¸e¹L83MxùöÌY6“˜[7oðÞÝ»Üh6Ù5æKs/Y–ÑØÙ¡±³Ãõ?¦^¯sáâE^ºv‹—.Q#ÍRzqL„„QH“¥J)DNn«|âðMMµ:Ÿ|ò o½õÖ ¤®h+NŸ>Í¥^@‡!w?¿ÍøÆzoŸ¶µÄ: 4iùÖ4O'|МCÞ!È[›žsô¤B”*Œ”+Lšbrñ<ë·¿`éömZÛ[‡Ö >íã?Ä9Ç_ýìg”GFØR©sþ.ÈDqÊgÏ@2¯‡i–=UÙ< NÒ¼…‘Λ[yÞ'!ç‹ ‹Mî§H2Žê™ŠåP­¡Ö¤YFfŒ½Yv:ÆxnÍZŒõŸ‰uv•3XÜTWC‰'àgá.ò‹6 ¥˪֞o+&EU?$ƒPCÑÊÇ}îÎ9\açæhKÉõu¶óO†œ?{–¹zþ§ŸÒîté–Kô¢}©)ã«T)WQ¢9¸^’$amcƒÝN‡ÚÔ)ìøí4!PÂ/¾Z*ôLy©ÎÁô´Ô¿¿ t¹ÂÈéy*/Ò¯T¸ÿðïܾÍÇ›[l§ÉcN«>ÃZJ‰.¸™ü*¾à¾1 ÄÁ8/?Œ14 šÍ&7oÞdy°´< IDATaa×^{«×®12:Jœ$¨XE%¢ @k…ÒòÒú› Ù’4emm÷Þ{å¥%Ò4ôÚ/^äå×^#¬Öøèî]Þ¾õ9%&Šè—4©ÒdJúp8qPÊ ë§>YJhÑPFT‘­ftô”flf–³cãŒÍÌpïúǬ?|H<äžWÐõ>BkÍ_ýüç”ÃÝ~LšOvlîì¸À ³äøs< 1tA ²Ôk“žu _Aœóu~¤{˸ávË«z'܉•ícãçáI”èü¼ sð1Æœ¡ßÛüæâ73s‡ÉþAö›m—yìv>LP¹÷Ô”D+ÒêPõ>Ìkš\‰üTíU©Œ™c½Ñàvû¨s!áÂÙ³¸Fƒxs‡~©D/ŠˆÃXIbëaH¹¡„$ŽolmqñìYÂñ1š½>m ²ŽÈzKÀ2û‰¶µP©|½à3¬¡ŒÊ%&NÏ0zî{½×?ü¿ûûôœRJJ¥§FF˜©×C"ç}rÉï°°o ÷㘠\ÆdÝv›ö~›$‰@䜣ÓnsëæMVVV¸sço¾ù&‹.g†^’P.— ƒÀ¡©°ÖýÞ¦_IA>ÝùâÎîܽK7¿ØÃ0äüâ"ß}óMª“§Xî÷xÇìJI'Š(‡!©T˜Ü¼°­, ¤±DYBà*ÐTcD¨à/x;4•1@Ç8LX¢~ù2WGF¢Ë_ܦ?d+1 @µZþä§t¬a/É0øÜzÇAJ¢Þ-]äûÊG%î1«YG’¦¤Iúì•åeƒp67 +6«‹ÑºŒ¸qî©z¤£Ð0móŠÊ*:hŒhÌŒ1ÀsÞ5bèâ<ð¤ôk2¯¸†AHJs5ôûü屃”§ðlv¬N:6ƽ[·XÉ¥J)&'&˜#ùècº.£F$A>ügÍ€›:ú4Ýn—•õu._¸@µ^gswN–Q¶ž#ì™ —¥dZ{ŠÃ˜˜øúÛ.„aÀô§ægÙÙÚäÞç½GKlÄýǪR©Äôø8—OŸfZ”z]ØÝôÛd½¾Ÿ (& BZ^X8ÏìÔÃÎú:kËKlonÒïv d _Ü?dmmïÿà¼ñÆw!Ðì¶;„¥ˆ(ÑÖ ”ÆþžïÅX}·Õâþýû4 ¯,–’©©)¾óÆÔNMŽÒLSºR£ëut­æ× “¬[*kQYvHÛRIb"kÑW¾ŽåÐ"+ÀÇF@ê¼’w_Jêóó¼øç?@xxëÉШßå¹é¿{ï=&OMqù•—Ù‹Zƒ”O" )Jæ¦]ù]¸˜ºœ`SêòÖÙØgß“R`òé¦ÈÅ|E›%) í–x!äÑ*h„Š–Yk=2öÐïðïN6{ ÇIAŽÛ¯{Ø< ðøi^€§+àóN›beV)ÅÔä$‘´wÄ*" C2­qRúÏ3M >óìúc{g‡~’P­"ª5â,£k-å4£œ¦ˆ4%Ó( 2ƒùù¯¿í*šÓ33ÌÏL²³ºÊ»ï¿Ïï=¢‘ć.o¥#µ×.\äÂø¥æ.Ùò:; â,ó Yq·G°!áºÉ—.ŽŒ271ι3g˜»t™ÍGxxçÛëë$ù&rAî®®¬ðË_ü‚f³ÉOÿò/Q¥û½=ëM†¢o„óI¡Ùj±¹±AœGÃDQĥ˗™ž›£[­ÑŽJt¥F–Ë\yñE^;{†kÙÝi°·½MÜÜŶ÷PiŠN¼}gfìÅ1‘3è(¤.“a0ˆo.¸?öŸkb);è9™žå…?ûq¯ÇÊÝ»˜!¸˜‚ýößÿ™ù9&ÇÆyhŒ¿ƒe!³œÀÏAAȪ$È«?†Àè('ò|u¶ËDf9¤ª=–„|†Öî8µ|‘ÒyÜŠÏÑuŸç1sû2N_vÁ×7=M£Ñä^γ\ãäØ8.î{Sý@“é+B€vv0e•ÇÄÒ7¨Ý½=jÕºR¦'ô2C/Mè§12‰I ZCª ª¯5·K+É\}”ócu:›¼ÿÁ‡àþÀÃ0dnjŠW/]bFJ²»÷Ø][£›úB’!FŠÉy~2¥ÆÑw=çØwŽHHTÍÌ0?9ÁÄ™³<øìSÞ¾MgÿP´ÛjñîÛocŒá¯~ö3t°ßí¢ÂR9ÁüƒþŠ””,ËØÛß§Ùja²lÐkŸ_\Ä…!ýj•Ž”¸0`a~ž ç¸8R£&%êEI¯ÍîÚ*{Ñ[]Çlm!âô©ôû„Ö «UFÍXˆL†8ã™s¤Î‘8GßZúΑ!==Ï‹ßý3º{{l¯­jYŒ1,?zÄûo¿Ã_ÿïK”f¹±•A‰42“(©PÒ ­·ß<È$?@…Ä^~„þìðp “´XFY‚¡uÜ :æ¹'´Ç¨ã«hžTå:B²»çs惛çÄëܾ”û2ˉðÈÑétØi¶85>IX*‘”ú&£Ÿ$ÄýÆzðÑ Æì×WùHá˜,—X,—pû»|úÉu>^zHóð”Ëe.;Ç«.PÙÝ¥sû6­æ=%IÊ!™P>_:‘Oáì€ï(LŠœô8©ÍŸæJ½ÎÈøw>¹Nck ›÷µÎ9¯$þÝïÃÿô§B°'Ô²ÌùÅó]ÏL4K¯Kqø4„át‚±±1&&'I£I „d~lœ‘J™öÒüö·t»&&&8;?ÏÔÙ3Ì.,6t>"~pŸäÑ#¶º«„iŠÌ2jAÀh)øgë'U©õ"¹ØZzÖÒµ#4³.°¸¹A{w÷ÿÞôü³O?áÚ+/3{æ Ÿ˜ ¡ý‚2 -óõ%‘Vx>ªØ£*¦CgçA†á—«|œ;ˆév!V'æ~E©È k:Oš/cc{À|Õµ Wˆ¥$î÷yÐé` îPJª• •R™duT€UÊÇ·O5–R–y³2{rÊG£ÕDšJ¥Œí'ÄiJ\Šé—"t/$ /ä” ´V_øT”æ|2jS–ï|Ág÷î²ÝíÚ©V«¼¸¸È«çÏ£7·hÞ¹K«Ó£W.‘(Q›çºËÂÖ1W°ª,#Ê2dÜ'È2J@%' ui££œ{õU‚RÄç|Àöúú!j·Û|ðþûŒOLðÒëßa/Mé¦Ùs ‰'Nåsž<Æ Ä„å2áH [®ø‘j²±ôˆ›}LóÖ vö÷QZ3Z«qnv–‹gΰxú §^y™±.Ó¾u‹Ú¿þ½´„ÄiM9 ½$_Ç8XÃãü˜81†ØÊÆßÙ:ÆÂH ×^bíþVîÝ}L Ýh4øðý÷ùÛ³g©:Ç®óiA®xœóŠc©<7%¥ßïÉ5&ØãHkï9ô¤øš“¾‡èz{<â÷£v(|Ý{ƒ¿·Ä8&}ø…œº¹B{¬R!RŠýý¶7Éù:í¼os”ùŠÀ𧆯Z­ÆZ*¥E$IBE$aD…žó)Hç¯kÚ%…`: ™$ÝËÜýâ{{y+sPñ¼°¸Èë—.#ÖÖØ¾ÿ€fšÒ«”I´ò9Ó€(–óèÛ0K Ò” Ké¤)²×§d2jF•̉TâYµÊé+WqÖ‘¾÷.­­­CÓ°ÝV‹~÷;fç盚¦—sLÏ(Ïpj­¿ð…@‡¡_Èùƒv»ÍþÞ>§ggcXÚXçã·ßfùó›ô÷÷Äl£Õ¢¹»Ë­˜çÂÜ—ΞeúìY*W¯lmCž·¤¤"ÐÎõ#Ånó½¯Y罡ëÑì,‹W®°³¾FïH€Ÿ1†»_|Ask‹™SSlXËžƒÀ:Bë<%enæå—6‹õq¤êq@ Õs¶]Ç ÃíÕ7½>ü§báâú}Äò —Î/P£ô”¢T¯“mo‘î¶°&CJ…ÆKÂÄÆ%SDæØhŸ~¿G5*!£ý$$ŽJÄQD†da:ðm—ùê•‚P)棨Ûfýî=m¬ÑŽAÀâ¹s¼vùÄÎ[KË4Œ¥W®(5(•µhk‰²,Ï^Š}þRš¦>ÌLõº”aTJƃ‹WÛÇ€ÇHœCT*œ½r…~·Ã­> ÝjbçWWVøìúu~ô×M7Mý¾Ê3Ä»a’ô9¶¸]Þ"Tk5Æ&&P‘& FƒwÞy›+½.ÁÈ[; nöoÝ¢wÌΓsŽ~¿Ïòú:ëÛÛ<ØÜäoþâ/PÓSP.a¥·ƒpB ”@*9¦))s›‹âRk<`(5°ͪŠó—/sÿæ V†&ˆÅÑjµ¸wû6/OÏû É"çõE‘µˆœóKž2O蔹¸°PØ9ÐAHT*Z8|VÑT‘È) ‹Tñ8ð|kk7LÍZJ—ÒŒ™‘ûÕ*íÑQúIJº± ÍÝ\‘œøUkˆöÅ)ú •OÁûtº]¢J ©5i‘„1I’¦¨|¤‚(üz*Ÿr ™TŠd{›Õ¥G´ÚíA»UŒ’_»z• ßcse…mcéDeRísäž,ñI‡å8¡÷)Ç}Êñø´³ÕëQ¶–z0^Š#äÌ:R±óÆišrïÞ=^ýþ÷©…$BPÊ@)·úŒ¬#þu ’3‡½›€¨ùu˜¯z¦wÜ· sÒ¡¬e¬½O©ß£ÜlQª4Ù­lÓ.—H¤ò+,™Á Ðø W>îdë“n·K§Ûedl¥Ò $ C’ " ² ÀÚÛËîëá|F‚€rš°»ºÂæÖÉMµZå•+W‹Jl—¤d$ +—ýj1|¬%q^ݳŽÄXƦf¸píÍí-¶VVµ_;ÛÛܾu‹3RÑÞÝ%ËÌ3ÝtÝPÛà†*›§õfJÔ꣜;žÛŸß¢Ýnc²Œ^·Kï˜lî§Uœããã¼zå f§ÁÖÚ:í ÄD= oó­ìœ”Gd±V’ÿ}­&„“Ò •A(H3åÌ·>úð1ðqαµ¹I§µËøÔ4w„ +%]!é8GÙAHœÏ“20Ø·rÃì7P®T¨Žø:=Ž¿³õÜsõÿ„Ç€'˃$!“‘†6ó\!8ßJg)å,£'h“=ñãM’„½ý}ÎjM¤. Hƒ€$ô@d‚¡¶+ýz*ŸŠV¨n—ÆÚûÝÎàõ)¥˜Ÿ›ãÂÙsì=zÄz§Ç^X"Íׄ³èÔÇ«V’„j3ÒïQë÷¨öûT’„ršPÊ ¡Í¨¦2ˈ¤ FŒ”ÊØvqŸxáiÍôÔo¼ô2Óõ:k«ëlôbZ•ª_Á¨”é+IA¤xQ¡œ,OO±‰üýI‰0VcüÔ)V—–s̲ŒÆöÓÎRÐ’žþÄ’| 4s–@x /|”~Fé TŠ­£O¶?™²°H‘±ß˜ ÁØés/ä,GE+\¾Éž©|å£òi—Ö_½ò±Îû¨´›Mv[-Ò¡hzzšs§O³µ¼ÂÐBO„Zë'X9ªVÓ„Z’0=Vçüw¾CyrÙî ··‘[›ˆÍ-ØÞ¢Ôh1º×¢Fu0ÆÎ9cß¶9Ï‘eô”bæôi.]¢¹µuˆ|ît:ôú}¯yk'r“³‚á,ÀåIwîÁг·M¨TkÌž;ÇwpDÕ*7?úˆ­µU’8~ „†bAÀh­Æù³g9ú J)îî´XM2²R™ž±¤Ja£D(AîE\ ºC;X"Á1=ÂXÆço¤ZeâÔZëÇ>c ÍV•"g‰¥ôF[BÐG‰udJ`§¿{–Í­]´¦>6FµZ(ÓŸ>Î:Ê…IûI…øQ3>IAmò¥×ât³'‡¿"k¼}lá…nŒ_•yÖ‡s(á,BeÉ?wôû}Œ±„ÒŸc™.€Ga´ÊGí‚à«U>Î9¬ñ{q{ŸÞPÛ"„àܹsHëý„½ DªsÙ,E9ˆŒ¡’¦Tã„Ñ4¡®T¥ÄŽspj’Êü‘~jÒï3Ýlò®¯qáêJ¥™±ù"ž@ Â!%H+ýÒ¥ô"*a ª>ÆÂ¥Ë<¼s‡îþ¾Gqž-Zø8rÏ#]ÄÁX]ÉÇÐç`×IHAmt”és ˆR™ÉÓ§Y}ð€õ¥G´[-’8!ÍR\–Q C¦&&851A}´Ž #šiÆÃ~Ÿá°aH oÄnÁ#FÈ!{ÏܵÑZÜqÆïÎ!•Dáy¢@)Bår‰‘Ñ‘ÇÌâ‹ÏmoŒ!À‘*E, –’XHb ¹ý†È=€s]‡Ü%õú(µzæÎÎϵ$IÎQ¶æH´Âî?T$ÎÛþú°tŽ:G¦iŠÉ#|, ¶âðˆœîP ÎøïCJI€7ÏwÅ€„"=Cø­ô¡àÁ'5†PK0š,Pþ¡ŒÖ8å$PJ~eð1&Å%ФÓ9TõAÀüÜý$e A/ üÒa–¢2Aè %c¨d)µ4¥’¤”’˜öÒ¿XZf G½ZåT½ÎT½Î©z¨ReñÚ5&§¦°Ö²±±N¥Z¥V« t,ï‹"òtƒÂ9M(Åìü­fƒ½f“3A@Uô5 ý„¦ƒž*7 ”’Šƒçò1>®d? åüœ#Ð…•JõDðé÷z` A*%©$ÂùvKȼåÞ~cP „8ìñ76>ÎØøËâþ9,Mùÿ©{³çº®ôìï·†½÷™1ƒIp”D²5K=ȱ¿ô;qÊ•r}U©Jnrë¿)‚o’‹NÅÏîvÚNêA–,µFŠó3pæ=¬µr±Ö>ç@P¢ÛíS…EаÏÞïzÞç}ÞçqÖ>…|Æô³øO9[k lll°³³ãUà¥q˜ÖA”9Þp ‡ÞúCiÒ4Ÿ¿D\ÍÉûB†µ%žQF0;‡ãkéõY>P;ï]ý¬W𦦠 ©1ʛޥƜ’ß¾øäyqù`¸¯e¨V«LµZ †(¢P •kýÆ:ª¦ –ÔŠœz‘S±†4ÍØÙõ‹oRJ kíU»I‚®×Y\Zâêµk¬œ;Ç0ËQ4v*”cCy-=Éê¬ÁY‰›žæüå—¸õÅþÁù&¼`´sö¼’6YZe"Â$ÊQÓš´^'Uš¬^§ÈsvÖ#î´ÛÜowéÆ1±T ´¦+YÈ€òÒøtÎ,Gg2Ï<ú›póÆ ”_mpÖâŒÅ)‰³ÞyŸ@.ÈòûŒ#MÜB".ð0…ä2!‡G>f¢õsBâÄDfI¸üSSÓÌ/.ø%Ôc2£²,kh:ƒ¤œ6†“Z¿Æ"&¸¹ÿ`#÷RßétøàƒøíoKQÔj5jµÕj•j­F­Z%©T¨$ ³ss(­™™ñrÀAø>îSy©’*°ÖØÂÓûö)˃ ‰´î`Öú‘È§È /U £5&|¶%ߣ^ò±!!ÁùHÑ\Ÿ8ŠÙfdQŒ1Æ›?å9G$ý5ã”Uc¨ -‹ q&D¹Œøímžll ¥dii™ÌZâÁ€J’IM¤­Ô£%%‰X \µÊò™3,¯¬°³±qb½§Z.!N\xDùPOXt*YF¢*RP—2…†Ö/{ntû<ÜÜäTµJ#è%”RT‚ ·tŽØêÖÑžàËâ›Ä4¤$ ¢Ma­OÿkÂièB4L@‹ûQ‰C*}±ã$„X©¼ þCHo®FívâßweÖ ªõ§–—©ÖëôŽ0“wÎyü4e.$VPÊ£ÄL 0æ~þ£)~Œ1ÞçÑ#nݾM¿×›†Mxúh¥¨T*üõ_ÿ5³óót†Y†…¦áˆ„ ˜qŒNyU‚€tåD¸†n3i‰t’+™eYž¡+„4%ƒë¦Æ–¨G*òÅ-–¬‰J)¤òÌyGØ"p/Ò_ÄY*…%)üh<±]©âœ×‡Ýæò,£ßïûÞX ö†)™ƒ$2Tˆ‰ÄB¢”÷ÉÕZµ†Ù¹9Î_~‰{7o²»¹ù “Ä2qtÛ%ƒ'!˜“ %)Œ ’BúË AÓY¢aÙÞ£–f´* &‰©¶¦iµZLµš4ë5QDM)ÏùXË®³t´æíÅšõº_sP -Ò-,…u äÈKè eƒ=q+Ž÷{ËÔÀ/Ÿ"´Z…ð?vÍ-çöËKcø¥åe¦gg->ý^~§Ë|­NTê¬F‡'ÊÝ>þGLX¼þñ£žòþ.Mõ‹¢ØG$|5ѺL7ˤ)V)2¥(å#*Gì¥ôD;V"ÝIÎl"¼oÇßû‰oKTN„¥Â*ßrY1*QR»y«ÉÒE³Å^—Á Š^’$¥èiÍ–RÔ˜‡G:Öyß"!PB"…×+X¡h´šœ»x‘¯?ýŒÇG<\Ï$ŸÃ{lß\Î –$„Õ ç§‰”¼qîWççØ^}Lo}ÛŸ}ÊÝõ5·Û´‡CÒ¢ðÞÇÖîãÙÊ×ÚãÇ#=R~_'‰cªµÓÓÓœ>}šóçÏsñâE–––H’ĉQÏ/QDš¦ôºÝC¥J)¦¦¦È¥¢«ã1c!æþõ“¹àïSI’pzå,­©)vŽh‡‹¢ Ón“KưC@\Rbdhñ¯G߇Ø?øcŒÍž|p‹¢ ÝnÓétž‰|Êž”ì IOHŠz“´Ñ '$ý¢ëÇwÔ IDAT@å¢9òn_4óQ÷jy{Û!®Ñàg°Äç©ô-—+ /ùH‰Ž"ú‘&š™¥ÑhÑn·G•ü믿æÝ·ßæµ™iž¤>±ôÖ-èpèV“ê[oòhu•Oûýc!^É54§¦°QÄNœ°¥#²çTH£H¤_vŒñé² dC‘Ä §Ïžeye…'ÉÓô¹[Í}óËç)@eñ!Dõ*ÞhAi´V$ÎòëOÏï~ñK†›ìöô _lÜIOðp’ÚÀ‘ ú}vwwY{ò„Û·nñá‡2;3Ù³gy饗¸zõ‹‹ ^)Ò†ƒíݽCsÔ•RÌÌÌÒWš=©žn=Å!Çêäh¿$<ƒê6©V9á"ó§NY|Œ1lnl`‡.(Ážõ7·™„÷£ÖË£ Z‡?öÖ˽Y†Ã!;{{ Ÿ!¸hµZ¨$¡El!FfjŠto¾ƒA:$Ư×Äà‡.ÖŽZRq\!ž,PB€ ×Ô¬øŒ4ÞPJ?í”Ò·]B|{y)%m'hÎÎ1{ê›k¤Y†s޵µ5>þäþä½?áõN—/=ÆY+Ö“XIBë­7)¦¦øàÃáñ3TÆBÍ&ó§NÑÓ›q•½ÐKj%I”¦¦4™>f&T[)²œp$qÄÌÜ+.pû«¯ØI7x®ò oäƒ) ãUi) VWWùÉOÊoó6××=üÚ…òß( Ì4MÙÝÙáÞýû|üñÇÌÏÏóÒË/óÎÛosñÒ% cØÜÚb{kóPøES3ÓlE;J✠!ä(9£D8ˆÃ®¤ /µbna•‹¹û6é!yõÆ6×Öhomq¾5ÍÍî ÛžÐô“6 ‘¿Ù­øCøkéloo“¦Ù3ï¥ÙÙYˆ6ã„¡Èö¦È[Sô‹œTŠ‘ÇÕ(Ì0Në&㥕öí¬;>jaß½*…דJ‰•¾}O^@ñQJцͳgÎÒ¸{›t{{Ôz}øÑG¬œ=Ë÷/œÃå9«Ý®÷à‚hºÅÜ¥·¨¿ö*ÿìgür{û™B&¥sóóL-.ò$©ð¸RÁ ‰PQŠT+2%ýMID07w-P©V9{þ‰¤ü6\Ù$ BÐ øüóÏùû¿ÿ{>ûôSºÝî¿)GQþÛEžÓi·é´Û<|ø>ü3gÎ0·°ÀöÎïßêÚ”ÞÓÕFƒŽŽâ'˜¾Ù£µ4óWÌyƒ€é…ġΡ¤¤Ñlpáòe>ûøcÖ><ô{‡l®¯sa~‘Ûå‘JP(I!•ÿ¬|ñ1å$gô ýñ¢I³1†v»ÍæÆ&Yv<¢ˆÙùyò8f»RepøhœÝépàCòSØ¢À…vô¹õme´¸s'&žKí•—AŒ›)óÚÅ Èj—B9ǽ(æ½³gY>»B¿×cÔ–[[[üäÿ‘¿þ«¿â¿{íON-ÒßÚ¦‘ç,O5iMMñûþgþ¯Û·Y?ê©×ëœ9ž¢Þàn­ÁFSªr)(”wE´!ÔßáþïªÒR@5IXX<ÅÒÙ³<¼sç9ÏŽ¥¡-IÞn¯Ço~ýk~üãsûβg´eë9;5ÅB«E3Š18ÚÖÒ×KËKÃAŸöî.{[[tööŽÔ5•…(KS¶67ÙÞÚBi=Šû9ì57?H*ìêç,Bx¿èÑï& Ý}Q*‰2¡üƒâ&F½ÒZ*• §Ïžeqi‰ÍÕÕC¹¦,Ëx²ºÊ•W_å´„U)(„ò k­0RûƒGz.È–mƒ(¹º?NÎdžk½½³ÃÆÆ:Å3ž…F£Akv–^±“Tè9r`ë ŠZÝK²ilæù–ÉŒµRÃs¢;> wœ#/ò“v å ¢ü €¢ˆ¢óÀ$Ë­i.\¸Äìú××GÌ÷ƒø?ô#Þ{÷»¼ñꫜ¾|i íÇyÿïþŽ÷oÞàÉ`ðÌJkÍ©åeN¿ÀýjëIB&¼ Îá¡bߺ%SïIg!G¤SÓ-Î_ºÄן}~"ųs*ÏvÑï÷yÿW¿âG?ú<8òaŸ<ñNŸ:Å•sç™I\@·ßçN¯Çõ¢`-®ð—¯½ÁL£Á”)8gÈÁ¬Ý¦½¹Á“{÷X{ðà)søƒ'±=F€YBþL)6•& †Bâó³Tˆ¡–a0ŽÔ™œŽU´¥ÑÙôÌ ç/]æît'ìOÊWžç¬=~LÚë±R­ðÙ0£P^¤™KM®…ÒÒBJŽï‡ÉÅÌ?¦ä&PÝÆúƉPïôô4õ™Ç {qÂÀ9gq•*6I¼üB+Ï)ÊqüÔ¡D䳈J)}̲À»džÔtoü(>¼äÞ-·†/•¢µ´ÌÊå—é÷úìtÚ£^ýÉ“'üßÿ_ùÇþ'šµÂ:ݽ,=tbs·433Ã¥«Wi7š|Po°!5µòFe_3>Åd$íXL¥¥$rŽj¥Êââ"­é)6¯žÈÉpò­|Ãûx0ðÑ¿þ+?þñ¹ÿþ3§µZ7_}•+gÎÐßÜæÁãu¶Šœ Á°®c6•æ±Pô¤B ‰P²Ú ™§qþW^ƒ«í=vî?àþõ¯Ø|ôh_NûIy¾¹ùy†Z³‹HN ]($®,Bá=à ŠiQzv‡D‰_;©5›¬\¼ÈôÜÝvû©¡Œç}ôàW®\eq»ÍºTäZ‘G^`—KI®$E˜tœxýñ½ÆXÚí6îߣwÄÁ0yýOB7[¬VjìDÖ:TQà‚U©SãçHqàIï])±IL. 0'×û?àÛ!̤µhcé[Çg:áís¸2òõõëlwÛ£›¦( :Ý.gúæ<}ÒNOOóî;ïÂüÿX©sGhêÖOe4x.Ç "<²ñã|?Æ-Ȧ/ÒG‚(åµJ¸7/1–þž¬ÏϲŒÛ·oó“øîܽûÌÂÓjµøOï½Çéf‹»÷pg0¤EØ$Yá•¥Në¡îöM‡Î‘ I=_¡¹pŠ7^övؼ}›‡×¿bwcã™¶%ú𙥫4[Â_;¬¥”B …ðù]2L7å„VÄíG?åȽ’T8³r–—®^ck}ýP¥Á`À×ׯóúw^å\¢YHr­É”&ÓŠL{T(…Vb”ÈàN´ü‡ç{¬säYÊúú:÷îÝ{&Ðl69{éýjÕJ…žÔ$˜rŠäë…+?Ê$7Fn2VèWC)l‘9Gêž#Ûw´æ2F?“Oõ‹I¯p!q¢°¤Æp](^^>Í5ç¸sç6ëí= c¾Ñ¿­”bvv–|÷{LŸ9Ãÿž[Öµ¦i-‘3Þ¸¨8H„ ‘ ‰(Œ³=!FˆÅYï«£…_;8qïëšàCü ïârngw—÷ýk¾¾qã™;fµZþÙŸ±Ülñù½Üv’a½ ±4ÒZœK}:¡-·æ}æ’”ÙÑ[ÞwíHÏ/ÒXXä­7Þ¤÷à>?ÿŒ‡t»GêŸõf‹ÕÈoÛW&÷ÁÀ‹HïÕê”åx=¸Z•c\Q¶j¡ýšž›ãòµ«ÜüêËC ì1l¬¯³±¾ÎùF/Û= ©ÈtD¦#ríÛ¯\J"1æ;ÊöÆ2}üwå{¬e0òàÁƒÑ&ûq¯¹¹9ΜåQµÆ©)„ 2"øËÅb/*”ÁwYX73Äóñ–R‚Ö!ëí9Ú.ÁHò1ª|/ ùذ­Ã¥ŒA›ÂOµ Ëb½Å+/3·¾ÆÃ-:ƒÏ^?A’£RŠJ’pvy™ï¾û.y”ðd†/ã„™Üøì®BQuŽš€šÔ¤ jçˆ/>:Àùò÷¿'NüF8çpÖ…í^ûܹ]“Äî`8äæÍ›|ñùçôž•R¼ýÖ[\X>ÍÇ·îðETaXõŠbé,:+Y†²•e( ê, áíFËâ[>f6¤GçF¦aJ•äÊU^½z·¾Î'?ûyrûö¡SÀ©©)TµÂžŽÂ>ÕxWH1~B’nßc4›|ÏehÕ¢h»rþ¯\asmí)~Ê9G§Óáú×_óŸøC–7¶¹§4i>tä§žZ)†bŸÁ ú Åtgg‡¯¾ü’Ýgs~++DSS<¨ÖØ•2ì ‚¢T4¨"|8ƒ²f"JZ<ª6IÈ!³†“KrÇΙ~Ö0ñU£oÛv•;"!0N[_â<#JSÒAŸMÅ3†ô~ªÑétøòË/Y]]%?fª!„`ii‰ï½ó·ï=àã¤J_iœYX¢ IÙ°Z1~žë{U «5CãWxNú·Ý3ž­‚|¢`aªœ!2†(/¨ä™ÏÜ ÈÒ”"/¨F’—¦¦@*rI°×t#`ijõÕjðõ ã^ÑaPM(òœÈˆÂ÷ZÔjCZ±¦L)IK8šRRÇQCA,D@>aƒ·œ€C%)ä(ÓûDí’ñpzÿZ6—Kfz•ê ‡#MSÖ×Ö¹w÷.½gôöqóƒïŸAoÀï…b¯Yà 2é†èÌ’ä9µtˆíõIú]t3-`Në îŸx%çaœÃ8BѱäÁI 0­ýÒiwsóHåw½^Ç*Å®ÒÞp¾D.Œ—fË_K9ºÖXÿ“ùíBÊÑɬð¼]Eœ¿t™Ë×®±»µE¿ÓyêýØØØà·¿ûÿã_þ¬llò°íQÏ0ŽÉ´&ÕšX)¬ ȧT};ÊþPµ–Ým>ùä6Öן‰zÎ_¾ÌÌÊ 7šÜ FqÚ9b±³ˆ,E§)Q–9Ú>Ćökâ^?iáQŒÕš~¿ÏÀœùŒæ1á+Jq`d£¿yñ)EQ.p.Ú”qhc‰MA’åÔ²”Ê0% È±}Ç@xA˜ ÄDIŒÔF:ÚEÎýá.}Ý%KŠ$‘ *Ë)œEe)Õé­©&3E”L㘒‚&ކT…÷ɉhåa© ðt4‚éóÜ‚Æ"çPX r¢ÐL"Ayä›áŒ%ËsÖ7ÖÙØØx¦–cii‰3gÎrgs›û©ÒHkPiJäI^PKSZý>ôºTº]t­ÂŒRÌ'1±ž£š@¼6dµ› |ÍdÖ9‡’ù8bØéÐÞÚÅÚn›aìÑÏ  XGÁOÆ‹MþOŠíþÐègRX8¹qó&_ýõ±ÜŸ‚Ù¹9®¼ö:{Íד*='¼¹;–Øú‘QiJ’¥ÄyNT_|œs>%úyÆÏí‚ÆÇ%1FJúiÊÀœ¼éÌâÅ#c 6XbDåž–³ÄÆ’˜‚jæÃÿâᙥ~qA¦$™RäyFo ‚É´ÿl£Þ†Ñ:d^x3j! /Ã>Õ¹9¦Ó9faJÁ´L AKIêRR•’DJ,BTpﺰœ"•|®ÂSnUGÁxÛˆqáí[ 9ÎqOOmЩlnn±×në]z`;!¸Åô’ªWìfHŠ‚Z–Ò ™ô¡ß§2èIÁL1[©—¤ºðênC“¶5”Kîip8œŠ"V»=ÒcPYÇX)è)9"’åhÂ%êñŨlwÅ!#ßIOb¯öHã[¯Ø)’$fåÒe^}çv·¶ÙÛzzçk{{›ßüîwüÏÿå¿ðÒL‹[݃8öQD¢µwÑ ]c­›âµHÿž¯¢(X__ç·¿ù_>æ•$ ç/]búÌ~ßhñP(„sDÎ'¶T¬¥b,r˜¢‡âÔmê"G[‹ “aá܉îûzW [¯“+M¯0äÖ<'ºGŽ‚¿ò) ok‘sذ¥®;æ9•4E¥C°Þ„:’L+R‘iMé0¥È)ro‰Zè!V)l€ëÒYd^ û=êgÏ0]äÌãhIh É”’4¤ ®%‰¥$*…nÁýMø'žw-=Àã,˼Òb¹¡09!'¸ OX§iJ¯ß#ÏŸ-&\X}Ä’HMkzš—_ƒwîòU·óT+X·nÝâú×_óÎK—i·»ô:úqÌ0 \E~ß A1Q|ã»?ú™\¥èv»|òûßóå_ËýI)™_Xàå×^g·5Å—qLÏ…vËùÿê|Æ–Ðiêy×<#2fÔv‰ob+«5¶Z!“0°'/=ûšÃh¦oU|Ê(“R[S„›L9|E6†jQPË3t–!rI\ H•$U¾/Ï”/@¹ÒäJa”Æ„>°+¤Eå9zУÑï3k-óJÒPŠf¤iDu­©*EEk"Uz:ËCEVÂMì¸A+òœØZtØr!ÿÅM$qŽsÜåDõ÷_Ìœ‹Â/bóªT*Ô› Îщ«£íȆëše4ÒŒæ0e*MÉÒŒ$ËQÆ'^´jU_|G#X1Zþ´6-Ga ëÃT8géïíQ³[$¥ß•s¡M’"ŒÔq(!ÇãuÜù =v4Ü/—Leàb ±ÖÌ/-ñÚw¿ËÆ“Ç<¹wï©¿···Ç/ߟ¥S§x÷¥‹üK§C¿ß£'DqLk¬Ö¹1¨Ð‚‹ {Û?D*Qž ÛëwïÞå7¿ù [aò(Üh4¸öÆ›ÔΜáÃæT@=–Ø:*ÎRu–Š)¨:‹L‡èÁ€$÷ƒ]”³ó™h»Nø³:­±ÕC½¢8‘ ø ñãþ-çrC:Qå?"DÖÃÁjaHò•å8c(€LàÛ/©É”"ÓŠ\*/‹W28Ó…ÓÉz4E–£}êYʬÌEõ8¦EÔ"M5ŠI´&ÖšH«‘ñ>¯âÂÎÃOš¦ÔC9;B9¾ð”¯£&=êp#g¸“ùT3”Þ|%Q¹PLLN-ÏidÍ<¥žgôòŒ¸ÈQÖPKbjIâ[ )žJŸ(…mÖŒúñDâp˜Òë´]ópΡÃ2¯™P“Äœì÷ .£ƒ­ïAcªÉ?“Â#|¬#‚jµÂ¹—_æê›o±³¹ùÔÎZËÇùù/É_þù_ð—/q½×¥ÛïÇy”P(Edeñq¡T(xãðߺcÈòœÇkküüç?çÖ͛ǚ†ÅqÌåW^áâk¯ò`z–ÏtÌÀZ´s$ÎQÅQs–:þY“Ã!:Ký´(ÐÖs±NÜr=5f¯VÝ,ã¤Ö{#/%3©ñ™x< óíD†yžãœEc°|bŸÊO=ÉR5ÖO«òkÃxW2ém8½^R”¦Pb¬GP΢­éѽ>¢`Zkf’˜jS‰"’("‰4qN|uˆIúXÖãŽ%Ãå|² ÚLÏ8:¹œ¦¸éÎÙQ¾—@‚ôñ4QGsðrÆ›á ¥ByŒ5$…¥ZT‹œZQP/ *Æ ­E:üµˆãÐr^|JäjCû¥ ©ÓaJ¿Û=¶øôû}jR0ca]BÙy Ã$¢ÓòÄçdY T»ÇÒÛ¤4¦Z¼òæ[ܾñ5nÜxêͲŒÏ¿ø‚¹™ÞûÞ÷°iƃ,£7% F{Ç¿aQxW¤Ç¨×Ç÷"¥ü7)@“ˆ§( ¶·¶xÿW¿âƒ>ðÙgÇ ÌSKK¼öîwéÎ-òqµÎzðZJê©9GGÝ9kéh8$¶ÎÚM@>%ÉüÈÅùS›T9cŸ ù8!¼È‹²Sp/®íÊóœ<ÏI¢íÆœ‡¢>¾@ì â,‰±(cqÆ`qr¹ðê¥#ݘ¨xõ«¶Î;³ úTf1U©ëhñiV5‘^ñìÉù "kÑù87vÊ›o‡›Mx¬$Jkê•j±»{¤àr8Òïõ˜[X¤n|®’/>ÎOñ‰!›>¶ã›L+å¯ÅAÔwÈ´Å–1;ZcœCHÁp08ÖbdccƒÄ.›Œ §ƒJ™¾=b|ANp©Å„”A)í±’T¬¢Å,œ^fåêU:[[ìmlìÛÅsÎÑívùí¿ü õz7¿ó* ðÐ:òNÇï}ƒ¼k´·ƒÈ߇xa…§¼ÎEQ°³³Ão~û[~ù‹_°µ¹yäû_îϽõƒœ]áƒf‹»~6Oì|á©;_tΣ!™¦è,ó cÐÖ¾ÇØK<êqµ&‰èôzt‹œçóü –>þVÅ'Í2ò,'®ûP1¤'‡ÏQÂÃÄÈù8?ýñ¨§Z"F¡e“¸Dâ‰ì=cPyFU@#‰iT*ãMëQÑ9Øj•ŸÝ!#Þ“‚çý~ŸÈYb7–Ï—-O‰‚lõR.¬¿>Jû½¨©™6ÖׄÜÃáõÇ«¼|íçÛ}VE(@Ö_ÃØZ"kˆŒÿµ¶þÏøè éÓCä¸|(¤”˜}^Z_…9V…þèÑ#v×Öx·ÑâUŒ54åá:.pÕ‘™ÕßW9Š/S>")‰¥¡ª}®YÜœbáô\@»×ÝW€¬µlmmñO?ÿ9yšòö믓Ä·lÁúΙôò|¤)*}³“£èmyX¢ë·@;%¹œeÛ;;¼ÿ«_ñÓŸþ”û¹Ï§”bvnŽw~ð¯\åë©i>—Š4û³4… )¡n@ çÕîYJ,•'›­Ùgqròºá']®^'‹bÚé6ƒo°"%ä˜XÝ×lkåô¡@à¤Ë‘iJši¶¡Ò 9öìi@¼0/âp‚ ç9SjO'8qŠûTÌ}¬5Õ8¦š$û Î(ßèÂó"zõn§ƒ¶þa߇tÄÄg&Š’À“ÒMHifççY\ZâþÝ» Ž(>Î9îܹûïÄ ·Ò‚ KÖóN:ìš)J!¡GRÊ‘”à8ô'ù,¥òßgðÆpÎ Ï·¶¶øè·¿å¿?{–¿¨TùHGV[[šÈ[Ÿbá„ÿ=!±Øà(ù,ôãßC…G¼±TT”#×¹)Z³<7GÅ9Öû½§ ÐÆÆÿôË_Òëõyï»ïòŸþóQ7oRÔët³|ü F‘ß=RzT,ÞCÛ°£®¥;€Â&Û¬,Ëx²¶Æ/~þs~öOÿÄúÚÚ‘2 ­53ss¼ýýïsö7¸33χ:f×ú÷:qŽj@=MMM'i”üh–¡³œHƒvá‘ωz^Ùlš †R²›åGFZÝDãaGV{„sžç¤iÊŒ”$þß—"Œ˜ÅÈûU2RƒŸŒ‰1 +ÉO‡£ü>K!&”¨†›FKIEÄ;™D:s¨^ÔËZK·ÝöQ“m–Øzܾñ{`û'vjTÑœšæì¹sÜøòË#Ó!ÖÖÖøýÇÿÊÿü/x¯ØãÃÉIõdõ;ÐÖ87FZå{=‚†) eËuÌÿ_Ÿ~ò ‹gÏòúŸþó&÷¤¢ZïÌ oìæ¼» ÑáÞ œÊ³~“Ši+-‘VÄÖP‘i ;¬ÁòÂfݲÙïïO]pŽÝÝ]~ý/0ôyã­·ùoßy›=ç (è•ÅÕYœ‹Báñ@¢|Êkvð~r'ð|*¹´¢(èv»Ü¾s‡_üâ|ðÁl“G¯µfn~×ß}—•W_cufž£˜mÊ¢ð Ô±¡èšRÐx¥È3TQ ¥7ít=WSÞ¿Q„m4é[Ãnšž¸øŒrå=”ôé%’VûçÉ?%üf3aiÑFï7*Ede6Šï˜l_Fî©ÖHI¸:$2DQtè r’Âó¼Å© ­!+'p"Ná­©}ñ”uÁ>õ+©¨5¬\¸Èò¹slooic‘¦)Ÿ|ò çϟ翹tm w;QPèúzd–ÎÄñD‹=0N~Š÷Á›ÌgyÎîÞ_õÛ[[Ç>dÎ9vvvøÿ~òÒ,çÕgfXwðÄ:ò(òÚ-gÉ•#Gy}”Ö£)ØQ¿É©ä(`A8©GÖÞ£ÛîòæåKpj‰/66X?Ђ•˧|ü1«ëë\{ý ®¼ö32k)òŒÕ8ë0…ÁDšØXl¤QÆ<Õ¾?ëÞšÔòÖÖ}ô¿þõ¯¹}ûö‘¦ðB¢(âÔò2¯¾õ6ËW¯òdf–O¢„ S•@.·€Ž))™’¦Ô…#YTQx*"Ð#¾çyîy!pÕ*¦^£›ele'/-Ç­ IDAT>¥¼ž;/TçS¶"ýÁ… îüÞ“ ~­.¤XNZ7 Æ*V5¡zä"Ý1 N—æ`¡Å:éñ¢ˆÃ,Ë0yNÍáF}Šç( ¶åؾALü„Öè8NXX^â¥k×xüèë ÷¶¶øÙÏ~Æ_E1²²ÂiàQ–"z]ŒÞõ™Ü™è. “˜±pï€n²-ÈóÜ;èmnññ'óË_þ’õõõgÆ YkY_[ãgÿõÿáÞí[\}ëmÎ^ºH3®Ði4©–¤áЈ…¦Ó8)lj©Ç¡Ÿ’‚ïË9ÐyNöx•zsþìYÔÒŸ¯­ñ¤Ûyª nß¹ÃæÖ=ä{ï½Çù ¨Tk8k(rÇÐXk°‘ÃZ?Š×Jùxâ‰ìà½Vœ¢(FŸÁþöí;|üñ¿òé§Ÿ²{Ì`AáýÄÏãµ·ß¡uþ«33|UyôX15ëOKÀ´”L h)IS j†R"L1Z$•lj„ln6Éë öº]öBüI^Y–)IlÂN£; r‡÷Ü+iš"q4VrvÂ:ql§¥©‚xJ rüÜILæbB¿ó‡‡M¶épÈTž£ ènŒØÊ¶¶Œ—=ˆz„ðÆN‘V4§§¹|å ëkk z=övv=¬µûè#º»»L×jDKKdY…’ œ¢j­×^•K¾Úoä }ôX{’k)¹ç±Öžã* âaJc}c,gÏ­ ——QOÚmî][kÙÛÛãóO>aíñc^¹z•—¯\ayy™V«…Œœ0d.õÖ«¥L#¬æÈ¯vØÔ°( ¯ZïõØÜÜäÎ;ܸqƒ{÷î±±¹yì¾–”’f«Å¥—_æÊo/Ÿæ^³ÅW*fݬ<)PÃѦL‡ÏSBЂ†RTœ§<„1~ÿÏí×ö<7Ò×;=MÇlöûtŸc§«Ûí Á”ÉÑH„3“ˆ’OÌG>â¤ÖÒëõ0EA? ŒFÄ«8 7Ο˜x°õ’NÅo^tÜ7™¶{'ÆN—é"§RŽÚ'ÐO‰t<ù|ˆÍÆ„‚7Iæ—–¹öö;´Ûm®òÉS›Û“_÷Þ½{t:Þxôˆ«/½ÂâÂ<•Åĵ+ˆímìö6ot;Ì% ‘Ö Ó4¬6¸}…§( ú!Ãkuu•›7orãÆ V?¦ßë»ov\ŠãÄì I_ ¢ Yš¤ðæf‘(+PÒbmص;‚ûyŠ|ëZ´õv"á³¹q–ų+¼¹|š$Šx¸·ûTm‰\?zÄÖæ&_~ö9KËËœ>{†¥¥%æææhÔëT«U¯‘R¥Æˆ§äÑŒ1azÕétØÙÞfccƒ'kka}ÕÕUž;޹~ý+Ï«„Â3 èu»´;z½iš’¦éS#)%³ss\½v3—.áju¶Ûm®ø!oߢ8Äß§Œ3Z:{–n³¡c†x7É>–“ q#î§ÓÂQ ï1Îa!‹JoœUªy¾s3…¡º´ÈÅÙy¦j5înmòh÷i4Ù2EA¿×c{sÓÚÍØA>Ñ–Ú´ žÇsâÃLJI£ÙäÌÊ —^y…¹³+ [-nÇ)Í®ud”þX A_xšB0…wn˜–0­$-¥hjM=ÒTu„p-å~1á7}¶„ pŽíµu¾n·y2èûµ þõ½½=®_¿Î+×^åt¿Çj6Dä¢ÈÁ` 87¡óqg[påâ%þêòeŒŽ|ô†ÖX¥q‘Æ–¿'(I¥V¥—çlä6+ˆ†t–¢ò iBųv »žëqÿ·}Yç(òœ$Ih´ZcÑWYQÄ~˜éˆn¿ï§ Eîûê°;52i’Gç!•j¹8©T•¢Ñh2{î¯VªL-.pý_ÿ•Çwî`Ž˜Ž”öY–ÑîõX=°B!ƒVåàCs˜j¹tl4›œ?žË¯¼Âìò2i½A§R¥¶tšïÌÌÐøxžG_}Iow3‚¤”Ì-,0sꢄ-!цÀÐ o\ï„ å‘J¹”\ŠŸA©C„lºouœvs“LiZó³ü§ó0: ¦l §½³¡Ó¡Œ"zÔh¸'U¶÷öP¿î¡‡)6K!Ï¡(`ÐwzBâÇÉ[Û´’ —€a’ЫÖèÖjtê º:¢%ôjUÒ¤BùoȦ9=cÉÓŒz¯KÜïû/˜f¨"GÚ  âçß ëˆp:` RJ¦gføÞŸü ßyóM ‡·ñPÞzÓ*…• ]…”Ô“„ûRQXã%ì6lv«`]aCLFé!¶c'û”ô09’‚XHêZ“Öd§$gªUT£AÒšâÉ͘n—ì8?)ߟ¼yNrÇIÂT«ÅÙ•V.\`fqêuÚI…=­i[H%Ô—–9ÿÃiæ.^dõóÏÙzð€ÞÞ.Eš¢µféôit½Ác­éGÈdásŽð*v¦œˆ>Åè=[1j#†vbÍþÇ’S( Úƒ7=ÅÌÜ,§Ï¬Ð>1ÝÛ£³»Kž¦Ïœè}“‚£´&Žcjõ: ‹‹,9Ãìâ)ª³³ «5E1[R²‹/ÌÖKjœ@ሠ"5 ÷¨j Á””´´…'¢EÔ¢ˆDKïS&É|“5Чo*T§ÃlQðZ·Íùzƒ4ŠVúI…n­F·^§Óh0ÔýZAµB'Ú»[J ¢í£±¢n¤ßÅúˆtyÝžÕ£“~ä§k©u»œyøˆvµŠ®Õ1½æ,Íhg9»yN§ZFE°¬ˆ³ŒÚ0¥ÖïRítIú=¢t€Î2Tîxa âêÙ?dÒZsêôi¢F“nžÑµÐuŽ=ëèàè9è"è#ȄϽ¬P Ó”ºT$¥wÚóB ÖøtÎð†–B¯Òð[#ˆT¤ .Y’5›¸Ù9¢³+LÃÒp@gw—íN‡N¿Oƺßä%¥$©T¨7ÌÍͱ|ú4 KKÔ§§¡Re't•¢Ìņ8†Vж’(©2ûò+¼tf…•'lݹËî£GD8Î\¸HWGl"0ÆAXü‚°×"É‘i´‚òœ§ÐHyŒO8¬Ã9sp±VMvÓ”v¯ËãZƒ|e…s¯½Âé™iöž¬±½ºJ{{‹A·ëi³ì™‘Ù¥å뤢>Žcâ$!NªµÓ33ÌÎ/И!i41Õ ƒ(fCjv„  wx3:ëüJ„ó–41Ž > ¡!¥ AK+Z‘/:8¦Gû”¢8 ú¶T†iJ¼¹ÍÔn›^Ó®TÕÃFÓLd;YÁ®±ôŒe2¥qR l°ø¨÷ú$6E¯ý SØÝ Åg”ÂY2Ó¾Xè<'NS%© ¯åÉò7 ’ iä£i…ƒ¨È©e~Qï÷¨õ{$ý>q:Dçª0H3^pãÛBÃo.Ø ËXžôlÔÈ$!É tQ`óœ~–c‹‚4/èCÇúÖ‘‡]¥H ƒ(EMIªZ“ˆ„C[´~9Rö‡&ƒò‚ïµ N‹Æúmþš’F`¶õ“^'ÛÛ¬LMóòù äÖÒéuét{ôÓ!ƒá4ËHóœ,Ï1ÆÞå E•ZJR¡Z«Òjµ˜™ŸgzvŽj³‰ªV)¢ˆŽÖô…`ˆ$n“¹±Ø ¤10–GRR­V™:‰¥3+œí÷Ñé¨Õâ‘Rô C,}Ž›B!¥/œ–æÍßULP^l:^$cœ ä)¥µ[ Š‚´Ýåpú4 §Ï0»¸Ä•+Ø~Ÿ´Ó¡·³Í Ý!ô1Yæ7ú*’Rù½Aí…­:ŠÐÝè8¦R©’Ôj¨$AÆ"Š1QÄ@i6¤¤'$!8ïäPNEe˜Ü)ZX'¨„–º4¤ )%ÍQщ¨G µXSÑÁÁA)¢°^4)§xaªcH¤õ«M¾& ©(åw,Áfª×#Žb íÍú¥³Þm3ÍhûT{=Švz]ao;ׇ=”ÒXtQ ITÔZò¼À¥)ª—D©Ò˜`£ mA¥(h¤)Í4¥>RM$ofíÛ/ï¨VXËžµ¬s¢“^¬›Æ00†G««|ñÅ ƒ§þŸZ­ÆÅ‹GêçcO1kÖà Coo»±›ç´aÏZÚÖ1p¾è|HžçÜ¿Ÿ­Na >Ͳ‘¸2—’\R¥êœažÑO#Úñ¡T + [kkqQ™”á9e÷m. Áèª^äT²œ¸ÈÆ­?‘ºÖòUžóÓÁ€<˜|ŸH? `¨5_^¿ÎÐZfggGVÚ{œ9s†¿ù›¿¡Ùl¢µ>R°X.¬*!ȇž½L?нw6ŽBx%„ +!ãÞÇ{‰àÅ\ä·¾øŒá`àorå­3œ”ÞY)œÒˆÈŽVHLÈCÓÖ’XoûQÉsD¿‡üÁX|í5Tµ†ÏUþkž ÿìÖ­[üíßþ-yžsçÎÖwvØ’Š•|—^V „÷¦¶–ÂrcH‹‚u¥èïl³~óʦOâü•«Ð¨Óuà¤çö„pD¢Štã{Ä9¯dÏ­áÁö ‡ÃQʭÛñ[Eˆ&šTõ»‰”m·µØ,ci~ŽÖòifçæ¨HAU jRùb£55­©Çž×©FÕ8&Ñ¿ûì}~ýþûäYF½VMåŒ1>]b0à†Öü~ì¨WšgL[ÇÛQÄŸaåÓz¿°âb-J¢Bx0bƒª;ÏÃq¤½%²òõá‘¶–¤0ÔŒ7쇠ÒVgú°/è­,QáM‹Œµ¸¢@æ‘TT´/U(*›}qϹßûûýÎo± :‚Ç&Çv&{ÏxÓM¬aPNYÿžHPTQNiqñˆ­ÀJ)‰[gΜ¹Â²I%ÍÍœ9“?üÃ?$''ç†î—–| Yñ8—êêxï­·()/'\\‚£ØwS:Æå©—îù°›4eHn ÇmÚíí¥³¹+則dÅâÅÉD¯k«¢õT »tÇ6{€¦úzvlÚÄÅ–¦WΦ8/^Ÿáuß(Ž8–éê…ãuOµ ãr?)Áv—ˆôö2kæLœ6W>IáMû§»J™>¶8¾wf,J~8—œPŽ»š†ŽÔ={*=žô¨^7c£ÛÛB PWSÃÌPˆ»gÏÆ()¹ÜÚ53|+õôéÓüìg?£°°òòrB¥¥XEEÈ(É(%Ý59ºŽ£i„<^úZ[8ÒØ@傅̯¨ 0/L\Bºe*BÓÄå¤ÑŒk°Á…–êêë± ¹¹é8é®}fœO 4i[t¶´ÂÜ9T””•ÐuB†‡ Ç äñ$ÇCÐëuåy<ø“ýª8Àë¿ü%999Ì™3¯×{EÛ¬Y³pfÎäè Âi--„:»0M“Õ~?«_ÎéYj]€W‚OH‚B¸±1ÛÆ0x !ÃÕ[×Éùì:Àm~'4Û&O E£nÀ¹§5qå“êñêwæ7ÂAÓm<¦;"¤k˜É7H”Ɇ_®%lŸ*áv×÷Hw¨™Îå‚ÁÊéÓù³¿ùžüêWoÚ=íêêâç?ÿ9Û·oñɉ®ëè"yì „<^þŸ?þ÷<ð1Ó"j™Ä“±Ké¶Çp‹æt¼€ÇÝ$!ŸžŽîÝËgŸ~BÈïÏ(‘ɦUÚ5dè:†8ÔtJ ¹gå=|çûß'j™ ˜QÇaж‰ÚQ!ÜãkkÉ2YÚ¡#…À²mvíÜe»]#EæøÉåv§’´Ø¤:zo² Š`íƒñЪû)*(tO+u-ý–·[KJ·í-n/Ÿ®ðzÉñzHDúøÉ_ÿ?ÉùnZFYÍsGK–,áOþäOX½z5¥¥¥Wœô¥NûGàç²ÕcZ˜ŽM²ˆÙ6ÇNžâó;é‹E)2tŠ ¸q¦”˜àQ”"½ÿ%©‘àÓ ò™1m: .H¿5®ì®k—G3éòòôV`ÇãlÞ°<à _8”x<=ƒ Çëî'ßeÁñ{½x O²g•AaA>ú(/½ô_ùÊW…BcŠñ|ðþûìY÷<8ti é÷¬›]®»Æ…”„ƒnÛxu@ªi²—ÈØÛ:nó;¯á2M7ÞãØÐÜ›ð ¥Úzªkž#ݬ\MGw<º† KKÊ+š~ÒÝ`^éöíIÿ@rÞéz4 aÎ _á>9gG‚Áàèò0¤D×dzR¦.¡åE…ÄM7n°l¬dJ½ÈhÀe:^Ý ¿×CÐçÇk™ä‡B®˜%ýñ¡Š_3kšRù+^Ãp~M#7à§¢´„XÂ$fYD-‹AÓ$jÛć˜í$ߨ"™?㊓¬]M˜>ÓÂpDò%p9Õ>³Ç²‘ÙîÍ=ú× |º†Ïð G£z fRZT”žø*Áum$îø!q’V‘–œ8ê÷¸=µ„À°l¼RâÕÜiéY^#˜•ºg‡p8LAAÁûåj%–mcÙ®ø$,›ö‚ò<¶C¾¡SâówâšÀ”‰ä!;Ùä.%êBºÙÙþX+‘ I™?®…¼œgª¥çe¾dÝý áMNmÉulrl‹|]£$À¯¼ž+Ç—‚Tß*Ã0äååQTTtc rÃa|¾ë‡;R ý ͼ"“ã®u!04ði:n3@[sÓh¯l(Ón¸%%!Ë"”Ðqq·+ÃõBÓÜÙYšpgi ’o;WxD2g#õv×qçm§ÚfxSµ8É ‘úxd_ŽcNFºn(ù@hRà3tr|>7ØnèX·BÜ—si´dÃ.nà1 ·«ÏGÀðàÑÜÓ¡ËS#®mÍ9dænª’:™€JÍ/3t¼º†_× ZqÛ!®[ć„㸽s„›9l'ãq)ñ `x½äIA8ÓÚHN˜H}¡ƒWsÝ>Ÿ¡';êx½z,FPB¾ÏGQ0˜R’Ó/’”üq’‚¦铼€×ƒÔutËLÖe]ß CALÛÆLÞKËq§xXŽƒ®µ›Ì ŽIAȲ±âqÂŽ Ä›L‘“$GA_”ÎÂNŽòî:zt·ÔÄ/$ r}^òƒ!¼^·ßvº°>´èLd³¼áfô«NÀ´T¯žäÔ[¡¹n¸ 50!óPGKºùÉrfiW—íò-•í.2ë‘2ÓÙå0§H©àej³^;J%»VÏPÖGj¨ ¡ënU³#p’ù"ÃêÉìpç~NC7Ro{ëš\Õo41¨T´ÏëM?4žd>‡ß00=6Vêa±ôÃ⊃-$ƒ¶?Ç+¼{=ø}é¸DúáH&Öôƒáõ¸"êÕ=ÈX ݱ 9á`(y€£%]™>(R£³3ïgÒUôzÓÝô¼ÆµãŒÆS€®N¨¼ÂºM¶ßMrù|„t|Ȳíô)™í¸÷ÒIZxB b„„M¶ÈÓ5JüþäKHKÇ{Róèõ«¬.C70t··v·Â߯ë„|>rƒw‡èA>\¡I}AgPê…1œÈ!ŽˆRŸÉšµŒ½ãöaȰ‚ÒÁ§Ì…a”’«D뚟IRí±˜ì$­¯×‹a„0®ˆ)df_aÞ'7Ša\ÙGz¤ý†´!N~Rņã¤ÅÇöxð'…Ʋ¬ËJòs„ë¤tOuB¿¢T¡pÒ*IYmiJЧ'ʳbW0t ßO0à¿â÷ÈŒ±ˆ«îQú7 ü¯ë†fŒñ™p—á*k(u=ît÷÷ø|ä$ÅÇÛvp¤Ûr$-¨)‹ˆên¢m I®aPœŒµ¤ßðZFí^r&½q…åe`º›™´2}dò«g2åT€ä ´àš ZbqM Çs݇!ùÅ™]åUGßÉÏNÑ.ös… ”ê7,åu»ÐeþFFAçh7ÍÕ-sôOêš<ÉűmÇØŽí6àO¾©!ñâŽÑõJIa0DQNNºGS*^•v 3Œä÷ºÎ Ïç&~ŸŸÏwEo›TÿêÚ2)e:§I×u|I÷DbŽÚD Pæ}M5‡Ox<œäA‚cÛ8Ž@ø. jJ\¥¦áÕܸ›_Óy=䃗î6ŠKÇšôË÷9ã^躎°íä(ékûµ%ð¤xZpu×Q™™z>ŒQbpÍôéé_NE™¯x=“x* ÐÕ.X¦’ÂÐm IDATßH|®v›Æ¼zóeZQ©‡GðxÒGËB8WX "™$êM>0¹Áy¡Ðål]®ŒdƦ2ü>àö]ö$ ™oëOš®¾OWÿÙè>9Üú¤,K×Âu­ÛÔ=M j¦°Êd¬Âïñ¤c‚áPhÈu»ú…võ'‰ KÙ-Ý˜Š¢s#ÎýZñžQ/"·'C-üHGèuâr³×’)8™3¶2Œ¡!Že¹N]#ä÷“ ùp\í:fþ™ùP¤b×feÈëŠtê³ÃÝßl¹×©ßÓãñ\qß®v±3…Uqù`ÁëM§u ee]O¼^ï"Ÿi-ßj/êÑè~•G7Ù@þv¡Ñn†ñÞ8W_ÇÕ"4ÔÆ<³@ @ )>#9žÎŒ} eÑ u}׫²oa¯µM=üCÝÓ¡Õ²¬´Û–*,n®÷çÕ–àTŠN6J|nAî¿úO!Äo×”{1”Õq½%3v£äzíi§â6œ‹3Ôà¿Ô§2Ý¥ëZ×éI=ÙGæJ|b ]ýà\íêdέɃ2Ö˜Õ­ú uO‡ú}2ƒå)“›r‹ŸÛÜ"Jõu¹:È{=·Iqã{¢,%>7$ÕÜÛ²¬+ú_Û¶Ý$¹qè°x¹ÈÑ5RRÍÌ33©oÖ¥HM]pgÔ‹}‡º'©û1š{’b¼Ö&sŒöZ2×'›÷Ãqœq¹%>ÀÀÀdÚ´iøýþ}¦££ƒêêj"‘Èå&òc$Õs¦ººzÄßî”ǃÒÜÜÌâÅ‹Çå^´··§ïÅhŧ³³sÄïGjâÆéÓ§9xð £þ7NžãH àþûïç?ü‡ÿ.‘éFu‡9sæ°zõjæÌ™3æk‡ÃÜ}÷ÝüÑýÀ¨7Hj|ï,]º” Œù|>?þ8¥¥¥îXáŒ#û‘î‘Y³f1gΜ¿\§¬ÁÚkàötý¿B!þïœîóú&ìK/9ŸYoæåñÿþÓ?ñÌóÏ¡P(&õë׳ý—¿¤h÷þ<&<Áô)9`š¼ÔÕI¿ë]hºZ…B‘ ”ø( %> …B‰B¡P(ñQ(J| …B‰B¡Pâ£P(J| ……B¡Pâ£P(¦Y©L‹KI‹eÑÒÓÍk¯ÿŠí»vª•P(&‘3gÎÐvö, '9ýŽ èQ~ûÞ{j'(Y ¬éäø¼8Yúþ¬ˆ(Ð |†AQAÁ¨šn)Šqð>âqñ8¹2{ã°²">źÎ#Ûùüñßþ-?ù¤Ú Å$òÙ§ŸRó›ß°ððüYêGññiš†‚ââbfÏž­vƒB1‰ä‡Ã„4"CÏÚ©SV¾×Ä lÇQ;A¡˜dÌD''¤iYs»²'>hJ|ЬŠO‚wšøhA aY8·É…âV;'x§Y>:àÑ4tMÃã,'…B1v,ÓD˜;M|R_ìÕtDr¨B¡˜DËDzp,Óµ|²tÚ•Õò ¡§§{*ŠÉÃq„ãàËâØçì‰]G*ËG¡˜t¤” $Ùœ8Ÿ5ñÑÐðé®Û¥b> Åäb;B8xá΋ù$Ÿ›O¬P(Æèv pž;ÑíÒ¯²|Šì¹]RdÕúȪåã×u„:jW(²cù×ò¹3Ý.)AJåz)“ŒiÛ8¶C;4æ£P(²ê|eõÛ•ø( %> …b2ѲúíJ| ……B¡Äg’¬>M­€B¡ÄG‰B¡¸“Ü.%@ E;7¹P½wóQ(î@<ºŽfèd³¶@‰Bqbè:ºn`Þé–¦\/…bòÝ.-»]%²*>–è†aj7(wY ÄlÃëÅçó©•P(&ÛÛÐt²9;&{â#%–hÊòQ(&¿×‹Çë%&åó±¤r»ŠlàñxÐ=Ìd[›¬\C6Ý®¸m+·kŠ"¥$ÒÓÓCWWÝÝÝD"LÓÄ4M‰¦ibY†aàM®cê' ‘ŸŸOqq1ÅÅÅäçç«užB†î1°ÈžåãÉ–ð)±ax<µ²Œ‚H$BOO===ôööÒÕÕEKK —.]¢¹¹™öövb±ñxœX,F,#‘H`~¿Ÿ`0H   ’ŸŸOii)3gΤ¢¢‚éÓ§STTD~~>EEEÐu•í‘ ¼^/º×KBr牅;·Km¾ì ŽmÛ˜¦I,£ªªŠ={ö°wï^ª««éééI5Ò’ÃåRi™ÿwê¸V&»Rfþ¤ƒTVVrÏ=÷°fÍzè!fΜINNÇ£R.&Ÿ×‹áó•w˜å#¤LŠ]m¸¬ÑÐÐÀþýûÙ¾};‡¢»»›þþþ´U“)<~¿Ÿââbrss …BWüX–E,cpph4J4¥··—LÓ ‘HP__Okk+;vì 77—… ²jÕ*Ö¬YêU«ƒJ€&ËíJÆ|ì,æùdE| Ž$àóa(ËgR‰D"œ={–={öP]]ÍÙ³gill¤­­ ˲…BL›6¹sç2}út¦M›Æ´iÓ(--%77ŸÏ‡ÇãÁëõ¦ÿLYP–e¥âñ8}}}´··ÓÚÚJkk+MMM466ÒÒÒ@{{;çÏŸgÏž=,Y²„ßû½ßãþûïgîܹÊŸè?éve3Ã9kâ“àóyÕI×$ÑÙÙImm-ÇŽãðáÃìÛ·ºº:âñ8yyy,[¶Œòòrf͚Ŝ9s˜3gÓ¦M£¬¬Œ²²2 G%RJb±ÝÝÝ´¶¶ÒÖÖFss3 \¸p¦¦&šššhnn¦¶¶–ƒRSSÃñãǹ÷Þ{Y¾|9³gÏ& ªÅ›¨˜ÏGâN³|D2æãõx0”™=aH)I$tvvràÀ>üðC¶nÝJss3>Ÿüü|æÎËâÅ‹yä‘GxàX¼x1ÅÅÅ7ýÝš¦¥Ý²™3g¦ÿwÓ4éììäàÁƒì۷ÇsöìYzzzؾ};»wïfÁ‚¼øâ‹<ýôÓÌŸ?Ÿüü|¼^¯ZÐñ¿ÃïÏjžOÖb>6àñxUÀyI$œ;wŽù—aÆ 455aÛ6†a0kÖ,¾þõ¯óÜsÏqÿý÷ãóùÐõ‰?ðù|̘1ƒgŸ}–¯~õ«´µµ±oß>Ö­[Çž={èèè ¦¦†ÚÚZ~÷»ßñGôG|ík_»BÀãóIº]öèvÅ€@ÀG¹]Â… øì³Ïxï½÷8uê†Á²eËøú׿Îc=ÆìÙ³)--% Mj WÓ´ô ×ôéÓyâ‰'Xºt)Çç³Ï>K[g'Ožä§?ý)à…^àÉ'Ÿ$¨ÅËÇçC÷ûIL’åã ‰_õ])ñ1q³=“*>¡†2§ÇÓ49rä6l`Ó¦MTWW“H$X°`>ø =ö?ü0óçÏŸI>Ÿ’’JJJ(//§¢¢‚Å‹óÅ_PUUEmm-‘H„îîn:::xâ‰'˜9s¦ Hß$þ@O0DTÓ&E|„th(Ë'‘üo3 f˜-%ƒ@(V¾ü8Æwz{{9qâëׯgÆ \¼x‘œœ–/_ΓO>É3Ï<ý÷ÞK(š’¿Cqq1kÖ¬aÉ’%,\¸ÒÒR8@cc#7n¤¹¹™Ö®]ËüùóñûýjáÇH Ä““C¿>9â#Gív9€.SÊkþÒ¸¿> W¥Ü›ðD£Qªªªø_ÿë±{÷nb±¹¹¹Ü}÷ÝüÅ_ü>ú(EEESþwÑ4iÓ¦ñÍo~“|W_}•õë×S__ÏÁƒinn¦µµ•þð‡Ìž=[–ŽU|¼9!z cr,ÀÆò±HNs$^fŸ²|r ñª·×McYŸ~ú)¯½öZZxJJJøÚ×¾ÆøC–/_>e­ë‰ÐŒ3xå•WX¸p!¯¾ú*¤­­uëÖÑÕÕÅßüÍßPVV¦¬ç1 ñ„BôkR› ñ‘×äeÆ|4`R¢ß0 ëJ|ÆÁâ±,‹wÞy‡õë×sðàAâñ8sæÌá[ßúÏ=÷Ë—/'77÷–üý¼^/Ó§OgíÚµäååñÆo°k×.ZZZزe š¦ñÊ+¯°hÑ"•4JB¡ÞPˆˆÉ„/•Òû gùxÜš+‰˜`ù±4>Ã`zAr»n‚¾¾>vìØÁÛo¿Íþýû‰F£Ì›7—_~™o~ó›,[¶ì¦­Çqèë룳³“žžúúú cš&ƒƒƒ!ƒ„ÃaùùùéŠö’’ü~ÿ˜OÓ Ã ¢¢‚'Ÿ|2]1¿cÇxï½÷(,,äþàøÒ—¾¤öÒ(-o(D¿“tÚ–Úí2/@BJì ¾KÓè÷xX—§6̤¦¦†þçæÐ¡CÄb1æÎË3Ï<ÃþÏÿ™3fŒ)"¥Ä4Múúúèë룧§‡ÆÆFêêêhmmM‹ÐÀÀýýýTWWPYYÉ‚ ÈÉÉ¡¬¬,]Ñ>þ|ÊÊÊÈÏÏ'//ÜÜÜ1ååææòì³Ï¦]²M›6ÑÙÙÉ/ùKB¡Ì›7OÕ†"æã pœIéf˜*©Êíê4)%1)¯ {Ì4 rÔiט©©©á—¿ü%;vìÀ4MæÍ›ÇóÏ?Ï_ÿõ_ …Æœ,hÛ6lݺ•Í›7SUUE?¥¥¥,Z´ˆ¹sç²dÉŠ‹‹‰ÇãÔÖÖ’ŸŸŸÎîééáܹs|þùçÔ××ÓÛÛËÒ¥Kyì±Çxê©§xðÁÉÉÉóïýôÓO“““ƒa¼ñÆ´··óÆo`ñ¡öÓh,Ÿ@€>Ër»L°hÛÉ<†ú„ÀœhËè×Pâ3FŽ?Î|ÀÇŒeY̘1ƒ—_~™ï}ï{c!ñxœ-[¶ðùçŸSSSƒmÛ,[¶Œ?ýÓ?eîܹ”—— …øý~¼^/­­­–/_ÎSO=Åc=†mÛéªø”ÕtîÜ9Nœ8ÁßÿýßSZZÊš5kxöÙg©¨¨õúëºÎ=÷ÜÃw¾óZZZÒGñ6l ¤¤„ï~÷»*q„–×ï'nÛØÉ俉”SBŸÚòéüƒRbNtÌGJú¥$¬Ü®Q»D‘H„7ò»ßýŽŽŽB¡ðÀóÏ?ÏüùóG-<­­­=z”;wRWW‡¦i¬X±‚E‹±páB*++)++#_»Ž–…®ë„ÃaÊÊʘ1cÆ•.é¾]ºt‰åË—sæÌ.\¸ÀæÍ›9wî«V­bÕªUÌŸ?TלŸŸÏ}÷ÝÇ+¯¼‚eYTWWsòäI~ûÛß²|ùr–/_N^^žÚ07q¯ÇƒÇë%Šëíx'P~L)é—bHñ‰¹k¢-)é‚%>£"‘H°{÷n6nÜÈÉ“' ‡Ã<òÈ#|ó›ßdåÊ•#¶"R§çÏŸçÀìÝ»—ÚÚZæÍ›Ç#<ÂÃ?Ì¢E‹nºÎ+3{yåÊ•´µµqäÈ6mÚÄ™3ghoo§¥¥…Ç{Œ%K– GüÅÅÅ<óÌ3\ºt‰X,Fuu5‡býúõäææ²téReU߯ÇCN0È `KðN éc"¼\^a]mù¤ÄÇœà2{[l›œÜ\µAF*Ø–EGG¯¾ú*‡`Þ¼yüå_þ%+V¬ñQsÊŪ««ãõ×_gÇŽƒA~ðƒðä“ORVV6aAÛiÓ¦ñÕ¯~•'Ÿ|’­[·òÆo°nÝ:Ž=ÊŸüÉŸ°hÑ"ÂáðˆHÓ4ßÿþ÷Ó½‚Z[[yíµ×X¶lYº‘â:/¯—ÜPýñ8–”'0îcIW[’Ê2xuÌ'*!H LÜh K m›œœ%>#¤¥¥…ßüæ79r„ÞÞ^–-[ÆË/¿<êr‰H$BUUÿóþOÇá™gžá¥—^¢²²rÒ L Ã`õêÕÌ;—Ï?ÿœ·ß~›ýèGüÙŸýO<ñĈ3±5M#óâ‹/Ò××Ç¿üË¿088È;ï¼Cqq1/½ô’Ú87Ÿp.}ñ¸kŠLhÌGÒ9æÍŸn Üìæ˜p#Ó¡ ؈ )1Ñø|n75ÕRã†Äb1N:źuëèèèHm_|ñÅQ˜/^¼ÈçŸλï¾KYYk×®å‘GaΜ9“^' ™={6_ýêW)))aýúõ¼ýöÛtttðo|ãšøÑõb‹/fíÚµœ:uŠ]»vQ]]Í®]»X¾|9‹/Vh8·Ëç#'/ŽÎkrpÆý¹G"Õ×{ S|Úôj÷'c2¡ ¨›‰IIÂÐÉ …T ÕR[[˶mÛ8~ü8Žã°jÕ*ž|òI,X0bᩯ¯gãÆìܹ“¼¼<^xá}ôÑ?äòæõù¨¬¬¤°°Ã0øè£Ø»w/º®óòË/“——7¢\¥¼¼<î½÷^^zé%NŸ>MGG{öìaáÂ…ÌŸ?Ã0TþÏP÷ßï'\XH_*3Ä¥¤ûrÀ9B†gÕš´~ÜÿD&H £R’0<åç+ñÉ¢Åãìß¿Ÿ7bÛ6%%%üþïÿ>?üðˆ„Çqzzzؼy3›7oFÁ+¯¼Â×¾öµ¬ O¦ë”——Ç7¾ñ ¾õ­oá÷ûùè£Ø¹s'½½½1²¸™3gòÔSOqß}÷‘——GMM Ÿ}öÍÍÍØ¶­6ÒøòKKéÕ˜ðÎËÙÔÝ™âÓ’)>½B‘“÷8 %q¯—’’U‘<ššš8tèGÅçóñÔSO±jÕ*ÊÊÊFv¿ؽ{7?ÿùÏ),,ä¿ý·ÿÆc=6%k¡~ÿ÷Ÿo}ë[ñçþçœ>}:=ýb$q¤éÓ§óÊ+¯0wî\9sæ ü1}}}j# A ¢`Æ º=xÌ.’FG—©lêîL·+%>Ðz…¤WȉŸÒiÓ”øŒ€ 6pøða<¥¥¥|ë[ßbÑ¢E#úl$áÀüÝßý<òÏ?ÿøàf̘ÁSO=ÅÊ•+oª´a2ÈÏÏçž{îá{ßû­­­ìÙ³‡3gÎŒØú)((`íÚµ|éK_"rúôiöïßO[[›ÚTCˆOAq ݺAb¿§O¸z’aÎ\!>nÝtvDLœø hñ`’éÓ•ø\‡X,Æ8}ú4ýýýÌž=›_|‘âââÅzêêêØ»w/uuu|ûÛßæ—©“Aqq1O?ý4+W®¤¶¶–;v‹Å®˜€z=V­ZÅý÷ßOAA}}}lÙ²…úúzµ©†´|ŠéFNèȵaœ+ÄÜVª­€Ý#½óÑ5â¡%¥¥Ê †TKÔ>úˆ¶¶6òòòXºt)_ùÊWnh¹¤ªÒ·nÝÊ©S§¸çž{xñÅ)--½e~ÿTÉÆw¿û]¼^oºô#5AõF̘1ƒ{ï½—•+Wâ8Û·oçܹs#ŽÝQâS\L·ãçvEäaÉUgp{º7V—tM”åƒF, T‰Ï ]®Í›7ÓÕÕÅ]wÝÅš5k7Œ}8ŽÃÙ³gÙµk¡Pˆüà·d"§¦iÌ;—5kÖ …ø÷ÿwâñøˆ?¿|ùržxâ „´µµqâÄ êêêÔæÊÀï÷“_XHIJH8××§S8t ÜØs|(Ëg8˜}RÒ-Ä„ ‘ïs¢ÓTÀyXš››Ù·oÝÝÝH)¹ë®»x衇FtÅb¼ýöÛ„ÃaV¯^MEEÅ-›ãâñxx衇X¼x1û÷ïçÌ™3 Žè³ååå¬\¹’ÊÊJ ÃàðáÃé²Åe Óïó‘ Ó5'Èõêpí®1c$“ 3Å' \LKJz… ÝqÆU|ÐoÛ$t²²2%>ÃÐØØÈÎ;1M“éÓ§³xñb*++oø¹Ôñò¡C‡X¸p!¿÷{¿wËOx˜9s&Ë–-£  €-[¶Œ8p …¨¬¬äÁ$ qúôiNœ8A"‘P,úôx<”õxèŸ o§]\!>õI­¹ÆíªÃíjH¿\gñ‰KI ^¯:ú¼ŽËÕÐÐ@UUŽãp×]w1þüåå´··³mÛ6t]géÒ¥Ì;÷–¿^¯— °zõj6nÜH}}=–5²|ÜââbÖ®]K8¦¥¥…sçÎÑÒÒ2âÄÅ;a0cÆ ¢>}`ùØI·«Û×™I':”哟ˆ48b/¨K¤?@a8¬„g¸{ÔÕE]]õõõ!¸ï¾û˜7oÞˆ>{éÒ%>ùäÖ¬Yüyón›º¹Y³f±fÍÎ;ÇÙ³géîîÑçŠŠŠøò—¿L8FJISSPÏ™âãõR1kƒ ½ã,Ê©Bõn!èwuÄLjÌ5âcãV·wñ>!]ñOßOŒp.Å·Àü¨lqîÜ9Î;—öÉ—-[6¢9åÝÝÝ\¸pÆÆFzè!***n›{âóù(++cÍš5œ?žS§NêsóçÏ'??Ÿ¶¶6><âS³;Ããaúœ9DsBôshP—‡¾Ë']&n\ùñ‘¡¾¾”å3ŽÓ)ôü|Šo¡cßÉæÂ… ÔÕÕ¥‹.ËËËG4ú¦±±‘‹/2kÖ,fΜyËÍéºQl"³fͨ­­QΦiøý~–-[FII œ8qÓ4Gœ3tÛ[>å æäЧ¯¥,‹ŽCßåcüÄpân²á9 7"$õ¶ÅøÍñê” R¬š< KCC øý~–,Y’®øÉçZZZX±bŘ'DLe‚Á >ø Ñh4ݘ~DovÃH7¼ ±±‘þþ~eýdˆÏŒ3ˆ†BDÆÛò‘’ó¶MëÎ%€ÜR®Äpâsè‚&Ç¡WÆËCn×5´¢bŠGXy§!„ ¥¥…¶¶¶´øŒ´¢­­žž–.]z[Î0÷ù|,]º”üü|:::¸páˆ>§ë:óçϧ°°Çq¤­­Mzeˆsyy9Q¯—¾q®ï@­mÓíŠO?p7¥ÇJ|DR|º%Erʶ§@T›¦£ÞRÙ¶“…”’X,–Èçóù˜;wÏ{{{éïïgæÌ™xnòMÓðz½é¼ÚÚÚnöìÙéz8Û¶immUâsµåcôZÖ¸†YଓŸnàLJx†³|Î'ÿ¢—’Ëb`œüãÛBÏÏW½u‡!c%ç(y<ÊÊÊFlÅ$ lÛ&??ÿ¶î9wî\4Mãüùó#Ÿ¢¢¢´ˆ !èëëS'^–a("œ“ƒíõŒ[eCBJZ‡vÇIÍëêI6ÊLþ¥V 7.%',››¬ûÉ‹éµm|áÜ[¦Àq²-Ó4Ó9(†aEJ‚H¶¨ôù|·u×¾ŠŠ t]§±±qÄâ ÓÖ ‚h4ªb>W¹^%EE›KÛ8Ý—¨”œ³m„L©MÊòI‹‰gkéÐ’²ø¤e¥?uKÛ©FB^/¡ÜÜ)ßÖ![ضMÓðù|£¶bn$<ñxœîîn(**"???«µ_)ÑmjjBÓ4òóó)((ö÷N•å477ã8ΈÄÙ0Œô¿'¥Ä²,•hxõ}>žÂBZúX>ÿÞ€”ÔØ–›TœÌ5ÄÍñÖòIÑ4Æ¥äŒm‘ò¦|ÁPçØ–”P0Äð9Åäà8ÍÍÍ|ôÑGüó?ÿ3»ví¢§§'«×$„ ³³“W_}•û·cß¾}×­ß*..ÆëõÒÓÓC"‘P"2^â3s&ZI)-ãt?¤à„e¥\®>  ·e¼žån ô JÁÛâK^Óõ±e%›RRo;VT_X¨V:Kœø€7ÞxƒH$‚‘L­/Ëâéc4åüùólذH$¾}û¨­­åOÿôO‡üûáp˜`0ˆeYôööRZZª& ŒÓËË9WVF‹g|*"BrÐ4‰ºâS—ü¹‚áħ·Ìà;mÛ<à8cŸDò+¬¬¤@Å{²*>Ÿþ9õõõø|>rrrF”À8‘ø|>Âá0ýýý´¶¶g÷ùÊ IDATF™>}:RÊ!]È@ €ÏçÃqTüp¼,ŸiÓ0Š i‡Ã¥)iv'5’çîAֈħhÂMšuʲôKŽÃ½c ˜À]cå¬Y(ËgÂb'}}};vŒh4:ä)Ùþýû©­­Å¶m‚Á YµzRâSXX˜XØßßÏÅ‹9xð Žã\“‰œr…é“AÅ8‰O^­–ãó£3öo»p8çØ©2åIT|ܯ â¬mëMŽƒŒÅö‰'e •øL˜øttt°e˪««‡Ìõ9v샃ƒH)BŸŸ?¢~Љ¦i†ANN†a „ §§‡?üh4:dLçĉjÁÇ™¢¢"üá0á0 ¹ºÎX°&Ûá”Ûy@âv˸ˆ{ˆ5"ñwà1à+MŽã¹d;ô AÁ(ýk Ä… vp’Š e&OàCœš:sæÌ!O°.]ºtÅß·m{J9K)±m;mÅøý~*++‡ &×××ÓÚÚª}ñx<æå‘_XÈ…hŒÅ^ï˜'79§-’IÎ@sÒ±ø´GÇ. ‡ZÛæŸoT2(%@nn.ùyy·eêÿTŸââb}ôQ–-[6ä}îì줪ªŠ¾¾>4M£««‹îî¦Ü§Tî¦iòÌ3Ï û%>CqI %³gsöÄ æx-[FH%f•/}éK<ýôÓ466ÒÚÚŠ®ëYw»ÇÁ4MJJJƒ¬\¹’/ùËj±²!>¥¥”Ì›ÇnM'>Æã„mSçØ8®ðD’¡›ŽÑЏ¨Û»›§$•pX i#FõmÁ ‹V¬ ”åcÝ;Y³fñôÓO Ù»w/>ú(³gÏÎê5ƒAæÍ›ÇW¾òB¡«W¯æ‘GQ‹•ŠŠŠ(™5‹³RŒI|$pÔ2¹àÖÍ âÖr¥*ÙG->ÀÀ@»ã°ÇLðe€ðýÁM£%äñÅ‹o«W·*eee<ûì³<þøãäææf}f»a”••ñ£ýMÓÈÉɹ%GýÜR2}:MÑ(^/Î(N¼nöˢѵ¦»“Ú1lºúÄÇÄͬ“°¤SˆÂ “U>aÍÑõØ6íšÆ"%>SÇC~~þ”Š›x’£”ÙÅëõ‡).*¢CˆQn'¤ä¨eÑ|¹Š½;é5 ÷}ú1€›ïÓÜ#%û-“ÈåJÕëÒ/ýš† 1}Æ |£ V+ŠÉCÓ4rsrX¸hÍ/Ý£HàŒJÉîD‚!n›Ô&ÜxO|¬â“¢ ¸0 „<4«¢#¸°Vá )Ÿ6`  NºŠ)NN8Ìò{ïåRÀO×[é _ v›fªqXp 7Ðl߬øj%ˆ˜”ì64à”¤ÁÄ‹ Y¸d š…bê‹On.wÝ{/Má0Ý#l(o&‡´ÌÔøsÀ}n¤ŠN§,àóD‚FçÆà.é:ñéÓY¸lÙmÝàJ¡¸]ÈÍÍeùòå4]#LÃhrv&L„Àq-³Iƒe\ÄÇÆíBVeKÉIËâÜå®ôÃÒ(‰¢".^¬ÄG¡¸ðûýÌœ5 Ð ©a×72‡í‰„;mÔ= :Í0‰…cŸ”)uHB¬[YcÙÔ ÓWâ»uòò¨¬¬Tâ£P܆A87—Ò²2þ¶U’ ŽMµeb»Bu8)>æxŠO p7ŠmU[&Ç,kÈ™^7ØœðxÈÍϧ¸¸X‰Bq‹ é:‹-"^XH£¸¾ø4Ø6'-›6÷”Ëö'Ý®2ñ‘¸QìÀàIËæ¸m•ò²¥äŒe“;£œò3Ôj*·’øh ﺋÄôé4Þ è|IJ¨²Lp§7á¦å´Œä{F{Õüˆ H!O[6ûLóšþÎpVƒð¼yÌÌrú¾B¡ƒø,^L¢¨˜Æaâºè‚£–™êÝ3lMyF!>}¸‰Cgè;ïØlIı®²~làŒ„ðœÙ”_§:Y¡PLMñ™={62/váf,_íÝ Ú²8iÛtº—z’†IÛH¿g´âc]À6àÒ%Çaw¤ír¯V$n¤é¼pWTP^^®VS¡¸Åħ´´”`~QÃC§ϹÂê1¥dS"Î9ÛFºVO°')B">$µå ..%—›Ï/g6bKIŸ´›Ó¦e½G°B¡›UL›Fî´iœ¶,œŒ#wGJ"BðE"Aƒ{âÝlN ψGÁŽE|œ¤Ê{„àX”¶¤øôIÉi)¨œ;‡¢ë S(S›™sç^0ŸSW)J§|šˆ»ëÕŸ1‚ãõ›‰[8¶¨ŽIÉÓä¸eÑ!)9¥ëÌÿÒ—(*)QGì Å-Jåœ9äÍ›ÇIdZ| Ùqx/£[¤Ûþnà¨:ÓÝŒYr¨²a°KùE"A­mNz<,X±‚â’µ‚ Å-JÅÌ™TVrÞqˆ%§w ÁqËfo"‘s 8ˆÛýbTsŒnF|“_|[q[&íRrÖ0˜×]©T(nQJJJ((+£è’‚D²´jKÓdW"N—Ìž;W5úV(nat]§¸°yóçsRHÚ…Cµe²ÍL¤<°ÃÀQÜ“*>)ëg;PgCüi²Ï0˜;{6¹¹¹†¡VP¡¸…).-eÁ=÷pÒc°Å49dYt¹‡K6ðp’Qœp§ø àÛt5 AG8Ìò+ðjåŠ[œ¢’¬XIáá“D‚jËÂ’2Š[¿µ7£yLxÆáúz€õÀ}–®—z }KV¬À§†*·<………Ì[º„óºFƒe1àæõtpã½Ñ1»uãp}ƒ¸™Gu]ï …B̘1cÈYá …âÖ"3köl"RÒç8˜îLë&à—¸MâÇÌxeJà!Ä‘K—.ñÉ'Ÿ088¨VN¡¸ÅÑ4 ŸÏǬ9sRC?Ï;p³šÍ›ù·Ç3ýø°bOOOè©©!‰¨ÕS(na„Äãqúûû±mÛÆ`ü; ˆ›ù·ÇS|Ú„{÷ÔÕÕ‰>úˆºº:„jŠ[”ÎÎNª««ikkÃ4Íz`îûM3Þ…WUŽãü.‰DÞxã §ªªŠµ‚ Å-ˆmÛœ·{ýºD"ѲgÏkçÎttt ¥T«©PÜ"8ŽÃgŸ}Æûï¿OŸb”òn¬gJŠ@Ÿ”òˆã8›[ZZšwïÞÍÆUìG¡¸EBpäÈvìØÁ©S§Ç9-¥ü÷ˆ}ܬˆ‰‘t¿ÞqçèÉ“'£ï¾û. $ µ² Å·xøè£8zô¨ˆÅbRʸ§\}ãù]ÕéËv !vôõõÕ:uŠwß}—öövµº Å&‹qöìY>ùäN:eš¦yø7‘É<Ùâ“âÛ¶wéÒ%ó¿øG¥¿¿_­°B1EÝ­ÚÚZ~üãS__b'nËän2§'âsQ±#‘Hl»xñ¢xçw¨ªªRÁg…b röìY6lØÀ‘#GäÀÀ@³”rîѺ5ß7ÑâŽJ)ß·m»mÛ¶mÖ_|Acc£Zi…b ÑÛÛËÞ½{Ù°aƒŒD"¶ã8Û¥”»pË(&„ÉèîÞ(¥Ü*„ØÕÕÕÕ»k×.¹mÛ6,ËRB1EÜ­ãdzmÛ6ªªª,Ó4›…ëp‡DLØC:¥ç©*ØŸ$‰{÷î-ÈËËó®^½šY³fáõzÕê+YžÁÁA^ýu6lØ€išMÀÏqO·&´8s²æÚ$€óRÊ·âñøáÇóÿøtvv*ëG¡È"‘H„W_}•d)T›”r7n®Î‰´z&S|îQÝ&!ÄööööÆ­[·²qãFš››ÕP(²@OOUUU|ôÑG444$lÛ®ÞíR˜ð¤¼ÉžèwNJ¹Å4Í---‰·ÞzKVWW«Þ? Å$“H$8sæ ¿ýío9qâ‘Hä¼ã8›-mñ¤ÈF»Á]¶mçô÷÷?´sçΙ˗/÷–——³råJ5`P¡˜¤”477³uëV^ýuÇm)åop§ŽNZ"^6ÄǪ¤”?I$?y÷Ýwg½¢¢BÍuW(&ÁÁAÞyç^ýuLÓR~Œ;¢n2¯#ƒÔÐlB¼ÝÖÖV¿qãF~ýë_ÇUZ¡˜@lÛæwÞaãÆ\ºt)â8Îàuà“çɶø¤¬ŸKÀ;¶mï>{ölûûï¿Ï–-[èííU;D¡˜úûû©ªªâý÷ß§¦¦&aYÖÜ“­½À¤?xzï…B|ÇÖÖÖ&þéŸþ‰“'O‹ÅÔNQ(Æóa³,êëëyíµ×8vì˜ìíím¶m{+ðîšIw9¦Â|› ¶mçöôôÌ?tèÐâõë×k>Ÿx@í…bœhjjbÛ¶m¬_¿žÇqœ€Çm“‘•XÇTŸ˜”ò ÇqÂýé§Ÿ†ÃaO0dùòåj×(7I{{;Ÿ|ò ¿øÅ/èïïw„oJ)?ÁÍçÉZu*ˆ𤔛Ç©¸xñâ~öÙg³B¡Q\\LYY™šù®PŒ!}}}|øá‡¼÷Þ{œ>}º×qœÀ»¸½˜³ߘ*cEͤ ÿ«mÛÅÇŽ{Ê0Œ™3fÌО~úiJJJÔT…b ÂóùçŸóæ›oràÀ~Û¶á6ÛËMNô)t¿Iúÿ,ËÚzäÈ‘øÿøÿƒÐÛÛ«ŽàŠ‘ºRF9}ú4û·Ë¡C‡Ó4OI)ßMZ=ÝSá:õ)xïÎëÇùMss³üñ̦M›Ô¼B1R7Â4Ù½{7õW•ê~NñðöTºÎ©èËÄqG/MÓÌ?wîÜÚ×^{-dš&/¾ø"yyyjw)Ãà8[¶laݺu;v̼ä8ο'³˜»”øÜ˜.`Ÿ›H$röíÛw¯Ïç+ |ík_# ª ´BqBöíÛÇûï¿Ï_|‘èëëkvç䨛3dñdëVpK0~'¥ Æb1}ÇŽD"‘ܲ²2V¬XAAA 1¾‰‰D‚@ €ßïG×õ¬_Sj¬vꚣ#‹ÑÔÔÄk¯½Æ¶mÛ쎎Ž&˲¶?Zgª]³>Õï)ðŽâX,v¸ººš?þã?fÿþýª Çéïïçøñãlذ³gÏf=›\JÉàà [·nå‹/¾ ®®N.ŒÁâ¹páù—ɦM›hjjê2Ms#ð·@ `OÅëžêç×· ãwRJ‰íííkþáþþð‡<ÿüó©Ý7Bؼy3¿þõ¯éííåë_ÿ:/¼ð÷ÜsOÖ®ippššþÏÿù?D£Q–-[ÆÓO?ÍË/¿¬l„VãÎ;yýõ×Ù¿?ÝÝݶm¿ ¼‰ÛpÊŽ ¾U’gZ¤”Ÿ !´D"a9räîuëÖ…mÛæ…^ °°0ë®Ã­À‰'ذaÀ0 ¢ÑhÖ{(†aÔÕÕÑÖÖFKK Á`P‰Ï…gëÖ­üö·¿eûöíV{{{·ã8ë„¿N2A#oî4ñ¨—R~,¥Ôb±Øܵk׊x<^ …xâ‰'(..¾ã›ÑÇb1.]ºDnnîq“ƒräÈLÓ$S^^NiiiV¯Ùï÷S\\L @A{{;gΜ¡®®!Ä.XWW×½ÎRJb±GeݺulÛ¶-ÑÚÚšŠñü 7¸<åg“ßjiÃíÀ/¤”f<ÿÞþýûijjò~øaJKKïØ ´”’ÎÎN¶nÝÊÉ“'‡⪪*"‘RJ„SXX˜ÕëÖu¯×KNN†a ¥¤»»›>øÓ4‡Ÿ'NÜñÂsþüyþþïÿžªª*§«««Å²¬MÀ_áv"´o…ßåV­Yø@J³m{ ©©éùÿþßÿ;ÿå¿üžþyæÎ{ÇnJÛ¶‰F£ô÷÷YދŰíËûRÓ´)ᮦ®CÓ4¤”8ŽC?¦i"ĵ!‹D"qÇŠO<gçÎüïÿý¿9~ü8½½½mÛ^¼†[¡.n•ßåVŸ>`§Â6M3vñâÅgýë_çöôôðo|ƒ•+WÞq›RÓ4JKKyâ‰'X´hÑn—eYÔÔÔ000€®ëôõõÑ××G Èzì"bÛ6š¦QXXÈsÏ=7üâ÷õqðàÁ;n{zzøøãY¿~=GޱúûûkmÛ~#Y¡ÞÈà%n:lÛNÔÔÔ<ÙÓÓ3£§§ÇóÝï~—»îº‹¼¼<5#ÉòåËyâ‰'¨««£··—H$’Nî˦i‰Dðù|°téRxàuz™!Î---ìÚµ‹Ÿþô§œ9s&bšfu²Hô ²Ø ìNHNÃþÁ¶í–¦¦¦WÞzë­¢ÖÖV^yåÖ®]«2¡“,]º”o|ãtuu±gÏ,Xõ‰!~¿Ÿ²²2*++™?>_ÿú×y饗Ôb%innæÃ?äïþîï¶mïþ øøVs³nGñËóàßB´ þèÀå¦iú/^¼Èw¾ó‚ÁàoéºÎüùóù¯ÿõ¿òýïŸÒÒRŠ‹‹³zM^¯—Ù³góÿøhšFqq±*N²k×.Þÿ}6lØ ûûû-!įp'ŠºÕ…çvp«áÏK)ãŽãX/TUUÝ‹ÅJ#‘/½ôåååY®f›P(ļyó¦ÌõhšF(R-s“!ˆD"lÞ¼™M›6±gϞĹsç.:Žó1ð!pè¹~×Û­=  \~eYVOOO÷¡C‡jll¬´m[{üñÇY¼xqÖs[Š¡ˆÅb466²gÏÞ|óMjjj"§mÛÞŒ{”ÞÌ-M¥R‘¾¾>|øá‡8|øð×¾¬O†x<Ž>ø÷î݃¢(Ô¶í€?À-£êé>êÕÄ’~G)=©iÚÛ©Têµ÷ß_îííÅ[o½…cÇŽ‰YÂ?ŽÛÍFGGÑ×ׇ+W® ›ÍBÓ´,!d®hpOÉ.-A>?M¦HPJ Již2ÇO†Ñ>;;Û8>>Ž'N »»@@¼Aß–e­®ýꫯ011Áâñ¸Êûçü&çü܉ƒj=ÞO½'7(ÜŽàç<î8Nqzzúh>Ÿß‹Åž_\\”>ŒW^yííí‚„¾3é,,, ‹all _|ñ¦§§+–eMBÆj!Ö¿,Öó=‰Ìª ®l=nÛö Û¶N§O}òÉ'‘Ë—/7œ={V>sæ ººº áõz…@Qàx4²Z­"ŸÏc``çÏŸç‰D‚˜¦©RJ'k¤sÀ ÖAo– Ÿ?ð€1Ö¯ëúo³Ùìk}ôÑÆ?~ï½÷ÚÛÛE>Hà „ T*áüùóèïïG<‡ªª¶iš³œó?¸àaÍÐ1qc‚|¾ &€yÎù?8çsŽãTåÔÔÔÔ¡åååçÆÇÇqìØ1ôööbÿþýuߨZïpñx7oÞÄÍ›7‘Éd0;;k­®®Æ9烌±¡éP'%tA>?ÐÁT¶Ì9Ÿ¡”æTUëº~(“ÉìÏçóÍÉdÒÛÓÓƒ}ûöa÷îÝhnnsƒêÅ=f š¦!•Ja||ccc¸{÷.w8çIJécì_FLÕB,± QÏ÷ åkë2clŒ1v À¯âñx÷ƒ^ Ÿ??oÛ¶­B–8ç1ýàætlq{‚|žÆLƒ›ºÃ9ï ”¾ªiÚ/5Mûù•+WÂCCCž––"=7©¹ `sžçœ'c·l5 £Í¶ím«««]„.ŸÏ×ñàÁƒðÄÄ„·½½‘H©T ¶mc``KKKhkkÃæÍ›‰Džé!hš¦AQ‹E‹Eär9ŒŒŒ T*áÓO?…ªª( ¼X,ꪪdYNSJ³”ÒiÆØ,ÜÞ¼bíY„#ÈGàƒ×,³ Sû­‰RºÕ0Œv8ŽÓ©ëú%ÏÖ0„IDATóÅb1’L&#”Ò°,Ë‘–––Ð¥K—6Äb1´µµaË–-ˆD"hjjBSSB¡B¡ZZZÐØØˆ††444`Æ ODkD)…mÛ0M†aÀ0 ¨ªúÈs¦iPUu|æçç‘ÍfÙÜÜœV.—Ëׯ__¢”–Ç)SJKpW$¥à&Œs–EGÀtj/Tºö]¢”vjš¶ À./éºÞµ¸¸ŽF£~¿ßïà#„ø½^¯ëÖ­¾ÎÎNyÇŽRgg'Z[[‰D‡ …àñxÖŽ×ë]Ë'ɲ¼¶ÙU–eH’I’P­V×HD×uT*pÎ×clíù(?C¥ô¿ååe(Š‚R©„\.‡d2É3™ ) 6!Ä–eÙ–$É1MÓ&„èœóùZ¾lªöLÁÿ‰A>?Ažh®Ä €§v8çÇqº!/rÎ_ÐeYÖ®l6»mff¦yddÄóHU-Ic Ôí… …Bظq#Z[[×¼¦@ €@ €ÆÆFȲ¼¶×Ìï÷Ã0 H’„J¥‚h4 MÓ`,Ë‚mÛ°, †aÀ4M”Ëe,--¡X,¢T*auu–eA’¤µVÇq`š&LÓ´cKŒ±4€¤$IiYÎyîtµªÒÚ]Páá<{õÚõ€@à¿N€FÍ'ìõz7I’áœGcÆX˜s¾Ñçóµø|¾ ßïøýþFŸÏç÷x<Ò#OèëÇãñ€1†l6‹`0ˆP(„††PJAã8kžcŒB,Û¶MÛ¶uÛ¶UÇqVcË’$-{<E–e…s®BBHnJ¯y}_ÿlBtޝ üõc§ÒcdEIEND®B`‚HaCi/html/Images/logout.png0000644000175000000000000000662010670113276015241 0ustar fighterroot‰PNG  IHDR00Wù‡ WIDAThí™kŒÕ•ÇçÞꮞîéî÷Ì0ö ~ãÁcˆIc’lPb1 Ø”|H”VD B”Ê—DÊ¢lX­D"¤,!l$”Ý $baѲÀÂÀK  ñ0‡yzº§_Uuëì‡êÏø•1E+í‘®º«êÔ­ÿÿ¼êÜ[¢ªü_óax¿òÿ>lñ>ÈɆï¸CÞ¾ýö¬«V ê\AU "RkË6—+¯ùö·k+¿ùÍ,ñäƒHâ'W­Êºéé8· ¸ è '©•£ÀsXû íì|ô³GŽÔÞï³ßÇ;:6E·¢ú…ä²@»¾étãûˆçá ‚Fƒf3«J ¡ŠÈ}xÞß\yüøÿUO,[¶"‚¿Fõ«9(–Dh+H à]pÞºu˜\<Œ(Bƒ7_£è'Y‘μç‘Ûµ ÛÓƒA2DbÛÓƒííÅd³¼înÒkÖZ½šÔêÕ 1väb-&ŸÇvt`{z0Ë–¡Ö¢aˆ©U«È^}5ykɉtjýäÙ¾>ÿ=ˆffvølQÿ£Å»ðBâ ŽcLg'^©„õ}R+W’Ùº•zµ A„ÌùÃL=ŠI§ÉlÛ†éîFÂãyÉ¥*B†¤7o&=0@QŸffv¾'O/_žCõûí"Æëè íSŸ‚ @|Ÿtw7Öó ýñã_r õ0äùGM¸Ó0„0Ääód.½oãF4Š ÀCª«+yw8GÛöíØ\Žvƒê÷Ÿ^¾bUÔ¹èIÀ94Š’ê#Bz`·v-®• ExÙ,^±ˆ´µáoÙB›.ÖÙÙçLÕ])0&“!µ~=6—ÃkkC‚šM¼ñúûçõù{ï¥>5Íæé „!:1Að‡? ÆÐ¾y3£Q”ÜÓl"A€M§I‹¤7mÂd2¤À ºëœìH‰êu¾éÁA¼l–T[†Iåó¤/º(áÇüÛm·ñÄ~À…\pÚ Š V£òÐC{æl*ÅŠk®áå—^Bƒ ™;ðÒiÒ…éÁA|Dõº}©%¨=ú %/•Â_¿?ŸŸOÚ¸I§8øÔSü×wrà 7°º¿ÿÌZ÷·‰{ùeÜô4…Þ^f{z˜~÷Ýùù%Hg³øë×c=¥úÑ£ŸX2K,`‹E2==˜n°kÖÌ[ÿñýˆ¿Ü¾ó::93%q뺭V Ÿž¹wІ«¯fß /œxF³‰„!Ù+ðŠEl ÓÒ ¨^`ÛÝM*•Z4±)‘l€Ñ×^Ãþñ\´nÝ"3z`NüÚkP¯°bÓ&ŽÒ¨T餬%ÕÛ‹ia:Ö3­È:Dÿ¼ó°qœ¸vŽ\>?ß"Œ ±yíÚ$±ÊÙ<ЭV‰gf°Ù,~{;¦P`ld„•}}'žøÝÝI¡PíX2 €—Éœδb <:ÊÚRé9¤•Ä:9I<5• ñð0vùrü|ž±}ûèo61}}óÕ-Õêtç0-•@] Ðh,º¦åòüÿή.ü8^dYà´ˆÁ½ó΢ðZ؆׎§6;‹>L<;‹]½D°ªIcõs!0…*ñä$”J-äIÂéø8Úz-[³&±ìI$OBÓÓH>â8›Åôô$êaÈìØZžE³Y¤XL ¦š`Z2c¡Jó­· »{>iQ%|ûm¼ÙY¼|žîM›¨¶·# ¼œÒeˆê¢Ð2^ˆtu0vè³K¥:£Éê2|çAD pŽf½Ž:矟´Ê€m4¨¾þ:ÅmÛðÛÛq;vÀ½÷&ëÞÙYtr’à¹çªVq-¯>LŸs‰uDHå+ˆ—<þÀcaêuz¬=¡3=NOÓ,—c^X2â¦M/μòʱz³¹ÜU«x‡£…’ËaÒif~õ+Ú·lÁz™+® xüqâ'žçUjwßÍÃÇŽQ[JÛº»“J{ýõØË.K,ÜhðôOJŸïÓiL¢ÓU¥š”ÚcÅM›^\2 {÷ºÛÛŽœ»a¶V£3›…ÉI˜œDEȼý6#;v°êª«0Ù,éï~—`l }é%rÀM­ê²Hâsíµ¤¾óÄ$¯ gö3&††¸º§sRõª @àá {÷žv|æfΘ߸ñrmu‹EHRh69vË-L´3ÓÕ…ÿóŸã}ë[HW× Ý¹á¦¿ŸÔm·‘þá1íí =õ}ï{ d2 ¦Ó§Ü7ž„Øߜ æ—”¯¬\™‰¦¦þ#‚Oöd³ôf³,\e;ੵkùø]wѽn¸œãljÿ{ôàA˜™R ¹øbÌàà¢*ôöï~Ç=_þ2ñÈ7uu±lnýÜ’©f“‘J {½eË®øÈððI¥îOø}¡p•ÆñÃØõù<oqÄU㘇::Øöã3°}ûçY(±s¼tÿýüóÍ7ã•Ëìéè`Mjq£ÙpŽ7+¬ªc®ùh¹üïgšï¬ö ¤¢£Gï‹`w¬Ïfé8‰D ÊCÕ*Õ;¹üß`å–-ø¹SPõ™?ó Þ~;o=û,k=/æótdùªs Õj Šÿ’êëûÂEo¾yj]^ €—K¥•4›Ï4UÏDXçû,KZÜy‰·Â'œãxo/½[·ÒÑ×G¶£ƒÙ‰ ¦†‡Þ·ê±c”TùL&ÃÇ|Ÿô‚9(;ÇP£¨â‹¼ƒïÿÅ%““Ãg÷¤}¡ÿ..×8þ׺j>JÖ²6•"-‹÷žb`<Ž CŽÅ1eU"  BÉ6XKŸµ¤Nº/Re8Šøcám"1æÚÍåòoÿ¶%o-¾’ÏIU磌f$õw¹1œg-YYÒ&Ú)ÒTeL•Qçhª’IÀ×Dä«©T~¹”9–ü#wùåÿT{úéélÿƒQí©ÃqÌÑ8¦S„’|’&PN"¥€S%ʪLµFHÒ6g_d̳'ûéOŸ1iO–sÞ~µ³ó#„á/lšMv•OLFÒ󦔩ֹP•I¨Í‰OòMÁÂk¤R_ºxzú•sÁóž¾¼Þߟ‹Ëå‰ã[B8¯ªJp°³‰!!™KH¾‹1k …;GFªçŠå}}¡y½««?n4¾)ðùúÕT“ÄÚ1IØ@â¤H,ž Œ(Ü/™Ì›&&FÞ+†³xàä‘G‘‘‘9~ü¸LMMI³Ù4AˆsNœs&Š"ù+çr×…áÖ‘Y¸Üƒ ² (Ô"ªÁoßU}ôAÏ{ñAÏ«cbk­Zk5•JÅ™LFs¹\\*•´··W7oÞ¬·ÜrËAθï¾û䃚ññqÛh4l†6Š"DZãØ¦õkUÕ¨ªi״Ήª_Ä|L$¿R¤`Xµú’j¥©êDDEdÎA1IW‹H,"®5bqÆg­u©TÊù¾ï:;;ÝòåËã]»vÅŸûÜçtÅŠ o¼ÑìÝ»×NMMÙz½n£(òœsÖ9gUÕSÕùßPÛúï‘ù±àÚ±–æ:q­ã¹-8Îã€ÈãDÄYk#Ïó"ß÷]±XtƒƒƒÑ¯ýëØؽ{·Ž¹7Þx眨ªSUæF|¢ÍUUÕ–UUã9R­a$‘…àçïmY|áp ÀÆ€‘h'œ1Æc¢–'â\.çúúúÜîÝ»õ´9päÈî¼óNyõÕWÍè訩T*Òh4lE& CÓòŠQUDZÌ\è,,'_Hb~´Beî7n…Ll­=Ïs¾ïÇù|>îêêŠõÖ[oW­ZuúXªüâ¿ÉÉIjµ”J¥"õz0 ©Õjâœ#‰ãx~çMD0ÆN§ÕZK6›Uk-šN§Ù°aƒf2º»»Ù³gÏ9ú@¾Ô˜ò?à²T[IEND®B`‚HaCi/html/Images/minus.gif0000644000175000000000000000151510474157163015047 0ustar fighterrootGIF89a ÷€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, 2°HÀþ¨¡½ö"J|È΀‹@œhOcE hp¤I$  ;HaCi/html/Images/move_small.png0000644000175000000000000000030710704777334016073 0ustar fighterroot‰PNG  IHDR%=m"gAMA± üaPLTEÿÿÿUÂÓ~tRNS@6:™öbKGDˆH pHYs  d_‘tIMEÐ 04f£›*IDATxœc`l``>ÀÀþ€È"ņÄ6†ÿÿAÈr!â@@eŒ >ô o²g0IEND®B`‚HaCi/html/Images/noLine.png0000644000175000000000000000017011030471134015135 0ustar fighterroot‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`£€ªSZ¶€!IEND®B`‚HaCi/html/Images/password_small.png0000644000175000000000000000110011031215111016725 0ustar fighterroot‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ¤IDATxÚ¥SKKQ¾êø˜QÇÔÁу¸3!8CÑV¢mТMôÜõ\EE´í!Dä ÑÊèÎMÐ*hÓ®Utú®Üµ#ø˜sîwÏëž3Ìív³IŒÈ6°Âþ’Ÿx<ž—Ïç)‰ô|>óûýθ\®M|Ai¹\ŽdYnz½Þ ¹Â™ò-È1H’t_½ Ç›l6K¡PˆÛk̷̡ÀPöy@uä Åb‘Eé&“IBÿ6Àáš— u íT£Ñ(×+lJ)ŒèËNäX,FxÈÆ/ü‚¦i„)N’3M“Pîƒ8×ìÀöE˲ø{ S·ÇXÃcñ/Ãápö£a¯X ~6ƒ=hð±B?ÿ¾•Éd>ÅXI, ÒÞø¨0}èu`NŒòxáYà, öpÿ¶1ì#‘H´‘©òæÎdŸ©Têå·¿:FÂñ¢\.²Wu]ïÇãq^žióh§[*•øÙ^:þPU•DuC©ÀùŽï°ˆ5nŽÜJ® s?[s€ˆþ…/ÆÚ&4e2 IEND®B`‚HaCi/html/Images/pluginMgmt.png0000644000175000000000000001114410670113276016050 0ustar fighterroot‰PNG  IHDR00Wù‡gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<öIDATxÚbüÿÿ?ÃPÄc022ÒÉÊN†ŸN ÿ,˜8u8YEþýýÃðóÛ}†_Î30¼:ÄÀ0ï:Pá/ &ºÄ‹úx %èè*U+ +I- QQN†ï¿þ1Üyú…áÌÕ· §?øúëÖ¥Õ ŽOd`8x¨é'>È´OF½¬ mÓÅT—ý¯žrïÿ½×ÿþÿù ÞÿÿãÙïÿ-ò.ügžüÁ7¨Yˆ™p™ @tð@;Cû"E³­ÿçìùþÿÞ‡ÿÿ_|úÿÿб¿€¾øûïÿÿ?@üý×ÿÿo¿üÿÿäýÿÿÇïÿÿÜ÷ì?ƒúüO öÉ@C„A‰›éÄBûdós¢¸ž^ljƒ”Ãëw "< ¹ˆ™˜>€á÷ë0)10¼ÿÆÀð˜h‚l$>ýðãÝ3ã[ÏÿG¾20\Ü Tþ=9=ÐæÍÀ'™.bl t)Ã[ õlü ì,ÇÿýÇÀÀôÉ?730|:üó +žøÄRüB ¬Ö¿ÞÜ­døvåPÇ% ÒoÈ6M=ÀÄÆ’ͪjÄðò ÃK`Ès³@ÏtôŸ¿ œ¿!øõÀ ÞÃú݆·@üðņÿäØÔlô~]8ÌÀpüP)Ð#`?ƒ@ÑÐmì’2öÿ¤”ÞüþÏpññN6Fprù t<ÐO@>v¼úÌÀpèÔKÏÿ1üaãdà•PbøÍ®æôÿçñ@¥/ø+Ì€¢¡þ³JËr}ãûÀl|è ..`ú†&^ 0°lbæŸ ÉägŽÃù#Ðy‚ÿØ™e~ð+Êþ~Å,4÷²ˆ…†ÉGƒYTˆá30ÂÿË“Ë÷Aþ‚dT` p³A=ªµ€ûü’|€Ä??Ã'–ÿ ¿ø€yG’÷÷+n)`îàæpf šy€™“U‰—ŸùÑuÖG~3 3<qc`üÁÍ £̨ÀÐfe†$!øtü¯¿  è9`Œ] y+¥3/ËWFPÀl@ÑÌþ³ÿe¸1‡AðÑl†?ß^0üºî¯˜ÃÁ5 ß°3ð‰c‰Rdž¹ÌÀðæ5Äñ¢b Z*@Ïq‚ .pnýôåÿ Ö6¨›á1@4ó€ Ûz–Ç×þþýÆðŸ™蚟 žlaày³Š‰=–Žÿ€N8u˜(îgþ¾áçï ¯_x3ÜfqgÐz‚(/ð”_þ2Üùðæ7¤ìBD(+KWýù}Õõ'¨9ÆÈ̤@#3(ù3°þ¾Í ,€Éä0³þ{Aÿw ÃïŸÏ¾~ûÆÀún ÃWÑE ¿”lØY!™ýïÇw ?ß=fÜïŸÑyÄDmÇ«©©5ýÿÏÞúï?+(¦aÍ”~10³°0(X¸3K#,3‹ÿ?ÈÀôû0£ò0üeâeøÿë3ûëUà„y‘÷áö=†ß_èe†ïÈ bb@ˆÝ€Ø„››[Ž÷/0]Á‹?þ\Šï⻌ÌÌÌ]@±\ô¶ÕÿÀE¥Ad;ƒŽ©5$‹³ä[öO ßÿ1ÂÛd˜Øþ<–Pÿþ3ã—¯ O\þÇðÿâ]hðÙl€Âç ßJøùù³Ŭ¬¬´´´xyy€ŽgxðàÃ… öíÛ÷þþýûKÿýû§ÌÊÊê‰ÑX:ž…•…A/lƒ¶g2;Ð#, òh3ÐqaN†;þk\F ÒÿXþ}g`Ÿ,ÀŠïäúã lÖ ÷@}„‡è \f!†ùNNN6ùùù vvv Š>}úÄpúôiÁ¥K—æ8p€ú @OÀ[¹ 2äï/F^ƒ M÷dp} `pSTþ³Ëvv`Fÿ TÏ …?¿¾3ps11<8}“áæž]Àd³ëPç ¾MBp@Øò€,0)¬ ±ioog011[ uA¡¢¿ÿ¦ÕÕÕÒÓÓ‚‚‚ÀމÃ:GÀ†òŸï¿™/<9ûìÆñ=@7üÿμŒÐ¦(È#̬ìà"Ѭ&§¿ßî:ŰwÆêoÿ~-> ôs@ ~Þ ôàZ¾ÖÕÕU/::šá÷ïß >|Öˆü@Ë ~Ñ O€ä~üøÁðùóg°G,--¾~ýʰk×.`™ÿ ä˜ß?~9dŸ``X|óêœ_NÏ.ùù[ø˜°)ËxV6 gØØ9Àe=8ù1#°rx÷ìÙó_1ü]{èf ~†£ÐÐÿ‰Ú„â ã“yxxLAÉävvv`¦¦Wˆ2PLÀ<r$HÍ—/_Àž…<ȳ ±_?~<ñóç¯ã@m‡ÁÑÿåþ÷'žÞ~Á$ö´¶¹–¼®<‹˜”0Ã? c™éé×Oh,càßï÷ÿÎ;öŸá×Q¨þèiÙÂ\\\UÀäÃðøñc†wïÞ1€Ø ǃÓ%0”AB÷С`‡ƒÔƒ26(_áß«W¯nÍyà4¿…$šK~ž8õæÜû7ç¤MVJˆJþQU`–üÿã/ØP… ´ ¨ú÷[ ØÉg¸ m¼aí6ò¨„;0ä¥@…ê;w Å0Ôþü Î詃9¤ Oóññ’¢P?(Í~„4áÀà(ïXšÜØ d‹òs«øÿþ# Ë r#°£‰Y‚{è8PÉÉÉ .*>|v ÈQ ‡Cœ<`y ïß¿gxñâãGá¨V¨Wˆܘ†§ÝÿÐÐüõ 7?¯Æ_`äÛ `ü Jÿ‡ô:™qõ…a €àZhJû € J:/_¾gTP(£{ $òH=,³ƒ< Ì;b@Zh4$ÔQ«¨§~r}þúd?0yú¾Í¶¦¿ýûÇûâ¼­€‚{è1`Ôƒ“ È¡ T,‚BúÍ›7`O€ø %+•DPσc ¤”@žzØŠa¶98±Y”&÷Ž[·2¥¥¥¤44¸1ÿhæëÇ™ݽëûðß?þ÷ Ž10•”9ü9TêÀJPhÂ0Ìá åPº±A1J:°ü<\m»³"7A ’Á(Øklb¤êË ÄÀ&.Î.Wvüûò‰áåÑ£œ§æÎs?ºm»ƒÀK€¹¹ý9°¯Mep@p÷èY'@! Qƒ`4È ¨%›7n0¼}ûœÜ@ýú58߀’H ¤ÔXB$gˆ``(”áæê‰MM`ÒzEY ” í È8 'ƒ¤5ƒ¿º ƒµ‹û–þÉÉÜ·ïd`HöL¯!L<È€ò<¬¨Dw<ˆy ä0Ãmp,À0ÈS döäɆçÏŸƒ3·°°ð7 §¿#{˜lâ¤ÙÙú "ý™tm-!ýH y ߀ùØf, ìª}º˜d>}dQR`ˆMŽb“2°b`˜t°r² x Óï!`fÌ’’’G?,é {”îA¥sX>y”ä@4ƒbØ:•63ræÊ¼zýzšòû÷¿ØÞ¾íp“aáV°ÀBÒàž>¨ýª€>0 ò0<ÀðXÑýÿÃ`§&ÇpóÑsÃ' ÿ³Ï00tB‡WþrE¶ÿöíÛW-NmcAIÝÀæ4(Ã‰ŠŠ2+*°ãA1*‘@4ˆÃ@1& ˜<'g¼¢b4ßçÏ€©\ò7(´Ÿ=:œ’t˜Nzšë+Â#ÀÒˆá0û¾¶ž_=úâ Ã×w¯ÁÍc}¦ð‡ ÿ޼†ŒÒ½ 憆°ë£þ“P È‘ äs8Ì *jjàu( hPl€Ä`%Ř~üà–ðXXéïÖŸ?Ô€-MfPSÔª9Tš’ÐW`‡ëÐ]ïÞ@bè90Ï>ôýÛ ·.ßaxóÓŒ,ì¯þ1=c×Òo9€ÁÀ°éرcÛÄÅŽdddÀCŽ ÒÀ^ƒ’’ØñW¯^gjPeJZ Ø@މŸ@²~ùÌ ÷—‘AXqÿùÏðäêE`û‡ñ/ÐÊÀv¸€Õ+ÐSÿþB<ó¯åÎÃ{ .Ýdøõöƒ°05EÿþÑf+EP2 ôùP¦V†ðr???#PLÀB¹d‚…4ˆ¥}G¶lÙÂpýúõO@sX€j¹`u °TRöªbßv2~0ðíÕl¢<ÀŠOE’Q^X#ˆ}Æ õÀ?H&~ôÔ³§ ?n?bxùâ+ÃgèÈî;>†U¿½[úÿO-(ÀÛ¨ê×:¬ÑÂÂÂÛÀÀ€IDDì`'›Ó =wïÞeÆÚ¿{÷îÝšuÚfWÊÍfâà`²þõ—! ØÚTù÷˜òÁÔ­dcƪ 0e ½Äà € U|ð~þùþ \ƒ}†Vç–; Óþýlr×¶¨Š¿L 出tîܹ  PTTdebP,ÀšÀÜ_`›é°äµi=yqI;;7¿ [eKe1aANö[Oß}ÚsóÝñCGϸuïú6†7>½k °@Ì%««Z:s×…ÇO_ýûï¾üüÿêÃ÷ÿ‹—ïþµ,Ú{[HÇÉ ¦ €À¼¾éú=§?ÿGÿþûæÎ»ÿ'n¼þÿë÷_d©ÿÇÏÝø)¢çèÒ @ ì}ûõÝɧÿ·ùÿÙÛoèÒÿWï:õ™™WT A¾ÿ9QgÙº¦˜Þëõ×Üäþù ðý  % Úæõ ýòðîóöú=1%0XsˆÅÄ5(އ‡ƒïÛ_†»o~2|øüè*`˜r31hÉ5¿úÁp(~ðÖg†¿ß¾0Hò²0¨(p30330x8;ú‹Œ §(0å„9*ÂLÎ<ú°åêGŠëïþ3<ù4ôû?!v†H Y[U>fpè32Hòsð ˆùÿ?÷ï¿XX˜Ìy>ýbd˜püH ßÿÌ ?30øhs38jð1üüñ—áçß¿ œœ ÿþýg ¦/?þü›ÇÈÈðÿÿßN>ÿÍ𗉕AE‚“AK’‹‹›“áô‹¿ ¯>üZöl;|þþû/@1]þøÿóÇïÿþü'&0²Äÿß¿~ƒ5_½ÿþ¿Wýš§~ JÄOì<™ÇÏ^öÿÊÃ÷ÿ_úñ”žÿ5ùþûÿËßþ?~óõÿ­§ÿ¼øôddž7ª…«,@) €!òvaí)I‰i¾–:‚2’¢Œ_!ÎÂÂÈðáë/†O¿0ºxóË‚õÛ×¾|õ*íýÅÝ¿@zŰ/ ¯¤b€‚•_° ?ŸHìíóGOŸ\9±ÿãã›ë®ý‚¬ €0 |ó‘@QWIEND®B`‚HaCi/html/Images/searchAndAddNet.png0000644000175000000000000001314312030622510016662 0ustar fighterroot‰PNG  IHDR00Wù‡sRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÜ  >4xÒãIDAThÞ½zy]×Yçï;çnoë}ÓÖ’¬H–¼[Nâ8Ƥ†0±™$• B• UÔ ©a©)jÊaÈ *&¤’b $ 32ŽE6'rìØ–b¹m©¥–ZÝê½ß{ýö{ß]Ï9ßüq_K60q&Up»n¿÷úTßú~ß÷û–ó;˜ßí:vìæÛß÷¾{çøØè ϱ¦²$.§Yf'q’…IÒëtƒ­úÎÎó=öØ™yÿÂý¿‹Åãþð‡?zÿ}oúA­Ò±–Q[9è§©R¤a§>LYªF«SþÌwþêôéo>ÊÌñ¿("zåßÄ»ßýî÷?òðß>xø°·¸ÝE¨J“û056‚rÁ!H3Ô[}tu$­uÄ­-­mœ¿0íå‹—ÿÔÒ¯A§åÇý^ æ”™•¥üÏà·?úÑß{ðG~ø—k¾FKÙ|ûñ#´g¤ˆ( ÙRn† ÒTƒŽ´¤$J©¶ÓAsó*¢ê"6×WøZ;½èí;¶ Bg™€ÎR=fÓÚY_ª­F lv²,]­./¬Ç}?ff¥³,3F«ï À#<òÿñ×þÃGNÏ-áà-wáGîœÅ…•_\m0H¶Lœÿ h­A&¡È÷ÑŒþÕg±±²ˆ‘=‡j÷¿õíM¥•Úî…þV½Óï÷#£´Q¦Ó8IÓ4MÒ8ð3£µ& Ž‚v˜ÅQõÚÝvuóÜÚÂÜÓYG¯ àž×¿þŸùãOýÝrµÍÓGNÐ}G¦ñÌü*7‚”Šž "Á¹Ù cÀÌÈÀ†¡´Ft„ßÚA¨-ì\yË‹ øÀ?ˆûﻡ‰“,q`˜Íj³×¬Ö[~è÷²~«Ž%A?2Aœf^v‚i’Pt×W/žûööòÂ…æöúœJ“äØàÀÑ?úïðäÈøÔþÈÆ·¿ÏÎ_C;TÚs]CR€ @ "1€™€1Œ~»!Úõ*QŠ­ Ï áúß~àCý©={ à r)¥K ÞWv‹D„ Óé é¢8ÍÒË«Û[ëÌÅsç/w…´Ë–%ËQ¯½±ðü7þveþÜ×T–† ?ò‘€ˆ>xÛ­·¾ó¶OÞß>% 3c¼‚k¤%!ˆ „ )¤ HI$AH@ ¤ K,‹à• LŠÃˆŒåÁ¯®Š(Sæ¦[Nh˶ «”Mš­âL©Z?Žj~5úIÜ Ý•Ù‰³dÿÔøämÇn:0>6*m[úYš¶4äðã·¿íÀÍwÜ»³¹²½ºüÎw¾3qï^ÿÉ@ËñÃGOÀ‚ÒW6›I¡T†""ÚÍ än'Ñ *¬¶çqt…‚ƒ¨µÚö†¸ë'õØø@D†!”È4(U T¤ŠE°îFYæGŠ×›QÔî«ìàÞ©©Çî¹÷îã{]ÛŠ/_Y^pçúåóW"¿»fyžw²\.¿®ÚWxèÈ ¾üü…DÚžf²¤6`"&"ÊIÃ: fì¾§<%Ķƒ¡ñ î›T˜ØÚÅÄ_~é’+¦]N3ƒ$’ÌPš1C`C`&ÎË0!%ر`´™’…ãS™žé|Ída©µ½Ýl×6W°å:ÎIá–P)O0ŒF3HÒáѲ0LÌ`"v#@»¾Ïsw`ù ›ˆ®¯daˆ‹¥éÂlÛ¢¹¹%ëè½ÇyïÌ0OÙ4â .zŽ%hÃO ÷Ím_›f7®¢ ™exšƒ˜9VÌ©””e` fä[ @ )…xÏ[ošž»²|ñô¥–©½´ `¬4ÍJJ+øa„0M‘iRQ’%ÅbÑe6 ƒDž£yð€*"‡¤ÁDXodÔÏǧ=ò,BDØ® ÃŒN`±¦Myˆ¯ç=àXƒYçȰÑ@¢˜Œ1æ¡7NMÎ-oRÔÝÞ€JüAØRiÚË’ M?(S’“T¥žG†Y„ arË A 0çQèÅšvCÇg<l‰ížâé¢K£5¤mÁ‘†lg×ËDfðVä­„xà~!H€Ø°!bW!“$U×¾TA½ Ö ÄúÆú£4ZŒV"ÑÆ+€4€AdˆòÏ$   A€ tbC–%Pò,êf@3P˜©`§Ó‡Êb8¥1@0„H‚H‚„„ "H䔡Á ™÷ æ’cPµÝé ì,vk·Õl¶tÔE»ï²C§f¨èGY·Æýb©<¤µA°€`‚à|Ž#ÈšQF`a¡¯ õx¨hÖ6jµ:T¢t` KäÆ O‚È ¢‘g8/B@°Ée ®¡¥šŸì´:>:g¤»OA¿Îo7à©5[=Jû¾Œ’Ôn´ƒ–ÒFk&6 &ì¾j&æü3kÚ0ÅÐhḎfLIZÜn­d 9HN¡’’Iòh@ÈçiWƒ5! gzdÄ–/^kÄI·¾‰ê…K¯Ðëõæjõ³ê£ÞWBõ[¥(Š ý8‹Â8 )fä7 ˜ÙP h&b&ä¼ Äˆ3@cfHàëÏœ…îÀ†1¶ï0;^L$ò\ê$ mxP‹TÊÛ ˆ†m.ÛKÕžÑõ‹‹Ðqk ß¼œ?¾³¹]ý´ÍˆˆF¾û‰^˜ L²RBC°abb‚fƒØ¬A`Ω+B'T|t Tk¶ÑZœC’)gnæÊè„$AP&Æ©—ÿP|ú¥Å©–ߨü˜ 1¨Ï"$h­'†…LÒÌ4{Lse @À\O<ñÄçÖ7·7Çt [lZ+Å~§º·Úètµa­ ò˜Ý(\t¸–àvãÖ‰Hܺ§€Ç¾x Ü^Gß8˜9~¢TÂÙk§èã/üñè´üúë~‘6ÿõW¡oîàÔËŠßüĬͭMBN‚ h2|ẖ›~¬:Ý6tÐXÛ­>ד’$Y>õå/ÿÜCïxÇ—öÏmZãìú3=•‰J¹?³wzHkÍ7f¸|nØR0 hÔ~üŽ"—,ü×?ý[„‹gÐëxÑžG+!\ŠÞ'»»°‹6dê¢ÒØ‹ñ¥qˆªÀf²®ºˆ¥Õ«bÏì>}Ý8‹ùÀXÁ;¿²ÕñýÀ l_ý#°¾¶öĹÏ=|÷]æ³û'cZ•ûQH»SÅ3:Žš©ý•ë9 –DH•¡T4»¾8:JâÁ[fèkVñ—öyè èwÛˆ£¥}„ów}žJv ûª·Àë{P¡+F† ©Âu‘m5±Õ˜#Ð[mPt@d´¹Pmë$ôëðëk¯äÿ«À™3gÿ+;+ódQŒc‡Žaß¾}¸rå N—OƒïgGÀð  2`Ø€SF«ÞDp1Á-åŸD2ÙBÃ~ªÒ@2ÞG4c@‚Ã.œ¾ wÎEx:D°¼4ðdþ@Ûî‡fg‡àcì½õ]‡ÊG]xüZ§6¿Ý3í^¬Ùîg)lÛB¡\¢beÅÒ¼R`&Ghm¯Ñß|ê§ä[¦ïÅìì,Ø0V×Vñô‘§ážtä†c lm·bÔTtÂŽW%VÏFe§‚áö0,ßB˜„èïôÑ­uÑ]ëVs å± ¬Ÿ¹íÈð}êíoó˜7ûtS]ýÜ‹«…ùjT­”Åm‡o.í’HA Iä{f&צÊä4ï»ÿ!óì·þN”KeLNMbjr 'WNbnlÎa"OâF'Ç%FöŽ¢B”·Ê=¬ŸÁ$œ1 61„%` °1,®ïD¤u‹öÞöñ:pâÍcÞìï®â‰ÿ|‰þêRS_+I=œé$S*‹H1ŶB0Älh0A:6Û¥ÿèCšÎ¾„çwæÑiwP.—1áN`òâ$Lh ^ùCÃb‡7cúÊ4h‹ôø™|ò‘P X€CØps…2àx‡&GŠÁÝ…éotqñ+UÌ]i;c£«$Êh4z~[ÊÜi`fCà ÀNwЭXÚ2<<»‹±P_@»Ý†c;˜¨MÀl˜]‚$ ¥ºYuYGÏêA 7L&,@†rÚƒ,lˆœB…"`LÙ.â­Ï7ñœ% …„ÔåJÓ;¡dS¬A{|lxRJá0J+¾zñŒxêåç×]]D8º ëÀš’ï/Àj[¸|ú2†æ† YbÉ,AAÁ6ö@Ô}Ö¹dÆ4,¯lr A˜¼È¢A#‹}@Ú¨‡Ôÿ‹ºz24¶¶,HHÜ®×£°·?Ë(X½z6Ui£°ÒyR¬¨¯I1Ñ!zSŠÞ”uPtJ(š=pú.dK -¸·¹XX_@¿Ñ‡¹×À›ñR ®W#â±`Àà†±Æ\¨…F*R¨‚‚k¹Rô;€ãö“tJ,uu}¶bO ¼1êÊÐ*…Y$ž;û ·ùºÓNéí%”G<@€ú„B§€ÊZnèÂ$‰I‰ J($”€ÿÃí»y#óh ®â:‰ 0€Ðš5”PH©H‘ˆ)¥ÈAk (À‘6àó@FÕ¡¢òµ¦Úž©pEÚ´»åµ0<´m‚Ò¶TYejô¡Íµ§N aP³ ³ö,D& (RˆD-u~àU ŒvÕ ~5`€ÌdÊB/DŠŠ2•Á¤fpÊÀ¬`ˆL*LÀ’€Q«ˆzãëíäÒÁ‰BcÈ–ãÊ v=ž£xhd ÍêÃGïÙÙ³òV¿¹ôt%é'Ø™ÞÁHqFšëF“0tÃà ÀH%¬l0Á˜ÁºHŒ2ð¦§A]†è¬À…QŽ&à&£ U‚I*;q8|ªuåê'ó'  WÑïn…Q<¾¨Úmr*7ÄÅB cKÇ•·¿ùW¶žùúüQ|±SÞk¹(:Å´É]*dV–SÁMydSb*›ÊË ¬sÎK-áÅ ]êyƒ±Þñ`È9ôéŠ}`«ìîyP©´cÌÐê“¿÷Þo¾ze„þ_ ßþ™õîÐúþ§S±hLó@9)ÛÆuÚ‚UYNÏú‡fŒ/7þ¬ÈFÛjƒB"$v‚ÄÉ_•¥‰œ&f”×Ji"LáÙ^s„HÇEêú˜tïúÂÜgþǯ¼Ö M€³‰6çÐmþ„ߟºÒq7ï˜tJRÀeÀ×Rzbbõê=©JùŽu{ã=u(énÊîPÓAš¥Ð©†ðNŽ+¨¤7ÓI¬`SZ3ÒIK)Ü’;h&œß–eÅ ¡¿ýÆïåˆ)odr ÊXB£Ózí;V{Qºšš´rJ@BÐèð6 Á®¯Mëïùãnš¤Èü Ù’‚ór•³ã(=5=/<ÀWþ}:³ü¡tæÊ{ª¥'‹¿_âGÂN(¤qšo_qSB@˜2#þ±÷ýþßïÿ#À» ? ÒÇPÛ:È#•—=kÁ²ŠrïíC à¤Ús}ö'z)õÊÓ?ÿð—‹õžt‚-qi;B·—éÊ­÷Æ¡SÖîÕ—ÎE+óß\?s*:›‚ì:Ó]o$uR8ŽsBld,TðEQ/x€Ï¾6ñŠcV)úþÿAuó·‚Rq{¾c­—<·8RÀH ѧJi³˜ô÷Ý93¼÷§G&n©Üï…·Þƒo¬W“kÏ_Ö®n‰¬ÝŒ‡¾ò¿—¿õÄ_+`@@@j‰ñÓi'z—,Jhèëåt·€E¬:ú½ÖÍß[Ä«ŽÍÆÔkŸç½783×^¾gÊ»å‡+ü¶Ã‡&ì›Ý?ìXXëæ“Ï.öž[ܦõõª°–ç7ìsßüR°qõÛ5¿{@w×ð])dvâÇ>µÙøô»œrJ*XÒ `t>:XÚ‚U¶‘¸=ìt/Üú=Rèž]0úql¬«ZfötV”Å}Õ0nž®µÖž]ÞœXØlÄÝÕõP¯/× ×Î?[{ñôb¥×ïíjø¯|ô•ÿöG§œ‡Ç/+?¹9qlH/ŸÉØ0v •.­—xË/|Nœþä{ÍwðO¦·”úC¬®® Kê\2Þ}ï…îgÕÒZ{óg›cë»v}}±»ve±Á¨hæýò†Çÿɯ1 ÝùX?øÖ£VÑ… †6Zkd&b@x„¾Y«6–îðÂÿ?€ÝÜ y Û[õÞúæñžíô†uüùï¬ae¾ÞåR ׯö±±·®­6ŸD9Ôh‰bŽ‘™ J«|Ö *i±…kWžºÿûp]«àf/ XR6.ﮤŽ+ܘ-_ó:0ö_X³f·n–¥sÈ €& “€b0(7ƒî|åê÷õm•îkúgô§ëÝ/ý Ò !3vÆÝQtã;ČLjzè>uÿNWùôw{ÎÿÎ’qîóÝMIEND®B`‚HaCi/html/Images/search_small.png0000644000175000000000000000177710704777334016406 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<‘IDATxÚb,-ícäaâàæ øÂ!Zü™CD•Ÿáó7¶/o¶0|zÛýãû?ýf`bbd@ÄÂÄøŸùË–ª‡ZõÌòŠÌü"ì ì¬ ßÿ3ñüýõ-‰ñÖe·¿w¯$311ìbdÄ4 €X~þeô~# Û(¤¯Ã¨,÷›¨ùÃû¿Øø¸Ø%Íe^ý8íÃå‹vÿ™Ÿ10 @LŸ˜xóØUÔ¤ÿ3¨‰31è 01ð030ÈK½ÁûŸAT‚™AÒJGù7“ß÷¯ß~üø‰‚ˆå› ±¸7Ç?! Ùýg`eedæbdøÁÃÈÀÆøŸOCšá+‹ûÇŸg0³±¡¸ €Xþ°ñ²s±ýcøðñ/ùÿ¸¸˜¤ø˜€PÛf&†_?~ò|üô…•Õ€bùýþùFVfο¿þ2üêââºè& Fa@üëï?†/>~øôù+ËÏ? Œ@WÁ@1½½uéè·wŸ~303üüÍÀðU?À°dàbY ·®>ÇðèÞÓÍßšÿ|ÿÀðëÛG†Ÿ_?2üøò‘ €˜y˜…12±Fj:°r²3£‡…¨Y¨ùõûO s§,gøñý÷$'©[ì¹O0ˆýÄÀûóû× Ä,.®þøý½Û/¾øé(£¥Ì.LÀ¨âj¾qõÃÌÞ ÌL :2e ¾mVøö›ñÚ÷Ÿÿ¿}ö_ÿA¾ÿþûùѬ©ÒãÌÈs#7 öëîðäÚß®Êôc#ÿÄR"!CöÖï4ûþ^ÿÿðÿˆŒÌÌ@æ¿ÓŸ=‹`üŒ ^n†Ï^2|z÷‘AKõ?óõÊ¡òA:ŽÑ l«—ú¾:ú~É‹¬ @­oäD”£ˆGP€ƒ“ƒá÷ ¿~10(ñbày}âÖ­Ûï¸Å•d-4,x>ÜR»ÿð«Æ«¯Ì{a0f˜XX8ùxy‰Â_÷‹AN”•çËÍß/>üÛyêÈ-~iy }ws‰ïwÔ¯Üùª@L Dv6æÿ¯Þÿ)ën\ÛwñÊ^[&ay€ba °²2ÿ~÷éoeWÍ*!·[Œæç ÛÑCaP2ú…IEND®B`‚HaCi/html/Images/showAll.png0000644000175000000000000000700610670113276015340 0ustar fighterroot‰PNG  IHDR00Wù‡ ÍIDAThí™kŒ]WuÇkŸÇ}Ì{ìÉLlǎljó$Í£”hB¡$PTª Ò D«ò¡Aú B%ªV•Šh"P©ò!ШICBˆ‡Ä$„‰òÛqÆ3öx÷q^{¯ÕçŽcJì74BbKKç~8çžÿo¯Ç^go13~“‡{½ü_Ço^ïñ[€×{Ä¿Ž?ýð‡¯ußðG«ª kÓn„.¼õ’ß]þúW¿ª¯å»äµ(£ï~ÿÕ®¢õ“““—¬Y3öÞ‰ññ W­i4M3yQ”Ù Ÿ½ð¾ÞÌÌÌ®ÙÙÙoœ›»ûÔ“'gïþΆ×àò|ì¬ß9÷œÎ:ëôwo™šZÝêŒ"QL0¡2(¼§ªj#(¡È˜Ÿ›å‰éŸ.>úØcß}þ¹gÿ~ßOøÄÿ;À.ùÀ†wn»ìï.þ½ó¯™šÚœJÒ¨‡ŒÌwk«z ª>x‚f‚h“DWU-Ên—Gwíò<ðÀ—ÍÏÞ¸÷Ñ{öüÚ{ì)®ÿÇ›ÞüŽË.þ÷ß¿øM§GÍ6ŽŠÀr¾—_‡’"T¡"¨R©Ô¨‚‚¢ªXˆHÃ8I¹ŠÙÝ»¹ûûw?;óÂÞkv?¼ýþ_+À;?rýŸ\ýþ?úÒÎ>c¼˜ ‡‰c!žn1‡EÈp R£òJŒR•*!(Þ¼8m2Rm¢·™;ïºsi~fÏÇ®þãË¿ñ¹þæWöªþüúÏ¿÷í—½í¶SNÝåãqçȵËÌÒ.JŸ‘û‘ƒ$ªŸ©g¾_úáï ø†ŠzeÔoay_Éﹻ̖\ýóoÿúk ðñOýóù¿éüí'Om™ôQJ‰#ˆÃ\Ä\oš¹ÞÓäUŸØAì'€€ªá‡VŬ‰¸¥7òr@Qæ”U‰øˆ±ê Lÿø ö=;} êÍ¿ëù¾ùðkpÙŸþõÚ÷¿wÛ=ëNÚx®´Kz¡@ÜZ¢ô$ŠèWxföûT~‰$""fFPðj¤ÉÎXÿ.*ë°œyyI6ÈéäY…e9 33üìÉŸ°<óì3®ìžÿÔŽoõŽ¥-úÌg>óŠû,þíĆ>8/{Yª8˜`àh‚®ÃÉ'œË £'±œÍ³˜ÏSzOá+ ïÉ}Eî+N_w9­Æ:²Jx%¯”¢RŠÊÈ+ePjrU TÅx‘ üºñÕ÷œsæÖ£j{ÅVâí¹þıµc×Ô}b¶·ÀbfÙÂB¡Ê™W íÆFNßp9Ý|@·èÓ-,¯_1ÚÞLî<e€"@¡P£Ðó%Ë¡O2ºšÖê ’öè_ÜðO7OKß+¶§LM]WÄÙªùbž¥2gJF›ob)|äi¤‘SÁÉÝ"CD 3f¬[}¥ÆäA‡‚BRÊ TãÅÁ,OÓ‰60Ò™"›äý¿®?.\xå'FV­ZõžÕ ƒ.‡]3¥“,æÂ•Kd0r¯<3÷eCSJU‚Á†µo¤ôõ}^ëêÄJú‰Ã9Çd{c èËæÛ;ˆGZDö•ç^ñg­ãétΨğ1_äPÖc!ëQøU ªˆ*jõUWc®»G÷|—0œuo†HÂÖ/amçLÊP—Q?|V­Nr0D ŽZŒ5ÞL+ÞŒûôW=GÔhnÊ+=︂·?Ì¥ˆ². ƒ.‹Y&‡âë©V‹*‚òľÿ&+ûøáÊÔ8gÓ•œrâP¡µ·ê•Y1STÁ Ìêº+ÑŽNAÄQ4`­"EÜÛ¦ñ˜9à³—Ëe¬ËR•Óóžñ‘µp­[ï•Ò)"ŽAÙëáT„Õ­“=‡¼R¼R‡›_i)Œ ³„¡'"i‚8Ä9B»‹›g€!볋Õ2]ïQÕ}ŒÕ!(!¼ˆ¢1‚îÁD@Œ5ÓÉ+EËYêÖÂû0|Þ{ÒjWPÙÎ9b×@%Z4G ¡íÛ·KtýbwÀr> XÀPzå³/‰÷/õ4•Œ¶N!˜Õ¦F­%÷Jî•Â+¥W*ïñÞ†_ñdm5DÊ8‘D4d„Žn'^5À¿~íNU[ý,ÉÅÝüéÃ|¨Chbuó V5OÅ«¡*8·š¼Rò*P!ëûk[™„ê¨1â62á4ãQ@F^5À7ÿíÆ`ÆAUÅTL©´Ožaeö«@UyBpl™x«›[QƒÞà E™ÿÂ=U{_7u‡C)(‘(± iK\÷UNf¦óØ9`:¬ö€˜‚9Yƒ*x¯8ñ ‡oFÕˆ\ÂÔøûØ{è.žž¹V2A+ä„ÎyŒ¤k±ºâOæ‚j@PbŒXÀ¡8”HÀ!/¾jÔ¢toZ¬EBÝ'Œ²6z ÞÞëád¬ª:<ª*P=±aõ66¯½UÇRƒb‘²Z £#=P‡jD(1J"†3#àPDì…ãó€ê¤hüåDö6\ÓH6k‚…@aËÌJ7œ3"'ˆSD„Nz*§ŒoFÍ#¤Ãܱaù­B`ŠXEbµ"S‚zœ°`Áû—b'÷iU,HÓ¨&I¬AB ¶€X¦æCðžêˆøª /¨ñU *¾òôòýì^¸²êcÁ@•^ùÎJbg¢%bõÕ’bÇpÚæÉõÕ_ ðy % Q( gЍ!ª°bá%ÓÐàk°*c9{Žƒ½GØ}èVå^ªjÓœ~¹‹RŸ#²œÈ<æ Î<¾*~ò{_Ý}ÔI>À×¾ð)=÷=Ÿ¸¹ÊïŠ} y‹¤ÓÂ0ÅLg *õ‡Œ°#ã+0×ýË峨p¥NıXn'–!#qŽˆQhA(0- ¾Ô¢(n:–ÆWüxçg};”Ùý>ëQf]"-IEIQѰJ‰YmCoˆ*˜´Oö’JL#NiÇ ZIJ36š±§§´’˜Drb+qVA(ñ¾Ü‘¦ñ· `zzš«®ºÊÝúåÓ|yîÆ2ëyo‰÷ˆBAOcÑ Z‘œÎth†S!u QB+Nh')¤A'n0§´“”Ñh”¶µH¨_ en3{ž¿ñ©»o•mÛ¶E;wî|Y/ûI¹}ûv>úѦÓÓÓí^¯×î¾øÌòÈäÔª(IÏãDFÛM"çˆ]T/4P×n'FD}uÎŒD"b1œôY¯g"9ñøLÒHˆ\IÓ5 §‘j© ²~׿÷ï»å‘;nùzY–Ñì쬻ãŽ;,„ ]tѱîºë.>ùÉO&333­¢(ZªÚ¾j÷æv?9rÂæÓ·yl´M9"'C!#ê+/YŒ‘`ŒF“ŒÅS¬‰·0âFiF Fe’“tÂzRM‰´"ë/3;óÂοsËç«|àÍLT•¢(˜žžÖf³©\pÁa½¿´+qù嗻ǼeYË{ßVÕ–™µ€v226>uÑUÿ0~âIgž±u3q³‰¦Ä‚9C%`&‚£I*ã$Òz¦^7uXa‚'cÎ iÙ!ë÷xêé'ŸÜ¹ý?nè/ìßd"’9ç²8ŽF#Ûºuë`ç·7„© -..º‚˜™æˆœ™¹¢{(ßýà}ΟÿŽGÎÞrÚÖu2h=‹aÔ=ÅKf@E›µr Y…«g AÁ¢¬¢£«ëu`еÇÿÉCÝqë¿äÝC}‰Ddåý¢ªNU%Ïs ÓéXE&" 6Q3Ó|ùàÒÏïÿÆ—Êþû}uΕ'ŸÝŠâ±ì%ñR—OçN*nÎ6ÓÖõV› !ÌcjìÛ?ëyèoOßwû7B™w‡|¿9çÔ9gNçÎ~)6mÚd÷Þ{¯eYæTu¥Us€ˆˆ˜™˜]~ñ™'ç^üQ¿7:Ú´Éu««£VÔ"–ˆ$çH£:ɉ{8<Ð""©õÌÏô?þñ®‡ï¼í–/ìýéý?´àðɴ(Šò$Iòñññü‹_ü¢?ù䓞PW¡ë®».››kEÑ !´Ì¬©ªM ef 3k ‰âÖØÆ³N›˜:û­›6oyãº'&ÖŒÄI+@£ JAˆ}—w þÀÁùƒÏ=óô£ÏM?²ãÀϦŸ2õ9PˆH!"ÙðwîœË¢(ÊÓ4Í'&&²Ï~ö³Õ‡>ô¡_|Ô­Åééi>ýéO»;w¦½^¯UUUCUªÚŠO†™%f–I”¶ZcOß²z⤩5kÆ×¶;µIÚ蘯zyÖ_X^\˜ß¿o÷î¥}O=ï‹,*©D¤J pΕ"R8çò8ŽóN§“_xá…Åí·ßþ²GS¿ÒÞèµ×^ëî½÷Þdii))Š¢BHU55³ÔÌb3K¨ó):ÂŽÌê•—¬Ät<àEÄ‹H)"•s®Š¢¨HÓ´+.½ôRÿ•¯|å˜gj¯j{ý¾ûîãæ›ov»víŠãÁ`„âB<‰ÌÌQÁøß"¢Ãœs>Š"DZo6›ÕØØX8ï¼óü5×\cÛ¶m{íÏ^nÜtÓM²cÇÙ»w¯ÌÏÏ»,ˤ,KñÞ»•ï\Á9gq[š¦622bãããºqãF»âŠ+ìꫯ>n¯É)åë9~ãºãþkoeãm5¨vIEND®B`‚HaCi/html/Images/showAuditLogs.png0000644000175000000000000001037511776645423016542 0ustar fighterroot‰PNG  IHDR00Wù‡bKGDùC» pHYs Ö Öoyœ vpAg00ÎîŒW]IDAThÞÕš{\UÇ?ç>ú=Ý=“yOBf&¯I2!AÀBªÁ]teŨå*«¥»Ö–능¶\·Êu¡`Ep]TXW@Þuq)`1Nò0É$™$“É<{zº§÷ÞóØ?z¦g&$It«öTýê>úž{Îç~¿ß9çÞÆþ?g!ÿ©§ÒFŠLô|F¼pûÍØ{þ÷9Kþí±g;–µ5ì[³¬#œH$¨T{s]!ÄÜfŽ”ÖLMÉLd868ÈèxfÂâí7Þ;€³VàÄÞíGRW‰š°ë:¸®»ðÆm4%Ï£&¯=ØüqàÚó°ÎöÂM›6éL>ÿXøä§ çÔX8"Q—NcÛ¯]õžïþèÑÏÿŸ£ò¾ï/¸1!ÑH„x,†cÛ¬\¾œt<²ùû=½î\Ä23òbÿÀ`ñ‚¶Ö˜çûضÍÃ?¼àF¥R¬î^G8æÊË/³žúù Ï?ôÐ +>þñë.­1fAöãg~v°X,š‰lÖh­ÏÉÆ'&̶×wcŒñ}ß¼¶u«ùÞ#O<¼Ð¾c¦À4r!0º’ž}öY2™Ì‚îàŠõm¨¸€ã°¦«‹Ýûû6Þÿ£Ÿ³ù{ßï¿ëÑGCP´1S¶m/\¼éR,•xíõÝ¡­©‘%m­,Y¼˜†dbiÍØ|¶÷zËlà‡b“v7ЏFÇ"¾=uïêßÿg1߈Çcç µf`h˜#ÇŽ³ÿЇ°m‹•íK9vâDW,þòg>3q6÷:£{¡ö麶ðÜó‘Ð2@`Ù6£££<÷Üsç¬@È®¿ê::ÀÖ×w 9±p"ù%àÎóˆMq¶¤®œ0à™ÒõXBÐÐÐÀm·Ýv^eÏcéе¢“×wìäøXfýÙÖ=#€”ô8¶C̺X–&êR©JEÇfÛ¶mìÞ½û¼>ºq#ñXe„®««e ÿ·W?ù7ÎóZªR1l`DkF„fDö|ä3ø¶ZS¯´ †™IÛ¸iae:,˦§§‡žžžóˆE#xñ8‘Pˆ±L6Ú~á¢ëR h”2fh¤ÄÉ¡â7/¿½Š$ ª. ª.K§¦õ9§–îînzzzMÏ“ ‚I‚À C%ÞaÛ"¶ÑŠÄÜúgV@‘’ Œ4`À`£Rã8Æ,Ëú½øÿÜ ‡Éç&qt–@šiõaÆŒ¥ˆŸ€T$mi0Bc€ sË;–ÿ^;|jq‡þ¢ä‘R#圇¥Aê³T@iRZ% †Šû\½¸Ï?ÿÁ‹‡Ã äâ4ÔÄéln$±sdŠ“¹¯ç=nùâ–%qþõÒ+@¾÷Â(ÍÑ]Ô×X¸Ó.µXÌÅ÷ RÍ5=°9$€ùù ¢9ØÉ½_¾…÷þç¶HÄÂÜÃJþhy©°C¡P`ë/åÊžŽM§bãEsÏØ­Ü¹ÇçÑ£š°ˆñ­µk¹~l‚{Oð© C¤â×9õ Ž˜·9µD£Rß N­ê“F`Î{¡ÇF®HÜqãŸr_ïI¶÷’¶ {þüRþdE5nå²R©ÄŽ=ðøÊ%Âù×øËäf¶^ã`žb«©cÝÊe¤›¯à7G Å€À÷Ñ*˜c~Å䌛þ=«<ÛR¹âF36#ÐÌŽþÐ ïK,êXËßmÙAÀƒ7­£6$pÐh]1Û¶ÉfƒJç}Yq‹‡è.|“¯¬ ApÇÖ ZW®áêîüòd;9Ÿ²ç£dp Äé€* !·;ž§‘Ê •FN´mÌ6W_{iØüÂnür™®iâÊöZ´ïA@A6çW:ïÉÙ­Šà6|’/®©euÔp2ïñã€Út;ܱŒ!_ðߤ™AŒò‰Å]<_OOìfçF* 0@‰ËWw¶[½}'!ðù»W`Ê‚ @J‰”r`Ò¯>ýò”Ï¿š„ޝC¬‹dÈâ¶Î>½£%’É$͵5¼1';%)ûR¾À¬%kÂÈ ÈšкÀÕ ŽEÜÖÆ¦f±w` ´amKš©Ì¡Ph~Ø Av*Or|¢ÄÝ[&ùØ_Ü ‘%Õx|wS|ŸÞáéeiškâp˜*¤£†°-N¸§äÚÚ#£yäô`ff†ï€ËV-éxc`¥ Í©RìÛ·––«éσh~úê0OïÜù­ѹ¢€ÑÑQÊå2«[!ðÙ?ª %›hˆGØé»”=‰çiTØ lqæô3ÀA+Ôì‚Л^4åù „Â(BŽïùŒ“H$H§ÓU˲ÁÓ»BÜuï“´¶-©6V(( ¤[À¯Ä2‚|©Œ#$R*©PRcŸEçj“6ÆPYZ ¦?ªÌ>ý*Àÿèï¿·½~½pl—,ªcxx˜¡É)Û90cŒQ³¾ü¶EP›v›ð±­ŠŠBa¦_)V²ö_Ú×?X¾¤³€_>@º¡…r¹ŒïûxžWµ™¬47;i­1Æ0Yxxûqð}.iŠ188ÈD>G‹;†0–ña:EžÞ‚Ş6%кʃ1Â_·n]zýúõñîîîPeˆ-_úõžþ‘;®Y‹ðÀ‹{ÈK’©åry€ïûAPM±J)´Ö}ÉC½ÇØzx””­¹¥k{ö$æõ * è€@xþŒÉS¬rÞŸc‰˜AO¯ÌŒm„/„h,—Ëi 0ñ‹ûsßÝòÒWk(ùŸ»öBn½ï—4.n§&™¬*áû>}}} U!‚ ÀWš¡\™/>Ñ Ï?ß°š½¯ò‹Ý{évv¢µÄ2>JJÊeI©$)•%¥rpZ+–ŠeI±,‰FLÅ}tÅŠÊPJµÛ¶]kYVe’cŒ1©Ý?}ü¯xêÙ»>Ðc–5¦81Qà’M3éÔÒÖ¾ ˲ð}ŸpäÈ‚ @A ¶ìàâ¯?‰W,sýŠz®m¶xð©-,)½Œ«ËØø8ÂG>e¯Ò±Ri.È鬨€¦F~`‹•…m®ë†J¥’ªÎFûúúµíåMß~rù•~þ½7ßósä¸êïŸæÓ׬áöw­bUk-uÍmD#Q,Çf×@†üÙ+ütÇQ°l._Ù¾„ûïý²Ùý¬ uɨ$’ØÖœ©ñ›vÎ\Z[ —‡BFJñ°L)uBJ¹wÞ÷úúúšè†ë6~êÖ~ó³7ßüÚãÛøÎÍy»´¾†’/É•ªçBŽÍ]7½ƒOô´rÏwÿÿÞ¹ K/±}bn@Âñ ; »2Œ Leíl¦÷«3ëéT9÷7!fô£¡á_ ®x@c½áyÞ¶ T„¢««+¡”ê$QÿÎÖKßó黿ðñ GUÄþίö°½”#£³Õ-!XÕ’¦§£‘/¾oƒ{{ùÆŸ)ŽŸ<ò»ºlß 0X6ÊÁ”Ê·,¡¦3±žNÆc˜ÙúKm* SÛeŒÑ£,ËèL9~$ë‡ú´Ö‡'''O –æX]]]5ƘV!D«eYÍŦ5W~ìÃ7Ý|uwgjõ²v+’H‰GLj†Ö¶¦äw‡úõÏ_éõ·lï;äíÛâ2ã€oY–§µö,ËòŒ1Ò²,=ÝŽQJ!„ª[­uõXk­„Zk­,ËRJ)íº®/¥Ì+¥&¦¦¦&ËÆ=OÅ‹G„‰X,–°,+iŒIèHjQ±ö‚?šÞP[[»äŠ®¥­^©î=8Ƀa„§F_fï Zë’Öº(¥,Æb±’mÛžmÛ&—Ë™éA¯ê³¶mW÷Ç™wÞu]0<Ìð ûU#m Lcýy…YÀšô’G/.R¤ÅX\ÞƒS,ÒÛù]×éˆIh\8î¡(OÁît31·J÷§0ë¶îa2<‰Í®0<áNëmú¾†‰$<\>âAó8QvÒ’DIa.biéoÀȶQ¦«Ęšž¡á\ïgàX¹†š—ìpcJ9€ ‘41„;kÐ|ö&m]w3°XÔÉF®ÇËüò8f*!L,+ɾâƒÄV%{;²g4 cÝ­UU% òðÍG÷na{Õi,¢JŽ éÚ¬A0\ðù|„B!¼9 ÓsD£N;8í# 廊H¥Œß!Dæ·ÿ”ÿ´Ÿ@ ‹+W›xÒÿ%W%ßíÀ­ÈDWÙ†ÃåÁV¦®_’¤ê½¬,Ëì.-¥þÐ)^é+Œ õ™  ¸ !»8qé:¿×öoÌßIEND®B`‚HaCi/html/Images/showSubnets_small.png0000644000175000000000000000103011024623161017423 0ustar fighterroot‰PNG  IHDRÉV%bKGDÿÿÿ ½§“ pHYs ÿ ÿ4bš‚tIMEÒ 6D‚ 7¥IDATx¥“OKAÆŸÙuU¨.B‡@è$D÷.^:ºtèJ _ [Ø'êê!I‘îAÇ.i7Ò¡(2ðO梩«N»ëî4ë!\ÝíÒ óÎ<¿÷yg˜!˜‰\.Çêõ:ºÝîd4›Í‰" N“ù|Z,™år™%“I‹Åؼê«@>ŸgñxÜtlãüê–ut?B¾"‘ˆ£ÆÑ7•¹aª¦±UÙæé½««{¦+\pðxú˜á{l"±»†>™Üè ]mö‡ÀÃáo'Â4LM‚ÕÐRPJ±±B‰-ãúd[‰½iédns6‰¢(Â+š`’E÷ 34ÑÖÕEü¶6˜’%Œ ¡¯ èR@VE¨ASsØç`„DÀˆŸ[¦5*Áä×öXQÁæe¿°Jχ»w/-†ò—€çÃkMƒ\íÆ'xewçºLpª@ê0 àrç Go!òÄàm{¼6Ø5) f6›eÑh”“Î!:-[O3MÓ ª* ƒÉR©t6«µ‚ïþûWÍ:¸å?vÏ*Äx:iIEND®B`‚HaCi/html/Images/split_small.png0000644000175000000000000000051010704777334016254 0ustar fighterroot‰PNG  IHDRóÿabKGDÿÿÿ ½§“ýIDATxÚµ’±JQE"Icºðòvæ;Þ‚2_BЯP++ÁÂ?0²11(bÄXÈLã!)£åµÙâ±¾Ín§›¹óîÜ;oàBëÔ7R¹Í!·¹ÓGÑ«<Ó4|îùê¿P.•½Ìß_KŠX¸šÏôº׳&ñ˜hôÐγ`ƒ Pô6tåê¡­Æ®U‘èóc¬ó³SzyºÕMØÖŠßñ“Üß…ôØeLu²î-41F•ÊV ´Š<²‰Là"©Ÿ—ÖÌ”]¯ïû{ÛI£u‰S˜Ÿ`6k³i\óR¶é&‡GÇÕ,öU˜»8¥äÁþ?2ZqNDfO£IEND®B`‚HaCi/html/Images/submit_small.png0000644000175000000000000000156310704777334016435 0ustar fighterroot‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<IDATxÚbüÿÿ?% ƒqp @ðÈ•~µ-“:,Ýׯ\Nf‡Ø6ÔZ:¦ÄxÕ±Õ˜ÒÿÇâ0ìÚü rˆ‰$ë@Žýý;A]YjW¤µ…•+ß?€"Ú WEÿ11Ìv·Ð›_ëé(sñÚóî¼J –ÿ ÿÁð ¨æ% ÿþü3ã㜟좥 ¡Ìбû<ñÓ2¹™6‹ïÕ_¿ÿÝüüøÃS½ÿþ³þøû'UNM°§ÂǘÓF\€¡|çy†]Ô3±³Ìb`üÇ@L|ÌÜŸ} ¸ÙVþþóO¬“Nÿküå`Xææ¨4uj˜§•¸$Cãžk »ö>ïgûÏÒÄÀ‰=€bzvçûL 1†ì@+kF.†uÿþ10þùýÏ•WœeWj°AHƒ«;ƒ8CïÁK Û¶¿\Åþ›¹œ)äA¾ÿ0æîôûdqIJ þÇ×âe"* avW:J"3øþóŸÉЮ­é¸úý]ü÷üúýùÿüA¾ÿ0!ðíî÷3W0<÷S6.*#îüé±ÔÓ±ªìÑòYìýýû ù A¾ÿ/"ÿýôûòï[/'0$ÿïýê¿ÝÖ®¥ñàÿíMàüüù ö ôˆYHC›õÇã/ŸÿYñ«~UâagaøôÿÃé'÷¶.ýþæ÷S–&ŽßWþ3ÿƒxšñ ÌôŒˆ‰ã;ç?¶ŸŒÏø¶ž>ýžá9ㆋ¯î3ì\úë˯Û\1ÌÌ §ñ%0€bbþËÎÂÿؽ>ÃýðÄíç GW1ÿú~Y ¨y'¡ @ÌjFÀ(ef`bdüÎôCìÍm‹×yë˜ÿLûÏö‡áóo†ÿ,À ö#†ÄHiv0P­!3•Žð#IEND®B`‚HaCi/locale/0000755000175000000000000000000012475447615012320 5ustar fighterrootHaCi/locale/de/0000755000175000000000000000000012475447615012710 5ustar fighterrootHaCi/locale/de/LC_MESSAGES/0000755000175000000000000000000012475447615014475 5ustar fighterrootHaCi/locale/de/LC_MESSAGES/HaCi.mo0000644000175000000000000006103312470236743015632 0ustar fighterrootÞ•Zì ͼð ?QQ2£ÖTæ;9UUYåY?™  § ±¼  ÍÛâ æòû  . 7 = A H S b | “ &ª .Ñ &!S'!{!$!´!¹!¾! Ñ!Ý!í!"("B"ab"MÄ"##-# A#L#T#\#m#„#Œ#“# ²#¼#Ò#Ú#ß# æ# ó#þ#$%%$K$P$a$i$}$›$¹$À$Ò$ æ$ò$%%:%1T%5†%0¼%4í%0"&S& X& b& o&y&&¤&#¼&à& æ&ô&''8'*V':'*¼'ç'í'(( !( -(19( k( x( ‚((¬(²(Ä(Ö(ç(î( ô( ÿ( )) %)3)G)_)y))¥)¬)Ê)Ò)Û)à)ï)1 *<*B* I*%T* z* †** •* £* ¯*»*À* Ä* Ñ*Ü*ö*þ*+6+E+K+O+ T+^+ g+ t++Š+›+ ª+&Ë+ò+ ,',$G,l, Œ, š,(¥,Î,å,é,î,5-+9- e-s-ˆ-›-­-°- ·-Ä-á-ó-. ..%. 5.@.I.].3t. ¨.´..Å.ô. ///#"/ F/P/ a/m//’/£/«/º/Á/É/Ü/â/ ç/ñ/ 00 0(0:0A0Z0o0 ~0ˆ0›0¤0Á0ß0ä0 ù01 1 $111 E1S1(c1Œ1¨1­1CÈ1& 2)32)]2 ‡2"¨2"Ë2î2ö2ý2 3 323 A3K3R3Y3/w39§3Tá364"V4y4 ˜4¹4Á4 È4$Ô4ù45 +5 95C5H5 Q5[5{5N—5Xæ5"?6b6€6•6¬6[Å6[!7’}7Í8nÞ8KM:™:&¹:*à:' ;3; ;;E;J;O;i;ˆ;™;©;²;¸;¾; Ã;Ñ;Ú;ë;*ü;'<@<V<]< c<n<v< |<†<< ¥<¯<²< ·<Á<Å<Ú<Ý<ã<ù<=0=eB=@¨=é=\ù=V>It>r¾>u1?o§?@ @ '@ 1@ <@ F@P@_@ f@q@@—@®@¾@Ø@î@÷@ü@AA)AFAbA/A5±AAçAp)BšB ¯BÐB×BÜB ñBþB!C,@CmC=‹C|ÉCcFDªD±DÉDáDñDùDEE 8EDEKE kEyE’EšE £E ­E ºEÆEÚE+êEFF,F5F#KF$oF”FœF³F ÇFÔFïFG"%G1HG1zG5¬G2âG5H KH VHaHqH‡H§HÁH"ÛHþHII%1IWI2tIA§IRéIN MNZNiNˆN$ N+ÅN#ñNO(.O&WO$~O£O¾O/ÏOÿOPP%PL9PB†PÉPÙPíPQQQ!Q!0QRQbQ vQ ‚QŒQ¦Q ·QÁQÊQàQAþQ@RGR2XR‹R œR§R°R,¹R æRðRSS(S ASNSWSnS wSS”S ›S ¦S±SÐSßSæSïSTT(T:T NTXT tT‚T!¢TÄTËTáTðT U U2UHUYU+sUŸU¿UÅU?âU'"V$JV oVV"¨VËVæVõV üVW"W=WNW WWaWiW-‡W6µWSìW @X!aXƒX0¢X ÓXßX äX#îXY1Y DYRY[Y `YlY$uYšYM¸YhZ!oZ‘Z®ZÃZÜZc÷Zb[[¾[ä\\ºA]Qü^/N_&~_-¥_)Ó_ý_```"`&B`i`~` ‘`ž`§`¬`´` Ê`Ö`é`3ý`$1aVamasa |aˆa ‘a ›a¦aµa Ía×aÜa âaíaña b bß༵±oÕþÆ:8Uq $D ñÞ@–D·mÄT›M(ÿ’õH/'l~Eèáä¢1>•æ I!¤ÙJìN\Nž®Ã‰¿¶]ø½yFªU7ö‡¹&=ïAse{-‚ÖG—ú¬å¦có©BϰíS# û¥%z,fC5­ÓB*kÔ×3L>|êÜ*X)9'Ò W_š$ð%2Ib^16-a=2ŠýRâjg<˜ZV´W ÉÛ§Ÿ ƒ #é…v«Ñ)Îüç(3Í+V;Œ†ÚG‹”ã M€¯‘ˆ¸¡ÊZ:ºrpÅÀô޾³0<„9»/uJ™?RSQiÌÂH7îx;.Á[&K?÷Çn"Ø@`"L“KÈ!Ë .wE8 A}ùÝœ0O h4O£d5¨4 QFYò²+TP6,CXYÐPët# of assigned subnets# of available Adresses# of free Subnets with CIDR '%s'# of free subnets%i IP Addresses found for Origin '%s'. %i were successfully saved under Root '%s'%i Routes found. Saved %i new Inetnums of %i found%i. Possibility%s is a new Value for inet6num Objects and so it's not available for inetnum Objects%s: Status (%s) not known%ss can only be made from allocations with a status of %s%ss can only be made when there's no less specific inetnum (%s) with a status of '%s'%ss can only be made when there's no less specific inetnum (%s) with an 'Assigned' status%ss can only be made when there's no more specific inetnum (%s) with an 'Assigned' statusANDARP EntryAS NumberASN routesAbortAbout HaCiAccess RightsActionAddAdd NetworkAdd RootAdd free networkAdd networkAdd network %sAdd rootAliveAllAmountAudit logsAuthenticationAuthentication ManagementAuthentication MethodsAuthentication failed!Authentication failed: Database Error!Authentication failed: Password doesn't match!Authentication failed: User not found!Automatic upgrade of database disabled. Please upgrade the database schema by hand.Available VariablesAvailable Variables from the PluginsBackCIDRCIDR (Subnet Size)CSV PreviewCannot add RootCannot change Password: %sCannot open File in Binmode: %sCannot parse any CSV DataCannot read ConfigFile '%s': %sCannot update Table '%s': %s. Please correct this issue by hand (i.e.: remove duplicate entries)!Cannot upgrade tables automatically. Required modules are not available (%s).ChangeChange own PasswordClose Error DetailsClose TreeColumnsCombineCombine NetworksCombine these networksCompareConfigConfiguration of '%s' for '%s'ConfigureConfigure Plugin '%s'ContactCopyCreateCreated fromCreated onCustomer Address SpaceDNS Info for %sDNS PTR-Record for current IP addressDeadDef. Subnet CIDRDefaultDefault Subnet CIDRDefaults for mode 'on Demand'Defaults for mode 'recurrent'DeleteDelete after CopyDelete this networkDescriptionDescription HelperDetails of IP %sDetails of Network %sDetails of Root %sDo you really want to delete the Group %s?Do you really want to delete the Network '%s'?Do you really want to delete the Root %s?Do you really want to delete the Template %s?Do you really want to delete the User %s?EditEdit MenuEdit NetworkEdit RootEdit Root %sEdit network %sEnable internal accountEntries (separated with semicolons)ErrorError DetailsError Details for Plugin %sError while deleting '%s' from '%s': %sError while deleting '%s': %sError while deleting network lock '%i': %sError while loading Plugin '%s'. Details in Error Logfile.Errors while updating Setting for '%s': %sExactExpand Network '%s'Expand Root '%s'Export SubnetsFinish EditFlush cacheForeign Networks will be
included (e.g. '%s')Free SubnetsFunctionsFuzzy SearchGlobal Configuration of '%s'GroupGroup '%s'Group AssociationGroup managementGroupsHLineIP addressIP addressesImportImport ASN routesImport ConfigImport DNS zonefileImport from Config FileImport from DNS zonefilesInfos for Plugin '%s'Inherit from supernetInsertInternal account is disabled.Jump ToLanguageLeftLocal ZonefileLock network for X secondsLogged in as %sLoginLogoutMACs foundMac Address related to the IP addressMaintenanceMax DepthMenuMiscellaneousModified byModified onMoveNEWName of RootNetaddressNetaddress doesn't match!NetmaskNettypeNetworkNetwork lock time must be provided in seconds not '%s'NeverNewNew New GroupNew LineNew PasswordNew TemplateNew UserNo Config given!No File given!No Group deleted. Nothing found!No IP Addresses found for Origin '%s'.No Origin given or found!No Password given!No Template for this ID '%i' available!No Templates deleted. Nothing found!No User deleted. Nothing found!No User givenNo changesNo free Subnets with this CIDR availableNo free networks foundNo.NoneNot Authenticated!!!Not enough rights to add this Network '%s' under '%s'Not enough rights to edit this Network '%s'Nothing foundNr of Bytes (0-1024)Number of NetworksNumber of entriesORObjectOld PasswordOld Password is not correct!Only IP addressesOnly insert newOrderOverviewOverwrite existingPage (%d .. %d)ParametersPasswordPassword ValidationPassword are not equalPassword is only for buildin 'HaCi' authentication!PermissionsPing Info for %sPing Status of current IP address (dead|alive)Plugin managementPopup-MenuPositionPreviewProgram Whois '%s' isn't executableProtocollRIPE Info for %sRandom TreeReduce Network '%s'Reduce Root '%s'Remove obsoletesReplaceReset last runResultReverseReverse DNS lookupRightRootRoot NameRouter for SNMP-ConnectionRouting PrefixRowsRuntimeSave result underSearchSearch for free networksSearch free networksSearch,CompareSeparatorSession is expiredSettingsSettings for the new NetworkSettings for the new NetworksShowShow Network DetailsShow NetworksShow Root DetailsShow RootsShow SubnetsShow Tree StructureShow all NetsShow audit logsShow free Subnets of '%s' with CIDR '%s'Show number of free subnetsSizeSorry, Username not found!Sorry, the network '%s' is still locked for '%i' seconds till '%s'!Sorry, this Root '%s' allready exists!Sorry, this doesn't look like a %s: '%s'!Sorry, this is not a correct Network: %s!Sorry, you have to give me a %s!Sorry, you have to give me a Name!Sorry, you have to give me a name!Sort bySourceSplitSplit DetailsSplit Network into these pieces:Status of HostStructureSubmitSubnetSuccessfully changed PasswordSuccessfully deleted '%s' (%i Networks deleted)Successfully deleted '%s' from '%s' (%i Networks deleted)Successfully deleted '%s' from '%s' (%i Networks deleted) and locked for %i seconds.Successfully deleted Group '%s'Successfully deleted Template '%s'Successfully deleted User '%s'Successfully updated Table '%s'.SupportTargetTarget RootTemplate '%s' for '%s'Template for the DescriptionsTemplate managementTemplate nameTemplatesTextTextareaTextfieldThe HaCi Daemon is not started.The minimum %s size is /%i!There are still Entries for this Template left. Please delete them first! (%s)There are still Users in this Group left. Please remove them from this Group first! (%s)This Network '%s' allready exists!This Network allready exists!This Network is freeThis Network is lockedThis Network is reservedThis Plugin queries on Demand the Whois DB for the current network and displays the Result.This Plugin queries on demand the DNS PTR-Record for the current IP adress and displays it.This Plugin runns recurrent in background and pings all associated IP addresses. In the Output it will display all IP addresses with their Status.This Plugin runs recurrent in background and collects DNS PTR-Records for its associated IP Addresses an saves them in DB. In the Output will all IP Adresses be listet with their corresponding PTR-Records.This Plugin will ping associated IP adresses and saves its status in DB. After that, it will collect the corresponding MAC addresses with SNMP from a specific router and save the result in DB, too. In the Output there were all IP addresses listed with their status and MAC address if available. With this method you catch all available Hosts, even if they deny ping.This Plugins pings the current IP adress on Demand and displays its status.This Root has %i SubnetsThis doesn't look like a network '%s'.This doesn't look like an IP address '%s'.This network contains %i subnetsTimeoutTimestampTreeTypeType info of IP %sType info of Network %sUser '%s'User managementUsernameUsersValueViewView SettingsWildcardWith SubnetworksWith all SubnetsYou cannot delete the administrator group!Zone transfer failed: %s[ permission denied ]activealivecompresseddefaultforceinvisiblelast Runmarked: NOT inheritedon Demandorreadrecurrentsecseperate
by spacetowrite# vergebener Subnetze# verfügbarer Adressen# freier Subnetze mit CIDR '%s'# freier Subnetze%i IP Adressen gefunden für Origin '%s'. %i wurden erfolgreich unter dem Startpunkt '%s' gespeichert.%i Routen gefunden. %i neue Inetnums von %i gefundenen gesichert%i. Möglichkeit%s ist ein neuer Wert für inet6num Objekte. Somit ist er nicht für inetnum Objekte verfügbar%s: Status (%s) nicht bekannt%ss können nur unter Allokationen mit einem Status von %s angelegt werden%ss können nur angelegt werden, wenn kein weniger spezifisches inetnum (%s) mit einem Status von %s vorhanden ist%ss können nur angelegt werden, wenn kein weniger spezifisches inetnum (%s) mit einem 'Assigned' status vorhanden ist%ss können nur angelegt werden, wenn kein spezifischeres inetnum (%s) mit einem 'Assigned' status vorhanden istUNDARP EintragAS NummerASN RoutenAbbrechenÜber HaCiZugriffsrechteAktionHinzufügenNetz hinzufügenStartpunkt hinzufügenFreies Netz hinzufügenNetz hinzufügenNetz %s hinzufügenStartpunkt hinzufügenLebendigAlleAnzahlAuditaufzeichnungenBenutzeranmeldungAuthentifizierungsverwaltungAuthentifizierungs MethodenAuthentifizierung schlug fehl!Authentifizierung schlug fehl: Datenbank FehlerAuthentifizierung schlug fehl: Passwort stimmt nicht!Authentifizierung schlug fehl: Benutzername wurde nicht gefunden!Automatische Aktualisierung der Datenbank nicht aktiviert. Bitte aktualisieren Sie das Datenbankschema per Hand.Verfügbare VariablenVerfügbare Variablen der PluginsZurückCIDRCIDR (Subnetz Größe)CSV VorschauKann Startpunkt nicht erstellenKonnte Passwort nicht ändern : %sKonnte Datei nicht im Binär-Modus öffnen: %sKonnte keine CSV Daten parsenKann die Konfigurations Datei '%s' nicht zum lesen öffnen: %sKonnte Tabelle '%s' nicht aktualisieren: %s. Bitte korrigieren Sie diesen Fehler per Hand (z.B.: doppelte Einträge löschen)!Keine automatische Aktualisierung der Tabellen möglich. Benötigte Module sind nicht verfügbar (%s).ÄndernEigenes Passwort ändernSchließe Fehler DetailsBaum schliessenSpaltenZusammenfassenNetze ZusammenfassenDiese Netze zusammenfassenVergleichenKonfigKonfiguration von '%s' für '%s'KonfigurierenKonfiguriere Plugin '%s'KontaktKopierenErstellenErstellt vonErstellt amKundenadressbereichDNS Info für %sDNS PTR-Record für die jeweilige IP AdresseTotStd. Subnetz CIDRStandardStandard Subnetz CIDRStandards für den Modus 'auf Abruf'Standards für den Modus 'regelmäßig'LöschenLöschen nachm KopierenDieses Netz löschenBeschreibungHilfe für die BeschreibungDetails der IP %sDetails des Netzes %sDetails des Startpunktes %sWollen Sie wirklich die Gruppe %s löschen?Wollen sie wirklich das Netz '%s' löschen?Wollen Sie wirklich den Startpunkt %s löschen?Wollen Sie wirklich die Vorlage %s löschen?Wollen sie wirklich den Benutzer '%s' löschen?BearbeitenBearbeitenNetz bearbeitenStartpunkt bearbeitenStartpunkt %s bearbeitenNetz %s bearbeiteninternes Konto aktivierenEinträge (getrennt mit Semikolons)FehlerFehler DetailsFehler Details für Plugin %sFehler beim Löschen '%s' von '%s': %sFehler beim Löschen '%s': %sFehler beim Entfernen der Netzwerk-Sperre '%i': %sFehler beim Laden des Plugins '%s'. Details stehen im Fehler Log.Wärend die Einstellungen für '%s' aktualisiert wurden, sind Fehler aufgetreten: %sExaktÖffne Netzwerk '%s'Öffne StartpunktNetzwerk '%s'Subnetze exportierenBearbeiten beendenCache leerenFremde Netze werden mit
eingeschlossen (z.B. '%s')Freie SubnetzeFunktionenUnscharfe SucheGlobale Konfiguration von '%s'GruppeGruppe '%s'GruppenzugehörigkeitGruppenverwaltungGruppenHLinieIP AdresseIP AdressenImportierenASN Routen importierenKonfig importierenDNS Zonefile importierenAus Konfig-Dateien importierenvon DNS Zonefiles importierenInformationen für Plugin '%s'Vom Supernetz erbenEinfügenInternes Konto is deaktiviertSpringe zuSpracheLinksLokales ZonefileSperre Netzwerk für X SekundenAngemeldet als %sAnmeldenAbmeldenMACs gefundenMac Adresse der IP AdresseVerwaltungMaximale TiefeMenüSonstigesGeändert vonGeändert amVerschiebenNEUName des StartpunktesNetzwerk AdresseNetzadresse passt nicht!Netz MaskeNetz TypNetzwerkNetzwerksperre muss in Sekunden angegeben werden, nicht '%s'NieNeuNeue Neue GruppeNeue ZeileNeues PasswortNeue VorlageNeuer BenutzerKeine Konfiguration übergeben!Keine Datei mitgegeben!Keine Gruppe gelöscht. Nix gefunden!Keine IP Adressen für Origin '%s' gefunden.Kein Origin angegeben oder gefundenKein Passwort angegeben!Keine Vorlage für diese ID '%i' gefundenKeine Vorlagen gelöscht. Nix gefunden!Kein Benutzer gelöscht. Nix gefundenKeinen Usernamen angegebenKeine ÄnderungenKeine freien Subnetze mit dieser CIDR verfügbarKeine freien Netze gefundenNr.KeineNicht Angemeldet!!!Nicht genügend Rechte vorhanden, um dieses Netz '%s' unter '%s' zu erstellenNicht genügend Rechte vorhanden, um dieses Netz '%s' zu editieren.Nichts gefundenByteanzahl (0-1024)Anzahl der SubnetzeAnzahl der EinträgeODERObjektAltes PasswortAltes Passwort ist nicht richtig!Nur IP AdressenNur Neue hinzufügenReihenfolgeÜbersichtÜberschreibe existierendeSeite (%d .. %d)ParameterPasswortPasswort WiederholungPasswörter sind nicht gleich!Das Passwort ist nur für die eingebaute 'HaCi' Authentifizierung!RechtePing Info für %sPing Status der jeweiligen IP Adresse (dead|alive)PluginverwaltungPopup-MenüPositionVorschauDas Whois Programm '%s' ist nicht ausführbarProtokollRIPE Info für %sZufälliger BaumSchließe Netzwerk '%s'Schließe Startpunkt '%s'Alte löschenErsetzenLetzen Aufruf resettenErgebnisUmgekehrtRückwärtsauflösungRechtsStartpunktStartpunktRouter für die SNMP VerbindungRouting PrefixReihenLaufzeitErgebnis speichern unterSuchenSuche nach freien NetzenSuche freie NetzeSuchen, VergleichenSeparatorIhre Sitzung ist abgelaufenEinstellungenEinstellungen für das neue NetzEinstellungen für die neuen NetzeZeigenNetz Details anzeigenNetze anzeigenStartpunkt Details anzeigenStartpunke anzeigenSubnetze anzeigenBaumstruktur anzeigenZeige alle NetzeZeige AuditaufzeichnungenZeige freie Subnetze von '%s' mit CIDR '%s'Zeige Anzahl freier Subnetze anGrößeBenutzername nicht gefunden!Das Netzwerk '%s' ist noch für '%i' Sekunden gesperrt bis '%s'!Dieser Startpunkt '%s' existiert schon!Das sieht nicht wie ein %s aus: '%s'Das ist kein korrektes Netz: %s!Ich brauche noch ein %sEs muss ein Name angegeben werden!Bitte gib mir einen Namen!Sortieren nachQuelleAufteilenAufteilungs DetailsNetz in folgende Stücke aufteilen:Status des HostsStrukturSpeichernSubnetzPasswort erfolgreich geändertErfolgreich '%s' gelöscht (%i Netze gelöscht)Erfolgreich '%s' von '%s' gelöscht (%i Netze gelöscht)Erfolgreich '%s' von '%s' gelöscht (%i Netze gelöscht) und für %i Sekunden gesperrtErfolgreich Gruppe '%s' gelöschtErfolgreich Vorlage '%s' gelöschtUser '%s' erfolgreich gelöschtDie Tabelle '%s' wurde erfolgreich aktualisiert.UnterstütztZielZielpunktVorlage '%s' für '%s'Vorlage für die BeschreibungenVorlagenverwaltungVorlagen NameVorlagenTextTextbereichTextfeldDer HaCi Daemon ist nicht gestartet.Die kleinste %s Größe ist /%sEs sind noch Einträge für diese Vorlage vorhanden. Bitte vorher löschen! (%s)Es sind noch Benutzer in der Gruppe eingetragen. Bitte tragen sie diese erstmal aus der Gruppe aus! (%s)Dieses Netz '%s' existiert schon!Dieses Netz existiert schon!Dieses Netz ist freiDieses Netz ist gesperrtDieses Netz ist reserviertDieses Plugin fragt auf Abruf die Whois DB für das jeweilige Netzwerk ab und zeigt das Ergebnis an.Dieses Plugin fragt auf Abruf den DNS PTR-Record für die jeweilige IP Adresse ab und zeigt ihn an.Dieses Plugin läuft regelmäßig im Hintergrund und pingt alle ihm zugeordneten IP Adressen. In der Ausgabe werden alle IP Adressen mit ihrem Status angezeigt.Dieses Plugin läuft regelmäßig im Hintergrund und sammelt DNS PTR-Records für alle ihm zugeordneten IP Adressen und speichert diese in der DB. In der Ausgabe werden alle IP Adressen mit deren zugehörigen PTR-Records aufgelistet.Dieses Plugin pingt alle ihm zugeordneten IP Adressen und speichert deren Status in der DB. Danach sammelt es die passenden MAC Adressen mittels SNMP von einem bestimmten Router und speichert die Ergebnisse ebenfalls in der DB. In der Ausgabe werden alle IP Adressen mit ihrem Status und, wenn vorhanden, ihrer MAC Adresse dargestellt. Mit dieser Methode können alle verfügbaren Geräte erkannt werden, auch wenn sie eigentlich Ping verbieten.Dieses Plugin pingt die jeweilige IP Adresse auf Abruf und zeigt deren Status an.Dieser Startpunkt beinhaltet %i SubnetzeDas sieht nicht wie ein Netz aus '%s'.Das sieht nicht wie eine IP Adresse aus '%s'.Dieses Netz beinhaltet %i SubnetzeZeitüberschreitungDatumBaumTypTyp Informationen der IP %sTyp Informationen des Netzes %sBenutzer '%s'BenutzerverwaltungBenutzernameBenutzerWertAnsichtAnsichtseinstellungenPlatzhalterInklusive SubnetzeMit allen SubnetzenDie Administratorgruppe kann nicht gelöscht werden!Zonefile transfer fehlgeschlagen: %s[ Zugriff verweigert ]aktivlebendigkomprimiertstandardforcierenunsichtbarletzter Aufrufmarkiert: NICHT vererbtauf Abrufoderlesenregelmäßigsektrennen mit
LeerzeichennachschreibenHaCi/locale/de/LC_MESSAGES/HaCi.po0000644000175000000000000005732412470236743015645 0ustar fighterrootdomain "HaCi" #: login msgid "Authentication" msgstr "Benutzeranmeldung" msgid "Language" msgstr "Sprache" msgid "Username" msgstr "Benutzername" msgid "Password" msgstr "Passwort" msgid "Authentication failed!" msgstr "Authentifizierung schlug fehl!" msgid "Authentication failed: User not found!" msgstr "Authentifizierung schlug fehl: Benutzername wurde nicht gefunden!" msgid "No User given" msgstr "Keinen Usernamen angegeben" msgid "Session is expired" msgstr "Ihre Sitzung ist abgelaufen" msgid "Authentication failed: Database Error!" msgstr "Authentifizierung schlug fehl: Datenbank Fehler" msgid "Authentication failed: Password doesn't match!" msgstr "Authentifizierung schlug fehl: Passwort stimmt nicht!" msgid "Support" msgstr "Unterstützt" #: misc msgid "or" msgstr "oder" msgid "NEW" msgstr "NEU" msgid "Users" msgstr "Benutzer" msgid "New User" msgstr "Neuer Benutzer" msgid "New Group" msgstr "Neue Gruppe" msgid "New Template" msgstr "Neue Vorlage" msgid "Login" msgstr "Anmelden" msgid "Save result under" msgstr "Ergebnis speichern unter" msgid "Right" msgstr "Rechts" msgid "Left" msgstr "Links" msgid "Compare" msgstr "Vergleichen" msgid "Target" msgstr "Ziel" msgid "Search,Compare" msgstr "Suchen, Vergleichen" msgid "Import ASN routes" msgstr "ASN Routen importieren" msgid "Import DNS zonefile" msgstr "DNS Zonefile importieren" msgid "No IP Addresses found for Origin '%s'." msgstr "Keine IP Adressen für Origin '%s' gefunden." msgid "Zone transfer failed: %s" msgstr "Zonefile transfer fehlgeschlagen: %s" msgid "Import Config" msgstr "Konfig importieren" msgid "No File given!" msgstr "Keine Datei mitgegeben!" msgid "Cannot open File in Binmode: %s" msgstr "Konnte Datei nicht im Binär-Modus öffnen: %s" msgid "%i IP Addresses found for Origin '%s'. %i were successfully saved under Root '%s'" msgstr "%i IP Adressen gefunden für Origin '%s'. %i wurden erfolgreich unter dem Startpunkt '%s' gespeichert." msgid "No Origin given or found!" msgstr "Kein Origin angegeben oder gefunden" msgid "Edit Network" msgstr "Netz bearbeiten" msgid "Show Root Details" msgstr "Startpunkt Details anzeigen" msgid "Show Roots" msgstr "Startpunke anzeigen" msgid "Show Networks" msgstr "Netze anzeigen" msgid "Show Network Details" msgstr "Netz Details anzeigen" msgid "Edit Root" msgstr "Startpunkt bearbeiten" msgid "Root Name" msgstr "Startpunkt" msgid "Cannot read ConfigFile '%s': %s" msgstr "Kann die Konfigurations Datei '%s' nicht zum lesen öffnen: %s" msgid "Abort" msgstr "Abbrechen" msgid "Submit" msgstr "Speichern" msgid "Change" msgstr "Ändern" msgid "New" msgstr "Neu" msgid "Delete" msgstr "Löschen" msgid "Combine" msgstr "Zusammenfassen" msgid "DNS Info for %s" msgstr "DNS Info für %s" msgid "RIPE Info for %s" msgstr "RIPE Info für %s" msgid "unknown" msgstr "" msgid "Nothing found" msgstr "Nichts gefunden" msgid "Error while deleting '%s' from '%s': %s" msgstr "Fehler beim Löschen '%s' von '%s': %s" msgid "Successfully deleted '%s' from '%s' (%i Networks deleted)" msgstr "Erfolgreich '%s' von '%s' gelöscht (%i Netze gelöscht)" msgid "Successfully deleted '%s' from '%s' (%i Networks deleted) and locked for %i seconds." msgstr "Erfolgreich '%s' von '%s' gelöscht (%i Netze gelöscht) und für %i Sekunden gesperrt" msgid "None" msgstr "Keine" msgid "invisible" msgstr "unsichtbar" msgid "read" msgstr "lesen" msgid "write" msgstr "schreiben" msgid "Group" msgstr "Gruppe" msgid "CSV Preview" msgstr "CSV Vorschau" msgid "Cannot parse any CSV Data" msgstr "Konnte keine CSV Daten parsen" msgid "[ permission denied ]" msgstr "[ Zugriff verweigert ]" msgid "This Network is locked" msgstr "Dieses Netz ist gesperrt" msgid "This Network is reserved" msgstr "Dieses Netz ist reserviert" msgid "This Network is free" msgstr "Dieses Netz ist frei" msgid "Contact" msgstr "Kontakt" msgid "Successfully updated Table '%s'." msgstr "Die Tabelle '%s' wurde erfolgreich aktualisiert." msgid "Cannot update Table '%s': %s. Please correct this issue by hand (i.e.: remove duplicate entries)!" msgstr "Konnte Tabelle '%s' nicht aktualisieren: %s. Bitte korrigieren Sie diesen Fehler per Hand (z.B.: doppelte Einträge löschen)!" msgid "Cannot upgrade tables automatically. Required modules are not available (%s)." msgstr "Keine automatische Aktualisierung der Tabellen möglich. Benötigte Module sind nicht verfügbar (%s)." msgid "Automatic upgrade of database disabled. Please upgrade the database schema by hand." msgstr "Automatische Aktualisierung der Datenbank nicht aktiviert. Bitte aktualisieren Sie das Datenbankschema per Hand." #: menu msgid "Back" msgstr "Zurück" msgid "Tree" msgstr "Baum" msgid "Overview" msgstr "Übersicht" msgid "Network" msgstr "Netzwerk" msgid "Show all Nets" msgstr "Zeige alle Netze" msgid "Add Root" msgstr "Startpunkt hinzufügen" msgid "Add root" msgstr "Startpunkt hinzufügen" msgid "Add Network" msgstr "Netz hinzufügen" msgid "Add network" msgstr "Netz hinzufügen" msgid "Import" msgstr "Importieren" msgid "ASN routes" msgstr "ASN Routen" msgid "Add network %s" msgstr "Netz %s hinzufügen" msgid "Edit network %s" msgstr "Netz %s bearbeiten" msgid "Separator" msgstr "Separator" msgid "Miscellaneous" msgstr "Sonstiges" msgid "Search" msgstr "Suchen" msgid "Exact" msgstr "Exakt" msgid "Show number of free subnets" msgstr "Zeige Anzahl freier Subnetze an" msgid "Fuzzy Search" msgstr "Unscharfe Suche" msgid "# of free subnets" msgstr "# freier Subnetze" msgid "Wildcard" msgstr "Platzhalter" msgid "Maintenance" msgstr "Verwaltung" msgid "User management" msgstr "Benutzerverwaltung" msgid "Group management" msgstr "Gruppenverwaltung" msgid "Logged in as %s" msgstr "Angemeldet als %s" msgid "Logout" msgstr "Abmelden" msgid "Random Tree" msgstr "Zufälliger Baum" msgid "Template management" msgstr "Vorlagenverwaltung" msgid "Flush cache" msgstr "Cache leeren" msgid "About HaCi" msgstr "Über HaCi" msgid "Search free networks" msgstr "Suche freie Netze" #: Import msgid "Import from DNS zonefiles" msgstr "von DNS Zonefiles importieren" msgid "Local Zonefile" msgstr "Lokales Zonefile" msgid "%i Routes found. Saved %i new Inetnums of %i found" msgstr "%i Routen gefunden. %i neue Inetnums von %i gefundenen gesichert" msgid "Import from Config File" msgstr "Aus Konfig-Dateien importieren" msgid "Source" msgstr "Quelle" msgid "Config" msgstr "Konfig" msgid "Cannot add Root" msgstr "Kann Startpunkt nicht erstellen" msgid "AS Number" msgstr "AS Nummer" msgid "Remove obsoletes" msgstr "Alte löschen" msgid "Only insert new" msgstr "Nur Neue hinzufügen" msgid "No Config given!" msgstr "Keine Konfiguration übergeben!" #: addRoot msgid "Name of Root" msgstr "Name des Startpunktes" msgid "Description" msgstr "Beschreibung" msgid "Sorry, you have to give me a name!" msgstr "Bitte gib mir einen Namen!" msgid "Details of Root %s" msgstr "Details des Startpunktes %s" msgid "Number of Networks" msgstr "Anzahl der Subnetze" msgid "Sorry, this Root '%s' allready exists!" msgstr "Dieser Startpunkt '%s' existiert schon!" #: addNet msgid "Available Variables from the Plugins" msgstr "Verfügbare Variablen der Plugins" msgid "Available Variables" msgstr "Verfügbare Variablen" msgid "Description Helper" msgstr "Hilfe für die Beschreibung" msgid "Insert" msgstr "Einfügen" msgid "Order" msgstr "Reihenfolge" msgid "New Line" msgstr "Neue Zeile" msgid "Netaddress" msgstr "Netzwerk Adresse" msgid "Netmask" msgstr "Netz Maske" msgid "CIDR" msgstr "CIDR" msgid "Target Root" msgstr "Zielpunkt" msgid "Sorry, you have to give me a %s!" msgstr "Ich brauche noch ein %s" msgid "Sorry, this is not a correct Network: %s!" msgstr "Das ist kein korrektes Netz: %s!" msgid "Sorry, this doesn't look like a %s: '%s'!" msgstr "Das sieht nicht wie ein %s aus: '%s'" msgid "Access Rights" msgstr "Zugriffsrechte" msgid "This Network '%s' allready exists!" msgstr "Dieses Netz '%s' existiert schon!" msgid "Not enough rights to edit this Network '%s'" msgstr "Nicht genügend Rechte vorhanden, um dieses Netz '%s' zu editieren." msgid "Not enough rights to add this Network '%s' under '%s'" msgstr "Nicht genügend Rechte vorhanden, um dieses Netz '%s' unter '%s' zu erstellen" msgid "Def. Subnet CIDR" msgstr "Std. Subnetz CIDR" msgid "Default Subnet CIDR" msgstr "Standard Subnetz CIDR" msgid "The minimum %s size is /%i!" msgstr "Die kleinste %s Größe ist /%s" msgid "%ss can only be made from allocations with a status of %s" msgstr "%ss können nur unter Allokationen mit einem Status von %s angelegt werden" msgid "%ss can only be made when there's no less specific inetnum (%s) with an 'Assigned' status" msgstr "%ss können nur angelegt werden, wenn kein weniger spezifisches inetnum (%s) mit einem 'Assigned' status vorhanden ist" msgid "%ss can only be made when there's no more specific inetnum (%s) with an 'Assigned' status" msgstr "%ss können nur angelegt werden, wenn kein spezifischeres inetnum (%s) mit einem 'Assigned' status vorhanden ist" msgid "%s is a new Value for inet6num Objects and so it's not available for inetnum Objects" msgstr "%s ist ein neuer Wert für inet6num Objekte. Somit ist er nicht für inetnum Objekte verfügbar" msgid "%ss can only be made when there's no less specific inetnum (%s) with a status of '%s'" msgstr "%ss können nur angelegt werden, wenn kein weniger spezifisches inetnum (%s) mit einem Status von %s vorhanden ist" msgid "force" msgstr "forcieren" #: treeMenu msgid "Menu" msgstr "Menü" msgid "Edit Menu" msgstr "Bearbeiten" msgid "Close Tree" msgstr "Baum schliessen" msgid "Jump To" msgstr "Springe zu" msgid "Edit" msgstr "Bearbeiten" msgid "Finish Edit" msgstr "Bearbeiten beenden" msgid "Copy" msgstr "Kopieren" msgid "Move" msgstr "Verschieben" msgid "to" msgstr "nach" msgid "Delete after Copy" msgstr "Löschen nachm Kopieren" msgid "Expand Network '%s'" msgstr "Öffne Netzwerk '%s'" msgid "Reduce Network '%s'" msgstr "Schließe Netzwerk '%s'" msgid "Expand Root '%s'" msgstr "Öffne StartpunktNetzwerk '%s'" msgid "Reduce Root '%s'" msgstr "Schließe Startpunkt '%s'" #: showNet msgid "Show" msgstr "Zeigen" msgid "Show Subnets" msgstr "Subnetze anzeigen" msgid "# of free Subnets with CIDR '%s'" msgstr "# freier Subnetze mit CIDR '%s'" msgid "Routing Prefix" msgstr "Routing Prefix" msgid "Subnet" msgstr "Subnetz" msgid "Customer Address Space" msgstr "Kundenadressbereich" msgid "compressed" msgstr "komprimiert" msgid "Details of Network %s" msgstr "Details des Netzes %s" msgid "Details of IP %s" msgstr "Details der IP %s" msgid "Functions" msgstr "Funktionen" msgid "Reverse DNS lookup" msgstr "Rückwärtsauflösung" msgid "Type" msgstr "Typ" msgid "Type info of Network %s" msgstr "Typ Informationen des Netzes %s" msgid "Type info of IP %s" msgstr "Typ Informationen der IP %s" msgid "Split" msgstr "Aufteilen" msgid "Split Details" msgstr "Aufteilungs Details" msgid "Split Network into these pieces:" msgstr "Netz in folgende Stücke aufteilen:" msgid "Template for the Descriptions" msgstr "Vorlage für die Beschreibungen" msgid "Delete this network" msgstr "Dieses Netz löschen" msgid "Settings for the new Networks" msgstr "Einstellungen für die neuen Netze" msgid "# of available Adresses" msgstr "# verfügbarer Adressen" #: delNet msgid "Do you really want to delete the Network '%s'?" msgstr "Wollen sie wirklich das Netz '%s' löschen?" msgid "With all Subnets" msgstr "Mit allen Subnetzen" msgid "This network contains %i subnets" msgstr "Dieses Netz beinhaltet %i Subnetze" #: editRoot msgid "Edit Root %s" msgstr "Startpunkt %s bearbeiten" msgid "Root" msgstr "Startpunkt" #: delRoot msgid "Error while deleting '%s': %s" msgstr "Fehler beim Löschen '%s': %s" msgid "Successfully deleted '%s' (%i Networks deleted)" msgstr "Erfolgreich '%s' gelöscht (%i Netze gelöscht)" msgid "This Root has %i Subnets" msgstr "Dieser Startpunkt beinhaltet %i Subnetze" msgid "Do you really want to delete the Root %s?" msgstr "Wollen Sie wirklich den Startpunkt %s löschen?" #: showTemplates msgid "Templates" msgstr "Vorlagen" msgid "Nettype" msgstr "Netz Typ" msgid "There are still Entries for this Template left. Please delete them first! (%s)" msgstr "Es sind noch Einträge für diese Vorlage vorhanden. Bitte vorher löschen! (%s)" #: editTemplate msgid "Add" msgstr "Hinzufügen" msgid "Replace" msgstr "Ersetzen" msgid "Template '%s' for '%s'" msgstr "Vorlage '%s' für '%s'" msgid "New " msgstr "Neue " msgid "Preview" msgstr "Vorschau" msgid "Position" msgstr "Position" msgid "No Template for this ID '%i' available!" msgstr "Keine Vorlage für diese ID '%i' gefunden" msgid "Parameters" msgstr "Parameter" msgid "Size" msgstr "Größe" msgid "Entries (separated with semicolons)" msgstr "Einträge (getrennt mit Semikolons)" msgid "HLine" msgstr "HLinie" msgid "Textfield" msgstr "Textfeld" msgid "Textarea" msgstr "Textbereich" msgid "Popup-Menu" msgstr "Popup-Menü" msgid "Text" msgstr "Text" msgid "Sorry, you have to give me a Name!" msgstr "Es muss ein Name angegeben werden!" msgid "Do you really want to delete the Template %s?" msgstr "Wollen Sie wirklich die Vorlage %s löschen?" msgid "Successfully deleted Template '%s'" msgstr "Erfolgreich Vorlage '%s' gelöscht" msgid "No Templates deleted. Nothing found!" msgstr "Keine Vorlagen gelöscht. Nix gefunden!" msgid "Rows" msgstr "Reihen" msgid "Columns" msgstr "Spalten" msgid "Created from" msgstr "Erstellt von" msgid "Created on" msgstr "Erstellt am" msgid "Modified by" msgstr "Geändert von" msgid "Modified on" msgstr "Geändert am" msgid "Structure" msgstr "Struktur" msgid "Template name" msgstr "Vorlagen Name" #: ShowGroups msgid "Groups" msgstr "Gruppen" msgid "Group '%s'" msgstr "Gruppe '%s'" msgid "Permissions" msgstr "Rechte" #: delGroup msgid "There are still Users in this Group left. Please remove them from this Group first! (%s)" msgstr "Es sind noch Benutzer in der Gruppe eingetragen. Bitte tragen sie diese erstmal aus der Gruppe aus! (%s)" msgid "No Group deleted. Nothing found!" msgstr "Keine Gruppe gelöscht. Nix gefunden!" msgid "You cannot delete the administrator group!" msgstr "Die Administratorgruppe kann nicht gelöscht werden!" msgid "Successfully deleted Group '%s'" msgstr "Erfolgreich Gruppe '%s' gelöscht" msgid "Do you really want to delete the Group %s?" msgstr "Wollen Sie wirklich die Gruppe %s löschen?" #: editUser msgid "User '%s'" msgstr "Benutzer '%s'" msgid "Group Association" msgstr "Gruppenzugehörigkeit" msgid "Password Validation" msgstr "Passwort Wiederholung" msgid "No Password given!" msgstr "Kein Passwort angegeben!" msgid "Password are not equal" msgstr "Passwörter sind nicht gleich!" msgid "Password is only for buildin 'HaCi' authentication!" msgstr "Das Passwort ist nur für die eingebaute 'HaCi' Authentifizierung!" msgid "Not Authenticated!!!" msgstr "Nicht Angemeldet!!!" #: delUser msgid "No User deleted. Nothing found!" msgstr "Kein Benutzer gelöscht. Nix gefunden" msgid "Successfully deleted User '%s'" msgstr "User '%s' erfolgreich gelöscht" msgid "Do you really want to delete the User %s?" msgstr "Wollen sie wirklich den Benutzer '%s' löschen?" #: showAuthMeths msgid "Authentication Management" msgstr "Authentifizierungsverwaltung" msgid "Authentication Methods" msgstr "Authentifizierungs Methoden" #: combineNets msgid "Combine Networks" msgstr "Netze Zusammenfassen" msgid "%i. Possibility" msgstr "%i. Möglichkeit" msgid "Combine these networks" msgstr "Diese Netze zusammenfassen" msgid "Settings for the new Network" msgstr "Einstellungen für das neue Netz" msgid "Netaddress doesn't match!" msgstr "Netzadresse passt nicht!" msgid "Foreign Networks will be
included (e.g. '%s')" msgstr "Fremde Netze werden mit
eingeschlossen (z.B. '%s')" msgid "This Network allready exists!" msgstr "Dieses Netz existiert schon!" #: pluginMgmt msgid "Plugin management" msgstr "Pluginverwaltung" msgid "Configure Plugin '%s'" msgstr "Konfiguriere Plugin '%s'" #: plugins msgid "Global Configuration of '%s'" msgstr "Globale Konfiguration von '%s'" msgid "Configuration of '%s' for '%s'" msgstr "Konfiguration von '%s' für '%s'" msgid "Status of Host" msgstr "Status des Hosts" msgid "Alive" msgstr "Lebendig" msgid "alive" msgstr "lebendig" msgid "Dead" msgstr "Tot" msgid "Ping Info for %s" msgstr "Ping Info für %s" msgid "active" msgstr "aktiv" msgid "default" msgstr "standard" msgid "Default" msgstr "Standard" msgid "on Demand" msgstr "auf Abruf" msgid "recurrent" msgstr "regelmäßig" msgid "last Run" msgstr "letzter Aufruf" msgid "Configure" msgstr "Konfigurieren" msgid "Reset last run" msgstr "Letzen Aufruf resetten" #plugins msgid "DNS PTR-Record for current IP address" msgstr "DNS PTR-Record für die jeweilige IP Adresse" msgid "Ping Status of current IP address (dead|alive)" msgstr "Ping Status der jeweiligen IP Adresse (dead|alive)" msgid "Mac Address related to the IP address" msgstr "Mac Adresse der IP Adresse" msgid "Infos for Plugin '%s'" msgstr "Informationen für Plugin '%s'" msgid "This Plugin will ping associated IP adresses and saves its status in DB. After that, it will collect the corresponding MAC addresses with SNMP from a specific router and save the result in DB, too. In the Output there were all IP addresses listed with their status and MAC address if available. With this method you catch all available Hosts, even if they deny ping." msgstr "Dieses Plugin pingt alle ihm zugeordneten IP Adressen und speichert deren Status in der DB. Danach sammelt es die passenden MAC Adressen mittels SNMP von einem bestimmten Router und speichert die Ergebnisse ebenfalls in der DB. In der Ausgabe werden alle IP Adressen mit ihrem Status und, wenn vorhanden, ihrer MAC Adresse dargestellt. Mit dieser Methode können alle verfügbaren Geräte erkannt werden, auch wenn sie eigentlich Ping verbieten." msgid "This Plugin runs recurrent in background and collects DNS PTR-Records for its associated IP Addresses an saves them in DB. In the Output will all IP Adresses be listet with their corresponding PTR-Records." msgstr "Dieses Plugin läuft regelmäßig im Hintergrund und sammelt DNS PTR-Records für alle ihm zugeordneten IP Adressen und speichert diese in der DB. In der Ausgabe werden alle IP Adressen mit deren zugehörigen PTR-Records aufgelistet." msgid "This Plugin queries on demand the DNS PTR-Record for the current IP adress and displays it." msgstr "Dieses Plugin fragt auf Abruf den DNS PTR-Record für die jeweilige IP Adresse ab und zeigt ihn an." msgid "This Plugins pings the current IP adress on Demand and displays its status." msgstr "Dieses Plugin pingt die jeweilige IP Adresse auf Abruf und zeigt deren Status an." msgid "This Plugin runns recurrent in background and pings all associated IP addresses. In the Output it will display all IP addresses with their Status." msgstr "Dieses Plugin läuft regelmäßig im Hintergrund und pingt alle ihm zugeordneten IP Adressen. In der Ausgabe werden alle IP Adressen mit ihrem Status angezeigt." msgid "This Plugin queries on Demand the Whois DB for the current network and displays the Result." msgstr "Dieses Plugin fragt auf Abruf die Whois DB für das jeweilige Netzwerk ab und zeigt das Ergebnis an." msgid "IP addresses" msgstr "IP Adressen" msgid "MACs found" msgstr "MACs gefunden" msgid "ARP Entry" msgstr "ARP Eintrag" msgid "Nr of Bytes (0-1024)" msgstr "Byteanzahl (0-1024)" msgid "Timeout" msgstr "Zeitüberschreitung" msgid "Protocoll" msgstr "Protokoll" msgid "Router for SNMP-Connection" msgstr "Router für die SNMP Verbindung" msgid "With Subnetworks" msgstr "Inklusive Subnetze" msgid "Only IP addresses" msgstr "Nur IP Adressen" msgid "Max Depth" msgstr "Maximale Tiefe" msgid "Runtime" msgstr "Laufzeit" msgid "sec" msgstr "sek" msgid "Close Error Details" msgstr "Schließe Fehler Details" msgid "Error Details for Plugin %s" msgstr "Fehler Details für Plugin %s" msgid "Error Details" msgstr "Fehler Details" msgid "Error" msgstr "Fehler" msgid "IP address" msgstr "IP Adresse" msgid "Error while loading Plugin '%s'. Details in Error Logfile." msgstr "Fehler beim Laden des Plugins '%s'. Details stehen im Fehler Log." msgid "This doesn't look like a network '%s'." msgstr "Das sieht nicht wie ein Netz aus '%s'." msgid "Program Whois '%s' isn't executable" msgstr "Das Whois Programm '%s' ist nicht ausführbar" msgid "This doesn't look like an IP address '%s'." msgstr "Das sieht nicht wie eine IP Adresse aus '%s'." msgid "Defaults for mode 'recurrent'" msgstr "Standards für den Modus 'regelmäßig'" msgid "Defaults for mode 'on Demand'" msgstr "Standards für den Modus 'auf Abruf'" msgid "The HaCi Daemon is not started." msgstr "Der HaCi Daemon ist nicht gestartet." #: showSubnets msgid "Show free Subnets of '%s' with CIDR '%s'" msgstr "Zeige freie Subnetze von '%s' mit CIDR '%s'" msgid "CIDR (Subnet Size)" msgstr "CIDR (Subnetz Größe)" msgid "Free Subnets" msgstr "Freie Subnetze" msgid "No free Subnets with this CIDR available" msgstr "Keine freien Subnetze mit dieser CIDR verfügbar" msgid "Create" msgstr "Erstellen" msgid "No." msgstr "Nr." #: showSettings msgid "Settings" msgstr "Einstellungen" msgid "Change own Password" msgstr "Eigenes Passwort ändern" msgid "Old Password" msgstr "Altes Passwort" msgid "New Password" msgstr "Neues Passwort" msgid "Old Password is not correct!" msgstr "Altes Passwort ist nicht richtig!" msgid "Sorry, Username not found!" msgstr "Benutzername nicht gefunden!" msgid "Successfully changed Password" msgstr "Passwort erfolgreich geändert" msgid "Cannot change Password: %s" msgstr "Konnte Passwort nicht ändern : %s" msgid "View" msgstr "Ansicht" msgid "View Settings" msgstr "Ansichtseinstellungen" msgid "Show Tree Structure" msgstr "Baumstruktur anzeigen" msgid "Errors while updating Setting for '%s': %s" msgstr "Wärend die Einstellungen für '%s' aktualisiert wurden, sind Fehler aufgetreten: %s" #: audit msgid "Show audit logs" msgstr "Zeige Auditaufzeichnungen" msgid "Audit logs" msgstr "Auditaufzeichnungen" msgid "Sort by" msgstr "Sortieren nach" msgid "Reverse" msgstr "Umgekehrt" msgid "Timestamp" msgstr "Datum" msgid "Action" msgstr "Aktion" msgid "Object" msgstr "Objekt" msgid "Value" msgstr "Wert" msgid "Number of entries" msgstr "Anzahl der Einträge" msgid "Page (%d .. %d)" msgstr "Seite (%d .. %d)" msgid "Overwrite existing" msgstr "Überschreibe existierende" msgid "Add free network" msgstr "Freies Netz hinzufügen" msgid "Search for free networks" msgstr "Suche nach freien Netzen" msgid "Amount" msgstr "Anzahl" msgid "Result" msgstr "Ergebnis" msgid "No free networks found" msgstr "Keine freien Netze gefunden" msgid "Never" msgstr "Nie" msgid "No changes" msgstr "Keine Änderungen" msgid "Sorry, the network '%s' is still locked for '%i' seconds till '%s'!" msgstr "Das Netzwerk '%s' ist noch für '%i' Sekunden gesperrt bis '%s'!" msgid "Error while deleting network lock '%i': %s" msgstr "Fehler beim Entfernen der Netzwerk-Sperre '%i': %s" msgid "Lock network for X seconds" msgstr "Sperre Netzwerk für X Sekunden" msgid "Network lock time must be provided in seconds not '%s'" msgstr "Netzwerksperre muss in Sekunden angegeben werden, nicht '%s'" msgid "All" msgstr "Alle" msgid "Inherit from supernet" msgstr "Vom Supernetz erben" msgid "Export Subnets" msgstr "Subnetze exportieren" msgid "# of assigned subnets" msgstr "# vergebener Subnetze" msgid "%s: Status (%s) not known" msgstr "%s: Status (%s) nicht bekannt" msgid "seperate
by space" msgstr "trennen mit
Leerzeichen" msgid "AND" msgstr "UND" msgid "OR" msgstr "ODER" msgid "marked: NOT inherited" msgstr "markiert: NICHT vererbt" msgid "Enable internal account" msgstr "internes Konto aktivieren" msgid "Internal account is disabled." msgstr "Internes Konto is deaktiviert" HaCi/locale/it/0000755000175000000000000000000012475447615012734 5ustar fighterrootHaCi/locale/it/LC_MESSAGES/0000755000175000000000000000000012475447615014521 5ustar fighterrootHaCi/locale/it/LC_MESSAGES/HaCi.mo0000644000175000000000000005161411776645425015673 0ustar fighterrootÞ•+t ‘Ì (QI2›ÎTÞ93UmYÃY w  ‹– œ §µ ¹ÅÛäêù*&A.h&—$¾ãèí  7Wq‘˜¬ ÀËÓÛì  1;QY^ e r}”%¤ÊÏàèü8?Q eq„¡1»5í0# 4T 0‰ º ¿ É Ö à ô # !/! 5!C!'_!‡!:¥!*à! ""%" 6" B"1N" €" " —"¤"Á"Ç"Ù"ë"ü"# # #!#(# :#H#\#t#Ž#¤#«#³#¼#Á#1Ð#$$ $%$ @$ L$V$ [$ i$ u$$†$ Š$ —$¢$¼$Ä$Ì$Ô$Ø$ Ý$ç$ ð$ ý$ %%$% 3%&T%{%•%'¨%$Ð%õ% &(#&L&P&U&5j&+ & Ì&Ú&ï& '','>'N'T' ]'h'q'…'3œ' Ð'Ü'.í'( .(9(B(#J( n(x( ‰(•(©(º(Ë(Ó(â(õ(û( ) )%)4)9)A)S)Z) i)s)†))¬)Ê)Ï) ä)ò) * ** 0*(>*g*l*&‡*)®*)Ø* +"#+"F+i+p+ v+ „+¥+ ´+¾+Å+Ì+/ê+9,T,"t,—,¶,¾, Å,$Ñ,ö,- (-2-7- @-J-j-N†-XÕ-"..Q.o.„.›.[´.[/’l/Íÿ/nÍ0K<2ˆ2&¨2*Ï2'ú2"3*3/343T3e3u3~3„3 ‰3—3 3±3Â3Û3ñ3ø3 þ3 44 4!4 *44474 <4F4J4M4U4[4#v4Uš41ð4"5S35C‡5WË5]#6\6 Þ6 è6 ò6 ý6 77%7.7@7[7o7v7…7¢7»7,Ó7(8+)8 U8v88!„8 ¦8´8#Ó8'÷8"97B9z99 œ9½9Í9Õ9Ý9í9 : :: ::D:]:f:l: q: {:$…:ª:*Â:í:ö:;;&#;%J;p;y;; ¥;±;Ê;è;.<.7<-f<0”</Å<õ<þ<=%=8=U="p=“=š='±=.Ù=+>M4>>‚>Á>È>Ý>ô> ?,?F?W? `?n?Œ?“?¦?½?Í?Ô? Ú? ç?ô?ü?@&@!;@]@v@ –@ @¨@¯@¸@1È@ú@A A%A 9AFAVA \A hA vA„A‹A‘A£A$µAÚA âAïAôAúA B BB)B 8B.EBtB,‘B'¾B æBC C/;C,kC˜C,µCâCæCîCCD=EDƒD“D§D¶D$ÇDìDEE E +E5E>EPE?mE­E¶ECÏEF #F /F 9F)CF mFxF”F£F·FÍF ÞF$èF G&G-G4GFGbGqGwG‹G¢G¨G ¹GÄG ÕGâGH H'HDHSHrHƒH—H·H0ÌH ýHI%I/BI+rIžI¼IÓIñI úIJ" JCJ TJ^J dJnJ1J;¿J#ûJ%K#EKiK rKK$–K»K×KéKòK øKL!L!7L?YLP™LêLM!M7MOM[hM^ÄM§#NÚËNƒ¦OW*Q*‚Q ­Q'ÎQ(öQ R-R4R'9RaRtR„R‹R’R™R°R¹RÎRåRÿRSS !S+S3S 9SDS VSbSdS jSuSwS yS…SÕ \ ”'¹(GkbhÞ>Я‹¬%L¶Í¨7Ã,Â_×òó§NJ¤ý“txÀFÚ fg¼ .)oö4¡Æ£!<z²–2 +š3ÁIävmºRO(ÔËês¥#l+@æùâŸ&]6/õµ!DÜ®íQ˜Zãw|H"-X÷­¸ ûÒY?´»V;Aé¿ ¦*`þ1*0jqß[=È$‘ü„Šø©ÙeÉ}îEÖSyÅa^dÛnWÝÄ›MàT«‚¾•á‰ÎU èÿŒ—'ô ¢ˆ#†  ØŽi8~c 5rpʳ uñëP9$BK±CÑçžÌ)úª½™{:Ï&ïƒ"ì€Ç’Ó%å·‡œ°ð…# of available Adresses# of free Subnets with CIDR '%s'%i IP Addresses found for Origin '%s'. %i were successfully saved under Root '%s'%i Routes found. Saved %i new Inetnums of %i found%i. Possibility%s is a new Value for inet6num Objects and so it's not available for inetnum Objects%ss can only be made from allocations with a status of %s%ss can only be made when there's no less specific inetnum (%s) with a status of '%s'%ss can only be made when there's no less specific inetnum (%s) with an 'Assigned' status%ss can only be made when there's no more specific inetnum (%s) with an 'Assigned' statusARP EntryAS NumberASN routesAbortAbout HaCiAccess RightsAddAdd networkAdd network %sAdd rootAliveAuthenticationAuthentication ManagementAuthentication MethodsAuthentication failed!Authentication failed: Database Error!Authentication failed: Password doesn't match!Authentication failed: User not found!Available Variables from the PluginsBackCIDRCIDR (Subnet Size)CSV PreviewCannot add RootCannot change Password: %sCannot open File in Binmode: %sCannot parse any CSV DataCannot read ConfigFile '%s': %sChangeChange own PasswordClose Error DetailsClose TreeColumnsCombineCombine NetworksCombine these networksCompareConfigConfiguration of '%s' for '%s'ConfigureConfigure Plugin '%s'ContactCopyCreateCreated fromCreated onCustomer Address SpaceDNS Info for %sDNS PTR-Record for current IP addressDeadDef. Subnet CIDRDefaultDefault Subnet CIDRDefaults for mode 'on Demand'Defaults for mode 'recurrent'DeleteDelete after CopyDelete this networkDescriptionDescription HelperDetails of Network %sDetails of Root %sDo you really want to delete the Group %s?Do you really want to delete the Network '%s'?Do you really want to delete the Root %s?Do you really want to delete the Template %s?Do you really want to delete the User %s?EditEdit MenuEdit NetworkEdit RootEdit Root %sEdit network %sEntries (separated with semicolons)ErrorError DetailsError Details for Plugin %sError while deleting '%s' from '%s': %sError while deleting '%s': %sError while loading Plugin '%s'. Details in Error Logfile.Errors while updating Setting for '%s': %sExactExpand Network '%s'Expand Root '%s'Finish EditFlush cacheForeign Networks will be
included (e.g. '%s')Free SubnetsFunctionsFuzzy SearchGlobal Configuration of '%s'GroupGroup '%s'Group AssociationGroup managementGroupsHLineIP addressIP addressesImportImport ASN RoutesImport ConfigImport DNS ZonefileImport from Config FileImport from DNS ZonefilesInfos for Plugin '%s'InsertJump ToLanguageLeftLocal ZonefileLogged in as %sLoginLogoutMACs foundMac Address related to the IP addressMaintenanceMax DepthMenuMiscellaneousModified byModified onMoveNEWName of RootNetaddressNetaddress doesn't match!NetmaskNettypeNetworkNewNew New GroupNew LineNew PasswordNew TemplateNew UserNo Config given!No File given!No Group deleted. Nothing found!No IP Addresses found for Origin '%s'.No Origin given or found!No Password given!No Template for this ID '%i' available!No Templates deleted. Nothing found!No User deleted. Nothing found!No User givenNo free Subnets with this CIDR availableNo.NoneNot Authenticated!!!Not enough rights to add this Network '%s' under '%s'Not enough rights to edit this Network '%s'Nothing foundNr of Bytes (0-1024)Number of NetworksOld PasswordOld Password is not correct!Only IP addressesOnly insert newOrderOverviewParametersPasswordPassword ValidationPassword are not equalPassword is only for buildin 'HaCi' authentication!PermissionsPing Info for %sPing Status of current IP address (dead|alive)Plugin managementPopup-MenuPositionPreviewProgram Whois '%s' isn't executableProtocollRIPE Info for %sRandom TreeReduce Network '%s'Reduce Root '%s'Remove obsoletesReplaceReset last runReverse DNS lookupRightRootRoot NameRouter for SNMP-ConnectionRouting PrefixRowsRuntimeSave result underSearchSearch,CompareSeparatorSession is expiredSettingsSettings for the new NetworkSettings for the new NetworksShowShow Network DetailsShow NetworksShow Root DetailsShow RootsShow SubnetsShow Tree StructureShow all NetsShow free Subnets of '%s' with CIDR '%s'SizeSorry, Username not found!Sorry, this Root '%s' allready exists!Sorry, this doesn't look like a %s: '%s'!Sorry, this is not a correct Network: %s!Sorry, you have to give me a %s!Sorry, you have to give me a Name!Sorry, you have to give me a name!SourceSplitSplit DetailsSplit Network into these pieces:Status of HostStructureSubmitSubnetSuccessfully changed PasswordSuccessfully deleted '%s' (%i Networks deleted)Successfully deleted '%s' from '%s' (%i Networks deleted)Successfully deleted Group '%s'Successfully deleted Template '%s'Successfully deleted User '%s'SupportTargetTarget RootTemplate '%s' for '%s'Template for the DescriptionsTemplate managementTemplatesTextTextareaTextfieldThe HaCi Daemon is not started.The minimum %s size is /%i!There are still Entries for this Template left. Please delete them first! (%s)There are still Users in this Group left. Please remove them from this Group first! (%s)This Network '%s' allready exists!This Network allready exists!This Network is freeThis Network is lockedThis Network is reservedThis Plugin queries on Demand the Whois DB for the current network and displays the Result.This Plugin queries on demand the DNS PTR-Record for the current IP adress and displays it.This Plugin runns recurrent in background and pings all associated IP addresses. In the Output it will display all IP addresses with their Status.This Plugin runs recurrent in background and collects DNS PTR-Records for its associated IP Addresses an saves them in DB. In the Output will all IP Adresses be listet with their corresponding PTR-Records.This Plugin will ping associated IP adresses and saves its status in DB. After that, it will collect the corresponding MAC addresses with SNMP from a specific router and save the result in DB, too. In the Output there were all IP addresses listed with their status and MAC address if available. With this method you catch all available Hosts, even if they deny ping.This Plugins pings the current IP adress on Demand and displays its status.This Root has %i SubnetsThis doesn't look like a network '%s'.This doesn't look like an IP address '%s'.This network contains %i subnetsTimeoutTreeTypeType Infos of Network %sUser '%s'User managementUsernameUsersViewView SettingsWildcardWith SubnetworksWith all SubnetsZone transfer failed: %s[ permission denied ]activealivecompresseddefaultforceinvisiblelast Runon Demandorreadrecurrentsectounknownwrite# di indirizzi disponibili# di sottoreti libere con CIDR '%s'Trovati %i indirizzi IP per Origin '%s'. %i salvati con success sotto la Radice '%s'.Trovate %i Rotte. Salvati %i nuovi Inetnums di %i%i. Possibilita'%s e` un nuovo valore per oggetti inet6num e non e` disponibile per oggetti inetnum%ss puo' essere definito solo a partire da allocazioni con stato %s%ss puo' essere definito solo se non ci sono inetnum meno specifici (%s) con stato '%s'%ss puo' essere definito solo se non ci sono inetnum meno specifici (%s) con stato 'Assigned'%ss puo' essere definito solo se non ci sono inetnum piu' specifici (%s) con stato 'AssignedARP EntryAS NumberASN RoutesInterrompiAbout HaCiDiritti d'accessoAggiungiAggiungi una ReteAggiungu la Rete %sAggiungi una RadiceAttivoAutenticazioneGestione dell'AutenticazioneMetodi di AutenticazioneAutenticazione fallita!Autenticazione fallita: Errore del Database!Autenticazione fallita: Password errata!Autenticazione fallita: non trovo l'Utente!Variabili disponibili dai PluginIndietroCIDRCIDR (dimensione della Sottorete)Anteprima CSVNon posso aggiungere la RadiceNon posso cambiare la Password : %sNon posso aprire il File in Binmode: %sNon riesco a legger alcun dato CSVNon riesco a leggere il file di configurazione '%s': %sCambiaCambia la propria PasswordChiudi il dettaglio degli erroriChiudi l'alberoColonneCombinaCombina le RetiCombina queste RetiConfrontaConfigurazioneConfigurazione di '%s' per '%s'ConfiguraConfigura il Plugin '%s'ContattoCopiaCreaCreatp daCreato ilSpazio di indirizzamento per ClientiInformazioni DNS per %sDNS PTR-Record per l'indirizzo IP correnteInattivoDef. Subnet CIDRDefaultDefault Subnet CIDRDefault per la modalita' 'a richiesta'Default per la modalita' 'ricorrente'CancellaCancella dopo la CopiaCancella questa ReteDescrizioneAiuto per la DescrizioneDettagli della Rete %sDettagli della Radice %sVuoi veramente cancellare il Gruppo %s?Vuoi veramente cancellare la Rete '%s'?Vuoi cancellare veramente la Radice %sVuoi veramente cancellare la Maschera %s?Vuoi veramente cancellare l'Utente '%s'?ModificaMenu' delle modificheModifica la ReteModifica la RadiceModifica la Radice %sModifica la Rete %sDati (separati da punto e virgola)ErroriDettaglio degli erroriDettaglio degli errori per il Plugin %sErrore cercando di cancellare '%s' da '%s': %sErrore durante la cancellazione di '%s': %sErrore nel caricamento del Plugin '%s'. I dettagli sono nel Log degli Errori.Errori durante l'aggiornamento delle impostazioni per '%s': %sEsattoEspandi la Rete '%s'Espandi la Radice '%s'Termina le modificheSvuota la CacheReti Estranee saranno
incluse (es. '%s')Sottoreti LibereFunzioniRicerca FuzzyConfigurazione globale di'%s'GruppoGruppo '%s'Associazione al GruppoGestione GruppiGruppiHLineIndirizzo IPIndirizzi IPImportaImporta ASN routesImporta ConfigurazioniImporta DNS ZonefileImporta da File di ConfigurazioneImporta da DNS ZonefilesInformazioni per il Plugin '%s'InserisciSalta aLinguaSinistraZonefile localeEntrato come %sAccessoEsciMAC trovatiMac Address relativo all'indirizzo IPManutenzioneLivello massimoMenu'MiscellaneaModificato daModificato ilSpostaNUOVONome della RadiceIndirizzo di ReteL'indirizzo di rete non corrisponde!NetmaskTipo di ReteReteNuovoNuovo Nuovo GruppoNuova LineaNuova PasswordNuovo TemplateNuovo UtenteNon hai specificato il File di Configurazione!Non hai specificato il File!Nessun gruppo cancellato, non ne ho trovati!Non trovo indirizzi IP per Origin '%s'.Non riesco a determinare Origin!Manca la Password!Non ci sono Maschere on ID '%i'!Nessuna Maschera cancellata, non ne ho trovate!Nessun Utente cancellato, non ne ho trovati!Non hai specificato l'UtenteNon ci sono Sottoreti libere con questo CIDRNo.NessunoNon Autenticato!!!Non hai abbastanza privilegi per aggiungere la Rete '%s' sotto '%s'Non hai abbastanza privilegi per modificare questa Rete '%s'.Non trovo nullaN. di Byte (0-1024)Numero di RetiVecchia PasswordLa vecchia Password non e` corretta!Solo gli indirizzi IPInserisci solo i nuoviOrdinaPanoramicaParametriPasswordVerifica PasswordLe Password non sono uguali!La Password e` solo per l'autenticazione incorporata in 'HaCi'!PermessiInformazioni Ping per %sStato del Ping Status per l'indirizzo IP corrente (attivo|inattivo)Gestione PluginPopup-Menu'PosizioneAnteprimaIl programma Whois '%s' non e` eseguibileProtocolloInformazioni da RIPE per %sAlbero CasualeRiduci la Rete '%s'Riduci la Radice '%s'Rimuovi obsoletiRimpiazzaAzzera l'orario di ultima esecuzioneInterroga il DNS inversoDestraRadiceNome della RadiceRouter per le funzioni SNMPRouting PrefixRigheTempo di esecuzioneSalva i risultati comeCercaCerca, ConfrontaSeparatoreSessione scadutaImpostazioniImpostazioni per la nuova ReteImpostazioni per le nuove RetiMostraMostra i dettagli della ReteMostra le RetiMostra i dettagli della RadiceMostra le RadiciMostra le SottoretiMostra la struttura dell'AlberoMostra tutte le RetiMostra le Sottoreti libere di '%s' con CIDR '%s'DimensioneUtente non trovato!Spiacente, esiste gia' una root '%s'!Spiacente, questo non sembra essere un %s: '%s'Spiacente, questa Rete non e` corretta: %s!Spiacente, mi devi dare un %sDevi fornirmi un nome!Per favore specifica un nome!SorgenteSuddividiDettagli della suddivisioneSuddividi la Rete in questi pezzi:Stato degli HostStrutturaInviaSottoretePassword cambiata con successo'%s' cancellata con successo (%i reti cancellate)Cancellazione di '%s' da '%s' riuscita (%i Reti cancellate)Gruppo '%s' cancellato con successoMaschera '%s' cancellata con successoUtente '%s' cancellato con successoSupportoDestinazioneRadice di destinazioneMaschera '%s' per '%s'Maschera per le DescrizioniGestione TemplateMaschereTestoArea di testoCampo di testoHaCi Daemon non e` stato avviato.La dimensione minima di %s e` /%sCi sono ancora dati per questa Maschera. Prima cancellali! (%s)Ci sono ancora Utenti in questo Gruppo. Li devi prima rimuovere dal Gruppo! (%s)Questa Rete '%s' esiste gia'!Questa Rete esiste gia`!Questa Rete e` liberaQuesta Rete e` bloccataQuesta Rete e` riservataQuesto Plugin interroga a richiesta il DB Whois per la rete corrente e mostra il risultato.Questo Plugin interroga a richiestsa il DNS e mostra il record PTR dell'indirizzo IP corrente.Questo Plugin viene eseguito a tempo in background ed esegue il Ping per tutti gli indirizzi IP associati. Fornisce quindi la lista degli indirizzi IP e il loro stato.Questo Plugin viene eseguito a tempo in background e raccoglie i record PTR dal DNS per gli indirizzi IP associati e li salva nel DB. Fornisce quindi la lista di tutti gli indirizzi IP con il corrispondente record PTR.Questo Plugin esegue un Ping agli indirizzi IP associati e salva lo stato nel DB. Dopodiche' raccoglie i corrispondenti MAC address con SNMP da un router specifico e salva anche questi risultati nel DB. Fornisce quindi la lista di tutti gli indirizzi IP con il loro stato e il MAC address se disponibile. Con questo metodo puoi analizzare tutti gli host, anche se non rispondono ai ping.Questo Plugin eseguoe un Ping a richiesta e mostra lo stato dell'indirizzo IP corrente.Questa Radice contiene %i SottoretiQuesta non sembra una rete '%s'.Questo non sembra un indirizzo IP '%s'.Questa Rete contiene %i sottoretiTempo scadutoAlberoTipoInformazioni sul tipo di Rete %sUtente '%s'Gestione UtentiUtenteUtentiMostraMostra le ImpostazioniWildcardIncludi le SottoretiCon tutte le SottoretiZone transfer fallito: %s[ permesso negato ]attivoattivocompressodefaultforzainvisibileultima esecuzionea richiestaoleggiricorrentesasconosciutoscriviHaCi/locale/it/LC_MESSAGES/HaCi.po0000644000175000000000000005026311776645425015675 0ustar fighterrootdomain "HaCi" #: login msgid "Authentication" msgstr "Autenticazione" msgid "Language" msgstr "Lingua" msgid "Username" msgstr "Utente" msgid "Password" msgstr "Password" msgid "Authentication failed!" msgstr "Autenticazione fallita!" msgid "Authentication failed: User not found!" msgstr "Autenticazione fallita: non trovo l'Utente!" msgid "No User given" msgstr "Non hai specificato l'Utente" msgid "Session is expired" msgstr "Sessione scaduta" msgid "Authentication failed: Database Error!" msgstr "Autenticazione fallita: Errore del Database!" msgid "Authentication failed: Password doesn't match!" msgstr "Autenticazione fallita: Password errata!" msgid "Support" msgstr "Supporto" #: misc msgid "or" msgstr "o" msgid "NEW" msgstr "NUOVO" msgid "Users" msgstr "Utenti" msgid "New User" msgstr "Nuovo Utente" msgid "New Group" msgstr "Nuovo Gruppo" msgid "New Template" msgstr "Nuovo Template" msgid "Login" msgstr "Accesso" msgid "Save result under" msgstr "Salva i risultati come" msgid "Right" msgstr "Destra" msgid "Left" msgstr "Sinistra" msgid "Compare" msgstr "Confronta" msgid "Target" msgstr "Destinazione" msgid "Search,Compare" msgstr "Cerca, Confronta" msgid "Import ASN Routes" msgstr "Importa ASN routes" msgid "Import DNS Zonefile" msgstr "Importa DNS Zonefile" msgid "No IP Addresses found for Origin '%s'." msgstr "Non trovo indirizzi IP per Origin '%s'." msgid "Zone transfer failed: %s" msgstr "Zone transfer fallito: %s" msgid "Import Config" msgstr "Importa Configurazioni" msgid "No File given!" msgstr "Non hai specificato il File!" msgid "Cannot open File in Binmode: %s" msgstr "Non posso aprire il File in Binmode: %s" msgid "%i IP Addresses found for Origin '%s'. %i were successfully saved under Root '%s'" msgstr "Trovati %i indirizzi IP per Origin '%s'. %i salvati con success sotto la Radice '%s'." msgid "No Origin given or found!" msgstr "Non riesco a determinare Origin!" msgid "Edit Network" msgstr "Modifica la Rete" msgid "Show Root Details" msgstr "Mostra i dettagli della Radice" msgid "Show Roots" msgstr "Mostra le Radici" msgid "Show Networks" msgstr "Mostra le Reti" msgid "Show Network Details" msgstr "Mostra i dettagli della Rete" msgid "Edit Root" msgstr "Modifica la Radice" msgid "Root Name" msgstr "Nome della Radice" msgid "Cannot read ConfigFile '%s': %s" msgstr "Non riesco a leggere il file di configurazione '%s': %s" msgid "Abort" msgstr "Interrompi" msgid "Submit" msgstr "Invia" msgid "Change" msgstr "Cambia" msgid "New" msgstr "Nuovo" msgid "Delete" msgstr "Cancella" msgid "Combine" msgstr "Combina" msgid "DNS Info for %s" msgstr "Informazioni DNS per %s" msgid "RIPE Info for %s" msgstr "Informazioni da RIPE per %s" msgid "unknown" msgstr "sconosciuto" msgid "Nothing found" msgstr "Non trovo nulla" msgid "Error while deleting '%s' from '%s': %s" msgstr "Errore cercando di cancellare '%s' da '%s': %s" msgid "Successfully deleted '%s' from '%s' (%i Networks deleted)" msgstr "Cancellazione di '%s' da '%s' riuscita (%i Reti cancellate)" msgid "None" msgstr "Nessuno" msgid "invisible" msgstr "invisibile" msgid "read" msgstr "leggi" msgid "write" msgstr "scrivi" msgid "Group" msgstr "Gruppo" msgid "CSV Preview" msgstr "Anteprima CSV" msgid "Cannot parse any CSV Data" msgstr "Non riesco a legger alcun dato CSV" msgid "[ permission denied ]" msgstr "[ permesso negato ]" msgid "This Network is locked" msgstr "Questa Rete e` bloccata" msgid "This Network is reserved" msgstr "Questa Rete e` riservata" msgid "This Network is free" msgstr "Questa Rete e` libera" msgid "Contact" msgstr "Contatto" #: menu msgid "Back" msgstr "Indietro" msgid "Tree" msgstr "Albero" msgid "Overview" msgstr "Panoramica" msgid "Network" msgstr "Rete" msgid "Show all Nets" msgstr "Mostra tutte le Reti" msgid "Add root" msgstr "Aggiungi una Radice" msgid "Add network" msgstr "Aggiungi una Rete" msgid "Import" msgstr "Importa" msgid "ASN routes" msgstr "ASN Routes" msgid "Add network %s" msgstr "Aggiungu la Rete %s" msgid "Edit network %s" msgstr "Modifica la Rete %s" msgid "Separator" msgstr "Separatore" msgid "Miscellaneous" msgstr "Miscellanea" msgid "Search" msgstr "Cerca" msgid "Exact" msgstr "Esatto" msgid "Fuzzy Search" msgstr "Ricerca Fuzzy" msgid "Wildcard" msgstr "Wildcard" msgid "Maintenance" msgstr "Manutenzione" msgid "User management" msgstr "Gestione Utenti" msgid "Group management" msgstr "Gestione Gruppi" msgid "Logged in as %s" msgstr "Entrato come %s" msgid "Logout" msgstr "Esci" msgid "Random Tree" msgstr "Albero Casuale" msgid "Template management" msgstr "Gestione Template" msgid "Flush cache" msgstr "Svuota la Cache" msgid "About HaCi" msgstr "About HaCi" #: Import msgid "Import from DNS Zonefiles" msgstr "Importa da DNS Zonefiles" msgid "Local Zonefile" msgstr "Zonefile locale" msgid "%i Routes found. Saved %i new Inetnums of %i found" msgstr "Trovate %i Rotte. Salvati %i nuovi Inetnums di %i" msgid "Import from Config File" msgstr "Importa da File di Configurazione" msgid "Source" msgstr "Sorgente" msgid "Config" msgstr "Configurazione" msgid "Cannot add Root" msgstr "Non posso aggiungere la Radice" msgid "AS Number" msgstr "AS Number" msgid "Remove obsoletes" msgstr "Rimuovi obsoleti" msgid "Only insert new" msgstr "Inserisci solo i nuovi" msgid "No Config given!" msgstr "Non hai specificato il File di Configurazione!" #: addRoot msgid "Name of Root" msgstr "Nome della Radice" msgid "Description" msgstr "Descrizione" msgid "Sorry, you have to give me a name!" msgstr "Per favore specifica un nome!" msgid "Details of Root %s" msgstr "Dettagli della Radice %s" msgid "Number of Networks" msgstr "Numero di Reti" msgid "Sorry, this Root '%s' allready exists!" msgstr "Spiacente, esiste gia' una root '%s'!" #: addNet msgid "Available Variables from the Plugins" msgstr "Variabili disponibili dai Plugin" msgid "Description Helper" msgstr "Aiuto per la Descrizione" msgid "Insert" msgstr "Inserisci" msgid "Order" msgstr "Ordina" msgid "New Line" msgstr "Nuova Linea" msgid "Netaddress" msgstr "Indirizzo di Rete" msgid "Netmask" msgstr "Netmask" msgid "CIDR" msgstr "CIDR" msgid "Target Root" msgstr "Radice di destinazione" msgid "Sorry, you have to give me a %s!" msgstr "Spiacente, mi devi dare un %s" msgid "Sorry, this is not a correct Network: %s!" msgstr "Spiacente, questa Rete non e` corretta: %s!" msgid "Sorry, this doesn't look like a %s: '%s'!" msgstr "Spiacente, questo non sembra essere un %s: '%s'" msgid "Access Rights" msgstr "Diritti d'accesso" msgid "This Network '%s' allready exists!" msgstr "Questa Rete '%s' esiste gia'!" msgid "Not enough rights to edit this Network '%s'" msgstr "Non hai abbastanza privilegi per modificare questa Rete '%s'." msgid "Not enough rights to add this Network '%s' under '%s'" msgstr "Non hai abbastanza privilegi per aggiungere la Rete '%s' sotto '%s'" msgid "Def. Subnet CIDR" msgstr "Def. Subnet CIDR" msgid "Default Subnet CIDR" msgstr "Default Subnet CIDR" msgid "The minimum %s size is /%i!" msgstr "La dimensione minima di %s e` /%s" msgid "%ss can only be made from allocations with a status of %s" msgstr "%ss puo' essere definito solo a partire da allocazioni con stato %s" msgid "%ss can only be made when there's no less specific inetnum (%s) with an 'Assigned' status" msgstr "%ss puo' essere definito solo se non ci sono inetnum meno specifici (%s) con stato 'Assigned'" msgid "%ss can only be made when there's no more specific inetnum (%s) with an 'Assigned' status" msgstr "%ss puo' essere definito solo se non ci sono inetnum piu' specifici (%s) con stato 'Assigned" msgid "%s is a new Value for inet6num Objects and so it's not available for inetnum Objects" msgstr "%s e` un nuovo valore per oggetti inet6num e non e` disponibile per oggetti inetnum" msgid "%ss can only be made when there's no less specific inetnum (%s) with a status of '%s'" msgstr "%ss puo' essere definito solo se non ci sono inetnum meno specifici (%s) con stato '%s'" msgid "force" msgstr "forza" #: treeMenu msgid "Menu" msgstr "Menu'" msgid "Edit Menu" msgstr "Menu' delle modifiche" msgid "Close Tree" msgstr "Chiudi l'albero" msgid "Jump To" msgstr "Salta a" msgid "Edit" msgstr "Modifica" msgid "Finish Edit" msgstr "Termina le modifiche" msgid "Copy" msgstr "Copia" msgid "Move" msgstr "Sposta" msgid "to" msgstr "a" msgid "Delete after Copy" msgstr "Cancella dopo la Copia" msgid "Expand Network '%s'" msgstr "Espandi la Rete '%s'" msgid "Reduce Network '%s'" msgstr "Riduci la Rete '%s'" msgid "Expand Root '%s'" msgstr "Espandi la Radice '%s'" msgid "Reduce Root '%s'" msgstr "Riduci la Radice '%s'" #: showNet msgid "Show" msgstr "Mostra" msgid "Show Subnets" msgstr "Mostra le Sottoreti" msgid "# of free Subnets with CIDR '%s'" msgstr "# di sottoreti libere con CIDR '%s'" msgid "Routing Prefix" msgstr "Routing Prefix" msgid "Subnet" msgstr "Sottorete" msgid "Customer Address Space" msgstr "Spazio di indirizzamento per Clienti" msgid "compressed" msgstr "compresso" msgid "Details of Network %s" msgstr "Dettagli della Rete %s" msgid "Functions" msgstr "Funzioni" msgid "Reverse DNS lookup" msgstr "Interroga il DNS inverso" msgid "Type" msgstr "Tipo" msgid "Type Infos of Network %s" msgstr "Informazioni sul tipo di Rete %s" msgid "Split" msgstr "Suddividi" msgid "Split Details" msgstr "Dettagli della suddivisione" msgid "Split Network into these pieces:" msgstr "Suddividi la Rete in questi pezzi:" msgid "Template for the Descriptions" msgstr "Maschera per le Descrizioni" msgid "Delete this network" msgstr "Cancella questa Rete" msgid "Settings for the new Networks" msgstr "Impostazioni per le nuove Reti" msgid "# of available Adresses" msgstr "# di indirizzi disponibili" #: delNet msgid "Do you really want to delete the Network '%s'?" msgstr "Vuoi veramente cancellare la Rete '%s'?" msgid "With all Subnets" msgstr "Con tutte le Sottoreti" msgid "This network contains %i subnets" msgstr "Questa Rete contiene %i sottoreti" #: editRoot msgid "Edit Root %s" msgstr "Modifica la Radice %s" msgid "Root" msgstr "Radice" #: delRoot msgid "Error while deleting '%s': %s" msgstr "Errore durante la cancellazione di '%s': %s" msgid "Successfully deleted '%s' (%i Networks deleted)" msgstr "'%s' cancellata con successo (%i reti cancellate)" msgid "This Root has %i Subnets" msgstr "Questa Radice contiene %i Sottoreti" msgid "Do you really want to delete the Root %s?" msgstr "Vuoi cancellare veramente la Radice %s" #: showTemplates msgid "Templates" msgstr "Maschere" msgid "Nettype" msgstr "Tipo di Rete" msgid "There are still Entries for this Template left. Please delete them first! (%s)" msgstr "Ci sono ancora dati per questa Maschera. Prima cancellali! (%s)" #: editTemplate msgid "Add" msgstr "Aggiungi" msgid "Replace" msgstr "Rimpiazza" msgid "Template '%s' for '%s'" msgstr "Maschera '%s' per '%s'" msgid "New " msgstr "Nuovo " msgid "Preview" msgstr "Anteprima" msgid "Position" msgstr "Posizione" msgid "No Template for this ID '%i' available!" msgstr "Non ci sono Maschere on ID '%i'!" msgid "Parameters" msgstr "Parametri" msgid "Size" msgstr "Dimensione" msgid "Entries (separated with semicolons)" msgstr "Dati (separati da punto e virgola)" msgid "HLine" msgstr "HLine" msgid "Textfield" msgstr "Campo di testo" msgid "Textarea" msgstr "Area di testo" msgid "Popup-Menu" msgstr "Popup-Menu'" msgid "Text" msgstr "Testo" msgid "Sorry, you have to give me a Name!" msgstr "Devi fornirmi un nome!" msgid "Do you really want to delete the Template %s?" msgstr "Vuoi veramente cancellare la Maschera %s?" msgid "Successfully deleted Template '%s'" msgstr "Maschera '%s' cancellata con successo" msgid "No Templates deleted. Nothing found!" msgstr "Nessuna Maschera cancellata, non ne ho trovate!" msgid "Rows" msgstr "Righe" msgid "Columns" msgstr "Colonne" msgid "Created from" msgstr "Creatp da" msgid "Created on" msgstr "Creato il" msgid "Modified by" msgstr "Modificato da" msgid "Modified on" msgstr "Modificato il" msgid "Structure" msgstr "Struttura" #: ShowGroups msgid "Groups" msgstr "Gruppi" msgid "Group '%s'" msgstr "Gruppo '%s'" msgid "Permissions" msgstr "Permessi" #: delGroup msgid "There are still Users in this Group left. Please remove them from this Group first! (%s)" msgstr "Ci sono ancora Utenti in questo Gruppo. Li devi prima rimuovere dal Gruppo! (%s)" msgid "No Group deleted. Nothing found!" msgstr "Nessun gruppo cancellato, non ne ho trovati!" msgid "Successfully deleted Group '%s'" msgstr "Gruppo '%s' cancellato con successo" msgid "Do you really want to delete the Group %s?" msgstr "Vuoi veramente cancellare il Gruppo %s?" #: editUser msgid "User '%s'" msgstr "Utente '%s'" msgid "Group Association" msgstr "Associazione al Gruppo" msgid "Password Validation" msgstr "Verifica Password" msgid "No Password given!" msgstr "Manca la Password!" msgid "Password are not equal" msgstr "Le Password non sono uguali!" msgid "Password is only for buildin 'HaCi' authentication!" msgstr "La Password e` solo per l'autenticazione incorporata in 'HaCi'!" msgid "Not Authenticated!!!" msgstr "Non Autenticato!!!" #: delUser msgid "No User deleted. Nothing found!" msgstr "Nessun Utente cancellato, non ne ho trovati!" msgid "Successfully deleted User '%s'" msgstr "Utente '%s' cancellato con successo" msgid "Do you really want to delete the User %s?" msgstr "Vuoi veramente cancellare l'Utente '%s'?" #: showAuthMeths msgid "Authentication Management" msgstr "Gestione dell'Autenticazione" msgid "Authentication Methods" msgstr "Metodi di Autenticazione" #: combineNets msgid "Combine Networks" msgstr "Combina le Reti" msgid "%i. Possibility" msgstr "%i. Possibilita'" msgid "Combine these networks" msgstr "Combina queste Reti" msgid "Settings for the new Network" msgstr "Impostazioni per la nuova Rete" msgid "Netaddress doesn't match!" msgstr "L'indirizzo di rete non corrisponde!" msgid "Foreign Networks will be
included (e.g. '%s')" msgstr "Reti Estranee saranno
incluse (es. '%s')" msgid "This Network allready exists!" msgstr "Questa Rete esiste gia`!" #: pluginMgmt msgid "Plugin management" msgstr "Gestione Plugin" msgid "Configure Plugin '%s'" msgstr "Configura il Plugin '%s'" #: plugins msgid "Global Configuration of '%s'" msgstr "Configurazione globale di'%s'" msgid "Configuration of '%s' for '%s'" msgstr "Configurazione di '%s' per '%s'" msgid "Status of Host" msgstr "Stato degli Host" msgid "Alive" msgstr "Attivo" msgid "alive" msgstr "attivo" msgid "Dead" msgstr "Inattivo" msgid "Ping Info for %s" msgstr "Informazioni Ping per %s" msgid "active" msgstr "attivo" msgid "default" msgstr "default" msgid "Default" msgstr "Default" msgid "on Demand" msgstr "a richiesta" msgid "recurrent" msgstr "ricorrente" msgid "last Run" msgstr "ultima esecuzione" msgid "Configure" msgstr "Configura" msgid "Reset last run" msgstr "Azzera l'orario di ultima esecuzione" #plugins msgid "DNS PTR-Record for current IP address" msgstr "DNS PTR-Record per l'indirizzo IP corrente" msgid "Ping Status of current IP address (dead|alive)" msgstr "Stato del Ping Status per l'indirizzo IP corrente (attivo|inattivo)" msgid "Mac Address related to the IP address" msgstr "Mac Address relativo all'indirizzo IP" msgid "Infos for Plugin '%s'" msgstr "Informazioni per il Plugin '%s'" msgid "This Plugin will ping associated IP adresses and saves its status in DB. After that, it will collect the corresponding MAC addresses with SNMP from a specific router and save the result in DB, too. In the Output there were all IP addresses listed with their status and MAC address if available. With this method you catch all available Hosts, even if they deny ping." msgstr "Questo Plugin esegue un Ping agli indirizzi IP associati e salva lo stato nel DB. Dopodiche' raccoglie i corrispondenti MAC address con SNMP da un router specifico e salva anche questi risultati nel DB. Fornisce quindi la lista di tutti gli indirizzi IP con il loro stato e il MAC address se disponibile. Con questo metodo puoi analizzare tutti gli host, anche se non rispondono ai ping." msgid "This Plugin runs recurrent in background and collects DNS PTR-Records for its associated IP Addresses an saves them in DB. In the Output will all IP Adresses be listet with their corresponding PTR-Records." msgstr "Questo Plugin viene eseguito a tempo in background e raccoglie i record PTR dal DNS per gli indirizzi IP associati e li salva nel DB. Fornisce quindi la lista di tutti gli indirizzi IP con il corrispondente record PTR." msgid "This Plugin queries on demand the DNS PTR-Record for the current IP adress and displays it." msgstr "Questo Plugin interroga a richiestsa il DNS e mostra il record PTR dell'indirizzo IP corrente." msgid "This Plugins pings the current IP adress on Demand and displays its status." msgstr "Questo Plugin eseguoe un Ping a richiesta e mostra lo stato dell'indirizzo IP corrente." msgid "This Plugin runns recurrent in background and pings all associated IP addresses. In the Output it will display all IP addresses with their Status." msgstr "Questo Plugin viene eseguito a tempo in background ed esegue il Ping per tutti gli indirizzi IP associati. Fornisce quindi la lista degli indirizzi IP e il loro stato." msgid "This Plugin queries on Demand the Whois DB for the current network and displays the Result." msgstr "Questo Plugin interroga a richiesta il DB Whois per la rete corrente e mostra il risultato." msgid "IP addresses" msgstr "Indirizzi IP" msgid "MACs found" msgstr "MAC trovati" msgid "ARP Entry" msgstr "ARP Entry" msgid "Nr of Bytes (0-1024)" msgstr "N. di Byte (0-1024)" msgid "Timeout" msgstr "Tempo scaduto" msgid "Protocoll" msgstr "Protocollo" msgid "Router for SNMP-Connection" msgstr "Router per le funzioni SNMP" msgid "With Subnetworks" msgstr "Includi le Sottoreti" msgid "Only IP addresses" msgstr "Solo gli indirizzi IP" msgid "Max Depth" msgstr "Livello massimo" msgid "Runtime" msgstr "Tempo di esecuzione" msgid "sec" msgstr "s" msgid "Close Error Details" msgstr "Chiudi il dettaglio degli errori" msgid "Error Details for Plugin %s" msgstr "Dettaglio degli errori per il Plugin %s" msgid "Error Details" msgstr "Dettaglio degli errori" msgid "Error" msgstr "Errori" msgid "IP address" msgstr "Indirizzo IP" msgid "Error while loading Plugin '%s'. Details in Error Logfile." msgstr "Errore nel caricamento del Plugin '%s'. I dettagli sono nel Log degli Errori." msgid "This doesn't look like a network '%s'." msgstr "Questa non sembra una rete '%s'." msgid "Program Whois '%s' isn't executable" msgstr "Il programma Whois '%s' non e` eseguibile" msgid "This doesn't look like an IP address '%s'." msgstr "Questo non sembra un indirizzo IP '%s'." msgid "Defaults for mode 'recurrent'" msgstr "Default per la modalita' 'ricorrente'" msgid "Defaults for mode 'on Demand'" msgstr "Default per la modalita' 'a richiesta'" msgid "The HaCi Daemon is not started." msgstr "HaCi Daemon non e` stato avviato." #: showSubnets msgid "Show free Subnets of '%s' with CIDR '%s'" msgstr "Mostra le Sottoreti libere di '%s' con CIDR '%s'" msgid "CIDR (Subnet Size)" msgstr "CIDR (dimensione della Sottorete)" msgid "Free Subnets" msgstr "Sottoreti Libere" msgid "No free Subnets with this CIDR available" msgstr "Non ci sono Sottoreti libere con questo CIDR" msgid "Create" msgstr "Crea" msgid "No." msgstr "No." #: showSettings msgid "Settings" msgstr "Impostazioni" msgid "Change own Password" msgstr "Cambia la propria Password" msgid "Old Password" msgstr "Vecchia Password" msgid "New Password" msgstr "Nuova Password" msgid "Old Password is not correct!" msgstr "La vecchia Password non e` corretta!" msgid "Sorry, Username not found!" msgstr "Utente non trovato!" msgid "Successfully changed Password" msgstr "Password cambiata con successo" msgid "Cannot change Password: %s" msgstr "Non posso cambiare la Password : %s" msgid "View" msgstr "Mostra" msgid "View Settings" msgstr "Mostra le Impostazioni" msgid "Show Tree Structure" msgstr "Mostra la struttura dell'Albero" msgid "Errors while updating Setting for '%s': %s" msgstr "Errori durante l'aggiornamento delle impostazioni per '%s': %s" HaCi/logs/0000775000175000000410000000000012475466225012602 5ustar fighterwww-dataHaCi/modules/0000755000175000000000000000000012475447616012532 5ustar fighterrootHaCi/modules/DBIEasy.pm0000644000175000000000000002451011776645425014313 0ustar fighterrootpackage DBIEasy; use strict; use Carp qw/carp croak/; use DBI; use Data::Dumper; use HaCi::Conf qw/getConfigValue/; my $dbHandles = {}; my $initConf = {}; our $lastError = ''; BEGIN { no strict 'refs'; foreach my $attr (qw/error errorStrs dbhost dbname dbuser dbpass dbtype PK dbh cols2Meths meths2Cols/) { *{$attr} = sub { my $proto = shift; my $value = shift; my $self = undef; my $caller = (caller)[0]; if (ref $proto) { $self = $proto; if (defined $value) { $self->{$attr} = $value; } else { return $self->{$attr}; } } else { if (defined $value) { $initConf->{$proto}->{$attr} = $value; } else { return $initConf->{$proto}->{$attr} = $value; } } } } } sub new { my $proto = shift; my $config = shift; my $self = undef; if (ref $proto) { $self = $proto; } else { $self = (ref $initConf->{$proto} eq 'HASH') ? $initConf->{$proto} : {}; unless (ref $self eq 'HASH') { _carp("Database Error"); return undef; } bless $self, $proto; } if (ref $config eq 'HASH') { $self->{CONFIG} = $config; } return undef unless $self->init(); my $dbh = $self->getDBConn(); if ($dbh) { DBI->trace(&getConfigValue('misc', 'dbitrace')); $self->dbh($dbh); if ($self->initChecks()) { $self->PK($self->getPK()); $self->initSubRefs(); return $self; } } return undef; } sub getDBConn { my $self = shift; my $dbh = undef; if ($dbHandles->{$$}->{$self->dbhost}->{$self->dbname}) { $dbh = $dbHandles->{$$}->{$self->dbhost}->{$self->dbname}; $self->_carp("Taking allready connected database handle: $dbh ($$: " . $self->TABLE() . ")") if 0; $self->dbh($dbh); unless ($dbh->ping) { $self->_carp("Mhh, that old handle doesn't smell well. Better taking a new one!"); $dbh = undef; } } unless (defined $dbh) { $dbh = DBI->connect($self->getDSN(), { PrintError => 0, AutoCommit => 1 }); $self->_croak($DBI::errstr) unless $dbh; $self->checkForOldHandles($dbh) if defined $dbh; } $dbHandles->{$$}->{$self->dbhost}->{$self->dbname} = $dbh; $self->dbh($dbh); return $dbh; } sub init { my $self = shift; foreach ('dbhost', 'dbname', 'dbuser', 'dbpass', 'dbtype') { my $key = $_; $self->{CONFIG}->{$key} = 'mysql' if $key eq 'dbtype' && !defined $self->{CONFIG}->{$key}; unless ($self->{$key}) { if ($self->{CONFIG}->{$key}) { $self->$key($self->{CONFIG}->{$key}); } else { $self->_carp("No '$key' defined"); return 0; } } } return 1; } sub mapColNames { my $self = shift; my @columns = @_; my @mappedColumns = (); my $col2Meth = $self->col2Meth(); return @columns unless $self->can('columnMapping'); my $columnMapping = $self->columnMapping(); die "columnMapping in " . ref($self) . " is not a valid hash!" unless ref($columnMapping) eq 'HASH'; foreach (keys %{$columnMapping}) { $col2Meth->{$columnMapping->{$_}} = $_; } map { $_ = $col2Meth->{$_} if exists $col2Meth->{$_} && $col2Meth->{$_}; } @columns; $self->cols2Meths($col2Meth); $self->meths2Cols($columnMapping); return @columns; } sub meth2Col { my $self = shift; my $meth = shift; my $meths2Cols = $self->meths2Cols(); return (exists $meths2Cols->{$meth}) ? $meths2Cols->{$meth} : $meth; } sub col2Meth { my $self = shift; my $col = shift; my $cols2Meths = $self->cols2Meths(); return (exists $cols2Meths->{$col}) ? $cols2Meths->{$col} : $col; } sub initSubRefs { my $self = shift; no strict 'refs'; foreach ($self->mapColNames($self->getColoumns())) { my $col = $_; next unless $col; unless ($self->can($col)) { *{(ref $self) . "::$col"} = sub { my $self = shift; my $value = shift; if (defined $value) { $self->{COLOUMNS}->{$col} = $value; } else { $self->{COLOUMNS}->{$col} = undef; } } } } } sub initChecks { my $self = shift; my $dbh = $self->dbh; my $error = 0; $error = 1 unless $self->checkIfTableExists(); return ($error == 0) ? 1 : 0; } sub clear { my $self = shift; foreach (keys %{$self->{COLOUMNS}}) { $self->$_(undef); } $self->error(0); $self->errorStrs(''); } sub count { my $self = shift; my $qryRestr = shift || 0; my $bLike = shift || 0; my $appStr = shift || 0; my $distinct = shift || 0; return $self->search('%COUNT%', $qryRestr, $bLike, $appStr, $distinct); } sub search { my $self = shift; my $qryCols = shift || ['*']; my $qryRestr = shift || 0; my $bLike = shift || 0; my $appStr = shift || 0; my $distinct = shift || 0; my $pk = $self->PK; my $dbh = $self->dbh; my $table = $self->TABLE; my @rows = (); my @whereStr = (); my @whereVals = (); my $con = 'AND'; my $cs = ''; my $count = 0; if (ref($qryCols) eq '' && $qryCols eq '%COUNT%') { $qryCols = []; $count = 1; } # map db method to db column foreach (@{$qryCols}) { $_ = $self->meth2Col($_) } unless ($count) { foreach (@$pk) { my $pkt = $_; push @$qryCols, $pkt unless grep {/^$pkt$/} @$qryCols; } } if ($qryRestr) { if (ref $qryRestr eq 'HASH') { foreach (keys %{$qryRestr}) { if ($_ eq '%CON%') { $con = $qryRestr->{$_}; } elsif ($_ eq '%CS%') { $cs = $qryRestr->{$_}; } else { push @whereStr, $self->meth2Col($_) . (($bLike) ? ' LIKE ' : ' = ') . '?'; push @whereVals, $qryRestr->{$_}; } } } elsif (ref $qryRestr eq '') { $whereStr[0] = $qryRestr; } } @whereStr = $self->mkCaseSensitive(@whereStr) if $cs; my $prStr = "SELECT " . (($count) ? 'COUNT(*) as cnt' : '') . (($distinct) ? 'DISTINCT ' : '') . join(',', @$qryCols) . ' FROM ' . $dbh->quote_identifier($table) . (($#whereStr > -1) ? ' WHERE ' . join(" $con ", @whereStr) : '' ); $prStr .= ' ' . $appStr if $appStr; warn "SEARCH: " . join('', split(/\n/, $prStr)) . " < @whereVals" if 0; my $sth = $dbh->prepare($prStr); $self->_carp($dbh->errstr()) if $dbh->errstr(); $sth->trace('SQL') if 0; $sth->execute(@whereVals); $self->_carp($dbh->errstr()) if $dbh->errstr(); if ($count) { my $cnter = $sth->fetchall_arrayref(); return ${${$cnter}[0]}[0]; } my $hashRef = $sth->fetchall_hashref($pk); $self->_carp($dbh->errstr()) if $dbh->errstr(); my $pkCnter = 1; push @rows, $self->getRows($hashRef, $pkCnter); warn "RETURN: " . Dumper(@rows) if 0; return @rows; } sub getRows { my $self = shift; my $hashRef = shift; my $pkCnter = shift; my @rows = (); return @rows unless ref $hashRef eq 'HASH'; foreach (keys %{$hashRef}) { my $cPK = $_; if ($pkCnter < ($#{$self->{PK}} + 1)) { push @rows, $self->getRows($hashRef->{$cPK}, ($pkCnter + 1)); } else { my $mappedHash = {}; foreach (keys %{$hashRef->{$cPK}}) { my $col = $_; $mappedHash->{$self->col2Meth($col)} = $hashRef->{$cPK}->{$col}; } push @rows, $mappedHash; } } return @rows; } sub insert { my $self = shift; return $self->modify('INSERT'); } sub update { my $self = shift; my $rowRestr = shift; my $bLike = shift; my $appStr = shift || 0; return $self->modify('UPDATE', $rowRestr, $bLike, $appStr); } sub delete { my $self = shift; my $rowRestr = shift; my $bLike = shift; my $appStr = shift || 0; return $self->modify('DELETE', $rowRestr, $bLike, $appStr); } sub modify { my $self = shift; my $type = shift; my $rowRestr = shift || 0; my $bLike = shift || 0; my $appStr = shift || 0; my $dbh = $self->dbh; my $table = $self->TABLE; my $col2Meth = $self->col2Meth(); my @whereStr = (); my @whereVals = (); my $modStr = $type; my $con = 'AND'; $modStr .= ' INTO' if $type eq 'INSERT'; $modStr .= ' FROM' if $type eq 'DELETE'; $modStr .= ' ' . $dbh->quote_identifier($table); $modStr .= ' SET' if $type eq 'UPDATE'; if ($rowRestr) { if (ref $rowRestr eq 'HASH') { foreach (keys %{$rowRestr}) { my $row = $_; if ($row eq 'CON') { $con = $rowRestr->{CON}; } else { push @whereStr, $self->meth2Col($row) . (($bLike) ? ' LIKE ' : ' = ') . '?'; push @whereVals, $rowRestr->{$row}; } } } elsif (ref $rowRestr eq '') { $whereStr[0] = $rowRestr; } } $self->_log("$type in $table") if 0; my @cols = (); my @vals = (); foreach (keys %{$self->{COLOUMNS}}) { my $value = $self->{COLOUMNS}->{$_}; if (defined $value) { my $col = $self->meth2Col($_); if ($type eq 'UPDATE') { $modStr .= ',' if $modStr !~ / SET$/; $modStr .= " $col = ?"; } unless ($type eq 'DELETE') { push @cols, $col; push @vals, $value; } } } if ($type eq 'INSERT') { $modStr .= ' (' . join(', ', @cols) . ') VALUES (' . join(', ', map {'?'} (1 .. scalar @vals)) . ')' } $modStr .= ' WHERE ' . join(" $con ", @whereStr) if $#whereStr > -1; $modStr .= ' ' . $appStr if $appStr; warn "DB-UPDATE: $modStr (" . join(', ', @vals) . '; ', join(', ', @whereVals) if 0; my $sth = $dbh->prepare($modStr); $self->_carp($dbh->errstr()) if $dbh->errstr(); $sth->trace('SQL') if 0; my $rows = $sth->execute(@vals, @whereVals); $self->_carp($dbh->errstr()) if $dbh->errstr(); return $rows; } sub alter { my $self = shift; my $altStr = shift; my $dbh = $self->dbh; warn $altStr if 0; my $rows = $dbh->do($altStr); $self->_carp($dbh->errstr()) if $dbh->errstr(); return $rows; } sub _log { my $self = shift, my @msg = @_; $self->_carp(@_); } sub _carp { my $self = shift; my ($message, %info) = @_; if (UNIVERSAL::isa($self, 'UNIVERSAL')) { $self->error(1); $self->errorStrs($message); $lastError .= $message; warn "DB " . (ref $self) . ' [' . (caller(4))[3] . '->' . (caller(3))[3] . '->' . (caller(2))[3] . ':' . (caller(1))[2] . '->' . (caller(1))[3] . ':' . (caller(0))[2] . "]: $message"; } else { Carp::carp($message || $self); } return; } sub _croak { my $self = shift; my ($message, %info) = @_; chomp($message); if (UNIVERSAL::isa($self, 'UNIVERSAL')) { $self->error(1); $self->errorStrs($message); $lastError .= $message; warn "DB " . (ref $self) . ": $message\n"; } else { Carp::croak($message || $self); } return; } END { foreach (keys %{$dbHandles}) { my $host = $_; foreach (keys %{$dbHandles->{$$}->{$host}}) { my $name = $_; if ($dbHandles->{$$}->{$host}->{$name}) { _carp("Disconnecting from DB $host:$name") if 0; $dbHandles->{$$}->{$host}->{$name}->disconnect(); } } } } 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/DBIEasy/0000755000175000000000000000000012475447616013752 5ustar fighterrootHaCi/modules/DBIEasy/mysql.pm0000644000175000000000000000630211772710410015436 0ustar fighterrootpackage DBIEasy::mysql; use strict; use warnings; use base 'DBIEasy'; my $queryTimeout = 300; sub getDSN { my $self = shift; return ( 'DBI:mysql:' . 'database=' . $self->dbname . ';host=' . $self->dbhost, $self->dbuser, $self->dbpass ); } sub checkIfTableExists { my $self = shift; my $dbh = $self->dbh; my $table = $self->TABLE; my @tables = $dbh->tables(undef, undef, $table); unless (grep {/^(`?$self->{dbname}`?.)?`?$table`?$/} @tables) { if ($self->SETUPTABLE) { $self->_carp("Table '$table' doesn't exist, try to create it...\n"); my $createStmt = "CREATE TABLE `$table` (" . $self->CREATETABLE . ') ENGINE=InnoDB DEFAULT CHARSET=utf8'; $dbh->do($createStmt) or $self->_croak($dbh->errstr); my @tables = $dbh->tables(undef, undef, $table); unless (grep {/^(`?$self->{dbname}`?.)?`?$table`?$/} @tables) { $self->_croak("Sorry, after creating the Table '$table', it isn't there?"); return 0; } } else { $self->_croak("Table '$table' doesn't exist!"); return 0; } } return 1; } sub getPK { my $self = shift; my $dbh = $self->dbh; my $table = $self->TABLE; my @PKs = (); my $sth = $dbh->prepare("SHOW INDEX FROM $table"); $sth->execute; $self->_croak($dbh->errstr) if $dbh->errstr; if ($sth) { my $hashRef = $sth->fetchall_hashref(['Key_name', 'Seq_in_index']); foreach (keys %{$hashRef}) { my $key = $_; foreach (keys %{$hashRef->{$key}}) { if ($key eq 'PRIMARY') { push @PKs, $hashRef->{$key}->{$_}->{Column_name}; } } } } if ($#PKs == -1) { $self->_croak("No Primary Keys in Table '$table'!"); } else { return \@PKs; } } sub getShowCreate { my $self = shift; my $dbh = $self->dbh; my $table = $self->TABLE; my $sth = $dbh->prepare("SHOW CREATE TABLE `$table`"); $sth->execute; $self->_croak($dbh->errstr) if $dbh->errstr; if ($sth) { my $showCreatet = $sth->fetchall_arrayref([1]); if (defined $showCreatet) { my $showCreate = $$showCreatet[0]; return $$showCreate[0]; } else { $self->_carp("Sorry, Show Create Table '$table' doesn't work!"); return ''; } } } sub getColoumns { my $self = shift; my $dbh = $self->dbh; my $table = $self->TABLE; my $sth = $dbh->prepare("DESCRIBE `$table`"); $sth->execute; $self->_croak($dbh->errstr) if $dbh->errstr; if ($sth) { my $hashRef = $sth->fetchall_hashref('Field'); return ((defined $hashRef && ref $hashRef eq 'HASH') ? keys %{$hashRef} : ()); } else { $self->_carp("Sorry, no Columns found!"); return (); } } sub checkForOldHandles { my $self = shift; my $dbh = shift; my $sth_sh_proc = $dbh->prepare("show full processlist"); my $sth_kill = $dbh->prepare("kill ?"); unless ($sth_sh_proc->execute()) { _carp("Unable to execute show procs [" . $dbh->errstr() . "]"); return 0; } while (my $row = $sth_sh_proc->fetchrow_hashref()) { if ($row->{Command} eq 'Sleep' && $row->{Time} > $queryTimeout) { my $id = $row->{Id}; $sth_kill->execute($id); } } return 1; } sub mkCaseSensitive { my $self = shift; my @whereStr = @_; return map {$_ = 'binary ' . $_} @whereStr; } sub _log { DBIEasy::_log(@_); } sub _carp { DBIEasy::_carp(@_); } sub _croak { DBIEasy::_croak(@_); } 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/DBIEasy/postgresql.pm0000644000175000000000000000672411772710410016504 0ustar fighterrootpackage DBIEasy::postgresql; use strict; use warnings; use Data::Dumper; use base 'DBIEasy'; my $queryTimeout = 300; sub getDSN { my $self = shift; return ( 'DBI:Pg:' . 'database=' . $self->dbname . ';host=' . $self->dbhost, $self->dbuser, $self->dbpass ); } sub checkIfTableExists { my $self = shift; my $dbh = $self->dbh; my $table = lc($self->TABLE); my @tables = $dbh->tables(undef, undef, $table); unless (grep {/^("?[^\.\s]+"?.)?"?$table"?$/} @tables) { if ($self->SETUPTABLE) { $self->_carp("Table '$table' doesn't exist, try to create it...\n"); my $createStmt = "CREATE TABLE `$table` (" . $self->CREATETABLE . ') ENGINE=InnoDB DEFAULT CHARSET=utf8'; $dbh->do($createStmt) or $self->_croak($dbh->errstr); my @tables = $dbh->tables(undef, undef, $table); unless (grep {/^(`?$self->{dbname}`?.)?`?$table`?$/} @tables) { $self->_croak("Sorry, after creating the Table '$table', it isn't there?"); return 0; } } else { $self->_croak("Table '$table' doesn't exist!"); return 0; } } return 1; } sub getPK { my $self = shift; my $dbh = $self->dbh; my $table = lc($self->TABLE); my @PKs = (); my $sth = $dbh->prepare(qq{ SELECT f.attname AS pk FROM pg_attribute f JOIN pg_class c ON c.oid = f.attrelid LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY ( p.conkey ) WHERE c.relkind = 'r'::char AND p.contype = 'p' AND c.relname = ? AND f.attnum > 0 }); $sth->execute($table); $self->_croak($dbh->errstr) if $dbh->errstr; if ($sth) { my $primKeys = $sth->fetchall_arrayref({}); foreach (@{$primKeys}) { push @PKs, $_->{pk}; } } if ($#PKs == -1) { $self->_croak("No Primary Keys in Table '$table'!"); } else { return \@PKs; } } sub getColoumns { my $self = shift; my $dbh = $self->dbh; my $table = lc($self->TABLE); my $sth = $dbh->prepare(qq{ SELECT f.attname AS name FROM pg_attribute f JOIN pg_class c ON c.oid = f.attrelid WHERE c.relkind = 'r'::char AND c.relname = ? AND f.attnum > 0 }); $sth->execute($table); $self->_croak($dbh->errstr) if $dbh->errstr; my $cols = $sth->fetchall_arrayref({}); if ($#{$cols} > -1) { return map {$_->{name}} @{$cols}; } else { $self->_carp("Sorry, no Columns found!"); return (); } } sub checkForOldHandles { my $self = shift; my $dbh = shift; my $superUser = ${$dbh->selectall_arrayref(qq{ SELECT usesuper FROM pg_user WHERE usename=? }, { Slice => {} }, $self->dbuser)}[0]; # Killing old queries only makes sense if database user has superuser rights return unless defined $superUser && $superUser->{usesuper}; my $sth_sh_proc = $dbh->prepare(q{ SELECT EXTRACT(EPOCH FROM query_start)::integer as query_start, procpid FROM pg_stat_activity WHERE current_query='idle in transaction' }); my $sth_kill = $dbh->prepare("SELECT pg_cancel_backend(?)"); unless ($sth_sh_proc->execute()) { _carp("Unable to execute show procs [" . $dbh->errstr() . "]"); return 0; } while (my $row = $sth_sh_proc->fetchrow_hashref()) { my $queryAge = time - $row->{query_start}; if ($queryAge > $queryTimeout) { my $id = $row->{procpid}; $sth_kill->execute($id); } } return 1; } sub mkCaseSensitive { my $self = shift; my @whereStr = @_; # postgresql is case sensitive by default return @whereStr; } sub _log { DBIEasy::_log(@_); } sub _carp { DBIEasy::_carp(@_); } sub _croak { DBIEasy::_croak(@_); } 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/HaCi/0000755000175000000000000000000012475466456013341 5ustar fighterrootHaCi/modules/HaCi/Conf.pm0000644000175000000000000000355411341614314014546 0ustar fighterrootpackage HaCi::Conf; use strict; use Config::General qw(ParseConfig); require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(getConfigValue); our $conf = {}; #------------------------------------------------ sub init($) { my $workDir = shift; $conf = {}; $ENV{workdir} = $workDir; my $internalConfFile = $ENV{workdir} . '/etc/internal.conf'; unless (open ICONF, $internalConfFile) { die "Cannot open internal Config File '$internalConfFile': $!\n"; } my $data = join('', ); close ICONF; eval { my %config = ParseConfig( -String => $data, -LowerCaseNames => 1, -UseApacheInclude => 1, -IncludeRelative => 1, -IncludeDirectories => 1, -IncludeGlob => 1, -AutoTrue => 1, -InterPolateVars => 1, -InterPolateEnv => 1, -FlagBits => { status => {}, pluginDefaultOndemandMenu => [], pluginDefaultGlobRecurrentMenu => [], pluginDefaultRecurrentMenu => [], pluginDefaultGlobOndemandMenu => [], }, ); foreach (qw/plugindefaultglobondemandmenu plugindefaultglobrecurrentmenu plugindefaultrecurrentmenu plugindefaultondemandmenu/) { if (ref($config{static}{$_}) eq 'HASH') { if (keys %{$config{static}{$_}} == 0) { $config{static}{$_} = []; } else { $config{static}{$_} = [$config{static}{$_}]; } } } %{$conf} = %config; undef %config; }; if ($@) { die "Error while reading Config String (Conf.pm): $@\n"; } } sub getConfigValue { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my @keys = @_; return unless @keys; my $key = ''; foreach (@keys) { my $currKey = lc($_); $key .= "->{$currKey}"; } my $userKey = eval"\$conf->{user}$key"; my $staticKey = eval"\$conf->{static}$key"; return (defined $userKey) ? $userKey : $staticKey; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/HaCi.pm0000644000175000000000000010226112467504603014471 0ustar fighterrootpackage HaCi::HaCi; use strict; use CGI; # use CGI::Pretty; use CGI::Carp qw/fatalsToBrowser/; use CGI::Session; use CGI::Cookie; use CGI::Ajax; use Math::BigInt 1.87; use Storable qw(lock_store lock_retrieve); use Digest::MD5; use Math::Base85; use Encode; use HaCi::GUI::init; use HaCi::GUI::main qw/ checkNet expandNetwork reduceNetwork reduceRoot expandRoot showPlugin mkShowStatus mkSubmitImportASNRoutes /; use HaCi::GUI::authentication; use HaCi::GUI::gettext qw/_gettext/; use HaCi::Log qw/warnl debug/; use HaCi::Mathematics qw/ net2dec dec2net getBroadcastFromNet getNetmaskFromCidr dec2ip ip2dec getNetaddress netv62Dec netv6Dec2net /; use HaCi::Utils qw/ prWarnl delUser getRights importCSV compare checkDB newWindow prNewWindows setStatus removeStatus netv6Dec2ipv6ID addRoot addNet importASNRoutes getConfig checkSpelling_Net rootID2Name rootName2ID delNet genRandBranch editRoot delRoot copyNetsTo delNets search checkSpelling_IP checkSpelling_CIDR saveTmpl delTmpl saveGroup delGroup saveUser lwd dec2bin groupID2Name checkRight importDNSTrans importDNSLocal getNetworkParentFromDB importConfig rootID2ipv6 expand splitNet combineNets updatePluginDB getTable checkTables checkNetworkTable checkNetworkACTable checkNetACL getRoots mkPluginConfig initTables initCache chOwnPW updSettings updateSettingsInSession flushACLCache flushNetCache exportSubnets searchAndGetFreeSubnets getParam changeTmplName checkNetworkLock getFreeSubnets getNetID getDBNetworkBefore /; local our $q = undef; local our $pjx = undef; local our $session = undef; local our $aclCache = undef; local our $netCache = undef; local our $cache = undef; our $conf; *conf = \$HaCi::Conf::conf; my $netCacheHandle = undef; my $aclCacheHandle = undef; sub run { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $soap = shift || 0; my $soapArgs = shift || []; my $soapPreRun = shift; my $soapPostRun = shift; $conf->{var}->{soap} = $soap; &getConfig(); &init(); &soapInit($soapArgs) if $soap; if (1 && $conf->{static}->{misc}->{debug}) { use Data::Dumper qw/Dumper/; # Loading Dumper for Debugging... foreach ($q->param) { my $k = $_; next if $k eq 'password'; foreach ($q->param($k)) { warn " $k => $_ \n"; } } } # Dump if (&getParam(1, 0, 'func')) { if (&getParam(1, '', 'func') eq 'logout') { if (defined $session && $session->param('authenticated')) { &debug("logging out"); &removeStatus(); $session->delete(); $session->flush(); $conf->{var}->{relogin} = 1; # &genNewSession(); } else { &debug("allready logged out!"); } } } &HaCi::GUI::init::init(); &HaCi::GUI::init::setVars(); my $t = $HaCi::GUI::init::t; if ( !$conf->{var}->{relogin} && &authentication( &getParam(1, undef, 'username', 0, 1), &getParam(1, undef, 'password'), &getParam(1, undef, 'login'), $session->param('username') ) ) { &getRights(); &$soapPreRun($soapArgs, $q) if $soap && defined $soapPreRun; &checkRights(); &main(); } else { &checkTables(); &checkNetworkTable(); &checkNetworkACTable(); &HaCi::GUI::authentication::login(); $t->{V}->{page} = 'login'; } my $warnings = ''; my $localQ = $q; $warnings = &finalize(); return &$soapPostRun($soapArgs, $localQ, $t, $warnings) if $soap && defined $soapPostRun; } sub main { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; &HaCi::GUI::init::setUserVars(); if (&getParam(1, 0, 'fname')) { # AJAX call return; } my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; if (&getParam(1, 0, 'submitAddRoot')) { if (&addRoot( &getParam(1, undef, 'name'), &getParam(1, undef, 'descr'), &getParam(1, 0, 'ipv6'), &getParam(1, undef, 'rootID'), )) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param( 'func', ((&getParam(1, 0, 'editRoot')) ? 'editRoot' : 'addRoot') ); } } elsif (&getParam(1, 0, 'submitAddNet')) { if (&addNet( &getParam(1, 0, 'netID'), &getParam(1, undef, 'rootID'), &getParam(1, undef, 'netaddress'), &getParam(1, undef, 'cidr'), &getParam(1, '', 'descr'), &getParam(1, 0, 'state'), &getParam(1, 0, 'tmplID'), &getParam(1, 0, 'defSubnetSize'), &getParam(1, 0, 'forceState'), 0, 0, &getParam(1, '', 'tags'), )) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'addNet'); } } elsif (&getParam(1, 0, 'abortAddRoot')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'abortAddNet')) { if (&getParam(1, 0, 'editNet')) { $q->delete('func'); $q->param('func', 'showNet'); } else { $q->delete('func'); $q->param('func', 'showAllNets'); } } elsif (&getParam(1, 0, 'checktAddNet')) { &checkNet( &getParam(1, undef, 'netaddress'), &getParam(1, undef, 'cidr'), ); if (&getParam(1, 0, 'editNet')) { $q->delete('func'); $q->param('func', 'editNet'); } else { $q->delete('func'); $q->param('func', 'addNet'); } } elsif (&getParam(1, 0, 'submitImportASNRoutes')) { if ( &importASNRoutes( &getParam(1, undef, 'asn') ) ) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importASNRoutes'); } } elsif (&getParam(1, 0, 'submitImpDNSTrans')) { if (&importDNSTrans()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importDNS'); } } elsif (&getParam(1, 0, 'submitImpDNSLocal')) { if (&importDNSLocal()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importDNS'); } } elsif (&getParam(1, 0, 'submitImpConfig')) { if (&importConfig()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importConfig'); } } elsif (&getParam(1, 0, 'impCSVChangeSep')) { $q->delete('func'); $q->param('func', 'importConfig'); } elsif (&getParam(1, 0, 'impCSVChangeType')) { $q->delete('func'); $q->param('func', 'importConfig'); } elsif (&getParam(1, 0, 'abortImpCSV')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'submitImpCSV')) { if (&importCSV()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importConfig'); } } elsif (&getParam(1, 0, 'editPluginConf')) { $q->delete('func'); $q->param('func', 'showPluginConf'); } elsif (&getParam(1, 0, 'editNet')) { $q->delete('func'); $q->param('func', 'editNet'); } elsif (&getParam(1, 0, 'splitNet')) { $q->delete('func'); $q->param('func', 'splitNet'); } elsif (&getParam(1, 0, 'submitSplitNet')) { &splitNet( &getParam(1, undef, 'netID'), &getParam(1, undef, 'splitCidr'), &getParam(1, '', 'descrTemplate'), &getParam(1, 0, 'state'), &getParam(1, 0, 'tmplID'), &getParam(1, 0, 'delParentNet'), ); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'submitCombineNets')) { &combineNets(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'abortCombineNets')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'abortSplitNet')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif (&getParam(1, 0, 'combineNets')) { my $nets = &getParam(0, [], 'selectedNetworks'); if ($#{$nets} > 0) { $q->delete('func'); $q->param('func', 'combineNets'); } } elsif (&getParam(1, 0, 'abortEditRoot')) { $q->delete('func'); $q->param('func', 'showRoot'); } elsif (&getParam(1, 0, 'editRoot')) { $q->delete('func'); $q->param('func', 'editRoot'); } elsif (&getParam(1, 0, 'abortDelRoot')) { $q->delete('func'); $q->param('func', 'showRoot'); } elsif (&getParam(1, 0, 'abortDelNet')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif (&getParam(1, 0, 'delRoot')) { if (&getParam(1, 0, 'commitDelRoot')) { &delRoot( &getParam(1, undef, 'rootID') ); $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'delRoot'); } } elsif (&getParam(1, 0, 'delNet')) { if (&getParam(1, 0, 'commitDelNet')) { my $networkLock = &getParam(1, 0, 'networkLock'); if (defined $networkLock && int($networkLock) ne $networkLock) { &warnl(sprintf(_gettext("Network lock time must be provided in seconds not '%s'"), $networkLock)); $q->delete('func'); $q->param('func', 'delNet'); } else { &delNet( &getParam(1, undef, 'netID'), ((&getParam(1, 0, 'withSubnets')) ? 1 : 0), 0, &getParam(1, 0, 'networkLock') ); $q->delete('func'); $q->param('func', 'showAllNets'); } } else { $q->delete('func'); $q->param('func', 'delNet'); } } elsif (&getParam(1, 0, 'reduceRoot')) { &expand( '-', 'root', &getParam(1, undef, 'reduceRoot') ); $session->param('currNet', ''); $session->param( 'currRootID', &getParam(1, undef, 'reduceRoot') ); } elsif (&getParam(1, 0, 'expandRoot')) { &expand( '+', 'root', &getParam(1, undef, 'expandRoot') ); $session->param('currNet', ''); $session->param( 'currRootID', &getParam(1, undef, 'expandRoot') ); } elsif (&getParam(1, 0, 'reduceNetwork')) { &expand( '-', 'network', &getParam(1, undef, 'reduceNetwork'), &getParam(1, undef, 'rootID') ); $session->param( 'currNet', &getParam(1, undef, 'reduceNetwork') ); $session->param( 'currRootID', &getParam(1, undef, 'rootID') ); } elsif (&getParam(1, 0, 'expandNetwork')) { &expand( '+', 'network', &getParam(1, undef, 'expandNetwork'), &getParam(1, undef, 'rootID') ); $session->param( 'currNet', &getParam(1, undef, 'expandNetwork') ); $session->param( 'currRootID', &getParam(1, undef, 'rootID') ); } elsif (&getParam(1, 0, 'closeTree')) { &expand('-', 'ALL', 'ALL'); } elsif (&getParam(1, 0, 'jumpToButton')) { &expandTo( &getParam(1, undef, 'rootIDJump'), &getParam(1, undef, 'jumpTo') ); } elsif (&getParam(1, 0, 'genRandBranch')) { &genRandBranch(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'finishEditTree')) { $q->delete('editTree'); } elsif (&getParam(1, 0, 'copyNetsTo')) { ©NetsTo( &getParam(1, undef, 'copyToRootID'), &getParam(0, [], 'selectedNetworks'), 0 ); } elsif (&getParam(1, 0, 'moveNetsTo')) { ©NetsTo( &getParam(1, undef, 'copyToRootID'), &getParam(0, [], 'selectedNetworks'), 1 ); } elsif (&getParam(1, 0, 'deleteNets')) { &delNets(&getParam(0, [], 'selectedNetworks')); } elsif (&getParam(1, 0, 'searchButton')) { if ((&getParam(1, '', 'func') eq 'getFreeSubnetsFromSearch')) { &searchAndGetFreeSubnets(); $t->{V}->{searchResult} = $t->{V}->{freeSubnets}; } else { &search(); } } elsif (&getParam(1, 0, 'compareButton')) { &compare(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'newTmpl')) { my $tmplName = &getParam(1, '', 'tmplName'); my $exists = 0; if ($tmplName) { $exists = &checkIfExistsInDB('template', 'name', $tmplName); &warnl("Template '$tmplName' already exists!") if $exists; } if ($tmplName && !$exists) { $q->delete('tmplID'); my $tmplName = &getParam(1, '', 'tmplName'); $tmplName =~ s/[<>&"']//g; $q->delete('tmplName'), $q->param('tmplName', $tmplName); $q->delete('func'); $q->param('func', 'editTmpl'); } else { $q->delete('tmplID'); $q->delete('func'); $q->param('func', 'showTemplates'); } } elsif (&getParam(1, 0, 'editNetTypeTmpl')) { unless (defined &getParam(1, undef, 'tmplID')) { $q->delete('func'); $q->param('func', 'showTemplates'); } else { $q->delete('func'); $q->param('func', 'editTmpl'); } } elsif (&getParam(1, 0, 'editTemplName')) { &changeTmplName(&getParam(1, undef, 'tmplID', 1), &getParam(1, 0, 'tmplName')); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif (&getParam(1, 0, 'abortEditTmpl')) { $q->delete('func'); $q->param('func', 'showTemplates'); } elsif (&getParam(1, 0, 'submitAddTmplEntry')) { my $tmplID = &saveTmpl(); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif (&getParam(1, 0, 'submitEditTmplEntry')) { my $tmplID = &saveTmpl(1); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif (&getParam(1, 0, 'submitDeleteTmplEntry')) { my $tmplID = &saveTmpl(2); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif (&getParam(1, 0, 'delTmpl')) { if (&getParam(1, 0, 'commitDelTmpl')) { &delTmpl(&getParam(1, undef, 'tmplID')); $q->delete('func'); $q->param('func', 'showTemplates'); } elsif (&getParam(1, 0, 'abortDelTmpl')) { $q->delete('func'); $q->param('func', 'showTemplates'); } else { if (&getParam(1, 0, 'tmplID')) { $q->delete('func'); $q->param('func', 'delTmpl'); } else { $q->delete('func'); $q->param('func', 'showTemplates'); } } } elsif (&getParam(1, 0, 'newGroup')) { my $groupName = &getParam(1, '', 'groupName'); #$groupName =~ s/[<>&"']//g; my $exists = 0; if ($groupName) { $exists = &checkIfExistsInDB('group', 'name', $groupName); &warnl("Group '$groupName' already exists!") if $exists; } if ($groupName && !$exists) { $q->delete('groupID'); $q->delete('groupName'), $q->param('groupName', $groupName); $q->delete('func'); $q->param('func', 'editGroup'); } else { $q->delete('func'); $q->param('func', 'showGroups'); } } elsif (&getParam(1, 0, 'editGroup')) { unless (defined &getParam(1, undef, 'groupID')) { $q->delete('func'); $q->param('func', 'showGroups'); } else { $q->delete('func'); $q->param('func', 'editGroup'); } } elsif (&getParam(1, 0, 'submitEditGroup')) { my $groupID = &saveGroup(); if (defined $groupID) { $q->delete('groupID'); $q->param('groupID', $groupID); $q->delete('func'); $q->param('func', 'showGroups'); } else { $q->delete('func'); $q->param('func', 'editGroup'); } } elsif (&getParam(1, 0, 'abortEditGroup')) { $q->delete('func'); $q->param('func', 'showGroups'); } elsif (&getParam(1, 0, 'delGroup')) { if (&getParam(1, 0, 'commitDelGroup')) { &delGroup(&getParam(1, undef, 'groupID')); $q->delete('func'); $q->param('func', 'showGroups'); } elsif (&getParam(1, 0, 'abortDelGroup')) { $q->delete('func'); $q->param('func', 'showGroups'); } else { if (&getParam(1, 0, 'groupID')) { my $groupName = &groupID2Name(&getParam(1, undef, 'groupID')); if ($groupName eq 'Administrator') { &warnl(_gettext("You cannot delete the administrator group!")); $q->delete('func'); $q->param('func', 'showGroups'); } else { $q->delete('func'); $q->param('func', 'delGroup'); } } else { $q->delete('func'); $q->param('func', 'showGroups'); } } } elsif (&getParam(1, 0, 'newUser')) { my $userName = &getParam(1, '', 'userName'); #$userName =~ s/[<>&"']//g; my $exists = 0; if ($userName) { $exists = &checkIfExistsInDB('user', 'username', $userName); &warnl("User '$userName' already exists!") if $exists; } if ($userName && !$exists) { $q->delete('userID'); $q->delete('userName'), $q->param('userName', $userName); $q->delete('func'); $q->param('func', 'editUser'); } else { $q->delete('func'); $q->param('func', 'showUsers'); } } elsif (&getParam(1, 0, 'editUser')) { unless (defined &getParam(1, undef, 'userID')) { $q->delete('func'); $q->param('func', 'showUsers'); } else { $q->delete('func'); $q->param('func', 'editUser'); } } elsif (&getParam(1, 0, 'submitEditUser')) { my $userID = &saveUser(); if (defined $userID) { $q->delete('userID'); $q->param('userID', $userID); $q->delete('func'); $q->param('func', 'showUsers'); } else { $q->delete('func'); $q->param('func', 'editUser'); } } elsif (&getParam(1, 0, 'abortEditUser')) { $q->delete('func'); $q->param('func', 'showUsers'); } elsif (&getParam(1, 0, 'delUser')) { if (&getParam(1, 0, 'commitDelUser')) { &delUser(&getParam(1, undef, 'userID')); $q->delete('func'); $q->param('func', 'showUsers'); } elsif (&getParam(1, 0, 'abortDelUser')) { $q->delete('func'); $q->param('func', 'showUsers'); } else { if (&getParam(1, 0, 'userID')) { $q->delete('func'); $q->param('func', 'delUser'); } else { $q->delete('func'); $q->param('func', 'showUsers'); } } } elsif (&getParam(1, 0, 'showSubnets')) { $q->delete('func'); $q->param('func', 'showSubnets'); } elsif (&getParam(1, 0, 'abortShowSubnets')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif (&getParam(1, 0, 'checkDB')) { &checkDB(); } elsif (&getParam(1, 0, 'submitshowPlugins')) { &updatePluginDB(); $q->delete('func'); $q->param('func', 'showPlugins'); } elsif (&getParam(1, 0, 'editPluginGlobConf')) { $q->delete('func'); $q->param('func', 'showPluginGlobConf'); } elsif (&getParam(1, 0, 'submitPluginConfig')) { my $global = &getParam(1, 0, 'global'); &mkPluginConfig($global); if ($global) { $q->delete('func'); $q->param('func', 'showPlugins'); } else { $q->delete('func'); $q->param('editNet', 'Bearbeiten'); $q->param('func', 'editNet'); } } elsif (&getParam(1, 0, 'abortPluginConfig')) { my $global = &getParam(1, 0, 'global'); if ($global) { $q->delete('func'); $q->param('func', 'showPlugins'); } else { $q->delete('func'); $q->param('func', 'editNet'); $q->delete('editNet'); $q->param('editNet', 1); } } elsif (&getParam(1, 0, 'abortShowSettings')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif (&getParam(1, 0, 'commitChOwnPW')) { unless (&chOwnPW()) { $q->param('changeOwnPW', 1); } } elsif (&getParam(1, 0, 'commitShowAuditLogs')) { $q->delete('func'); $q->param('func', 'showAuditLogs'); } elsif (&getParam(1, 0, 'showAuditLogsLeft')) { $q->delete('func'); $q->param('func', 'showAuditLogs'); } elsif (&getParam(1, 0, 'showAuditLogsRight')) { $q->delete('func'); $q->param('func', 'showAuditLogs'); } elsif (&getParam(1, 0, 'commitViewSettings')) { unless (&updSettings()) { $q->param('showViewSettings', 1); } &HaCi::GUI::init::setUserVars(); } elsif (&getParam(1, 0, 'searchAndGetFreeSubnets')) { &searchAndGetFreeSubnets(); } elsif (&getParam(1, 0, 'getFreeSubnets')) { my $netID = &getParam(1, undef, 'netID'); my $subnetSize = &getParam(1, undef, 'subnetSize'); my $amount = &getParam(1, undef, 'amount'); &getFreeSubnets($netID, 0, $subnetSize, $amount); } elsif (&getParam(1, 0, 'exportSubnets')) { my $rootID = &getParam(1, undef, 'rootID'); my $netID = &getParam(1, undef, 'netID'); my $ID = (defined $rootID) ? $rootID : $netID; &exportSubnets($ID, (defined $rootID) ? 1 : 0); } if (defined &getParam(1, 0, 'func')) { if (&getParam(1, '', 'func') eq 'addNet') { my $roots = &getRoots(0); if ($#{$roots} == -1) { &warnl("Cannot find any Root! Please create some before you create networks\n"); $q->delete('func'); $q->param('func', 'addRoot'); } } if (&getParam(1, '', 'func') eq 'addNet' && &getParam(1, 0, 'networkDec') && &getParam(1, 0, 'rootID') && !(&getParam(1, 0, 'editNet'))) { my $rootID = &getParam(1, undef, 'rootID'); my $networkDec = &getParam(1, undef, 'networkDec'); my $ipv6 = &rootID2ipv6($rootID); $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); unless (defined $parent) { warn "No Parent found!\n"; $q->delete('func'); $q->param('func', 'showAllNets'); } else { my $parentDec = $parent->{network}; my $parentID = $parent->{ID}; unless (&checkNetACL($parentID, 'w')) { my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $parent = ($ipv6) ? &netv6Dec2net($parentDec) : &dec2net($parentDec); &warnl(sprintf(_gettext("Not enough rights to add this Network '%s' under '%s'"), $network, $parent)); $q->delete('func'); $q->param('func', 'showAllNets'); } } unless (&checkNetworkLock($rootID, $networkDec, $ipv6)) { $q->delete('func'); $q->param('func', 'showAllNets'); } } elsif (&getParam(1, '', 'func') eq 'flushCache') { &flushACLCache(); &flushNetCache(); $q->delete('func'); $q->param('func', 'showAllNets'); } } $HaCi::GUI::init::t->{V}->{jumpTo} = $session->param('currRootID') . '-' . $session->param('currNet') if $session->param('currRootID') && $session->param('currNet'); &HaCi::GUI::main::start() unless $conf->{var}->{soap} || &getParam(1, 0, 'exportSubnet'); if (&getParam(1, 0, 'newWindow') && &getParam(1, '', 'newWindow') eq 'showStatus') { $t->{V}->{page} = 'showStatus'; } else { $t->{V}->{page} = 'main'; } } sub expandTo { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $network = shift || ''; my $ipv6 = &rootID2ipv6($rootID); $network =~ s/[^\w\.\:\/]//g; return 0 unless $network; unless (&checkSpelling_Net($network, $ipv6) || &checkSpelling_IP($network, $ipv6)) { warn "Cannot jump to. No IP Address / network: '$network'\n"; return 0; } $network .= ($ipv6) ? '/128' : '/32' unless $network =~ /\/\d+$/; my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my $s = $HaCi::HaCi::session; my $expands = $s->param('expands') || {}; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot jump to network. DB Error\n"; return 0; } my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); my $bestMatch = (defined $netID) ? $networkDec : 0; unless ($bestMatch) { $networkDec = &getDBNetworkBefore($rootID, $networkDec, $ipv6); $netID = &getNetID($rootID, $networkDec, $ipv6ID); $bestMatch = (defined $netID) ? $networkDec : 0; } while (my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6)) { my $parentDec = $parent->{network}; &expand('+', 'network', $parentDec, $rootID) unless $expands->{network}->{$rootID}->{$parentDec}; $bestMatch = $parentDec unless $bestMatch; $networkDec = $parentDec; } if ($bestMatch) { &expand('+', 'root', $rootID); $session->param('jumpTo', 1); $session->param('jumpToNet', $bestMatch); $session->param('jumpToRoot', $rootID); $session->param('currNet', $bestMatch); $session->param('currRootID', $rootID); } else { $session->clear('jumpTo'); $session->clear('jumpToNet'); $session->clear('jumpToRoot'); } } sub hook { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my ($filename, $buffer, $bytes_read, $data) = @_; my $percent = int(($bytes_read * 100) / $data); my $status = { title => "Retrieving File '$filename'", percent => $percent, detail => "Read $bytes_read/$data bytes of $filename", }; my $statFile = $conf->{static}->{path}->{statusfile} . '_.stat'; eval { Storable::lock_store($status, $statFile) or warn "Cannot store Status ($statFile)!\n"; }; if ($@) { warn $@; }; } sub getSession { my $sessID = shift; eval { $session = CGI::Session->load($sessID); }; if ($@ || !defined $session) { warn $@ . ' ' . CGI::Session->errstr; $conf->{var}->{authenticationError} = _gettext("Session is broken"); $conf->{var}->{relogin} = 1; #Do not return 0 if sessID is undef, but force to gen new session, so that an API call didn't fail return 0 if defined $sessID && $sessID; } if ( defined $session && $session->is_expired ) { $conf->{var}->{authenticationError} = _gettext("Session is expired"); $conf->{var}->{relogin} = 1; return 0; } if ( !defined $session || $session->is_empty ) { if (defined $session) { &debug("Session " . $session->id() . " is empty. Generating new..."); } else { &debug("Session cannot be loaded! Generating new..."); } &genNewSession(); } $session->expire($conf->{static}->{misc}->{sessiontimeout}); return $session; } sub init { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; $q = CGI->new(\&hook, $ENV{CONTENT_LENGTH}); $pjx = new CGI::Ajax( 'reduceNetwork' => \&reduceNetwork, 'expandNetwork' => \&expandNetwork, 'reduceRoot' => \&reduceRoot, 'expandRoot' => \&expandRoot, 'showPlugin' => \&showPlugin, 'mkShowStatus' => \&mkShowStatus, ); $pjx->DEBUG(0); my %cookies = fetch CGI::Cookie; my $sessID = (exists $cookies{CGISESSID}) ? $cookies{CGISESSID}->value : undef; return unless &getSession($sessID); ($aclCacheHandle, $netCacheHandle) = &initCache(); if (defined $netCacheHandle) { $netCache->{DB} = $netCacheHandle->get('DB'); $netCache->{NET} = $netCacheHandle->get('NET'); $netCache->{FILL} = $netCacheHandle->get('FILL'); } else { $netCache->{DB} = {}; $netCache->{NET} = {}; $netCache->{FILL} = {}; } $aclCache = (defined $aclCacheHandle) ? $aclCacheHandle->get('HASH') : {}; &initTables(); if (&getParam(1, 0, 'locale')) { $session->param('locale', &getParam(1, '', 'locale')); } $q->delete('jumpTo') if &getParam(1, 0, 'jumpTo') && &getParam(1, '', 'jumpTo') eq ''; } sub soapInit { my $soapArgs = shift; my $user = shift @{$soapArgs}; my $pass = shift @{$soapArgs}; $q->delete('login'), $q->param('login', 1); $q->delete('username'), $q->param('username', $user); $q->delete('password'), $q->param('password', $pass); } sub genNewSession { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; $session = CGI::Session->new() or die CGI::Session->errstr; $session->expire($conf->{static}->{misc}->{sessiontimeout}); if (ref($q) && &getParam(1, 0, 'locale')) { $session->param('locale', &getParam(1, '', 'locale')); } } sub authentication { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $user = shift; my $pass = shift; my $bLogin = shift; my $sessionUser = shift; if ($bLogin) { unless (defined $user) { $conf->{var}->{authenticationError} = _gettext("No User given"); return 0; } my @authModules = (($bLogin && $user eq 'admin') || ($sessionUser eq 'admin')) ? 'internal' : (exists $conf->{user}->{auth}->{authmodule} && $conf->{user}->{auth}->{authmodule} ne '') ? split(/\s*,\s*/, $conf->{user}->{auth}->{authmodule}) : ('internal'); my $authErrors = []; foreach (@authModules) { my $authModule = $_; $authModule = 'internal' if $authModule eq 'HaCi'; # fix to be backward compatible my $authModuleFile = $conf->{static}->{path}->{authmodules} . '/' . $authModule . '.pm'; eval { require $authModuleFile; }; if ($@) { $conf->{var}->{authenticationError} = _gettext("Authentication Error"); &warnl($@); return 0; } my $auth = "HaCi::Authentication::$authModule"->new(); $auth->session($session); $auth->init() if $authModule eq 'internal'; $auth->user($user); $auth->pass($pass); if ($auth->authenticate()) { $conf->{var}->{authenticated} = 1; &updateSettingsInSession(); &debug("Successfully authenticated by '$authModule'") if 1; return 1; } else { push @{$authErrors}, $conf->{var}->{authenticationError}; $conf->{var}->{authenticationError} = ''; &debug("Not authenticated by '$authModule'") if 1; } } $conf->{var}->{authenticationError} = join('
', @{$authErrors}); return 0; } else { if (defined $session->param('authenticated') && $session->param('authenticated')) { &debug("Allready authenticated") if 0; $conf->{var}->{authenticated} = 1; return 1; } else { &debug("Not authenticated"); return 0; } } } sub finalize { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $template = $HaCi::GUI::init::t; my $cookie; if (defined $session) { $cookie = $q->cookie( -name => $session->name, -value => $session->id ); } else { &debug("No session available while finalizing. Setting session-id to 0"); $cookie = $q->cookie( -name => 'CGISESSID', -value => 0 ); } unless ($conf->{var}->{soap}) { my $html_output = ''; my $warnl = &prWarnl(); my $prWarnl = ($warnl) ? '' : ''; $template->{T}->process($conf->{static}->{path}->{templateinit}, $template->{V}, \$html_output) || die $template->{T}->error(); $html_output = &prNewWindows(1) . $html_output . $prWarnl; if ($ENV{MOD_PERL}) { print $q->header( -cookie => $cookie, -charset => 'UTF-8' ); print $pjx->build_html($q, $html_output); } else { $q->header(); # Bad Fix for CGI - AJAX - Cookie Bug print $pjx->build_html($q, $html_output, { -cookie => $cookie, -charset => 'UTF-8' }); } print $q->Dump() if 0; if (0) { map { warn " $_ -> " . &getParam(1, '', $_); } $q->param; } # Dump print '' if $conf->{var}->{reloadPage}; print '' if $conf->{var}->{closePage}; } undef $HaCi::HaCi::q; # Cache Statistics if (0) { warn "Netcache-DB : $conf->{var}->{CACHESTATS}->{DB}->{FAIL}/$conf->{var}->{CACHESTATS}->{DB}->{TOTAL}\n"; warn "Netcache-NET : $conf->{var}->{CACHESTATS}->{NET}->{FAIL}/$conf->{var}->{CACHESTATS}->{NET}->{TOTAL}\n"; warn "Netcache-FILL: $conf->{var}->{CACHESTATS}->{FILL}->{FAIL}/$conf->{var}->{CACHESTATS}->{FILL}->{TOTAL}\n"; } if (defined $netCacheHandle) { warn "Cannot set Cache (netCache-DB!)\n" unless $netCacheHandle->set('DB', $netCache->{DB}); warn "Cannot set Cache (netCache-FILL!)\n" unless $netCacheHandle->set('FILL', $netCache->{FILL}); warn "Cannot set Cache (netCache-NET!)\n" unless $netCacheHandle->set('NET', $netCache->{NET}); } if (defined $aclCacheHandle) { warn "Cannot set Cache (aclCache!)\n" unless $aclCacheHandle->set('HASH', $aclCache); } if (defined $session) { $session->clear('jumpTo'); $session->clear('jumpToNet'); $session->clear('jumpToRoot'); $session->flush(); } &warnl($conf->{var}->{authenticationError}) if $conf->{var}->{authenticationError} && $conf->{var}->{soap}; return &prWarnl(1) if $conf->{var}->{soap}; } sub checkRights { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; foreach ($q->param()) { &delParam($_) if ( /^abortAddNet$/ || /^abortDelNet$/ || /^checktAddNet$/ || /^submitAddNet$/ ) && !&checkRight('addNet') || ( /^commitDelNet$/ || /^delNet$/ || /^splitNet$/ || /^submitSplitNet$/ || /^abortSplitNet$/ || /^editNet$/ || /^editPluginConf$/ || /^netFuncType$/ ) && !&checkRight('editNet') || ( /^submitAddRoot$/ ) && !&checkRight('addRoot') || ( /^abortDelRoot$/ || /^abortEditRoot$/ || /^commitDelRoot$/ || /^delRoot$/ || /^editRoot$/ ) && !&checkRight('editRoot') || ( /^abortDelGroup$/ || /^abortEditGroup$/ || /^commitDelGroup$/ || /^delGroup$/ || /^editGroup$/ || /^newGroup$/ || /^submitEditGroup$/ ) && !&checkRight('groupMgmt') || ( /^abortDelTmpl$/ || /^abortEditTmpl$/ || /^commitDelTmpl$/ || /^delTmpl$/ || /^editNetTypeTmpl$/ || /^newTmpl$/ || /^submitAddTmplEntry$/ || /^submitEditTmplEntry$/ || /^submitDeleteTmplEntry$/ ) && !&checkRight('tmplMgmt') || ( /^abortDelUser$/ || /^abortEditUser$/ || /^commitDelUser$/ || /^delUser$/ || /^editUser$/ || /^newUser$/ || /^submitEditUser$/ ) && !&checkRight('userMgmt') || ( /^submitshowPlugins$/ || /^submitPluginGlobConfig$/ || /^abortPluginGlobConfig$/ || /^editPluginGlobConf$/ ) && !&checkRight('pluginMgmt') || ( /^copyNetsTo$/ || /^moveNetsTo$/ || /^copyToRootID$/ || /^deleteNets$/ || /^combineNets$/ || /^submitCombineNets$/ || /^abortCombineNets$/ || /^editTree$/ || /^finishEditTree$/ ) && !&checkRight('editTree') || ( /^search$/ || /^compare$/ ) && !&checkRight('search') || ( /^getFreeSubnetsFromSearch$/ ) && (!&checkRight('search') || !&checkRight('addNet')) || ( /^getFreeSubnets$/ ) && (!&checkRight('search')) || ( /^submitImportASNRoutes$/ ) && !&checkRight('impASNRoutes') || ( /^getFreeSubnets$/ ) && !&checkRight('showNetDet') || ( /^submitImpDNSTrans$/ || /^submitImpDNSLocal$/ ) && !&checkRight('impDNS') || ( /^submitImpConfig$/ || /^impCSVChangeSep$/ || /^submitImpCSV$/ ) && !&checkRight('impConfig') || ( /^showAditLogs$/ || /^showAditLogsLeft$/ || /^showAditLogsRight$/ || /^commitShowAuditLogs$/ ) && !&checkRight('showAuditLogs') } } sub delParam { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $param = shift; my $q = $HaCi::HaCi::q; &debug("Deleting Parameter '$param'! Not enough rights!!!"); $q->delete($param); } sub checkIfExistsInDB { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $table = shift; my $col = shift; my $val = shift; my $table = $conf->{var}->{TABLES}->{$table}; unless (defined $table) { &warn("Cannot check. DB Error ($table)"); return 1; } my $DB = ($table->search(['ID'], {$col => $val}))[0]; return (defined $DB) ? 1 : 0; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/HaCiAPI.pm0000644000175000000000000003676212451674756015051 0ustar fighterrootpackage HaCi::HaCiAPI; use strict; use warnings; use File::Basename; use vars qw/$workDir/; # Polluting Environment for Config::General $ENV{script_name} = $ENV{SCRIPT_NAME}; ($workDir = dirname( __FILE__ )) =~ s#/modules/HaCi##; $ENV{workdir} = $workDir; unshift @INC, $ENV{workdir} . '/modules'; use HaCi::Conf; use HaCi::HaCi; use HaCi::Utils qw/networkStateName2ID getNetID checkNetACL rootName2ID netv6Dec2ipv6ID tmplName2ID tmplEntryDescr2EntryID/; use HaCi::Log qw/warnl debug/; use HaCi::Mathematics qw/netv62Dec &net2dec/; use HaCi::SOAP::Type::network; our $conf; *conf = \$HaCi::Conf::conf; sub init { my @args = @_; map { warn " ARGS: $_\n"; } @args if 0; &HaCi::Conf::init($workDir); } sub fillParams { my $q = shift; my $params = shift; return unless ref($params) eq 'HASH'; foreach (keys %{$params}) { my $key = $_; my $value = $params->{$key}; if (defined $value) { $q->delete($key); if (ref($value) eq 'ARRAY') { $q->param($key, @{$value}); } else { $q->param($key, $value); } warn "\$q->param($key, $value);\n" if $conf->{static}->{misc}->{debug}; } } } =begin WSDL _IN username $string Username _IN password $string Password _IN searchString $string Search String _IN state $string (optional) (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN exact $boolean (optional) Search for the Exact search String? _IN fuzzy $boolean (optional) Fuzzy search? _IN template $string (optional) isolate your Search by defining a Template _IN templateQueries $string (optional) Define special Queries for the specified Template. spererated by semicolon. E.g.: value1=foo;value2=bar _IN root $string (optional) Define special root to search in _IN tags $string (optional) return only networks with this tags, seperate with spaces (i.e.: 'OR foo bar', 'AND foo bar') _RETURN @HaCi::SOAP::Type::network _DOC This is a search function =cut sub search { my ($class, $user, $pass, @funcArgs) = @_; &init(); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&search_pre, \&search_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub search_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $searchStr = $$funcArgs[0] || ''; my $state = $$funcArgs[1] || ''; my $exact = $$funcArgs[2] || ''; my $fuzzy = $$funcArgs[3] || ''; my $tmplName = $$funcArgs[4] || ''; my $tmplQuery = $$funcArgs[5] || ''; my $rootName = $$funcArgs[6] || ''; my $tagsT = $$funcArgs[7] || ''; $tagsT =~ s/^\s+//; $tagsT =~ s/\s+$//; my @tags = split(/\s+/, $tagsT); my $tagOp = ($tagsT ne '') ? shift @tags : 0; if ($tagsT ne '' && scalar @tags == 0) { @tags = ($tagOp); $tagOp = 'OR'; } my $tmplID = ($tmplName ne '') ? &tmplName2ID($tmplName) : -1; my $rootID = ($rootName ne '') ? &rootName2ID($rootName) : 0; unless (defined $tmplID) { &warnl("No such Template found! ($tmplName)"); $tmplID = -1; } unless (defined $rootID) { &warnl("No such root found! ($rootName)"); $rootID = -1; } foreach (split(/;/, $tmplQuery)) { my $query = $_; next if !$query || $query !~ /=/; my ($key, $value) = split(/=/, $query, 2); my $tmplEntryID = &tmplEntryDescr2EntryID($tmplID, $key); next unless defined $tmplEntryID; my $tmplDescr = 'tmplEntryDescrID_' . $tmplEntryID; my $tmplEntry = 'tmplEntryID_' . $tmplEntryID; &fillParams($q, { $tmplDescr => $key, $tmplEntry => $value, }); } &fillParams($q, { searchButton => 1, search => $searchStr || '', state => (($state eq '') ? -1 : &networkStateName2ID($state) || 0), exact => $exact || undef, fuzzy => $fuzzy || undef, tmplID => $tmplID, rootID => $rootID, tags => (scalar @tags > 0) ? \@tags : undef, tagOp => $tagOp, }); } sub search_post { my $funcArgs = shift; my $q = shift; my $t = shift; my $warnings = shift; my $result = $t->{V}->{searchResult}; &dieWarnings($warnings) if $warnings ne ''; if (defined $result && ref($result) eq 'ARRAY') { my $networks = []; foreach (@{$result}) { push @{$networks}, new HaCi::SOAP::Type::network($_->{rootName}, $_->{network}, $_->{description}, $_->{state}, $_->{tags}); } return $networks; } else { return []; } } =begin WSDL _IN username $string Username _IN password $string Password _IN root $string Root Name _IN supernet $string Supernet (e.g. 192.168.0.0/24) _IN size $int Subnet CIDR (e.g. 29) _IN amount $int (optional) Amount of returned Networks (e.g. 1) _RETURN @HaCi::SOAP::Type::network _DOC This Service returns all free Subnets of a certain size from a given Supernet =cut sub getFreeSubnets { my ($class, $user, $pass, @funcArgs) = @_; &init(@funcArgs); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&getFreeSubnets_pre, \&getFreeSubnets_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub getFreeSubnets_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $rootName = $$funcArgs[0] || 0; my $rootID = &rootName2ID($rootName); unless ($rootID) { &warnl("No such Root found! ($rootName)"); return; } my $network = $$funcArgs[1] || 0; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my $cidr = $$funcArgs[2] || (($ipv6) ? 128 : 32); my $amount = $$funcArgs[3] || 0; my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &warnl("No such network found! ($network)"); return; } unless (&checkNetACL($netID, 'r')) { &warnl("Not enouph permissions to read this Network"); return; } &fillParams($q, { getFreeSubnets => 1, netID => $netID, subnetSize => $cidr, amount => $amount, }); } sub getFreeSubnets_post { my $funcArgs = shift; my $q = shift; my $t = shift; my $warnings = shift; my $result = $t->{V}->{freeSubnets}; my $amount = &getParam($q, 1, 0, 'amount'); my $rootName = $$funcArgs[0] || 0; &dieWarnings($warnings) if $warnings ne ''; if (defined $result && ref($result) eq 'ARRAY') { my $networks = []; my $cnter = 0; foreach (@{$result}) { my $cRootName = ($_->{rootName}) ? $_->{rootName} : $rootName; my $cNetwork = ($_->{network}) ? $_->{network} : $_->{net}; push @{$networks}, new HaCi::SOAP::Type::network($cRootName, $cNetwork); return $networks if $amount && ++$cnter == $amount; } return $networks; } else { return []; } } sub dieWarnings { my $warnings = shift; die SOAP::Fault->faultcode('HaCiAPI')->faultstring($warnings); } =begin WSDL _IN username $string Username _IN password $string Password _IN searchString $string Search String _IN state $string (optional) (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN exact $boolean (optional) Search for the Exact search String? _IN fuzzy $boolean (optional) Fuzzy search? _IN template $string (optional) isolate your Search by defining a Template _IN templateQueries $string (optional) Define special Queries for the specified Template. spererated by semicolon. E.g.: value1=foo;value2=bar _IN size $int Subnet CIDR (e.g. 29) _IN amount $int (optional) Amount of returned Networks (e.g. 1) _RETURN @HaCi::SOAP::Type::network _DOC This Service returns all free Subnets of a certain size from networks, that fit the search criteria =cut sub getFreeSubnetsFromSearch { my ($class, $user, $pass, @funcArgs) = @_; &init(); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&getFreeSubnetsFromSearch_pre, \&getFreeSubnets_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub getFreeSubnetsFromSearch_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $searchStr = $$funcArgs[0] || ''; my $state = $$funcArgs[1] || ''; my $exact = $$funcArgs[2] || ''; my $fuzzy = $$funcArgs[3] || ''; my $tmplName = $$funcArgs[4] || ''; my $tmplQuery = $$funcArgs[5] || ''; my $size = $$funcArgs[6] || -1; my $amount = $$funcArgs[7] || 1; my $tmplID = ($tmplName ne '') ? &tmplName2ID($tmplName) : 0; unless (defined $tmplID) { &warnl("No such Template found! ($tmplName)"); $tmplID = -1; } foreach (split(/;/, $tmplQuery)) { my $query = $_; next if !$query || $query !~ /=/; my ($key, $value) = split(/=/, $query, 2); my $tmplEntryID = &tmplEntryDescr2EntryID($tmplID, $key); next unless defined $tmplEntryID; my $tmplDescr = 'tmplEntryDescrID_' . $tmplEntryID; my $tmplEntry = 'tmplEntryID_' . $tmplEntryID; &fillParams($q, { $tmplDescr => $key, $tmplEntry => $value, }); } &fillParams($q, { searchAndGetFreeSubnets => 1, search => $searchStr || '', state => (($state eq '') ? -1 : &networkStateName2ID($state) || 0), exact => $exact || undef, fuzzy => $fuzzy || undef, tmplID => $tmplID, size => $size, amount => $amount, }); } =begin WSDL _IN username $string Username _IN password $string Password _IN rootName $string Name of Root _IN ipaddress $string IP Address (E.g.: 192.168.0.1) _IN cidr $int CIDR (E.g.: 24) _IN description $string (optional) Description of the Network _IN state $string (optional) The State of the Network. (One of: UNSPECIFIED, ALLOCATED PA, ALLOCATED PI, ALLOCATED UNSPECIFIED, SUB-ALLOCATED PA, LIR-PARTITIONED PA, LIR-PARTITIONED PI, EARLY-REGISTRATION, NOT-SET, ASSIGNED PA, ASSIGNED PI, ASSIGNED ANYCAST, ALLOCATED-BY-RIR, ALLOCATED-BY-LIR, ASSIGNED, RESERVED, LOCKED, FREE) _IN defSubnetSize $int (optional) Default CIDR for Subnets _IN templateName $string (optional) Template, which should be linked to the Network _IN tags $string (optional) Tags (seperate by comma) _RETURN $string Prints if addition has failed or was successfull _DOC This function adds a Network =cut sub addNet { my ($class, $user, $pass, @funcArgs) = @_; &init(@funcArgs); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&addNet_pre, \&addNet_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub addNet_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $rootName = $$funcArgs[0] || ''; my $ip = $$funcArgs[1] || 0; my $cidr = $$funcArgs[2]; my $descr = $$funcArgs[3] || ''; my $state = (($$funcArgs[1] eq '') ? -1 : &networkStateName2ID($$funcArgs[1]) || 0), my $defss = $$funcArgs[5] || 0; my $tmplName = $$funcArgs[6] || ''; my $tags = $$funcArgs[7] || ''; $cidr = (($ip =~ /:/) ? 128 : 32) if !defined $cidr || !$cidr; my $rootID = &rootName2ID($rootName); unless ($rootID) { &warnl("No such Root found! ($rootName)"); return; } my $tmplID = ($tmplName ne '') ? &tmplName2ID($tmplName) : 0; unless (defined $tmplID) { &warnl("No such Template found! ($tmplName)"); $tmplID = 0; } &fillParams($q, { submitAddNet => 1, func => 'addNet', rootID => $rootID, netaddress => $ip, cidr => $cidr, descr => $descr, state => $state, defSubnetSize => $defss, tmplID => $tmplID, tags => $tags, forceState => 1 }); } sub addNet_post { my $funcArgs = shift; my $q = shift; my $t = shift; my $warnings = shift; my $rootName = $$funcArgs[0] || ''; my $ip = $$funcArgs[1] || 0; my $cidr = $$funcArgs[2]; $cidr = (($ip =~ /:/) ? 128 : 32) if !defined $cidr || !$cidr; my $network = $ip . '/' . $cidr; my $func = &getParam($q, 1, undef, 'func'); warn "FUNC: $func\n"; if (defined $func && $func eq 'showAllNets') { return "Sucessfully added Network '$network' to Root '$rootName'" . (($warnings) ? " ($warnings)" : ''); } else { return "Error while adding Network '$network' to Root '$rootName': $warnings"; } } =begin WSDL _IN username $string Username _IN password $string Password _IN root $string Root Name _IN network $string Network (e.g. 192.168.0.0/24) _RETURN $string Prints if deletion has failed or was successfull _DOC This function deletes a Network =cut sub delNet { my ($class, $user, $pass, @funcArgs) = @_; &init(@funcArgs); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&delNet_pre, \&delNet_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub delNet_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $rootName = $$funcArgs[0] || 0; my $rootID = &rootName2ID($rootName); unless ($rootID) { &warnl("No such Root found! ($rootName)"); return; } my $network = $$funcArgs[1] || 0; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &warnl("No such network found! ($network)"); return; } unless (&checkNetACL($netID, 'w')) { &warnl("Not enouph permissions to delete this Network"); return; } &fillParams($q, { showSubnets => 1, netID => $netID, commitDelNet => 1, delNet => 1, }); } sub delNet_post { my $funcArgs = shift; my $q = shift; my $t = shift; my $warnings = shift; my $rootName = $$funcArgs[0] || ''; my $network = $$funcArgs[1] || '0/0'; my $func = &getParam($q, 1, undef, 'func'); if (defined $func && $func eq 'showAllNets') { return "Sucessfully deleted Network '$network' from Root '$rootName'" . (($warnings) ? " ($warnings)" : ''); } else { return "Error while deleting Network '$network' from Root '$rootName': $warnings"; } } =begin WSDL _IN username $string Username _IN password $string Password _IN param $string Parameter _RETURN $string Return Stuff _DOC Template =cut sub template { my ($class, $user, $pass, @funcArgs) = @_; &init(@funcArgs); my $result = ''; eval { $result = &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&template_pre, \&template_post); }; if ($@) { warn "Error: $@"; return $@; } return $result; } sub template_pre { my $funcArgs = shift; my $q = shift; return unless ref($funcArgs) eq 'ARRAY'; my $param = $$funcArgs[0] || 0; &fillParams($q, { param => $param, }); } sub template_post { my $funcArgs = shift; my $q = shift; my $t = shift; my $warnings = shift; } sub getParam { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = shift; my $onlyScalar = shift; my $ifUndef = shift; my $paramName = shift; return $ifUndef unless defined $paramName; my @values = $q->param($paramName); unless (defined $values[0]) { return $ifUndef if $onlyScalar; return () if $ifUndef == 0; # return empty list if onlyScalar != 1 && ifUndef == 0 return $ifUndef; } if ($onlyScalar) { &debug("called function 'getParam' with option 'onlyScalar' AND more than one value! (" . (caller(0))[0] . '->' . (caller(0))[2] . ')') if $#values > 0; return $values[0]; } return @values; } 1; HaCi/modules/HaCi/Log.pm0000644000175000000000000000124611341614314014376 0ustar fighterrootpackage HaCi::Log; use warnings; use strict; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(warnl debug); our $conf; *conf = \$HaCi::Conf::conf; sub warnl { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $error = shift; my $bNPub = shift || 0; my ($caller, $line) = (caller)[0, 2]; warn "$caller:$line $error\n"; push @{$conf->{var}->{warnl}}, $error unless $bNPub; } sub debug { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $msg = shift; chomp($msg); $msg .= "\n"; warn $msg if $conf->{static}->{misc}->{debug}; } # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Mathematics.pm0000644000175000000000000003135412377724510016131 0ustar fighterrootpackage HaCi::Mathematics; use strict; use constant LOG2 => log(2); use Net::IPv6Addr; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( dec2net net2dec ip2dec dec2ip getIPFromDec ipv6Parts2NetDec netv6Dec2PartsDec getNetaddress getBroadcastFromNet ipv62dec netv6Dec2IpCidr getCidrFromNetmask getNetmaskFromCidr getCidrFrom2IPs netv6Dec2net getV6BroadcastIP netv6Dec2ip ipv62Dec2 ipv6Dec2ip getV6BroadcastNet netv6Dec2NextNetDec ipv6DecCidr2netv6Dec netv62Dec ipv6DecCidr2NetaddressV6Dec getCidrFromNetv6Dec getCidrFromDec ipv6Sort ); our $conf; *conf = \$HaCi::Conf::conf; sub getCidrFrom2IPs { my $from = shift; my $to = shift; my @froms = split/\./, $from; my @tos = split/\./, $to; my $cidr = 0; for (0 .. 3) { next if $tos[$_] - $froms[$_] < 0; $cidr += int(log((($tos[$_] - $froms[$_]) + 1))/LOG2); } return 32 - $cidr; } sub getNetaddress { my $ipaddress = shift; my $netmask = shift; my $netAddress = &getNetCacheEntry('NET', "$ipaddress:$netmask", 'getNetaddress'); unless (defined $netAddress) { $ipaddress = &ip2dec($ipaddress) if $ipaddress =~ /\./; $netmask = &ip2dec($netmask) if $netmask =~ /\./; $netAddress = $ipaddress & $netmask; &updateNetcache('NET', "$ipaddress:$netmask", 'getNetaddress', $netAddress); } return $netAddress; } sub getCidrFromNetmask { my $netmask = shift; $netmask = &dec2ip($netmask) unless $netmask =~ /\./; my $cidr = 0; foreach (split/\./, $netmask) { $cidr += log(256 - $_)/LOG2; } return 32 - $cidr; } sub getBroadcastFromNet { my $networkDec = shift; my $broadcastFromNet = &getNetCacheEntry('NET', $networkDec, 'getBroadcastFromNet'); unless (defined $broadcastFromNet) { $networkDec = &net2dec($networkDec) if $networkDec =~ /\./; my $cidr = $networkDec % 256; my $ipaddress = ($networkDec - $cidr) / 256; my $netmask = &getNetmaskFromCidr($cidr); my $netaddress = &getNetaddress($ipaddress, $netmask); my $broadcast = ($netaddress + (2 ** (32 - $cidr)) - 1); $broadcastFromNet = $broadcast; &updateNetcache('NET', $networkDec, 'getBroadcastFromNet', $broadcast); } return $broadcastFromNet; } sub getNetmaskFromCidr { my $cidr = shift; my $netmaskFromCidr = &getNetCacheEntry('NET', $cidr, 'getNetmaskFromCidr'); unless (defined $netmaskFromCidr) { my $netmask = ''; for (0 .. 3) { $netmask .= '.' if $netmask ne ''; if ($cidr > 8) { $netmask .= 255; $cidr -= 8; } else { $netmask .= 256 - (2 ** (8 - $cidr)); $cidr = 0; } } $netmaskFromCidr = $netmask; &updateNetcache('NET', $cidr, 'getNetmaskFromCidr', $netmask); } return $netmaskFromCidr; } sub dec2net { my $networkDec = shift; return 0 unless defined $networkDec; return 0 if $networkDec eq ''; if (ref $networkDec) { warn " dec2net for IPv6 (" . (caller)[0] . ':' . (caller)[2] . ")\n"; return &netv6Dec2net($networkDec); } else { return int($networkDec / 256 ** 4) . '.' . int($networkDec % 256 ** 4 / 256 ** 3) . '.' . int($networkDec % 256 ** 3 / 256 ** 2) . '.' . int($networkDec % 256 ** 2 / 256) . '/' . $networkDec % 256; } } sub net2dec { my $network = shift; if ($network =~ /:/) { warn "CANNOT net2dec for IPv6!!!\n"; return 0; } else { $network =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/; return $1 * 256 ** 4 + $2 * 256 ** 3 + $3 * 256 ** 2 + $4 * 256 + $5; } } sub ip2dec { my $ip = shift; unless ($ip && $ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) { warn " Bad ip2dec call (" . (caller)[0] . ':' . (caller)[2] . ")\n"; return 0; } return $1 * 256 ** 3 + $2 * 256 ** 2 + $3 * 256 + $4; } sub dec2ip { return 0 unless defined $_[0]; return 0 if $_[0] eq ''; return int($_[0] / 256**3) . '.' . int($_[0] % 256**3 / 256**2) . '.' . int($_[0] % 256**2 / 256**1) . '.' . $_[0] % 256 } sub getIPFromDec { my $networkDec = shift; my $cidr = $networkDec % 256; return ($networkDec - $cidr) / 256; } sub updateNetcache { my $type = shift; my $ID = shift; my $key = shift; my $value = shift; $conf->{var}->{CACHESTATS}->{$type}->{FAIL}++; $HaCi::HaCi::netCache->{$type}->{$ID}->{$key} = $value; } sub getNetCacheEntry { my $type = shift; my $ID = shift; my $key = shift; my $value = $HaCi::HaCi::netCache->{$type}->{$ID}->{$key} || undef; $conf->{var}->{CACHESTATS}->{$type}->{TOTAL}++; return $value; } sub hex2dec { my $hex = shift; return hex($hex); } sub dec2hex { my $dec = shift; return sprintf("%04x", $dec); } sub ipv62dec { my $ipv6 = shift; my @intArray = Net::IPv6Addr::to_intarray($ipv6); my $base = 65536; my $i0 = Math::BigInt->new($base)->bpow('7')->bmul($intArray[0]); my $i1 = Math::BigInt->new($base)->bpow('6')->bmul($intArray[1]); my $i2 = Math::BigInt->new($base)->bpow('5')->bmul($intArray[2]); my $i3 = Math::BigInt->new($base)->bpow('4')->bmul($intArray[3]); my $i4 = Math::BigInt->new($base)->bpow('3')->bmul($intArray[4]); my $i5 = Math::BigInt->new($base)->bpow('2')->bmul($intArray[5]); my $i6 = Math::BigInt->new($base)->bmul($intArray[6]); my $ipv6Dec = Math::BigInt->new($intArray[7])->badd($i6)->badd($i5)->badd($i4)->badd($i3)->badd($i2)->badd($i1)->badd($i0); return $ipv6Dec; } sub ipv62dec2 { my $ipv6 = shift; my @intArray = Net::IPv6Addr::to_intarray($ipv6); my $base = 65536; my $n0 = Math::BigInt->new($base)->bpow('3')->bmul($intArray[0]); my $n1 = Math::BigInt->new($base)->bpow('2')->bmul($intArray[1]); my $n2 = Math::BigInt->new($base)->bmul($intArray[2]); my $netDec = Math::BigInt->new($intArray[3])->badd($n2)->badd($n1)->badd($n0); my $h4 = Math::BigInt->new($base)->bpow('3')->bmul($intArray[4]); my $h5 = Math::BigInt->new($base)->bpow('2')->bmul($intArray[5]); my $h6 = Math::BigInt->new($base)->bmul($intArray[6]); my $hostDec = Math::BigInt->new($intArray[7])->badd($h6)->badd($h5)->badd($h4); return ($netDec, $hostDec); } sub ipv6Dec2ip { my $nett = shift; my $hostt = shift; my $base = Math::BigInt->new(65536); unless (defined $hostt) { return 0 unless ref $nett; my $ipv6 = &getNetCacheEntry('NET', "$nett:-1", 'ipv6Dec2ip'); unless (defined $ipv6) { my $net = $nett->copy(); my $i8 = $net->copy()->bmod($base); $ipv6 = &dec2hex($i8); my $i7 = $net->bsub($i8)->copy()->bdiv($base)->bmod($base); $ipv6 = &dec2hex($i7) . ':' . $ipv6; my $i6 = $net->bsub($i7->bmul($base))->copy()->bdiv($base->copy()->bpow(2))->bmod($base); $ipv6 = &dec2hex($i6) . ':' . $ipv6; my $i5 = $net->bsub($i6->bmul($base->copy()->bpow(2)))->copy()->bdiv($base->copy()->bpow(3))->bmod($base); $ipv6 = &dec2hex($i5) . ':' . $ipv6; my $i4 = $net->bsub($i5->bmul($base->copy()->bpow(3)))->copy()->bdiv($base->copy()->bpow(4))->bmod($base); $ipv6 = &dec2hex($i4) . ':' . $ipv6; my $i3 = $net->bsub($i4->bmul($base->copy()->bpow(4)))->copy()->bdiv($base->copy()->bpow(5))->bmod($base); $ipv6 = &dec2hex($i3) . ':' . $ipv6; my $i2 = $net->bsub($i3->bmul($base->copy()->bpow(5)))->copy()->bdiv($base->copy()->bpow(6))->bmod($base); $ipv6 = &dec2hex($i2) . ':' . $ipv6; my $i1 = $net->bsub($i2->bmul($base->copy()->bpow(6)))->copy()->bdiv($base->copy()->bpow(7))->bmod($base); $ipv6 = &dec2hex($i1) . ':' . $ipv6; &updateNetcache('NET', "$nett:-1", 'ipv6Dec2ip', $ipv6); } return $ipv6; } return 0 unless ref $nett && ref $hostt; my $ipv6 = &getNetCacheEntry('NET', "$nett:$hostt", 'ipv6Dec2ip'); unless (defined $ipv6) { my $net = $nett->copy(); my $host = $hostt->copy(); my $h4 = $host->copy()->bmod($base); $ipv6 = &dec2hex($h4); my $h3 = $host->bsub($h4)->copy()->bdiv($base)->bmod($base); $ipv6 = &dec2hex($h3) . ':' . $ipv6; my $h2 = $host->bsub($h3->bmul($base))->copy()->bdiv($base ** 2)->bmod($base); $ipv6 = &dec2hex($h2) . ':' . $ipv6; my $h1 = $host->bsub($h2->bmul($base ** 2))->copy()->bdiv($base ** 3)->bmod($base); $ipv6 = &dec2hex($h1) . ':' . $ipv6; my $n4 = $net->copy()->bmod($base); $ipv6 = &dec2hex($n4) . ':' . $ipv6; my $n3 = $net->bsub($n4)->copy()->bdiv($base)->bmod($base); $ipv6 = &dec2hex($n3) . ':' . $ipv6; my $n2 = $net->bsub($n3->bmul($base))->copy()->bdiv($base ** 2)->bmod($base); $ipv6 = &dec2hex($n2) . ':' . $ipv6; my $n1 = $net->bsub($n2->bmul($base ** 2))->copy()->bdiv($base ** 3)->bmod($base); $ipv6 = &dec2hex($n1) . ':' . $ipv6; &updateNetcache('NET', "$nett:$hostt", 'ipv6Dec2ip', $ipv6); } return $ipv6; } sub ipv6Parts2NetDec { my $net = shift; my $host = shift; my $cidr = shift; my $network = Math::BigInt->new(); my $nett = Math::BigInt->new(65536); my $hostt = Math::BigInt->new(65536); $nett->bpow(5)->bmul($net); $hostt->bmul($host); $network->badd($cidr); $network->badd($nett); $network->badd($hostt); return $network; } sub netv6Dec2IpCidr { my $netv6Dect = shift; return 0 unless ref $netv6Dect; my ($ipv6Dec, $cidr); my $ipv6DecCidr = &getNetCacheEntry('NET', $netv6Dect, 'netv6Dec2IpCidr'); unless (defined $ipv6DecCidr) { my $netv6Dec = $netv6Dect->copy(); my $base = 65536; $ipv6Dec = $netv6Dec->copy(); $cidr = $netv6Dec->bmod($base)->copy(); ($ipv6Dec->bsub($cidr))->bdiv($base); &updateNetcache('NET', $netv6Dect, 'netv6Dec2IpCidr', ["$ipv6Dec", $cidr]); } else { $ipv6Dec = Math::BigInt->new(${$ipv6DecCidr}[0]); $cidr = ${$ipv6DecCidr}[1]; } return ($ipv6Dec, $cidr); } sub getV6BroadcastIP { my $netv6Dec = shift; return 0 unless ref $netv6Dec; my ($ipv6Dec, $cidr) = &netv6Dec2IpCidr($netv6Dec); my $two = Math::BigInt->new(2); my $add = $two->bpow((128 - $cidr))->bsub(1); $ipv6Dec->badd($add); return $ipv6Dec; } sub getV6BroadcastNet { my $netv6Dec = shift; my $targetCidr = shift; unless (ref $netv6Dec) { warn "Calling 'getV6BroadcastNet' without Math::Bigint Reference! (" . (caller(0))[0] . '->' . (caller(0))[2] . ")\n"; return 0; } my ($ipv6Dec, $cidr) = &netv6Dec2IpCidr($netv6Dec); $targetCidr = $cidr unless defined $cidr; my $two = Math::BigInt->new(2); my $add = $two->bpow(128 - $cidr)->bsub(1); $ipv6Dec->badd($add); my $netv6DecNew = &ipv6DecCidr2netv6Dec($ipv6Dec, $targetCidr); return $netv6DecNew; } sub netv6Dec2NextNetDec { my $netv6Dec = shift; my $targetCidr = shift; return 0 unless ref $netv6Dec; my ($ipv6Dec, $cidr) = &netv6Dec2IpCidr($netv6Dec); $targetCidr = $cidr unless defined $targetCidr; my $two = Math::BigInt->new(2); my $add = $two->bpow(128 - $cidr); $ipv6Dec->badd($add); my $netv6DecNew = &ipv6DecCidr2netv6Dec($ipv6Dec, $targetCidr); return $netv6DecNew; } sub netv62Dec { my $netv6 = shift; my ($ipv6, $cidr) = split/\//, $netv6; my $ipv6Dec = Math::BigInt->new(&ipv62dec($ipv6)); return &ipv6DecCidr2netv6Dec($ipv6Dec, $cidr); } sub ipv6DecCidr2netv6Dec { my $ipv6Dec = shift; my $cidr = shift; my $netv6Dec = Math::BigInt->new(); my $ipv6Dect = Math::BigInt->new(65536); return 0 unless ref $ipv6Dec; $ipv6Dect->bmul($ipv6Dec); $netv6Dec->badd($ipv6Dect); $netv6Dec->badd($cidr); return $netv6Dec; } sub netv6Dec2ip { my $netv6Dect = shift; return 0 unless ref $netv6Dect; my $netv6Dec = $netv6Dect->copy(); my ($ipv6Dec, $cidr) = &netv6Dec2IpCidr($netv6Dec); my $ipv6 = &ipv6Dec2ip($ipv6Dec); return $ipv6; } sub netv6Dec2net { my $netv6Dect = shift; unless (ref $netv6Dect) { warn "Calling 'netv6Dec2net' without Math::Bigint Reference! (" . (caller(0))[0] . '->' . (caller(0))[2] . ")\n"; return 0; } my $netv6Dec = $netv6Dect->copy(); my ($ipv6Dec, $cidr) = &netv6Dec2IpCidr($netv6Dec); my $ipv6 = &ipv6Dec2ip($ipv6Dec); return $ipv6 . '/' . $cidr; } sub getCidrFromDec { my $dec = shift; my $cidr = $dec % 256; return $cidr; } sub getCidrFromNetv6Dec { my $netv6Dect = shift; return 0 unless ref $netv6Dect; my $netv6Dec = $netv6Dect->copy(); my $base = 65536; my $cidr = $netv6Dec->copy()->bmod($base); return $cidr; } sub netv6Dec2PartsDec { my $netv6Dect = shift; return 0 unless ref $netv6Dect; my $netv6Dec = $netv6Dect->copy(); my $base = 65536; my $temp = Math::BigInt->new($base); my $cidr = $netv6Dec->copy()->bmod($base); ($netv6Dec->bsub($cidr))->bdiv($base); $temp->bpow(4); my $host = $netv6Dec->copy()->bmod($temp); ($netv6Dec->bsub($host))->bdiv($temp); $temp->bpow(2); my $net = $netv6Dec->copy()->bmod($temp); return ($net, $host, $cidr); } sub ipv6DecCidr2NetaddressV6Dec { my $ipv6Dect = shift; my $cidr = shift; return 0 unless ref $ipv6Dect; my $ipv6Dec = $ipv6Dect->copy(); my $base = Math::BigInt->new(2); $base->bpow(128 - $cidr); my $tooMuch = $ipv6Dec->copy()->bmod($base); $ipv6Dec->bsub($tooMuch); return $ipv6Dec; } sub ipv6Sort { # Because sorting with Math::BigInt objects is too expensive, we use a trick to sort those big numbers numerically with strings sort {substr('0'x 44 .$a, -44) cmp substr('0'x 44 .$b, -44)} @_; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Plugin.pm0000644000175000000000000000547611706125774015140 0ustar fighterrootpackage HaCi::Plugin; use strict; use warnings; use HaCi::Utils qw(getPluginValue); require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( $INFO ); our $INFO = { name => 'NONAME', version => '0.1', recurrent => 0, onDemand => 0, description => '', api => [], globMenuRecurrent => [], globMenuOnDemand => [], menuRecurrent => [], menuOnDemand => [], }; sub new { my $class = shift; my $ID = shift || -1; my $valueTable = shift || undef; my $self = { ID => $ID, VALUETABLE => $valueTable, ERROR => 0, ERRORSTR => '', }; bless $self, $class; return $self; } sub run_recurrent { my $self = shift; return 1; } sub run_onDemand { my $self = shift; return 1; } sub show { my $self = shift; my $show = {}; return $show; } sub saveValue { my $self = shift; my $netID = shift; my $origin = shift; my $name = shift; my $value = shift; unless (defined $self->{VALUETABLE}) { warn "Cannot update Plugin Value. DB Error (pluginValue)\n"; return 0; } my $pluginValueEntry = ($self->{VALUETABLE}->search(['ID'], {pluginID => $self->{ID}, netID => $netID, name => $name}))[0]; $self->{VALUETABLE}->clear(); $self->{VALUETABLE}->pluginID($self->{ID}); $self->{VALUETABLE}->netID($netID); $self->{VALUETABLE}->origin($origin); $self->{VALUETABLE}->name($name); $self->{VALUETABLE}->value($value); if (defined $pluginValueEntry) { unless ($self->{VALUETABLE}->update({ID => $pluginValueEntry->{ID}})) { warn "Cannot update Plugin Value '$name': " . $self->{VALUETABLE}->errorStrs(); } } else { $self->{VALUETABLE}->insert(); if ($self->{VALUETABLE}->error()) { warn "Cannot insert Plugin Value '$name': " . $self->{VALUETABLE}->errorStrs(); } } } sub getValues { my $self = shift; my $origin = shift; my $netID = shift; my $results = {}; unless (defined $self->{VALUETABLE}) { warn "Cannot get Plugin Value. DB Error (pluginValue)\n"; return 0; } my @entries = (); if (defined $netID) { @entries = $self->{VALUETABLE}->search(['netID', 'name', 'value'], {pluginID => $self->{ID}, origin => $origin, netID => $netID}); } else { @entries = $self->{VALUETABLE}->search(['netID', 'name', 'value'], {pluginID => $self->{ID}, origin => $origin}); } if (@entries) { foreach (@entries) { $results->{$_->{netID}}->{$_->{name}} = $_->{value}; } } return $results; } sub getValue { my $self = shift; return &getPluginValue($self->{ID}, @_); } sub warnl { my $self = shift; my $msg = shift; my $toUser = shift; # unless ($toUser) { # warn $msg; # return; # } warn $msg; $self->{ERRORSTR} .= "\n" if $self->{ERROR}; $self->{ERROR} = 1; $self->{ERRORSTR} .= $msg; } sub ERROR { my $self = shift; return $self->{ERROR}; } sub ERRORSTR { my $self = shift; return $self->{ERRORSTR}; } 1; # vim:ts=2:sw=2:sts=2 HaCi/modules/HaCi/RESTWrapper.pm0000755000175000000000000000705712470146640016012 0ustar fighterrootpackage HaCi::RESTWrapper; # REST wrapper for HaCi XML-RPC-API # # The methods and parameters are equal to the XML-RPC-API # # Authentication: # * via HTTP basic authentication # * via username and password parameter # # USAGE: # * http(s)://$hostname/RESTWrapper/[API-METHOD]?username=&password=&[API-PARAMETER] # * http(s)://@\@$hostname/RESTWrapper/[API-METHOD]?[API-PARAMETER] # # Examples: # * http(s)://foo:bar\@$hostname/RESTWrapper/search?search=test?rootName=bigRoot # * http(s)://$hostname/RESTWrapper/getFreeSubnets?username=foo&password=bar&rootName=bigRoot&supernet=192.168.0.0%2f24&cidr=32&amount=3\n"; # # Return: JSON-Object use strict; use warnings; use Data::Dumper; use Apache2::RequestRec (); use Apache2::RequestIO (); use Apache2::Access (); use Apache2::Const -compile => qw(OK SERVER_ERROR HTTP_BAD_REQUEST); use JSON (); use URI::Query (); use Frontier::Client (); use Switch; my $r; $SIG{__DIE__} = sub { my $msg = join('', @_); $r->content_type('text/html'); warn $msg; print $msg; $r->status(Apache2::Const::SERVER_ERROR); }; sub handler { $r = shift; $r->allow_methods(1, qw(GET)); my $pathInfo = $r->path_info(); my $hostname = $r->hostname(); my $uq = URI::Query->new($r->args()); my %params = $uq->hash(); unless ($pathInfo =~ /^\/(\w+)$/) { print " USAGE: * http(s)://$hostname/RESTWrapper/[API-METHOD]?username=&password=&[API-PARAMETER] * http(s)://@\@$hostname/RESTWrapper/[API-METHOD]?[API-PARAMETER] Examples: * http(s)://foo:bar\@$hostname/RESTWrapper/search?search=test?rootName=bigRoot * http(s)://$hostname/RESTWrapper/getFreeSubnets?username=foo&password=bar&rootName=bigRoot&supernet=192.168.0.0%2f24&cidr=32&amount=3\n"; return Apache2::Const::HTTP_BAD_REQUEST; } my $method = $1; my $user = $r->user(); (undef, my $pass) = $r->get_basic_auth_pw(); $user ||= $params{username}; $pass ||= $params{password}; my $result = &wrapper($hostname, $method, $user, $pass, \%params); $r->content_type('application/json'); print JSON->new->allow_nonref->encode($result) unless ref($result) eq 'HASH' && $result->{omitOutput}; return Apache2::Const::OK; } sub wrapper { my $server = shift; my $method = shift; my $user = shift; my $pass = shift; my $params = shift; my $api = new Frontier::Client(url => "http://$server/RPC2"); my $session = $api->call('login', [$user, $pass]); die 'Login failed!' unless $session; # warn "Params: " . Dumper($params); my @methParams = (); switch ($method) { case "search" { @methParams = qw(search state exact templateName templateQuery rootName nrOfFreeSubs withDetails); } case "getFreeSubnets" { @methParams = qw(rootName supernet cidr amount); } case "getFreeSubnetsFromSearch" { @methParams = qw(search state exact templateName templateQuery rootName cidr amount); } case "addNet" { @methParams = qw(rootName network description state defSubnetSize templateName templateValues); } case "delNet" { @methParams = qw(rootName network); } case "assignFreeSubnet" { @methParams = qw(rootName supernet cidr description state defSubnetSize templateName templateValues); } case "getNetworkDetails" { @methParams = qw(rootName network); } case "getSubnets" { @methParams = qw(rootName supernet); } else { die "Unkown method '$method'\n"; } } my @args = (); foreach (@methParams) { push @args, $params->{$_} // ''; } # warn "Calling $method with: " . join(', ', @args) . "\n"; return $api->call($method, $session, @args); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/SnmpUtils.pm0000644000175000000000000000622411341614314015614 0ustar fighterrootpackage HaCi::SnmpUtils; use warnings; use strict; use Net::SNMP qw(:snmp); use HaCi::Log qw/warnl debug/; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( getSNMPSession getSNMPTree getSNMPValue ); our $conf; *conf = \$HaCi::Conf::conf; sub getSNMPSession { my $host = shift; my $comm = shift; my $opts = shift || {}; my $port = $opts->{port} || 161; my $nBlocking = $opts->{nBlocking} || 1; my $version = $opts->{version} || 'snmpv2'; my $timeout = $opts->{timeout} || 10; $version = 'snmpv' . $version unless $version =~ /^snmpv/; my ($session, $error) = Net::SNMP->session( -hostname => $host, -port => $port, -community => $comm, -version => $version, -nonblocking => $nBlocking, -timeout => $timeout ); if (!defined($session)) { &warnl("Cannot initiate Session for " . $host . ": $error! Abort...", 1); return undef; } return $session; } sub getSNMPTree { my $session = shift; my $oid = shift; my $results = {}; my $result = undef; my $version = $session->version; if ($version == 0) { $result = $session->get_next_request( -callback => [\&snmpCB, $results, $oid], -delay => 0, -varbindlist => [$oid] ); } elsif ($version == 1 || $version == 3) { $result = $session->get_bulk_request( -callback => [\&snmpCB, $results, $oid], -maxrepetitions => 10, -delay => 0, -varbindlist => [$oid] ); } else { &warnl("Bad SNMP Version '$version'!", 1); return undef; } if (!defined($result)) { &warnl("Cannot retrieve any Results for System: " . $session->hostname . ": " . $session->error, 1); return undef; } snmp_dispatcher(); return $results; } sub snmpCB { my ($session, $results, $query) = @_; my $version = $session->version; if (!defined($session->var_bind_list)) { &warnl("SNMP-Error: " . $session->error, 1); } else { my $next = undef; foreach my $oid (oid_lex_sort(keys(%{$session->var_bind_list}))) { if (!oid_base_match($query, $oid)) { $next = undef; last; } $next = $oid; my $value = $session->var_bind_list->{$oid}; $results->{$oid} = $value; } if (defined($next)) { my $result = undef; if ($version == 0) { $result = $session->get_next_request( -callback => [\&snmpCB, $results, $query], -delay => 0, -varbindlist => [$next] ); } elsif ($version == 1 || $version == 3) { $result = $session->get_bulk_request( -callback => [\&snmpCB, $results, $query], -maxrepetitions => 10, -delay => 0, -varbindlist => [$next] ); } else { &warnl("Bad SNMP Version '$version'!", 1); } if (!defined($result)) { &warnl("SNMP-Errro: " . $session->error, 1); } } } } sub getSNMPValue { my $session = shift; my $snmpOID = shift; my $result = $session->get_request( -varbindlist => [$snmpOID] ); if (!defined($result)) { &warnl("SNMP-Error: " . $session->error, 1) unless $session->error =~ /noSuchName/; return undef; } return (ref $result eq 'HASH') ? $result->{$snmpOID} : undef; } 1; HaCi/modules/HaCi/Tree.pm0000644000175000000000000003630712470146640014570 0ustar fighterrootpackage HaCi::Tree; use strict; use Data::Dumper; use HaCi::Mathematics qw/ dec2net net2dec getBroadcastFromNet getIPFromDec dec2ip ip2dec netv6Dec2net ipv6Sort netv6Dec2NextNetDec netv6Dec2ip netv6Dec2IpCidr ipv6DecCidr2netv6Dec getV6BroadcastIP /; use HaCi::Utils qw/checkSpelling_Net debug checkRight networkStateID2Name fillHoles nd dn quoteHTML checkNetACL getConfigValue/; use HaCi::GUI::gettext qw/_gettext/; our $conf; *conf = \$HaCi::Conf::conf; sub new { my $class = shift; my $self = {CNTER => 0}; bless $self, $class; return $self; } sub setNewRoot { my $self = shift; my $rootID = shift; if (exists $self->{TREE}->{$rootID}) { &debug("setNewRoot: This rootID '$rootID' allready exists!"); } else { $self->{TREE}->{$rootID}->{ENABLED} = 1; } } sub setRootName { my $self = shift; my $rootID = shift; my $name = shift || ''; if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{NAME} = $name; } else { &debug("setRootName: This rootID '$rootID' doesn't exists!"); } } sub setRootDescr { my $self = shift; my $rootID = shift; my $descr = shift; if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{DESCR} = $descr; } else { &debug("setRootDescr: This rootID '$rootID' doesn't exists!"); } } sub setRootExpanded { my $self = shift; my $rootID = shift; my $bExp = shift; if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{EXPANDED} = $bExp; } else { &debug("setRootExpanded: This rootID '$rootID' doesn't exists!"); } } sub setRootParent { my $self = shift; my $rootID = shift; my $bParent = shift; if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{PARENT} = $bParent; } else { &debug("setRootExpanded: This rootID '$rootID' doesn't exists!"); } } sub setRootV6 { my $self = shift; my $rootID = shift; my $v6 = shift; if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{V6} = $v6; } else { &debug("setRootV6: This rootID '$rootID' doesn't exists!"); } } sub addNet { my $self = shift; my $netID = shift; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $descr = shift; my $status = shift; my $parent = shift || 0; my $bFillNet = shift || 0; my $defSubnetSize = shift || 0; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); if ($ipv6) { my ($ipv6, $cidr) = split/\//, $network; $network = Net::IPv6Addr::to_string_preferred($ipv6) . '/' . $cidr; } unless (exists $self->{TREE}->{$rootID}) { &debug("setRootDescr: This rootID '$rootID' doesn't exists!"); return 0; } unless (defined $network) { &debug("addNetwork: you don't give me a network!"); return 0; } unless (&checkSpelling_Net($network, $ipv6)) { &debug("addNetwork: This doesn't look like a network: $network"); return 0; } if (exists $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}) { &debug("addNetwork: There exists allready such a network '$rootID:$network'"); } $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec} = { NETID => $netID, NETPARENT => $parent, IPV6 => $ipv6, NETWORK => $network, DESCR => (defined $descr) ? $descr : '', STATUS => &networkStateID2Name($status), FILLNET => (defined $bFillNet && $bFillNet) ? 1 : 0, DEFSUBNETSIZE => $defSubnetSize, }; } sub setNetExpanded { my $self = shift; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $bExp = shift; if (exists $self->{TREE}->{$rootID}) { if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{EXPANDED} = $bExp; } else { my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); &debug("setNetExpanded: This network '$network' doesn't exists!"); } } else { &debug("setNetExpanded: This rootID '$rootID' doesn't exists!"); } } sub setNetParent { my $self = shift; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $bParent = shift; if (exists $self->{TREE}->{$rootID}) { if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{PARENT} = $bParent; } else { my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); &debug("setNetParent: This network '$network' doesn't exists!"); } } else { &debug("setNetParent: This rootID '$rootID' doesn't exists!"); } } sub setInvisible { my $self = shift; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $bInv = shift; if (exists $self->{TREE}->{$rootID}) { if (exists $self->{TREE}->{$rootID}) { $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{INVISIBLE} = $bInv; } else { my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); &debug("setInvisible: This network '$network' doesn't exists!"); } } else { &debug("setInvisible: This rootID '$rootID' doesn't exists!"); } } sub checkNetworkHoles { my $self = shift; my $rootID = shift; my $ipv6 = shift; my $networkDecStart = shift; my $broadcastEnd = shift; my $nextNetDec = ($ipv6) ? &netv6Dec2NextNetDec($broadcastEnd, 128) : &net2dec(&dec2ip(&getIPFromDec($broadcastEnd) + 1) . '/32'); my $lastNetDec = ($ipv6) ? $networkDecStart->copy() : $networkDecStart; my @netst = keys %{$self->{TREE}->{$rootID}->{NETWORKS}}; my @nets = (); my $defSubnetSize = ($lastNetDec == $networkDecStart) ? ((exists $self->{TREE}->{$rootID}->{NETWORKS}->{$lastNetDec}) ? $self->{TREE}->{$rootID}->{NETWORKS}->{$lastNetDec}->{DEFSUBNETSIZE} : 0) : ((exists $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDecStart}) ? $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDecStart}->{DEFSUBNETSIZE} : 0); if ($ipv6) { @nets = &ipv6Sort(@netst); } else { @nets = sort {$a<=>$b} @netst; } foreach (@nets) { my $networkDec = ($ipv6) ? Math::BigInt->new($_) : $_; next if $networkDec < $networkDecStart; last if $networkDec > $broadcastEnd; next if $networkDecStart != $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{NETPARENT}; foreach (&fillHoles($lastNetDec, $networkDec, $ipv6, $defSubnetSize)) { &addNet($self, -1, $rootID, $ipv6, $_, '', 0, 1, $networkDecStart, 0); } $lastNetDec = ($ipv6) ? &netv6Dec2NextNetDec($networkDec, 0) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec) + 1) . '/0'); } foreach (&fillHoles($lastNetDec, $nextNetDec, $ipv6, $defSubnetSize)) { &addNet($self, -1, $rootID, $ipv6, $_, '', 0, 1, $networkDecStart, 0); } } sub print_html { my $self = shift; my $html = []; my $session = $HaCi::HaCi::session; my $sortBox = {}; foreach my $rootID (keys %{$self->{TREE}}) { my $rootName = $self->{TREE}->{$rootID}->{NAME} || ''; $sortBox->{$rootName} = $rootID; } foreach my $rootName (sort keys %{$sortBox}) { my $rootID = $sortBox->{$rootName}; my $tmp = &print_html_root($self, $rootID); my $networks = &print_html_networks($self, $rootID); @{$tmp->{networks}} = @{$networks}; push @{$html}, $tmp; } return $html; } sub print_html_root { my $self = shift; my $rootID = shift; my $root = $self->{TREE}->{$rootID}; my $rootName = "eHTML($root->{NAME}) || '???'; my $rootDescr = "eHTML($root->{DESCR}) || ''; my $rootExpanded = $root->{EXPANDED}; my $rootParent = $root->{PARENT}; my $ipv6 = $root->{V6}; my $rootColor = $self->{user}->{gui}->{colors}->{root}; my $bEditTree = (defined $HaCi::HaCi::q->param('editTree') && $HaCi::HaCi::q->param('editTree')) ? 1 : 0; my $bShowRoot = &checkRight('showRootDet'); my $thisScript = $conf->{var}->{thisscript} || ''; my $picUrl = (($rootExpanded) ? 'reduceRoot' : 'expandRoot') . "(['args__$rootID', 'args__$bEditTree', 'NO_CACHE'], ['$rootID'], 'POST');"; my $root = { space => 0, ID => $rootID, name => $rootName, descr => $rootDescr, expanded => $rootExpanded, parent => $rootParent, picTitle => sprintf(_gettext((($rootExpanded) ? 'Reduce ' : 'Expand ') . "Root '%s'"), $rootName), picUrl => $picUrl, rootUrl => ($bShowRoot) ? "$thisScript?func=showRoot&rootID=$rootID" : '', rootPic => ($ipv6) ? 'ipv6.png' : '', rootAlt => ($ipv6) ? 'IPv6' : '', color => $rootColor, ipv6 => $ipv6, }; return $root; } sub print_html_networks { my $self = shift; my $rootID = shift; my $newLevel = shift || 0; my $origNetworkDec = shift || 0; my $networks = []; my $root = $self->{TREE}->{$rootID}; my $ipv6 = $root->{V6}; $origNetworkDec = Math::BigInt->new($origNetworkDec) if $ipv6; my $thisScript = $conf->{var}->{thisscript} || ''; my $session = $HaCi::HaCi::session; my $bEditTree = (defined $HaCi::HaCi::q->param('editTree') && $HaCi::HaCi::q->param('editTree')) ? 1 : 0; my $bShowNet = &checkRight('showNetDet'); my $bAddNet = &checkRight('addNet'); my $level = 1 + $newLevel; my $offset = 19; my $lastBroadcast = 0; my $lastCidr = 0; my $parentBroadcast = 0; my $parentCidr = 0; my @broadcasts = (); my @parentCidrs = (); my $bNoDiv = 0; my @netst = keys %{$root->{NETWORKS}}; my @nets = (); if ($ipv6) { @nets = &ipv6Sort(@netst); } else { @nets = sort {$a<=>$b} @netst; } my $netCnter = 0; my $lastParents = {}; my $parentMarker = {}; foreach (@nets) { my $networkDec = $_; my $net = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}; my $network = $net->{NETWORK}; my $netID = $net->{NETID}; my $descr = "eHTML($net->{DESCR}); my $expanded = $net->{EXPANDED}; my $parent = $net->{PARENT}; my $fillNet = $net->{FILLNET} || 0; my $status = $net->{STATUS} || 'assigned'; my $invisible = $net->{INVISIBLE} || 0; my $defSubnetSize = $net->{DEFSUBNETSIZE} || 0; $ipv6 = $net->{IPV6}; my $bJumpTo = (defined $session->param('jumpTo') && $session->param('jumpToNet') == $networkDec && $session->param('jumpToRoot') == $rootID) ? 1 : 0; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $broadcast = ($ipv6) ? &getV6BroadcastIP($networkDec) : &getBroadcastFromNet($networkDec); my $lastNet = 1 if $netCnter++ == $#nets; my ($ipaddressDec, $cidr); if ($ipv6) { ($ipaddressDec, $cidr) = &netv6Dec2IpCidr($networkDec); } else { (my $ipaddress, $cidr) = split/\//, $network; $ipaddressDec = &ip2dec($ipaddress); } my $netColor = '#' . ( ($invisible) ? $conf->{user}->{gui}->{colors}->{network}->{invisible} : ($fillNet) ? $conf->{user}->{gui}->{colors}->{network}->{new} : ($conf->{user}->{gui}->{colors}->{network}->{(lc($status))}) ? $conf->{user}->{gui}->{colors}->{network}->{(lc($status))} : $conf->{user}->{gui}->{colors}->{network}->{assigned}); $netColor = '#000000' unless $netColor; my $bgColor = ($bJumpTo) ? $conf->{user}->{gui}->{colors}->{network}->{searched} : ''; if ($lastBroadcast > $ipaddressDec) { $level++; push @broadcasts, $lastBroadcast; $parentBroadcast = $lastBroadcast; push @parentCidrs, $lastCidr; $parentCidr = $lastCidr; } else { foreach (reverse @broadcasts) { next unless $_ < $ipaddressDec; $level--; pop @broadcasts; $parentBroadcast = $broadcasts[-1]; pop @parentCidrs; $parentCidr = $parentCidrs[-1]; my $cNet = pop @{$networks}; $cNet->{parentBroadcast} = 1; push @{$networks}, $cNet; } } if (!$ipv6 && &getConfigValue('gui', 'shownetborders')) { if ($fillNet && $cidr == (($ipv6) ? 128 : 32)) { if (!($ipaddressDec % (2 ** ((($ipv6) ? 128 : 32) - $parentCidr)))) { $descr = '[netaddress]'; $netColor = '#' . $conf->{user}->{gui}->{colors}->{network}->{netborder}; } elsif ($parentBroadcast == $ipaddressDec) { $descr = '[broadcast]'; $netColor = '#' . $conf->{user}->{gui}->{colors}->{network}->{netborder}; } } } my $picUrl = ($invisible) ? '' : (($expanded) ? 'reduceNetwork' : 'expandNetwork') . "(['args__$rootID', 'args__$networkDec', 'args__" . ($level - 1) . "', 'args__$bEditTree', 'NO_CACHE'], ['${rootID}.$networkDec'], 'POST');setTimeout('showStatus(1)', 1000);"; $bNoDiv = 1 if $networkDec == $origNetworkDec; my $statePic = 0; my $stateAlt = ''; my $ucStatus = uc($status); if ($ucStatus eq 'LOCKED') { $statePic = 'key_small.png'; $stateAlt = _gettext("This Network is locked"); } elsif ($ucStatus eq 'RESERVED') { $statePic = 'reserved_small.png'; $stateAlt = _gettext("This Network is reserved"); } elsif ($ucStatus eq 'FREE') { $statePic = 'free_small.png'; $stateAlt = _gettext("This Network is free"); } $lastParents->{$level} = $netCnter; $parentMarker->{$netCnter} = $level if $parent && $level > ($newLevel + 1); my $lastParent = 0; if ($origNetworkDec == $networkDec) { # don't set lastParent, because it doesn't work probably $lastParent = 0; #($HaCi::HaCi::netcache->{GUI}->{$rootID}->{$origNetworkDec}->{LASTPARENT}) ? $level : 0; } # remove CIDR from IP if wanted $network =~ s/\/\d+// if &getConfigValue('gui', 'removecidrfromips') && $cidr == (($ipv6) ? 128 : 32); push @{$networks}, { rootID => $rootID, space => $level * $offset, level => $level, network => $network, networkDec => $networkDec, descr => ($invisible) ? _gettext("[ permission denied ]") : $descr, expanded => $expanded, parent => ($invisible) ? 0 : $parent, fillNet => $fillNet, picTitle => sprintf(_gettext((($expanded) ? 'Reduce ' : 'Expand ') . "Network '%s'"), $network), picUrl => $picUrl, netUrl => ($invisible) ? '' : ($fillNet && $bAddNet) ? "a('addNet', 0, '$rootID', '$networkDec', 1);" : (!$fillNet && $bShowNet) ? "a('showNet', '$netID', 0, 0, 0);" : '', color => $netColor, bgColor => $bgColor, noDiv => ($networkDec == $origNetworkDec) ? 1 : 0, statePic => $statePic, stateAlt => $stateAlt, invisible => $invisible, ipv6 => $ipv6, bEditNet => &checkRight('editNet') && &checkNetACL($netID, 'w'), editPic => 'edit_small.png', editAlt => _gettext('Edit'), editNetUrl => "a('editNet', '$netID',0,0,0);", bDelNet => &checkRight('editNet') && &checkNetACL($netID, 'w'), delPic => 'del_small.png', delAlt => _gettext('Delete'), delNetUrl => "a('delNet', '$netID',0,0,0);", defSubnetSize => $defSubnetSize, gettext_defSubnetSize => _gettext("Default Subnet CIDR"), lastNet => $lastNet, lastParent => $lastParent, GUINetCache => ((exists $HaCi::HaCi::netcache->{GUI}->{$rootID}->{$networkDec}->{noLines}) ? $HaCi::HaCi::netcache->{GUI}->{$rootID}->{$networkDec}->{noLines} : []), }; $lastBroadcast = $broadcast; $lastCidr = $cidr; } foreach my $netCnter (sort {$a<=>$b} keys %{$parentMarker}) { my $level = $parentMarker->{$netCnter}; my $net = ${$networks}[($netCnter - 1)]; if ($lastParents->{$level} == $netCnter) { # don't set lastParent, because it doesn't work probably $net->{lastParent} = 0; #$level; ${$networks}[($netCnter - 1)] = $net; $HaCi::HaCi::netcache->{GUI}->{$rootID}->{$net->{networkDec}}->{LASTPARENT} = 1; } else { $HaCi::HaCi::netcache->{GUI}->{$rootID}->{$net->{networkDec}}->{LASTPARENT} = 0; } } if ($level > 1) { my $net = pop @{$networks}; $net->{parentBroadcast} = ($level - 1); $net->{parentBroadcast}-- if $bNoDiv; push @{$networks}, $net; } return $networks; } 1; # vi: ts=2:sw=2:sws=2 HaCi/modules/HaCi/Utils.pm0000644000175000000000000065243212475466456015013 0ustar fighterrootpackage HaCi::Utils; use warnings; use strict; use File::Temp qw/tempfile/; use Net::IPv6Addr; use Time::Local; use Config::General qw(ParseConfig); use Net::CIDR; use Digest::SHA; use Encode; use Encode::Guess; use HTML::Entities; use POSIX qw(strftime); use Data::Dumper qw/Dumper/; use Text::CSV; #use Time::HiRes qw/gettimeofday tv_interval/; #my $t0 = undef; use HaCi::Conf qw/getConfigValue/; use HaCi::Mathematics qw/ net2dec dec2net ip2dec dec2ip getCidrFrom2IPs getBroadcastFromNet getNetmaskFromCidr getNetaddress getCidrFromNetmask getIPFromDec ipv62dec ipv6Parts2NetDec netv6Dec2PartsDec getV6BroadcastIP getV6BroadcastNet netv6Dec2ip ipv62Dec2 ipv6Dec2ip netv6Dec2net netv62Dec netv6Dec2IpCidr ipv6DecCidr2netv6Dec netv6Dec2NextNetDec ipv6DecCidr2NetaddressV6Dec ipv6Sort /; use HaCi::Authentication::internal qw/getCryptPassword lwe bin2dec/; use HaCi::GUI::gettext qw/_gettext/; use HaCi::Log qw/warnl debug/; use HaCi::Importer::Cisco; use HaCi::Importer::Juniper; use HaCi::Importer::Foundry; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( warnl debug importCSV compare checkDB newWindow prNewWindows getID getStatus setStatus addRoot addNet getRoots checkSpelling_Net importASNRoutes getNextDBNetwork getConfig getWHOISData getNSData rootID2Name rootName2ID getNrOfChilds getMaintInfosFromRoot checkNetworkLock getMaintInfosFromNet editRoot delNet genRandBranch delRoot copyNetsTo delNets search checkSpelling_IP checkSpelling_CIDR networkStateName2ID networkStateID2Name getNetworkTypes getTemplate saveTmpl prWarnl getTemplateEntries tmplID2Name delTmpl getTemplateData getAuditLogs getGroups getGroup saveGroup groupID2Name delGroup getUsers getUser userID2Name saveUser currDate lwd dec2bin checkRight importDNSTrans importDNSLocal fillHoles _gettext getParam netID2Stuff getNetworkParentFromDB importConfig delUser getRights parseCSVConfigfile exportSubnets removeStatus expand splitNet combineNets getDBNetworkBefore getNetID getPlugins updatePluginDB getPluginsForNet pluginID2Name getTable checkTables updatePluginLastRun rootID2ipv6 changeTmplName checkRootACL checkNetACL checkNetworkTable checkNetworkACTable netv6Dec2ipv6ID getPluginInfos getPluginConfMenu mkPluginConfig getNetworksForPlugin getPluginConfValues getNetworkChilds initTables initCache finalizeTables getPluginLastRun pluginName2ID getPluginValue getHaCidInfo nd dn quoteHTML getAvailTags getFreeSubnets pluginID2File chOwnPW getSettings userName2ID updSettings updateSettingsInSession flushACLCache flushNetCache getConfigValue tmplName2ID tmplEntryDescr2EntryID searchAndGetFreeSubnets ); our $conf; *conf = \$HaCi::Conf::conf; sub prWarnl { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $plain = shift || 0; my $t = $HaCi::GUI::init::t; my $ret = ($plain) ? '' : 0; if ($#{$conf->{var}->{warnl}} > -1) { $ret = ($plain) ? join('', @{$conf->{var}->{warnl}}) : 1; map {s/\\n/\n/g;$_ = "eHTML($_)} @{$conf->{var}->{warnl}}; $t->{V}->{warnlHeader} = _gettext("Infos / Warnings / Errors"); push @{$t->{V}->{warnlMenu}}, ( { elements => [ { target => 'single', type => 'label', width => 500, align => 'left', value => '
' . join("\n", @{$conf->{var}->{warnl}}) . '
' }, ] }, { value => { type => 'hline' } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'statusInfoCloser', onClick => "hideWarnl()", value => '1', img => 'cancel_small.png', picOnly => 1, title => _gettext("Close Info"), }, ], }, ] } ); } return $ret; } sub checkSpelling_Net { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $net = shift; my $ipv6 = shift; if ($net =~ /([\w\.\:]+)\/(\d{1,3})/) { return 0 unless &checkSpelling_IP($1, $ipv6); return 0 unless &checkSpelling_CIDR($2, $ipv6); return 1; } else { return 0; } } sub checkSpelling_IP { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $address = shift; my $ipv6 = shift; if ($ipv6) { eval { Net::IPv6Addr::ipv6_parse($address) }; if ($@) { warn $@; return 0; } else { return 1; } } else { if ($address =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) { foreach (split/\./, $1) { return 0 if $_ < 0 || $_ > 255; } return 1; } else { return 0; } } } sub checkSpelling_CIDR { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $cidr = shift; my $ipv6 = shift; if ($ipv6) { if ($cidr =~ /^(\d{1,3})$/) { return 0 if $1 < 0 || $1 > 128; return 1; } else { return 0; } } else { if ($cidr =~ /^(\d{1,2})$/) { return 0 if $1 < 0 || $1 > 32; return 1; } else { return 0; } } } sub addRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $name = shift; my $descr = shift; my $ipv6 = shift; my $rootID = shift || 0; my $editRoot = shift || 0; my $q = $HaCi::HaCi::q; my $session = $HaCi::HaCi::session; $descr ||= ''; $ipv6 ||= 0; my $bImp = 0; if (ref($q)) { # NON-API access? $editRoot = (&getParam(1, 0, 'editRoot')) ? 1 : 0; $bImp = (&getParam(1, 0, 'submitImportASNRoutes')) ? 1 : 0; $bImp ||= (&getParam(1, 0, 'submitImportDNS')) ? 1 : 0; } unless (defined $name && $name) { &warnl(sprintf(_gettext("Sorry, you have to give me a %s!"), 'name')); return 0; } my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot add Route. DB Error (root)\n"; return 0; } $rootTable->clear(); $rootTable->name($name); $rootTable->description($descr); $rootTable->ipv6($ipv6); if (defined $rootID && $rootID) { my $root = ($rootTable->search(['ID', 'name'], {ID => $rootID}))[0]; if (defined $root && !$editRoot) { &warnl(sprintf(_gettext("Sorry, this Root '%s' allready exists!"), $root->{name})); return 0; } $rootTable->modifyFrom($session->param('username')); $rootTable->modifyDate(&currDate('datetime')); unless ($rootTable->update({ID => $rootID})) { my $errStr = "Cannot update Root: " . $rootTable->errorStrs; &warnl($errStr); &audit('root.update', $name, &tableContent2Str($rootTable), $errStr); return 0; } else { &audit('root.update', $name, &tableContent2Str($rootTable)); } } else { my $DBROOT = ($rootTable->search(['ID', 'name'], {name => $name}))[0]; if (defined $DBROOT) { unless ($editRoot) { &warnl(sprintf(_gettext("Sorry, this Root '%s' allready exists!"), $DBROOT->{name})); return 0; } $rootTable->modifyFrom($session->param('username')); $rootTable->modifyDate(&currDate('datetime')); &debug("Change Root-Entry for '$name'\n"); unless ($rootTable->update({ID => $DBROOT->{'ID'}})) { my $errStr = "Cannot update Root: " . $rootTable->errorStrs; &warnl($errStr); &audit('root.update', $name, &tableContent2Str($rootTable), $errStr); return 0; } else { &audit('root.update', $name, &tableContent2Str($rootTable)); } } else { $rootTable->ID(undef); $rootTable->createFrom($session->param('username')); $rootTable->createDate(&currDate('datetime')); unless ($rootTable->insert()) { my $errStr = "Cannot insert new Root: " . $rootTable->errorStrs; &warnl($errStr); &audit('root.add', $name, &tableContent2Str($rootTable), $errStr); return 0; } else { &audit('root.add', $name, &tableContent2Str($rootTable)); } } } $rootID = &rootName2ID($name); my $errors = ''; my $rootACTable = $conf->{var}->{TABLES}->{rootAC}; unless (defined $rootACTable) { warn "Cannot add Root ACL. DB Error (rootAC)\n"; } else { my @acls = $rootACTable->search(['ID', 'groupID'], {rootID => $rootID}); foreach (@acls) { $rootACTable->clear(); unless ($rootACTable->delete({ID => $_->{ID}})) { $errors .= $rootACTable->errorStrs(); } &removeACLEntry($rootID, 'root', $_->{groupID}); } &warnl($errors) if $errors; my $box = {}; foreach (split(/, /, $session->param('groupIDs'))) { s/\D//g; $box->{$_} = 3; } if (ref($q)) { # No API access? foreach ($q->param) { if (/accGroup_([rwi])_(\d+)/) { my $right = $1; my $groupID = $2; if ($right eq 'i') { $box->{$groupID} = 0; } else { $box->{$groupID} += ($right eq 'r') ? 1 : ($right eq 'w') ? 2 : 0; } } } } $errors = ''; my $auditData = ''; foreach (keys %{$box}) { my $groupName = &groupID2Name($_); unless ($groupName eq 'Administrator') { my $acl = 'none'; $acl = ($box->{$_} == 3) ? 'rw' : 'ro' if $box->{$_}; $auditData .= ';' if $auditData; $auditData .= $groupName . '=' . $acl; } $rootACTable->clear(); $rootACTable->rootID($rootID); $rootACTable->ID(undef); $rootACTable->groupID($_); $rootACTable->ACL($box->{$_}); unless ($rootACTable->insert()) { $errors .= $rootACTable->errorStrs; } &removeACLEntry($rootID, 'root', $_); } &warnl("Errors while setting Root ACLs: " . $errors) if $errors; &audit('rootACL.update', $name, $auditData, $errors) if $auditData; } warn "Successfully " . (($editRoot) ? 'updated' : 'created') . " Root '$name'\n"; return 1; } sub currDate { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $time = shift; if ($type eq 'datetime') { return strftime "%F %T", ((defined $time) ? localtime($time) : localtime); } } sub addNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $rootID = shift; my $netaddress = shift; my $cidr = shift; my $descr = shift; my $state = shift; my $tmplID = shift; my $defSubnetSize = shift || 0; my $forceState = shift || 0; my $tmplDataBox = shift || 0; my $editNet = shift || 0; my $tags = shift || ''; $tags =~ s/^\s+//; $tags =~ s/\s+$//; my $dontChangeACL = 0; my $dontChangePlugins = 0; my $session = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; my $bImp = 0; my $onlyNew = 0; my $aclsFromParent = 0; my $netPluginActives = []; my $netPluginNewLines = []; if (ref($q)) { $bImp = (&getParam(1, 0, 'submitImportASNRoutes')) ? 1 : 0; $bImp ||= (&getParam(1, 0, 'submitImpDNSTrans')) ? 1 : 0; $bImp ||= (&getParam(1, 0, 'submitImpDNSLocal')) ? 1 : 0; $bImp ||= (&getParam(1, 0, 'submitImpConfig')) ? 1 : 0; $bImp ||= (&getParam(1, 0, 'submitImpCSV')) ? 1 : 0; $onlyNew = (&getParam(1, 0, 'onlyNew')) ? 1 : 0; $editNet = (&getParam(1, 0, 'editNet')) ? 1 : 0; $netPluginActives = &getParam(0, [], 'netPluginActives'); $netPluginNewLines = &getParam(0, [], 'netPluginNewLines'); $aclsFromParent = (&getParam(1, 0, 'aclsFromParent')) ? 1 : 0; $tmplDataBox = {}; } else { $dontChangeACL = 1; # you can't change ACLs by API, yet $dontChangePlugins = 1; # you can't change Plugins by API, yet } my $rootInfos = &getMaintInfosFromRoot($rootID); my $ipv6 = $rootInfos->{ipv6}; my $ipv6ID = ''; eval { $netaddress = Net::IPv6Addr::to_string_preferred($netaddress) if $ipv6 && $netaddress =~ /::/; }; if ($@) { &warnl($@); return 0; } if (ref($q) && !&getParam(1, 0, 'overwrite')) { $onlyNew = (&getParam(1, 0, 'overwrite')) ? 0 : 1; } unless (&checkSpelling_IP($netaddress, $ipv6)) { &warnl("This Netaddress '$netaddress' is incorrect!"); return 0; } unless (&checkSpelling_CIDR($cidr, $ipv6)) { &warnl("This Cidr '$cidr' is incorrect!"); return 0; } if (($defSubnetSize != 0 && $defSubnetSize <= $cidr) || ($ipv6 && $defSubnetSize > 128) || (!$ipv6 && $defSubnetSize > 32)) { &warnl("This default subnet size '$defSubnetSize' is incorrect!"); return 0; } my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot add Network. DB Error (network)\n"; return 0; } my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot add NetworkV6. DB Error (networkV6)\n"; return 0; } unless (defined $rootID && $rootID) { &warnl(sprintf(_gettext("Sorry, you have to give me a %s!"), 'rootID')); return 0; } unless (defined $netaddress && $netaddress) { &warnl(sprintf(_gettext("Sorry, you have to give me a %s!"), 'netaddress')); return 0; } $cidr = (($ipv6) ? 128 : 32) unless defined $cidr; my $netaddressT = ($ipv6) ? &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($netaddress), $cidr)) : &dec2ip(&getNetaddress($netaddress, &getNetmaskFromCidr($cidr))); if ($ipv6) { eval { $netaddressT = Net::IPv6Addr::to_string_preferred($netaddressT); $netaddress = Net::IPv6Addr::to_string_preferred($netaddress); }; if ($@) { &warnl($@); return 0; } } if (lc($netaddress) ne lc($netaddressT)) { &warnl(sprintf(_gettext("Sorry, this is not a correct Network: %s!"), $netaddress . '/' . $cidr)); return 0; } my $network = $netaddress . '/' . $cidr; &debug((($editNet) ? 'Edit' : 'Add') . " Network $network on " . $rootInfos->{name}); unless (&checkSpelling_Net($network, $ipv6)) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'network', $network)); return 0; } my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); return 0 unless &checkNetworkLock($rootID, $networkDec, $ipv6); if ($bImp) { $ipv6ID = Net::IPv6Addr::to_string_base85($netaddress) . sprintf("%x", $cidr) if $ipv6; my $DBNET = ($networkTable->search(['ID'], {rootID => $rootID, network => (($ipv6) ? 0 : $networkDec), ipv6ID => $ipv6ID}))[0]; if (defined $DBNET) { $editNet = 1 unless $onlyNew; $netID = $DBNET->{ID}; } } unless ($forceState) { return 0 unless &checkStateRules($netID, $rootID, $networkDec, $state, $cidr, $ipv6); } if ($editNet) { if (defined $netID) { my ($rootID, $networkDec, undef) = &netID2Stuff($netID); my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); unless (&checkNetACL($netID, 'w')) { &warnl(sprintf(_gettext("Not enough rights to edit this Network '%s'"), $network)); return 0; } unless (ref($q) && &getParam(1, 0, 'submitImpCSV')) { &debug("Flushing Cache for: " . $rootInfos->{name}); &removeFromNetcache($rootID); } } else { &warnl("Without a netID I cannot edit this Net!"); return 0; } } unless ($editNet) { my $parent = &getNetworkParentFromDB($rootID, $networkDec); my $parentDec = (defined $parent) ? $parent->{network} : 0; my $parentID = (defined $parent) ? $parent->{ID} : 0; unless (($parentID && &checkNetACL($parentID, 'w')) || (!$parentID && &checkRootACL($rootID, 'w'))) { my $parent = ($ipv6) ? &netv6Dec2net($parentDec) : &dec2net($parentDec); &warnl(sprintf(_gettext("Not enough rights to add this Network '%s' under '%s'"), $network, $parent)); return 0; } } if ($ipv6) { my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot add NetworkV6. DB Error (networkV6)\n"; return 0; } $ipv6ID = Net::IPv6Addr::to_string_base85($netaddress) . sprintf("%x", $cidr); my ($net, $host) = &HaCi::Mathematics::ipv62dec2($netaddress); $networkV6Table->clear(); $networkV6Table->ID($ipv6ID); $networkV6Table->rootID($rootID); $networkV6Table->networkPrefix($net); $networkV6Table->hostPart($host); $networkV6Table->cidr($cidr); } $networkTable->clear(); $networkTable->rootID($rootID); $networkTable->network(($ipv6) ? 0 : $networkDec); $networkTable->description($descr); $networkTable->state($state); $networkTable->tmplID($tmplID); $networkTable->ipv6ID($ipv6ID); $networkTable->defSubnetSize($defSubnetSize); $networkTable->searchStr($network); my $auditData = "root => $rootInfos->{name}, description => $descr, state => " . &networkStateID2Name($state) . ", template => " . &tmplID2Name($tmplID) . ", defSubnetSize => $defSubnetSize"; my $origTmplID = undef; if ($editNet) { my $DBNET = ($networkTable->search(['ID', 'rootID', 'tmplID', 'ipv6ID'], {ID => $netID}))[0]; unless (defined $DBNET) { &warnl("Cannot update Net: No such Network exists ($netID)"); return 0; } $origTmplID = $DBNET->{'tmplID'}; $networkTable->modifyFrom($session->param('username')); $networkTable->modifyDate(&currDate('datetime')); unless ($networkTable->update({ID => $netID})) { my $errStr = "Cannot update Net: " . $networkTable->errorStrs; &warnl($errStr); &audit('network.update', $network, $auditData, $errStr); return 0; } else { &audit('network.update', $network, $auditData); } if ($ipv6) { unless (defined $DBNET) { &warnl("Cannot update Net: No such Network exists ($netID)"); return 0; } my $origRootID = $DBNET->{'rootID'}; unless ($networkV6Table->update({ID => $DBNET->{'ipv6ID'}, rootID => $origRootID})) { my $errStr = "Cannot update V6 Net: " . $networkV6Table->errorStrs; &warnl($errStr); return 0; } } unless (ref($q) && &getParam(1, 0, 'submitImpCSV')) { &debug("Flushing Cache for: " . $rootInfos->{name}); &removeFromNetcache($rootID); } } else { my $DBNET = ($networkTable->search(['ID'], {network => ($ipv6) ? 0 : $networkDec, rootID => $rootID, ipv6ID => $ipv6ID}))[0]; if (defined $DBNET) { &warnl(sprintf(_gettext("This Network '%s' allready exists!"), $network)); return 0; } $networkTable->ID(undef); $networkTable->createFrom($session->param('username')); $networkTable->createDate(&currDate('datetime')); unless ($networkTable->insert()) { my $errStr = "Cannot add Net: " . $networkTable->errorStrs; &warnl($errStr); &audit('network.add', $network, $auditData, $errStr); return 0; } else { &audit('network.add', $network, $auditData); } if ($ipv6) { unless ($networkV6Table->insert()) { my $errStr = "Cannot add V6 Net: " . $networkV6Table->errorStrs; &warnl($errStr); return 0; } } unless (ref($q) && &getParam(1, 0, 'submitImpCSV')) { &debug("Flushing Cache for: " . $rootInfos->{name}); &removeFromNetcache($rootID); } } $netID = &getNetID($rootID, $networkDec, $ipv6ID) unless $editNet; my $networkTagTable = $conf->{var}->{TABLES}->{networkTag}; unless (defined $networkTagTable) { warn "Cannot add Network Tags. DB Error (networkTag)\n"; } else { my @tagsDB = $networkTagTable->search(['ID'], {netID => $netID}); my $errors = ''; foreach (@tagsDB) { &debug("Deleting network tag ID: " . $_->{ID} . "\n"); $networkTagTable->clear(); unless ($networkTagTable->delete({ID => $_->{ID}})) { $errors .= $networkTagTable->errorStrs(); } } &warnl($errors) if $errors; foreach (split(/\s+/, $tags)) { my $tag = $_; $networkTagTable->clear(); $networkTagTable->netID($netID); $networkTagTable->ID(undef); $networkTagTable->tag($tag); unless ($networkTagTable->insert()) { $errors .= $networkTagTable->errorStrs; } } &warnl("Errors while setting network tags: " . $errors) if $errors; &audit('networkTag.update', $network, $tags, $errors); } unless ($dontChangeACL) { my $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { warn "Cannot add Network ACL. DB Error (networkAC)\n"; } else { my @acls = $networkACTable->search(['ID', 'groupID'], {netID => $netID}); my $errors = ''; foreach (@acls) { &debug("Deleting NetworkACL ID: " . $_->{ID} . "\n"); my $groupID = $_->{groupID}; $networkACTable->clear(); unless ($networkACTable->delete({ID => $_->{ID}})) { $errors .= $networkACTable->errorStrs(); } &removeACLEntry($netID, 'net', $groupID); } &warnl($errors) if $errors; my $box = {}; unless ($aclsFromParent) { foreach (split(/, /, $session->param('groupIDs'))) { s/\D//g; my $acl = &checkNetACL($netID, 'ACL', $_); $box->{$_} = ($acl == 3) ? -1 : 3; } if (ref($q)) { my $accGroups = &getParam(0, [], 'accGroup'); foreach (@{$accGroups}) { my $groupID = $_; my $acl = &checkNetACL($netID, 'ACL', $groupID); $box->{$groupID} = 0; foreach ('r', 'w') { my $right = $_; if (&getParam(1, 0, 'accGroup_' . $right . '_' . $groupID)) { $box->{$groupID} += (($right eq 'r') ? 1 : (($right eq 'w') ? 2 : 0)); } } $box->{$groupID} = -1 if $box->{$groupID} == $acl; } } } $errors = ''; my $auditData = ''; foreach (keys %{$box}) { my $groupID = $_; next if $box->{$groupID} == -1; my $groupName = &groupID2Name($groupID); unless ($groupName eq 'Administrator') { my $acl = 'none'; $acl = ($box->{$_} == 3) ? 'rw' : 'ro' if $box->{$_}; $auditData .= ';' if $auditData; $auditData .= $groupName . '=' . $acl; } $networkACTable->clear(); $networkACTable->netID($netID); $networkACTable->ID(undef); $networkACTable->groupID($groupID); $networkACTable->ACL($box->{$_}); unless ($networkACTable->insert()) { $errors .= $networkACTable->errorStrs; } &removeACLEntry($netID, 'net', $groupID); } &warnl("Errors while setting Network ACLs: " . $errors) if $errors; &audit('networkACL.update', $network, $auditData, $errors) if $auditData; } } if ($tmplDataBox && ref($tmplDataBox) eq 'HASH') { if ($tmplID) { my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { warn "Cannot add Template Value. DB Error (templateValue)\n"; return 0; } if (ref($q)) { foreach ($q->param) { if (/^tmplEntryID_(\d+)$/) { my $tmplEntryID = $1; next unless $tmplEntryID; my $value = &getParam(1, '', 'tmplEntryID_' . $tmplEntryID); $tmplDataBox->{$tmplEntryID} = $value; } } }; my $auditData = ''; foreach (keys %{$tmplDataBox}) { my $tmplEntryID = $_; my $value = $tmplDataBox->{$tmplEntryID}; if (ref($value)) { &warnl("Only strings are allowed for template values! (not " . ref($value) . ")\n"); &delNet($netID, 0, 0, 0) unless $editNet; return 0; } $tmplValueTable->clear(); $tmplValueTable->netID($netID); $tmplValueTable->tmplID($tmplID); $tmplValueTable->tmplEntryID($tmplEntryID); $tmplValueTable->value($value); my $tmplEntryDescr = &tmplEntryID2Name($tmplEntryID); $auditData .= "$tmplEntryDescr => $value "; my $DB = ($tmplValueTable->search(['ID'], {netID => $netID, tmplID => $tmplID, tmplEntryID => $tmplEntryID}))[0]; if ($DB) { &debug("Change Template-Value for '$rootID:$network $tmplID:$tmplEntryID'\n"); unless ($tmplValueTable->update({ID => $DB->{'ID'}})) { my $errStr = "Cannot update Template Value: " . $tmplValueTable->errorStrs; &warnl($errStr); return 0; } } else { $tmplValueTable->ID(undef); unless ($tmplValueTable->insert()) { my $errStr = "Cannot add Template Value: " . $tmplValueTable->errorStrs; &warnl($errStr); return 0; } } } &audit('templateValue.update', $network, $auditData); } if (defined $origTmplID && $origTmplID != $tmplID && $origTmplID) { my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { warn "Cannot remove old Template Values. DB Error (templateValue)\n"; return 1; } my @oldEntries = $tmplValueTable->search(['ID'], {netID => $netID, tmplID => $origTmplID}); my $errors = ''; foreach (@oldEntries) { &debug("Deleting Template Value for ID: " . $_->{ID} . "\n"); $tmplValueTable->errorStrs(''); unless ($tmplValueTable->delete({ID => $_->{ID}})) { $errors .= $tmplValueTable->errorStrs(); } } &warnl($errors) if $errors; } } unless ($dontChangePlugins) { my $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { warn "Cannot update PluginDB. DB Error (networkPlugin)\n"; return 0; } $networkPluginTable->clear(); my $netPlugins = {}; my $availPlugins = &getPlugins(); my $pluginsForNet = &getPluginsForNet($netID); foreach (keys %{$pluginsForNet}) { $netPlugins->{$_}->{ACTIVE} = 1;# unless $availPlugins->{$_}->{DEFAULT}; } my $errors = ''; foreach (@{$netPluginActives}) { my $pluginID = $_; my $pluginOrder = 0; my $entry = ($networkPluginTable->search(['ID'], {netID => $netID, pluginID => $pluginID}))[0]; $pluginOrder = &getParam(1, 0, 'pluginOrder_' . $pluginID) if ref($q); $networkPluginTable->clear(); $networkPluginTable->netID($netID); $networkPluginTable->pluginID($pluginID); $networkPluginTable->sequence($pluginOrder); $networkPluginTable->newLine(scalar grep {/^$pluginID$/} @{$netPluginNewLines}); unless (defined $entry) { &debug("Adding Plugin '$availPlugins->{$pluginID}->{NAME}' for network '$network'\n"); unless ($networkPluginTable->insert()) { $errors .= "Cannot insert Network Plugin Entry for '$availPlugins->{$pluginID}->{NAME}': " . $networkPluginTable->errorStrs(); } } else { my $ID = $entry->{ID}; &debug("Updating Plugin '$availPlugins->{$pluginID}->{NAME}' for network '$network'\n"); unless ($networkPluginTable->update({ID => $ID})) { $errors .= "Cannot update Network Plugin Entry for '$availPlugins->{$pluginID}->{NAME}': " . $networkPluginTable->errorStrs(); } } delete $netPlugins->{$pluginID}; } foreach (keys %{$netPlugins}) { my $pluginID = $_; my $entry = ($networkPluginTable->search(['ID'], {netID => $netID, pluginID => $pluginID}))[0]; if (defined $entry) { &debug("Deleting Plugin '$availPlugins->{$pluginID}->{NAME}' from network '$network'\n"); $networkPluginTable->clear(); $networkPluginTable->delete({netID => $netID, pluginID => $pluginID}); if ($networkPluginTable->error) { $errors .= '\n' . sprintf(_gettext("Error while deleting '%s' from '%s': %s"), $availPlugins->{$pluginID}->{NAME}, $network, $networkPluginTable->errorStrs); } } } &warnl($errors) if $errors; } return 1; } sub getRoots { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $checkACL = shift || 0; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot get Roots. DB Error (root)\n"; return []; } my @rootst = $rootTable->search(['ID', 'name', 'ipv6']); my $rootst = {}; foreach (@rootst) { my $id = $_->{ID}; next if $checkACL && !&checkRootACL($id, 'r'); $rootst->{$_->{name}} = { ID => $_->{ID}, ipv6 => $_->{ipv6}, }; } my $roots = []; foreach (sort keys %{$rootst}) { push @$roots, { name => $_, ID => $rootst->{$_}->{ID}, ipv6 => $rootst->{$_}->{ipv6}, }; } return $roots; } sub importASNRoutes { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $asn = shift; my $q = $HaCi::HaCi::q; my $delOld = (&getParam(1, 0, 'delOld')) ? 1 : 0; $asn = 'AS' . $asn unless $asn =~ /^AS/; my $status = $conf->{var}->{STATUS}; $status->{TITLE} = "Importing Routes for '$asn'..."; $status->{STATUS} = 'Runnung...'; $status->{PERCENT} = 0; &setStatus(); unless ($asn =~ /^AS\d{1,10}$/) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'AS Number in asplain notation', $asn)); return 0; } my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot import ASNRoutes. DB Error (root)\n"; return 0; } my $root = ($rootTable->search(['ID'], {name => $asn}))[0]; my $rootV6 = ($rootTable->search(['ID'], {name => $asn . '_IPv6'}))[0]; if ($delOld) { $status->{DATA} = 'removing old Roots'; $status->{PERCENT} = 1; &setStatus(); if (defined $root && exists $root->{ID}) { return 0 unless &delRoot($root->{ID}); $root = ($rootTable->search(['ID'], {name => $asn}))[0]; } if (defined $rootV6 && exists $rootV6->{ID}) { return 0 unless &delRoot($rootV6->{ID}); $rootV6 = ($rootTable->search(['ID'], {name => $asn . '_IPv6'}))[0]; } } $status->{DATA} = "Getting ASR Name for '$asn'";$status->{PERCENT} = 5; &setStatus(); my $asnName = &getASRName($asn); unless (defined $root && exists $root->{ID}) { unless (&addRoot($asn, $asnName)) { warn "AddRoot failed!\n"; return 0; } } unless (defined $rootV6 && exists $rootV6->{ID}) { unless (&addRoot($asn . '_IPv6', $asnName . ' (IPv6)', 1)) { warn "AddRoot failed!\n"; return 0; } } my $rootID = { V4 => &rootName2ID($asn), V6 => &rootName2ID($asn . '_IPv6'), }; $status->{DATA} = "Getting Routes for '$asnName'";$status->{PERCENT} = 10; &setStatus(); my $box = &getASRoutes($asn); my $nrOfRoutes = 0; my $nrOfINs = 0; my $nrOfSaves = 0; my @routes = keys %{$box}; my $ipv4Used = 0; my $ipv6Used = 0; foreach (@routes) { my $route = $_; my $ipv6 = $box->{$route}->{IPV6}; my $descr = $box->{$route}->{DESCR}; my $state = &networkStateName2ID(&getRouteState($route)); $nrOfINs++; if ($ipv6) { $ipv6Used = 1; } else { $ipv4Used = 1; } my $statPerc = (10 + int((90 / ($#routes + 1)) * $nrOfRoutes)); $status->{DATA} = "Getting Inetnums for '$route'";$status->{PERCENT} = $statPerc; &setStatus(); $nrOfRoutes++; &debug("found Route: $route"); my ($na, $c) = split/\//, $route; $nrOfSaves++ if &addNet(0, $rootID->{(($ipv6) ? 'V6' : 'V4')}, $na, $c, $descr, $state, 0, 0, 1); my $box = &getInetnums($route, $ipv6); my @inets = keys %{$box}; my $statCnter = 0; foreach (@inets) { my $inetnum = $_; my @cidrs = Net::CIDR::range2cidr($inetnum); foreach (@cidrs) { my ($from, $cidr) = split/\//, $_, 2; $statCnter++; $nrOfINs++; &debug("found Inetnum: $from/$cidr"); my $statPerc1 = (100 / ($#inets + 1)) * $statCnter; $status->{DATA} = "Saving Inetnum '$from/$cidr'";$status->{PERCENT} = int($statPerc + ((1 / ($#routes + 1)) * $statPerc1)); &setStatus(); next unless &addNet(0, $rootID->{(($ipv6) ? 'V6' : 'V4')}, $from, $cidr, $box->{$inetnum}->{DESCR}, &networkStateName2ID($box->{$inetnum}->{STATUS}), 0, 0, 1); $nrOfSaves++; } } } unless ($ipv4Used) { my $root = ($rootTable->search(['ID'], {name => $asn}))[0]; &delRoot($root->{ID}) if defined $root && exists $root->{ID}; } unless ($ipv6Used) { my $rootV6 = ($rootTable->search(['ID'], {name => $asn . '_IPv6'}))[0]; &delRoot($rootV6->{ID}) if defined $rootV6 && exists $rootV6->{ID}; } &warnl(sprintf(_gettext("%i Routes found. Saved %i new Inetnums of %i found"), $nrOfRoutes, $nrOfSaves, $nrOfINs)); $status->{STATUS} = 'FINISH'; $status->{DATA} = ''; $status->{PERCENT} = 100; &setStatus(); return 1; } sub getRouteState { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $route = shift; return '' unless $route =~ /^[\w\.:\/]+$/; unless (-x $conf->{static}->{path}->{whois}) { &warnl(sprintf(_gettext("Program Whois '%s' isn't executable"), $conf->{static}->{path}->{whois})); return ''; } my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -- $route); foreach (@whois) { return $1 if /^status:\s+(.*)$/; } return ''; } sub getASRName { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $asn = shift; return '' unless $asn =~ /^AS\d+$/; unless (-x $conf->{static}->{path}->{whois}) { &warnl(sprintf(_gettext("Program Whois '%s' isn't executable"), $conf->{static}->{path}->{whois})); return ''; } my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -- $asn); foreach (@whois) { return $1 if /^as-name:\s+(.*)$/; } return ''; } sub getASRoutes { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $asn = shift; return {} unless $asn =~ /^AS\d+$/; unless (-x $conf->{static}->{path}->{whois}) { &warnl(sprintf(_gettext("Program Whois '%s' isn't executable"), $conf->{static}->{path}->{whois})); return {}; } my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -- -i origin $asn); my $route = 0; my $box = {}; foreach (@whois) { $route = $1 if /^route6?:\s+(.*)$/; $box->{$route}->{DESCR} = $1 if /^descr:\s+(.*)$/; $box->{$route}->{IPV6} = 1 if /^route6:\s+/; } return $box; } sub getInetnums { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $route = shift; my $ipv6 = shift; return {} unless &checkSpelling_Net($route, $ipv6); unless (-x $conf->{static}->{path}->{whois}) { &warnl(sprintf(_gettext("Program Whois '%s' isn't executable"), $conf->{static}->{path}->{whois})); return {}; } my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -- -M $route); my $inetnum = 0; my $box = {}; foreach (@whois) { $inetnum = $1 if /^inet6?num:\s+(.*)$/; $box->{$inetnum}->{DESCR} = $1 if /^netname:\s+(.*)$/; $box->{$inetnum}->{STATUS} = $1 if /^status:\s+(.*)$/; } return $box; } sub importDNSTrans { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $nsServer = &getParam(1, '', 'nameserver'); my $domain = &getParam(1, '', 'domain'); my $status = &getParam(1, 0, 'state'); my $origin = &getParam(1, '', 'origin'); my $targetRootID = &getParam(1, -1, 'targetRoot'); my $stat = $conf->{var}->{STATUS}; $stat->{TITLE} = "Importing DNS Zonefile from Server '$nsServer'";$stat->{STATUS} = 'Running...'; $stat->{PERCENT} = 0; &setStatus(); unless ($nsServer =~ /^[\d\w\.\-]+$/) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'Nameserver', $nsServer)); $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'ERROR'; &setStatus(); return 0; } unless ($domain =~ /^[\d\w\.\-]+$/) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'Domain', $domain)); $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'ERROR'; &setStatus(); return 0; } my $zoneFile = &zoneTrans($nsServer, $domain); if ($#{$zoneFile} < 0) { $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'ERROR'; &setStatus(); return 0; } my $r = &parseZonefile($zoneFile, $domain, $status, $origin, $targetRootID); $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'FINISH'; &setStatus(); return $r; } sub zoneTrans { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $nsServer = shift; my $domain = shift; my $zoneFile = []; &debug("Retrieving Zonefile '$domain' from '$nsServer'"); eval { require Net::DNS; }; if ($@) { warn $@; return [] } else { open OLDOUT, ">&STDOUT"; open STDOUT, ">&STDERR"; my $res = Net::DNS::Resolver->new( debug => $conf->{static}->{misc}->{debug}, ); $res->tcp_timeout(10); $res->udp_timeout(10); $res->nameservers($nsServer); my @zone = $res->axfr($domain); unless (@zone) { &warnl(sprintf(_gettext("Zone transfer failed: %s"), $res->errorstring)); } else { foreach my $rr (@zone) { push @$zoneFile, $rr->string; } } close STDOUT; open STDOUT, ">&OLDOUT"; close OLDOUT; return $zoneFile; } } sub importConfig { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $file = &getParam(1, undef, 'config'); my $source = &getParam(1, undef, 'source'); my $state = &getParam(1, 0, 'state'); my $targetRootID = &getParam(1, -1, 'targetRoot'); my $configFile = ''; my $data = ''; my $session = $HaCi::HaCi::session; my $status = $conf->{var}->{STATUS}; $status->{DATA} = "Retrieving File '$file'";$status->{STATUS} = 'Running...'; $status->{PERCENT} = 0; &setStatus(); unless (defined $file) { &warnl(_gettext("No File given!")); $q->delete('source'); return 0; } unless (defined $source) { &warnl(_gettext("No source given!")); $q->delete('source'); return 0; } unless (binmode $file) { &warnl(sprintf(_gettext("Cannot open File in Binmode: %s"), $!)); $q->delete('source'); return 0; } while(read $file,$data,1024) { $configFile .= $data; } my ($rootName, $box) = (); if ($source eq 'csv') { my $id = $session->id(); unless (open EXPORT, '>' . $conf->{static}->{path}->{spoolpath} . '/' . "$id.tmp") { &warnl("Cannot open Temp File '$conf->{static}->{path}->{spoolpath}/$id.tmp' for writing: $!"); return 0; } print EXPORT $configFile; close EXPORT; $conf->{var}->{exportID} = $id; return 0; } elsif ($source eq 'cisco') { my $ic = new HaCi::Importer::Cisco; $ic->config($configFile); $ic->status($state); ($rootName, $box) = $ic->parse(); if ($ic->error) { &warnl('Error (HaCi::Importer::Cisco): ' . _gettext($ic->errorStr)); return 0; } } elsif ($source eq 'juniper') { my $ij = new HaCi::Importer::Juniper; $ij->config($configFile); $ij->status($state); ($rootName, $box) = $ij->parse(); if ($ij->error) { &warnl('Error (HaCi::Importer::Juniper): ' . _gettext($ij->errorStr)); return 0; } } elsif ($source eq 'foundry') { my $if = new HaCi::Importer::Foundry; $if->config($configFile); $if->status($state); ($rootName, $box) = $if->parse(); if ($if->error) { &warnl('Error (HaCi::Importer::Foundry): ' . _gettext($if->errorStr)); return 0; } } $rootName = (($targetRootID == -1) ? $rootName : &rootID2Name($targetRootID)); $rootName ||= $file; my @cnter = (0, 0); if ($#$box > -1) { my $rootIDv4; my $rootNamev4; my $rootIDv6; my $rootNamev6; my $rootID = &rootName2ID($rootName) || -1; if ($rootID != -1) { my $bIPv6 = &rootID2ipv6($rootID); if ($bIPv6) { $rootNamev4 = $rootName . '_v4'; $rootNamev6 = $rootName; $rootIDv4 = &rootName2ID($rootNamev4) || -1; $rootIDv6 = $rootID; } else { $rootNamev4 = $rootName; $rootNamev6 = $rootName . '_v6'; $rootIDv4 = $rootID; $rootIDv6 = &rootName2ID($rootNamev6) || -1; } } else { $rootNamev4 = $rootName . '_v4'; $rootNamev6 = $rootName . '_v6'; } my $v4 = 1; foreach my $entry (@$box) { if ($entry->{ip} =~ /^[\d\.]+$/) { if ($rootIDv4 == -1) { unless (&addRoot($rootNamev4)) { warn "AddRoot failed!\n"; return 0; } $rootIDv4 = &rootName2ID($rootNamev4); } $rootID = $rootIDv4; $v4 = 1; } elsif ($entry->{ip} =~ /^[\w\:]+$/) { if ($rootIDv6 == -1) { unless (&addRoot($rootNamev6, '', 1)) { warn "AddRoot failed!\n"; return 0; } $rootIDv6 = &rootName2ID($rootNamev6); } $rootID = $rootIDv6; $v4 = 0; } else { warn "Cannot add Network '" . $entry->{ip} . "' because it's malformed!\n"; next; } if(&addNet(0, $rootID, $entry->{ip}, $entry->{cidr}, $entry->{descr}, $state, 0, 0, 1)) { ($v4) ? $cnter[0]++ : $cnter[1]++; } } } &warnl(sprintf(_gettext("Added %i IPv4 and %i IPv6 Networks"), $cnter[0], $cnter[1])); return 1; } sub importCSV { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $configFileID = &getParam(1, undef, 'configFileID'); my $fileName = &getParam(1, undef, 'config'); my $state = &getParam(1, 0, 'state'); my $targetRootID = &getParam(1, -1, 'targetRoot'); my $tmplID = &getParam(1, 0, 'tmplID'); my $stat = $conf->{var}->{STATUS}; $stat->{TITLE} = "Importing CSV '$fileName'";$stat->{STATUS} = 'Running...'; $stat->{PERCENT} = 0; &setStatus(); my @data = &parseCSVConfigfile($configFileID, 0); unlink "$conf->{static}->{path}->{spoolpath}/$configFileID.tmp"; $stat = $conf->{var}->{STATUS}; $stat->{TITLE} = "Importing CSV '$fileName'";$stat->{STATUS} = 'Initializing...'; $stat->{PERCENT} = 9; &setStatus(); unless (defined $configFileID) { warn "No configFileID passed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } unless (defined $fileName) { warn "No config passed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } my $cnv = {}; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot add Root. DB Error (root)\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } foreach ($q->param) { if (/^COL_(\d+)$/) { $cnv->{$1} = &getParam(1, undef, $_); } } my $colCnter = 0; my @cnter = (0, 0); my $rootIDs = {}; foreach (@data) { my $cnter = 0; my @cols = @{$_}; my $network = ''; my $status = ''; my $description = ''; my $tags = ''; my $defSubnetSize = 0; my $percent = int(($colCnter++ / (($#data + 1) || 1)) * 100); foreach (@cols) { $cnter++; $network = $_ if $cnv->{$cnter} == -1; $status = $_ if $cnv->{$cnter} == -2; $description = $_ if $cnv->{$cnter} == -3; $tags = $_ if $cnv->{$cnter} == -4; $defSubnetSize = $_ if $cnv->{$cnter} == -5; if ($cnv->{$cnter} > 0) { $q->delete('tmplEntryID_' . $cnv->{$cnter}); $q->param('tmplEntryID_' . $cnv->{$cnter}, $_); } } next unless $network; if ($status ne '') { my $origStatus = $status; $status = &networkStateName2ID($status); &warnl(sprintf(_gettext("%s: Status (%s) not known"), $network, $origStatus)) if (!$status && $origStatus ne 'UNSPECIFIED'); } $status ||= $state; $stat->{DATA} = "Importing Network '$network' ($description)"; $stat->{PERCENT} = $percent; &setStatus(); my ($ip, $cidr) = split/\//, $network; my $rootName = (($targetRootID == -1) ? $fileName : &rootID2Name($targetRootID)); my $rootIDv4 = -1; my $rootNamev4 = ''; my $rootIDv6 = -1; my $rootNamev6 = ''; my $rootID = &rootName2ID($rootName) || -1; if ($rootID != -1) { my $bIPv6 = &rootID2ipv6($rootID); if ($bIPv6) { $rootNamev4 = $rootName . '_v4'; $rootNamev6 = $rootName; $rootIDv4 = &rootName2ID($rootNamev4) || -1; $rootIDv6 = $rootID; } else { $rootNamev4 = $rootName; $rootNamev6 = $rootName . '_v6'; $rootIDv4 = $rootID; $rootIDv6 = &rootName2ID($rootNamev6) || -1; } } else { $rootNamev4 = $rootName . '_v4'; $rootNamev6 = $rootName . '_v6'; } my $v = 4; if (&checkSpelling_Net($network, 0)) { if ($rootIDv4 == -1) { unless (&checkIfRootExists($rootNamev4)) { unless (&addRoot($rootNamev4, '', 0)) { warn "AddRoot failed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } } $rootIDv4 = &rootName2ID($rootNamev4); } $rootID = $rootIDv4; $v = 4; } elsif (&checkSpelling_Net($network, 1)) { if ($rootIDv6 == -1) { unless (&checkIfRootExists($rootNamev6)) { unless (&addRoot($rootNamev6, '', 1)) { warn "AddRoot failed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } } $rootIDv6 = &rootName2ID($rootNamev6); } $rootID = $rootIDv6; $v = 6; } else { warn "Bad Network: $ip/$cidr ($description)\n"; next; } my $netaddress = ''; my $stdCidr = 32; if ($v == 4) { $netaddress = ($cidr == 32) ? $ip : &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); } else { $netaddress = &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($ip), $cidr)); $stdCidr = 128; eval { $ip = Net::IPv6Addr::to_string_preferred($ip); $netaddress = Net::IPv6Addr::to_string_preferred($netaddress); }; if ($@) { warn $@; next; } } $rootIDs->{$rootID} = 1; if ($ip eq $netaddress) { $cnter[0 + (($v == 6) ? 1 : 0)]++ if &addNet(0, $rootID, $ip, $cidr, $description, $status, $tmplID, $defSubnetSize, 1, 0, 0, $tags); } else { $cnter[0 + (($v == 6) ? 1 : 0)]++ if &addNet(0, $rootID, $netaddress, $cidr, '', 0, $tmplID, 0, 1); $cnter[0 + (($v == 6) ? 1 : 0)]++ if &addNet(0, $rootID, $ip, $stdCidr, $description, $status, $tmplID, $defSubnetSize, 1, 0, 0, $tags); } } foreach (keys %{$rootIDs}) { &debug("Flushing Cache for: " . &rootID2Name($_)); &removeFromNetcache($_); } $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'FINISH'; &setStatus(); &warnl(sprintf(_gettext("Added %i IPv4 and %i IPv6 Networks"), $cnter[0], $cnter[1])); $stat->{STATUS} = 'FINISH'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 1; } sub parseCSVConfigfile { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $configFileID = shift; my $bPreview = shift || 0; my $q = $HaCi::HaCi::q; my $sep = &getParam(1, undef, 'sep'); my @box = []; my $nrOfCols = 0; my $stat = $conf->{var}->{STATUS}; $stat->{TITLE} = "Parsing CSV file."; $stat->{STATUS} = 'Runnung...'; $stat->{PERCENT} = 5; &setStatus(); unless (defined $sep) { $sep = ','; $q->param('sep', ','); } eval { require Text::CSV; }; if ($@) { warn $@; return (); } my $csv = Text::CSV->new({ 'sep_char' => $sep, binary => 1 }) or die "Cannot use CSV: ".Text::CSV->error_diag (); unless (open EXPORT, $conf->{static}->{path}->{spoolpath} . '/' . "$configFileID.tmp") { &warnl("Cannot open Temp File '$conf->{static}->{path}->{spoolpath}/$configFileID.tmp' for reading: $!"); return (); } my $config = join('', ); close EXPORT; my $cnter = 0; my $error = ''; my $line = 1; my @conf = split(/[\n\r]/, $config); my $maxLines = ($bPreview) ? 30 : $#conf + 1; foreach (@conf) { my $status = $csv->parse($_); if ($status) { $cnter++; last if $bPreview && $cnter > $maxLines; my @fields = $csv->fields(); map {s/^\s+//; s/\s+$//} @fields; $nrOfCols = ($#fields + 1) if $nrOfCols < ($#fields + 1); push @box, \@fields; } else { $error = 1; &warnl("CVS Error in line $line: " . $csv->error_diag()); } my $ml = ($maxLines / 4); $ml = 1 if $ml < 1; unless ($line % $ml) { $stat->{TITLE} = "Parsing CSV file." . '.' x (int($line * 4 / $maxLines)); $stat->{STATUS} = 'Runnung...'; $stat->{PERCENT} = (5 + (int($line * 4 / $maxLines))); &setStatus(); } $line++; } &warnl(_gettext("Error while parsing CSV: Invalid Line")) if $error; $conf->{var}->{nrOfCols} = $nrOfCols; return @box; } sub parseCiscoConfigfile { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $config = shift; my $fileName = shift; my $status = shift; my $box = []; my $hostname = ''; my $bInt = 0; my $intName = ''; my $intDescr = ''; foreach (split/\n/, $config) { chomp; next if /^\s*$/; $hostname = $1 if /^hostname\s+(\w+)/; if ($bInt && /^!/) { $bInt = 0; $intName = ''; $intDescr = ''; } if (!$bInt && /^interface\s+(.*)/) { $bInt = 1; $intName = $1; } $intDescr = $1 if $bInt && /^\s+description\s+(.*)/; if ($bInt && /^\s+ip address\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { my $cidr = &getCidrFromNetmask($2); push @$box, { ip => $1, cidr => $cidr, descr => ($intDescr ne '') ? $intDescr : $intName }; } } my $rootName = ($hostname ne '') ? $hostname : $fileName; if ($#$box > -1) { unless (&addRoot($rootName)) { warn "AddRoot failed!\n"; return 0; } my $rootID = &rootName2ID($rootName); foreach my $entry (@$box) { &addNet(0, $rootID, $entry->{ip}, $entry->{cidr}, $entry->{descr}, $status, 0, 0, 1) if $entry->{ip} =~ /^[\d\.]+$/; } } return 1; } sub importDNSLocal { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $file = &getParam(1, undef, 'zonefile'); my $status = &getParam(1, 0, 'state'); my $origin = &getParam(1, '', 'origin'); my $targetRootID = &getParam(1, -1, 'targetRoot'); my $zoneFileT = ''; my $data = ''; my $stat = $conf->{var}->{STATUS}; $stat->{TITLE} = "Importing local DNS Zonefile '$file'";$stat->{STATUS} = 'Running...'; $stat->{PERCENT} = 0; &setStatus(); return 0 unless $file; return 0 unless binmode $file; while(read $file,$data,1024) { $zoneFileT .= $data; } my $zoneFile = [split/\n/, $zoneFileT]; my $ret = &parseZonefile($zoneFile, $file, $status, $origin, $targetRootID); $stat->{DATA} = ""; $stat->{PERCENT} = 100; $stat->{STATUS} = 'FINISH'; &setStatus(); return $ret; } sub parseZonefile { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $zoneFile = shift; my $domain = shift; my $status = shift; my $origin = shift; my $targetRootID = shift; my $stat = $conf->{var}->{STATUS}; my $box = {}; my $cnter = 0; my $originOrig = $origin; $stat->{TITLE} = "Parsing Zonefile";$stat->{STATUS} = 'Running...'; $stat->{PERCENT} = 10; &setStatus(); eval { require DNS::ZoneParse; }; if ($@) { warn $@; return 0; } else { my $zft = join("\n", @$zoneFile); my $zf = DNS::ZoneParse->new(\$zft); my $soa = $zf->soa(); $origin = $soa->{origin} unless $origin; $originOrig = $origin; $originOrig =~ s/\.$//; if ($origin =~ /in-addr\.arpa\.?/) { my $ip = $origin; $ip =~ s/\.in-addr\.arpa\.?//g; $origin = join('.', reverse(split/\./, $ip)); $origin .= '.'; } my $as = $zf->a(); foreach my $r (@$as) { unless ($r->{name} =~ /\.$/) { $r->{name} .= '.' . $origin if length($origin) > 1; } $r->{name} =~ s/\.$//; &debug("NEW A ENTRY: name => $r->{name}, host => $r->{host}\n"); if (&checkSpelling_IP($r->{host}, 0)) { $cnter++; $box->{V4}->{$r->{host}} = $r->{name}; } } my $a4s = $zf->aaaa(); foreach my $r (@$a4s) { unless ($r->{name} =~ /\.$/) { $r->{name} .= '.' . $origin if length($origin) > 1; } $r->{name} =~ s/\.$//; &debug("NEW AAAA ENTRY: name => $r->{name}, host => $r->{host}\n"); if (&checkSpelling_IP($r->{host}, 1)) { $cnter++; $box->{V6}->{$r->{host}} = $r->{name}; } } my $ptrs = $zf->ptr(); foreach my $r (@$ptrs) { unless ($r->{name} =~ /\.$/) { $r->{name} = $origin . $r->{name} if length($origin) > 1; } else { if ($r->{name} =~ /in-addr\.arpa\.?/) { my $ip = $r->{name}; $ip =~ s/\.in-addr\.arpa\.?//g; $r->{name} = join('.', reverse(split/\./, $ip)); } } $r->{host} =~ s/\.$//; &debug("NEW PTR ENTRY: name => $r->{name}, host => $r->{host}\n"); if (&checkSpelling_IP($r->{name}, ($r->{name} =~ /:/) ? 1 : 0)) { $cnter++; if ($r->{name} =~ /:/) { $box->{V6}->{$r->{name}} = $r->{host}; } else { $box->{V4}->{$r->{name}} = $r->{host}; } } } } unless (length($origin) > 1) { &warnl(_gettext("No Origin given or found!")); return 0; } my $targetRoot = (($targetRootID == -1) ? $originOrig : &rootID2Name($targetRootID)); my $targetRootV6 = $targetRoot . '_IPv6'; my @v4s = keys %{$box->{V4}}; my @v6s = keys %{$box->{V6}}; my $saveCnter = 0; if ($#v4s > -1) { my $rootOK = 1; unless (&checkIfRootExists($targetRoot)) { unless (&addRoot($targetRoot, $originOrig)) { warn "AddRoot failed!\n"; $rootOK = 0; } } if ($rootOK) { my $rootID = &rootName2ID($targetRoot); my $colCnter = 0; foreach (@v4s) { my $host = $_; my $name = $box->{V4}->{$host}; if ($host =~ /^[\d\.]+$/) { my $percent = int(($colCnter / ($#v4s + 1)) * 100); $stat->{DATA} = "Importing Network '$host' ($name)"; $stat->{PERCENT} = $percent; &setStatus(); $saveCnter++ if &addNet(0, $rootID, $host, '32', $name, $status, 0, 0, 1) } $colCnter++; } } } if ($#v6s > -1) { my $rootOK = 1; unless (&checkIfRootExists($targetRootV6)) { unless (&addRoot($targetRootV6, $originOrig, 1)) { warn "AddRoot failed!\n"; $rootOK = 0; } } if ($rootOK) { my $rootID = &rootName2ID($targetRootV6); my $colCnter = 0; foreach (@v6s) { my $host = $_; my $name = $box->{V6}->{$host}; if ($host =~ /^[\w:]+$/) { my $percent = int(($colCnter / ($#v6s + 1)) * 100); $stat->{DATA} = "Importing Network '$host' ($name)"; $stat->{PERCENT} = $percent; &setStatus(); $saveCnter++ if &addNet(0, $rootID, $host, '128', $name, $status, 0, 0, 1); } $colCnter++; } } } if ($cnter > 0) { &warnl(sprintf(_gettext("%i IP Addresses found for Origin '%s'. %i were successfully saved under Root '%s'"), $cnter, $originOrig, $saveCnter, $targetRoot . (($#v6s > -1) ? '/' . $targetRootV6 : ''))); } else { &warnl(sprintf(_gettext("No IP Addresses found for Origin '%s'."), $originOrig)); } return 1; } sub getNextDBNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $inNetwork = shift || 0; my $nextNet = &getNetCacheEntry('DB', $rootID, "$networkDec:$inNetwork"); warn "getNextDBNetwork: " . &rootID2Name($rootID) . "-($ipv6)-" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . " (" . (caller)[0] . ':' . (caller)[2] . ")\n" if 0; unless (defined $nextNet) { my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get Next Network. DB Error (network)\n"; return undef; } my $networkT = undef; if ($ipv6) { my ($net, $host, $cidr) = (0, 0, 0); if ($networkDec) { if (ref $networkDec) { ($net, $host, $cidr) = &netv6Dec2PartsDec($networkDec); } else { &debug("V6 NetworkDec ($networkDec) should be an Math::BigInt Reference!"); } } my $broadcastStr = ''; my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get Next Network. DB Error (networkV6)\n"; return undef; } my $npCol = $networkV6Table->meth2Col('networkPrefix'); my $hpCol = $networkV6Table->meth2Col('hostPart'); my $cidrCol = $networkV6Table->meth2Col('cidr'); if ($inNetwork) { my $broadcastNetDec = &getV6BroadcastNet($networkDec, 128); my ($bNet, $bHost, undef) = &netv6Dec2PartsDec($broadcastNetDec); $broadcastStr = "AND ($npCol < $bNet OR ($npCol = $bNet AND $hpCol < $bHost) OR ($npCol = $bNet AND $hpCol = $bHost AND $cidrCol <= 128))"; } $networkT = ($networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( ($npCol > $net) OR ($npCol = $net AND $hpCol > $host) OR ($npCol = $net AND $hpCol = $host AND $cidrCol > $cidr) ) $broadcastStr ORDER BY $npCol, $hpCol, $cidrCol LIMIT 1") )[0]; if (defined $networkT) { $networkT->{ipv6} = 1; $networkT->{network} = &ipv6Parts2NetDec($networkT->{networkPrefix}, $networkT->{hostPart}, $networkT->{cidr}); my $networkT1 = ($networkTable->search(['ID', 'network', 'description', 'state', 'defSubnetSize'], {ipv6ID => $networkT->{ID}, rootID => $rootID, network => 0}, 0))[0]; if (defined $networkT1) { $networkT->{ID} = $networkT1->{ID}; $networkT->{description} = $networkT1->{description}; $networkT->{state} = $networkT1->{state}; $networkT->{defSubnetSize} = $networkT1->{defSubnetSize}; } else { $networkT = undef; } } } else { my $broadcastStr = ''; my $nwCol = $networkTable->meth2Col('network'); if ($inNetwork) { my $broadcast = &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); # <= damit /32er auch in einem /31er angezeigt werden $broadcastStr = "AND $nwCol <= $broadcast"; } $networkT = ($networkTable->search( ['ID', 'network', 'description', 'state', 'defSubnetSize'], {rootID => $rootID, ipv6ID => ''}, 0, "AND $nwCol > $networkDec $broadcastStr ORDER BY $nwCol LIMIT 1") )[0]; $networkT->{ipv6} = 0 if defined $networkT; } $nextNet = (defined $networkT) ? $networkT : undef; &updateNetcache('DB', $rootID, "$networkDec:$inNetwork", ((defined $networkT) ? $networkT : -1)); } $nextNet = undef if defined $nextNet && $nextNet == -1; warn " Next: " . ((defined $nextNet) ? (($ipv6) ? &netv6Dec2net($nextNet->{network}) : &dec2net($nextNet->{network}) . "\n") : '') if 0; return $nextNet; } sub getDBNetworkBefore { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $ipv6 = shift; my $inNetwork = shift || 0; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get Next Network. DB Error (network)\n"; return undef; } my $networkT; if ($ipv6) { warn "No IPV6 Reference ($networkDec) [getDBNetworkBefore]\n" unless ref $networkDec; my ($net, $host, $cidr) = &netv6Dec2PartsDec($networkDec); my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get Next Network. DB Error (networkV6)\n"; return undef; } my $npCol = $networkV6Table->meth2Col('networkPrefix'); my $hpCol = $networkV6Table->meth2Col('hostPart'); my $cidrCol = $networkV6Table->meth2Col('cidr'); $networkT = ($networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( ($npCol < $net) OR ($npCol = $net AND $hpCol < $host) OR ($npCol = $net AND $hpCol = $host AND $cidrCol < $cidr) ) ORDER BY $npCol DESC, $hpCol DESC, $cidrCol DESC LIMIT 1") )[0]; if (defined $networkT) { my $currNetworkDec = &ipv6Parts2NetDec($networkT->{networkPrefix}, $networkT->{hostPart}, $networkT->{cidr}); $networkT->{ipv6} = 1; $networkT->{network} = $currNetworkDec; if ($inNetwork) { my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); my $currParent = &getNetworkParentFromDB($rootID, $networkT->{network}, $ipv6); return undef if $parent->{network} != $currParent->{network}; } if (0) { # needless... my $networkT1 = ($networkTable->search(['ID', 'network', 'description', 'state', 'defSubnetSize'], {ipv6ID => $networkT->{ID}, network => 0, rootID => $rootID}, 0))[0]; if (defined $networkT1) { $networkT->{ID} = $networkT1->{ID}; $networkT->{description} = $networkT1->{description}; $networkT->{state} = $networkT1->{state}; $networkT->{defSubnetSize} = $networkT1->{defSubnetSize}; } } } } else { my $nwCol = $networkTable->meth2Col('network'); $networkT = ($networkTable->search( ['ID', 'network'], {rootID => $rootID, ipv6ID => ''}, 0, "AND $nwCol < $networkDec ORDER BY $nwCol DESC LIMIT 1") )[0]; if (defined $networkT && $inNetwork) { my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); my $currParent = &getNetworkParentFromDB($rootID, $networkT->{network}, $ipv6); return undef if $parent->{network} != $currParent->{network}; } } return (defined $networkT) ? $networkT->{network} : undef; } sub updateNetcache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $ID = shift; my $key = shift; my $value = shift; $conf->{var}->{CACHESTATS}->{$type}->{FAIL}++; $HaCi::HaCi::netCache->{$type}->{$ID}->{$key} = $value; } sub removeFromNetcache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; delete $HaCi::HaCi::netCache->{DB}->{$rootID}; } sub getConfig { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $file = $conf->{static}->{path}->{configfile}; my $configFile = $conf->{static}->{path}->{workdir} . '/etc/' . $file if -f $conf->{static}->{path}->{workdir} . '/etc/' . $file; $configFile = '/etc/' . $file if -f '/etc/' . $file; if (-d $conf->{static}->{path}->{workdir} . '/CVS') { $configFile .= '.dev'; warn "Seems to be a development Branch. Extending '.dev' to the ConfigFile: '$configFile'\n"; $conf->{static}->{misc}->{debug} = 1; } unless ($configFile) { &warnl("No config file found! (Looking at " . $conf->{static}->{path}->{workdir} . '/etc/' . $file . ", " . '/etc/' . $file . ")\n"); return; } my %config; eval { %config = ParseConfig( -ConfigFile => $configFile, -LowerCaseNames => 1, -UseApacheInclude => 1, -IncludeRelative => 1, -IncludeDirectories => 1, -IncludeGlob => 1, -AutoTrue => 1, -InterPolateVars => 1, -InterPolateEnv => 1, ); }; if ($@) { &warnl("Error while parsing Configfile '$configFile': $@"); } if (exists $config{'db::dbhost'}) { warn "You are using a deprecated Configfile format. Please consider to upgrade it (Example: 'etc/HaCi.conf.sample')\n"; &getConfigOld($configFile); } else { $conf->{user} = \%config; } $conf->{var}->{STATUS} = {}; } sub getConfigOld { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $configFile = shift; unless (open CONF, $configFile) { warn sprintf(_gettext("Cannot read ConfigFile '%s': %s"), $configFile, $!); return 0; } my @CONF = ; close CONF; foreach (@CONF) { s/#.*//; next if /^\s+$/; if (/^([\w:]+)\s+=\s+(.*)$/) { my $key = $1; my $value = $2; next unless $key =~ /^[\w|:]+$/; $value =~ s/'//g; $key =~ s/::/}->{/g; $key = lc($key); $key = '$conf->{user}->{' . $key . '} = ' . "'$value'"; eval "$key"; if ($@) { warn "Error while evaluating Configkey: $@\n"; } } } } sub getWHOISData { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $network = shift; return {data=>[ {key=>_gettext('Error'),value=>sprintf(_gettext("This doesn't look like a network '%s'."), $network)} ]} unless &checkSpelling_Net($network, 0); return {data=>[ {key=>_gettext('Error'),value=>sprintf(_gettext("Program Whois '%s' isn't executable"), $conf->{static}->{path}->{whois})} ]} unless -x $conf->{static}->{path}->{whois}; my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -- $network); my $route = 0; my $box = {}; foreach (@whois) { push @{$box->{data}}, {key =>$1, value => $2} if $box->{inetnum} && /^([\w\-]+):\s+(.*)$/; $box->{inetnum} = $1 if /^inetnum:\s+(.*)$/; last if $box->{inetnum} && /^\s*$/; } return $box; } sub getNSData { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ip = shift; return {data=>_gettext('Error') . ': ' . sprintf(_gettext("This doesn't look like an IP address '%s'."), $ip)} unless &checkSpelling_IP($ip, 0); eval { require Net::Nslookup; }; if ($@) { warn $@; return { ipaddress => $ip, data => 'unknown' }; } else { my $ptr = Net::Nslookup->nslookup(host => $ip, type => "PTR"); $ptr ||= ''; return { ipaddress => $ip, data => $ptr }; } } sub rootID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot convert RootID to name. DB Error (root)\n"; return ''; } my $root = ($rootTable->search(['ID', 'name'], {ID => $rootID}))[0]; return $root->{name} || ''; } sub rootName2ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootName = shift; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot convert RootName to ID. DB Error (root)\n"; return ''; } my $root = ($rootTable->search(['ID'], {name => $rootName}))[0]; return $root->{ID} || 0; } sub rootID2ipv6 { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot get ipv6 Flag for RootID. DB Error (root)\n"; return 0; } my $root = ($rootTable->search(['ID', 'ipv6'], {ID => $rootID}))[0]; return $root->{ipv6} || 0; } sub netID2Stuff { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if 0 || $conf->{var}->{showsubs}; my $netID = shift; my $netTable = $conf->{var}->{TABLES}->{network}; unless (defined $netTable) { warn "Cannot get Network. DB Error (network)\n"; return (); } my $network = ($netTable->search(['rootID', 'network', 'ipv6ID'], {ID => $netID}))[0]; return () unless defined $network; if ($network->{ipv6ID}) { my $netTableV6 = $conf->{var}->{TABLES}->{networkV6}; unless (defined $netTableV6) { warn "Cannot get Network. DB Error (networkV6)\n"; return (); } my $networkV6 = ($netTableV6->search(['networkPrefix', 'hostPart', 'cidr'], {ID => $network->{ipv6ID}}))[0]; $network->{network} = &ipv6Parts2NetDec($networkV6->{networkPrefix}, $networkV6->{hostPart}, $networkV6->{cidr}); } $network->{network} = Math::BigInt->new($network->{network}) if $network->{ipv6ID}; return ($network->{rootID}, $network->{network}, ($network->{ipv6ID}) ? 1 : 0); } sub tmplID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; return 'other' unless $tmplID; my $tmplTable = $conf->{var}->{TABLES}->{template}; unless (defined $tmplTable) { warn "Cannot convert TmplID to name. DB Error (template)\n"; return ''; } my $tmpl = ($tmplTable->search(['ID', 'name'], {ID => $tmplID}))[0]; return $tmpl->{name} || ''; } sub tmplName2ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplName = shift; return undef unless $tmplName; my $tmplTable = $conf->{var}->{TABLES}->{template}; unless (defined $tmplTable) { warn "Cannot convert TmplName to ID. DB Error (template)\n"; return undef; } my $tmpl = ($tmplTable->search(['ID'], {name => $tmplName}))[0]; return (defined $tmpl && exists $tmpl->{ID}) ? $tmpl->{ID} : undef; } sub groupID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $groupID = shift; my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot convert GroupID to name. DB Error (group)\n"; return ''; } my $group = ($groupTable->search(['ID', 'name'], {ID => $groupID}))[0]; return $group->{name} || ''; } sub userID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userID = shift; my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot convert UserID to name. DB Error (user)\n"; return ''; } my $user = ($userTable->search(['ID', 'username'], {ID => $userID}))[0]; return $user->{username} || ''; } sub userName2ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userName = shift; my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot convert Username to ID. DB Error (user)\n"; return -1; } my $user = ($userTable->search(['ID'], {username => $userName}))[0]; return $user->{ID} || -1; } sub getNetworkChilds { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $onlyRoot = shift; my $withParent = shift; my $ignoreRoot = shift || 0; $onlyRoot ||= 0; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get Number of Parents. DB Error (network)\n"; return (); } my $networkV6Table = undef; $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get NetworkV6 Parent. DB Error (networkV6)\n"; return (); } my $currLevel = 0; my @childs = (); if ($onlyRoot) { if (&rootID2ipv6($netID)) { @childs = $networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $netID}); if (@childs) { my @newChilds = (); foreach (@childs) { my $child = $_; my $net = ($networkTable->search(['ID', 'network', 'state'], {ipv6ID => $child->{ID}, network => 0, rootID => $netID}))[0]; $net->{network} = &ipv6Parts2NetDec($child->{networkPrefix}, $child->{hostPart}, $child->{cidr}); push @newChilds, $net; } @childs = @newChilds; } } else { @childs = $networkTable->search(['ID', 'network', 'state'], {rootID => $netID}); } } else { my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); return () if !defined $rootID || !defined $networkDec; my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); if ($ipv6) { my ($net, $host, $cidr) = &netv6Dec2PartsDec($networkDec); my ($netB, $hostB, $cidrB) = &netv6Dec2PartsDec($broadcast); my $npCol = $networkV6Table->meth2Col('networkPrefix'); my $hpCol = $networkV6Table->meth2Col('hostPart'); my $cidrCol = $networkV6Table->meth2Col('cidr'); my $rootFilter = ($ignoreRoot) ? {1 => 1} : {rootID => $rootID}; @childs = $networkV6Table->search( ['ID', 'rootID', 'networkPrefix', 'hostPart', 'cidr'], $rootFilter, 0, "AND ( ($npCol > $net) OR ($npCol = $net AND $hpCol > $host) OR ($npCol = $net AND $hpCol = $host AND $cidrCol >" . (($withParent) ? '=' : '') . " $cidr) ) AND ( ($npCol < $netB) OR ($npCol = $netB AND $hpCol < $hostB) OR ($npCol = $netB AND $hpCol = $hostB AND $cidrCol <= $cidrB) ) ORDER BY $npCol, $hpCol, $cidrCol"); if (@childs) { my @newChilds = (); foreach (@childs) { my $child = $_; my $rootID = $child->{rootID}; my $searchFilter = {ipv6ID => $child->{ID}, network => 0, rootID => $rootID}; my $net = ($networkTable->search(['ID', 'network', 'state'], $searchFilter))[0]; $net->{network} = &ipv6Parts2NetDec($child->{networkPrefix}, $child->{hostPart}, $child->{cidr}); push @newChilds, $net; } @childs = @newChilds; } } else { my $nwCol = $networkTable->meth2Col('network'); my $searchFilter = {ipv6ID => ''}; $searchFilter->{rootID} = $rootID unless $ignoreRoot; @childs = $networkTable->search( ['ID', 'network', 'state'], $searchFilter, 0, "AND $nwCol >" . (($withParent) ? '=' : '') . " $networkDec AND $nwCol <= $broadcast" ); } } return (@childs) ? @childs : (); } sub getNrOfChilds { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $networkDec = shift; my $rootID = shift; my $ipv6 = shift; $ipv6 = &rootID2ipv6($rootID) unless defined $ipv6; $networkDec = Math::BigInt->new(0) if $ipv6 && !$networkDec; my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); my @nrs = (); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get Number of Parents. DB Error (network)\n"; return 0; } unless ($networkDec) { @nrs = $networkTable->search(['ID'], {rootID => $rootID}); return ($#nrs + 1) || 0; } if ($ipv6) { my ($net, $host, $cidr) = &netv6Dec2PartsDec($networkDec); my ($netB, $hostB, $cidrB) = &netv6Dec2PartsDec($broadcast); my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get NetworkV6 Parent. DB Error (networkV6)\n"; return undef; } my $npCol = $networkV6Table->meth2Col('networkPrefix'); my $hpCol = $networkV6Table->meth2Col('hostPart'); my $cidrCol = $networkV6Table->meth2Col('cidr'); @nrs = $networkV6Table->search( ['ID'], {rootID => $rootID}, 0, "AND ( ($npCol > $net) OR ($npCol = $net AND $hpCol > $host) OR ($npCol = $net AND $hpCol = $host AND $cidrCol > $cidr) ) AND ( ($npCol < $netB) OR ($npCol = $netB AND $hpCol < $hostB) OR ($npCol = $netB AND $hpCol = $hostB AND $cidrCol <= $cidrB) ) ORDER BY $npCol, $hpCol, $cidrCol"); } else { my $nwCol = $networkTable->meth2Col('network'); @nrs = $networkTable->search( ['ID'], {rootID => $rootID, ipv6ID => ''}, 0, "AND $nwCol > $networkDec AND $nwCol <= $broadcast" ); } return ($#nrs + 1) || 0; } sub getMaintInfosFromNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $networkTable = $conf->{var}->{TABLES}->{network}; my $networkTagTable = $conf->{var}->{TABLES}->{networkTag}; return {} unless defined $netID; unless (defined $networkTable) { warn "Cannot get Maintenance Infos for Network. DB Error (network)\n"; return {}; } my $net = ($networkTable->search(['*'], {ID => $netID}))[0]; if (defined $net) { if ($net->{ipv6ID}) { my $netTableV6 = $conf->{var}->{TABLES}->{networkV6}; unless (defined $netTableV6) { warn "Cannot get Network. DB Error (networkV6)\n"; return (); } my $netV6 = ($netTableV6->search(['networkPrefix', 'hostPart', 'cidr'], {ID => $net->{ipv6ID}}))[0]; if (defined $netV6) { $net->{network} = &ipv6Parts2NetDec($netV6->{networkPrefix}, $netV6->{hostPart}, $netV6->{cidr}); $net->{ipv6} = 1; } } else { $net->{ipv6} = 0; } } unless (defined $networkTagTable) { warn "Cannot get Tag infos for network. DB Error (networkTag)\n"; return (defined $net) ? $net : {}; } my @tagsDB = $networkTagTable->search(['*'], {netID => $netID}); my $tags = []; foreach (@tagsDB) { push @{$tags}, $_->{tag}; } $net->{tags} = $tags; return (defined $net) ? $net : {}; } sub getV6Net { my $ipv6ID = shift; my $netTableV6 = $conf->{var}->{TABLES}->{networkV6}; unless (defined $netTableV6) { warn "Cannot get Network. DB Error (networkV6)\n"; return (); } my $netV6 = ($netTableV6->search(['*'], {ID => $ipv6ID}))[0]; return $netV6; } sub getMaintInfosFromRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $box = {}; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot get Maintenance Infos for Root. DB Error (root)\n"; return {}; } my $root = ($rootTable->search(['*'], {ID => $rootID}))[0]; return $root || {}; } sub delNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $bWithSubnets = shift || 0; my $bLocal = shift || 0; my $networkLock = shift || 0; my $errors = ''; my $ipv6 = 0; my $s = $HaCi::HaCi::session; my $expands = $s->param('expands'); return '' unless defined $netID; my ($rootID, $networkDec) = (); if ($bWithSubnets == -1) { $rootID = $netID; $ipv6 = &rootID2ipv6($rootID); $bWithSubnets = 0; $networkDec = -1; } else { ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); } my $rootName = &rootID2Name($rootID); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { my $err = 'Cannot delete Network. DB Error (network)\n'; &warnL($err) unless $bLocal; return ($bLocal) ? $err : 0; } my $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { my $err = "Cannot delete Network. DB Error (networkAC)\n"; &warnl($err) unless $bLocal; return ($bLocal) ? $err : 0; } my $networkTagTable = $conf->{var}->{TABLES}->{networkTag}; unless (defined $networkTagTable) { my $err = "Cannot delete network tags. DB Error (networkTag)\n"; &warnl($err) unless $bLocal; return ($bLocal) ? $err : 0; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { my $err = "Cannot delete Network. DB Error (templateValue)"; &warnl($err) unless $bLocal; return ($bLocal) ? $err : 0; } my $rows = 0; my @netIDs = (); if ($bWithSubnets) { my @tmp = &getNetworkChilds($netID, 0, 1); map {push @netIDs, [$_->{ID}, $_->{network}]} @tmp; } else { if ($networkDec == -1) { my @tmp = &getNetworkChilds($rootID, 1, 0); map {push @netIDs, [$_->{ID}, $_->{network}]} @tmp; } else { push @netIDs, [$netID, $networkDec]; } } foreach (reverse @netIDs) { my $netID = $$_[0]; my $networkDec = $$_[1]; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); my $parentDec = (defined $parent) ? $parent->{network} : 0; &debug("Delete Network $network from $rootName"); unless (&checkNetACL($netID, 'w')) { $errors .= "\n" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ": Not enouph permissions (write) to delete this Network"; next; } if ($ipv6) { my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { my $err = 'Cannot delete Network. DB Error (networkV6)\n'; &warnL($err) unless $bLocal; return ($bLocal) ? $err : 0; } my $net = ($networkTable->search(['ipv6ID', 'rootID'], {ID => $netID}))[0]; unless (defined $net) { $errors .= '\n' . sprintf(_gettext("Error while deleting '%s' from '%s': %s"), $network, $rootName, "Network not found!"); next; } $networkV6Table->clear(); $networkV6Table->delete({ID => $net->{ipv6ID}, rootID => $net->{rootID}}); if ($networkV6Table->error) { $errors .= '\n' . sprintf(_gettext("Error while deleting V6 '%s' from '%s': %s"), $network, $rootName, $networkV6Table->errorStrs); next; } } $networkTable->clear(); $networkTable->delete({ID => $netID}); if ($networkTable->error) { $errors .= '\n' . sprintf(_gettext("Error while deleting '%s' from '%s': %s"), $network, $rootName, $networkTable->errorStrs); next; } $rows++; $networkACTable->clear(); $networkACTable->delete({netID => $netID}); if ($networkACTable->error) { warn sprintf("Error while deleting ACLs for '%s' from '%s': %s", $network, $rootName, $networkACTable->errorStrs); } $networkTagTable->clear(); $networkTagTable->delete({netID => $netID}); if ($networkTagTable->error) { warn sprintf("Error while deleting tags for '%s' from '%s': %s", $network, $rootName, $networkTagTable->errorStrs); } $tmplValueTable->clear(); $tmplValueTable->delete({netID => $netID}); if ($tmplValueTable->error) { warn sprintf("Error while deleting Templates for '%s' from '%s': %s", $network, $rootName, $tmplValueTable->errorStrs); } &audit('network.delete', $network, "root => $rootName"); &addNetworkLock($rootID, $networkDec, $ipv6, $networkLock, $bLocal) if $networkLock; if (defined $parentDec && $expands->{network}->{$rootID}->{$parentDec}) { my $nrOfChilds = &getNrOfChilds($parentDec, $rootID, $ipv6); &expand('-', 'network', $parentDec, $rootID) unless $nrOfChilds; } } &removeFromNetcache($rootID); if ($errors) { &warnl($errors) unless $bLocal; return ($bLocal) ? $errors : 0; } else { $rows =~ s/0E0/0/; unless ($networkDec == -1) { my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); if ($networkLock) { &warnl(sprintf(_gettext("Successfully deleted '%s' from '%s' (%i Networks deleted) and locked for %i seconds."), $network, $rootName, $rows, $networkLock)); } else { &warnl(sprintf(_gettext("Successfully deleted '%s' from '%s' (%i Networks deleted)"), $network, $rootName, $rows)); } } return ($bLocal) ? $rows : 1; } } sub addNetworkLock { my $rootID = shift; my $networkDec = shift; my $ipv6 = shift; my $networkLock = shift; my $bLocal = shift; my $networkPrefix = 0; my $hostPart = 0; my $cidr = 0; my $networkLockTable = $conf->{var}->{TABLES}->{networkLock}; unless (defined $networkLockTable) { my $err = "Cannot lock network. DB Error (networkLock)"; &warnl($err) unless $bLocal; return ($bLocal) ? $err : 0; } if ($ipv6) { ($networkPrefix, $hostPart, $cidr) = &HaCi::Mathematics::netv6Dec2PartsDec($networkDec); } else { ($networkPrefix, $cidr) = ($networkDec, &HaCi::Mathematics::getCidrFromDec($networkDec)); } my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); $networkLockTable->clear(); $networkLockTable->ts(&currDate('datetime')); $networkLockTable->duration($networkLock); $networkLockTable->rootID($rootID); $networkLockTable->networkPrefix($networkPrefix); $networkLockTable->hostPart($hostPart); $networkLockTable->cidr($cidr); $networkLockTable->ipv6($ipv6); unless ($networkLockTable->insert()) { my $errStr = "Cannot lock network '$network' for $networkLock seconds: " . $networkLockTable->errorStrs(); &warnl($errStr); &audit('network.lock', $network, $networkLock, $errStr); } else { &debug("Successfully locked network '$network' for $networkLock seconds"); &audit('network.lock', $network, $networkLock); } } sub genRandBranch { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootName = ''; my $rootDescr = ''; for (0 .. 5) { $rootName .= chr(97 + int(rand(25))); $rootDescr .= chr(97 + int(rand(25))); } &addRoot($rootName, $rootDescr); my $rootID = &rootName2ID($rootName); for (0 .. 500) { my $ipaddress = '192.168'; for (0 .. 2) { $ipaddress .= '.'; $ipaddress .= 1 + int(rand(255)); } my $cidr = 32 - 16 + int(rand(15)); my $descr = ''; for (0 .. 5) { $descr .= chr(97 + int(rand(25))); } my $netmask = &getNetmaskFromCidr($cidr); my $netaddress = &dec2ip(&getNetaddress($ipaddress, $netmask)); &addNet(0, $rootID, $netaddress, $cidr, $descr, 0, 0, 0, 1) if $netaddress; } } sub delRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $rootName = &rootID2Name($rootID); my $rootTable = $conf->{var}->{TABLES}->{root}; my $rootACTable = $conf->{var}->{TABLES}->{rootAC}; unless (defined $rootTable) { warn "Cannot delete Root. DB Error (root)\n"; return 0; } unless (defined $rootACTable) { warn "Cannot delete Root. DB Error (rootAC)\n"; return 0; } my $rows = &delNet($rootID, -1, 1, 0); if ($rows !~ /^\d+$/) { &warnl(sprintf(_gettext("Error while deleting '%s': %s"), $rootName, $rows)); return 0; } else { $rootTable->clear(); $rootTable->delete({ID => $rootID}); if ($rootTable->error) { &warnl(sprintf(_gettext("Error while deleting '%s': %s"), $rootName, $rootTable->errorStrs)); return 0; } else { &removeFromNetcache($rootID); $rootACTable->clear(); $rootACTable->delete({rootID => $rootID}); if ($rootACTable->error) { &warnl(sprintf(_gettext("Error while deleting '%s': %s"), $rootName, $rootACTable->errorStrs)); } else { &debug("$rows netAC Entries removed"); } &warnl(sprintf(_gettext("Successfully deleted '%s' (%i Networks deleted)"), $rootName, $rows)); &audit('root.delete', $rootName); } } return 1; } sub copyNetsTo { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $targetRootID = shift; my $networks = shift; my $bDel = shift || 0; my $bSingle = shift || 0; my $targetIPv6 = &rootID2ipv6($targetRootID); my $s = $HaCi::HaCi::session; my $expands = $s->param('expands') || {}; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot copy Networks. DB Error (network)\n"; return 0; } my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot copy Networks. DB Error (networkV6)\n"; return 0; } my $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { warn "Cannot copy Networks. DB Error (networkAC)\n"; return 0; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { &warnl("Cannot delete Template. DB Error (templateValue)"); return 0; } my $error = 'Error while copying:'; foreach (@$networks) { my ($network, $rootID) = split/_/; my $ipv6 = &rootID2ipv6($rootID); if (($ipv6 && !$targetIPv6) || (!$ipv6 && $targetIPv6)) { $error .= "\n" . "$network: Cannot copy " . (($ipv6) ? 'IPv6 ' : 'IPv4') . " Net into an " . (($ipv6) ? 'IPv4 ' : 'IPv6') . " Root!"; next; } my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my @networks = (); if ($bSingle || $expands->{network}->{$rootID}->{$networkDec}) { push @networks, $networkDec; } else { my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); my @childs = &getNetworkChilds($netID, 0, 1); if (@childs) { foreach (@childs) { push @networks, $_->{network}; } } } foreach (@networks) { my $networkDec = $_; my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); my $networkt = &getMaintInfosFromNet($netID); next unless defined $networkt; my $origNetID = $networkt->{ID}; unless (&checkNetACL($origNetID, 'r')) { $error .= "\n" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ": Not enouph permissions (source:read) to copy this Network"; next; } my $parent = &getNetworkParentFromDB($targetRootID, $networkDec, $targetIPv6); my $parentDec = (defined $parent) ? $parent->{network} : 0; my $parentID = (defined $parent) ? $parent->{ID} : 0; unless (($parentID && &checkNetACL($parentID, 'w')) || (!$parentID && &checkRootACL($rootID, 'w'))) { $error .= "\n" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ": Not enouph permissions (target:write) to copy this Network"; next; } if ($bDel) { $networkTable->clear(); $networkTable->rootID($targetRootID); $networkTable->modifyFrom($s->param('username')); $networkTable->modifyDate(&currDate('datetime')); $networkTable->update({ID => $networkt->{ID}}); if ($networkTable->error) { $error .= "\n$network: " . $networkTable->errorStrs(); next; }; if ($ipv6) { $networkV6Table->clear(); $networkV6Table->rootID($targetRootID); $networkV6Table->update({ID => $networkt->{ipv6ID}, rootID => $rootID}); if ($networkV6Table->error) { $error .= "\n$network: " . $networkV6Table->errorStrs(); $networkTable->clear(); $networkTable->rootID($rootID); $networkTable->modifyFrom($s->param('username')); $networkTable->modifyDate(&currDate('datetime')); $networkTable->update({ID => $networkt->{ID}}); next; }; } &removeFromNetcache($rootID); &removeFromNetcache($targetRootID); } else { $networkTable->clear(); foreach (keys %{$networkt}) { if ($_ eq 'rootID') { $networkTable->rootID($targetRootID); } elsif ($_ eq 'ipv6') { } elsif ($_ eq 'ID') { $networkTable->ID(undef); } elsif ($_ eq 'network' && $ipv6) { $networkTable->network(0); } else { if ($networkTable->can($_)) { $networkTable->$_($networkt->{$_}); } else { warn " copyNetsTo: networkTable hasn't this method: $_\n"; } } } $networkTable->createFrom($s->param('username')); $networkTable->createDate(&currDate('datetime')); $networkTable->insert(); if ($networkTable->error) { $error .= "\n$network: " . $networkTable->errorStrs; next; }; my $newNetID = &getNetID($targetRootID, $networkDec, $networkt->{ipv6ID}); if ($ipv6) { my $ipv6Error = 0; my $v6Net = ($networkV6Table->search(['ID', 'networkPrefix', 'hostPart', 'cidr'], {ID => $networkt->{ipv6ID}, rootID => $rootID}, 0))[0]; if (defined $v6Net) { $networkV6Table->clear(); $networkV6Table->ID($networkt->{ipv6ID}); $networkV6Table->rootID($targetRootID); $networkV6Table->networkPrefix($v6Net->{networkPrefix}); $networkV6Table->hostPart($v6Net->{hostPart}); $networkV6Table->cidr($v6Net->{cidr}); $networkV6Table->insert(); if ($networkV6Table->error) { $error .= "\n$network: " . $networkV6Table->errorStrs; $ipv6Error = 1; } } else { $ipv6Error = 1; } if ($ipv6Error) { &delNet($newNetID, 0, 1, 0); next; } } &removeFromNetcache($targetRootID); my $netID = &getNetID($targetRootID, $networkDec, $networkt->{ipv6ID}); unless (defined $netID) { $error .= "\n" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ": Cannot set Access/TmplValues. No netID found"; } my @networkACs = $networkACTable->search(['*'], {netID => $origNetID}); foreach (@networkACs) { my $networkAC = $_; $networkACTable->clear(); foreach (keys %{$networkAC}) { if ($_ eq 'rootID') { } elsif ($_ eq 'network') { } elsif ($_ eq 'netID') { $networkACTable->netID($netID); } elsif ($_ eq 'ID') { $networkACTable->ID(undef); } else { $networkACTable->$_($networkAC->{$_}); } } $networkACTable->insert(); if ($networkACTable->error) { $error .= "\n$network: " . $networkACTable->errorStrs(); } } my @tmplValues = $tmplValueTable->search(['*'], {netID => $origNetID}); foreach (@tmplValues) { my $tmplValue = $_; $tmplValueTable->clear(); foreach (keys %{$tmplValue}) { if ($_ eq 'netID') { $tmplValueTable->netID($netID); } elsif ($_ eq 'ID') { $tmplValueTable->ID(undef); } else { $tmplValueTable->$_($tmplValue->{$_}); } } $tmplValueTable->insert(); if ($tmplValueTable->error()) { $error .= "\n" . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ": Cannot insert TmplValue: " . $tmplValueTable->errorStrs(); } } } } } if ($error ne 'Error while copying:') { &warnl($error); return 0; } else { return 1; } } sub delNets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $networks = shift; my $networkLock = shift || 0; my $s = $HaCi::HaCi::session; my $expands = $s->param('expands') || {}; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot delete Networks. DB Error (network)\n"; return 0; } my $error = 'Error while deleting:'; foreach (@$networks) { my ($network, $rootID) = split/_/; my $ipv6 = &rootID2ipv6($rootID); my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); if ($expands->{network}->{$rootID}->{$networkDec}) { my $rows = &delNet($netID, 0, 1, $networkLock); $error .= $rows if $rows !~ /^\d+$/; } else { my $rows = &delNet($netID, 1, 1, $networkLock); $error .= $rows if $rows !~ /^\d+$/; } } if ($error ne 'Error while deleting:') { &warnl($error); return 0; } else { return 1; } } sub search { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $search = shift || ''; my $bLike = shift || 0; my $bFuzzy = shift || 0; my $bshNrOfFreeSubs = shift || 0; my $rootID = shift; my $state = shift; my $tmplID = shift; my $tmplBox = shift || {}; my $tags = shift || []; my $tagOp = shift || 'OR'; my $q = $HaCi::HaCi::q; if (ref($q)) { $search = &getParam(1, '', 'search'); $bLike = (&getParam(1, 0, 'exact')) ? 0 : 1; $bFuzzy = (&getParam(1, 0, 'fuzzy')) ? 1 : 0; $bshNrOfFreeSubs = (&getParam(1, 0, 'shNrOfFreeSubs')) ? 1 : 0; $rootID = &getParam(1, '', 'rootID'); $state = &getParam(1, undef, 'state'); $tmplID = &getParam(1, -1, 'tmplID'); $tagOp = &getParam(1, 'AND', 'tagOp'); $tags = &getParam(0, [], 'tags'); } my $searchLimit = $conf->{static}->{misc}->{searchlimit} || 1000; my $dbType = &getConfigValue('db', 'dbtype'); $bLike = (defined $bLike) ? $bLike : 1; $search = '*' if !$search && $tmplID != -1; $search = '*' if !$search && scalar @{$tags} > 0; $rootID = '' if $rootID == -1; return unless $search; if ($tmplID > 0) { if (ref($q)) { foreach ($q->param) { $tmplBox->{$1} = &getParam(1, undef, $_) if /^tmplEntryID_(\d+)$/ && &getParam(1, '', $_) ne ''; } } } my $tmplResultBox = {}; if ($tmplID ne -1) { my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { &warnl("Cannot get Template Values. DB Error (templateValue)"); return ''; } my $tmplValIDCol = $tmplValueTable->meth2Col('tmplEntryID'); my $tmplSearch = '0=1'; foreach (keys %{$tmplBox}) { my $search = $tmplBox->{$_}; $search =~ s/'/\\'/g; $search =~ s/"/\\"/g; $search =~ s/;/\\;/g; if (lc($dbType) eq 'mysql') { $search =~ s/%/\\%/g; } else { $search =~ s/%/\\\\%/g; } $search =~ s/([^\\]|^)\*/$1 . '%'/eg; if ($dbType eq 'postgresql') { $tmplSearch .= " OR ($tmplValIDCol=$_ AND value::text " . (($bLike) ? "ILIKE '%$search%'" : "='$search'") . ')'; } else { $tmplSearch .= " OR ($tmplValIDCol=$_ AND CONVERT(value USING latin1) " . (($bLike) ? "like '%$search%'" : "='$search'") . ')'; } } &debug("TmplSearch: " . $tmplSearch); my @results = $tmplValueTable->search(['netID', 'tmplEntryID'], $tmplSearch); foreach (@results) { $tmplResultBox->{$_->{netID}}->{$_->{tmplEntryID}} = 1; } } $search =~ s/'/\\'/g; $search =~ s/"/\\"/g; $search =~ s/;/\\;/g; if (lc($dbType) eq 'mysql') { $search =~ s/%/\\%/g; } else { $search =~ s/%/\\\\%/g; } $search =~ s/([^\\]|^)\*/$1 . '%'/eg; $search = '%' . $search . '%' if $bLike && !$bFuzzy; $state = ($state eq '-1') ? undef : int($state); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot search. DB Error (network)\n"; return 0; } my $nwCol = $networkTable->meth2Col('network'); my $descrCol = $networkTable->meth2Col('description'); my $stateCol = $networkTable->meth2Col('state'); my $tmplIDCol = $networkTable->meth2Col('tmplID'); my $rootIDCol = $networkTable->meth2Col('rootID'); my $searchStrCol = $networkTable->meth2Col('searchStr'); my $fuzzySearch = ''; if (lc($dbType) eq 'mysql') { $fuzzySearch = "((substring(soundex($descrCol), 2) LIKE " . (($bLike) ? "concat('%', " : '') . "substring(soundex('$search'), 2)" . (($bLike) ? ", '%')" : '') . ')'; $fuzzySearch .= " OR $searchStrCol LIKE '$search')"; $fuzzySearch .= " AND $stateCol='$state'" if defined $state; $fuzzySearch .= " AND $tmplIDCol='$tmplID'" unless $tmplID == -1; } my $normSearch = ''; if (lc($dbType) eq 'postgresql') { $normSearch = "($descrCol ILIKE E'$search'"; $normSearch .= " OR $searchStrCol ILIKE E'$search'"; $normSearch .= ')'; $normSearch .= " AND $stateCol='$state'" if defined $state; $normSearch .= " AND $tmplIDCol='$tmplID'" unless $tmplID == -1; $normSearch .= " AND $rootIDCol='$rootID'" if $rootID; } else { $normSearch = "($descrCol LIKE '$search'"; $normSearch .= " OR $searchStrCol LIKE '$search'"; $normSearch .= ')'; $normSearch .= " AND $stateCol='$state'" if defined $state; $normSearch .= " AND $tmplIDCol='$tmplID'" unless $tmplID == -1; $normSearch .= " AND $rootIDCol='$rootID'" if $rootID; } my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot search. DB Error (networkV6)\n"; return 0; } my $qryRef = ($bFuzzy) ? $fuzzySearch : $normSearch; &debug("Search: $qryRef"); my @results = $networkTable->search( ['ID', 'network', 'description', 'rootID', 'ipv6ID', 'state', 'defSubnetSize'], $qryRef, $bLike, "ORDER BY $nwCol" ); my $tmp = {}; my $tmplInfos = {}; foreach (@results) { my $net = $_; my $rootID = $net->{rootID}; my $netID = $net->{ID}; $net->{tags} = &getTags($netID); my $ipv6 = &rootID2ipv6($rootID); if ($tmplID ne -1) { my $tmplResult = 0; foreach (keys %{$tmplBox}) { $tmplResult = 1 unless $tmplResultBox->{$net->{ID}}->{$_}; } next if $tmplResult; $tmplInfos->{$net->{ID}} = &getTemplateData($net->{ID}, $tmplID, 1); } if (scalar @{$tags} > 0) { my $tagsDB = $net->{tags}; my $ok = 0; foreach (@{$tagsDB}) { my $tagDB = $_; foreach (@{$tags}) { my $tag = $_; $ok++ if $tag eq $tagDB; last if $tagOp eq 'OR' && $ok; } last if $tagOp eq 'OR' && $ok; } next unless $ok; next if $tagOp eq 'AND' && $ok < scalar @{$tags}; } if ($ipv6) { my $v6Net = ($networkV6Table->search(['ID', 'networkPrefix', 'hostPart', 'cidr'], {ID => $net->{ipv6ID}, rootID => $rootID}, 0))[0]; if (defined $v6Net) { $net->{network} = &ipv6Parts2NetDec($v6Net->{networkPrefix}, $v6Net->{hostPart}, $v6Net->{cidr}); } } $tmp->{A}->{$rootID}->{NETS}->{$net->{network}} = $net; $tmp->{A}->{$rootID}->{IPV6} = $ipv6; } my $pushCnter = 0; foreach my $rootID (keys %{$tmp->{A}}) { my $ipv6 = $tmp->{A}->{$rootID}->{IPV6}; my @nets = (); if ($ipv6) { @nets = &ipv6Sort(keys %{$tmp->{A}->{$rootID}->{NETS}}); } else { @nets = sort {$a<=>$b} keys %{$tmp->{A}->{$rootID}->{NETS}}; } foreach (@nets) { next unless $tmp->{A}->{$rootID}->{NETS}->{$_}->{network}; next if !&checkRootACL($rootID, 'r') || !&checkNetACL($tmp->{A}->{$rootID}->{NETS}->{$_}->{ID}, 'r'); my $network = ($ipv6) ? &netv6Dec2net($tmp->{A}->{$rootID}->{NETS}->{$_}->{network}) : &dec2net($tmp->{A}->{$rootID}->{NETS}->{$_}->{network}); my ($ipaddress, $cidr) = split(/\//, $network); $network = Net::IPv6Addr::to_string_compressed($ipaddress) . '/' . $cidr if $ipv6; my $rootName = &rootID2Name($rootID); if ($rootName ne '') { my $nrOfFreeSubsStr = 0; if ($bshNrOfFreeSubs) { if ($tmp->{A}->{$rootID}->{NETS}->{$_}->{defSubnetSize}) { my $freeSubnets = &getFreeSubnets($tmp->{A}->{$rootID}->{NETS}->{$_}->{ID}, 1, $tmp->{A}->{$rootID}->{NETS}->{$_}->{defSubnetSize}); $nrOfFreeSubsStr = $freeSubnets . ' /' . $tmp->{A}->{$rootID}->{NETS}->{$_}->{defSubnetSize}; } else { my $cntBox; foreach (&getFreeSubnets($tmp->{A}->{$rootID}->{NETS}->{$_}->{ID})) { (undef, my $cidr) = split(/\//); $cntBox->{$cidr}++; } $nrOfFreeSubsStr = '' if scalar keys %{$cntBox} > 0; foreach (sort keys %{$cntBox}) { $nrOfFreeSubsStr .= ', ' if $nrOfFreeSubsStr; $nrOfFreeSubsStr .= $cntBox->{$_} . ' /' . $_; } } } (my $cidr = $network) =~ s#.*/##; my $defSubnetSize = $tmp->{A}->{$rootID}->{NETS}->{$_}->{defSubnetSize} || '?'; last if $pushCnter++ > $searchLimit; push @{$tmp->{B}}, { netID => $tmp->{A}->{$rootID}->{NETS}->{$_}->{ID}, tags => "eHTML(join(' ', @{$tmp->{A}->{$rootID}->{NETS}->{$_}->{tags}})), network => $network, description => "eHTML($tmp->{A}->{$rootID}->{NETS}->{$_}->{description}), url => "$conf->{var}->{thisscript}?jumpToButton=1&rootIDJump=$rootID&jumpTo=$network", rootName => "eHTML($rootName), state => &networkStateID2Name($tmp->{A}->{$rootID}->{NETS}->{$_}->{state}), nrOfFreeSubs => $nrOfFreeSubsStr } } } last if $pushCnter++ > $searchLimit; } my $t = $HaCi::GUI::init::t; $t->{V}->{'gettext_rootName'} = _gettext("Root Name"); if ($#{$tmp->{B}} == -1) { $t->{V}->{searchResult} = {}; $t->{V}->{noSearchResult} = 1; $t->{V}->{'gettext_nothing_found'} = _gettext("Nothing found"); return; } $t->{V}->{searchResult} = $tmp->{B}; $t->{V}->{tmplInfos} = $tmplInfos if $tmplID ne -1; $t->{V}->{tmplDescr} = &getTemplateEntries($tmplID, 0, 0, 1, 0, 1) if $tmplID ne -1; return $tmp->{B}; } sub networkStateName2ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $name = shift; if (exists $conf->{var}->{misc}->{networkstates}->{name}->{$name}) { return $conf->{var}->{misc}->{networkstates}->{name}->{$name} || 0; } my $states = $conf->{static}->{misc}->{networkstates}; foreach (@$states) { if ($name eq $_->{name}) { $conf->{var}->{misc}->{networkstates}->{name}->{$name} = $_->{id}; return $_->{id}; } } return 0; } sub networkStateID2Name{ warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = shift; if (exists $conf->{var}->{misc}->{networkstates}->{id}->{$ID}) { return $conf->{var}->{misc}->{networkstates}->{id}->{$ID}; } my $states = $conf->{static}->{misc}->{networkstates}; foreach (@$states) { if ($ID eq $_->{id}) { $conf->{var}->{misc}->{networkstates}->{id}->{$ID} = $_->{name}; return $_->{name}; } } return ''; } sub getNetworkTypes { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $bOther = shift || 0; my $types = []; my $tmplTable = $conf->{var}->{TABLES}->{template}; push @{$types}, {ID => 0, name => 'other'} if $bOther; unless (defined $tmplTable) { warn "Cannot get Network Types. DB Error (template)\n"; return $types; } my $nameCol = $tmplTable->meth2Col('name'); my @netTypes = $tmplTable->search(['ID', 'name'], {type => 'Nettype'}, 0, "ORDER BY $nameCol"); foreach (@netTypes) { push @{$types}, {ID => $_->{ID}, name => $_->{name}}; } return $types; } sub getTemplate { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; my $return = {}; my $returnFail = { Positions => [ {ID => 0, name => 1} ], MaxPosition => 0, }; return $returnFail unless defined $tmplID; my $tmplTable = $conf->{var}->{TABLES}->{template}; unless (defined $tmplTable) { warn "Cannot get Template. DB Error (template)\n"; return $returnFail; } my $tmpl = ($tmplTable->search(['*'], {ID => $tmplID}))[0]; $return = $tmpl; unless (defined $tmpl) { &warnl(sprintf(_gettext("No Template for this ID '%i' available!"), $tmplID)); return $returnFail; } my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { warn "Cannot get Template. DB Error (templateEntry)\n"; return $returnFail; } my @entries = $tmplEntryTable->search(['ID'], {tmplID => $tmplID}); return $returnFail if $#entries < 0; for (0 .. ($#entries + 1)) { push @{$return->{Positions}}, {ID => (($#entries + 1) - $_), name => (($#entries + 2) - $_)}; } $return->{MaxPosition} = ($#entries + 1); return $return; } sub tmplEntryID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = shift; $ID = -1 unless defined $ID; my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { warn "Cannot get Template. DB Error (templateEntry)\n"; return ''; } my $DB = ($tmplEntryTable->search(['description'], {ID => $ID}))[0]; return ($DB) ? $DB->{description} : ''; } sub changeTmplName { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; my $tmplName = shift || ''; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $tmplTable = $conf->{var}->{TABLES}->{template}; $tmplID = -1 unless defined $tmplID; unless (defined $tmplTable) { &warnl("Cannot update Template. DB Error (template)"); } if ($tmplID < 0) { &warnl(_gettext('Sorry, wrong tamplate ID passed!')); return 0; } unless ($tmplName) { &warnl(_gettext('Sorry, you have to give me a name!')); return 0; } $tmplTable->clear(); $tmplTable->name($tmplName); my $DB = ($tmplTable->search(['name'], {ID => $tmplID}))[0]; if ($DB) { $tmplTable->modifyFrom($s->param('username')); $tmplTable->modifyDate(&currDate('datetime')); &debug("Change Template-Name from '$DB->{name}' to '$tmplName'\n"); unless ($tmplTable->update({ID => $tmplID})) { my $errStr = "Cannot update Template-Name: " . $tmplTable->errorStrs(); &warnl($errStr); &audit('template.updateName', $DB->{name}, $tmplName, $errStr, 1); } else { &audit('template.updateName', $DB->{name}, $tmplName, '', 1); } } else { &warnl(sprintf(_gettext("No such template found for id '%s'"), $tmplID)); return 0; } } sub saveTmpl { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift || 0; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $tmplID = &getParam(1, -1, 'tmplID'); my $tmplType = &getParam(1, undef, 'tmplType'); my $tmplTable = $conf->{var}->{TABLES}->{template}; my $position = &getParam(1, 0, 'position'); unless (defined $tmplTable) { &warnl("Cannot save Template. DB Error (template)"); } my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { &warn("Cannot save Template. DB Error (templateEntries)"); return 0; } unless (defined $tmplType) { &warnl("Cannot save Template. No Template Type given!"); } my $posCol = $tmplEntryTable->meth2Col('position'); if ($tmplID < 0) { unless (defined &getParam(1, undef, 'tmplName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return 0; } my $tmplName = &getParam(1, '', 'tmplName'); $tmplTable->clear(); $tmplTable->name($tmplName); $tmplTable->type($tmplType); my $DB = ($tmplTable->search(['ID'], {name => $tmplName}))[0]; if ($DB) { $tmplTable->modifyFrom($s->param('username')); $tmplTable->modifyDate(&currDate('datetime')); &debug("Change Template-Entry for '$tmplName'\n"); unless ($tmplTable->update({ID => $DB->{'ID'}})) { &warnl("Cannot update Template-Entry '$tmplName': " . $tmplTable->errorStrs()); } } else { $tmplTable->ID(undef); $tmplTable->createFrom($s->param('username')); $tmplTable->createDate(&currDate('datetime')); unless ($tmplTable->insert()) { my $errStr = "Cannot create Template-Entry '$tmplName': " . $tmplTable->errorStrs(); &warnl($errStr); &audit('template.add', $tmplName, &tableContent2Str($tmplTable), $errStr, 1); } else { &audit('template.add', $tmplName, &tableContent2Str($tmplTable), '', 1); } } my $newTmpl = ($tmplTable->search(['ID'], {name => $tmplName}))[0]; $tmplID = $newTmpl->{ID}; } if ($type == 2) { my $tmplEntryID = &getParam(1, undef, 'tmplEntryID'); if (!defined $tmplEntryID || $tmplEntryID eq '') { &warnl("No Template Entry ID!"); return $tmplID; } $tmplEntryTable->clear(); my $nrs = $tmplEntryTable->delete({ID => $tmplEntryID}); if ($tmplEntryTable->error) { &warnl("Error: " . $tmplEntryTable->errorStrs); return $tmplID; } &debug("$nrs Entries from Template deleted!\n"); my @entries = $tmplEntryTable->search(['ID', 'position'], {tmplID => $tmplID}, 0, "AND $posCol > $position"); foreach (@entries) { $tmplEntryTable->clear(); $tmplEntryTable->position(($_->{position} - 1)); unless ($tmplEntryTable->update({ID => $_->{ID}})) { warn "Cannot update TmplEntryTable: " . $tmplEntryTable->errorStrs(); } } } else { unless ($type) { my $descrNew = &getParam(1, '', 'TmplEntryParamDescr'); my $tmplEntries = &HaCi::Utils::getTemplateEntries($tmplID, 0, 0, 1, 0, 1); foreach (keys %{$tmplEntries}) { my $id = $_; my $descr = $tmplEntries->{$id}; if ($descr eq $descrNew) { &warnl("Label '$descr' already exists!"); return $tmplID; } } my @entries = $tmplEntryTable->search(['ID', 'position'], {tmplID => $tmplID}, 0, "AND $posCol >= $position"); foreach (@entries) { $tmplEntryTable->clear(); $tmplEntryTable->position(($_->{position} + 1)); unless ($tmplEntryTable->update({ID => $_->{ID}})) { warn "Cannot update TmplEntryTable: " . $tmplEntryTable->errorStrs(); } } } my $tmpl = ($tmplTable->search(['*'], {ID => $tmplID}))[0]; $tmplEntryTable->clear(); $tmplEntryTable->tmplID($tmplID); $tmplEntryTable->type(&getParam(1, 0, 'TmplEntryType')); $tmplEntryTable->position($position); $tmplEntryTable->description(&getParam(1, '', 'TmplEntryParamDescr')); $tmplEntryTable->size(&getParam(1, 1, 'TmplEntryParamSize')); $tmplEntryTable->entries(&getParam(1, '', 'TmplEntryParamEntries')); $tmplEntryTable->rows(&getParam(1, 1, 'TmplEntryParamRows')); $tmplEntryTable->cols(&getParam(1, 1, 'TmplEntryParamCols')); my $bError = 0; if ($type == 1) { my $tmplEntryID = &getParam(1, undef, 'tmplEntryID'); unless (defined $tmplEntryID) { &warnl("No Template Entry ID!"); return $tmplID; } unless ($tmplEntryTable->update({ID => $tmplEntryID})) { my $errStr = "Cannot update Template-Entry for Template '$tmpl->{name}': " . $tmplEntryTable->errorStrs(); &warnl($errStr); &audit('templateEntry.update', $tmpl->{name}, &tableContent2Str($tmplEntryTable), $errStr, 1); $bError = 1; } else { &audit('templateEntry.update', $tmpl->{name}, &tableContent2Str($tmplEntryTable), '', 1); } } else { $tmplEntryTable->ID(undef); unless ($tmplEntryTable->insert()) { my $errStr = "Cannot add Template-Entry for Template '$tmpl->{name}': " . $tmplEntryTable->errorStrs(); &warnl($errStr); &audit('templateEntry.add', $tmpl->{name}, &tableContent2Str($tmplEntryTable), $errStr, 1); $bError = 1; } else { &audit('templateEntry.add', $tmpl->{name}, &tableContent2Str($tmplEntryTable), '', 1); } } unless ($bError) { $tmplTable->modifyFrom($s->param('username')); $tmplTable->modifyDate(&currDate('datetime')); &debug("Change Template-Entry for '$tmpl->{name}'\n"); $tmplTable->errorStrs(''); unless ($tmplTable->update({ID => $tmpl->{ID}})) { &warnl("Cannot update Template-Entry '$tmpl->{name}': " . $tmplTable->errorStrs()); } } } return $tmplID; } sub getTemplateEntries { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; my $bWithValues = shift || 0; my $bWithChecks = shift || 0; my $bOnlyDescrs = shift || 0; my $bWithALLInMenus = shift || 0; my $bQuoteHTML = shift || 0; my $q = $HaCi::HaCi::q; my $tmplEntries = []; my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; my $tmplPos2IDs = []; my $descrs = {}; unless (defined $tmplEntryTable) { &warn("Cannot show Template. DB Error (templateEntries)"); return 0; } my @entries = $tmplEntryTable->search(['*'], {tmplID => $tmplID}); foreach (&sortDBEntriesBy(\@entries, 'position', 1)) { my $ID = $_->{ID}; my $type = $_->{type}; my $descr = $_->{description}; my $size = $_->{size}; my $entries = $_->{entries}; my $rows = $_->{rows}; my $cols = $_->{cols}; my $pos = $_->{position}; my $title = $type; $descr = "eHTML($descr) if $bQuoteHTML; $descrs->{$ID} = $descr if $descr && (!$bOnlyDescrs || ($bOnlyDescrs && $type != 0 && $type != 4)); next if $bOnlyDescrs; push @$tmplPos2IDs, ( { name => 'tmplEntryPos2ID_' . $pos, value => (($type == 4) ? 'tmplEntryDescrID_' : 'tmplEntryID_') . $ID } ); my $popupValues = []; foreach (split(/\s*;\s*/, $entries)) { push @{$popupValues}, { ID => $_, name => $_ }; } unshift @{$popupValues}, { ID => '', name => '[ALL]' } if $bWithALLInMenus; if ($type == 0) { push @{$tmplEntries}, { onClick => ($bWithChecks) ? "updTmplParamsFromPreview($ID, 0, $pos)" : '', value => { type => 'hline', name => 'tmplEntryID_' . $ID, title => $title, colspan => 2, } }; } elsif ($type == 1) { my $value = (($bWithValues && defined &getParam(1, undef, 'tmplEntryID_' . $ID)) ? &getParam(1, undef, 'tmplEntryID_' . $ID) : ''); $value =~ s/"/"/g; push @{$tmplEntries}, { onClick => ($bWithChecks) ? "javascript:updTmplParamsFromPreview($ID, 1, $pos)" : '', elements => [ { target => 'key', type => 'label', value => $descr, name => 'tmplEntryDescrID_' . $ID, hidden => 1 }, { target => 'value', type => 'textfield', name => 'tmplEntryID_' . $ID, size => $size, title => $title, value => $value, } ] }; } elsif ($type == 2) { my $value = (($bWithValues && defined &getParam(1, undef, 'tmplEntryID_' . $ID)) ? &getParam(1, undef, 'tmplEntryID_' . $ID) : ''); $value =~ s/"/"/g; push @{$tmplEntries}, { onClick => ($bWithChecks) ? "javascript:updTmplParamsFromPreview($ID, 2, $pos)" : '', elements => [ { target => 'key', type => 'label', value => $descr, name => 'tmplEntryDescrID_' . $ID, hidden => 1 }, { target => 'value', type => 'textarea', name => 'tmplEntryID_' . $ID, rows => $rows, cols => $cols, value => $value, title => $title, } ] }; } elsif ($type == 3) { push @{$tmplEntries}, { onClick => ($bWithChecks) ? "javascript:updTmplParamsFromPreview($ID, 3, $pos)" : '', elements => [ { target => 'key', type => 'label', value => $descr, name => 'tmplEntryDescrID_' . $ID, hidden => 1 }, { target => 'value', type => 'popupMenu', name => 'tmplEntryID_' . $ID, size => $size, values => $popupValues, selected => (($bWithValues && defined &getParam(1, undef, 'tmplEntryID_' . $ID)) ? [&getParam(1, 0, 'tmplEntryID_' . $ID)] : []), title => $title, } ] }; } elsif ($type == 4) { push @{$tmplEntries}, { onClick => ($bWithChecks) ? "javascript:updTmplParamsFromPreview($ID, 4, $pos)" : '', elements => [ { target => 'single', type => 'label', value => $descr, name => 'tmplEntryDescrID_' . $ID, title => $title, hidden => 1, colspan => 2, align => 'center', }, ] } } } if ($bOnlyDescrs) { return $descrs; } else { return ($tmplEntries, $tmplPos2IDs); } } sub sortDBEntriesBy { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $dbEntries = shift; my $sortCol = shift; my $num = shift || 0; my $ipv6 = shift || 0; my $hash = {}; map { $hash->{$_->{$sortCol}} = $_ } @{$dbEntries}; my @array = (); if ($num) { if ($ipv6) { map {push @array, $hash->{$_}} &ipv6Sort(keys %{$hash}); } else { map {push @array, $hash->{$_}} sort {$a<=>$b} keys %{$hash}; } return @array; } else { map {push @array, $hash->{$_}} sort keys %{$hash}; return @array; } } sub delTmpl { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; my $tmplName = &tmplID2Name($tmplID); unless (defined $tmplID) { &warnl("No Template ID given!"); return 0; } my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { &warnl("Cannot delete Template. DB Error (templateEntry)"); return ''; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { &warnl("Cannot delete Template. DB Error (templateValue)"); return ''; } my $tmplIDCol = $tmplValueTable->meth2Col('tmplID'); my @values = $tmplValueTable->search(['ID', 'netID'], {tmplID => $tmplID}, 0, "Group by $tmplIDCol"); my $nets = ''; foreach (@values) { my ($rootID, $network, $ipv6) = &netID2Stuff($_->{netID}); $nets .= ', ' . &rootID2Name($rootID) . ':' . (($ipv6) ? &netv62Dec($network) : &dec2net($network)); } if ($#values > -1) { &warnl(sprintf(_gettext("There are still Entries for this Template left. Please delete them first! (%s)"), $nets)); return ''; } my @entries = $tmplEntryTable->search(['ID'], {tmplID => $tmplID}); my $cnter = 0; foreach (@entries) { $tmplEntryTable->clear(); my $nrs = $tmplEntryTable->delete({ID => $_->{ID}}); if ($tmplEntryTable->error) { &warnl("Error: " . $tmplEntryTable->errorStrs); return 0; } else { $cnter++ if $nrs ne '0E0'; } } &debug("$cnter Entries from Template deleted!\n"); my $tmplTable = $conf->{var}->{TABLES}->{template}; unless (defined $tmplTable) { warn "Cannot delete Template. DB Error (template)\n"; return ''; } $tmplTable->clear(); my $nrs = $tmplTable->delete({ID => $tmplID}); if ($tmplTable->error) { &warnl("Error: " . $tmplTable->errorStrs); } else { if ($nrs eq '0E0') { &warnl(_gettext("No Templates deleted. Nothing found!")) } else { &warnl(sprintf(_gettext("Successfully deleted Template '%s'"), $tmplName)); &audit('template.delete', $tmplName); } } } sub getTemplateData { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $tmplID = shift; my $onlyNV = shift || 0; # Only Hash of Name => Value my $tmplEntries = []; if (0 && $tmplID == 0) { push @{$tmplEntries}, { elements => [ { target => 'single', type => 'label', value => _gettext("None"), colspan => 2, }, ] }; } my $tmplName = &tmplID2Name($tmplID); push @{$tmplEntries}, ( { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'label', value => "eHTML($tmplName) }, ], }, { value => { type => 'hline', colspan => 2, }, }, ); my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { warn "Cannot show Template Data. (Entries) DB Error (templateEntry)\n"; return []; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { warn "Cannot show Template Data. (Values) DB Error (templateValue)\n"; return []; } my @tmplEntries = $tmplEntryTable->search(['*'], {tmplID => $tmplID}); my $box = {}; foreach (@tmplEntries) { my $tmplEntryID = $_->{ID}; my $pos = $_->{position}; my $valueT = ($tmplValueTable->search(['value'], {netID => $netID, tmplID => $tmplID, tmplEntryID => $tmplEntryID}))[0]; $box->{$pos}->{TMPLENTRY} = $_; $box->{$pos}->{VALUE} = (defined $valueT) ? $valueT->{value} : ''; } my $nvHash = {}; foreach (sort {$a<=>$b} keys %{$box}) { my $tmplEntry = $box->{$_}->{TMPLENTRY}; my $value = $box->{$_}->{VALUE}; my $ID = $tmplEntry->{ID}; my $type = $tmplEntry->{type}; my $descr = $tmplEntry->{description}; my $size = $tmplEntry->{size}; my $entries = $tmplEntry->{entries}; my $rows = $tmplEntry->{rows}; my $cols = $tmplEntry->{cols}; $nvHash->{$descr} = $value if $descr && $type != 0 && $type != 4; next if $onlyNV; if ($type == 0) { push @{$tmplEntries}, { value => { type => 'hline', colspan => 2, } }; } elsif ($type == 4) { push @{$tmplEntries}, { elements => [ { target => 'single', type => 'label', value => "eHTML($descr), colspan => 2, }, ] } } else { $value =~ s/\n/
/g; push @{$tmplEntries}, { elements => [ { target => 'key', type => 'label', value => "eHTML($descr) }, { target => 'value', type => 'label', value => "eHTML($value) }, ] }; } } return (($onlyNV) ? $nvHash : $tmplEntries); } sub getGroups { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $groups = []; my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot get Groups. DB Error (group)\n"; return []; } my @groupsDB = $groupTable->search(['*']); my $groupsT = {}; foreach (@groupsDB) { $groupsT->{lc($_->{name}) . '_' . $_->{ID}} = $_; } foreach (sort keys %{$groupsT}) { push @$groups, { ID => $groupsT->{$_}->{ID}, name => $groupsT->{$_}->{name} }; } return $groups; } sub getGroup { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $groupID = shift; unless (defined $groupID) { warn "No Group ID given!"; return {}; } my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot get Groups. DB Error (group)\n"; return []; } my $group = ($groupTable->search(['*'], {ID => $groupID}))[0]; return $group || {}; } sub saveGroup { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $groupID = &getParam(1, -1, 'groupID'); my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { &warnl("Cannot save Group. DB Error (group)"); } if ($groupID < 0) { unless (&getParam(1, 0, 'groupName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return undef; } } my $groupName = &getParam(1, '', 'groupName'); my $permStr = '1'; my $perms = ''; foreach (sort {$a<=>$b} keys %{$conf->{static}->{rights}}) { my $cPerm = ($groupName eq 'Administrator') ? 1 : (defined &getParam(1, undef, 'groupPerm_' . $_) && &getParam(1, undef, 'groupPerm_' . $_)) ? 1 : 0; $permStr .= $cPerm; $perms .= ', ' if $perms; $perms .= $conf->{static}->{rights}->{$_}->{short} . ":$cPerm"; } my $cryptStr = &lwe(&bin2dec($permStr)); my $descr = &getParam(1, '', 'groupDescr'); my $auditData = "description => $descr, permissions => $perms"; $groupTable->clear(); $groupTable->name($groupName); $groupTable->description($descr); $groupTable->permissions('1' . $cryptStr); my $DB = ($groupTable->search(['ID'], {name => $groupName}))[0]; if ($DB) { $groupTable->modifyFrom($s->param('username')); $groupTable->modifyDate(&currDate('datetime')); &debug("Change Group for '$groupName'\n"); unless ($groupTable->update({ID => $DB->{'ID'}})) { my $errStr = "Cannot update Group '$groupName': " . $groupTable->errorStrs(); &warnl($errStr); &audit('group.update', $groupName, $auditData, $errStr, 1); } else { &audit('group.update', $groupName, $auditData, '', 1); } } else { $groupTable->ID(undef); $groupTable->createFrom($s->param('username')); $groupTable->createDate(&currDate('datetime')); unless ($groupTable->insert()) { my $errStr = "Cannot add Group '$groupName': " . $groupTable->errorStrs(); &warnl($errStr); &audit('group.add', $groupName, $auditData, $errStr, 1); } else { &audit('group.add', $groupName, $auditData, '', 1); } } my $newGroup = ($groupTable->search(['ID'], {name => $groupName}))[0]; if (defined $newGroup) { return $newGroup->{ID}; } else { return undef; } } sub delGroup { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $groupID = shift; my $groupName = &groupID2Name($groupID); unless (defined $groupID) { &warnl("No Group ID given!"); return 0; } my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { &warnl("Cannot delete Group. DB Error (Group/Squat)"); return 0; } my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { &warnl("Cannot delete Group. DB Error (User)"); return 0; } my @users = $userTable->search(['username'], {groupIDs => ' ' . $groupID . ';'}, 1); my $users = ''; foreach (@users) { $users .= ', ' . $_->{username}; } if ($#users > -1) { &warnl(sprintf(_gettext("There are still Users in this Group left. Please remove them from this Group first! (%s)"), $users)); return ''; } $groupTable->clear(); my $nrs = $groupTable->delete({ID => $groupID}); if ($groupTable->error) { &warnl("Error: " . $groupTable->errorStrs); } else { if ($nrs eq '0E0') { &warnl(_gettext("No Group deleted. Nothing found!")) } else { &warnl(sprintf(_gettext("Successfully deleted Group '%s'"), $groupName)); &audit('group.delete', $groupName); } } } sub delUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userID = shift; my $userName = &userID2Name($userID); unless (defined $userID) { &warnl("No User ID given!"); return 0; } my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { &warnl("Cannot delete User. DB Error (user)"); return 0; } $userTable->clear(); my $nrs = $userTable->delete({ID => $userID}); if ($userTable->error) { &warnl("Error: " . $userTable->errorStrs); } else { if ($nrs eq '0E0') { &warnl(_gettext("No User deleted. Nothing found!")) } else { &warnl(sprintf(_gettext("Successfully deleted User '%s'"), $userName)); } } } sub getUsers { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $users = []; my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot get Users. DB Error (user)\n"; return []; } my @usersDB = $userTable->search(['*']); my $usersT = {}; foreach (@usersDB) { $usersT->{lc($_->{username}) . '_' . $_->{ID}} = $_; } foreach (sort keys %{$usersT}) { push @$users, { ID => $usersT->{$_}->{ID}, name => $usersT->{$_}->{username}, }; } return $users; } sub getUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userID = shift; return {} unless $userID; unless (defined $userID) { warn "No User ID given!"; return {}; } my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot get User. DB Error (user)\n"; return {}; } my $user = ($userTable->search(['*'], {ID => $userID}))[0]; return $user || {}; } sub getUserFromName { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userName = shift; unless (defined $userName) { warn "No Username given!"; return {}; } my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot get User. DB Error (user)\n"; return {}; } my $user = ($userTable->search(['*'], {username => $userName}))[0]; return $user || {}; } sub saveUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $userID = &getParam(1, -1, 'userID') || 0; my $userTable = $conf->{var}->{TABLES}->{user}; my $enableIntAcc = &getParam(1, 0, 'enableInternalAccount'); unless (defined $userTable) { &warnl("Cannot save User. DB Error (user)"); } if ($userID < 0) { unless (defined &getParam(1, undef, 'userName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return undef; } } my $userName = &getParam(1, '', 'userName'); my $descr = &getParam(1, '', 'userDescr'); my $groupStr = ''; my $auditData = "description => $descr, groups => "; foreach ($q->param) { if (/^userGroup_(\d+)/) { my $groupID = $1; $auditData .= ', ' if $groupStr; $groupStr .= ' ' . $groupID . ','; $auditData .= &groupID2Name($groupID); } } my $pw = &getParam(1, '', 'password1'); my $pw1 = &getParam(1, '', 'password2'); $userTable->clear(); my $user = ($userID > 0) ? &getUser($userID) : {}; if ($userID > 0 && $user->{password} ne '' && $enableIntAcc && $pw eq '') { &debug("Okay, no Password Change!"); } else { if ($enableIntAcc && $pw eq '') { &warnl("No Password given"); return undef; } if ($pw ne $pw1) { &warnl("Passwords are not equal"); return undef; } my $crypt = ($enableIntAcc) ? &getCryptPassword($pw) : ''; $userTable->password($crypt); } $userTable->username($userName); $userTable->description($descr); $userTable->groupIDs($groupStr); my $DB = ($userTable->search(['ID'], {username => $userName}))[0]; if ($DB) { $userTable->modifyFrom($s->param('username')); $userTable->modifyDate(&currDate('datetime')); &debug("Change User '$userName'\n"); unless ($userTable->update({ID => $DB->{'ID'}})) { my $errStr = "Cannot update user '$userName': " . $userTable->errorStrs(); &warnl($errStr); &audit('user.update', $userName, $auditData, $errStr, 1); } else { &audit('user.update', $userName, $auditData, '', 1); } } else { $userTable->ID(undef); $userTable->createFrom($s->param('username')); $userTable->createDate(&currDate('datetime')); unless ($userTable->insert()) { my $errStr = "Cannot add user '$userName': " . $userTable->errorStrs(); &warnl($errStr); &audit('user.add', $userName, $auditData, $errStr, 1); } else { &audit('user.add', $userName, $auditData, '', 1); } } my $newUser = ($userTable->search(['ID'], {username => $userName}))[0]; if (defined $newUser) { return $newUser->{ID}; } else { return undef; } } sub dec2bin { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $dec = shift; my $bin = sprintf("%b", $dec); return $bin; } sub lwd { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $crypt = shift; my $clear = ''; my @nrs = split//, reverse $crypt; my $first = ''; for (0 .. $#nrs) { my $new = (($nrs[$_] - (($_ == $#nrs) ? $first : $nrs[($_ + 1)])) + 10) % 10; $clear .= $new; $first = $new if $first eq ''; } $clear = reverse $clear; return $clear; } sub checkRight { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $right = shift; my $groupID = shift || 0; my $hasRight = 0; if ($groupID) { my $s = $HaCi::HaCi::session; my $rights = $s->param('rights_' . $groupID); unless (defined $rights) { $rights = &getRights($groupID); $s->param('rights_' . $groupID, $rights); $s->flush(); } $hasRight = (exists $rights->{$right} && $rights->{$right}) ? 1 : 0; } else { my $s = $HaCi::HaCi::session; my $rights = $s->param('rights'); $hasRight = (exists $rights->{$right} && $rights->{$right}) ? 1 : 0; } return $hasRight; } sub getACLCacheEntry { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $acl = $HaCi::HaCi::aclCache->{$type}; return $acl; } sub checkNetACL { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $right = shift; my $groupID = shift; my $showIfInherit = shift || 0; my $checkGroupID = 1 if defined $groupID; my $acls = &getACLCacheEntry('net'); my $s = $HaCi::HaCi::session; my @groupIDs = ($groupID) ? ($groupID) : split(/, /, $s->param('groupIDs')); my $return = 0; my $fromDB = 0; my $rootID = 0; my $bInherit = 0; return 0 if $netID == -1; return 1 if !defined $groupID && $s->param('bAdmin'); my $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { warn "Cannot check ACL. DB Error (networkAC)\n"; return 0; } foreach (@groupIDs) { s/\D//g; my $groupID = $_; my $currReturn = 0; unless ($checkGroupID) { next unless &checkRight('showNets', $groupID); } if (!exists $acls->{$netID}->{$groupID}->{$right}) { $fromDB = 1; $acls->{$netID}->{$groupID}->{r} = 0; $acls->{$netID}->{$groupID}->{w} = 0; $acls->{$netID}->{$groupID}->{ACL} = 0; my $acl = undef; $acl = ($networkACTable->search(['ACL'], {netID => $netID, groupID => $groupID}))[0]; unless (defined $acl) { ($rootID, my $networkDec, my $ipv6) = &netID2Stuff($netID); my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); if (defined $parent) { $acl->{ACL} = &checkNetACL($parent->{ID}, 'ACL', $groupID, $showIfInherit); $bInherit = 1; } } if (defined $acl) { if ($acl->{ACL} % 4 == 1 || $acl->{ACL} % 4 == 3) { $acls->{$netID}->{$groupID}->{r} ||= 1; } if ($acl->{ACL} % 4 == 2 || $acl->{ACL} % 4 == 3) { $acls->{$netID}->{$groupID}->{w} ||= 1; } } else { $acls->{$netID}->{$groupID}->{r} ||= &checkRootACL($rootID, 'r', $groupID); $acls->{$netID}->{$groupID}->{w} ||= &checkRootACL($rootID, 'w', $groupID); $bInherit = 1; } my $newACL = 0; $newACL += 1 if $acls->{$netID}->{$groupID}->{r}; $newACL += 2 if $acls->{$netID}->{$groupID}->{w}; $acls->{$netID}->{$groupID}->{ACL} = $newACL if $acls->{$netID}->{$groupID}->{ACL} < $newACL; $acls->{$netID}->{$groupID}->{ACL} += 4 if $bInherit; $currReturn = $acls->{$netID}->{$groupID}->{$right}; &updateACLCache($acls, 'net'); } else { $currReturn = $acls->{$netID}->{$groupID}->{$right}; } $return ||= $currReturn; } $return ||= 0; &debug("netAC ($netID :@groupIDs :$right [$fromDB]): $return\n") if 0; return ($showIfInherit) ? $return : $return % 4; } sub checkRootACL { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $right = shift; my $groupID = shift; my $acls = &getACLCacheEntry('root'); my $s = $HaCi::HaCi::session; my @groupIDs = ($groupID) ? ($groupID) : split(/, /, $s->param('groupIDs')); my $ok = 0; my $fromDB = 0; return 1 if !defined $groupID && $s->param('bAdmin'); my $rootACTable = $conf->{var}->{TABLES}->{rootAC}; unless (defined $rootACTable) { warn "Cannot check ACL. DB Error (rootAC)\n"; return 0; } foreach (@groupIDs) { s/\D//g; my $groupID = $_; my $currOK = 0; next unless &checkRight('showRoots', $groupID); if (!exists $acls->{$rootID}->{$groupID}->{$right}) { $fromDB = 1; $acls->{$rootID}->{$groupID}->{r} ||= 0; $acls->{$rootID}->{$groupID}->{w} ||= 0; my $acl = ($rootACTable->search(['ACL'], {rootID => $rootID, groupID => $groupID}))[0]; if (defined $acl) { if ($acl->{ACL} == 1 || $acl->{ACL} == 3) { $acls->{$rootID}->{$groupID}->{r} ||= 1; } if ($acl->{ACL} == 2 || $acl->{ACL} == 3) { $acls->{$rootID}->{$groupID}->{w} ||= 1; } } $currOK = $acls->{$rootID}->{$groupID}->{$right}; &updateACLCache($acls, 'root'); } else { $currOK = $acls->{$rootID}->{$groupID}->{$right}; } $ok ||= $currOK; } $ok ||= 0; &debug("rootAC ($rootID :@groupIDs :$right [$fromDB]): $ok\n") if 0; return $ok; } sub updateACLCache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $acls = shift; my $type = shift; $HaCi::HaCi::aclCache->{$type} = $acls; } sub removeACLEntry { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = shift; my $type = shift; my $groupID = shift; my $acls = &getACLCacheEntry($type); if ($groupID) { if (exists $acls->{$ID}->{$groupID}) { delete $acls->{$ID}->{$groupID}; } } else { if (exists $acls->{$ID}) { delete $acls->{$ID}; } } my @childs = &getNetworkChilds($ID, (($type eq 'root') ? 1 : 0), 0); foreach (@childs) { my $ID = $_->{ID}; if ($groupID) { if (exists $acls->{$ID}->{$groupID}) { delete $acls->{$ID}->{$groupID}; } } else { if (exists $acls->{$ID}) { delete $acls->{$ID}; } } } &updateACLCache($acls, $type); } sub getNetworkParentFromDB { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $ipv6 = shift; my $limit = shift // 1; $ipv6 = &rootID2ipv6($rootID) unless defined $ipv6; my $parent = undef; my $limitStr = ($limit) ? "LIMIT $limit" : ''; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get Parent Network. DB Error (network)\n"; return undef; } if ($ipv6) { my $broadcast = &getV6BroadcastNet($networkDec, 128); my ($net, $host, $cidr) = (0, 0, 0); if ($networkDec && ref $networkDec) { ($net, $host, $cidr) = &netv6Dec2PartsDec($networkDec); } else { &debug("V6 NetworkDec ($networkDec) should be an Math::BigInt Reference!"); } my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get NetworkV6 Parent. DB Error (networkV6)\n"; return undef; } my $npCol = $networkV6Table->meth2Col('networkPrefix'); my $hpCol = $networkV6Table->meth2Col('hostPart'); my $cidrCol = $networkV6Table->meth2Col('cidr'); my $rootIDFilter = ($rootID == -1) ? {1 => 1} : {rootID => $rootID}; my @potParents = $networkV6Table->search( ['ID', 'rootID', 'networkPrefix', 'hostPart', 'cidr'], $rootIDFilter, 0, "AND ( ($npCol < $net) OR ($npCol = $net AND $hpCol < $host) OR ($npCol = $net AND $hpCol = $host AND $cidrCol < $cidr) ) ORDER BY $npCol DESC, $hpCol DESC, $cidrCol DESC"); return $parent unless @potParents; my $potParentsWR = {}; foreach (@potParents) { my $potParent = $_; $potParent->{network} = &ipv6Parts2NetDec($potParent->{networkPrefix}, $potParent->{hostPart}, $potParent->{cidr}); push @{$potParentsWR->{$potParent->{rootID}}}, $potParent; } my @parents = (); foreach (sort {$a<=>$b} keys %{$potParentsWR}) { my $rootID = $_; foreach (reverse &sortDBEntriesBy(\@{$potParentsWR->{$rootID}}, 'network', 1, 1)) { my $potParent = $_; $networkDec = $potParent->{network}; my $potBroadcast = &getV6BroadcastNet($networkDec, 128); if ($potBroadcast >= $broadcast) { $parent = $potParent; $parent->{ipv6} = 1; my $network = ($networkTable->search(['ID', 'network', 'description', 'state', 'defSubnetSize'], {ipv6ID => $parent->{ID}, rootID => $rootID, network => 0}, 0))[0]; if (defined $network) { $parent->{ID} = $network->{ID}; $parent->{description} = $network->{description}; $parent->{state} = $network->{state}; $parent->{defSubnetSize} = $network->{defSubnetSize}; if ($limit == 1) { last; } else { push @parents, $parent; } } else { warn "NetV6 found ($parent->{ID}) with no matching network!\n"; $parent = undef; } } } last if defined $parent && $limit == 1; } $parent = \@parents unless $limit == 1; } else { my $broadcast = &getBroadcastFromNet($networkDec); my $riCol = $networkTable->meth2Col('rootID'); my $ipv6IDCol = $networkTable->meth2Col('ipv6ID'); my $nwCol = $networkTable->meth2Col('network'); my $rootIDFilter = ($rootID == -1) ? '' : "$riCol='$rootID' AND"; my @parents = $networkTable->search( ['ID', 'network', 'description', 'state', 'defSubnetSize'], "$rootIDFilter $ipv6IDCol='' AND $nwCol<'$networkDec' AND (FLOOR($nwCol / 256) + power(2, (32 - MOD($nwCol, 256))) > $broadcast) ORDER BY $nwCol DESC $limitStr" ); $parent = ($limit == 1) ? $parents[0] : \@parents; } return $parent; } sub getRights { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $groupID = shift || 0; my $session = $HaCi::HaCi::session; my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { &warnl("Cannot get Rights. DB Error (user)"); return {}; } my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { &warnl("Cannot get Rights. DB Error (group)"); return {}; } unless (defined $session->param('groupIDs') && $session->param('groupIDs')) { my $user = ($userTable->search(['ID', 'groupIDs'], {username => $session->param('username')}))[0]; unless (defined $user) { &warnl("Cannot get Rights. No such User '" . $session->param('username') . "' in Database"); return {}; } $session->param('groupIDs', $user->{groupIDs}); } my $groupIDs = $session->param('groupIDs'); my $rights = {}; $session->clear('bAdmin') unless $groupID; my @groupIDs = ($groupID) ? ($groupID) : split(/, /, $groupIDs); foreach (@groupIDs) { s/\D//g; my $group = ($groupTable->search(['ID', 'permissions', 'name'], {ID => $_}))[0]; next unless defined $group; $session->param('bAdmin', 1) if !$groupID && $group->{name} eq 'Administrator'; if ($group->{name} eq 'Administrator') { foreach (keys %{$conf->{static}->{rights}}) { $rights->{$conf->{static}->{rights}->{$_}->{short}} = 1; } } else { my $cnter = 0; my $cryptStr = substr($group->{permissions}, 1, length($group->{permissions}) - 1); my $permStr = &dec2bin(&lwd($cryptStr)); foreach (split//, substr($permStr, 1, length($permStr) - 1)) { if (exists $conf->{static}->{rights}->{$cnter}) { my $right = ($_ eq '1') ? 1 : 0; $rights->{$conf->{static}->{rights}->{$cnter}->{short}} ||= $right; } $cnter++; } } } $session->param('rights', $rights) unless $groupID; return $rights if $groupID; } sub getNetID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $ipv6ID = shift; $ipv6ID = '' unless defined $ipv6ID; $networkDec = 0 if $ipv6ID; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot get netID Network. DB Error (network)\n"; return undef; } my $network = ($networkTable->search(['ID'], {rootID => $rootID, network => $networkDec, ipv6ID => $ipv6ID}))[0]; if (defined $network && exists $network->{ID}) { return $network->{ID}; } else { return undef; } } sub getAllNets { my $rootID = shift; my $ipv6 = shift; $ipv6 = &rootID2ipv6($rootID) unless defined $ipv6; my @nets = (); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot compare. DB Error (network)\n"; return 0; } @nets = $networkTable->search(['ID', 'network', 'ipv6ID'], {rootID => $rootID}); if ($ipv6) { my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot get NetworkV6 Parent. DB Error (networkV6) \n"; return undef; } foreach (@nets) { my $v6Net = &getV6Net($_->{ipv6ID}); if (defined $v6Net) { $_->{network} = &ipv6Parts2NetDec($v6Net->{networkPrefix}, $v6Net->{hostPart}, $v6Net->{cidr}); } } } return @nets; } sub compare { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $leftID = &getParam(1, undef, 'leftRootID'); my $rightID = &getParam(1, undef, 'rightRootID'); my $rootName = &getParam(1, undef, 'resultName'); my $leftName = &rootID2Name($leftID); my $rightName = &rootID2Name($rightID); my $ipv6L = &rootID2ipv6($leftID); my $ipv6R = &rootID2ipv6($rightID); my $status = $conf->{var}->{STATUS}; $status->{TITLE} = "Comparing '$leftName' <-> '$rightName'"; $status->{STATUS} = 'Runnging...'; $status->{PERCENT} = 0; &setStatus(); if (($ipv6R && !$ipv6L) || (!$ipv6R && $ipv6L)) { &warnl("Cannot compare an IPv4 Root with an IPv6!"); return 0; } unless ($rootName) { $rootName = $leftName . ' - ' . $rightName; } $status->{DATA} = "Adding Root '$rootName'"; $status->{PERCENT} = 10; &setStatus(); unless (&addRoot($rootName, "These Networks are missing in $rightName", $ipv6L)) { warn "AddRoot failed!\n"; return 0; } my $rootID = &rootName2ID($rootName); my $box = {}; my $statCnter = 25; @{$box->{NETS}->{LEFT}} = &getAllNets($leftID); @{$box->{NETS}->{RIGHT}} = &getAllNets($rightID); foreach ('LEFT', 'RIGHT') { my $type = $_; $status->{DATA} = "Compare $type Side...!"; $status->{PERCENT} = $statCnter; &setStatus(); $statCnter += 25; foreach (@{$box->{NETS}->{$type}}) { return unless $_->{network}; my $key = $_->{network} . '_' . $_->{ipv6ID}; $box->{RESULT}->{$key}->{$type} = $_->{ID}; } } $status->{DATA} = "Processing Result...!"; $status->{PERCENT} = $statCnter; &setStatus(); foreach (keys %{$box->{RESULT}}) { my $key = $_; my ($networkDec, $ipv6ID) = split/_/, $key; my $netIDL = $box->{RESULT}->{$key}->{LEFT}; my $netIDR = $box->{RESULT}->{$key}->{RIGHT}; $networkDec = Math::BigInt->new($networkDec) if $ipv6ID; my $network = ($ipv6ID) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); next unless ( ((defined $netIDL && &checkNetACL($netIDL, 'r')) || (!defined $netIDL && &checkRootACL($leftID, 'r'))) && ((defined $netIDR && &checkNetACL($netIDR, 'r')) || (!defined $netIDR && &checkRootACL($rightID, 'r'))) ); if (exists $box->{RESULT}->{$key}->{LEFT} && !exists $box->{RESULT}->{$key}->{RIGHT}) { ©NetsTo($rootID, ["${network}_$leftID"], 0, 1); } } $status->{DATA} = "FINISH"; $status->{PERCENT} = 100; $status->{STATUS} = 'FINISH'; &setStatus(); return; } sub checkDB { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot search. DB Error (network)\n"; return 0; } my @results = $networkTable->search(); warn "CheckDB. Checking " . ($#results + 1) . " Results...\n"; my $tmp = {}; foreach (@results) { my $networkDec = $_->{network}; my $netID = $_->{ID}; my ($ip, $cidr) = split(/\//, &dec2net($networkDec)); my $netaddress = &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); if ($ip ne $netaddress) { my $newNetwork = &net2dec($netaddress . '/' . $cidr); $networkTable->clear(); $networkTable->network($newNetwork); $networkTable->searchStr($netaddress . '/' . $cidr); unless ($networkTable->update({ID => $netID})) { &warnl("Cannot update Net: " . $networkTable->errorStrs); if ($networkTable->errorStrs =~ /Duplicate entry/) { my $newNetwork2 = &net2dec($ip . '/' . 32); $networkTable->clear(); $networkTable->network($ip . '/' . 32); unless ($networkTable->update({ID => $netID})) { &warnl("Cannot update Net: " . $networkTable->errorStrs); } } } } } } sub newWindow { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; if ($type eq 'showStatus') { push @{$conf->{var}->{newWindows}}, { URL => "$conf->{var}->{thisscript}?func=showStatus", WIDTH => 200, HEIGHT => 150, TITLE => 'Status' } } } sub prNewWindows { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $retString = shift || 0; my $ret = ''; foreach (@{$conf->{var}->{newWindows}}) { my $hash = $_; $ret .= ""; } if ($retString) { return $ret; } else { print $ret; } } sub getID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = Digest::MD5::md5_hex(time . $$); return $ID; } sub setStatus { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $status = shift; $status = $conf->{var}->{STATUS} unless ref $status eq 'HASH'; return unless defined $HaCi::HaCi::session; my $statID = $HaCi::HaCi::session->id(); my $statFile = $conf->{static}->{path}->{statusfile} . '_' . $statID . '.stat'; return unless ref $status; eval { Storable::lock_store($status, $statFile) or warn "Cannot store Status ($statFile)!\n"; }; if ($@) { warn $@; }; } sub removeStatus { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $statID = $HaCi::HaCi::session->id(); unlink $conf->{static}->{path}->{statusfile} . '_' . $statID . '.stat'; } sub getStatus { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $session = $HaCi::HaCi::session; return {} unless defined $session; my $statID = $session->id(); my $statFile = $conf->{static}->{path}->{statusfile} . '_' . $statID . '.stat'; return {} unless -f $statFile; my $status = {}; eval { $status = Storable::lock_retrieve($statFile); }; if ($@) { warn $@; }; return $status; } sub expand { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $target = shift; my $value = shift; my $rootID = shift; my $s = $HaCi::HaCi::session; return unless defined $s; my $expands = $s->param('expands'); $s->clear('expands'); if ($type eq '-' && $target eq 'ALL' && $value eq 'ALL') { $expands = {}; } else { if ($target eq 'root') { $expands->{$target}->{$value} = ($type eq '+') ? 1 : 0; } else { my $ipv6 = &rootID2ipv6($rootID); $value = Math::BigInt->new($value) if $ipv6 && !ref $value; if (0) { # we don't want to check for a parent, because we also want to expand an empty subnet my $parent = &getNextDBNetwork($rootID, $ipv6, $value, 1); if (defined $parent) { $expands->{$target}->{$rootID}->{$value} = ($type eq '+') ? 1 : 0; } } else { $expands->{$target}->{$rootID}->{$value} = ($type eq '+') ? 1 : 0; } } } $s->param('expands', $expands); $s->flush(); } sub splitNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $splitCidr = shift; my $descrTmpl = shift; my $state = shift; my $tmplID = shift; my $delParent = shift; $conf->{var}->{STATUS} = {TITLE => 'Splitting Network...', STATUS => 'Running...'}; &setStatus(); my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); my $netaddressDec = ($ipv6) ? (&netv6Dec2IpCidr($networkDec))[0] : &getIPFromDec($networkDec); my $broadcast = ($ipv6) ? &getV6BroadcastIP($networkDec) : &getBroadcastFromNet($networkDec); my $adder = Math::BigInt->new(2); my $mul = (($ipv6) ? 128 : 32); $mul -= $splitCidr; $adder->bpow($mul); my $cnter = 0; while ($netaddressDec <= $broadcast) { $cnter++; my $descr = $descrTmpl; $descr =~ s/\%d/$cnter/g; my $netaddress = ($ipv6) ? &ipv6Dec2ip($netaddressDec) : &dec2ip($netaddressDec); $conf->{var}->{STATUS}->{DATA} = $netaddress; &setStatus(); &addNet(0, $rootID, $netaddress, $splitCidr, $descr, $state, $tmplID, 0, 1); if ($ipv6) { $netaddressDec->badd($adder); } else { $netaddressDec += 2 ** $mul; } } &delNet($netID, 0, 0, 0) if $delParent; $conf->{var}->{STATUS}->{STATUS} = 'FINISH'; &setStatus(); } sub combineNets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $combineNetsNrs = &getParam(0, [], 'combineNetsNr'); foreach (@{$combineNetsNrs}) { my $cnter = $_; my $rootID = &getParam(1, 0, 'combineNets_' . $cnter . '_rootID'); my $networkDec = &getParam(1, 0, 'combineNets_' . $cnter . '_result'); my $sources = &getParam(0, [], 'combineNets_' . $cnter . '_source'); my $descr = &getParam(1, 0, 'combineNets_' . $cnter . '_descr'); my $state = &getParam(1, 0, 'combineNets_' . $cnter . '_state'); my $tmplID = &getParam(1, 0, 'combineNets_' . $cnter . '_tmplID'); my $ipv6 = &rootID2ipv6($rootID); $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my ($ipaddress, $cidr) = split(/\//, $network, 2); if (&addNet(0, $rootID, $ipaddress, $cidr, $descr, $state, $tmplID, 0, 1)) { foreach (@{$sources}) { my $networkDec = $_; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); &delNet($netID, 0, 0, 0); } } } } sub getPlugins { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $pluginInfos = {}; my $pluginDir = $conf->{static}->{path}->{plugins}; return {} unless -d $pluginDir; &warnl("Cannot open Directory '$pluginDir': $!") unless opendir DIR, $pluginDir; my @plugins = grep { /\.pm$/ && -f "$pluginDir/$_" } readdir(DIR); closedir DIR; foreach (@plugins) { my $pluginFile = $_; (my $pluginFilename = $pluginFile) =~ s/\.pm//; my ($pluginID, $pluginInfo) = &getPluginInfos($pluginFilename); next unless exists $pluginInfo->{ACTIVE} || defined $pluginID; if (defined $type) { next unless $pluginInfo->{uc($type)}; } $pluginInfos->{$pluginID} = $pluginInfo; } return $pluginInfos; } sub getPluginInfos { my $pluginFilename = shift; my $pluginDir = $conf->{static}->{path}->{plugins}; my $pluginFullFile = $pluginDir . '/' . $pluginFilename . '.pm'; my $pluginInfos = {}; unless (open PLUG, $pluginFullFile) { my $error = "Cannot open Plugin '$pluginFullFile' for reading: $!\n"; warn $error; return (undef, {ERROR=>$error}); } my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { my $error = "Cannot get Plugins. DB Error (plugin)\n"; warn $error; return (undef, {ERROR=>$error}); } my $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { my $error = "Cannot get Plugins. DB Error (networkPlugin)\n"; warn $error; return (undef, {ERROR=>$error}); } my $package = ''; foreach () { if (/[^#]*package\s([^;]+)/) { $package = $1; last; } } close PLUG; unless ($package) { my $error = "$pluginFilename: Cannot determine Package! Next...\n"; warn $error; return (undef, {ERROR=>$error}); } (my $packageFile = $package) =~ s/::/\//g; eval { require $packageFile . '.pm'; }; if ($@) { my $error = "Cannot load module $package: $@\n"; warn $error; return (undef, {ERROR=>$error}); } unless ($package->can('new')) { my $error = "Cannot load module $package: No constructor available!\n"; warn $error; return (undef, {ERROR=>$error}); } my $id = 0; { no strict qw/refs/; my $file = $pluginFilename; my $name = ${"${package}::INFO"}->{name}; my $version = ${"${package}::INFO"}->{version}; my $recurrent = ${"${package}::INFO"}->{recurrent} || 0; my $onDemand = ${"${package}::INFO"}->{onDemand} || 0; my $api = ${"${package}::INFO"}->{api}; my $descr = ${"${package}::INFO"}->{description}; my $dbPlug = ($pluginTable->search(['*'], {name => $name}))[0]; my $globMenuRecurrent = ${"${package}::INFO"}->{globMenuRecurrent}; my $globMenuOnDemand = ${"${package}::INFO"}->{globMenuOnDemand}; my $menuRecurrent = ${"${package}::INFO"}->{menuRecurrent}; my $menuOnDemand = ${"${package}::INFO"}->{menuOnDemand}; unless ($dbPlug) { $pluginTable->clear(); $pluginTable->name($name); $pluginTable->filename($file); $pluginTable->active(0); unless ($pluginTable->insert()) { &warnl("Cannot create Plugin Entry for '$name': " . $pluginTable->errorStrs()); } $dbPlug = ($pluginTable->search(['*'], {name => $name}))[0]; } $dbPlug = { ID => 0, active => 0, } unless defined $dbPlug; unless ($dbPlug->{filename} eq $file) { warn "Updating Plugin Database...\n"; $pluginTable->clear(); $pluginTable->filename($file); unless ($pluginTable->update({ID => $dbPlug->{ID}})) { warn "Cannot create Plugin Entry for '$name': " . $pluginTable->errorStrs() . "\n"; } } my $plugDefault = ($networkPluginTable->search(['*'], {netID => -1, pluginID => $dbPlug->{ID}}))[0]; $id = $dbPlug->{ID}; $pluginInfos = { FILE => $file, NAME => $name, VERSION => $version, ACTIVE => $dbPlug->{active}, RECURRENT => $recurrent, ONDEMAND => $onDemand, PACKAGE => $package, LASTRUN => $dbPlug->{lastRun}, RUNTIME => $dbPlug->{runTime}, LASTERROR => $dbPlug->{lastError} || '', API => $api, DESCR => $descr, DEFAULT => (defined $plugDefault && $plugDefault) ? 1 : 0, GLOBMENURECURRENT => $globMenuRecurrent || [], GLOBMENUONDEMAND => $globMenuOnDemand || [], MENURECURRENT => $menuRecurrent || [], MENUONDEMAND => $menuOnDemand || [], } } return ($id, $pluginInfos); } sub updatePluginDB { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot update PluginDB. DB Error (plugin)\n"; return 0; } my $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { warn "Cannot update PluginDB. DB Error (networkPlugin)\n"; return 0; } my $box = {}; my $pluginActives = &getParam(0, [], 'pluginActives'); foreach (@{$pluginActives}) { $box->{$_}->{ACTIVE} = 1; } my $pluginDefaults = &getParam(0, [], 'pluginDefaults'); foreach (@{$pluginDefaults}) { $box->{$_}->{DEFAULT} = 1; } my @plugins = $pluginTable->search(); return unless @plugins; foreach (@plugins) { my $ID = $_->{ID}; my $name = $_->{name}; my $active = $_->{active}; my $plugDefault = ($networkPluginTable->search(['*'], {netID => -1, pluginID => $ID}))[0]; my $default = (defined $plugDefault) ? 1 : 0; if ($active && !exists $box->{$ID}->{ACTIVE} || !$active && exists $box->{$ID}->{ACTIVE}) { my $newActive = (exists $box->{$ID}->{ACTIVE}) ? 1 : 0; $pluginTable->clear(); $pluginTable->active($newActive); unless ($pluginTable->update({ID => $ID})) { &warnl("Cannot update Plugin Entry for '$name': " . $pluginTable->errorStrs()); } } if ($default && !exists $box->{$ID}->{DEFAULT} || !$default && exists $box->{$ID}->{DEFAULT}) { if (exists $box->{$ID}->{DEFAULT}) { $networkPluginTable->clear(); $networkPluginTable->ID(undef); $networkPluginTable->netID(-1); $networkPluginTable->pluginID($ID); $networkPluginTable->errorStrs(''); unless ($networkPluginTable->insert()) { # WAS replace. perhaps we have to check if it already exists &warnl("Cannot update Network Plugin Entry for '$name': " . $networkPluginTable->errorStrs()); } } else { $networkPluginTable->clear(); $networkPluginTable->delete({netID => -1, pluginID => $ID}); if ($networkPluginTable->error) { &warnl("Error: " . $networkPluginTable->errorStrs); } } } } } sub pluginID2File { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot get PluginName. DB Error (plugin)\n"; return 0; } my $plugin = ($pluginTable->search(['filename'], {ID => $pluginID}))[0]; return (defined $plugin) ? $plugin->{filename} : ''; } sub pluginID2Name { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot get PluginName. DB Error (plugin)\n"; return 0; } my $plugin = ($pluginTable->search(['name'], {ID => $pluginID}))[0]; return (defined $plugin) ? $plugin->{name} : ''; } sub pluginName2ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $name = shift; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot get PluginName. DB Error (plugin)\n"; return 0; } my $plugin = ($pluginTable->search(['ID'], {name => $name}))[0]; return (defined $plugin) ? $plugin->{ID} : ''; } sub getNetworksForPlugin { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my @return = (); my $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { warn "Cannot get Networks. DB Error (networkPlugin)\n"; return 0; } my @networks = $networkPluginTable->search(['netID'], {pluginID => $pluginID}); return @return unless @networks; foreach (@networks) { push @return, $_->{netID}; } return @return; } sub getPluginsForNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $return = (); my $networkPluginTable = $conf->{var}->{TABLES}->{networkPlugin}; unless (defined $networkPluginTable) { warn "Cannot get Plugins. DB Error (networkPlugin)\n"; return $return; } my $netIDCol = $networkPluginTable->meth2Col('netID'); my @plugins = $networkPluginTable->search(['*'], "$netIDCol='$netID' OR $netIDCol='-1'", 0, 0, 1); return $return unless @plugins; foreach (@plugins) { $return->{$_->{pluginID}} = $_ unless $_->{netID} == -1 && exists $return->{$_->{pluginID}}; } return $return; } sub getTable { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $name = shift; return if $conf->{var}->{DatabaseNotExist}; my $dbType = &getConfigValue('db', 'dbtype'); eval { require "HaCi/Tables/$dbType/$name.pm"; }; if ($@) { warn "Error while loading Table '$name': $@\n"; return; } if (exists $conf->{var}->{TABLES}->{$name}) { my $dbh = $conf->{var}->{TABLES}->{$name}->dbh(); if (ref($dbh) && $dbh->can('ping') && $dbh->ping()) { return; } else { &closeTable($name); } } $DBIEasy::lastError = ''; $conf->{var}->{TABLES}->{$name} = "HaCi::Tables::${dbType}::$name"->new($conf->{user}->{db}); if ($DBIEasy::lastError =~ /Unknown database (.*)/) { &warnl("Database $1 is not available! Perhaps you have to create it?"); $conf->{var}->{DatabaseNotExist} = 1; } elsif ($DBIEasy::lastError =~ /Access denied for user (.*)/) { &warnl("User ($1) is not allowed to access! Is the User created and has it permission to access the Database?"); $conf->{var}->{DatabaseNotExist} = 1; } else { warn ($DBIEasy::lastError) if $DBIEasy::lastError; } $DBIEasy::lastError = ''; } sub closeTable { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $name = shift; return if $conf->{var}->{DatabaseNotExist}; $DBIEasy::lastError = ''; if (exists $conf->{var}->{TABLES}->{$name}) { my $dbh = $conf->{var}->{TABLES}->{$name}->dbh(); $dbh->disconnect() or warn "Cannot disconnect from DB!\n"; undef $dbh; delete $conf->{var}->{TABLES}->{$name}; } if ($DBIEasy::lastError =~ /Unknown database (.*)/) { &warnl("Database $1 is not available! Perhaps you have to create it?"); $conf->{var}->{DatabaseNotExist} = 1; } elsif ($DBIEasy::lastError =~ /Access denied for user (.*)/) { &warnl("User ($1) is not allowed to access! Is the User created and has it permission to access the Database?"); $conf->{var}->{DatabaseNotExist} = 1; } else { warn ($DBIEasy::lastError) if $DBIEasy::lastError; } $DBIEasy::lastError = ''; } sub getPluginLastRun { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot get PluginName. DB Error (plugin)\n"; return 0; } my $plugin = ($pluginTable->search(['lastRun'], {ID => $pluginID}))[0]; my $lastRun = $plugin->{lastRun}; $lastRun = 0 unless defined $lastRun; $lastRun = &convDatetime2time($lastRun); return $lastRun; } sub updatePluginLastRun { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $lastRun = shift; my $runTime = shift; my $error = shift; $lastRun = &currDate('datetime', ((defined $lastRun) ? $lastRun : undef)) if $lastRun ne '-1'; $runTime = 0 unless defined $runTime; $error = '' unless defined $error; my $pluginTable = $conf->{var}->{TABLES}->{plugin}; unless (defined $pluginTable) { warn "Cannot get PluginName. DB Error (plugin)\n"; return 0; } $pluginTable->clear(); $pluginTable->lastRun($lastRun) if $lastRun ne '-1'; $pluginTable->runTime($runTime) if $runTime > -1; $pluginTable->lastError($error) if defined $error; unless ($pluginTable->update({ID => $pluginID})) { return 0; } } sub getHashFromFile { my $filename = shift; return '' unless -f $filename; my $sha = Digest::SHA->new(1); $sha->addfile($filename); my $digest = $sha->b64digest; undef($sha); return $digest; } sub getTableHashes { my $tableHashFile = $conf->{static}->{path}->{tablehashfile}; return {} unless -f $tableHashFile; my $tableHashes = {}; eval { $tableHashes = Storable::lock_retrieve($tableHashFile); }; if ($@) { warn $@; }; return $tableHashes; } sub setTableHashes { my $tableHashes = shift || {}; my $tableHashFile = $conf->{static}->{path}->{tablehashfile}; eval { Storable::lock_store($tableHashes, $tableHashFile) or warn "Cannot store TableHashFile ($tableHashFile)!\n"; }; if ($@) { warn $@; }; } sub diffTable { my $table = shift; my $tableName = $table->TABLE(); my $dbType = &getConfigValue('db', 'dbtype'); &debug("Checking Table: $tableName"); my $dbType2Parser = { mysql => 'MySQL', postgresql => 'PostgreSQL' }; my $alterCnter = 0; my $errorCnter = 0; my $dbh = $table->getDBConn(); my $orig = ${${$dbh->selectall_arrayref("SHOW CREATE TABLE `$tableName`")}[0]}[1] . ";\n"; my $newTable = "CREATE TABLE `$tableName` (" . $table->CREATETABLE() . ");\n"; $newTable =~ s/\t/ /g; my $t1 = SQL::Translator->new(parser=>$dbType2Parser->{$dbType}, show_warnings=>0); my $t2 = SQL::Translator->new(parser=>$dbType2Parser->{$dbType}, show_warnings=>0); my $diff; { BEGIN { $^W = 0 } $diff = SQL::Translator::Diff::schema_diff( $t1->translate(\$orig), $dbType2Parser->{$dbType}, $t2->translate(\$newTable), $dbType2Parser->{$dbType}, { ignore_index_names => 1, ignore_constraint_names => 1, ignore_missing_methods => 1, no_batch_alters => 1 } ); } $diff =~ s/.*BEGIN;/BEGIN;/ms; $diff =~ s/COMMIT;.*/COMMIT;/ms; return 0 if $diff =~ /^\s*BEGIN;\s*ALTER TABLE\s*$tableName\s*;\s*COMMIT;\s*$/ms; if (0) { warn "T1: $orig\n"; warn "T2: $newTable\n"; } &debug("Altering Table $tableName..."); foreach (split/\n/, $diff) { s/\s*--.*//; next if /^\s*$/; next if /^\s*ALTER TABLE\s*$tableName\s*;\s*$/; $alterCnter++ if /^\s*ALTER\s/; &debug(" $_"); $table->clear(); $table->alter($_); if ($table->error()) { &warnl(sprintf(_gettext("Cannot update Table '%s': %s. Please correct this issue by hand (i.e.: remove duplicate entries)!"), $tableName, $table->errorStrs())); $errorCnter++; } } &warnl(sprintf(_gettext("Successfully updated Table '%s'."), $tableName)) if $alterCnter > 0 && $errorCnter == 0; return $alterCnter; } sub checkTables { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $checkForce = shift || 0; my $tableHashes = &getTableHashes(); my $dbType = &getConfigValue('db', 'dbtype'); # we currently support database checking only for mysql return unless $dbType eq 'mysql'; my $bChanged = 0; my $errorTold = 0; $errorTold = 1 if $conf->{user}->{misc}->{ignoreupgradefailures}; my $modError = ''; if ($conf->{user}->{misc}->{autoupgradedatabase}) { eval { require SQL::Translator; require SQL::Translator::Diff; }; if ($@) { $modError = sprintf(_gettext("Cannot upgrade tables automatically. Required modules are not available (%s)."), $@); } } else { $modError = _gettext("Automatic upgrade of database disabled. Please upgrade the database schema by hand."); } &debug("Checking Tables...\n"); foreach (keys %{$conf->{var}->{TABLES}}) { my $tableFileName = $_; &debug("Checking Table $tableFileName...\n") if 0; my $tableFile = $conf->{static}->{path}->{workdir} . "/modules/HaCi/Tables/$dbType/$tableFileName.pm"; unless (-f $tableFile) { warn "Configured Table '$tableFileName' doesnt't exists? ($tableFile: $!)\n"; } my $hash = &getHashFromFile($tableFile); if (!$checkForce && exists $tableHashes->{$tableFileName} && $tableHashes->{$tableFileName} eq $hash) { &debug("Table $tableFileName is okay") if 0; } else { warn "Table $tableFileName NOT okay\n"; if ($modError) { &warnl($modError) unless $errorTold; $errorTold = 1; next; } next unless $conf->{user}->{misc}->{autoupgradedatabase}; unless (defined $conf->{var}->{TABLES}->{$tableFileName}) { warn "Cannot check Table '$tableFileName'. Tablemodule is not loaded!\n"; next; } my $table = $conf->{var}->{TABLES}->{$tableFileName}; my $changes = &diffTable($table); unless ($changes) { $tableHashes->{$tableFileName} = $hash; $bChanged = 1; } } } if ($bChanged) { &debug("Updating TableHashes..."); &setTableHashes($tableHashes); } } sub writeTmpFile { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $content = shift; my ($fh, $filename) = tempfile(); print $fh $content; close $fh; return $filename; } sub checkNetworkTable { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; &debug ("Checking Network table...\n"); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot check networks. DB Error (network)\n"; return 0; } unless ($networkTable->can('searchStr')) { warn "Cannot update missing network search strings, because the table network object is not up to date\n"; return 0; } my @missingNetworkSearchStrs = $networkTable->search(['ID', 'network'], {searchStr => ''}); my $cnter = 0; if (scalar @missingNetworkSearchStrs > 0) { warn '' . scalar @missingNetworkSearchStrs . " missing network search strings found. Updating...\n"; foreach (@missingNetworkSearchStrs) { my $networkEntry = $_; my $netID = $networkEntry->{ID}; my $networkDec = $networkEntry->{network}; my $ipv6 = 0; (undef, $networkDec, $ipv6) = &netID2Stuff($netID) unless $networkDec; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); if ($ipv6) { my ($ip, $cidr) = split(/\//, $network, 2); $ip = Net::IPv6Addr::to_string_compressed($ip); $network = join('/', $ip, $cidr); } $networkTable->searchStr($network); unless ($networkTable->update({ID => $netID})) { warn "Cannot update Net: " . $networkTable->errorStrs; } else { $cnter++; } } warn "Successfully updated $cnter networks\n"; } } sub checkNetworkACTable { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; &debug ("Checking Network Access Table...\n"); my $networkACTable = $conf->{var}->{TABLES}->{networkAC}; unless (defined $networkACTable) { warn "Cannot check ACL. DB Error (networkAC)\n"; return 0; } my $netAC = ($networkACTable->search(['ID', 'ACL'], {ACL => 4}))[0]; unless (defined $netAC) { &debug("Network Access Table out of date! Updating...\n"); my @netACs = $networkACTable->search(['*']); foreach (@netACs) { my $netID = &getNetID($_->{rootID}, $_->{network}); if ($netID) { $networkACTable->clear(); $networkACTable->netID($netID); unless ($networkACTable->update({ID => $_->{ID}})) { warn "Cannot update networkACTable: " . $networkACTable->errorStrs(); } } else { $networkACTable->clear(); unless ($networkACTable->delete({ID => $_->{ID}})) { warn "Cannot update networkACTable: " . $networkACTable->errorStrs(); } } } unless (defined $netAC) { $networkACTable->clear(); $networkACTable->netID(0); $networkACTable->groupID(0); $networkACTable->ACL(4); $networkACTable->insert(); if ($networkACTable->error()) { warn "Cannot update networkACTable: " . $networkACTable->errorStrs(); } } } } sub netv6Dec2ipv6ID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netv6Dec = shift; my $netv6 = &netv6Dec2net($netv6Dec); my ($netaddress, $cidr) = split/\//, $netv6; my $ipv6ID = Net::IPv6Addr::to_string_base85($netaddress) . sprintf("%x", $cidr); return $ipv6ID; } sub getPluginConfValues { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $netID = shift; my $pluginConf = {}; unless (defined $pluginID) { warn "updatePluginConf: No pluginID given...\n"; return {}; } unless (defined $netID) { warn "updatePluginConf: No netID given...\n"; return {}; } my $pluginConfTable = $conf->{var}->{TABLES}->{pluginConf}; unless (defined $pluginConfTable) { warn "Cannot update Plugin Config. DB Error (pluginConf)\n"; return 0; } my @pluginConfEntries = $pluginConfTable->search(['*'], {pluginID => $pluginID, netID => $netID}); my @pluginConfEntriesGlobal = $pluginConfTable->search(['*'], {pluginID => $pluginID, netID => -1}); foreach (@pluginConfEntries) { my $pluginConfEntry = $_; $pluginConf->{$pluginConfEntry->{name}} = $pluginConfEntry->{value}; } foreach (@pluginConfEntriesGlobal) { my $pluginConfEntry = $_; next if $pluginConfEntry->{value} eq ''; $pluginConf->{$pluginConfEntry->{name}} = $pluginConfEntry->{value} unless exists $pluginConf->{$pluginConfEntry->{name}} && $pluginConf->{$pluginConfEntry->{name}} ne ''; } return $pluginConf; } sub getPluginConfMenu { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $global = shift; my $netID = shift; my $plugin = &pluginID2Name($pluginID); my $pluginFilename = &pluginID2File($pluginID); my $pluginInfos = (&getPluginInfos($pluginFilename))[1]; my $pluginConfValues = &getPluginConfValues($pluginID, $netID); my $menu = []; my @confMenus = (); if ($global) { if ($pluginInfos->{RECURRENT}) { push @confMenus, @{$conf->{static}->{plugindefaultglobrecurrentmenu}}; if ($#{$pluginInfos->{GLOBMENURECURRENT}} > -1) { push @confMenus, ({type => 'hline'}), @{$pluginInfos->{GLOBMENURECURRENT}}; } } if ($pluginInfos->{ONDEMAND}) { push @confMenus, @{$conf->{static}->{plugindefaultglobondemandmenu}}; if ($#{$pluginInfos->{GLOBMENUONDEMAND}} > -1) { push @confMenus, ({type => 'hline'}), @{$pluginInfos->{GLOBMENUONDEMAND}}; } } } else { if ($pluginInfos->{RECURRENT}) { push @confMenus, @{$conf->{static}->{plugindefaultrecurrentmenu}}; if ($#{$pluginInfos->{MENURECURRENT}} > -1) { push @confMenus, ({type => 'hline'}), @{$pluginInfos->{MENURECURRENT}}; } } if ($pluginInfos->{ONDEMAND}) { push @confMenus, @{$conf->{static}->{plugindefaultondemandmenu}}; if ($#{$pluginInfos->{MENUONDEMAND}} > -1) { push @confMenus, ({type => 'hline'}), @{$pluginInfos->{MENUONDEMAND}}; } } } foreach (@confMenus) { my $entry = $_; # for compatibility reasons map { $entry->{uc($_)} = $entry->{$_}; } keys %{$entry}; my $label = { target => 'key', type => 'label', value => _gettext($entry->{DESCR} || ''), title => _gettext($entry->{HELP} || ''), }; if ($entry->{TYPE} eq 'textbox') { push @$menu, ( { elements => [ $label, { target => 'value', type => 'textfield', name => 'pluginConfName_' . $entry->{NAME}, size => $entry->{SIZE} || 20, maxlength => $entry->{MAXLENGTH} || 255, value => (exists $pluginConfValues->{$entry->{NAME}}) ? $pluginConfValues->{$entry->{NAME}} : ($entry->{VALUE} || ''), title => _gettext($entry->{HELP} || ''), }, ], } ); } elsif ($entry->{TYPE} eq 'hline') { push @$menu, ( { value => { type => 'hline', colspan => 2, }, }, ); } elsif ($entry->{TYPE} eq 'label') { push @$menu, ( { elements => [ { target => 'single', type => 'label', value => _gettext($entry->{VALUE}), title => _gettext($entry->{HELP} || ''), align => 'center', bold => 1, colspan => 2, } ], } ); } elsif ($entry->{TYPE} eq 'checkbox') { push @$menu, ( { elements => [ $label, { target => 'value', type => 'checkbox', name => 'pluginConfName_' . $entry->{NAME}, descr => '', value => 1, checked => (exists $pluginConfValues->{$entry->{NAME}}) ? $pluginConfValues->{$entry->{NAME}} : ($entry->{CHECKED} || 0), title => _gettext($entry->{HELP} || ''), }, ], } ); } elsif ($entry->{TYPE} eq 'popupmenu') { my $values = []; unless (ref $entry->{VALUE} eq 'ARRAY') { warn "$plugin: The Value Content for 'popupmenu' in 'Menu' has to be an Array Reference ([])\n"; $entry->{VALUE} = []; } foreach (@{$entry->{VALUE}}) { push @$values, { ID => $_, name => $_, } } push @$menu, ( { elements => [ $label, { target => 'value', type => 'popupMenu', name => 'pluginConfName_' . $entry->{NAME}, size => 1, values => $values, selected => (exists $pluginConfValues->{$entry->{NAME}}) ? $pluginConfValues->{$entry->{NAME}} : ($entry->{DEFAULT} || ''), }, ], } ); } } push @{$menu}, ( { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 2, buttons => [ { type => 'submit', name => 'submitPluginConfig', value => _gettext("Submit"), img => 'submit_small.png', }, { type => 'submit', name => 'abortPluginConfig', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ); return $menu; } sub mkPluginConfig { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $global = shift || 0; my $q = $HaCi::HaCi::q; my $pluginID = &getParam(1, undef, 'pluginID'); my $netID = &getParam(1, undef, 'netID'); my $pluginFilename = &pluginID2File($pluginID); my $pluginInfos = (&getPluginInfos($pluginFilename))[1]; $netID = -1 unless defined $netID; my @confMenus = (); if ($global) { if ($pluginInfos->{RECURRENT}) { push @confMenus, @{$conf->{static}->{plugindefaultglobrecurrentmenu}}, @{$pluginInfos->{GLOBMENURECURRENT}}; } if ($pluginInfos->{ONDEMAND}) { push @confMenus, @{$conf->{static}->{plugindefaultglobondemandmenu}}, @{$pluginInfos->{GLOBMENUONDEMAND}}; } } else { if ($pluginInfos->{RECURRENT}) { push @confMenus, @{$conf->{static}->{plugindefaultrecurrentmenu}}, @{$pluginInfos->{MENURECURRENT}}; } if ($pluginInfos->{ONDEMAND}) { push @confMenus, @{$conf->{static}->{plugindefaultondemandmenu}}, @{$pluginInfos->{MENUONDEMAND}}; } } foreach (@confMenus) { my $entry = $_; # for compatibility reasons map { $entry->{uc($_)} = $entry->{$_}; } keys %{$entry}; next unless exists $entry->{TYPE}; next if exists $entry->{NODB} && $entry->{NODB}; next if $entry->{TYPE} eq 'label' || $entry->{TYPE} eq 'hline'; my $value = &getParam(1, undef, 'pluginConfName_' . $entry->{NAME}); $value = '' unless defined $value; &updatePluginConf($pluginID, $netID, $entry->{NAME}, $value); } my $resetLastRun = &getParam(1, 0, 'pluginConfName_def_recurrent_resetLastRun'); $resetLastRun ||= &getParam(1, 0, 'pluginConfName_def_glob_recurrent_resetLastRun'); &updatePluginLastRun($pluginID, 0, 0, '') if $resetLastRun; } sub updatePluginConf { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $netID = shift; my $name = shift; my $value = shift; unless (defined $pluginID) { warn "updatePluginConf: No pluginID given...\n"; return 0; } unless (defined $netID) { warn "updatePluginConf: No netID given...\n"; return 0; } unless (defined $name) { warn "updatePluginConf: No name for plugin Config Entry given...\n"; return 0; } my $pluginConfTable = $conf->{var}->{TABLES}->{pluginConf}; unless (defined $pluginConfTable) { warn "Cannot update Plugin Config. DB Error (pluginConf)\n"; return 0; } my $pluginConfEntry = ($pluginConfTable->search(['ID'], {pluginID => $pluginID, netID => $netID, name => $name}))[0]; $pluginConfTable->clear(); $pluginConfTable->pluginID($pluginID); $pluginConfTable->netID($netID); $pluginConfTable->name($name); $pluginConfTable->value($value); if (defined $pluginConfEntry) { unless ($pluginConfTable->update({ID => $pluginConfEntry->{ID}})) { &warnl("Cannot update Plugin Configuration: " . $pluginConfTable->errorStrs()); } } else { $pluginConfTable->insert(); if ($pluginConfTable->error()) { &warnl("Cannot insert Plugin Configuration: " . $pluginConfTable->errorStrs()); } } } sub finalizeTables { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; &closeTable('user'); &closeTable('group'); &closeTable('root'); &closeTable('rootAC'); &closeTable('network'); &closeTable('networkV6'); &closeTable('networkAC'); &closeTable('networkPlugin'); &closeTable('networkLock'); &closeTable('networkTag'); &closeTable('template'); &closeTable('templateEntry'); &closeTable('templateValue'); &closeTable('plugin'); &closeTable('pluginConf'); &closeTable('pluginValue'); &closeTable('setting'); &closeTable('audit'); } sub initTables { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; &getTable('user'); &getTable('group'); &getTable('root'); &getTable('rootAC'); &getTable('network'); &getTable('networkV6'); &getTable('networkAC'); &getTable('networkPlugin'); &getTable('networkLock'); &getTable('networkTag'); &getTable('template'); &getTable('templateEntry'); &getTable('templateValue'); &getTable('plugin'); &getTable('pluginConf'); &getTable('pluginValue'); &getTable('setting'); &getTable('audit'); } sub initCache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netCache = undef; my $aclCache = undef; if (&getConfigValue('misc', 'disableCache')) { &debug("Cache disabled!"); return ($aclCache, $netCache); } eval { require Cache::FastMmap; }; if ($@) { warn "Cannot load Cache::FastMmap: $@. Trying Cache::FileCache...\n"; eval { require Cache::FileCache; }; if ($@) { warn "Cannot load Cache::FileCache: $@. => No Cache!\n"; } else { eval { $netCache = new Cache::FileCache({ namespace => 'HaCi_NET' }); $aclCache = new Cache::FileCache({ namespace => 'HaCi_ACL' }); }; if ($@) { warn "Something went wrong while initialising the Cache: $@\n"; } } } else { eval { $netCache = Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_NET' || '/tmp/HaCi.cache_NET', page_size => '4024k', num_pages => 3, ); unless (defined $netCache) { $netCache = Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_NET' || '/tmp/HaCi.cache_NET', init_file => 1, page_size => '4024k', num_pages => 3, ); } $aclCache = Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_ACL' || '/tmp/HaCi.cache_ACL', page_size => '4024k', num_pages => 3, ); unless (defined $aclCache) { $aclCache = Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_ACL' || '/tmp/HaCi.cache_ACL', init_file => 1, page_size => '4024k', num_pages => 3, ); } }; if ($@) { warn "Something went wrong while initialising the Cache: $@\n"; } } return ($aclCache, $netCache); } sub fillHoles { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $fromNetDec = shift; my $toNetDec = shift; my $ipv6 = shift; my $defSubnetSize = shift || 0; my $maxNetworks = shift || 0; my $checkNetworkLock = shift || 0; my $rootID = shift || 0; my $newNets = ($checkNetworkLock) ? undef : &getNetCacheEntry('FILL', 0, "$fromNetDec:$toNetDec:$ipv6:$defSubnetSize:$maxNetworks"); my @newNets = (); warn "From: " . (($ipv6) ? &netv6Dec2net($fromNetDec) : &dec2net($fromNetDec)) . "\n" if 0; warn "To: " . (($ipv6) ? &netv6Dec2net($toNetDec) : &dec2net($toNetDec)) . "\n" if 0; warn "SNS: $defSubnetSize\n" if 0; unless (defined $newNets) { my $fromIPDec = ($ipv6) ? (&netv6Dec2IpCidr($fromNetDec))[0] : &getIPFromDec($fromNetDec); my $endIPDec = ($ipv6) ? (&netv6Dec2IpCidr($toNetDec))[0] : &getIPFromDec($toNetDec); my ($startIP, $startCidr) = (); if ($ipv6) { ($startIP, $startCidr) = &netv6Dec2IpCidr($fromNetDec); } else { ($startIP, $startCidr) = split(/\//, &dec2net($fromNetDec)); } my $startIPDec = ($ipv6) ? $fromIPDec->copy() : $fromIPDec; my $newNetsCnter = 0; if ($ipv6) { while ($startIPDec->bcmp($endIPDec) < 0) { my $ipDiff = $endIPDec->copy()->bsub($startIPDec); my $exp = $ipDiff->blog(2); $exp = 128 - $defSubnetSize if $defSubnetSize > (128 - $exp); my $offset = Math::BigInt->new(2)->bpow($exp); last if $exp < 0; while ($exp > 0) { warn "EXP: $exp (" . (128 - $exp) . ")\n" if 0; last if ($startIPDec->copy()->bmod($offset) == 0) && ( (($startIPDec->bcmp($fromIPDec) == 0) && ((128 - $exp) > $startCidr)) || (!($startIPDec->bcmp($fromIPDec) == 0)) ); last if ($startIPDec->copy()->badd($offset)->bcmp($endIPDec) == 1); $offset = Math::BigInt->new(2)->bpow(--$exp); } my $cidr = 128 - $exp; if (!$defSubnetSize || ($defSubnetSize && ($cidr == $defSubnetSize))) { my $newNet = &ipv6DecCidr2netv6Dec($startIPDec, $cidr); warn "Found new network: " . &netv6Dec2net($newNet) . "\n" if 0; if (($checkNetworkLock && &checkNetworkLock($rootID, $newNet, $ipv6, 1)) || !$checkNetworkLock) { push @newNets, $newNet; $newNetsCnter++; last if ($maxNetworks && $newNetsCnter >= $maxNetworks) || $newNetsCnter > 10000; } } $startIPDec->badd(Math::BigInt->new(2)->bpow($exp)); } } else { while ($startIPDec < $endIPDec) { my $exp = int(log($endIPDec - $startIPDec)/log(2)); $exp = 32 - $defSubnetSize if $defSubnetSize > (32 - $exp); my $offset = 2 ** $exp; last if $exp < 0; while ($exp > 0) { warn "EXP: $exp (" . (32 - $exp) . ")\n" if 0; last if (($startIPDec % $offset) == 0) && ( (($startIPDec == $fromIPDec) && ((32 - $exp) > $startCidr)) || (!($startIPDec == $fromIPDec)) ); last if (($startIPDec + $offset) > $endIPDec); $offset = 2 ** --$exp; } my $cidr = 32 - $exp; if (!$defSubnetSize || ($defSubnetSize && ($cidr == $defSubnetSize))) { my $newNet = &net2dec(&dec2ip($startIPDec) . "/$cidr"); warn "Found new network: " . &dec2net($newNet) . "\n" if 0; if (($checkNetworkLock && &checkNetworkLock($rootID, $newNet, $ipv6, 1)) || !$checkNetworkLock) { push @newNets, $newNet; $newNetsCnter++; last if ($maxNetworks && $newNetsCnter >= $maxNetworks) || $newNetsCnter > 10000; } } $startIPDec += 2 ** $exp; } } &updateNetcache('FILL', 0, "$fromNetDec:$toNetDec:$ipv6:$defSubnetSize:$maxNetworks", \@newNets); } else { @newNets = @{$newNets}; } return @newNets; } sub convDatetime2time { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $datetime = shift; return 0 unless $datetime =~ /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/; my ($sec, $min, $hour, $mday, $mon, $year) = ($6, $5, $4, $3, $2, $1); $mon--; my $time = 0; eval { $time = timelocal($sec,$min,$hour,$mday,$mon,$year); }; if ($@) { warn "timelocal raised an error ($sec,$min,$hour,$mday,$mon,$year): $@\n"; return 0; } $time = 0 unless (defined $time && $time > 0); return $time; } sub getPluginValue { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = shift; my $netID = shift; my $name = shift; my $pluginValueTable = $conf->{var}->{TABLES}->{pluginValue}; unless (defined $pluginValueTable) { warn "Cannot get Plugin Value. DB Error (pluginValue)\n"; return 0; } my $entry = ($pluginValueTable->search(['value'], {pluginID => $ID, netID => $netID, name => $name}))[0]; if (defined $entry) { return $entry->{value}; } else { return ''; } } sub getHaCidInfo { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $data = {}; my $pidFile = $conf->{static}->{path}->{hacidpid}; return undef unless -f $pidFile; return undef unless open PID, $pidFile; my $PID = ()[0]; close PID; chomp($PID); return undef unless $PID; my $parent = (qx(ps --pid $PID -o "pid,pcpu,rss,etime" --no-header))[0]; my @childs = qx(ps --ppid $PID -o "pid,pcpu,rss,etime" --no-header); return undef unless $parent; $parent =~ s/^\s+//; ($data->{PARENT}->{PID}, $data->{PARENT}->{CPU}, $data->{PARENT}->{RSS}, $data->{PARENT}->{TIME}) = split/\s+/, $parent; foreach (@childs) { s/^\s+//; my ($pid, $cpu, $rss, $time) = split/\s+/; push @{$data->{CHILDS}}, { PID => $pid, CPU => $cpu, RSS => $rss, TIME => $time }; } return $data; } sub nd { my $net = shift; if ($net =~ /:/) { return &netv62Dec($net); } else { return &net2dec($net); } } sub dn { my $dec = shift; if (ref($dec)) { return &netv6Dec2net($dec); } else { return &dec2net($dec); } } sub getFreeSubnets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $scalar = shift || 0; my $subnetSize = shift || 0; my $amount = shift || 0; my $ignoreSubnet = shift || 0; # return also netaddress and broadcast (with v4) my $net = &getMaintInfosFromNet($netID); my $ipv6 = ($net->{ipv6ID}) ? 1 : 0; my $networkDec = $net->{network}; my $netaddressDec = ($ipv6) ? (&netv6Dec2IpCidr($networkDec))[0] : &getIPFromDec($networkDec); my $rootID = $net->{rootID}; my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); my $nextNetDec = ($ipv6) ? &netv6Dec2NextNetDec($broadcast, 128) : &net2dec(&dec2ip(&getIPFromDec($broadcast) + 1) . '/32'); my $defSubnetSize = $net->{defSubnetSize}; my $freeSubnetst = {}; $defSubnetSize = $subnetSize if defined $subnetSize; $amount = 0 if $amount == -1; my @childs = &getNetworkChilds($netID); my $startNet = $networkDec; foreach (&sortDBEntriesBy(\@childs, 'network', 1, $ipv6)) { my $child = $_; my $childDec = $child->{network}; my $childNet = ($ipv6) ? &netv6Dec2net($childDec) : &dec2net($childDec); foreach (&fillHoles($startNet, $childDec, $ipv6, $defSubnetSize, (($amount) ? ($amount + 1) : $amount), 1, $rootID)) { my $currNet = $_; my $network = ($ipv6) ? &netv6Dec2net($currNet) : &dec2net($currNet); my $cidr = (split/\//, $network, 2)[1]; my $bOK = 1; if (!$ignoreSubnet && !$ipv6 && $subnetSize eq (($ipv6) ? 128 : 32)) { my $currNetaddrDec = ($ipv6) ? &ipv6DecCidr2NetaddressV6Dec(&ipv62dec(&netv6Dec2ip($currNet)), $cidr) : &getNetaddress(&getIPFromDec($currNet), &getNetmaskFromCidr($cidr)); $bOK = 0 if $currNetaddrDec == $netaddressDec; my $currBroadcast = ($ipv6) ? &getV6BroadcastNet($currNet, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($currNet)) . '/32'); $bOK = 0 if $currBroadcast == $broadcast; } $freeSubnetst->{$currNet} = $network if $bOK && (!$defSubnetSize || ($cidr == $defSubnetSize)); } $startNet = ($ipv6) ? &netv6Dec2NextNetDec($childDec, 0) : &net2dec(&dec2ip(&getBroadcastFromNet($childDec) + 1) . '/0'); } foreach (&fillHoles($startNet, $nextNetDec, $ipv6, $defSubnetSize, (($amount) ? ($amount + 1) : $amount), 1, $rootID)) { my $currNet = $_; my $network = ($ipv6) ? &netv6Dec2net($currNet) : &dec2net($currNet); my $cidr = (split/\//, $network, 2)[1]; my $bOK = 1; if (!$ignoreSubnet && !$ipv6 && $subnetSize eq (($ipv6) ? 128 : 32)) { my $currNetaddrDec = ($ipv6) ? &ipv6DecCidr2NetaddressV6Dec(&ipv62dec(&netv6Dec2ip($currNet)), $cidr) : &getNetaddress(&getIPFromDec($currNet), &getNetmaskFromCidr($cidr)); $bOK = 0 if $currNetaddrDec == $netaddressDec; my $currBroadcast = ($ipv6) ? &getV6BroadcastNet($currNet, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($currNet)) . '/32'); $bOK = 0 if $currBroadcast == $broadcast; } $freeSubnetst->{$currNet} = $network if $bOK && ($currNet != $networkDec && (!$defSubnetSize || ($cidr == $defSubnetSize))); } return scalar keys %{$freeSubnetst} if $scalar; my @sorted = (); if ($ipv6) { @sorted = &ipv6Sort(keys %{$freeSubnetst}); } else { @sorted = sort {$a<=>$b} keys %{$freeSubnetst}; } my $cnter = 0; my @freeSubnets = (); foreach (@sorted) { my $networkDec = $_; my $network = $freeSubnetst->{$networkDec}; last if $amount && ++$cnter > $amount; push @freeSubnets, $network; }; if ($conf->{var}->{soap}) { @{$HaCi::GUI::init::t->{V}->{freeSubnets}} = map { { network => $_, rootName => &rootID2Name($rootID) }; } @freeSubnets; } return @freeSubnets; } sub chOwnPW { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $oldPW = &getParam(1, undef, 'oldPassword'); my $newPW = &getParam(1, undef, 'newPassword'); my $newPWVal = &getParam(1, undef, 'newPasswordVal'); my $session = $HaCi::HaCi::session; my $userName = $session->param('username') || ''; unless ($userName) { &warnl(_gettext('Sorry, Username not found!')); return 0; } my $user = &getUserFromName($userName); unless (exists $user->{ID}) { &warnl(_gettext('Sorry, Username not found!')); return 0; } my $origPW = $user->{password}; my $oldPWCrypt = &getCryptPassword($oldPW); unless ($oldPWCrypt eq $origPW) { &warnl(_gettext("Old Password is not correct!")); return 0; } unless ($newPW && $newPW) { &warnl("No Password given"); return 0; } if ($newPW ne $newPW) { &warnl("Passwords are not equal"); return 0; } my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { &warnl("Cannot save User. DB Error (user)"); } $userTable->clear(); my $crypt = &getCryptPassword($newPW); $userTable->password($crypt); my $userID = $user->{ID} || -1; if ($userID < 0) { &warnl(_gettext('Sorry, Username not found!')); return 0; } $userTable->modifyFrom($session->param('username')); $userTable->modifyDate(&currDate('datetime')); &debug("Change Password for User '$userName'\n"); unless ($userTable->update({ID => $userID})) { &warnl(sprintf(_gettext("Cannot change Password: %s"), $userTable->errorStrs())); return 0; } else { &warnl(_gettext("Successfully changed Password")); } return 1; } sub getSettings { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userID = shift; my $settings = {}; unless (defined $userID) { warn "No UserID given!\n"; return {}; } my $settingTable = $conf->{var}->{TABLES}->{setting}; unless (defined $settingTable) { &warnl("Cannot get settings. DB Error (setting)"); } my @settingsDB = $settingTable->search(['ID', 'param', 'value'], {userID => $userID}); foreach (@settingsDB) { my $setting = $_; push @{$settings->{$setting->{param}}}, $setting->{value}; } return $settings; } sub updSettings { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $newSettings = {}; my $userID = &userName2ID($s->param('username')); my $settings = &getSettings($userID); if ($userID < 0) { &warnl(_gettext('Sorry, Username not found!')); return 0; } my $settingTable = $conf->{var}->{TABLES}->{setting}; unless (defined $settingTable) { &warnl("Cannot set settings. DB Error (setting)"); } my @params = $q->param(); foreach (@params) { if (/^setting_(.*)$/) { my $param = $1; @{$newSettings->{$param}} = (&getParam(1, undef, $_)); } } my $settingParams = &getParam(0, [], 'settingParams'); foreach (@{$settingParams}) { my $param = $_; $newSettings->{$param} = [0] unless exists $newSettings->{$param}; } my $errors = ''; foreach (keys %{$settings}) { my $param = $_; unless (exists $newSettings->{$param}) { warn "Deleting Setting $param, because it wasn't selected!\n"; $settingTable->errorStrs(''); unless ($settingTable->delete({userID => $userID, param => $param})) { $errors .= $settingTable->errorStrs(); next; } } } foreach (keys %{$newSettings}) { my $param = $_; my @values = @{$newSettings->{$param}}; $settingTable->errorStrs(''); unless ($settingTable->delete({userID => $userID, param => $param})) { $errors .= $settingTable->errorStrs(); next; } foreach (@values) { my $value = $_; $settingTable->clear(); $settingTable->userID($userID); $settingTable->param($param); $settingTable->value($value); unless ($settingTable->insert()) { $errors .= "Cannot update Settings for '" . $s->param('username') . "': " . $settingTable->errorStrs(); next; } } } if ($errors) { &warnl(sprintf(_gettext("Errors while updating Setting for '%s': %s"), $s->param('username'), $errors)); } &updateSettingsInSession(); } sub updateSettingsInSession { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $s = $HaCi::HaCi::session; my $userID = &userName2ID($s->param('username')); my $settings = &getSettings($userID); $s->param('settings', $settings); $s->flush(); } sub quoteHTML { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $word = shift; return $word unless $word; if ($word =~ /&#\d+/) { $word = decode_entities($word); $word = encode('utf8', $word); } else { $word = encode('utf8', $word) unless ref(guess_encoding($word)); } $word =~ s/&/&/g; $word =~ s//>/g; $word =~ s/"/"/g; $word =~ s/'/'/g; return $word; } sub checkIfRootExists { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootName = shift; my $rootTable = $conf->{var}->{TABLES}->{root}; unless (defined $rootTable) { warn "Cannot add Route. DB Error (root)\n"; return 0; } my $root = ($rootTable->search(['ID'], {name => $rootName}))[0]; if (defined $root) { return 1; } else { return 0; } } sub flushACLCache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; $HaCi::HaCi::aclCache = {}; } sub flushNetCache { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; $HaCi::HaCi::netCache = {}; } sub getNetCacheEntry { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $rootID = shift; my $key = shift; my $value = $HaCi::HaCi::netCache->{$type}->{$rootID}->{$key} || undef; $conf->{var}->{CACHESTATS}->{$type}->{TOTAL}++; return $value; } sub tmplEntryDescr2EntryID { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tmplID = shift; my $tmplEntryDescr = shift; my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { warn "Cannot get TemplateEntryID. DB Error (templateEntry)\n"; return undef; } my $entry = ($tmplEntryTable->search(['ID'], {tmplID => $tmplID, description => $tmplEntryDescr}))[0]; return (defined $entry) ? ($entry->{ID} || undef) : undef; } sub searchAndGetFreeSubnets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $search = shift || ''; my $bLike = shift || 0; my $bFuzzy = shift || 0; my $bshNrOfFreeSubs = shift || 0; my $rootID = shift; my $state = shift; my $tmplID = shift; my $tmplBox = shift || {}; my $subnetSize = shift || 0; my $amount = shift || 0; my $q = $HaCi::HaCi::q; if (ref($q)) { $subnetSize = &getParam(1, -1, 'size'); $amount = &getParam(1, 3, 'amount'); } my $result = &search($search, $bLike, $bFuzzy, $bshNrOfFreeSubs, $rootID, $state, $tmplID, $tmplBox); my $t = $HaCi::GUI::init::t; $t->{V}->{freeSubnets} = []; my $returnFreeSubnets = []; my @freeSubnetst = (); if (defined $result && ref($result) eq 'ARRAY') { my $counter = 0; foreach (@{$result}) { my $network = $_; my ($rootID, $networkDec, undef) = &netID2Stuff($network->{netID}); if ($subnetSize == -1) { $subnetSize = ($network->{network} =~ /:/) ? 128 : 32; } my @freeSubnets = &getFreeSubnets($network->{netID}, 0, $subnetSize, $amount); next unless @freeSubnets; foreach (@freeSubnets) { push @freeSubnetst, {rootID => $rootID, network => $_}; last if ++$counter >= $amount; } last if ++$counter >= $amount; } map { my $ipv6 = ($_ =~ /:/) ? 1 : 0; my $dec = ($ipv6) ? &netv62Dec($_->{network}) : &net2dec($_->{network}); my $rootID = $_->{rootID}; push @{$returnFreeSubnets}, { network => $_->{network}, url => "$conf->{var}->{thisscript}?func=addNet&netID=0&rootID=$rootID&networkDec=$dec&fillNet=1", dec => $dec, rootID => $rootID, rootName => &rootID2Name($rootID) }; } @freeSubnetst; $t->{V}->{freeSubnets} = $returnFreeSubnets; map {$_->{rootName} = "eHTML($_->{rootName})} @{$t->{V}->{freeSubnets}}; if (scalar @{$returnFreeSubnets} == 0) { $t->{V}->{noSearchResult} = 1; $t->{V}->{'gettext_nothing_found'} = _gettext("No free networks found"); } } return $returnFreeSubnets; } sub checkStateRules { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $rootID = shift; my $networkDec = shift; my $state = shift; my $cidr = shift; my $ipv6 = shift; &debug("Checking Network State ($rootID:" . &dn($networkDec) . " => $state)\n") if 0; my $states = $conf->{static}->{misc}->{networkstates}; foreach (@$states) { if ($state eq $_->{id}) { my $se = $_; my $stateName = &networkStateID2Name($state); if (exists $se->{minsize} && $cidr > $se->{minsize}) { &warnl(sprintf(_gettext("The minimum %s size is /%i!"), $stateName, $se->{minsize})); return 0; } if (exists $se->{parents}) { my $nok = 1; my $netParent = &getNetworkParentFromDB($rootID, $networkDec); if (defined $netParent) { my @parents = split(/\s*,\s*/, $se->{parents}); foreach (@parents) { $nok = 0 if $_ == $netParent->{state}; } if ($nok) { &warnl(sprintf(_gettext("%ss can only be made from allocations with a status of %s"), $stateName, join(' ' . _gettext('or') . ' ', map {&networkStateID2Name($_)} @parents))); return 0; } } } if (exists $se->{banparents}) { my @basnishedStates = split(/\s*,\s*/, $se->{banparents}); if (my $badNet = &checkbanishFromParents($rootID, $networkDec, \@basnishedStates)) { &warnl(sprintf(_gettext("%ss can only be made when there's no less specific inetnum (%s) with a status of '%s'"), $stateName, &dn($badNet->{network}), join(' ' . _gettext('or') . ' ', map {&networkStateID2Name($_)} @basnishedStates))); return 0; } } if (exists $se->{banish}) { my @basnishedStates = split(/\s*,\s*/, $se->{banish}); if (my $badNet = &checkbanishFromParents($rootID, $networkDec, \@basnishedStates)) { &warnl(sprintf(_gettext("%ss can only be made when there's no less specific inetnum (%s) with an 'Assigned' status"), $stateName, &dn($badNet->{network}))); return 0; } if ($netID) { if (my $badNet = &checkbanishFromChilds($netID, \@basnishedStates)) { &warnl(sprintf(_gettext("%ss can only be made when there's no more specific inetnum (%s) with an 'Assigned' status"), $stateName, &dn($badNet->{network}))); return 0; } } } if (exists $se->{ipv6} && !$ipv6) { &warnl(sprintf(_gettext("%s is a new Value for inet6num Objects and so it's not available for inetnum Objects"), $stateName)); return 0; } return 1; } } &warnl("State id '$state' doesn't exists in Config!", 1); return 0; } sub checkbanishFromParents { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $basnishedStates = shift; my $parent = &getNetworkParentFromDB($rootID, $networkDec); return 0 unless defined $parent; foreach (@{$basnishedStates}) { return $parent if $parent->{state} == $_; } return &checkbanishFromParents($rootID, $parent->{network}, $basnishedStates); } sub checkbanishFromChilds { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $basnishedStates = shift; my @childs = &getNetworkChilds($netID, 0, 0); return 0 if $#childs == -1; foreach (@childs) { my $child = $_; foreach (@{$basnishedStates}) { return $child if $child->{state} == $_; } my $result = &checkbanishFromChilds($child->{ID}, $basnishedStates); return $result if $result; } return 0; } sub getParam { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $onlyScalar = shift; my $ifUndef = shift; my $paramName = shift; my $urlEncode = shift || 0; return $ifUndef unless defined $paramName; my @values = $q->param($paramName); unless (defined $values[0]) { return $ifUndef if $onlyScalar; return [] if $ifUndef =~ /^0$/; # return empty list if onlyScalar != 1 && ifUndef == 0 return $ifUndef; } if ($onlyScalar) { &debug("called function 'getParam' on parameter '$paramName' with option 'onlyScalar' AND more than one value! (" . (caller(0))[0] . '->' . (caller(0))[2] . ')') if $#values > 0; return "eHTML($values[0]) if $urlEncode; return $values[0]; } return \@values; } sub tableContent2Str { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $tableObject = shift; my $returnStr = ''; foreach (sort keys %{$tableObject->{COLOUMNS}}) { next if $_ eq 'createFrom' || $_ eq 'createDate' || $_ eq 'modifyFrom' || $_ eq 'modifyDate' || $_ eq 'ID'; $returnStr .= ';' if $returnStr; $returnStr .= $_ . '=' . ($tableObject->{COLOUMNS}->{$_} || '') } return $returnStr; } sub audit { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $action = shift; my $object = shift; my $value = shift || ''; my $error = shift || ''; my $global = shift || 0; my $s = $HaCi::HaCi::session; my $username = $s->param('username'); my $groupIDs = $s->param('groupIDs'); $groupIDs = '*,' if $global; $groupIDs =~ s/[^\d,\*]//g; my $auditTable = $conf->{var}->{TABLES}->{audit}; unless (defined $auditTable) { warn "Cannot add audit log. DB Error (audit)\n"; return 0; } $auditTable->clear(); $auditTable->ts(&currDate('datetime')); $auditTable->username($username); $auditTable->accessGroups(',' . $groupIDs); $auditTable->action($action); $auditTable->object($object); $auditTable->value($value); $auditTable->error($error); unless ($auditTable->insert()) { my $errStr = "Cannot insert new audit log: " . $auditTable->errorStrs; &warnl($errStr, 1); return 0; } return 1; } sub getAuditLogs { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $limit = shift || 100; my $page = shift || 0; my $sortBy = shift || 'ts'; my $bReverse = shift || 0; my $searchFor = shift || 0; my $quoteHTML = shift || 0; my $offset = $limit * $page; my $s = $HaCi::HaCi::session; my $auditTable = $conf->{var}->{TABLES}->{audit}; unless (defined $auditTable) { warn "Cannot get audit log. DB Error (audit)\n"; return 0; } $searchFor =~ s/['"]//g; my $query = 'WHERE 1=1'; if ($searchFor) { my $tsCol = $auditTable->meth2Col('ts'); my $usernameCol = $auditTable->meth2Col('username'); my $actionCol = $auditTable->meth2Col('action'); my $objectCol = $auditTable->meth2Col('object'); my $valueCol = $auditTable->meth2Col('value'); my $dbType = &getConfigValue('db', 'dbtype'); $query .= qq{ AND (}; if ($dbType eq 'postgresql') { $query .= qq{CAST($tsCol AS text) like '%$searchFor%' OR }; } else { $query .= qq{$tsCol like '%$searchFor%' OR }; } $query .= qq{$usernameCol like '%$searchFor%' OR $actionCol like '%$searchFor%' OR $objectCol like '%$searchFor%' OR $valueCol like '%$searchFor%')}; } my $accGrtCol = $auditTable->meth2Col('accessGroups'); unless ($s->param('bAdmin')) { $query .= " AND ($accGrtCol like '%,*,%'"; my $groupIDs = $s->param('groupIDs'); foreach (split(/, /, $groupIDs)) { s/\D//g; $query .= " OR $accGrtCol like '%,$_,%'" } $query .= ')'; } my $queryC = $query; my $sortCol = $auditTable->meth2Col($sortBy); my $dir = (!$bReverse) ? 'DESC' : 'ASC'; $query .= " ORDER BY $sortCol $dir LIMIT $limit OFFSET $offset"; my @auditLogsT = $auditTable->search(['*'], {}, 0, $query); my $sorted = {}; foreach (@auditLogsT) { my $audit = $_; if ($quoteHTML) { foreach (qw(object value username error)) { $audit->{$_} = "eHTML($audit->{$_}); } } push @{$sorted->{$_->{$sortCol}}}, $audit; } my @auditLogs = (); my @sortKeys = (!$bReverse) ? reverse sort keys %{$sorted} : sort keys %{$sorted}; foreach (@sortKeys) { push @auditLogs, @{$sorted->{$_}}; } my $totalAuditLogs = $auditTable->count({}, 0, $queryC); return (\@auditLogs, $totalAuditLogs); } sub checkNetworkLock { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $ipv6 = shift; my $quiet = shift || 0; my $bOK = 1; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); &debug("Checking network lock for '$network'"); my $networkLockTable = $conf->{var}->{TABLES}->{networkLock}; unless (defined $networkLockTable) { my $err = "Cannot check network lock. DB Error (networkLock)"; &warnl($err); return 0; } my $networkPrefix = 0; my $hostPart = 0; my $cidr = 0; if ($ipv6) { ($networkPrefix, $hostPart, $cidr) = &HaCi::Mathematics::netv6Dec2PartsDec($networkDec); } else { ($networkPrefix, $cidr) = ($networkDec, &HaCi::Mathematics::getCidrFromDec($networkDec)); } my @networkLocks = $networkLockTable->search(); foreach (@networkLocks) { my $networkLockDB = $_; my $networkLockID = $networkLockDB->{ID}; my $duration = $networkLockDB->{duration}; my $cRootID = $networkLockDB->{rootID}; my $cNetworkPrefix = $networkLockDB->{networkPrefix}; my $cHostPart = $networkLockDB->{hostPart}; my $cCidr = $networkLockDB->{cidr}; my $cIPv6 = $networkLockDB->{ipv6}; my $start = &convDatetime2time($networkLockDB->{ts}); my $till = $start + $duration; my $tillStr = scalar localtime($start + $duration); my $still = $till - time; my $cNetwork = ($ipv6) ? &netv6Dec2net(&ipv6Parts2NetDec($cNetworkPrefix, $cHostPart, $cCidr)) : &dec2net($cNetworkPrefix); if ((time - $till) < 0) { if ($networkPrefix eq $cNetworkPrefix && $hostPart eq $cHostPart && $cidr == $cCidr && $rootID eq $cRootID && $ipv6 == $cIPv6) { &warnl(sprintf(_gettext("Sorry, the network '%s' is still locked for '%i' seconds till '%s'!"), $cNetwork, $still, $tillStr)) unless $quiet; $bOK = 0; } } else { &debug("Locking for network '$cNetwork' is expired (" . scalar localtime() . " > $tillStr)"); &removeNetworkLock($networkLockID); } } return $bOK; } sub removeNetworkLock { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $networkLockID = shift; my $networkLockTable = $conf->{var}->{TABLES}->{networkLock}; unless (defined $networkLockTable) { my $err = "Cannot remove network lock. DB Error (networkLock)"; &warnl($err); return 0; } &debug("Deleting network lock '$networkLockID'"); unless ($networkLockTable->delete({ID => $networkLockID})) { &warnl(sprintf(_gettext("Error while deleting network lock '%i': %s"), $networkLockID, $networkLockTable->errorStrs)); } } sub exportSubnets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $ID = shift; my $bRootID = shift || 0; my $name = 'NONAME'; my $exportData = {}; my @childs = (); if ($bRootID) { my $rootID = $ID; my $details = &getMaintInfosFromRoot($rootID); my $rootName = $details->{name} || ''; $name = $rootName; unless (&checkRootACL($rootID, 'r')) { &warnl(sprintf(_gettext("Not enough rights to export this Root '%s'"), $rootName), 0); return 0; } @childs = &getNetworkChilds($rootID, 1, 0); } else { my $netID = $ID; my $details = &getMaintInfosFromNet($netID); my $network = ($details->{ipv6}) ? &netv6Dec2net($details->{network}) : &dec2net($details->{network}); $name = $network; unless (&checkNetACL($netID, 'r')) { &warnl(sprintf(_gettext("Not enough rights to export this Network '%s'"), $network), 0); return 0; } @childs = &getNetworkChilds($netID, 0, 0); } my $columns = {}; foreach (@childs) { my $child = $_; my $details = &getMaintInfosFromNet($child->{ID}); my $tmplData = &getTemplateData($child->{ID}, $details->{tmplID}, 1); my $network = ($details->{ipv6}) ? &netv6Dec2net($child->{network}) : &dec2net($child->{network}); unless (&checkNetACL($child->{ID}, 'r')) { $exportData->{$child->{network}} = {'01_network' => $network, '02_description' => _gettext("[ permission denied ]")}; next; } my $childData = { '01_network' => $network, '02_description' => $details->{description}, '03_state' => &networkStateID2Name($details->{state}), '04_createFrom' => $details->{createFrom}, '05_createDate' => $details->{createDate}, '06_modifyFrom' => $details->{modifyFrom}, '07_modifyDate' => $details->{modifyDate}, '08_defSubnetSize' => $details->{defSubnetSize}, '09_tags' => join(' ', @{$details->{tags}}), }; map {$columns->{$_} = 1} keys %{$childData}; if ($details->{tmplID}) { my $tmplName = &tmplID2Name($details->{tmplID}); foreach (keys %{$tmplData}) { my $key = $_; my $value = $tmplData->{$key}; my $colName = $tmplName . '_' . $key; $value =~ s/[\n\r]+//g; $childData->{$colName} = $value; $columns->{$colName} = 1; } } $exportData->{$child->{network}} = $childData; } print $HaCi::HaCi::q->header( -type => 'text/csv', -'Content-Disposition' => 'attachment; filename=HaCi_' . $name . '_export.csv' ); my $csv = Text::CSV->new ( { binary => 1 } ) or die "Cannot use CSV: ".Text::CSV->error_diag (); $csv->combine(map {s/^0\d_//; $_} sort keys %{$columns}); print $csv->string() . "\n"; foreach (sort {$a<=>$b} keys %{$exportData}) { my $network = $_; $csv->combine(map {$exportData->{$network}->{$_}} sort keys %{$columns}); print $csv->string() . "\n"; } exit 0; } sub getAvailTags { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $networkTagTable = $conf->{var}->{TABLES}->{networkTag}; unless (defined $networkTagTable) { warn "Cannot get network tags. DB Error (networkTag)"; return []; } my @tagsDB = $networkTagTable->search(['tag']); my $tags = {}; foreach (@tagsDB) { $tags->{$_->{tag}} = $_->{ID} unless exists $tags->{$_->{tag}}; } my @return = keys %{$tags}; return \@return; } sub getTags { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netID = shift; my $networkTagTable = $conf->{var}->{TABLES}->{networkTag}; unless (defined $networkTagTable) { warn "Cannot get network tags. DB Error (networkTag)"; return []; } my @tagsDB = $networkTagTable->search(['tag'], {netID => $netID}); my $tags = {}; foreach (@tagsDB) { $tags->{$_->{tag}} = $_->{ID} unless exists $tags->{$_->{tag}}; } my @return = keys %{$tags}; return \@return; } 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/HaCi/XMLRPC.pm0000644000175000000000000010055112470236743014673 0ustar fighterrootpackage HaCi::XMLRPC; use strict; use warnings; use Frontier::RPC2; use Data::Dumper; use Encode; use File::Basename; use Digest::MD5 qw/md5_hex/; use Time::HiRes qw/usleep/; use HaCi::Conf; use HaCi::HaCi; use HaCi::Utils; use HaCi::Mathematics; use HaCi::Log qw/warnl debug/; =head1 Class XMLRPC This class provides a XML-RPC API for HaCi. Basic usage: my $api = new Frontier::Client(url => "http://$server/RPC2"); my $session = $api->call('login', [$user, $pass]); die 'Login failed!' unless $session; print Dumper($api->call($method, $session, @args)); $api->call('logout', $session); exit 0; =cut my $aclCacheHandle = undef; my $netCacheHandle = undef; my $lockName = undef; $SIG{__DIE__} = sub {&releaseLock($lockName) if defined $lockName}; my $methods = { search => \&search, getFreeSubnets => \&getFreeSubnets, getFreeSubnetsFromSearch => \&getFreeSubnetsFromSearch, assignFreeSubnet => \&assignFreeSubnet, getSubnets => \&getSubnets, getNetworkDetails => \&getNetworkDetails, listRoots => \&listRoots, addRoot => \&addRoot, editRoot => \&editRoot, delRoot => \&delRoot, addNet => \&addNet, editNet => \&editNet, delNet => \&delNet, login => \&login, logout => \&logout, }; sub handler { my $r = shift; my $coder = Frontier::RPC2->new(); (my $workDir = dirname( __FILE__ )) =~ s#/modules/HaCi##; &HaCi::Conf::init($workDir); my $content = ''; $r->read($content, $r->headers_in->get('Content-Length')); my $response = ''; eval { $response = &serve($coder, $content); }; if ($@) { $r->content_type("text/xml"); $r->print(encode("UTF-8", $coder->encode_fault(1, 'Error: ' . $@))); return Apache2::Const::SERVER_ERROR; } else { #$r->content_type("application/json"); $r->content_type("text/xml"); $r->print(encode("UTF-8", $response)); return Apache2::Const::OK; } } sub finalize { if (defined $netCacheHandle) { warn "Cannot set Cache (netCache-DB!)\n" unless $netCacheHandle->set('DB', $HaCi::HaCi::netCache->{DB}); warn "Cannot set Cache (netCache-FILL!)\n" unless $netCacheHandle->set('FILL', $HaCi::HaCi::netCache->{FILL}); warn "Cannot set Cache (netCache-NET!)\n" unless $netCacheHandle->set('NET', $HaCi::HaCi::netCache->{NET}); } if (defined $aclCacheHandle) { warn "Cannot set Cache (aclCache!)\n" unless $aclCacheHandle->set('HASH', $HaCi::HaCi::aclCache); } $HaCi::HaCi::session->flush(); } sub init { &HaCi::Utils::getConfig(); ($aclCacheHandle, $netCacheHandle) = &HaCi::Utils::initCache(); if (defined $netCacheHandle) { $HaCi::HaCi::netCache->{DB} = $netCacheHandle->get('DB'); $HaCi::HaCi::netCache->{NET} = $netCacheHandle->get('NET'); $HaCi::HaCi::netCache->{FILL} = $netCacheHandle->get('FILL'); } else { $HaCi::HaCi::netCache->{DB} = {}; $HaCi::HaCi::netCache->{NET} = {}; $HaCi::HaCi::netCache->{FILL} = {}; } $HaCi::HaCi::aclCache = (defined $aclCacheHandle) ? $aclCacheHandle->get('HASH') : {}; &HaCi::Utils::initTables(); } sub serve { my $coder = shift; my $xml = shift; $xml =~ s/(<\?XML\s+VERSION)/\L$1\E/; my $call; eval { $call = $coder->decode($xml) }; if ($@) { return $coder->encode_fault(1, "error decoding RPC.\n" . $@); } if ($call->{'type'} ne 'call') { return $coder->encode_fault(2,"expected RPC \`methodCall', got \`$call->{'type'}'\n"); } my $method = $call->{'method_name'}; if (!defined $methods->{$method}) { return $coder->encode_fault(3, "no such method \`$method'\n"); } &init(); return $coder->encode_fault(7, "error executing RPC \`$method'.\n" . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}})) if $#{$HaCi::Conf::conf->{var}->{warnl}} > -1; my @values = @{$call->{'value'}}; my $authInfo = shift @values; return $coder->encode_fault(7, "Authentication error: " . $HaCi::Conf::conf->{var}->{authenticationError}) unless &authenticate($authInfo); my $result; eval { $result = &{$methods->{$method}}(@values); }; if ($@) { return $coder->encode_fault(4, "error executing RPC \`$method'.\n" . $@); } my $response_xml = $coder->encode_response($result); &finalize(); return $response_xml; } sub authenticate { my $authInfo = shift; my $user = undef; my $pass = undef; my $sessionUser = undef; my $bLogin = 0; if (ref($authInfo) eq 'ARRAY') { &debug("Authentiating via username and password"); $user = ${$authInfo}[0]; $pass = ${$authInfo}[1]; $HaCi::HaCi::session = &HaCi::HaCi::getSession(); $bLogin = 1; } elsif (ref($authInfo) eq '') { &debug("Authentiating via sessionID: " . $authInfo); $HaCi::HaCi::session = &HaCi::HaCi::getSession($authInfo); $sessionUser = $HaCi::HaCi::session->param('username'); } my $auth = &HaCi::HaCi::authentication($user, $pass, $bLogin, $sessionUser); if ($auth) { &HaCi::Utils::getRights(); } return $auth; } =over =item login() Login and return your session Params Returns - session [string] session token you can use for authentication =cut sub login { &debug("Login: return session id"); return $HaCi::HaCi::session->id(); } =item logout() Logout Params Returns =cut sub logout { &debug("Logout: delete session"); $HaCi::HaCi::session->delete(); $HaCi::HaCi::session->flush(); return 1; } =item search() Search networks Params - search [string] string to search for - state [string] filter the result by that network state (ALLOCATED PA, ASSIGNED, ...) - exact [string] don't do a substring search - template name [string] filter the result by that template name - template query [hash] filter the result by defining special values for template entries (i.e.: {name => 'POOL1'}) - root name [string] limit the search in that root - nrOfFreeSubs [boolean] compute the number of free subnets in each found network - withDetails [boolean] show details for found networks - tags [string] return only networks with this tags, seperate with spaces (i.e.: 'OR foo bar', 'AND foo bar') Returns - networks [array] array of found network blocks (hash) - netID - network - rootName - description - state - nrOfFreeSubs Example my $networks = search('Test', 'ASSIGNED', 0, 'DSL-POOL', {name => 'Pool1'}, 'DSL-Test-Pool-Root', 0); =cut sub search { my $searchStr = shift || ''; my $state = shift || ''; my $exact = shift || 0; my $tmplName = shift || ''; my $tmplQuery = shift || {}; my $rootName = shift || ''; my $shNrOfFree = shift || 0; my $withDetails = shift || 0; my $tagsT = shift || ''; $tagsT =~ s/^\s+//; $tagsT =~ s/\s+$//; my @tags = split(/\s+/, $tagsT); my $tagOp = ($tagsT ne '') ? shift @tags : 0; if ($tagsT ne '' && scalar @tags == 0) { @tags = ($tagOp); $tagOp = 'OR'; } &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('search'); my $tmplID = ($tmplName ne '') ? &HaCi::Utils::tmplName2ID($tmplName) : -1; unless (defined $tmplID) { &daw("No such Template found! ($tmplName)\n"); } my $rootID = ($rootName ne '') ? &HaCi::Utils::rootName2ID($rootName) : -1; unless ($rootID) { &daw("No such root found! ($rootName)\n"); } $rootID = 0 if $rootID == -1; my $tmplBox = {}; if ($tmplID != -1) { foreach (keys %{$tmplQuery}) { my $key = $_; my $value = $tmplQuery->{$key}; my $tmplEntryID = &HaCi::Utils::tmplEntryDescr2EntryID($tmplID, $key); next unless defined $tmplEntryID; $tmplBox->{$tmplEntryID} = $value; } } my $stateID = -1; if ($state ne '') { $stateID = &HaCi::Utils::networkStateName2ID($state); &daw("State not known! ($state)\nAvailable states: " . join(', ', map {$_->{name}} @{$HaCi::Conf::conf->{static}->{misc}->{networkstates}}) . "\n") unless $stateID; } my $networks = &HaCi::Utils::search( $searchStr, ($exact) ? 0 : 1, 0, $shNrOfFree, $rootID, $stateID, $tmplID, $tmplBox, \@tags, $tagOp, ); map { delete $_->{url}; delete $_->{nrOfFreeSubs} unless $shNrOfFree; if ($withDetails) { my $orig = $_; $_ = &getNetworkDetails($_->{rootName}, $_->{network}); $_->{netID} = $_->{ID}; $_->{tmplName} = &HaCi::Utils::tmplID2Name($_->{tmplID}); $_->{rootName} = $rootName; $_->{nrOfFreeSubs} = $orig->{nrOfFreeSubs} if $shNrOfFree; delete $_->{ipv6ID}; delete $_->{ID}; delete $_->{tmplID}; delete $_->{rootID}; } } @{$networks}; return $networks; } =item getFreeSubnets() Find free subnets in a specified root and supernet and return the wanted amount of subnets Params - root name [string] search free subnets in that root - supernet [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120) - cidr [integer] specify the target cidr of your subnets (e.g. 30) - amount [integer] how many subnets you want to get Returns - networks [array] array of free subnets found (hash) - network Example my $freeSubnets = getFreeSubnets('Test root', '10.184.120.0/23', 32, 1); =cut sub getFreeSubnets { my $rootName = shift; my $network = shift; my $size = shift || 0; my $amount = shift || 0; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('search'); &daw("You have to provide a root name!\n") unless $rootName; my $rootID = &HaCi::Utils::rootName2ID($rootName); unless ($rootID) { &daw("No such Root found! ($rootName)\n"); return; } &daw("You have to provide a network from which you want to get a free subnet!\n") unless $network; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &HaCi::Mathematics::netv62Dec($network) : &HaCi::Mathematics::net2dec($network); my $ipv6ID = ($ipv6) ? &HaCi::Utils::netv6Dec2ipv6ID($networkDec) : ''; my $netID = &HaCi::Utils::getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &daw("No such network found! ($network)\n"); return; } unless (&HaCi::Utils::checkNetACL($netID, 'r')) { &daw("Not enouph permissions to read this Network\n"); return; } my @freeSubnets = &HaCi::Utils::getFreeSubnets($netID, 0, $size, $amount || 1); return \@freeSubnets; } =item getFreeSubnetsFromSearch() Search networks and return free subnets from them Params - search [string] string to search for - state [string] filter the result by that network state (ALLOCATED PA, ASSIGNED, ...) - exact [string] don't do a substring search - template name [string] filter the result by that template name - template query [hash] filter the result by defining special values for template entries (i.e.: {name => 'POOL1'}) - root name [string] limit the search in that root - cidr [integer] specify the target cidr of your subnets (e.g. 30) - amount [integer] how many subnets you want to get Returns - networks [array] array of free subnets found (hash) - network - root name Example my $freeSubnets = getFreeSubnetsFromSearch('search me', '', 1, '', {}, 'Test-Root', 29, 3); =cut sub getFreeSubnetsFromSearch { my $searchStr = shift || ''; my $state = shift || ''; my $exact = shift || 0; my $tmplName = shift || ''; my $tmplQuery = shift || {}; my $rootName = shift || ''; my $size = shift || 0; my $amount = shift || 0; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('search'); &daw("You have to provide a search string!\n") unless $searchStr; my $tmplID = ($tmplName ne '') ? &HaCi::Utils::tmplName2ID($tmplName) : -1; unless (defined $tmplID) { &daw("No such Template found! ($tmplName)\n"); } my $rootID = ($rootName ne '') ? &HaCi::Utils::rootName2ID($rootName) : -1; unless ($rootID) { &daw("No such root found! ($rootName)\n"); } $rootID = 0 if $rootID == -1; my $tmplBox = {}; if ($tmplID > -1) { foreach (keys %{$tmplQuery}) { my $key = $_; my $value = $tmplQuery->{$key}; my $tmplEntryID = &HaCi::Utils::tmplEntryDescr2EntryID($tmplID, $key); next unless defined $tmplEntryID; $tmplBox->{$tmplEntryID} = $value; } } my $stateID = -1; if ($state ne '') { $stateID = &HaCi::Utils::networkStateName2ID($state); &daw("State not known! ($state)\nAvailable states: " . join(', ', map {$_->{name}} @{$HaCi::Conf::conf->{static}->{misc}->{networkstates}}) . "\n") unless $stateID; } my $networks = &HaCi::Utils::searchAndGetFreeSubnets( $searchStr, ($exact) ? 0 : 1, 0, 0, $rootID, $stateID, $tmplID, $tmplBox, $size, $amount ); map { delete $_->{url}; delete $_->{dec}; delete $_->{rootID}; } @{$networks}; return $networks; } =item listRoots() Add a root to HaCi Params Returns - roots [array] array of found roots (hash) - ID - name - ipv6 (boolean) =cut sub listRoots { &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('showRoots'); my $roots = HaCi::Utils::getRoots(); return $roots } =item addRoot() Add a root to HaCi Params - root name [string] add a root with this name - description [string] description of the root - ipv6 [boolean] this root contains IPv6 networks Returns - success [string] 0 on success, error-String at error =cut sub addRoot { my $rootName = shift || ''; my $descr = shift || ''; my $ipv6 = shift || 0; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('addRoot'); my $success = &HaCi::Utils::addRoot( $rootName, $descr, $ipv6, ); my $error = ($success) ? '' : "error adding a new root '$rootName': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item editRoot() Edit an existing root Params - root name [string] edit this root - new root name [string] change root name - description [string] new description of the root Returns - success [string] 0 on success, error-String at error =cut sub editRoot { my $rootName = shift || ''; my $newRootName = shift || ''; my $description = shift || ''; $newRootName = $rootName if $newRootName eq ''; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('editRoot'); &daw("No root name committed!\n") unless $rootName; my $rootID = &HaCi::Utils::rootName2ID($rootName); unless ($rootID) { &daw("No such root found! ($rootName)\n"); } my $root = &HaCi::Utils::getMaintInfosFromRoot($rootID); $description = $root->{description} if $description eq ''; my $success = &HaCi::Utils::addRoot( $newRootName, $description, $root->{ipv6}, $rootID, 1, ); my $error = ($success) ? '' : "error while modifying this root '$rootName': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item delRoot() Delete a root from HaCi Params - root name [string] remove the root Returns - success [string] 0 on success, error-String at error =cut sub delRoot { my $rootName = shift || ''; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('editRoot'); &daw("No root name committed!\n") unless $rootName; my $rootID = &HaCi::Utils::rootName2ID($rootName); unless ($rootID) { &daw("No such root found! ($rootName)\n"); } my $success = &HaCi::Utils::delRoot($rootID); my $error = ($success) ? '' : "error removing this root '$rootName': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item addNet() Add a network to HaCi Params - root name [string] add the network to this root - network [network] add this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - description [string] description of the network - state [string] state of the network (e.g. ALLOCATED PA, ASSIGNED, ...) - def subnet Size [integer] define a default subnet cidr size (e.g. 30) - template name [string] assign a template to the network - template values [hash] pass template values (e.g. {hostname => 'abc.de', os => 'redhat'}) - tags [string] add tags (seperate by comma) Returns - success [string] 0 on success, error-String at error =cut sub addNet { my $rootName = shift || ''; my $network = shift || ''; my $descr = shift || ''; my $state = shift || ''; my $defSubSize = shift || 0; my $tmplName = shift || ''; my $tmplValues = shift || {}; my $tags = shift || ''; my $isIPv4 = qr/(\d{1,3}\.){3}\d{1,3}/i; my $isIPv6 = qr/(((?=(?>.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?{name}} @{$HaCi::Conf::conf->{static}->{misc}->{networkstates}}) . "\n") unless $stateID; } my $tmplID = ($tmplName ne '') ? &HaCi::Utils::tmplName2ID($tmplName) : -1; unless (defined $tmplID) { &daw("No such Template found! ($tmplName)\n"); } my $rootID = ($rootName ne '') ? &HaCi::Utils::rootName2ID($rootName) : -1; unless ($rootID) { &daw("No such root found! ($rootName)\n"); } $rootID = 0 if $rootID == -1; my $tmplValueBox = {}; if ($tmplID != -1) { my $tmplEntries = &HaCi::Utils::getTemplateEntries($tmplID, 0, 0, 1, 0, 1); foreach (keys %{$tmplEntries}) { my $id = $_; my $name = $tmplEntries->{$id}; foreach (keys %{$tmplValues}) { if ($name eq $_) { $tmplValueBox->{$id} = $tmplValues->{$_}; } } } } my $success = &HaCi::Utils::addNet( 0, $rootID, $ip, $cidr, $descr, $stateID, $tmplID, $defSubSize, 1, $tmplValueBox, 0, $tags, ); my $error = ($success) ? '' : "error adding this new network '$ip/$cidr': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item editNet() Edit an existing network Params - root name [string] edit the network in this root - network [network] edit this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - changes [hash] changes (valid attributes are: network description state rootName defSubnetSize tmplName tmplValues) e.g.: {description => 'test_8'} {network => '192.168.0.8/29', defSubnetSize=>30, state=>'FREE', rootName=>'larsux.de'} Returns - success [string] 0 on success, error-String at error =cut sub editNet { my $rootName = shift || ''; my $network = shift || ''; my $changeData = shift || {}; my @keys = qw(network description state rootName defSubnetSize tmplName tmplValues tags); my $isIPv4 = qr/(\d{1,3}\.){3}\d{1,3}/i; my $isIPv6 = qr/(((?=(?>.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?{rootName}) { $newRootID = &HaCi::Utils::rootName2ID($changeData->{rootName}); unless ($newRootID) { &daw("No such root found! ($changeData->{rootName})\n"); } } my $newNetwork = (exists $changeData->{network}) ? $changeData->{network} : $network; my ($ip, $cidr) = split(/\//, $network); my ($newIP, $newCIDR) = split(/\//, $newNetwork); &daw("This '$ip' is not a valid ip address (i.e.: 1.2.3.4/30, 2001:dead:beaf::/66)\n") if $ip !~ /$isIPv4/ && $ip !~ /$isIPv6/; &daw("This '$newIP' is not a valid ip address (i.e.: 1.2.3.4/30, 2001:dead:beaf::/66)\n") if $newIP !~ /$isIPv4/ && $newIP !~ /$isIPv6/; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &HaCi::Mathematics::netv62Dec($network) : &HaCi::Mathematics::net2dec($network); my $ipv6ID = ($ipv6) ? &HaCi::Utils::netv6Dec2ipv6ID($networkDec) : ''; my $netID = &HaCi::Utils::getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &daw("No such network found! ($network)\n"); return; } my $netDetails = &HaCi::Utils::getMaintInfosFromNet($netID); my $descr = (exists $changeData->{description}) ? $changeData->{description} : $netDetails->{description}; my $defSubSize = (exists $changeData->{defSubnetSize}) ? $changeData->{defSubnetSize} : $netDetails->{defSubnetSize}; my $tags = (exists $changeData->{tags}) ? $changeData->{tags} : join(',', @{$netDetails->{tags}}); my $stateID = $netDetails->{state}; if (exists $changeData->{state}) { $stateID = &HaCi::Utils::networkStateName2ID($changeData->{state}); &daw("State not known! ($changeData->{state})\nAvailable states: " . join(', ', map {$_->{name}} @{$HaCi::Conf::conf->{static}->{misc}->{networkstates}}) . "\n") unless $stateID; } my $tmplID = (exists $changeData->{tmplName}) ? $changeData->{tmplName} : $netDetails->{tmplID}; if (exists $changeData->{tmplName}) { $tmplID = &HaCi::Utils::tmplName2ID($changeData->{tmplName}); unless (defined $tmplID) { &daw("No such Template found! ($changeData->{tmplName})\n"); } } my $tmplValueBox = 0; if (exists $changeData->{tmplValues}) { $tmplValueBox = {}; die "Template values must be a hash\n" unless ref($changeData->{tmplValues}) eq 'HASH'; my $tmplEntries = &HaCi::Utils::getTemplateEntries($tmplID, 0, 0, 1, 0, 1); foreach (keys %{$tmplEntries}) { my $id = $_; my $name = $tmplEntries->{$id}; foreach (keys %{$changeData->{tmplValues}}) { if ($name eq $_) { $tmplValueBox->{$id} = $changeData->{tmplValues}->{$_}; } } } } my $success = &HaCi::Utils::addNet( $netID, $newRootID, $newIP, $newCIDR, $descr, $stateID, $tmplID, $defSubSize, 1, $tmplValueBox, 1, $tags, ); my $error = ($success) ? '' : "error while modifying this network '$ip/$cidr': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item delNet() Delete a network from HaCi Params - root name [string] remove the netork from this root - network [network] remove this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) - network lock [integer] lock network for X seconds - withSubnets [boolean] also remve subnets Returns - success [string] 0 on success, error-String at error =cut sub delNet { my $rootName = shift || ''; my $network = shift || 0; my $networkLock = shift || $HaCi::Conf::conf->{user}->{misc}->{dftnetworklock} || 0; my $bWithSubnets = shift || 0; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('editNet'); my $rootID = ($rootName ne '') ? &HaCi::Utils::rootName2ID($rootName) : -1; unless ($rootID) { &daw("No such root found! ($rootName)\n"); } $rootID = 0 if $rootID == -1; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &HaCi::Mathematics::netv62Dec($network) : &HaCi::Mathematics::net2dec($network); my $ipv6ID = ($ipv6) ? &HaCi::Utils::netv6Dec2ipv6ID($networkDec) : ''; my $netID = &HaCi::Utils::getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &daw("No such network found! ($network)\n"); return; } my $success = &HaCi::Utils::delNet($netID, $bWithSubnets, 0, $networkLock); my $error = ($success) ? '' : "error removing this new network '$network': " . join("\n", @{$HaCi::Conf::conf->{var}->{warnl}}) . "\n"; &warnl($error) if $error; return ($success) ? 0 : $error; } =item assignFreeSubnet() Find the next free subnet and assign it in one step Params - root name [string] search free subnets in that root - supernet [network] search free subnets in that supernet (e.g. 192.168.0.0/24, 2001::/120) - cidr [integer] specify the target cidr of your subnets (e.g. 30) - description [string] description of the network - state [string] state of the network (e.g. ALLOCATED PA, ASSIGNED, ...) - def subnet Size [integer] define a default subnet cidr size (e.g. 30) - template name [string] assign a template to the network - template values [hash] pass template values (e.g. {hostname => 'abc.de', os => 'redhat'}) Returns - network details [hash] new network assigned: - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from =cut sub assignFreeSubnet { my $rootName = shift; my $network = shift; my $size = shift || 0; my $descr = shift || ''; my $state = shift || ''; my $defSubSize = shift || 0; my $tmplName = shift || ''; my $tmplValueBox = shift || {}; my $secondTry = shift || 0; my $amount = 1; &getLock($network); my $freeNetworks = &getFreeSubnets($rootName, $network, $size, $amount); &daw("No free subnets found!\n") if scalar @{$freeNetworks} < 1; my $freeNet = ${$freeNetworks}[0]; if (my $error = &addNet($rootName, $freeNet, $descr, $state, $defSubSize, $tmplName, $tmplValueBox)) { if (($error =~ /allready exists/ || $error =~ /already exists/) && !$secondTry) { # retry one time &warnl("Network already exists! Retry one more time..."); &releaseLock($network); usleep(rand(1000000)); return &assignFreeSubnet($rootName, $network, $size, $descr, $state, $defSubSize, $tmplName, $tmplValueBox, 1); } else { &releaseLock($network); &daw($error); } } else { my $netDetails = &getNetworkDetails($rootName, $freeNet); $netDetails->{netID} = $netDetails->{ID}; $netDetails->{tmplName} = &HaCi::Utils::tmplID2Name($netDetails->{tmplID}); $netDetails->{rootName} = $rootName; delete $netDetails->{ipv6ID}; delete $netDetails->{ID}; delete $netDetails->{tmplID}; delete $netDetails->{rootID}; &releaseLock($network); return $netDetails; } } =item getNetworkDetails() Get details from a network in HaCi Params - root name [string] get details of a network in this root - network [network] get details of this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) Returns - network [hash] network details: - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from =cut sub getNetworkDetails { my $rootName = shift; my $network = shift; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('showNetDet'); my $rootID = ($rootName ne '') ? &HaCi::Utils::rootName2ID($rootName) : -1; unless ($rootID) { &daw("No such root found! ($rootName)\n"); } $rootID = 0 if $rootID == -1; my $ipv6 = ($network =~ /:/) ? 1 : 0; my $networkDec = ($ipv6) ? &HaCi::Mathematics::netv62Dec($network) : &HaCi::Mathematics::net2dec($network); my $ipv6ID = ($ipv6) ? &HaCi::Utils::netv6Dec2ipv6ID($networkDec) : ''; my $netID = &HaCi::Utils::getNetID($rootID, $networkDec, $ipv6ID); unless ($netID) { &daw("No such network found! ($network)\n"); return; } my $netDetails = &HaCi::Utils::getMaintInfosFromNet($netID); $netDetails->{network} = $network; $netDetails->{state} = &HaCi::Utils::networkStateID2Name($netDetails->{state}); if ($netDetails->{tmplID}) { $netDetails->{templateValues} = &HaCi::Utils::getTemplateData($netID, $netDetails->{tmplID}, 1); $netDetails->{template} = &HaCi::Utils::tmplID2Name($netDetails->{tmplID}); } return $netDetails; } sub daw { my $msg = shift; &warnl($msg); die $msg; } sub getLock { my $name = shift; my $fileName = md5_hex($name) . '.lock'; my $lockFile = $HaCi::Conf::conf->{static}->{path}->{lockpath} . '/' . $fileName; my $waitCnter = 5; while (-f $lockFile) { warn "Lockfile '$lockFile' for '$name' found. Waiting...\n"; usleep(rand(1000000)); last unless $waitCnter--; } if (-f $lockFile) { my $lockProc = 'unkonwn'; unless (open F, $lockFile) { warn "Cannot open lockfile '$lockFile' for reading: $!\n"; } else { $lockProc = ; close F; } warn "Locking prozess '$lockProc' seems to die. Overwriting lockfile '$lockFile'.\n"; } open F, '>' . $lockFile or warn "Cannot open lockfile '$lockFile' for writing: $!\n"; print F $$; close F; $lockName = $name; } sub releaseLock { my $name = shift; my $fileName = md5_hex($name) . '.lock'; my $lockFile = $HaCi::Conf::conf->{static}->{path}->{lockpath} . '/' . $fileName; unlink $lockFile or warn "Cannot release lockfile '$lockFile': $!\n"; $lockName = undef; } =item getSubnets() Get subnets from a root and optional from a supernet Params - root name [string] get subnets from this root - supernet [network] get subnets from this network (e.g. 192.168.0.1/32, 2001::dead:beef:0/125) Returns - networks [array] array of found networks (hash) - netID - network - modify date - ipv6 (boolean) - description - state - create from - create date - default subnet cidr size - template name - modify from Example my $subnets = getSubnets('Test'); my $subnets = getSubnets('Test', '192.168.0.0/24'); =cut sub getSubnets { my $rootName = shift; my $network = shift || 0; &daw("Permission denied!\n") unless &HaCi::Utils::checkRight('showNets'); &daw("You have to provide a root name!\n") unless $rootName; my $rootID = &HaCi::Utils::rootName2ID($rootName); unless ($rootID) { &daw("No such Root found! ($rootName)\n"); return; } my $ipv6 = &HaCi::Utils::rootID2ipv6($rootID); my @childs = (); if ($network) { my $networkDec = ($ipv6) ? &HaCi::Mathematics::netv62Dec($network) : &HaCi::Mathematics::net2dec($network); my $ipv6ID = ($ipv6) ? &HaCi::Utils::netv6Dec2ipv6ID($networkDec) : ''; my $netID = &HaCi::Utils::getNetID($rootID, $networkDec, $ipv6ID); @childs = &HaCi::Utils::getNetworkChilds($netID, 0, 0); } else { @childs = &HaCi::Utils::getNetworkChilds($rootID, 1, 0); } my @childDetails = map { my $networkDec = $_->{network}; my $network = ($ipv6) ? &HaCi::Mathematics::netv6Dec2net($networkDec) : &HaCi::Mathematics::dec2net($networkDec); my $netDetails = &getNetworkDetails($rootName, $network); $netDetails->{netID} = $netDetails->{ID}; $netDetails->{tmplName} = &HaCi::Utils::tmplID2Name($netDetails->{tmplID}); $netDetails->{rootName} = $rootName; delete $netDetails->{ipv6ID}; delete $netDetails->{ID}; delete $netDetails->{tmplID}; delete $netDetails->{rootID}; $netDetails; } @childs; return \@childDetails; } =back =cut 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/HaCi/Authentication/0000755000175000000000000000000012475447616016315 5ustar fighterrootHaCi/modules/HaCi/Authentication/imap.pm0000644000175000000000000000435211605415405017566 0ustar fighterrootpackage HaCi::Authentication::imap; use strict; use HaCi::Log qw/warnl debug/; use HaCi::GUI::gettext qw/_gettext/; use Net::IMAP::Simple; require Exporter; our @ISA = qw(Exporter); our $conf; *conf = \$HaCi::Conf::conf; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub user { my $self = shift; my $user = shift; if (defined $user) { $self->{user} = $user; } else { return $self->{user}; } } sub pass { my $self = shift; my $pass = shift; if (defined $pass) { $self->{pass} = $pass; } else { return $self->{pass}; } } sub session { my $self = shift; my $sess = shift; if (defined $sess) { $self->{sess} = $sess; } else { return $self->{sess}; } } sub authenticate { my $self = shift; $self->{sess}->param('authenticated', 0); my $host = $conf->{user}->{auth}->{authparams}->{imap}->{host} || 'localhost'; my $imap = undef; unless ($imap = Net::IMAP::Simple->new($host)) { $conf->{var}->{authenticationError} = "Authentication failed: Cannot connect to IMAP Server: " . $Net::IMAP::Simple::errstr; return 0; } unless ($imap->login($self->user(), $self->pass())) { &HaCi::Utils::debug("Authentication Failed: " . $imap->errstr . "\n"); $conf->{var}->{authenticationError} = _gettext("Authentication failed!"); } else { $self->{sess}->param('authenticated', 1); $self->{sess}->param('username', $self->user()); &HaCi::Utils::debug("Sucessfully logged in!"); } return &isAutenticated($self); } sub isAutenticated { my $self = shift; my $username = $self->{sess}->param('username') || $self->user(); if ($username) { my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot authenticate! DB Error (user)\n"; return 0; } my $user = ($userTable->search(['ID'], {'%CS%' => 1, username => $username}))[0]; if (!defined $user || !exists $user->{ID}) { &debug("Authentication failed: User '$username' not in Database!"); $conf->{var}->{authenticationError} = _gettext("Authentication failed!"); $self->{sess}->clear('authenticated'); $self->{sess}->param('authenticated', 0); return 0; } } return (defined $self->{sess}->param('authenticated')) ? $self->{sess}->param('authenticated') : 0; } 1; HaCi/modules/HaCi/Authentication/internal.pm0000644000175000000000000001502312467504603020457 0ustar fighterrootpackage HaCi::Authentication::internal; use strict; use Data::Dumper; use HaCi::Log qw/warnl debug/; use HaCi::GUI::gettext qw/_gettext/; use POSIX qw(strftime); require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(getCryptPassword lwe bin2dec); our $conf; *conf = \$HaCi::Conf::conf; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub user { my $self = shift; my $user = shift; if (defined $user) { $self->{user} = $user; } else { return $self->{user}; } } sub pass { my $self = shift; my $pass = shift; if (defined $pass) { $self->{pass} = $pass; } else { return $self->{pass}; } } sub session { my $self = shift; my $sess = shift; if (defined $sess) { $self->{sess} = $sess; } else { return $self->{sess}; } } sub authenticate { my $self = shift; $self->{sess}->param('authenticated', 0); my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Authentication failed: Database error!"); return 0; } my $user = ($userTable->search(['*'], {username => $self->user()}))[0]; unless (defined $user) { warn "User '" . $self->user() . "' not found in database!\n"; $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Authentication failed!"); return 0; } else { $self->{sess}->param('username', $self->user()); } my $password = $user->{password}; if ($password eq '') { $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Internal account is disabled."); return 0; } my $newPass = &getCryptPassword($self->pass()); if ($password eq $newPass) { $self->{sess}->param('authenticated', 1); &HaCi::Utils::debug("Sucessfully logged in!"); } else { $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Authentication failed!"); } return &isAutenticated($self); } sub getCryptPassword { use Digest::SHA; my $clear = shift; my $sha = Digest::SHA->new('256'); $sha->add($clear); return $sha->hexdigest; } sub isAutenticated { my $self = shift; my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot authenticate! DB Error (user)\n"; return 0; } if (defined $self->{sess}->param('username')) { my $userParam = $self->{sess}->param('username'); my $user = ($userTable->search(['ID'], {username => $userParam}))[0]; if (!defined $user || !exists $user->{ID}) { warn "User '$userParam' not found in database!\n"; $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Authentication failed!"); $self->{sess}->clear('authenticated'); $self->{sess}->param('authenticated', 0); return 0; } } return (defined $self->{sess}->param('authenticated')) ? $self->{sess}->param('authenticated') : 0; } sub init { my $self = shift; &checkAdminGroup($self); &checkAdminUser($self); } sub checkAdminUser { my $self = shift; my $session = $self->session(); my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot init Authentication! DB Error (user)\n"; return 0; } my @users = $userTable->search(['ID']); if ($#users == -1) { &HaCi::Utils::warnl("No users available! Generating new admin user with empty password! Please change it right after you logged in!"); my $pass = &getCryptPassword(''); my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot init authentication! DB Error (group)\n"; return 0; } my $adminGroup = ($groupTable->search(['ID'], {name => 'Administrator'}))[0]; $userTable->username('admin'); $userTable->password($pass); $userTable->description('The Administrator'); $userTable->groupIDs(' ' . $adminGroup->{ID} . ','); my $DB = ($userTable->search(['ID'], {username => 'admin'}))[0]; if ($DB) { $userTable->modifyFrom($session->param('username')); $userTable->modifyDate(&HaCi::Utils::currDate('datetime')); &HaCi::Utils::debug("Change User 'admin'\n"); unless ($userTable->update({ID => $DB->{ID}})) { &warnl("Cannot update admin user: " . $groupTable->errorStrs); return 0; } return 1; } else { $userTable->ID(undef); $userTable->createFrom($session->param('username')); $userTable->createDate(&HaCi::Utils::currDate('datetime')); unless ($userTable->insert()) { &warnl("Cannot create admin user: " . $groupTable->errorStrs); return 0; } return 1; } } } sub checkAdminGroup { my $self = shift; my $session = $self->session(); my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot init Authentication! DB Error (group)\n"; return 0; } my @groups = $groupTable->search(['ID']); if ($#groups == -1) { warn "No Groups available! Generating new Administator Group with all Rights!\n"; my $rightsStr = '1'; foreach (keys %{$conf->{static}->{rights}}) { $rightsStr .= '1'; } $rightsStr = &HaCi::Utils::lwe(&HaCi::Utils::bin2dec($rightsStr)); $groupTable->name('Administrator'); $groupTable->description('The Administrators'); $groupTable->permissions('1' . $rightsStr); my $DB = ($groupTable->search(['ID'], {name => 'Administrator'}))[0]; if ($DB) { $groupTable->modifyFrom($session->param('username')); $groupTable->modifyDate(&HaCi::Utils::currDate('datetime')); &HaCi::Utils::debug("Change Group 'Administrator'\n"); unless ($groupTable->update({ID => $DB->{ID}})) { &warnl("Cannot update admin group: " . $groupTable->errorStrs); return 0; } return 1; } else { $groupTable->ID(undef); $groupTable->createFrom($session->param('username')); $groupTable->createDate(&HaCi::Utils::currDate('datetime')); unless ($groupTable->insert()) { &warnl("Cannot create admin group: " . $groupTable->errorStrs); return 0; } return 1; } } return 1; } sub currDate { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $type = shift; my $time = shift; if ($type eq 'datetime') { return strftime "%F %T", ((defined $time) ? localtime($time) : localtime); } } sub lwe { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $clear = shift; my $crypt = ''; my @nrs = split//, $clear; my $last = ''; for (0 .. $#nrs) { $last = $nrs[-1] if $last eq ''; $last = ($nrs[$_] + $last) % 10; $crypt .= $last; } return $crypt; } sub bin2dec { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $bin = shift; my $dec = oct("0b" . $bin); return $dec; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Authentication/ldap.pm0000644000175000000000000000676512377724510017601 0ustar fighterrootpackage HaCi::Authentication::ldap; use strict; use HaCi::Log qw/warnl debug/; use HaCi::GUI::gettext qw/_gettext/; use Net::LDAP; use Data::Dumper; require Exporter; our @ISA = qw(Exporter); our $conf; *conf = \$HaCi::Conf::conf; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub user { my $self = shift; my $user = shift; if (defined $user) { $self->{user} = $user; } else { return $self->{user}; } } sub pass { my $self = shift; my $pass = shift; if (defined $pass) { $self->{pass} = $pass; } else { return $self->{pass}; } } sub session { my $self = shift; my $sess = shift; if (defined $sess) { $self->{sess} = $sess; } else { return $self->{sess}; } } sub authenticate { my $self = shift; my $user = $self->user(); $self->{sess}->param('authenticated', 0); my $ldap = undef; my $host = $conf->{user}->{auth}->{authparams}->{ldap}->{host} || ['localhost']; my $bOK = 0; foreach (@{$host}) { my $host = $_; unless ($ldap = Net::LDAP->new($host)) { my $error = "Authentication failed: Cannot connect to LDAP Server '$host': " . $@; $conf->{var}->{authenticationError} .= $error . '
'; warn $error; } else { $bOK = 1; last; } } return 0 unless $bOK; my $bindDN = $conf->{user}->{auth}->{authparams}->{ldap}->{binddn} || ''; my $bindPW = $conf->{user}->{auth}->{authparams}->{ldap}->{bindpw} || ''; my $baseDN = $conf->{user}->{auth}->{authparams}->{ldap}->{basedn} || ''; my $filter = $conf->{user}->{auth}->{authparams}->{ldap}->{filter} || ''; if ($filter) { unless ($bindPW) { $conf->{var}->{authenticationError} = "Authentication failed: No passsword configured for binding"; return 0; } my $msg = $ldap->bind($bindDN, password => $bindPW); if ($msg->code()) { $conf->{var}->{authenticationError} = "Authentication failed: Cannot bind to LDAP server"; return 0; } $filter =~ s//$user/g; my $search = $ldap->search( base => $baseDN, filter => $filter, scope => 'sub', attrs => ['cn'] ); my @results = $search->entries; if ($#results < 0) { $conf->{var}->{authenticationError} = "Authentication failed: User not known"; return 0; } $bindDN = $results[0]->dn(); } else { $bindDN =~ s//$user/g; } &HaCi::Utils::debug("Authenticate [ldap] with: $bindDN\n"); my $login = $ldap->bind ($bindDN, password => $self->pass()); if ($login->code) { &HaCi::Utils::debug("Authentication Failed: " . $login->error() . "\n"); $conf->{var}->{authenticationError} = _gettext("Authentication failed!"); } else { $self->{sess}->param('authenticated', 1); $self->{sess}->param('username', $self->user()); &HaCi::Utils::debug("Sucessfully logged in!"); } return &isAutenticated($self); } sub isAutenticated { my $self = shift; my $username = $self->{sess}->param('username') || $self->user(); if ($username) { my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot authenticate! DB Error (user)\n"; return 0; } my $user = ($userTable->search(['ID'], {'%CS%' => 1, username => $username}))[0]; if (!defined $user || !exists $user->{ID}) { &debug("Authentication failed: User '$username' not in Database!"); $conf->{var}->{authenticationError} = _gettext("Authentication failed!"); $self->{sess}->clear('authenticated'); $self->{sess}->param('authenticated', 0); return 0; } } return (defined $self->{sess}->param('authenticated')) ? $self->{sess}->param('authenticated') : 0; } 1; HaCi/modules/HaCi/GUI/0000755000175000000000000000000012475454232013752 5ustar fighterrootHaCi/modules/HaCi/GUI/authentication.pm0000644000175000000000000000416412450700325017322 0ustar fighterrootpackage HaCi::GUI::authentication; use strict; use HaCi::Conf qw/getConfigValue/; use HaCi::Utils qw/getParam/; use HaCi::GUI::init; use HaCi::GUI::gettext qw/_gettext/; our $conf; *conf = \$HaCi::Conf::conf; sub login { my $t = $HaCi::GUI::init::t; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; my $locales = $conf->{static}->{gui}->{locales}; my $localesEnabled = &getConfigValue('gui', 'enableLocaleSupport'); map {$_->{ID} = $_->{id}} @{$locales}; $localesEnabled = 1 unless defined $localesEnabled; $t->{V}->{buttonFocus} = 'login'; $t->{V}->{loginMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext('Username'), }, { target => 'value', type => 'textfield', value => &getParam(1, '', 'username', 1), name => 'username', size => 13, maxlength => 255, focus => 1 }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext('Password'), }, { target => 'value', type => 'passwordfield', value => '', name => 'password', size => 13, maxlength => 255, }, ] }, ($localesEnabled) ? ( { elements => [ { target => 'key', type => 'label', value => _gettext('Language'), }, { target => 'value', type => 'popupMenu', name => 'locale', size => 1, values => $locales, onChange => 'submit()', selected => $s->param('locale') }, ] }) : (), { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', colspan => 2, align => 'center', buttons => [ { name => 'login', type => 'submit', value => _gettext('Login'), img => 'login_small.png', } ], }, ] }, ]; $t->{V}->{loginHeader} = _gettext('Authentication'); $t->{V}->{authError} = $conf->{var}->{authenticationError} if exists $conf->{var}->{authenticationError}; } 1; # vim:ts=2:sts=2:sw=2 HaCi/modules/HaCi/GUI/gettext.pm0000644000175000000000000000146412000412522015755 0ustar fighterrootpackage HaCi::GUI::gettext; use warnings; use strict; use Locale::gettext; use POSIX qw/setlocale LC_ALL/; use Encode; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( initLocale _gettext ); our $conf; *conf = \$HaCi::Conf::conf; sub _gettext { my $msgid = shift; unless (defined $conf->{var}->{GETTEXT}) { my $locale = (defined $HaCi::HaCi::session) ? $HaCi::HaCi::session->param('locale') : 'C'; &initLocale($locale); } return encode('UTF-8', $conf->{var}->{GETTEXT}->get($msgid)); } sub initLocale { my $locale = shift || 'C'; setlocale(LC_ALL, $locale); $conf->{var}->{GETTEXT} = Locale::gettext->domain_raw('HaCi'); $conf->{var}->{GETTEXT}->dir($conf->{static}->{path}->{localepath}); $conf->{var}->{GETTEXT}->codeset('utf-8-strict'); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/GUI/init.pm0000644000175000000000000000442112377724510015254 0ustar fighterrootpackage HaCi::GUI::init; use warnings; use strict; use Template; use HaCi::Conf qw/getConfigValue/; use HaCi::GUI::gettext qw/_gettext initLocale/; local our $t = undef; our $conf; *conf = \$HaCi::Conf::conf; sub init { my $s = $HaCi::HaCi::session; if (&getConfigValue('gui', 'enableLocaleSupport')) { my $locale = (defined $s) ? ($s->param('locale') || undef) : undef; my $langsT = (exists $ENV{HTTP_ACCEPT_LANGUAGE}) ? $ENV{HTTP_ACCEPT_LANGUAGE} : ''; my $langs = (split(/;/, $langsT))[0] || ''; my @httpLangs = split(/,/, $langs); unless (defined $locale) { foreach (reverse @httpLangs) { my $currLoc = (split(/[^a-z]/, lc($_)))[0]; foreach (@{$conf->{static}->{gui}->{locales}}) { my $newLoc = (split(/[^a-z]/, lc($_->{id})))[0]; $locale = $_->{id} if $currLoc eq $newLoc; } } } $locale = 'C' unless defined $locale && $locale; &initLocale($locale); $s->param('locale', $locale) if defined $s; } $t->{T} = Template->new( INCLUDE_PATH => $conf->{static}->{path}->{templateincludepath}, PRE_CHOMP => 3, POST_CHOMP => 3, TRIM => 1, COMPILE_DIR => $conf->{static}->{path}->{templatecompilepath}, COMPILE_EXT => '.ttc', ); } sub setUserVars { my $s = $HaCi::HaCi::session; $t->{V}->{showTreeStructure} = (defined $s && defined $s->param('settings') && exists $s->param('settings')->{bShowTreeStruct}) ? ${$s->param('settings')->{bShowTreeStruct}}[0] : $conf->{user}->{gui}->{showtreestructure}; $t->{V}->{directaccess} = $conf->{user}->{gui}->{directaccess}; my $style = (defined $s && defined $s->param('settings') && exists $s->param('settings')->{layout}) ? ${$s->param('settings')->{layout}}[0] : $conf->{user}->{gui}->{style} || $conf->{static}->{gui}->{style}; foreach (@{$conf->{static}->{gui}->{layouts}}) { my $layout = $_; if ($layout->{name} eq $style) { $t->{V}->{style} = $layout->{file}; $t->{V}->{styleDescr} = $layout->{descr}; } } } sub setVars { $t->{V} = $conf->{static}->{gui}; $t->{V}->{thisScript} = $conf->{var}->{thisscript}; $t->{V}->{techContact} = $conf->{user}->{gui}->{techcontact}; $t->{V}->{gettext_contact} = _gettext("Contact"); $t->{V}->{gettext_support} = _gettext("Supports"); &setUserVars(); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/GUI/main.pm0000644000175000000000000053241012475452522015241 0ustar fighterrootpackage HaCi::GUI::main; use warnings; use strict; use Data::Dumper; use HaCi::Utils qw/ getRoots getNextDBNetwork getWHOISData getNSData checkSpelling_Net getNrOfChilds rootID2Name getMaintInfosFromRoot getPluginValue getMaintInfosFromNet networkStateID2Name getNetworkTypes getTemplate getTemplateEntries tmplID2Name getTemplateData getHaCidInfo getGroups getGroup groupID2Name getUsers getUser userID2Name dec2bin lwd checkRight netID2Stuff getNetworkParentFromDB nd dn parseCSVConfigfile getID getStatus removeStatus expand getDBNetworkBefore getNetID getPlugins getPluginsForNet pluginID2Name updatePluginLastRun rootID2ipv6 checkRootACL checkNetACL netv6Dec2ipv6ID getPluginInfos setStatus getPluginConfMenu pluginName2ID getFreeSubnets pluginID2File getSettings userName2ID quoteHTML getConfigValue _gettext getPluginConfValues getAuditLogs getRights getParam getAvailTags tmplName2ID /; use HaCi::Mathematics qw/ dec2net net2dec getBroadcastFromNet dec2ip getIPFromDec getNetmaskFromCidr getNetaddress getV6BroadcastNet ipv62dec ipv6Sort netv6Dec2net getV6BroadcastIP ipv6Dec2ip netv62Dec netv6Dec2IpCidr ipv6DecCidr2NetaddressV6Dec ipv6DecCidr2netv6Dec /; use HaCi::Log qw/warnl debug/; use HaCi::GUI::gettext qw/_gettext/; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( mkTree mkAddRoot mkAddNet mkImportASNRoutes checkNet expandNetwork reduceNetwork expandRoot reduceRoot showPlugin mkShowStatus mkSubmitImportASNRoutes ); our $conf; *conf = \$HaCi::Conf::conf; sub start { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; if (my $func = &getParam(1, undef, 'func')) { if ($func eq 'addRoot' && &checkRight('addRoot')) { $t->{V}->{mainPage} = 'addRoot'; &mkAddRoot(); } elsif ($func eq 'addNet' && &checkRight('addNet')) { $t->{V}->{mainPage} = 'addNet'; &mkAddNet(); } elsif ($func eq 'editNet' && &checkRight('editNet')) { $t->{V}->{mainPage} = 'addNet'; &mkAddNet(); } elsif ($func eq 'splitNet' && &checkRight('editNet')) { $t->{V}->{mainPage} = 'splitNet'; &mkSplitNet(); } elsif ($func eq 'combineNets' && &checkRight('editTree')) { $t->{V}->{mainPage} = 'combineNets'; &mkCombineNets(); } elsif ($func eq 'editRoot' && &checkRight('editRoot')) { $t->{V}->{mainPage} = 'addRoot'; &mkEditRoot(); } elsif ($func eq 'delNet' && &checkRight('editNet')) { $t->{V}->{mainPage} = 'delNet'; &mkDelNet(); } elsif ($func eq 'delRoot' && &checkRight('editRoot')) { $t->{V}->{mainPage} = 'delRoot'; &mkDelRoot(); } elsif ($func eq 'showAllNets') { $t->{V}->{mainPage} = 'showAllNets'; &mkTree(); &mkTreeMenu(); } elsif ($func eq 'importASNRoutes' && &checkRight('impASNRoutes')) { $t->{V}->{mainPage} = 'importASNRoutes'; &mkImportASNRoutes(); } elsif ($func eq 'importDNS' && &checkRight('impDNS')) { $t->{V}->{mainPage} = 'importDNS'; &mkImportDNS(); } elsif ($func eq 'importConfig' && &checkRight('impConfig')) { $t->{V}->{mainPage} = 'importConfig'; if (&getParam(1, 0, 'source') && &getParam(1, '', 'source') eq 'csv') { &mkImportCSV(); } else { &mkImportConfig(); } } elsif ($func eq 'showNet' && &checkRight('showNetDet')) { $t->{V}->{mainPage} = 'showNet'; my ($rootID, $networkDec) = &netID2Stuff(&getParam(1, 0, 'netID')); $s->param('currNet', $networkDec); $s->param('currRootID', $rootID); &mkShowNet(); } elsif ($func eq 'showRoot' && &checkRight('showRootDet')) { $t->{V}->{mainPage} = 'showRoot'; &mkShowRoot(); } elsif ($func eq 'search' && &checkRight('search')) { $t->{V}->{mainPage} = 'search'; &mkSearch(); } elsif ($func eq 'getFreeSubnetsFromSearch' && &checkRight('search') && &checkRight('addNet')) { $t->{V}->{mainPage} = 'getFreeSubnetsFromSearch'; &mkSearch(); } elsif ($func eq 'compare' && &checkRight('search')) { $t->{V}->{mainPage} = 'compare'; &mkCompare(); } elsif ($func eq 'showTemplates' && &checkRight('tmplMgmt')) { $t->{V}->{mainPage} = 'showTemplates'; &mkShowTemplates(); } elsif ($func eq 'editTmpl' && &checkRight('tmplMgmt')) { $t->{V}->{mainPage} = 'editTemplate'; &mkEditTemplate(); } elsif ($func eq 'delTmpl' && &checkRight('tmplMgmt')) { $t->{V}->{mainPage} = 'delTmpl'; &mkDelTmpl(); } elsif ($func eq 'showGroups' && &checkRight('groupMgmt')) { $t->{V}->{mainPage} = 'showGroups'; &mkShowGroups(); } elsif ($func eq 'editGroup' && &checkRight('groupMgmt')) { $t->{V}->{mainPage} = 'editGroup'; &mkEditGroup(); } elsif ($func eq 'delGroup' && &checkRight('groupMgmt')) { $t->{V}->{mainPage} = 'delGroup'; &mkDelGroup(); } elsif ($func eq 'delUser' && &checkRight('userMgmt')) { $t->{V}->{mainPage} = 'delUser'; &mkDelUser(); } elsif ($func eq 'showUsers' && &checkRight('userMgmt')) { $t->{V}->{mainPage} = 'showUsers'; &mkShowUsers(); } elsif ($func eq 'editUser' && &checkRight('userMgmt')) { $t->{V}->{mainPage} = 'editUser'; &mkEditUser(); } elsif ($func eq 'delUser' && &checkRight('userMgmt')) { $t->{V}->{mainPage} = 'delUser'; &mkDelUser(); } elsif ($func eq 'showPlugins' && &checkRight('pluginMgmt')) { $t->{V}->{mainPage} = 'showPlugins'; &mkShowPlugins(); } elsif ($func eq 'showStatus') { $t->{V}->{mainPage} = 'showStatus'; &mkShowStatus(); } elsif ($func eq 'showAbout') { $t->{V}->{mainPage} = 'showAbout'; } elsif ($func eq 'showPluginGlobConf') { $t->{V}->{mainPage} = 'showPluginConf'; &mkShowPluginConf(1); } elsif ($func eq 'showPluginConf') { $t->{V}->{mainPage} = 'showPluginConf'; &mkShowPluginConf(); } elsif ($func eq 'showSubnets' && &checkRight('showNetDet')) { $t->{V}->{mainPage} = 'showSubnets'; &mkShowSubnets(); } elsif ($func eq 'showSettings') { $t->{V}->{mainPage} = 'showSettings'; &mkShowSettings(); } elsif ($func eq 'showAuditLogs') { $t->{V}->{mainPage} = 'showAuditLogs'; &mkShowAuditLogs(); } } else { $t->{V}->{mainPage} = 'showAllNets'; &mkTree(); &mkTreeMenu(); } &mkMenu(); } sub mkTreeMenu { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $bEditTree = &getParam(1, 0, 'editTree'); my $roots = &getRoots(1); map {my $h=$_;$h->{name} = "eHTML($h->{name}); $_ = $h;} @{$roots}; $t->{V}->{rootID2Ver} = $roots; $t->{V}->{treeMenuHiddens} = [ { name => 'editTree', value => $bEditTree }, ]; $t->{V}->{treeMenuHeader} = _gettext("Menu"); $t->{V}->{treeMenuFormName} = 'treeMenu'; $t->{V}->{treeMenu} = [ { elements => [ { target => 'single', type => 'popupMenu', name => 'rootIDJump', size => 1, values => $roots, selected => (defined (&getParam(1, undef, 'rootID')) ? [&getParam(1, 0, 'rootID')] : []), onChange => 'javascript:checkIfIPv6(this.value, "TREE")', }, { target => 'single', type => 'textfield', name => 'jumpTo', size => 25, maxlength => 39, value => ((&getParam(1, 0, 'jumpTo') && &getParam(1, '', 'jumpTo') ne '') ? &getParam(1, 0, 'jumpTo', 1) : '<' . _gettext('IP address') . '>'), style => ((defined &getParam(1, undef, 'jumpTo')) ? '' : 'color:#AAAAAA'), onClick => ((defined &getParam(1, undef, 'jumpTo')) ? '' : "clearTextfield('jumpTo')"), onKeyDown => "submitOnEnter(event, 'jumpToButton')", }, { target => 'single', type => 'buttons', buttons => [ { type => 'submit', name => 'jumpToButton', value => _gettext('Jump To'), img => 'jumpTo_small.png', }, ], }, { target => 'single', type => 'vline' }, { target => 'single', type => 'buttons', buttons => [ { name => 'closeTree', type => 'submit', value => _gettext("Close Tree"), img => 'close_small.png', }, ], }, { target => 'single', type => 'vline' }, { target => 'single', type => 'buttons', buttons => [ { name => (($bEditTree) ? 'finishEditTree' : 'editTree'), type => 'submit', value => _gettext((($bEditTree) ? "Finish Edit" : "Edit")), disabled => (&checkRight('editTree')) ? 0 : 1, img => (($bEditTree) ? 'editClose_small.png' : 'edit_small.png'), }, ], }, ] } ]; if ($bEditTree) { $t->{V}->{editTreeMenuHeader} = _gettext("Edit Menu"); $t->{V}->{editTreeMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { type => 'submit', name => 'deleteNets', value => _gettext('Delete'), img => 'del_small.png', }, ], }, { target => 'single', type => 'vline' }, { target => 'single', type => 'buttons', buttons => [ { type => 'submit', name => 'combineNets', value => _gettext('Combine'), img => 'combine_small.png', }, ], }, { target => 'single', type => 'vline' }, { target => 'single', type => 'buttons', buttons => [ { type => 'submit', name => 'copyNetsTo', value => _gettext('Copy'), img => 'copy_small.png', }, ], }, { target => 'single', type => 'dline' }, { target => 'single', type => 'buttons', buttons => [ { type => 'submit', name => 'moveNetsTo', value => _gettext('Move'), img => 'move_small.png', }, ], }, { target => 'single', type => 'label', value => _gettext("to"), }, { target => 'single', type => 'popupMenu', name => 'copyToRootID', size => 1, values => $roots, selected => ((&getParam(1, 0, 'rootID')) ? [&getParam(1, undef, 'rootID')] : []), }, ] } ]; } } sub mkTree { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; use HaCi::Tree; my $t = $HaCi::GUI::init::t; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; my $thisScript = $conf->{var}->{thisscript}; my $rootTable = $conf->{var}->{TABLES}->{root}; my $networkTable = $conf->{var}->{TABLES}->{network}; my $expands = $s->param('expands') || {}; unless (defined $rootTable || defined $networkTable) { warn "Cannot generate Tree. DB Error\n"; return 0; } my @roots = $rootTable->search(); my $tree = new HaCi::Tree; foreach (@roots) { my $rootID = $_->{ID}; my $ipv6 = &rootID2ipv6($rootID); next unless &mkTreeAddRoot(\$tree, $_, $ipv6); next unless $expands->{root}->{$rootID}; &mkTreeNetwork(\$tree, $rootID, $ipv6, (($ipv6) ? Math::BigInt->new(0) : 0), 0, 0); } $t->{V}->{tree} = $tree->print_html(); $t->{V}->{editTree} = &getParam(1, 0, 'editTree'); } sub mkTreeNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $treet = shift; my $rootID = shift; my $ipv6 = shift; my $networkDec = shift; my $bAddParent = shift || 0; my $bParentOnly = shift || 0; my $broadcast = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); my $tree = $$treet; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; my $expands = $s->param('expands') || {}; my $expandAll = (&getParam(1, 0, 'expandAll')) ? 1 : 0; my $networkDecOrig = ($ipv6) ? $networkDec->copy() : $networkDec; if ($bAddParent) { unless ($networkDec) { my $root = &getMaintInfosFromRoot($rootID); &mkTreeAddRoot(\$tree, $root, $ipv6); } else { my $netID = ($ipv6) ? &getNetID($rootID, 0, &netv6Dec2ipv6ID($networkDec)) : &getNetID($rootID, $networkDec, ''); my $networkT = &getMaintInfosFromNet($netID); &mkTreeAddNetwork(\$tree, $rootID, $ipv6, $networkT); } return if $bParentOnly; } my $networkT; while ($networkT = &getNextDBNetwork($rootID, $ipv6, $networkDec)) { $conf->{var}->{STATUS}->{DATA} = (($ipv6) ? &netv6Dec2net($networkT->{network}) : &dec2net($networkT->{network})); &setStatus(); last if $networkT->{network} == $networkDec || $networkT->{network} > $broadcast || !defined $networkT; my $bACL = &mkTreeAddNetwork(\$tree, $rootID, $ipv6, $networkT, $networkDecOrig); $networkDec = $networkT->{network}; if (($expandAll || $expands->{network}->{$rootID}->{$networkDec}) && $bACL) { &mkTreeNetwork(\$tree, $rootID, $ipv6, (($ipv6) ? Math::BigInt->new($networkDec) : $networkDec), 0, 0); } $networkDec = ($ipv6) ? &getV6BroadcastNet($networkDec, 128) : &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); last unless $networkDec; } if ($networkDecOrig && $networkDecOrig ne $broadcast) { $tree->checkNetworkHoles($rootID, $ipv6, $networkDecOrig, $broadcast); } } sub mkTreeAddRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $treet = shift; my $root = shift; my $ipv6 = shift; my $tree = $$treet; my $expands = $HaCi::HaCi::session->param('expands') || {}; my $expandAll = (defined $HaCi::HaCi::q->param('expandAll') && $HaCi::HaCi::q->param('expandAll')) ? 1 : 0; my $rootID = $root->{ID}; my $rootName = $root->{name}; my $rootDescr = $root->{description}; my $bACL = &checkRootACL($rootID, 'r'); if ($bACL) { $tree->setNewRoot($rootID); $tree->setRootName($rootID, $rootName); $tree->setRootDescr($rootID, $rootDescr); $tree->setRootExpanded($rootID, (($expandAll || $expands->{root}->{$rootID}) ? 1 : 0)); $tree->setRootParent($rootID, ((defined &getNextDBNetwork($rootID, $ipv6, (($ipv6) ? Math::BigInt->new(0) : 0))) ? 1 : 0)); $tree->setRootV6($rootID, $ipv6); } return $bACL; } sub subDescription { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $description = shift; my $netID = shift; while ($description =~ /\%\%(.*?)\%\%/) { my ($plugin, $name) = split/\%/, $1; if ($plugin && $name) { my $pluginID = &pluginName2ID($plugin); if ($pluginID ne '') { my $value = &getPluginValue($pluginID, $netID, $name); $description =~ s/\%\%.*?\%\%/$value/; } else { last; } } else { last; } } while ($description =~ /\%\*(.*?)\*\%/) { my ($tmpl, $name) = split/\%/, $1; if ($tmpl && $name) { my $tmplID = &tmplName2ID($tmpl); if ($tmplID ne '') { my $values = &getTemplateData($netID, $tmplID, 1); my $value = $values->{$name} || ''; $description =~ s/\%\*.*?\*\%/$value/; } else { last; } } else { last; } } return $description; } sub mkTreeAddNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $treet = shift; my $rootID = shift; my $ipv6 = shift; my $networkT = shift; my $networkDecOrig = shift; my $tree = $$treet; my $expands = $HaCi::HaCi::session->param('expands') || {}; my $expandAll = (defined $HaCi::HaCi::q->param('expandAll') && $HaCi::HaCi::q->param('expandAll')) ? 1 : 0; my $netID = $networkT->{ID}; my $networkDec = $networkT->{network}; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $description = $networkT->{description}; my $status = $networkT->{state}; my $defSubnetSize = $networkT->{defSubnetSize}; my $bACL = 0; $description = &subDescription($description, $netID) if $description =~ /\%(\%|\*)/; if (&checkSpelling_Net($network, $ipv6)) { my $bChilds = (defined &getNextDBNetwork($rootID, $ipv6, $networkDec, 1)) ? 1 : 0; $bACL = &checkNetACL($netID, 'r'); $tree->addNet($netID, $rootID, $ipv6, $networkDec, $description, ($status || 0), $networkDecOrig, 0, $defSubnetSize); $tree->setNetExpanded($rootID, $ipv6, $networkDec, ((($expandAll || (exists $expands->{network}->{$rootID}->{$networkDec} && $expands->{network}->{$rootID}->{$networkDec})) && $bACL) ? 1 : 0)); $tree->setNetParent($rootID, $ipv6, $networkDec, (($bChilds || $defSubnetSize) ? 1 : 0)); $tree->setInvisible($rootID, $ipv6, $networkDec, ($bACL == 0) ? 1 : 0); } return $bACL; } sub mkMenu { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $s = $HaCi::HaCi::session; my $thisScript = $conf->{var}->{thisscript}; my $menu = [ { title => sprintf(_gettext("Logged in as %s"), "eHTML($s->param('username'))), entries => [ { title => _gettext("Logout"), link => "$thisScript?func=logout", img => '/Images/logout.png', }, ], }, { title => _gettext("Tree"), entries => [ { title => _gettext("Overview"), link => "$thisScript?func=showAllNets", img => '/Images/showAll.png', }, { title => _gettext("Add root"), link => "$thisScript?func=addRoot", img => '/Images/addRoot.png', disabled => (&checkRight('addRoot')) ? 0 : 1, }, { title => _gettext("Add network"), link => "$thisScript?func=addNet", img => '/Images/addNet.png', disabled => (&checkRight('addNet')) ? 0 : 1, }, ], }, { title => _gettext("Import"), entries => [ { title => _gettext("ASN routes"), link => "$thisScript?func=importASNRoutes", img => '/Images/impASNRoutes.png', disabled => (&checkRight('impASNRoutes')) ? 0 : 1, }, { title => _gettext("DNS zonefile"), link => "$thisScript?func=importDNS", img => '/Images/impDNSZoneFile.png', disabled => (&checkRight('impDNS')) ? 0 : 1, }, { title => _gettext("Config"), link => "$thisScript?func=importConfig", img => '/Images/impConfig.png', disabled => (&checkRight('impConfig')) ? 0 : 1, }, ] }, { title => _gettext("Miscellaneous"), entries => [ { title => _gettext("Search"), link => "$thisScript?func=search", img => '/Images/search.png', disabled => (&checkRight('search')) ? 0 : 1, }, { title => _gettext("Search free networks"), link => "$thisScript?func=getFreeSubnetsFromSearch", img => '/Images/searchAndAddNet.png', disabled => (&checkRight('search') && &checkRight('addNet')) ? 0 : 1, }, { title => _gettext("Compare"), link => "$thisScript?func=compare", img => '/Images/compare.png', disabled => (&checkRight('search')) ? 0 : 1, }, { title => _gettext("Flush cache"), link => "$thisScript?func=flushCache", img => '/Images/flushACL.png', }, ], }, { title => _gettext("Maintenance"), entries => [ { title => _gettext("User management"), link => "$thisScript?func=showUsers", img => '/Images/userMgmt.png', disabled => (&checkRight('userMgmt')) ? 0 : 1, }, { title => _gettext("Group management"), link => "$thisScript?func=showGroups", img => '/Images/groupMgmt.png', disabled => (&checkRight('groupMgmt')) ? 0 : 1, }, { title => _gettext("Template management"), link => "$thisScript?func=showTemplates", img => '/Images/tmplMgmt.png', disabled => (&checkRight('tmplMgmt')) ? 0 : 1, }, { title => _gettext("Plugin management"), link => "$thisScript?func=showPlugins", img => '/Images/pluginMgmt.png', disabled => (&checkRight('pluginMgmt')) ? 0 : 1, }, { title => _gettext("Audit logs"), link => "$thisScript?func=showAuditLogs", img => '/Images/showAuditLogs.png', disabled => (&checkRight('showAuditLogs')) ? 0 : 1, }, { title => _gettext("Settings"), link => "$thisScript?func=showSettings", img => '/Images/configure.png', disabled => 0, }, { title => _gettext("About HaCi"), link => "$thisScript?func=showAbout", img => '/Images/HaCi_Logo2_small.png', disabled => 0, }, ], }, ]; $t->{V}->{menu} = $menu; } sub mkEditRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $rootID = &getParam(1, undef, 'rootID'); $rootID =~ s/\D//g; my $root = &getMaintInfosFromRoot($rootID); my $rootACTable = $conf->{var}->{TABLES}->{rootAC}; unless (defined $rootACTable) { warn "Cannot Edit Root. DB Error (rootAC)\n"; return 0; } my @acls = $rootACTable->search(['groupID', 'ACL'], {rootID => $rootID}); foreach (@acls) { if ($_->{ACL} == 1 || $_->{ACL} == 3) { $q->delete('accGroup_r_' . $_->{groupID}); $q->param('accGroup_r_' . $_->{groupID}, 1); } if ($_->{ACL} == 2 || $_->{ACL} == 3) { $q->delete('accGroup_w_' . $_->{groupID}); $q->param('accGroup_w_' . $_->{groupID}, 1); } } $q->delete('name'); $q->param('name', $root->{name}); $q->delete('descr'); $q->param('descr', $root->{description}); $q->delete('ipv6'); $q->param('ipv6', $root->{ipv6}); $t->{V}->{addRootHiddens} = [ { name => 'rootID', value => $rootID }, { name => 'ipv6', value => $root->{ipv6} }, { name => 'editRoot', value => 1, }, ]; &mkAddRoot(); } sub mkAddRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; my $name = ((defined &getParam(1, undef, 'name')) ? &getParam(1, '', 'name', 1) : ''); my $descr = ((defined &getParam(1, undef, 'descr')) ? &getParam(1, '', 'descr', 1) : ''); my $ipv6 = &getParam(1, 0, 'ipv6'); $ipv6 = 1 if $ipv6; my $bEditRoot = (&getParam(1, 0, 'editRoot')) ? 1 : 0; my $nrOfNetworks = ((defined &getParam(1, undef, 'rootID')) ? &getNrOfChilds(0, &getParam(1, undef, 'rootID')) : 0); if ($bEditRoot) { $t->{V}->{addRootHeader} = sprintf(_gettext("Edit Root %s"), $name); } else { $t->{V}->{addRootHeader} = _gettext("Add root"); } $t->{V}->{addRootFormName} = 'addRoot'; $t->{V}->{addRootMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name of Root"), }, { target => 'value', type => 'textfield', name => 'name', size => 20, maxlength => 255, value => $name, focus => 1, onKeyDown => "submitOnEnter(event, 'submitAddRoot')", } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'descr', size => 20, maxlength => 255, value => $descr, onKeyDown => "submitOnEnter(event, 'submitAddRoot')", } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("IPv6"), }, { target => 'value', type => 'checkbox', name => 'ipv6', value => 1, descr => '', checked => $ipv6, disabled => ($bEditRoot && $nrOfNetworks) ? 1 : 0, }, ], }, ]; my $groups = {}; foreach (@{&getGroups()}) { my $groupID = $_->{ID}; my $groupName = $_->{name}; next if $groupName eq 'Administrator' || $s->param('groupIDs') =~ / $_->{ID},/; my $rights = &getRights($groupID); next unless $rights->{showRoots} || $rights->{addRoot} || $rights->{editRoot}; $groups->{$groupID} = $groupName; } $t->{V}->{rootGroupRightsHeader} = _gettext("Access Rights"); $t->{V}->{rootGroupRightsMenu} = [ { elements => [ { target => 'single', type => 'label', value => '' . _gettext('read') . '', width => '3em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('write') . '', width => '5em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Group') . '', width => '8em', } ] }, { value => { type => 'hline', colspan => 3, } }, ]; push @{$t->{V}->{rootGroupRightsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'checkAll_read', value => 1, descr => '', checked => 0, align => 'center', width => '3em', onChange => "javascript:setAllACLs(\"checkAll_read\", \"r\")", }, { target => 'single', type => 'checkbox', name => 'checkAll_write', value => 1, descr => '', checked => 0, width => '5em', align => 'center', onChange => "javascript:setAllACLs(\"checkAll_write\", \"w\")", }, { target => 'single', type => 'label', value => '[' . _gettext("All") . ']', width => '8em', } ] }, ); foreach (sort {$a<=>$b} keys %{$groups}) { push @{$t->{V}->{rootGroupRightsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'accGroup_r_' . $_, value => 1, descr => '', checked => &getParam(1, 0, 'accGroup_r_' . $_), align => 'center', width => '3em', onChange => "javascript:setACLs(\"$_\",\"r\")", }, { target => 'single', type => 'checkbox', name => 'accGroup_w_' . $_, value => 1, descr => '', checked => &getParam(1, 0, 'accGroup_w_' . $_), width => '5em', align => 'center', onChange => "javascript:setACLs(\"$_\",\"w\")", }, { target => 'single', type => 'label', value => "eHTML($groups->{$_}), width => '8em', } ] }, ) }; $t->{V}->{addRootButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitAddRoot', type => 'submit', value => _gettext("Submit"), img => 'submit_small.png', }, { name => 'abortAddRoot', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', } ] } ] }, ]; push @{$t->{V}->{addRootHiddens}}, ( { name => 'func', value => 'addRoot' }, ); } sub mkAddNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $s = $HaCi::HaCi::session; # get Globals... my $stats = $conf->{static}->{misc}->{networkstates}; my $types = &getNetworkTypes(1); my $plugins = (); my $availPlugins = &getPlugins(); my $availPluginst = {}; my $nrOfPlugins = []; my $pluginOrders = {}; my $ipv6 = 0; my $maxSubnetSize = &getConfigValue('gui', 'maxsubnetsize'); my $roots = &getRoots(1); map {my $h = $_; $h->{name} = "eHTML($h->{name}); $_ = $h;} @{$roots}; map {$_->{name} = "eHTML($_->{name})} @{$types}; my $defSubnetSizes = [{ID => "0", name => "min"}]; my $defSubnetSizesV6 = [{ID => "0", name => "min"}]; map {$_->{ID} = $_->{id}} @{$stats}; my $plugCnter = 1; foreach (keys %{$availPlugins}) { $availPluginst->{$availPlugins->{$_}->{NAME}} = $_; if ($availPlugins->{$_}->{ACTIVE}) { push @{$nrOfPlugins}, {name => $plugCnter, ID => $plugCnter}; $plugCnter++; } } # get Variables.... my $editNet = &getParam(1, 0, 'editNet'); my $fillNet = &getParam(1, 0, 'fillNet'); my $checktAddNet = &getParam(1, 0, 'checktAddNet'); my $chTmplID = &getParam(1, 0, 'chTmplID'); my $submitAddNet = &getParam(1, 0, 'submitAddNet'); my $forceState = &getParam(1, 0, 'forceState'); my $aclsFromParent = &getParam(1, 0, 'aclsFromParent'); # Init variables... my $netaddress = ''; my $netmask = ''; my $cidr = ''; my $descr = ''; my $tags = ''; my $state = 0; my $defSubnetSize = 'min'; my $rootID = 0; my $tmplID = 0; my $availTagsT = &getAvailTags(); my $availTags = []; foreach (sort @{$availTagsT}) { push @{$availTags}, {ID => $_, name => $_}; } # If Fillnet if ($fillNet) { if (defined &getParam(1, undef, 'rootID') && defined &getParam(1, undef, 'networkDec')) { $rootID = &getParam(1, 0, 'rootID'); my $networkDec = &getParam(1, 0, 'networkDec'); $ipv6 = &rootID2ipv6($rootID); $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); (my $ipaddress, $cidr) = split/\//, $network; $netmask = ($ipv6) ? 0 : &getNetmaskFromCidr($cidr); $netaddress = ($ipv6) ? $ipaddress : &dec2ip(&getNetaddress($ipaddress, $netmask)); $t->{V}->{addNetHeader} = sprintf(_gettext("Add network %s"), $network); my $newRoots = []; foreach (@$roots) { push @$newRoots, $_ if ($_->{ipv6} && $ipv6) || (!$_->{ipv6} && !$ipv6); } $roots = $newRoots; } else { warn "mkAddnet: (fillNet) No RootID or NetworkDec given!\n"; } } # If editNet get Values from DB if ($editNet) { $t->{V}->{editNet} = 1; my $netID = &getParam(1, 0, 'netID'); if (defined $netID) { my $maintenanceInfos = &getMaintInfosFromNet($netID); my $networkDec = $maintenanceInfos->{network}; $rootID = $maintenanceInfos->{rootID}; $ipv6 = ($maintenanceInfos->{ipv6ID}) ? 1 : 0; $tmplID = $maintenanceInfos->{tmplID} || 0; $descr = $maintenanceInfos->{description} || ''; $state = $maintenanceInfos->{state} || 0; $defSubnetSize = $maintenanceInfos->{defSubnetSize} || 0; $tags = $maintenanceInfos->{tags} || []; $tags = join(' ', @{$tags}); my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); (my $ipaddress, $cidr) = split/\//, $network; $netmask = ($ipv6) ? 0 : &getNetmaskFromCidr($cidr); $netaddress = ($ipv6) ? $ipaddress : &dec2ip(&getNetaddress($ipaddress, $netmask)); $t->{V}->{addNetHeader} = sprintf(_gettext("Edit network %s"), $network); my $newRoots = []; foreach (@$roots) { push @$newRoots, $_ if ($_->{ipv6} && $ipv6) || (!$_->{ipv6} && !$ipv6); } $roots = $newRoots; # Get Template Values from DB if ($tmplID) { my $error = 0; my $tmplEntryTable = $conf->{var}->{TABLES}->{templateEntry}; unless (defined $tmplEntryTable) { warn "Cannot show Template Data. (Entries) DB Error\n"; $error = 1; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { warn "Cannot show Template Data. (Values) DB Error\n"; $error = 1; } unless ($error) { my @tmplEntries = $tmplEntryTable->search(['*'], {tmplID => $tmplID}); foreach (@tmplEntries) { my $tmplEntryID = $_->{ID}; my $valueT = ($tmplValueTable->search(['value'], {netID => $netID, tmplID => $tmplID, tmplEntryID => $tmplEntryID}))[0]; $q->delete('tmplEntryID_' . $tmplEntryID); $q->param('tmplEntryID_' . $tmplEntryID, ((defined $valueT) ? $valueT->{value} : '')); } } } $plugins = &getPluginsForNet($netID); foreach (keys %{$plugins}) { $pluginOrders->{$plugins->{$_}->{sequence}} = $_; } } else { warn "mkAddnet: (EditNet) No NetID given! => Free Form\n"; } } # If checktAddNet or chTmplID or return because of bad values => get values from before if ($checktAddNet || $chTmplID || $submitAddNet) { $netaddress = &getParam(1, undef, 'netaddress', 1); $netmask = &getParam(1, undef, 'netmask', 1); $cidr = &getParam(1, undef, 'cidr', 1); $descr = &getParam(1, undef, 'descr', 1); $state = &getParam(1, undef, 'state', 1); $defSubnetSize = &getParam(1, undef, 'defSubnetSize', 1); $rootID = &getParam(1, undef, 'rootID', 1); $tmplID = &getParam(1, undef, 'tmplID', 1); $t->{V}->{addNetHeader} = sprintf(_gettext((($editNet) ? 'Edit' : 'Add') . " Network %s"), $netaddress . '/' . $cidr); } else { $aclsFromParent = 1 unless $editNet; } # Generate default Subnet Cidr Menu { $cidr = 0 unless $cidr; map { push @{$defSubnetSizes}, {ID => "$_", name => "$_"}; } (($cidr + 1) .. ((32 < ($cidr + $maxSubnetSize)) ? 32 : ($cidr + $maxSubnetSize))); map { push @{$defSubnetSizesV6}, {ID => "$_", name => "$_"} } (($cidr + 1) .. ((128 < ($cidr + $maxSubnetSize)) ? 128 : ($cidr + $maxSubnetSize))); } # Corrections $descr = "eHTML($descr); $t->{V}->{rootID2Ver} = $roots; $t->{V}->{addNetHeader} ||= _gettext("Add network"); $t->{V}->{addNetFormName} = 'addNet'; $t->{V}->{addNetMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root") . (($ipv6) ? ' (IPv6)' : ''), }, { target => 'value', type => 'popupMenu', name => 'rootID', size => 1, values => $roots, selected => $rootID, onChange => 'javascript:checkIfIPv6(this.value, "ADDNET")', focus => 1, colspan => 3, }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Netaddress"), }, { target => 'value', type => 'textfield', name => 'netaddress', size => 43, maxlength => 43, value => $netaddress, onChange => "javascript:setnetmask_cidr(this.value, 'netmask', 'cidr', 'netaddress')", colspan => 3, } ] }, { name => 'netmaskBlock', elements => [ { target => 'key', type => 'label', value => _gettext("Netmask"), }, { target => 'value', type => 'textfield', name => 'netmask', size => 15, maxlength => 15, value => $netmask, onChange => "javascript:setCIDR(this.value, 'cidr', 'netaddress', 'netmask')", colspan => 3, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("CIDR"), }, { target => 'value', type => 'textfield', name => 'cidr', size => 3, maxlength => 3, value => $cidr, onChange => "javascript:setNetmask(this.value, 'netmask', 'netaddress', 'cidr')", colspan => 3, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'single', type => 'textfield', name => 'descr', size => 30, maxlength => 255, value => $descr, colspan => 2, }, { target => 'value', type => 'buttons', align => 'center', buttons => [ { name => 'showDescrHelperBtn', type => '', onClick => "showDescrHelper()", value => '1', img => 'info_small.png', picOnly => 1, title => _gettext("Available variables"), }, ], }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Tags"), }, { target => 'single', type => 'textfield', name => 'tags', size => 20, maxlength => 255, value => $tags, colspan => 1, }, { target => 'single', type => 'popupMenu', name => 'availTags', size => 1, onClick => 'javascript:addAvailTag();', values => $availTags, }, { target => 'single', type => 'label', align => 'center', value => _gettext("seperate
by space"), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'single', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => $state, }, { target => 'single', type => 'checkbox', name => 'forceState', value => 1, descr => _gettext('force'), checked => $forceState, align => 'left', }, { target => 'value', type => 'buttons', align => 'center', buttons => [ { name => 'showStatusHelperBtn', type => '', onClick => "showStatusHelper()", value => '1', img => 'info_small.png', picOnly => 1, title => _gettext("IPv4 Address Allocation and Assignment Policies"), }, ], }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Def. Subnet CIDR"), }, { target => 'value', type => 'popupMenu', name => 'defSubnetSize', size => 1, values => (($ipv6) ? $defSubnetSizesV6 : $defSubnetSizes), selected => [$defSubnetSize], colspan => 3, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'popupMenu', name => 'tmplID', size => 1, values => $types, selected => [$tmplID], onChange => 'javascript:document.getElementById("chTmplID").value=1;enableACLs();submit()', colspan => 3, } ] }, ]; $t->{V}->{helpDescrHeader} = _gettext("Available variables"); $t->{V}->{helpDescrMenu} = [ { elements => [ { target => 'single', type => 'label', value => '', width => '0.1em', }, { target => 'single', type => 'label', value => '' . _gettext('Name') . '', width => '3em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Description') . '', width => '8em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Insert') . '', width => '1em', align => 'center', } ] }, { value => { type => 'hline', colspan => 4, } }, ]; if ($tmplID) { my $tmplName = &tmplID2Name($tmplID); push @{$t->{V}->{addNetMenu}}, ( { value => { type => 'hline', colspan => 4, } }, ); (my $menu, undef) = &getTemplateEntries($tmplID, 1, 0, 0, 0, 1); push @{$t->{V}->{addNetMenu}}, @{$menu}; my $elements = []; foreach (@{$menu}) { my $row = $_; next unless exists $row->{elements}; foreach (@{$row->{elements}}) { my $element = $_; if ($element->{target} eq 'key') { my $name = $element->{value}; push @{$elements}, ( {elements => [ { target => 'single', type => 'label', value => '', width => '0.1em', }, { target => 'single', type => 'label', value => '', }, { target => 'single', type => 'label', value => $name, width => '3em', align => 'center', }, { target => 'single', type => 'buttons', align => 'center', width => '1em', buttons => [ { name => 'insertPluginVar', type => '', onClick => "insertPluginVar('descr','\%\*$tmplName\%$name\*\%')", value => '1', img => 'insert_small.png', picOnly => 1, title => sprintf(_gettext("Insert '%s' from Type '%s'"), $name, $tmplName), }, ], }, ]} ); } } } push @{$t->{V}->{helpDescrMenu}}, ( { elements => [ { target => 'single', type => 'label', value => sprintf(_gettext("Type '%s'"), $tmplName), bold => 1, colspan => 4, }, ], }, @{$elements} ) if $#{$menu} != -1; } my $groups = {}; { my $knowNet = 0; my $rootID = 0; my $networkDec = 0; my $netID = 0; if ($fillNet && defined &getParam(1, undef, 'rootID') && defined &getParam(1, undef, 'networkDec')) { $knowNet = 1; $rootID = &getParam(1, 0, 'rootID'); $networkDec = &getParam(1, 0, 'networkDec'); $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); $netID = $parent->{ID}; } elsif ($editNet && defined &getParam(1, undef, 'netID')) { $knowNet = 1; $netID = &getParam(1, 0, 'netID'); } foreach (@{&getGroups()}) { my $groupID = $_->{ID}; next if $_->{name} eq 'Administrator' || $s->param('groupIDs') =~ / $_->{ID},/; my $rights = &getRights($groupID); next unless $rights->{showNets} || $rights->{addNet} || $rights->{editNet}; $groups->{$_->{ID}}->{NAME} = $_->{name}; # If checktAddNet or chTmplID or return because of bad values => get values from before if ($checktAddNet || $chTmplID || $submitAddNet) { $groups->{$_->{ID}}->{R} = &getParam(1, 0, 'accGroup_r_' . $groupID); $groups->{$_->{ID}}->{W} = &getParam(1, 0, 'accGroup_w_' . $groupID); $groups->{$_->{ID}}->{I} = &getParam(1, 0, 'accGroup_i_' . $groupID); } else { # If you know the network (fillnet, editNet) get ACLs if ($knowNet) { my $acl = &checkNetACL($netID, 'ACL', $groupID, 1); $groups->{$_->{ID}}->{R} = ($acl % 4 == 1 || $acl % 4 == 3) ? 1 : 0; $groups->{$_->{ID}}->{W} = ($acl % 4 == 2 || $acl % 4 == 3) ? 1 : 0; $groups->{$_->{ID}}->{I} = ($acl > 3) ? 1 : 0; # inherited? } } } } $t->{V}->{netGroupRightsHeader} = _gettext("Access Rights"); $t->{V}->{netGroupRightsMenu} = [ { elements => [ { target => 'single', type => 'label', value => '' . _gettext('read') . '', width => '3em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('write') . '', width => '5em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Group') . '', width => '8em', } ] }, { value => { type => 'hline', colspan => 3, } }, ]; push @{$t->{V}->{netGroupRightsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'checkAll_read', value => 1, descr => '', checked => 0, align => 'center', disabled => ($aclsFromParent) ? 1 : 0, width => '3em', onChange => "javascript:setAllACLs(\"checkAll_read\", \"r\")", }, { target => 'single', type => 'checkbox', name => 'checkAll_write', value => 1, descr => '', checked => 0, width => '5em', align => 'center', disabled => ($aclsFromParent) ? 1 : 0, onChange => "javascript:setAllACLs(\"checkAll_write\", \"w\")", }, { target => 'single', type => 'label', value => '[' . _gettext("All") . ']', width => '8em', } ] }, ); foreach (sort {$a<=>$b} keys %{$groups}) { push @{$t->{V}->{netGroupRightsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'accGroup_r_' . $_, value => 1, descr => '', checked => $groups->{$_}->{R}, align => 'center', disabled => ($aclsFromParent) ? 1 : 0, width => '3em', onChange => "javascript:setACLs(\"$_\",\"r\")", }, { target => 'single', type => 'checkbox', name => 'accGroup_w_' . $_, value => 1, descr => '', checked => $groups->{$_}->{W}, width => '5em', align => 'center', disabled => ($aclsFromParent) ? 1 : 0, onChange => "javascript:setACLs(\"$_\",\"w\")", }, { target => 'single', type => 'label', value => "eHTML($groups->{$_}->{NAME}), bgColor => ((($groups->{$_}->{I} || 0) || !$editNet) ? 0 : 'FFAAAA'), width => '8em', } ] }, ); push @{$t->{V}->{addNetHiddens}}, ( { name => 'accGroup', value => $_ }, { name => 'accGroup_i_' . $_, value => $groups->{$_}->{I} }, ); }; push @{$t->{V}->{netGroupRightsMenu}}, ( { value => { type => 'hline', colspan => 3, } }, { elements => [ { target => 'single', type => 'label', align => 'right', colspan => 3, value => '(' . _gettext("marked: NOT inherited") . ')', }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Inherit from supernet"), }, { target => 'value', type => 'checkbox', name => 'aclsFromParent', value => 1, descr => '', checked => ($aclsFromParent) ? 1 : 0, onChange => "javascript:changeACLFromParent()", }, ], }, ); $t->{V}->{pluginInfoBoxHeader} = _gettext("Plugin Info"); push @{$t->{V}->{pluginInfoBoxMenu}}, ( { elements => [ { name => 'pluginInfoContent', target => 'single', type => 'label', value => '', width => '100%', bold => 0, align => 'center', wrap => 1, }, ] }, { value => { type => 'hline', colspan => 1, width => '100%', } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'abortPluginInfo', type => '', value => _gettext("Abort"), img => 'cancel_small.png', onClick => "hidePluginInfo()", picOnly => 1, title => _gettext("Close Infos for Plugin"), }, ], }, ] } ); $t->{V}->{netPluginsHeader} = _gettext("Plugins"); push @{$t->{V}->{netPluginsMenu}}, ( { elements => [ { target => 'single', type => 'label', value => _gettext("Name"), width => '10em', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext("active"), width => '6em', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext("Order"), width => '6em', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext("New Line"), width => '6em', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext("Configure"), width => '6em', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext("Info"), width => '6em', bold => 1, align => 'center', }, ] }, { value => { type => 'hline', colspan => 6, } }, ); my $currRow = 1; foreach (sort keys %{$availPluginst}) { my $ID = $availPluginst->{$_}; my $netmenu = $availPlugins->{$ID}->{NETMENU}; next unless $availPlugins->{$ID}->{ACTIVE}; unless (exists $plugins->{$ID} && $plugins->{$ID}->{sequence}) { while (exists $pluginOrders->{$currRow}) {$currRow++}; $pluginOrders->{$currRow} = 1; } my $bConf = 1 if ( $availPlugins->{$ID}->{RECURRENT} && ( exists $conf->{static}->{plugindefaultrecurrentmenu} && ref($conf->{static}->{plugindefaultrecurrentmenu}) eq 'ARRAY' && $#{$conf->{static}->{plugindefaultrecurrentmenu}} != -1 ) || ( exists $availPlugins->{$ID}->{MENURECURRENT} && ref($availPlugins->{$ID}->{MENURECURRENT}) eq 'ARRAY' && $#{$availPlugins->{$ID}->{MENURECURRENT}} != -1 ) ) || ( $availPlugins->{$ID}->{ONDEMAND} && ( exists $conf->{static}->{plugindefaultondemandmenu} && ref($conf->{static}->{plugindefaultondemandmenu}) eq 'ARRAY' && $#{$conf->{static}->{plugindefaultondemandmenu}} != -1 ) || ( exists $availPlugins->{$ID}->{MENUONDEMAND} && ref($availPlugins->{$ID}->{MENUONDEMAND}) eq 'ARRAY' && $#{$availPlugins->{$ID}->{MENUONDEMAND}} != -1 ) ); my $elements = []; my $api = (exists $availPlugins->{$ID}->{API} && ref($availPlugins->{$ID}->{API}) eq 'ARRAY') ? $availPlugins->{$ID}->{API} : []; foreach (@{$api}) { my $name = $_->{name}; my $descr = $_->{descr}; push @{$elements}, ( {elements => [ { target => 'single', type => 'label', value => '', width => '0.1em', }, { target => 'single', type => 'label', value => $name, width => '3em', align => 'left', }, { target => 'single', type => 'label', value => $descr, width => '8em', align => 'left', }, { target => 'single', type => 'buttons', align => 'center', width => '1em', buttons => [ { name => 'insertPluginVar', type => '', onClick => "insertPluginVar('descr','\%\%$availPlugins->{$ID}->{NAME}\%$name\%\%')", value => '1', img => 'insert_small.png', picOnly => 1, title => sprintf(_gettext("Insert '%s' from Plugin '%s'"), $name, $availPlugins->{$ID}->{NAME}), }, ], }, ]} ); }; push @{$t->{V}->{helpDescrMenu}}, ( { elements => [ { target => 'single', type => 'label', value => sprintf(_gettext("Plugin '%s'"), $availPlugins->{$ID}->{NAME}), bold => 1, colspan => 4, }, ], }, @{$elements} ) if $#{$api} != -1; my $pluginDescr = "eHTML($availPlugins->{$ID}->{DESCR} || ''); $pluginDescr =~ s/'/\\'/g; my $pluginName = "eHTML($availPlugins->{$ID}->{NAME} || ''); $pluginName =~ s/'/\\'/g; push @{$t->{V}->{netPluginsMenu}}, ( { elements => [ { target => 'single', type => 'label', value => $pluginName, width => '10em', }, { target => 'single', type => 'checkbox', name => 'netPluginActives', value => $ID, descr => ($availPlugins->{$ID}->{DEFAULT}) ? ' (' . _gettext("Default") . ')' : '', checked => ($availPlugins->{$ID}->{DEFAULT}) ? 1 : ((exists $plugins->{$ID}) ? 1 : 0), align => 'left', width => '6em', disabled => ($availPlugins->{$ID}->{DEFAULT}) ? 1 : 0, }, { target => 'single', type => 'popupMenu', name => 'pluginOrder_' . $ID, size => 1, values => $nrOfPlugins, align => 'center', width => '6em', selected => (exists $plugins->{$ID} && $plugins->{$ID}->{sequence}) ? [$plugins->{$ID}->{sequence}] : [$currRow], }, { target => 'single', type => 'checkbox', name => 'netPluginNewLines', value => $ID, descr => '', checked => (exists $plugins->{$ID} && $plugins->{$ID}->{newLine}) ? $plugins->{$ID}->{newLine} : 0, align => 'center', width => '6em', }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'editPluginConf', type => 'submit', onClick => "setPluginID('$ID')", value => '1', img => 'config_small' . (($editNet && $bConf) ? '' : '_disabled') . '.png', picOnly => 1, title => sprintf(_gettext("Configure Plugin '%s'"), $pluginName), disabled => ($editNet && $bConf) ? 0 : 1, }, ], }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'pluginInfo', onClick => "showPluginInfo('" . ("$pluginName
" . ($pluginDescr || '')) . "')", value => '1', img => 'info_small.png', picOnly => 1, title => sprintf(_gettext("Infos for Plugin '%s'"), $pluginName), }, ], }, ] }, ); if ($availPlugins->{$ID}->{DEFAULT}) { push @{$t->{V}->{addNetHiddens}}, ( { name => 'netPluginActives', value => $ID }, ); } } push @{$t->{V}->{helpDescrMenu}}, ( { value => { type => 'hline', colspan => 4, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 4, buttons => [ { name => 'pluginInfoCloser', onClick => "hideDescrHelper()", value => '1', img => 'cancel_small.png', picOnly => 1, title => _gettext("Close Overview"), }, ], }, ] } ); $t->{V}->{helpStatusHeader} = _gettext("IP Address Allocation and Assignment Policies"); push @{$t->{V}->{helpStatusMenu}}, ( { elements => [ { target => 'single', type => 'label', width => 500, align => 'left', value => '
# ALLOCATED PA: This address space has been allocated to an LIR and no assignments or sub-allocations made from it are portable.
  Assignments and suballocations cannot be kept when moving to another provider.  
# ALLOCATED PI: This address space has been allocated to an LIR or RIR and all
  assignments made from it are portable. Assignments can be kept as long as the criteria for the original assignment are met.
  Sub-allocations cannot be made from this type of address space.
# ALLOCATED UNSPECIFIED: This address space has been allocated to an LIR or RIR. Assignments may be PA or PI. This status is intended to document past
  allocations where assignments of both types exist. It is avoided for new allocations. Sub-allocations cannot be made from this type of address space.
# SUB-ALLOCATED PA: This address space has been sub-allocated by an LIR to a downstream network operator that will make assignments from it. All
  assignments made from it are PA. They cannot be kept when moving to a service provided by another provider.
# LIR-PARTITIONED PA: This allows an LIR to document distribution and delegate management of allocated space within their organisation. Address space
  with a status of LIR-PARTITIONED is not considered used. When the addresses are used, a more specific inetnum should be registered.
# LIR-PARTITIONED PI: This allows an LIR to document distribution and delegate management of allocated space within their organisation. Address space
  with a status of LIR-PARTITIONED is not considered used. When the addresses are used, a more specific inetnum should be registered.
# EARLY-REGISTRATION: This is used by the RIPE Database administration when transferring pre-RIR registrations from the ARIN Database. The value can
  be changed by database users (except for ALLOCATED PA). Only the RIPE Database administrators can create objects with this value.
# NOT-SET: This indicates that the registration was made before the "status:" attributes became mandatory for inetnum objects. The object has not been
  updated since then. New objects cannot be created with this value. The value can be changed by database users.
# ASSIGNED PA: This address space has been assigned to an End User for use with services provided by the issuing LIR. It cannot be kept when terminating
  services provided by the LIR.
# ASSIGNED PI: This address space has been assigned to an End User and can be kept as long as the criteria for the original assignment are met.
# ASSIGNED ANYCAST: This address space has been assigned for use in TLD anycast networks. It cannot be kept when no longer used for TLD anycast
  services.

# ALLOCATED-BY-RIR: For allocations made by an RIR to an LIR. # ALLOCATED-BY-LIR: For allocations made by an LIR or an LIR\'s downstream customer to another downstream organisation. # ASSIGNED: For assignments made to End User sites.
> Source: v4 / v6 <
' }, ] }, { value => { type => 'hline' } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'statusInfoCloser', onClick => "hideStatusHelper()", value => '1', img => 'cancel_small.png', picOnly => 1, title => _gettext("Close Info"), }, ], }, ] } ); $t->{V}->{addNetButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitAddNet', type => 'submit', value => _gettext("Submit"), img => 'submit_small.png', }, ] }, { target => 'single', type => 'buttons', buttons => [ { name => 'abortAddNet', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ] }, { target => 'single', type => 'buttons', buttons => [ { name => 'checktAddNet', type => 'submit', value => _gettext("Check"), img => 'check_small.png', }, ] } ] }, ]; push @{$t->{V}->{addNetHiddens}}, ( { name => 'func', value => 'addNet' }, { name => 'chTmplID', value => 0 }, { name => 'fillNet', value => $fillNet }, { name => 'editNet', value => $editNet }, { name => 'conf_maxSubnetSize', value => $maxSubnetSize }, ); if ($fillNet) { push @{$t->{V}->{addNetHiddens}}, ( { name => 'networkDec', value => &getParam(1, undef, 'networkDec', 1) }, ) if defined &getParam(1, undef, 'networkDec'); } if ($editNet) { push @{$t->{V}->{addNetHiddens}}, ( { name => 'netID', value => &getParam(1, undef, 'netID', 1) } ) if defined &getParam(1, undef, 'netID'); } } sub mkImportASNRoutes { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; &removeStatus(); $t->{V}->{importASNRoutesHeader} = _gettext("Import ASN routes"); $t->{V}->{importASNRoutesFormName} = 'importASNRoutes'; $t->{V}->{importASNRoutesMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("AS Number") . " (asplain)", }, { target => 'value', type => 'textfield', name => 'asn', size => 6, maxlength => 10, value => &getParam(1, '', 'asn', 1), focus => 1, onKeyDown => "submitOnEnter(event, 'submitImportASNRoutes')", } ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Only insert new"), }, { target => 'value', type => 'checkbox', name => 'onlyNew', value => 1, descr => '', checked => 0, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Remove obsoletes"), }, { target => 'value', type => 'checkbox', name => 'delOld', value => 1, descr => '', checked => 0, }, ], }, ]; $t->{V}->{importASNRoutesButtons} = [ { elements => [ { type => 'buttons', target => 'single', buttons => [ { name => 'submitImportASNRoutes', type => 'submit', value => _gettext("Import"), onClick => "showStatus(1)", img => 'import_small.png', }, ] }, { type => 'buttons', target => 'single', buttons => [ { name => 'abortImportASNRoutes', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], } ] }, ]; } sub checkNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $netaddress = shift; my $cidr = shift; my $network = "$netaddress/$cidr"; my $whoisData = &getWHOISData($network); my $ptrData = &getNSData($netaddress); my $t = $HaCi::GUI::init::t; $t->{V}->{checkNet} = 1; $t->{V}->{addNetDNSInfoHeader} = sprintf(_gettext("DNS Info for %s"), $ptrData->{ipaddress} || ''); $t->{V}->{addNetDNSInfo} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Reverse DNS lookup"), }, { target => 'value', type => 'label', value => $ptrData->{data}, } ] }, ]; $t->{V}->{addNetWHOISInfoHeader} = sprintf(_gettext("RIPE Info for %s"), $whoisData->{inetnum} || ''); foreach (@{$whoisData->{data}}) { push @{$t->{V}->{addNetWHOISInfo}}, { elements => [ { target => 'key', type => 'label', value => $_->{key}, }, { target => 'value', type => 'label', value => $_->{value}, } ] } } } sub mkShowNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $netID = &getParam(1, undef, 'netID'); return unless defined $netID; my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); return unless &checkSpelling_Net($network, $ipv6); my $maintenanceInfos = &getMaintInfosFromNet($netID); my $tmplID = $maintenanceInfos->{tmplID} || 0; my $descr = $maintenanceInfos->{description} || ''; my $state = $maintenanceInfos->{state} || 0; my $defSubnetSize = $maintenanceInfos->{defSubnetSize} || 0; my $tags = $maintenanceInfos->{tags} || []; $tags = join(' ', @{$tags}); my ($ipaddress, $cidr) = split(/\//, $network); my $netmask = ($ipv6) ? 0 : &getNetmaskFromCidr($cidr); my $netaddress = ($ipv6) ? $ipaddress : &dec2ip(&getNetaddress($ipaddress, $netmask)); my $broadcastDec = ($ipv6) ? &getV6BroadcastIP($networkDec) : &getBroadcastFromNet($networkDec); my $broadcast = ($ipv6) ? &ipv6Dec2ip($broadcastDec) : &dec2ip($broadcastDec); my $nrOfAddresses = Math::BigInt->new(2); my $nrOfFreeSubnets = 0; my $nrOfChilds = &getNrOfChilds($networkDec, $rootID, $ipv6); $state = &networkStateID2Name($state); if ($defSubnetSize) { my $freeSubnets = &getFreeSubnets($netID, 1, $defSubnetSize); $nrOfFreeSubnets = $freeSubnets; } else { # We don't wan't to count all free subnets with minimal cidr size if (&getConfigValue('gui', 'shownroffreesubnetswithmincidr')) { my $cntBox; foreach (&getFreeSubnets($netID)) { (undef, my $cidr) = split(/\//); $cntBox->{$cidr}++; } $nrOfFreeSubnets = '' if scalar keys %{$cntBox} > 0; foreach (sort keys %{$cntBox}) { $nrOfFreeSubnets .= ', ' if $nrOfFreeSubnets; $nrOfFreeSubnets .= $cntBox->{$_} . ' /' . $_; } } } $nrOfAddresses->bpow((($ipv6) ? 128 : 32) - (($ipv6) ? (($cidr < 64) ? 64 : $cidr) : $cidr)); my ($routPref, $subnet, $hostID) = (($ipv6) ? ($ipaddress =~ /(.{14}):(.{4}):(.*)/) : ()); $descr = &subDescription($descr, $netID) if $descr =~ /\%{2}/; my $plugins = &getPluginsForNet($netID); my $pluginOrder = {}; foreach (keys %{$plugins}) { my $seq = (exists $plugins->{$_}->{sequence}) ? $plugins->{$_}->{sequence} : 0; push @{$pluginOrder->{$seq}}, $_; } foreach (sort {$a<=>$b} keys %{$pluginOrder}) { foreach (@{$pluginOrder->{$_}}) { my $pluginID = $_; my $pluginFilename = &pluginID2File($pluginID); my $pluginInfos = (&getPluginInfos($pluginFilename))[1]; push @{$t->{V}->{plugins}}, { ID => $pluginID, name => &pluginID2Name($pluginID), netID => $netID, newLine => (exists $plugins->{$pluginID}->{newLine}) ? $plugins->{$pluginID}->{newLine} : 0, } if $pluginInfos->{ACTIVE}; } } $network =~ s/\/\d+// if &getConfigValue('gui', 'removecidrfromips') && $cidr == (($ipv6) ? 128 : 32); $t->{V}->{netBasicInfoHeader} = sprintf(_gettext("Details of " . (($cidr == (($ipv6) ? 128 : 32)) ? 'IP' : 'Network') . " %s"), $network); $t->{V}->{netBasicInfo} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Netaddress"), }, { target => 'value', type => 'label', value => $netaddress } ] }, ($ipv6) ? ({ elements => [ { target => 'key', type => 'label', value => _gettext("compressed"), }, { target => 'value', type => 'label', value => Net::IPv6Addr::to_string_compressed($netaddress), } ] }) : ({ elements => [ { target => 'key', type => 'label', value => _gettext("Netmask"), }, { target => 'value', type => 'label', value => $netmask } ] }), { elements => [ { target => 'key', type => 'label', value => _gettext("CIDR"), }, { target => 'value', type => 'label', value => $cidr } ] }, ($ipv6) ? () : ({ elements => [ { target => 'key', type => 'label', value => _gettext("Broadcast"), }, { target => 'value', type => 'label', value => $broadcast } ] }), ($ipv6) ? ( { elements => [ { target => 'key', type => 'label', value => _gettext("Routing Prefix"), }, { target => 'value', type => 'label', value => $routPref, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Subnet"), }, { target => 'value', type => 'label', value => $subnet, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("HostID"), }, { target => 'value', type => 'label', value => $hostID, } ] }) : (), { elements => [ { target => 'key', type => 'label', value => _gettext("# of available Adresses"), }, { target => 'value', type => 'label', value => $nrOfAddresses, } ] }, ($defSubnetSize || &getConfigValue('gui', 'shownroffreesubnetswithmincidr')) ? ( { elements => [ { target => 'key', type => 'label', value => sprintf(_gettext("# of free Subnets with CIDR '%s'"), ($defSubnetSize) ? $defSubnetSize : 'min'), }, { target => 'value', type => 'label', value => $nrOfFreeSubnets, } ] }) : (), { elements => [ { target => 'key', type => 'label', value => _gettext("# of assigned subnets"), }, { target => 'value', type => 'label', value => $nrOfChilds, } ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'label', value => "eHTML($descr), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Tags"), }, { target => 'value', type => 'label', value => "eHTML($tags), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'label', value => $state, } ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{createDate}, } ] }, ($maintenanceInfos->{modifyDate} =~ /^1970/) ? () : ( { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{modifyDate}, } ] }), ]; $t->{V}->{netFunctionsHeader} = _gettext("Menu"); $t->{V}->{netFunctionsFormName} = "netFunctions"; $t->{V}->{netFunctions} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'editNet', type => 'submit', value => _gettext("Edit"), disabled => (&checkRight('editNet') && &checkNetACL($netID, 'w')) ? 0 : 1, img => 'edit_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'splitNet', type => 'submit', value => _gettext("Split"), disabled => ((($ipv6 && $cidr != 128) || (!$ipv6 && $cidr != 32)) && &checkRight('editNet') && &checkNetACL($netID, 'w')) ? 0 : 1, img => 'split_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'showSubnets', type => 'submit', value => _gettext("Show Subnets"), disabled => ((($ipv6 && $cidr != 128) || (!$ipv6 && $cidr != 32)) && (&checkRight('showNetDet'))) ? 0 : 1, img => 'showSubnets_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'exportSubnets', type => 'submit', value => _gettext("Export Subnets"), disabled => ($nrOfChilds > 0) && ((($ipv6 && $cidr != 128) || (!$ipv6 && $cidr != 32)) && (&checkRight('showNetDet'))) ? 0 : 1, img => 'exportSubnets_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'delNet', type => 'submit', value => _gettext("Delete"), disabled => (&checkRight('editNet') && &checkNetACL($netID, 'w')) ? 0 : 1, img => 'del_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'abortShowNet', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], }, { target => 'single', type => 'floatEnd', }, ] }, ]; $t->{V}->{netFunctionHiddens} = [ { name => 'netID', value => $netID } ]; $t->{V}->{netInfoHeader} = sprintf(_gettext("Type info of " . (($cidr == (($ipv6) ? 128 : 32)) ? 'IP' : 'Network') . " %s"), $network); $t->{V}->{netInfo} = &getTemplateData($netID, $tmplID); } sub mkDelNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $netID = &getParam(1, undef, 'netID', 1); return unless defined $netID; my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); my $t = $HaCi::GUI::init::t; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $nrOfChilds = &getNrOfChilds($networkDec, $rootID, $ipv6); $t->{V}->{delNetHeader} = sprintf(_gettext("Do you really want to delete the Network '%s'?"), $network); $t->{V}->{delNetFormName} = 'delNet'; $t->{V}->{delNetMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("With all Subnets"), }, { target => 'value', type => 'checkbox', name => 'withSubnets', value => 1, descr => ' (' . sprintf(_gettext("This network contains %i subnets"), $nrOfChilds) . ')', checked => 0, disabled => ($nrOfChilds == 0) ? 1 : 0, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Lock network for X seconds"), }, { target => 'value', type => 'textfield', name => 'networkLock', value => 0, size => 5, maxlength => 10, }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 2, buttons => [ { name => 'commitDelNet', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, { name => 'abortDelNet', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{delNetHiddens} = [ { name => 'delNet', value => '1' }, { name => 'netID', value => $netID }, ]; } sub mkShowRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $rootID = &getParam(1, undef, 'rootID'); my $rootName = &rootID2Name($rootID); my $maintenanceInfos = &getMaintInfosFromRoot($rootID); my $nrOfNetworks = &getNrOfChilds(0, $rootID); $t->{V}->{rootInfoHeader} = sprintf(_gettext("Details of Root %s"), "eHTML($rootName)); $t->{V}->{rootInfo} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{description}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("IPv6"), }, { target => 'value', type => 'checkbox', name => '', value => '', descr => '', checked => $maintenanceInfos->{ipv6}, disabled => 1, }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Number of Networks"), }, { target => 'value', type => 'label', value => $nrOfNetworks } ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{createDate}, } ] }, ($maintenanceInfos->{modifyFrom}) ? ( { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{modifyDate}, } ] }) : (), ]; $t->{V}->{rootFunctionsHeader} = _gettext("Menu"); $t->{V}->{rootFunctionFormName} = "rootFunctionMenu"; $t->{V}->{rootFunctions} = [ { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'editRoot', type => 'submit', value => _gettext("Edit"), disabled => (&checkRight('editRoot') && &checkRootACL($rootID, 'w')) ? 0 : 1, img => 'edit_small.png', }, ], }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'delRoot', type => 'submit', value => _gettext("Delete"), disabled => (&checkRight('editRoot') && &checkRootACL($rootID, 'w')) ? 0 : 1, img => 'del_small.png', }, ], }, { target => 'single', type => 'buttons', buttons => [ { name => 'exportSubnets', type => 'submit', value => _gettext("Export Subnets"), disabled => ($nrOfNetworks > 0) && (&checkRight('showNetDet')) ? 0 : 1, img => 'exportSubnets_small.png', }, ], }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'abortShowRoot', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], }, ] }, ]; $t->{V}->{rootFunctionHiddens} = [ { name => 'rootID', value => $rootID } ]; } sub mkDelRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $rootID = &getParam(1, undef, 'rootID'); my $rootName = "eHTML(&rootID2Name($rootID)); my $nrOfChilds = &getNrOfChilds(0, $rootID); $t->{V}->{delRootHeader} = sprintf(_gettext("Do you really want to delete the Root %s?"), $rootName); $t->{V}->{delRootFormName} = 'delRoot'; $t->{V}->{delRootMenu} = [ { elements => [ { target => 'single', type => 'label', value => sprintf(_gettext("This Root has %i Subnets"), $nrOfChilds), }, ], }, { value => { type => 'hline', colspan => 1, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'commitDelRoot', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, { name => 'abortDelRoot', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{delRootHiddens} = [ { name => 'delRoot', value => '1' }, { name => 'rootID', value => $rootID }, ]; } sub mkSearch { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $func = &getParam(1, '', 'func'); my $bSearchForFree = ($func eq 'getFreeSubnetsFromSearch') ? 1 : 0; my $searchValue = &getParam(1, '', 'search'); my $tags = &getParam(0, [], 'tags'); my $tagOp = &getParam(1, 0, 'tagOp'); my $stats = $conf->{static}->{misc}->{networkstates}; my $state = &getParam(1, -1, 'state'); my $tmplID = &getParam(1, -1, 'tmplID'); my $types = &getNetworkTypes(1); map {$_->{name} = "eHTML($_->{name})} @{$types}; my $dbType = &getConfigValue('db', 'dbtype'); $searchValue =~ s/"/"/g; unshift @{$types}, {ID => -1, name => '[ALL]'}; my $availTagsT = &getAvailTags(); my $availTags = []; foreach (sort @{$availTagsT}) { push @{$availTags}, {ID => $_, name => $_}; } my $roots = &getRoots(1); unshift @{$roots}, {ID => -1, name => '[' . _gettext('ALL') . ']'}; map {my $h=$_;$h->{name} = "eHTML($h->{name}); $_ = $h;} @{$roots}; map {$_->{ID} = $_->{id}} @{$stats}; unshift @{$stats}, {ID => -1, name => '[ALL]'}; $t->{V}->{searchHeader} = ($func eq 'getFreeSubnetsFromSearch') ? _gettext("Search and get free network") : _gettext("Search"); $t->{V}->{searchResultHeader} = _gettext("Result"); $t->{V}->{gettext_network} = _gettext("Network"); $t->{V}->{gettext_description} = _gettext("Description"); $t->{V}->{gettext_state} = _gettext("Status"); $t->{V}->{gettext_tags} = _gettext("Tags"); $t->{V}->{gettext_nrOfFreeSubnets} = _gettext("# of free subnets"); $t->{V}->{searchFormName} = 'search'; $t->{V}->{buttonFocus} = 'searchButton'; $t->{V}->{bShowNrOfFreeSubs} = ((defined &getParam(1, undef, 'shNrOfFreeSubs')) ? 1 : 0); $t->{V}->{bSearchForFree} = $bSearchForFree; $t->{V}->{searchMenu} = [ { elements => [ { target => 'single', type => 'textfield', name => 'search', size => 40, maxlength => 255, value => $searchValue, focus => 1, onKeyDown => "submitOnEnter(event, 'searchButton')", colspan => 3, align => 'center', }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Wildcard"), }, { target => 'value', type => 'label', value => '*', bold => 1, colspan => 2, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Exact"), }, { target => 'value', type => 'checkbox', name => 'exact', value => 1, descr => '', checked => ((defined &getParam(1, undef, 'exact')) ? 1 : 0), colspan => 2, }, ], }, ((lc($dbType) eq 'mysql') ? { elements => [ { target => 'key', type => 'label', value => _gettext("Fuzzy Search"), }, { target => 'value', type => 'checkbox', name => 'fuzzy', value => 1, descr => '', checked => ((defined &getParam(1, undef, 'fuzzy')) ? 1 : 0), colspan => 2, } ] } : ()), ($bSearchForFree) ? ({ elements => [ { target => 'key', type => 'label', value => _gettext("CIDR"), }, { target => 'single', type => 'textfield', name => 'size', value => &getParam(1, 30, 'size'), size => 3, maxlength => 3, colspan => 2, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Amount"), }, { target => 'single', type => 'textfield', name => 'amount', value => &getParam(1, 3, 'amount'), size => 3, maxlength => 4, colspan => 2, }, ], }) : ({ elements => [ { target => 'key', type => 'label', value => _gettext("Show number of free subnets"), }, { target => 'value', type => 'checkbox', name => 'shNrOfFreeSubs', value => 1, descr => '', checked => ((defined &getParam(1, undef, 'shNrOfFreeSubs')) ? 1 : 0), colspan => 2, }, ], }), { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => [$state], colspan => 2, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'popupMenu', name => 'tmplID', size => 1, values => $types, selected => $tmplID, onChange => 'javascript:document.getElementById("chTmplID").value=1;submit()', colspan => 2, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Root"), }, { target => 'value', type => 'popupMenu', name => 'rootID', size => 1, values => $roots, selected => [], colspan => 2, }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Tags"), }, { target => 'single', type => 'popupMenu', name => 'tags', size => 3, values => $availTags, selected => $tags, multiple => 1, }, { target => 'single', type => 'radio', name => 'tagOp', values => [{value=>'AND',label=>_gettext('AND')}, {value=>'OR',label=>_gettext('OR')}], cr => 1, selected => ($tagOp) ? $tagOp : 'AND', colspan => 1, align => 'left', } ] } ]; if ($tmplID) { push @{$t->{V}->{searchMenu}}, ( { value => { type => 'hline', colspan => 3, } }, ); (my $menu, undef) = &getTemplateEntries($tmplID, 1, 0, 0, 1, 1); push @{$t->{V}->{searchMenu}}, @{$menu}; } push @{$t->{V}->{searchMenu}}, ( { value => { type => 'hline', colspan => 3, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 3, buttons => [ { name => 'searchButton', type => 'submit', value => _gettext(($bSearchForFree) ? 'Search for free networks' : "Search"), img => 'search_small.png', }, ] } ] } ); push @{$t->{V}->{searchHiddens}}, ( { name => 'func', value => $func }, { name => 'chTmplID', value => 0 }, ); } sub mkShowTemplates { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $netTypes = &getNetworkTypes(); foreach (@{$netTypes}) { $_->{name} = "eHTML($_->{name}); } $t->{V}->{newTemplateHeader} = _gettext("New Template"); $t->{V}->{newTemplateFormName} = 'newTemplate'; $t->{V}->{newTemplateMenu} = [ { elements => [ { target => 'single', type => 'label', value => _gettext("Name"), }, { target => 'single', type => 'textfield', name => 'tmplName', size => 20, maxlength => 255, onKeyDown => "submitOnEnter(event, 'newTmpl')", }, { target => 'single', type => 'buttons', buttons => [ { name => 'newTmpl', type => 'submit', value => _gettext("New"), img => 'add-template_small.png', }, ] } ], }, ]; $t->{V}->{newTemplateHiddens} = [ { name => 'tmplType', value => 'Nettype' }, ]; $t->{V}->{netTypeTemplatesHeader} = _gettext("Templates"); $t->{V}->{netTypeTemplatesFormName} = 'netTypeTmpl'; $t->{V}->{netTypeTemplatesMenu} = [ { elements => [ { target => 'single', type => 'popupMenu', name => 'tmplID', size => 1, values => $netTypes, colspan => 3, align => 'center', }, ], }, { value => { type => 'hline', colspan => 1, } }, { elements => [ { target => 'single', type => 'buttons', colspan => 3, align => 'center', buttons => [ { name => 'editNetTypeTmpl', type => 'submit', value => _gettext("Edit"), img => 'edit_small.png', }, { name => 'delTmpl', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, ], }, ], } ]; $t->{V}->{netTypeTemplatesHiddens} = [ { name => 'tmplType', value => 'Nettype' }, ]; } sub mkEditTemplate { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $tmplID = &getParam(1, undef, 'tmplID'); my $tmplName = (defined &getParam(1, undef, 'tmplName') && &getParam(1, '', 'tmplName') ne '') ? &getParam(1, '', 'tmplName') : &tmplID2Name($tmplID); my $tmpl = &getTemplate($tmplID); my $types = [ {ID => 0, name => _gettext('HLine')}, {ID => 1, name => _gettext('Textfield')}, {ID => 2, name => _gettext('Textarea')}, {ID => 3, name => _gettext('Popup-Menu')}, {ID => 4, name => _gettext('Text')}, ]; $t->{V}->{maxPositions} = $tmpl->{MaxPosition}; $t->{V}->{templateHeader} = ((defined &getParam(1, undef, 'newTmpl')) ? (_gettext("New")) : '') . sprintf(_gettext("Template '%s' for '%s'"), "eHTML($tmplName), _gettext(&getParam(1, '', 'tmplType') || '')); $t->{V}->{templateMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($tmpl->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $tmpl->{createDate}, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($tmpl->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $tmpl->{modifyDate}, } ] }, ]; $t->{V}->{editTemplateNameFormName} = 'editTmpl'; $t->{V}->{editTemplateNameHeader} = _gettext("Template name"); $t->{V}->{editTemplateNameMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name"), }, { target => 'value', type => 'textfield', name => 'tmplName', size => 20, maxlength => 255, value => "eHTML($tmplName) }, ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 2, buttons => [ { name => 'editTemplName', type => 'submit', value => _gettext("Change"), img => 'change_small.png', }, ] } ] } ]; $t->{V}->{editTemplateFormName} = 'editTmpl'; $t->{V}->{gettext_Templates} = _gettext("Templates"); $t->{V}->{editTemplateHeader} = _gettext("Structure"); $t->{V}->{editTemplateMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Position"), }, { target => 'value', type => 'popupMenu', name => 'position', size => 1, values => $tmpl->{Positions}, onChange => "javacsript:chkTmplPosition($tmpl->{MaxPosition});updTmplParamsFromPreview()", }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'popupMenu', name => 'TmplEntryType', size => 1, values => $types, onChange => 'javacsript:updTmplParams();', }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', colspan => 2, align => 'center', buttons => [ { name => 'submitAddTmplEntry', type => 'submit', value => _gettext("Add"), img => 'submit_small.png', }, { name => 'submitEditTmplEntry', type => 'submit', value => _gettext("Replace"), img => 'replace_small.png', }, { name => 'submitDeleteTmplEntry', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, { name => 'abortEditTmpl', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], }, ], } ]; $t->{V}->{editTemplateHiddens} = [ { name => 'tmplType', value => &getParam(1, '', 'tmplType') || '', }, { name => 'tmplEntryID', value => '' }, ]; push @{$t->{V}->{editTemplateHiddens}}, ( { name => 'tmplID', value => &getParam(1, undef, 'tmplID') }, ) if defined $tmplID; $t->{V}->{editTemplateEntryHeader} = _gettext("Parameters"); $t->{V}->{editTemplateEntryMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'TmplEntryParamDescr', size => 20, maxlength => 255, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Size"), }, { target => 'value', type => 'textfield', name => 'TmplEntryParamSize', size => 3, maxlength => 3, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Entries (separated with semicolons)"), }, { target => 'value', type => 'textfield', name => 'TmplEntryParamEntries', size => 20, maxlength => 1024, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Rows"), }, { target => 'value', type => 'textfield', name => 'TmplEntryParamRows', size => 3, maxlength => 3, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Columns"), }, { target => 'value', type => 'textfield', name => 'TmplEntryParamCols', size => 3, maxlength => 3, }, ], } ]; $t->{V}->{templatePreviewHeader} = _gettext("Preview"); ($t->{V}->{templatePreviewMenu}, $t->{V}->{templatePreviewHiddens}) = &getTemplateEntries(&getParam(1, undef, 'tmplID'), 0, 1, 0, 0, 1); } sub mkDelTmpl { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $tmplID = &getParam(1, undef, 'tmplID'); my $tmplName = &tmplID2Name($tmplID); $t->{V}->{delTmplHeader} = sprintf(_gettext("Do you really want to delete the Template %s?"), $tmplName); $t->{V}->{delTmplFormName} = 'delTmpl'; $t->{V}->{delTmplMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'commitDelTmpl', type => 'submit', value => _gettext("Delete"), }, { name => 'abortDelTmpl', type => 'submit', value => _gettext("Abort"), }, ], }, ], } ]; $t->{V}->{delTmplHiddens} = [ { name => 'delTmpl', value => '1' }, { name => 'tmplID', value => $tmplID }, ]; } sub mkShowGroups { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $groups = &getGroups(); foreach (@{$groups}) { $_->{name} = "eHTML($_->{name}); } $t->{V}->{newGroupHeader} = _gettext("New Group"); $t->{V}->{newGroupFormName} = 'showGroup'; $t->{V}->{newGroupMenu} = [ { elements => [ { target => 'single', type => 'label', value => _gettext("Name"), }, { target => 'single', type => 'textfield', name => 'groupName', size => 20, maxlength => 255, onKeyDown => "submitOnEnter(event, 'newGroup')", }, { target => 'single', type => 'buttons', buttons => [ { name => 'newGroup', type => 'submit', value => _gettext("New"), img => 'add-group_small.png', }, ] } ], }, ]; $t->{V}->{showGroupsHeader} = _gettext("Groups"); $t->{V}->{showGroupsFormName} = 'newGroup'; $t->{V}->{showGroupsMenu} = [ { elements => [ { target => 'single', type => 'popupMenu', name => 'groupID', size => 1, values => $groups, colspan => 3, align => 'center', }, ], }, { value => { type => 'hline', colspan => 1, } }, { elements => [ { target => 'single', type => 'buttons', colspan => 3, align => 'center', buttons => [ { name => 'editGroup', type => 'submit', value => _gettext("Edit"), img => 'edit_small.png', }, { name => 'delGroup', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, ], }, ], } ]; } sub mkEditGroup { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $groupID = &getParam(1, undef, 'groupID'); my $groupName = (defined &getParam(1, undef, 'groupName') && &getParam(1, '', 'groupName') ne '') ? &getParam(1, '', 'groupName') : &groupID2Name($groupID); my $group = &getGroup($groupID); my $bAdmin = 1 if $groupName eq 'Administrator'; if (&getParam(1, 0, 'editGroup')) { if (defined $groupID) { my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { warn "Cannot Edit Group. DB Error\n"; return 0; } my $group = ($groupTable->search(['*'], {ID => $groupID}))[0]; $q->delete('groupDescr'); $q->param('groupDescr', $group->{description}); if ($bAdmin) { foreach (keys %{$conf->{static}->{rights}}) { $q->delete('groupPerm_' . $_); $q->param('groupPerm_' . $_, 1); } } else { my $cnter = 0; my $cryptStr = substr($group->{permissions}, 1, length($group->{permissions}) - 1); my $permStr = &dec2bin(&lwd($cryptStr)); foreach (split//, substr($permStr, 1, length($permStr) - 1)) { if ($_) { $q->delete('groupPerm_' . $cnter); $q->param('groupPerm_' . $cnter, 1); } $cnter++; } } } } my $groupDescr = &getParam(1, '', 'groupDescr'); $groupDescr =~ s/"/"/g; $t->{V}->{editGroupHeader} = ((defined &getParam(1, undef, 'newGroup')) ? (_gettext("New ")) : '') . sprintf(_gettext("Group '%s'"), "eHTML($groupName)); $t->{V}->{editGroupMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name"), }, { target => 'value', type => 'label', value => "eHTML($groupName) } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'groupDescr', size => 25, maxlength => 255, value => "eHTML($groupDescr), }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($group->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $group->{createDate}, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($group->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $group->{modifyDate}, } ] }, ]; $t->{V}->{editGroupPermHeader} = _gettext("Permissions"); my $rightsSort = {}; foreach (keys %{$conf->{static}->{rights}}) { $rightsSort->{$conf->{static}->{rights}->{$_}->{order}} = $_; } foreach (sort {$a<=>$b} keys %{$rightsSort}) { my $cnter = $rightsSort->{$_}; push @{$t->{V}->{editGroupPermMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'groupPerm_' . $cnter, value => 1, descr => ' ' . _gettext($conf->{static}->{rights}->{$cnter}->{long}), checked => &getParam(1, 0, 'groupPerm_' . $cnter), disabled => ($bAdmin) ? 1 : 0, } ] }, ) }; $t->{V}->{editGroupButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitEditGroup', type => 'submit', value => _gettext("Submit"), img => 'submit_small.png', }, { name => 'abortEditGroup', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{editGroupHiddens} = [ { name => 'groupName', value => $groupName }, ]; push @{$t->{V}->{editGroupHiddens}}, ( { name => 'groupID', value => &getParam(1, undef, 'groupID') }, ) if defined $groupID; } sub mkDelGroup { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $groupID = &getParam(1, undef, 'groupID'); my $groupName = &groupID2Name($groupID); $t->{V}->{delGroupHeader} = sprintf(_gettext("Do you really want to delete the Group %s?"), $groupName); $t->{V}->{delGroupFormName} = 'delGroup'; $t->{V}->{delGroupMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'commitDelGroup', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, { name => 'abortDelGroup', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{delGroupHiddens} = [ { name => 'delGroup', value => '1' }, { name => 'groupID', value => $groupID }, ]; } sub mkDelUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $userID = &getParam(1, undef, 'userID'); my $userName = &userID2Name($userID); $t->{V}->{delUserHeader} = sprintf(_gettext("Do you really want to delete the User %s?"), $userName); $t->{V}->{delUserFormName} = 'delUser'; $t->{V}->{delUserMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'commitDelUser', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, { name => 'abortDelUser', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{delUserHiddens} = [ { name => 'delUser', value => '1' }, { name => 'userID', value => $userID }, ]; } sub mkShowUsers { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $users = &getUsers(); foreach (@{$users}) { $_->{name} = "eHTML($_->{name}); } $t->{V}->{newUserHeader} = _gettext("New User"); $t->{V}->{newUserFormName} = 'newUser'; $t->{V}->{newUserMenu} = [ { elements => [ { target => 'single', type => 'label', value => _gettext("Name"), }, { target => 'single', type => 'textfield', name => 'userName', size => 20, maxlength => 255, onKeyDown => "submitOnEnter(event, 'newUser')", }, { target => 'single', type => 'buttons', buttons => [ { name => 'newUser', type => 'submit', value => _gettext("New"), img => 'add-user_small.png', }, ] } ], }, ]; $t->{V}->{showUsersHeader} = _gettext("Users"); $t->{V}->{showUsersFormName} = 'showUsers'; $t->{V}->{showUsersMenu} = [ { elements => [ { target => 'single', type => 'popupMenu', name => 'userID', size => 1, values => $users, colspan => 3, align => 'center', }, ], }, { value => { type => 'hline', colspan => 1, } }, { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'editUser', type => 'submit', value => _gettext("Edit"), img => 'edit_small.png', }, { name => 'delUser', type => 'submit', value => _gettext("Delete"), img => 'del_small.png', }, ], }, ], } ]; } sub mkEditUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $userID = &getParam(1, 0, 'userID'); my $userName = (defined &getParam(1, undef, 'userName') && &getParam(1, '', 'userName') ne '') ? &getParam(1, '', 'userName') : &userID2Name($userID); my $user = &getUser($userID); my $groups = {}; foreach (@{&getGroups()}) { $groups->{$_->{ID}} = $_->{name}; } if (&getParam(1, 0, 'editUser')) { $q->delete('userDescr'); $q->param('userDescr', $user->{description}); foreach (split(/, /, $user->{groupIDs})) { s/\D//g; $q->delete('userGroup_' . $_); $q->param('userGroup_' . $_, 1); } } my $userDescr = &getParam(1, '', 'userDescr'); $userDescr =~ s/"/"/g; $t->{V}->{gettext_userpassword} = _gettext("Password is only for buildin 'HaCi' authentication!"); $t->{V}->{editUserHeader} = ((defined &getParam(1, undef, 'newUser')) ? (_gettext("New ")) : '') . sprintf(_gettext("User '%s'"), "eHTML($userName)); $t->{V}->{editUserMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name"), }, { target => 'value', type => 'label', value => "eHTML($userName) } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'userDescr', size => 25, maxlength => 255, value => "eHTML($userDescr) }, ], }, ($userName eq 'admin') ? () : ( { elements => [ { target => 'key', type => 'label', value => _gettext("Enable internal account"), }, { target => 'value', type => 'checkbox', name => 'enableInternalAccount', value => 1, descr => '', checked => &getParam(1, (($user->{password} // '') eq '') ? 0 : 1, 'enableInternalAccount'), onChange => 'toggleEnableIntAcc();', } ] }), { elements => [ { target => 'key', type => 'label', value => _gettext("Password"), }, { target => 'value', type => 'passwordfield', name => 'password1', size => 25, maxlength => 255, disabled => ($userName eq 'admin') ? 0 : (&getParam(1, (($user->{password} // '') eq '') ? 0 : 1, 'enableInternalAccount')) ? 0 : 1, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Password Validation"), }, { target => 'value', type => 'passwordfield', name => 'password2', size => 25, maxlength => 255, disabled => ($userName eq 'admin') ? 0 : (&getParam(1, (($user->{password} // '') eq '') ? 0 : 1, 'enableInternalAccount')) ? 0 : 1, }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($user->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $user->{createDate}, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($user->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $user->{modifyDate}, } ] }, ]; $t->{V}->{editUserGroupsHeader} = _gettext("Group Association"); foreach (sort {$a<=>$b} keys %{$groups}) { push @{$t->{V}->{editUserGroupsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'userGroup_' . $_, value => 1, descr => ' ' . "eHTML($groups->{$_}), checked => &getParam(1, 0, 'userGroup_' . $_), } ] }, ) }; $t->{V}->{editUserButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitEditUser', type => 'submit', value => _gettext("Submit"), img => 'submit_small.png', }, { name => 'abortEditUser', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{editUserHiddens} = [ { name => 'userName', value => $userName }, ]; push @{$t->{V}->{editUserHiddens}}, ( { name => 'userID', value => &getParam(1, undef, 'userID') }, ) if defined $userID; push @{$t->{V}->{editUserHiddens}}, ( { name => 'enableInternalAccount', value => 1 }, ) if $userName eq 'admin' } sub mkImportDNS { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $stats = $conf->{static}->{misc}->{networkstates}; my $roots = &getRoots(1); unshift @{$roots}, {ID => -1, name => '[' . _gettext('NEW') . ']'}; map {$_->{name} = "eHTML($_->{name})} @{$roots}; map {$_->{ID} = $_->{id}} @{$stats}; $t->{V}->{importDNSHeader} = _gettext("Import from DNS zonefiles"); $t->{V}->{importDNSTransHeader} = _gettext("Zonefile Transfer"); $t->{V}->{importDNSTransFormName} = 'importDNSTrans'; $t->{V}->{importDNSTransMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Nameserver"), }, { target => 'value', type => 'textfield', name => 'nameserver', size => 15, maxlength => 256, value => &getParam(1, '', 'nameserver'), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Domain"), }, { target => 'value', type => 'textfield', name => 'domain', size => 10, maxlength => 256, value => &getParam(1, '', 'domain'), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined &getParam(1, undef, 'targetRoot')) ? [&getParam(1, 0, 'targetRoot')] : []), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined &getParam(1, undef, 'state')) ? [&getParam(1, 0, 'state')] : []), } ] }, ]; $t->{V}->{importDNSTransButtons} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitImpDNSTrans', type => 'submit', value => _gettext("Start"), onClick => "showStatus(1);", img => 'submit_small.png', }, ], }, { type => 'buttons', target => 'single', buttons => [ { name => 'abortImpDNSTrans', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], } ], } ]; $t->{V}->{importDNSLocalHeader} = _gettext("Local Zonefile"); $t->{V}->{importDNSLocalFormName} = 'importDNSLocal'; $t->{V}->{importDNSLocalFormType} = 'multipart/form-data'; $t->{V}->{importDNSLocalMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Zonefile"), }, { target => 'value', type => 'file', name => 'zonefile', size => 25, maxlength => 64000, value => &getParam(1, 0, 'domain'), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Origin (optional)"), }, { target => 'single', type => 'textfield', name => 'origin', size => 25, maxlength => 255, value => &getParam(1, '', 'origin'), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined &getParam(1, undef, 'targetRoot')) ? [&getParam(1, 0, 'targetRoot')] : []), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined &getParam(1, undef, 'state')) ? [&getParam(1, 0, 'state')] : []), } ] }, ]; $t->{V}->{importDNSLocalButtons} = [ { elements => [ { type => 'buttons', target => 'single', buttons => [ { name => 'submitImpDNSLocal', type => 'submit', value => _gettext("Start"), onClick => "showStatus(1);", img => 'submit_small.png', }, ], }, { type => 'buttons', target => 'single', buttons => [ { name => 'abortImpDNSLocal', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], } ] }, ]; } sub mkImportConfig { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $stats = $conf->{static}->{misc}->{networkstates}; my $roots = &getRoots(1); map {$_->{name} = "eHTML($_->{name})} @{$roots}; unshift @{$roots}, {ID => -1, name => '[' . _gettext('NEW') . ']'}; my $sources = [ {ID => 'cisco', name => 'cisco'}, {ID => 'juniper', name => 'juniper'}, {ID => 'foundry', name => 'foundry'}, {ID => 'csv', name => 'csv'}, ]; map {$_->{ID} = $_->{id}} @{$stats}; $t->{V}->{importConfigHeader} = _gettext("Import from Config File"); $t->{V}->{importConfigFormName} = 'importConfig'; $t->{V}->{importConfigFormType} = 'multipart/form-data'; $t->{V}->{importConfigMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Config"), }, { target => 'value', type => 'file', name => 'config', size => 25, maxlength => 64000, value => &getParam(1, '', 'config'), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Source"), }, { target => 'value', type => 'popupMenu', name => 'source', size => 1, values => $sources, selected => ((defined &getParam(1, undef, 'source')) ? [&getParam(1, 0, 'source')] : []), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined &getParam(1, undef, 'state')) ? [&getParam(1, 0, 'state')] : []), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined &getParam(1, undef, 'targetRoot')) ? [&getParam(1, 0, 'targetRoot')] : []), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Overwrite existing"), }, { target => 'value', type => 'checkbox', name => 'overwrite', value => 1, descr => '', checked => 0, }, ], }, ]; $t->{V}->{importConfigButtons} = [ { elements => [ { type => 'buttons', target => 'single', align => 'center', colspan => 2, buttons => [ { name => 'submitImpConfig', type => 'submit', value => _gettext("Start"), onClick => "showStatus(1);", img => 'submit_small.png', }, ], }, { type => 'buttons', target => 'single', buttons => [ { name => 'abortImpConfig', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], } ] }, ]; } sub mkImportCSV { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $id = (defined &getParam(1, undef, 'configFileID')) ? &getParam(1, 0, 'configFileID') : $conf->{var}->{exportID}; my @data = &parseCSVConfigfile($id, 1); $conf->{var}->{STATUS}->{STATUS} = 'FINISH'; $conf->{var}->{STATUS}->{PERCENT} = 100; &setStatus(); my $nrOfCols = $conf->{var}->{nrOfCols}; my $netTypes = &getNetworkTypes(1); map {$_->{name} = "eHTML($_->{name})} @{$netTypes}; my $descrs = (defined &getParam(1, undef, 'tmplID') && &getParam(1, 0, 'tmplID')) ? &getTemplateEntries(&getParam(1, 0, 'tmplID'), 0, 0, 1, 0, 1) : {}; my @cols = ( {ID => 0, name => ''}, {ID => -1, name => _gettext('Network')}, {ID => -2, name => _gettext('Status')}, {ID => -3, name => _gettext('Description')}, {ID => -4, name => _gettext('Tags')}, {ID => -5, name => _gettext('Def. Subnet CIDR')}, ); foreach (keys %{$descrs}) { push @cols, {ID => $_, name => $descrs->{$_}} } &removeStatus(); $t->{V}->{csvPreview} = _gettext("CSV Preview"); $t->{V}->{gettext_noContent} = _gettext("Cannot parse any CSV Data"); $t->{V}->{csvData} = \@data; $t->{V}->{cols} = \@cols; $t->{V}->{nrOfCols} = $nrOfCols; $t->{V}->{noCSVContent} = 1 if $#data == -1; $t->{V}->{importCSVFormName} = 'importCVSMenu'; $t->{V}->{importCSVHeader} = _gettext("Separator"); $t->{V}->{importCSVMenu} = [ { elements => [ { target => 'single', type => 'textfield', name => 'sep', size => 1, maxlength => 1, value => &getParam(1, '', 'sep'), }, { type => 'buttons', buttons => [ { name => 'impCSVChangeSep', type => 'submit', value => _gettext("Change"), img => 'change_small.png', }, ], } ] }, ]; $t->{V}->{importCSVTypeHeader} = _gettext("Nettype"); $t->{V}->{importCSVTypeMenu} = [ { elements => [ { target => 'single', type => 'popupMenu', name => 'tmplID', size => 1, values => $netTypes, selected => ((defined &getParam(1, undef, 'tmplID')) ? [&getParam(1, 0, 'tmplID')] : []), }, { type => 'buttons', buttons => [ { name => 'impCSVChangeType', type => 'submit', value => _gettext("Change"), img => 'change_small.png', }, ], } ] }, ]; $t->{V}->{importCSVButtonMenu} = [ { elements => [ { type => 'buttons', buttons => [ { name => 'submitImpCSV', type => 'submit', value => _gettext("Import"), onClick => "showStatus(1);", img => 'import_small.png', }, { name => 'abortImpCSV', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], } ] }, ]; push @{$t->{V}->{importCSVHiddens}}, ( { name => 'config', value => &getParam(1, undef, 'config') }, { name => 'source', value => &getParam(1, undef, 'source') }, { name => 'state', value => &getParam(1, undef, 'state') }, { name => 'targetRoot', value => &getParam(1, undef, 'targetRoot') }, { name => 'overwrite', value => &getParam(1, 0, 'overwrite') }, { name => 'configFileID', value => $id }, ) } sub mkCompare { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $roots = &getRoots(1); map {$_->{name} = "eHTML($_->{name})} @{$roots}; &removeStatus(); $t->{V}->{compareHeader} = _gettext("Compare"); $t->{V}->{compareFormName} = 'compare'; $t->{V}->{compareMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Source"), }, { target => 'value', type => 'popupMenu', name => 'leftRootID', size => 1, values => $roots, selected => ((defined &getParam(1, undef, 'leftRootID')) ? [&getParam(1, 0, 'leftRootID')] : []), }, { target => 'key', type => 'label', value => _gettext("Target"), }, { target => 'value', type => 'popupMenu', name => 'rightRootID', size => 1, values => $roots, selected => ((defined &getParam(1, undef, 'rightRootID')) ? [&getParam(1, 0, 'rightRootID')] : []), }, { target => 'key', type => 'label', value => _gettext("Save result under"), }, { target => 'value', type => 'textfield', name => 'resultName', size => 10, maxlength => 256, value => &getParam(1, '', 'resultName'), focus => 1, }, ] } ]; $t->{V}->{compareButtons} = [ { elements => [ { type => 'buttons', target => 'single', buttons => [ { name => 'compareButton', type => 'submit', value => _gettext("Compare"), onClick => "showStatus(1);", img => 'compare_small.png', }, ], }, { type => 'buttons', target => 'single', buttons => [ { name => 'abortCompareButton', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], } ], }, ]; } sub expandNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $level = shift; my $bEditTree = shift; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; $q->delete('editTree'); $q->param('editTree', $bEditTree); $conf->{var}->{STATUS} = {TITLE => 'Expanding Network...', STATUS => 'Runnung...'}; &setStatus(); &expand('+', 'network', $networkDec, $rootID); $s->param('currNet', $networkDec); $s->param('currRootID', $rootID); my $return = &genTreeNetwork($rootID, $networkDec, $level); $conf->{var}->{STATUS}->{STATUS} = 'FINISH'; &setStatus(); return $return; } sub genTreeNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $level = shift; my $parentOnly = shift || 0; my $t = $HaCi::GUI::init::t; my $tree = new HaCi::Tree; my $ipv6 = &rootID2ipv6($rootID); unless ($conf->{var}->{authenticated}) { warn "Not Authenticated!\n"; return _gettext("Not Authenticated!!!"); } $tree->setNewRoot($rootID) if $networkDec; $tree->setRootV6($rootID, $ipv6) if $networkDec; &mkTreeNetwork(\$tree, $rootID, $ipv6, (($ipv6) ? Math::BigInt->new($networkDec) : $networkDec), 1, $parentOnly); $t->{V}->{editTree} = (defined $HaCi::HaCi::q->param('editTree') && $HaCi::HaCi::q->param('editTree')) ? 1 : 0; $t->{V}->{root} = $tree->print_html_root($rootID) if $networkDec == 0; $t->{V}->{networks} = $tree->print_html_networks($rootID, $level, $networkDec); $t->{V}->{page} = ($networkDec) ? 'treeNetworkTable' : 'treeRootNetworkTable'; $t->{V}->{noHeader} = 1; my $html_output = ''; $t->{T}->process($conf->{static}->{path}->{templateinit}, $t->{V}, \$html_output) || die $t->{T}->error(); return $html_output; } sub reduceNetwork { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $networkDec = shift; my $level = shift; my $bEditTree = shift; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; $q->delete('editTree'); $q->param('editTree', $bEditTree); return _gettext("Not Authenticated!!!") unless $conf->{var}->{authenticated}; &expand('-', 'network', $networkDec, $rootID); $s->param('currNet', $networkDec); $s->param('currRootID', $rootID); return &genTreeNetwork($rootID, $networkDec, $level, 1); } sub expandRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $bEditTree = shift; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; $q->delete('editTree'); $q->param('editTree', $bEditTree); return _gettext("Not Authenticated!!!") unless $conf->{var}->{authenticated}; &expand('+', 'root', $rootID); $s->param('currNet', ''); $s->param('currRootID', $rootID); return &genTreeNetwork($rootID, 0, 0); } sub reduceRoot { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $rootID = shift; my $bEditTree = shift; my $s = $HaCi::HaCi::session; my $q = $HaCi::HaCi::q; $q->delete('editTree'); $q->param('editTree', $bEditTree); return _gettext("Not Authenticated!!!") unless $conf->{var}->{authenticated}; &expand('-', 'root', $rootID); $s->param('currNet', ''); $s->param('currRootID', $rootID); return &genTreeNetwork($rootID, 0, 0, 1); } sub mkSplitNet { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $netID = &getParam(1, undef, 'netID'); return unless defined $netID; my $types = &getNetworkTypes(1); my $stats = $conf->{static}->{misc}->{networkstates}; my ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID); my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); return unless &checkSpelling_Net($network, $ipv6); map {$_->{name} = "eHTML($_->{name})} @{$types}; map {$_->{ID} = $_->{id}} @{$stats}; my $maintenanceInfos = &getMaintInfosFromNet($netID); my $tmplID = $maintenanceInfos->{tmplID} || 0; my $descr = $maintenanceInfos->{description} || ''; my $stateNr = $maintenanceInfos->{state} || 0; my ($ipaddress, $cidr) = split(/\//, $network); my $netmask = ($ipv6) ? 0 : &getNetmaskFromCidr($cidr); my $netaddress = ($ipv6) ? $ipaddress : &dec2ip(&getNetaddress($ipaddress, $netmask)); my $broadcast = ($ipv6) ? &getV6BroadcastIP($networkDec) : &dec2ip(&getBroadcastFromNet($networkDec)); my $state = &networkStateID2Name($stateNr); my $availPlugins = &getPlugins(); my $availPluginst = {}; foreach (keys %{$availPlugins}) { $availPluginst->{$availPlugins->{$_}->{NAME}} = $_; } $t->{V}->{netBasicInfoHeader} = sprintf(_gettext("Details of Network %s"), $network); $t->{V}->{netBasicInfo} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Network"), }, { target => 'value', type => 'label', value => $network } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Netaddress"), }, { target => 'value', type => 'label', value => $netaddress } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("CIDR"), }, { target => 'value', type => 'label', value => $cidr } ] },(($ipv6) ? () : ({ elements => [ { target => 'key', type => 'label', value => _gettext("Netmask"), }, { target => 'value', type => 'label', value => $netmask } ] })), { elements => [ { target => 'key', type => 'label', value => _gettext("Broadcast"), }, { target => 'value', type => 'label', value => $broadcast } ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'label', value => "eHTML($descr), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'label', value => $state, } ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{createFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{createDate}, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified by"), }, { target => 'value', type => 'label', value => "eHTML($maintenanceInfos->{modifyFrom}), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $maintenanceInfos->{modifyDate}, } ] }, ]; $t->{V}->{splitNetHeader} = _gettext("Split Details"); $t->{V}->{splitNetFormName} = 'splitNetMenu'; $t->{V}->{splitNetMenu} = [ { elements => [ { target => 'single', type => 'label', value => _gettext("Split Network into these pieces:"), colspan => 3, align => 'center', bold => 1, underline => 1, }, ] }, ]; my $elements = []; my @values = (); for (($cidr + 1) .. (($ipv6) ? 128 : 32)) { my $amount = 2**($_ - $cidr); push @values, { value => $_, label => "$amount * /$_" }; last if $amount > 1024; } push @{$t->{V}->{splitNetMenu}}, ( { elements => [ { target => 'single', type => 'radio', name => 'splitCidr', values => \@values, cr => 1, selected => ($cidr + 1), colspan => 3, align => 'center', } ] }, { value => { type => 'hline', colspan => 3, } }, { elements => [ { target => 'single', type => 'label', value => _gettext("Settings for the new Networks"), colspan => 3, align => 'center', bold => 1, underline => 1, }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Template for the Descriptions"), }, { target => 'single', type => 'textfield', name => 'descrTemplate', size => 20, maxlength => 255, value => "eHTML($descr) . ' %d', }, { target => 'value', type => 'buttons', align => 'left', buttons => [ { name => 'showDescrHelperBtn', type => '', onClick => "showDescrHelper()", value => '1', img => 'info_small.png', picOnly => 1, title => _gettext("Available variables"), }, ], }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => $stateNr, colspan => 2, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'popupMenu', name => 'tmplID', size => 1, values => $types, selected => [$tmplID], colspan => 2, } ] }, { value => { type => 'hline', colspan => 3, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Delete this network"), }, { target => 'value', type => 'checkbox', name => 'delParentNet', value => 1, descr => '', checked => 0, colspan => 2, }, ], }, ); $t->{V}->{helpDescrHeader} = _gettext("Available variables"); $t->{V}->{helpDescrMenu} = [ { elements => [ { target => 'single', type => 'label', value => '', width => '0.1em', }, { target => 'single', type => 'label', value => '' . _gettext('Name') . '', width => '3em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Description') . '', width => '8em', align => 'center', }, { target => 'single', type => 'label', value => '' . _gettext('Insert') . '', width => '1em', align => 'center', } ] }, { value => { type => 'hline', colspan => 4, } }, ]; foreach (sort keys %{$availPluginst}) { my $ID = $availPluginst->{$_}; next unless $availPlugins->{$ID}->{ACTIVE}; my $elements = []; my $api = (exists $availPlugins->{$ID}->{API} && ref($availPlugins->{$ID}->{API}) eq 'ARRAY') ? $availPlugins->{$ID}->{API} : []; foreach (@{$api}) { my $name = $_->{name}; my $descr = $_->{descr}; push @{$elements}, ( {elements => [ { target => 'single', type => 'label', value => '', width => '0.1em', }, { target => 'single', type => 'label', value => $name, width => '3em', align => 'left', }, { target => 'single', type => 'label', value => $descr, width => '8em', align => 'left', }, { target => 'single', type => 'buttons', align => 'center', width => '1em', buttons => [ { name => 'insertPluginVar', type => '', onClick => "insertPluginVar('descrTemplate','\%\%$availPlugins->{$ID}->{NAME}\%$name\%\%')", value => '1', img => 'insert_small.png', picOnly => 1, title => sprintf(_gettext("Insert '%s' from Plugin '%s'"), $name, $availPlugins->{$ID}->{NAME}), }, ], }, ]}, ); }; push @{$t->{V}->{helpDescrMenu}}, ( { elements => [ { target => 'single', type => 'label', value => sprintf(_gettext("Plugin '%s'"), $availPlugins->{$ID}->{NAME}), bold => 1, colspan => 4, }, ], }, @{$elements} ) if $#{$api} != -1; } push @{$t->{V}->{helpDescrMenu}}, ( { value => { type => 'hline', colspan => 4, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 4, buttons => [ { name => 'pluginInfoCloser', onClick => "hideDescrHelper()", value => '1', img => 'cancel_small.png', picOnly => 1, title => _gettext("Close Overview"), }, ], }, ] } ); $t->{V}->{splitNetButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitSplitNet', type => 'submit', value => _gettext("Split"), img => 'split_small.png', onClick => 'showStatus(1)', }, { name => 'abortSplitNet', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', } ] } ] }, ]; $t->{V}->{splitNetHiddens} = [ { name => 'netID', value => $netID } ]; } sub mkCombineNets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $types = &getNetworkTypes(1); my $stats = $conf->{static}->{misc}->{networkstates}; my $oneOK = 0; map {$_->{name} = "eHTML($_->{name})} @{$types}; map {$_->{ID} = $_->{id}} @{$stats}; $t->{V}->{combineNetsHeader} = _gettext("Combine Networks"); $t->{V}->{combineNetsFormName} = 'combineNetsMenu'; my $box = {}; my $selectedNets = &getParam(0, [], 'selectedNetworks'); foreach (@{$selectedNets}) { my ($network, $rootID) = split/_/, $_, 2; my $ipv6 = &rootID2ipv6($rootID); my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); my $parentDec = (defined $parent) ? $parent->{network} : 0; $box->{$rootID}->{IPV6} = $ipv6; push @{$box->{$rootID}->{PARENTS}->{$parentDec}}, $networkDec; } my $cnter = 0; my $box1 = {}; my $lastNet = undef; foreach (sort {$a<=>$b} keys %{$box}) { my $rootID = $_; my $ipv6 = $box->{$rootID}->{IPV6}; my @parents = (); if ($ipv6) { @parents = &ipv6Sort(keys %{$box->{$rootID}->{PARENTS}}); } else { @parents = sort {$a<=>$b} keys %{$box->{$rootID}->{PARENTS}}; } foreach (@parents) { my $parent = $_; my @nets = (); if ($ipv6) { @nets = &ipv6Sort(@{$box->{$rootID}->{PARENTS}->{$parent}}); } else { @nets = sort {$a<=>$b} @{$box->{$rootID}->{PARENTS}->{$parent}}; } foreach (@nets) { my $currNet = $_; $currNet = Math::BigInt->new($currNet) if $ipv6; if (defined $lastNet) { my $netBefore = &getDBNetworkBefore($rootID, $currNet, $ipv6, 1); $cnter++ if $lastNet != $netBefore; } push @{$box1->{$rootID}->{NETS}->{$cnter}}, $currNet; $box1->{$rootID}->{IPV6} = $ipv6; $lastNet = $currNet; } } } my $transCnter = 0; foreach (sort {$a<=>$b} keys %{$box1}) { my $rootID = $_; my $ipv6 = $box1->{$rootID}->{IPV6}; foreach (sort {$a<=>$b} keys %{$box1->{$rootID}->{NETS}}) { my $cnter = $_; my $first = ${$box1->{$rootID}->{NETS}->{$cnter}}[0]; $first = Math::BigInt->new($first) if $ipv6; my $last = ${$box1->{$rootID}->{NETS}->{$cnter}}[-1]; $last = Math::BigInt->new($last) if $ipv6; my $cidr = $first % 256; my $firstNetaddress = ($ipv6) ? (&netv6Dec2IpCidr($first))[0] : &getIPFromDec($first); my $lastBroadcast = ($ipv6) ? &getV6BroadcastIP($last) : &getBroadcastFromNet($last); my $descr = ''; my $stateNr = 0, my $tmplID = 0; my $base = Math::BigInt->new(2); my $currIPT = ($ipv6) ? Math::BigInt->new($firstNetaddress) : $firstNetaddress; my $currNetaddress = ($ipv6) ? &ipv6DecCidr2NetaddressV6Dec($currIPT, $cidr) : &getNetaddress($currIPT, &getNetmaskFromCidr($cidr)); my $currIP = ($ipv6) ? $currNetaddress->copy()->badd($base->copy()->bpow(128 - $cidr)) : $currNetaddress + (2**(32 - $cidr)); while ($currIP <= $lastBroadcast) { last if $cidr < 1; $cidr--; $currNetaddress = ($ipv6) ? &ipv6DecCidr2NetaddressV6Dec($currIPT, $cidr) : &getNetaddress($currIPT, &getNetmaskFromCidr($cidr)); $currIP = ($ipv6) ? $currNetaddress->copy()->badd($base->copy()->bpow(128 - $cidr)) : $currNetaddress + (2**(32 - $cidr)); } my $newNetRevisedIPDec = ($ipv6) ? &ipv6DecCidr2NetaddressV6Dec($firstNetaddress, $cidr) : &getNetaddress($firstNetaddress, &getNetmaskFromCidr($cidr)); my $newNetRevisedNetDec = ($ipv6) ? &ipv6DecCidr2netv6Dec($newNetRevisedIPDec, $cidr) : &net2dec(&dec2ip($newNetRevisedIPDec) . '/' . $cidr); my $newNetRevised = ($ipv6) ? &netv6Dec2net(&ipv6DecCidr2netv6Dec($newNetRevisedIPDec, $cidr)) : &dec2ip($newNetRevisedIPDec) . '/' . $cidr; my $newNetBroadcast = ($ipv6) ? &getV6BroadcastNet(&ipv6DecCidr2netv6Dec($newNetRevisedIPDec, $cidr), 128) : &net2dec(&dec2ip(&getBroadcastFromNet(&net2dec($newNetRevised))) . '/' . 32); if ($transCnter) { push @{$t->{V}->{combineNetsMenu}}, ( { value => { type => 'hline' } }, ); } push @{$t->{V}->{combineNetsMenu}}, ( { elements => [ { target => 'single', type => 'label', bold => 1, underline => 1, align => 'center', colspan => 2, value => sprintf(_gettext("%i. Block"), ($transCnter + 1)) }, ] }, ); my $cnter1 = 0; my @nets = (); if ($ipv6) { @nets = &ipv6Sort(@{$box1->{$rootID}->{NETS}->{$cnter}}); } else { @nets = sort {$a<=>$b} @{$box1->{$rootID}->{NETS}->{$cnter}}; } foreach (@nets) { my $networkDec = $_; $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($networkDec) : ''; my $netID = &getNetID($rootID, $networkDec, $ipv6ID); my $maintenanceInfos = &getMaintInfosFromNet($netID); push @{$t->{V}->{combineNetsMenu}}, ( { elements => [ { type => 'label', hidden => 1, noShow => 1, name => 'combineNets_' . $transCnter . '_source', value => $networkDec, }, { target => 'key', type => 'label', value => ($cnter1) ? '+' : ' ' }, { target => 'value', type => 'label', value => (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . ' (' . "eHTML($maintenanceInfos->{description}) . ')', } ] }, ); $cnter1++; } my $bOK = 1; my $error = ''; my $ipv6ID = ($ipv6) ? &netv6Dec2ipv6ID($newNetRevisedNetDec) : ''; my $netID = &getNetID($rootID, $newNetRevisedNetDec, $ipv6ID); if (defined $netID && $netID) { $bOK = 0; $error = _gettext("This Network allready exists!"); } my $netBefore = &getDBNetworkBefore($rootID, $first, $ipv6); if (defined $netBefore && $netBefore > $newNetRevisedNetDec) { $bOK = 0; $error .= '
' if $error; $error .= sprintf(_gettext("Foreign Networks will be
included (e.g. '%s')"), (($ipv6) ? &netv6Dec2net($netBefore) : &dec2net($netBefore))); } my $netNext = &getNextDBNetwork($rootID, $ipv6, $last); if (defined $netNext && $netNext->{network} < $newNetBroadcast) { $bOK = 0; $error .= '
' if $error; $error .= sprintf(_gettext("Foreign Networks will be
included (e.g. '%s')"), (($ipv6) ? &netv6Dec2net($netNext->{network}) : &dec2net($netNext->{network}))); } $oneOK ||= $bOK; push @{$t->{V}->{combineNetsMenu}}, ( { elements => [ { type => 'label', hidden => 1, noShow => 1, name => 'combineNets_' . $transCnter . '_rootID', value => $rootID, }, { type => 'label', hidden => 1, noShow => 1, name => 'combineNets_' . $transCnter . '_result', value => $newNetRevisedNetDec, }, { target => 'key', type => 'label', bold => 1, value => '=' }, { target => 'value', type => 'label', bold => 1, color => '#' . (($bOK) ? '00AA00' : 'AA0000'), value => $newNetRevised . (($bOK) ? '' : ' (' . $error . ')'), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Combine these networks"), }, { target => 'value', type => 'checkbox', name => 'combineNetsNr', value => $transCnter, descr => '', checked => 0, disabled => ($bOK) ? 0 : 1, }, ], }, ); push @{$t->{V}->{combineNetsMenu}}, ( { elements => [ { target => 'single', type => 'label', bold => 1, underline => 1, value => _gettext("Settings for the new Network"), colspan => 2, align => 'center', }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'combineNets_' . $transCnter . '_descr', size => 20, maxlength => 255, value => "eHTML($descr), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'combineNets_' . $transCnter . '_state', size => 1, values => $stats, selected => $stateNr, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Type"), }, { target => 'value', type => 'popupMenu', name => 'combineNets_' . $transCnter . '_tmplID', size => 1, values => $types, selected => [$tmplID], } ] }, ) if $bOK; $transCnter++; } } $t->{V}->{combineNetsButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitCombineNets', type => 'submit', value => _gettext("Combine Networks"), disabled => ($oneOK) ? 0 : 1, img => 'combine_small.png', }, { name => 'abortCombineNets', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', } ] } ] }, ]; } sub mkShowPlugins { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $plugins = &getPlugins(); my $hacidInfo = &getHaCidInfo(); push @{$t->{V}->{floatingPopupMenu}}, ( { elements => [ { name => 'floatingPopupContent', target => 'single', type => 'label', value => '', width => '100%', bold => 0, align => 'center', wrap => 1, }, ] }, { value => { type => 'hline', colspan => 1, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'hideFloatingPopup', type => '', value => _gettext("Abort"), img => 'cancel_small.png', onClick => "hideFloatingPopup()", picOnly => 1, title => _gettext("Close Error Details"), }, ], }, ] } ); $t->{V}->{showPluginsHeader} = _gettext("Plugins"); $t->{V}->{showPluginsFormName} = 'editPlugins'; push @{$t->{V}->{showPluginsMenu}}, ( { elements => [ { target => 'single', type => 'label', value => _gettext("Name"), width => '15em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("Version"), width => '4em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("active"), width => '4em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("default"), width => '4em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("Configure"), width => '2em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("on Demand"), width => '6em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("recurrent"), width => '5em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("last Run"), width => '10em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("Runtime"), width => '10em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("Error"), width => '10em', bold => 1, align => 'center', }, ] }, ); my $box = {}; foreach (keys %{$plugins}) { $box->{$plugins->{$_}->{NAME}} = $_; } foreach (sort keys %{$box}) { my $ID = $box->{$_}; my $error = $plugins->{$ID}->{LASTERROR}; my $globConf = 1 if ($plugins->{$ID}->{RECURRENT} && ($#{$conf->{static}->{plugindefaultglobrecurrentmenu}} != -1 || $#{$plugins->{$ID}->{GLOBMENURECURRENT}} != -1)) || ($plugins->{$ID}->{ONDEMAND} && ($#{$conf->{static}->{plugindefaultglobondemandmenu}} != -1 || $#{$plugins->{$ID}->{GLOBMENUONDEMAND}} != -1)); $error =~ s/'/\\'/g; $error =~ s/"//g; $error =~ s/\n/\\n/g; $error =~ s/\r/\\r/g; $error = '' . $error . ''; push @{$t->{V}->{showPluginsMenu}}, ( { elements => [ { target => 'single', type => 'label', value => $plugins->{$ID}->{NAME}, width => '15em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $plugins->{$ID}->{VERSION}, width => '4em', align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'checkbox', name => 'pluginActives', value => $ID, descr => '', checked => $plugins->{$ID}->{ACTIVE}, width => '4em', align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'checkbox', name => 'pluginDefaults', value => $ID, descr => '', checked => $plugins->{$ID}->{DEFAULT}, width => '4em', disabled => ($plugins->{$ID}->{ONDEMAND}) ? 0 : 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'editPluginGlobConf', type => 'submit', onClick => "setPluginID('$ID')", value => '1', img => 'config_small.png', img => 'config_small' . (($globConf) ? '' : '_disabled') . '.png', picOnly => 1, title => sprintf(_gettext("Configure Plugin '%s'"), $plugins->{$ID}->{NAME}), disabled => ($globConf) ? 0 : 1, }, ], }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'checkbox', name => '', value => '', descr => '', checked => $plugins->{$ID}->{ONDEMAND}, width => '6em', disabled => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'checkbox', name => '', value => '', descr => '', checked => $plugins->{$ID}->{RECURRENT}, width => '5em', disabled => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $plugins->{$ID}->{LASTRUN}, width => '10em', align => 'center', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $plugins->{$ID}->{RUNTIME} . 's', width => '10em', align => 'left', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'buttons', align => 'center', buttons => [ { name => 'pluginInfo', onClick => "showFloatingPopup('" . sprintf(_gettext("Error Details for Plugin %s"), $plugins->{$ID}->{NAME}) . "', '$error')", value => '1', img => ($plugins->{$ID}->{LASTERROR}) ? 'info_small.png' : 'info_small_disabled.png', picOnly => 1, title => _gettext("Error Details"), disabled => ($plugins->{$ID}->{LASTERROR}) ? 0 : 1, }, ], }, ] }, ); } $t->{V}->{showPluginsButtonsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'submitshowPlugins', type => 'submit', value => _gettext("Submit"), img => 'submit_small.png', }, { name => 'abortshowPlugins', type => 'submit', value => _gettext("Back"), img => 'back_small.png', } ] } ] }, ]; $t->{V}->{HaCidInfoHeader} = _gettext('HaCi Daemon Infos'); if (defined $hacidInfo) { push @{$t->{V}->{HaCidInfoMenu}}, ( { elements => [ { target => 'single', type => 'label', value => _gettext("PID"), width => '5em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("CPU"), width => '5em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("RSS"), width => '5em', bold => 1, align => 'center', }, { target => 'single', width => '0.5em', type => 'vline', align => 'center', }, { target => 'single', type => 'label', value => _gettext("TIME"), width => '5em', bold => 1, align => 'center', }, ], }, { elements => [ { target => 'single', type => 'label', value => $hacidInfo->{PARENT}->{PID}, width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hacidInfo->{PARENT}->{CPU} . '%', width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hacidInfo->{PARENT}->{RSS} . ' kb', width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hacidInfo->{PARENT}->{TIME}, width => '5em', }, ], }, ); if ($#{$hacidInfo->{CHILDS}} > -1) { push @{$t->{V}->{HaCidInfoMenu}}, ( { value => { type => 'hline', colspan => 7, } }, { elements => [ { target => 'single', type => 'label', value => _gettext("Childs"), width => '5em', colspan => 7, bold => 1, }, ] }, ); } foreach (@{$hacidInfo->{CHILDS}}) { my $hash = $_; push @{$t->{V}->{HaCidInfoMenu}}, ( { elements => [ { target => 'single', type => 'label', value => $hash->{PID}, width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hash->{CPU} . '%', width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hash->{RSS} . ' kb', width => '5em', }, { target => 'single', width => '0.5em', type => 'label', value => ' ', align => 'center', }, { target => 'single', type => 'label', value => $hash->{TIME}, width => '5em', }, ], }, ); } } else { push @{$t->{V}->{HaCidInfoMenu}}, ( { elements => [ { target => 'single', type => 'label', value => _gettext("The HaCi Daemon is not started."), width => '5em', bold => 1, align => 'center', }, ] } ); } } sub showPlugin { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $pluginID = shift; my $netID = shift; my $t = $HaCi::GUI::init::t; my $maintenanceInfos = &getMaintInfosFromNet($netID); my $plugin = &pluginID2Name($pluginID); my $pluginFilename = &pluginID2File($pluginID); my $pluginInfos = (&getPluginInfos($pluginFilename))[1]; my $pluginConfValues = &getPluginConfValues($pluginID, $netID); my $error = $pluginInfos->{ERROR} || ''; return if !$pluginInfos->{ACTIVE} && !$pluginInfos->{ERROR}; unless ($error) { eval { require "HaCi/Plugins/$pluginFilename.pm"; }; if ($@) { $error = "Cannot load Plugin: $plugin: $@"; warn "$error\n"; } } my $plug; unless ($error) { eval { $plug = "HaCi::Plugins::$plugin"->new($pluginID, $conf->{var}->{TABLES}->{pluginValue}); }; if ($@) { $error = "Error while initiating Module: $@"; warn "$error\n"; } } unless ($error) { if ($pluginInfos->{ONDEMAND}) { my $lastRun = time; eval { $plug->run_onDemand($maintenanceInfos, $pluginConfValues); }; if ($@) { $error = "Error while running Module: $@"; warn "$error\n"; } unless ($plug->can('ERROR')) { warn "Plugin '$plugin' has no ERROR Method!\n"; } else { if ($plug->ERROR()) { unless ($plug->can('ERRORSTR')) { warn "Plugin '$plugin' has no ERRORSTR Method!\n"; } else { $error = "Error while running Module: " . $plug->ERRORSTR(); warn "$error\n"; } } } &updatePluginLastRun($pluginID, $lastRun, (time - $lastRun), $error); } } my $plugShow = { HEADER => '', BODY => [] }; unless ($error) { eval { $plugShow = $plug->show($netID); }; if ($@) { $error .= "Error while loading Module Output: $@"; warn "$error\n"; } } if ($error && !$conf->{user}->{gui}->{showerrordetails}) { $error = sprintf(_gettext("Error while loading Plugin '%s'. Details in Error Logfile."), $plugin); } $t->{V}->{plugin} = { HEADER => $plugShow->{HEADER}, BODY => (($error) ? [{elements=>[{target=>'single',type=>'label',value=>"
" . $error . "
"}]}] : $plugShow->{BODY}), }; $t->{V}->{page} = 'showPlugin'; $t->{V}->{pluginID} = $pluginID; $t->{V}->{noHeader} = 1; my $html_output = ''; $t->{T}->process($conf->{static}->{path}->{templateinit}, $t->{V}, \$html_output) || die $t->{T}->error(); return $html_output; } sub mkShowStatus { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $fresh = shift || 0; my $status = ($fresh) ? {STATUS => 'Starting...'} : &getStatus(); unless ($fresh) { $status->{STATUS} = 'FINISH' unless exists $status->{STATUS} && $status->{STATUS}; } $status->{DATA} = '' unless exists $status->{DATA}; $status->{TITLE} = '' unless exists $status->{TITLE}; my $returnStr = $status->{TITLE} . ': ' . $status->{DATA}; $returnStr .= " ($status->{PERCENT}%)" if $status->{PERCENT}; $returnStr = '' unless $returnStr; return ($status->{STATUS}, $returnStr); } sub mkShowPluginConf { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $global = shift || 0; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $pluginID = &getParam(1, undef, 'pluginID'); my $netID = &getParam(1, undef, 'netID'); my $plugin = &pluginID2Name($pluginID); $netID = -1 unless defined $netID; my ($rootID, $networkDec, $ipv6); ($rootID, $networkDec, $ipv6) = &netID2Stuff($netID) if $netID != -1; my $network = ($global) ? '' : ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); $t->{V}->{pluginConfHeader} = sprintf(_gettext((($global) ? 'Global ' : '') . "Configuration of '%s'" . (($global) ? '' : " for '%s'")), $plugin, $network); $t->{V}->{pluginConfMenu} = &getPluginConfMenu($pluginID, $global, $netID); $t->{V}->{pluginConfFormName} = 'pluginConf'; $t->{V}->{pluginConfHiddens} = [ { name => 'pluginID', value => $pluginID }, { name => 'global', value => $global }, { name => 'netID', value => $netID }, ]; } sub mkShowSubnets { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $thisScript = $conf->{var}->{thisscript}; my $maxSubnetSize = &getConfigValue('gui', 'maxsubnetsize'); my $netID = &getParam(1, undef, 'netID'); my $net = &getMaintInfosFromNet($netID); my $ipv6 = ($net->{ipv6ID}) ? 1 : 0; my $subnetSize = &getParam(1, $net->{defSubnetSize}, 'subnetSize'); my $network = ($ipv6) ? &netv6Dec2net($net->{network}) : &dec2net($net->{network}); my $cidr = (split/\//, $network, 2)[1]; my @freeSubnetst = &getFreeSubnets($netID, 0, $subnetSize); my $freeSubnets = []; my $subnetSizes = [{ID => 0, name => 'min'}]; my $subnetSizesV6 = [{ID => 0, name => 'min'}]; map { push @{$freeSubnets}, { net => $_, dec => ($ipv6) ? &netv62Dec($_) : &net2dec($_), }; } @freeSubnetst; # Generate Subnet Cidr Menu { $cidr = 0 unless $cidr; map { push @{$subnetSizes}, {ID => $_, name => $_} } (($cidr + 1) .. ((32 < ($cidr + $maxSubnetSize)) ? 32 : ($cidr + $maxSubnetSize))); map { push @{$subnetSizesV6}, {ID => $_, name => $_} } (($cidr + 1) .. ((128 < ($cidr + $maxSubnetSize)) ? 128 : ($cidr + $maxSubnetSize))); } $t->{V}->{showSubnetsHeader} = sprintf(_gettext("Show free Subnets of '%s' with CIDR '%s'"), $network, ($subnetSize) ? $subnetSize : 'min'); $t->{V}->{freeSubnetsHeader} = _gettext("Free Subnets"); $t->{V}->{showSubnetsFormName} = 'showSubnets'; $t->{V}->{buttonFocus} = 'showSubnets'; $t->{V}->{bAddNet} = &checkRight('addNet'); $t->{V}->{freeSubnets} = $freeSubnets; $t->{V}->{noResults} = ($#freeSubnetst == -1) ? 1 : 0; $t->{V}->{gettext_subnet} = _gettext('Subnet'); $t->{V}->{gettext_nr} = _gettext('No.'); $t->{V}->{gettext_create} = _gettext('Create'); $t->{V}->{gettext_nothing_found} = _gettext('No free Subnets with this CIDR available'); $t->{V}->{rootID} = $net->{rootID}; $t->{V}->{thisScript} = $thisScript; $t->{V}->{showSubnetsMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("CIDR"), }, { target => 'value', type => 'popupMenu', name => 'subnetSize', size => 1, values => ($ipv6) ? $subnetSizesV6 : $subnetSizes, selected => $subnetSize, }, ] }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', colspan => 2, align => 'center', buttons => [ { name => 'showSubnets', type => 'submit', value => _gettext("Show"), img => 'showSubnets_small.png', }, { name => 'abortShowSubnets', type => 'submit', value => _gettext("Back"), img => 'back_small.png', } ] } ], }, ]; $t->{V}->{showSubnetsHiddens} = [ { name => 'netID', value => $netID }, ]; } sub mkShowSettings { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $subMenu = ''; if (&getParam(1, 0, 'changeOwnPW')) { $subMenu = 'chOwnPW'; &mkChOwnPW(); } elsif (&getParam(1, 0, 'showViewSettings')) { $subMenu = 'showViewSettings'; &mkShowViewSettings(); } $t->{V}->{settingsMenuHeader} = _gettext("Menu"); $t->{V}->{settingsMenuFormName} = "settingsMenu"; $t->{V}->{settingsSubMenu} = $subMenu; $t->{V}->{settingsMenu} = [ { elements => [ { target => 'single', type => 'buttons', buttons => [ { name => 'changeOwnPW', type => 'submit', value => _gettext("Change own Password"), img => 'password_small.png', }, { name => 'showViewSettings', type => 'submit', value => _gettext("View"), img => 'showSettingsView_small.png', }, { name => 'abortShowSettings', type => 'submit', value => _gettext("Back"), img => 'back_small.png', }, ], }, ] }, ]; $t->{V}->{settingsMenuHiddens} = [ { name => 'func', value => 'showSettings' } ]; } sub mkChOwnPW { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; $t->{V}->{chOwnPWHeader} = _gettext("Change own Password"); $t->{V}->{chOwnPWFormName} = "chOwnPW"; $t->{V}->{chOwnPW} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Old Password"), }, { target => 'value', type => 'passwordfield', name => 'oldPassword', size => 25, maxlength => 255, focus => 1, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("New Password"), }, { target => 'value', type => 'passwordfield', name => 'newPassword', size => 25, maxlength => 255, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Password Validation"), }, { target => 'value', type => 'passwordfield', name => 'newPasswordVal', size => 25, maxlength => 255, onKeyDown => "submitOnEnter(event, 'commitChOwnPW')", }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 2, buttons => [ { name => 'commitChOwnPW', type => 'submit', value => _gettext("Change"), img => 'change_small.png', }, { name => 'abortChOwnPW', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{chOwnPWHiddens} = [ { name => 'func', value => 'showSettings' } ]; } sub mkShowViewSettings { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $layouts = $conf->{static}->{gui}->{layouts} || []; my $s = $HaCi::HaCi::session; my $settings = $s->param('settings'); my $layout = (defined $settings && exists $settings->{layout}) ? ${$settings->{layout}}[0] : $conf->{user}->{gui}->{style} || $conf->{static}->{gui}->{style}; my $bShowTreeStruct = (defined $settings && exists $settings->{bShowTreeStruct}) ? ${$settings->{bShowTreeStruct}}[0] : $conf->{user}->{gui}->{showTreeStructure} || 0; my @temps = (defined $settings && exists $settings->{temp}) ? @{$settings->{temp}} : []; map {$_->{ID} = $_->{id}} @{$layouts}; $t->{V}->{viewSettingsHeader} = _gettext("View Settings"); $t->{V}->{viewSettingsFormName} = "viewSettings"; $t->{V}->{viewSettings} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Layout"), }, { target => 'single', type => 'popupMenu', name => 'setting_layout', size => 1, values => $layouts, selected => $layout, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Show Tree Structure"), }, { target => 'value', type => 'checkbox', name => 'setting_bShowTreeStruct', value => 1, descr => '', checked => $bShowTreeStruct, disabled => 0, }, ], }, { value => { type => 'hline', colspan => 5, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 5, buttons => [ { name => 'commitViewSettings', type => 'submit', value => _gettext("Change"), img => 'change_small.png', }, { name => 'abortViewSettings', type => 'submit', value => _gettext("Abort"), img => 'cancel_small.png', }, ], }, ], } ]; $t->{V}->{viewSettingsHiddens} = [ { name => 'func', value => 'showSettings' }, { name => 'settingParams', value => 'layout' }, { name => 'settingParams', value => 'bShowTreeStruct' }, ]; } sub mkShowAuditLogs { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; my $pageSize = &getParam(1, 100, 'pageSize'); my $page = &getParam(1, 1, 'page'); my $sortBy = &getParam(1, 'ts', 'sortBy'); my $bReverse = &getParam(1, 0, 'reverse'); my $searchFor = &getParam(1, '', 'searchFor'); $pageSize =~ s/\D//g; $page =~ s/\D//g; $pageSize ||= 100; $page ||= 1; $page++ if &getParam(1, 0, 'showAuditLogsRight'); $page-- if &getParam(1, 0, 'showAuditLogsLeft'); my ($auditLogs, $totalAuditLogs) = &getAuditLogs($pageSize, ($page - 1), $sortBy, $bReverse, $searchFor, 1); $auditLogs ||= []; $totalAuditLogs ||= 0; my $totalNrOfPages = int(($totalAuditLogs / $pageSize) + (($totalAuditLogs % $pageSize) ? 1 : 0)); $totalNrOfPages ||= 1; if ($page > $totalNrOfPages) { $page = $totalNrOfPages; ($auditLogs, $totalAuditLogs) = &getAuditLogs($pageSize, ($page - 1)); } my $pageValues = []; map {push @{$pageValues}, {ID => $_, name => $_}} (10, 50, 100, 500, 1000); my $sortByValues = [ {ID => 'ts', name => _gettext('Timestamp')}, {ID => 'username', name => _gettext('Username')}, {ID => 'action', name => _gettext('Action')}, {ID => 'object', name => _gettext('Object')}, ]; $t->{V}->{auditLogs} = $auditLogs; $t->{V}->{showAuditLogs} = _gettext("Audit logs"); $t->{V}->{gettext_noContent} = _gettext("No audit logs available"); $t->{V}->{gettext_timestamp} = _gettext("Timestamp"); $t->{V}->{gettext_username} = _gettext("Username"); $t->{V}->{gettext_action} = _gettext("Action"); $t->{V}->{gettext_object} = _gettext("Object"); $t->{V}->{gettext_value} = _gettext("Value"); $t->{V}->{gettext_error} = _gettext("Error"); $t->{V}->{showAuditLogsFormName} = 'showAuditLogsMenu'; $t->{V}->{showAuditLogsHeader} = _gettext("Show audit logs"); $t->{V}->{showAuditLogsMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Number of entries"), }, { target => 'value', type => 'popupMenu', name => 'pageSize', size => 1, values => $pageValues, selected => [$pageSize], }, ], }, { elements => [ { target => 'key', type => 'label', value => sprintf(_gettext("Page (%d .. %d)"), 1, $totalNrOfPages) }, { target => 'value', type => 'textfield', name => 'page', size => 3, maxlength => 7, value => $page, onKeyDown => "submitOnEnter(event, 'commitShowAuditLogs')", } ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Search") }, { target => 'value', type => 'textfield', name => 'searchFor', size => 10, maxlength => 64, value => $searchFor, onKeyDown => "submitOnEnter(event, 'commitShowAuditLogs')", } ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Sort by"), }, { target => 'value', type => 'popupMenu', name => 'sortBy', size => 1, values => $sortByValues, selected => [$sortBy] }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Reverse"), }, { target => 'value', type => 'checkbox', name => 'reverse', value => 1, descr => '', checked => $bReverse, disabled => 0, }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 5, buttons => [ (($page == 1) ? () : { name => 'showAuditLogsLeft', type => 'submit', value => ' ', img => 'left.png', }), { name => 'commitShowAuditLogs', type => 'submit', value => _gettext("Show"), img => 'search_small.png', }, (($page == $totalNrOfPages) ? () : { name => 'showAuditLogsRight', type => 'submit', value => ' ', img => 'right.png', }), ], }, ], } ]; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Importer/0000755000175000000000000000000012475447616015137 5ustar fighterrootHaCi/modules/HaCi/Importer/Cisco.pm0000644000175000000000000000531311323136211016510 0ustar fighterrootpackage HaCi::Importer::Cisco; use warnings; use strict; use base qw(Class::Accessor); HaCi::Importer::Cisco->mk_accessors(qw(error errorStr config status)); use HaCi::Mathematics qw/getCidrFromNetmask dec2ip getNetaddress getNetmaskFromCidr ipv6DecCidr2NetaddressV6Dec ipv62dec ipv6Dec2ip/; sub new { my $class = shift; my $self = {}; bless $self, $class; } sub parse { my $self = shift; my $config = $self->config; my $status = $self->status || 0; unless ($config) { $self->error(1); $self->errorStr('No Config given!'); } my $hostname = ''; my $bInt = 0; my $intName = ''; my $intDescr = ''; foreach (split/\n/, $config) { chomp; next if /^\s*$/; $hostname = $1 if /^hostname\s+([[:alpha:]][[:alnum:]\-_]+[[:alnum:]])\s*$/; # http://www.cisco.com/en/US/docs/ios/fundamentals/command/reference/cf_f1.html#wp1015617 + '_' if ($bInt && /^!/) { $bInt = 0; $intName = ''; $intDescr = ''; } if (!$bInt && /^interface\s+(.*)/) { $bInt = 1; $intName = $1; } $intDescr = $1 if $bInt && /^\s+description\s+(.*)/; if ($bInt) { if (/^\s+ip address\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { my ($ip, $netmask) = ($1, $2); my $cidr = &getCidrFromNetmask($netmask); my $netaddress = ($cidr == 32) ? $ip : &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); &pushNet($self, $ip, $netaddress, $cidr, 32, ($intDescr ne '') ? $intDescr : $intName); } elsif (/^\s+ipv6 address\s+([\w\d:]+)\/(\d{1,3})/) { my ($ip, $cidr) = ($1, $2); my $netaddress = &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($ip), $cidr)); eval {&pushNet($self, Net::IPv6Addr::to_string_preferred($ip), Net::IPv6Addr::to_string_preferred($netaddress), $cidr, 128, ($intDescr ne '') ? $intDescr : $intName)}; warn $@ if $@; } } } return ($hostname, $self->{newNets}); } sub pushNet { my $self = shift; my $ip = shift; my $netaddress = shift; my $cidr = shift; my $cidrDef = shift; my $descr = shift; if ($ip eq $netaddress) { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => $descr }; } } else { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => '' }; } unless (exists $self->{newNetCheck}->{"$ip/$cidrDef"}) { $self->{newNetCheck}->{"$ip/$cidrDef"} = 1; push @{$self->{newNets}}, { ip => $ip, cidr => $cidrDef, descr => $descr }; } } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Importer/Foundry.pm0000644000175000000000000000556711305012064017110 0ustar fighterrootpackage HaCi::Importer::Foundry; use warnings; use strict; use base qw(Class::Accessor); HaCi::Importer::Foundry->mk_accessors(qw(error errorStr config status)); use HaCi::Mathematics qw/getCidrFromNetmask dec2ip getNetaddress getNetmaskFromCidr ipv6DecCidr2NetaddressV6Dec ipv62dec ipv6Dec2ip/; sub new { my $class = shift; my $self = {}; bless $self, $class; } sub parse { my $self = shift; my $config = $self->config; my $status = $self->status || 0; unless ($config) { $self->error(1); $self->errorStr('No Config given!'); } my $hostname = ''; my $bInt = 0; my $intName = ''; my $intDescr = ''; foreach (split/\n/, $config) { chomp; next if /^\s*$/; $hostname = $1 if /^hostname\s+(\w+)/; if ($bInt && /^!/) { $bInt = 0; $intName = ''; $intDescr = ''; } if (!$bInt && /^interface\s+(.*)\s*$/) { $bInt = 1; $intName = $1; } if ($bInt) { if (/^\s+port-name\s+(.*)\s*$/) { $intDescr = $1; } elsif (/^\s+ip address\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { my ($ip, $netmask) = ($1, $2); my $cidr = &getCidrFromNetmask($netmask); my $netaddress = ($cidr == 32) ? $ip : &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); &pushNet($self, $ip, $netaddress, $cidr, 32, ($intDescr ne '') ? $intDescr : $intName); } elsif (/^\s+ip address\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})/) { my ($ip, $cidr) = ($1, $2); my $netaddress = &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); &pushNet($self, $ip, $netaddress, $cidr, 32, ($intDescr ne '') ? $intDescr : $intName); } elsif (/^\s+ipv6 address\s+([\w\d:]+)\/(\d{1,3})/) { my ($ip, $cidr) = ($1, $2); my $netaddress = &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($ip), $cidr)); eval {&pushNet($self, Net::IPv6Addr::to_string_preferred($ip), Net::IPv6Addr::to_string_preferred($netaddress), $cidr, 128, ($intDescr ne '') ? $intDescr : $intName)}; warn $@ if $@; } } } return ($hostname, $self->{newNets}); } sub pushNet { my $self = shift; my $ip = shift; my $netaddress = shift; my $cidr = shift; my $cidrDef = shift; my $descr = shift; if ($ip eq $netaddress) { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => $descr }; } } else { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => '' }; } unless (exists $self->{newNetCheck}->{"$ip/$cidrDef"}) { $self->{newNetCheck}->{"$ip/$cidrDef"} = 1; push @{$self->{newNets}}, { ip => $ip, cidr => $cidrDef, descr => $descr }; } } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Importer/Juniper.pm0000644000175000000000000001143111305012064017061 0ustar fighterrootpackage HaCi::Importer::Juniper; use warnings; use strict; use base qw(Class::Accessor); HaCi::Importer::Juniper->mk_accessors(qw(error errorStr config status)); use HaCi::Mathematics qw/dec2ip getNetaddress getNetmaskFromCidr ipv6DecCidr2NetaddressV6Dec ipv62dec ipv6Dec2ip/; sub new { my $class = shift; my $self = {}; bless $self, $class; } sub parse { my $self = shift; my $config = $self->config; my $status = $self->status || 0; my $box = {}; my $parent = {}; my @parents = (); my $lc = 0; my $secName = 'ROOT'; my @secNames = (); unless ($config) { $self->error(1); $self->errorStr('No Config given!'); } foreach (split/\n/, $config) { chomp; s#/\*.*\*/##; $lc = 0 if $lc && /\*\//; next if $lc; $lc = 1 if /\/\*/; s#/\*.*##; s#.*\*/##; s/#.*//; next if /^\s*$/; if (/^\s*(.+)\s*\{/) { push @secNames, $secName; $secName = $1; $secName =~ s/\s+$//; push @parents, $parent; $parent = $box; $box = {}; s/^\s*(.+)\s*\{//; } if (/\}/) { $parent->{$secName} = $box; $box = $parent; $parent = pop @parents; $secName = pop @secNames; s/\}//; } if (/\s*(\S+)\s(.*)$/) { (my $key = $1) =~ s/;$// if $1; (my $descr = $2) =~ s/;$// if $2; $descr =~ s/^\s*"//; $descr =~ s/"\s*$//; $box->{$key} = $descr if $key; } } $parent->{$secName} = $box; $box = $parent; &getInts($self, $box->{ROOT}->{interfaces}); if (exists $box->{ROOT}->{groups} && ref($box->{ROOT}->{groups}) eq 'HASH') { foreach (keys %{$box->{ROOT}->{groups}}) { my $group = $_; &getInts($self, $box->{ROOT}->{groups}->{$group}->{interfaces}) if exists $box->{ROOT}->{groups}->{$group}->{interfaces} && ref($box->{ROOT}->{groups}->{$group}->{interfaces}) eq 'HASH'; } } return (0, $self->{newNets}); } sub getInts { my $self = shift; my $key = shift; foreach (keys %{$key}) { my $int = $_; next unless ref($key->{$int}) eq 'HASH'; foreach (keys %{$key->{$int}}) { next unless ref($key->{$int}->{$_}) eq 'HASH' && /^unit /; my $unit = $_; my $descr = $key->{$int}->{$unit}->{description}; my $ipv4 = $key->{$int}->{$unit}->{'family inet'}->{address}; my $ipv6 = $key->{$int}->{$unit}->{'family inet6'}->{address}; my ($sUnit) = $unit =~ /unit (.*)/; $descr = $int unless $descr; $descr .= " ($sUnit)" if $sUnit != 0; if ($ipv4) { ($ipv4, my $cidr) = split(/\//, $ipv4); $cidr = 32 unless defined $cidr; my $netaddress = ($cidr == 32) ? $ipv4 : &dec2ip(&getNetaddress($ipv4, &getNetmaskFromCidr($cidr))); &pushNet($self, $ipv4, $netaddress, $cidr, 32, $descr); } elsif ($ipv6) { ($ipv6, my $cidr) = split(/\//, $ipv6); $cidr = 128 unless defined $cidr; my $netaddress = ($cidr == 128) ? $ipv6 : &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($ipv6), $cidr)); eval {&pushNet($self, Net::IPv6Addr::to_string_preferred($ipv6), Net::IPv6Addr::to_string_preferred($netaddress), $cidr, 128, $descr)}; warn $@ if $@; } for ('family inet', 'family inet6') { my $type = $_; if (exists $key->{$int}->{$unit}->{$type} && ref($key->{$int}->{$unit}->{$type}) eq 'HASH') { foreach (keys %{$key->{$int}->{$unit}->{$type}}) { next unless ref($key->{$int}->{$unit}->{$type}->{$_}) eq 'HASH' && /^address /; s/^address //; my ($ip, $cidr) = split(/\//); my $netaddress = ''; if ($ip =~ /^[\d\.]+$/) { $cidr = 32 unless defined $cidr; $netaddress = &dec2ip(&getNetaddress($ip, &getNetmaskFromCidr($cidr))); &pushNet($self, $ip, $netaddress, $cidr, 32, $descr); } else { $cidr = 128 unless defined $cidr; $netaddress = &ipv6Dec2ip(&ipv6DecCidr2NetaddressV6Dec(&ipv62dec($ip), $cidr)); eval {&pushNet($self, Net::IPv6Addr::to_string_preferred($ip), Net::IPv6Addr::to_string_preferred($netaddress), $cidr, 128, $descr)}; warn $@ if $@; } } } } } } } sub pushNet { my $self = shift; my $ip = shift; my $netaddress = shift; my $cidr = shift; my $cidrDef = shift; my $descr = shift; if ($ip eq $netaddress) { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => $descr }; } } else { unless (exists $self->{newNetCheck}->{"$netaddress/$cidr"}) { $self->{newNetCheck}->{"$netaddress/$cidr"} = 1; push @{$self->{newNets}}, { ip => $netaddress, cidr => $cidr, descr => '' }; } unless (exists $self->{newNetCheck}->{"$ip/$cidrDef"}) { $self->{newNetCheck}->{"$ip/$cidrDef"} = 1; push @{$self->{newNets}}, { ip => $ip, cidr => $cidrDef, descr => $descr }; } } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Plugins/0000755000175000000000000000000012475447616014757 5ustar fighterrootHaCi/modules/HaCi/Plugins/CrossRootNetTrace.pm0000644000175000000000000002241512470146641020671 0ustar fighterrootpackage HaCi::Plugins::CrossRootNetTrace; # Cross root tracing plugin for HaCi # # Based on code written by # Andreas Lewitzki # Pär Karlsson use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw(dec2net net2dec netv6Dec2net ipv62dec); use HaCi::Utils qw(getNetworkParentFromDB getNetworkChilds getMaintInfosFromNet getTemplateData rootID2Name); use HaCi::GUI::gettext qw(_gettext); use Carp; use Encode; use English qw(-no_match_vars); use Net::DNS; use Data::Dumper; our $conf; *conf = \$HaCi::Conf::conf; our $session; *session = \$HaCi::HaCi::session; our $INFO = { name => 'CrossRootNetTrace', version => '3.0', onDemand => 1, description => _gettext('This plugin tracks overlaying networks across all roots'), menuOnDemand => [ { NAME => 'dnsResolver', DESCR => _gettext('Special DNS Resolver for this network'), TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => '8.8.8.8,8.8.4.4' }, ], globMenuOnDemand => [ { NAME => 'dnsResolver', DESCR => _gettext('DNS Resolver'), TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => '8.8.8.8,8.8.4.4' }, ] }; my $RE_IPV4_NETWORK = qr{ (?:\d+[.]){3}\d+\/\d+ }msx; my $RE_IPV6_NETWORK = qr{ (?:\w+[:])+\w+\/\d+ }msx; my $RE_NETWORK = qr{ \A (?: $RE_IPV4_NETWORK|$RE_IPV6_NETWORK) \z }imsx; sub makeKeyVal { my $self = shift; my $key = shift; my $val = shift, my $url = shift; $key = encode( 'latin1', decode( 'utf8', $key ) ); return { elements => [ { colspan => 2, target => 'key', type => 'label', value => $url ? "" . _gettext($key) . '' : _gettext($key) }, { target => 'value', type => 'label', color => '#0000ff', value => $url ? "" . $val . '' : _gettext($val) }, ] }; } sub ipSort { if ( $a =~ $RE_NETWORK && $b =~ $RE_NETWORK ) { my ( $a_ip, $a_cidr ) = split m{/}msx, $a; my ( $b_ip, $b_cidr ) = split m{/}msx, $b; my ( $a_addr, $b_addr ); if ( $a =~ $RE_IPV4_NETWORK && $b =~ $RE_IPV4_NETWORK ) { $a_addr = net2dec($a); $b_addr = net2dec($b); } elsif ( $a =~ $RE_IPV6_NETWORK && $b =~ $RE_IPV6_NETWORK ) { $a_addr = ipv62dec($a_ip); $b_addr = ipv62dec($b_ip); } else { carp "Cannot compare mixed IPv4 and IPv6 addresses: $a <=> $b"; return -1; } if ( $a_addr > $b_addr ) { return 1; } if ( $a_addr == $b_addr ) { if ( $a_cidr > $b_cidr ) { return 1; } else { return -1; } } } return -1; } sub run_onDemand { my $self = shift; my $networkRef = shift; my $config = shift; my $netID = $networkRef->{ID}; my $networkDec = $networkRef->{network}; my $ipv6 = $networkRef->{ipv6}; my $network = ($ipv6) ? netv6Dec2net($networkDec) : dec2net($networkDec); $self->{networkRef} = $networkRef; $self->{inetnum} = $network; $self->{username} = $session->param('username'); if ( $network !~ $RE_NETWORK ) { $self->warnl("'$network' does not look like a network!"); return; } my $supernets = &getNetworkParentFromDB(-1, $networkDec, $ipv6, 0); my @subnets = &getNetworkChilds($netID, 0, 1, 1); if (ref($supernets) eq 'ARRAY' && $#{$supernets} > -1) { foreach (@{$supernets}) { my $sNetRef = $_; my $sNetID = $sNetRef->{ID}; my $sNetDec = $sNetRef->{network}; my $sNetInfo = &getMaintInfosFromNet($sNetID); my $sNetIPv6 = $sNetInfo->{ipv6}; my $sNet = ($sNetIPv6) ? netv6Dec2net($sNetDec) : dec2net($sNetDec); if (0) { # do we need template data for output? my $templateData = &getTemplateData($sNetID, $sNetInfo->{tmplID}, 1); $sNetInfo->{tmplData} = $templateData; } push @{$self->{data}->{up}->{$sNet}}, $sNetInfo; } } if ($#subnets > -1) { foreach (@subnets) { my $sNetRef = $_; my $sNetID = $sNetRef->{ID}; my $sNetDec = $sNetRef->{network}; my $sNetInfo = &getMaintInfosFromNet($sNetID); my $sNetIPv6 = $sNetInfo->{ipv6}; my $sNet = ($sNetIPv6) ? netv6Dec2net($sNetDec) : dec2net($sNetDec); my $cidr = $sNetDec % 256; if (0) { # do we need template data for output? my $templateData = &getTemplateData($sNetID, $sNetInfo->{tmplID}, 1); $sNetInfo->{tmplData} = $templateData; } push @{$self->{data}->{cidr}->{$cidr}->{$sNet}}, $sNetInfo; push @{$self->{data}->{down}->{$sNet}}, $sNetInfo; } } if (($network =~ /\/32$/ || $network =~ /\/128$/) && exists $config->{dnsResolver} && $config->{dnsResolver}) { my $resolver = Net::DNS::Resolver->new(nameservers => [split(/\s*,\s*/, $config->{dnsResolver})]); my $ip = (split(/\//, $network, 2))[0]; my $reply = $resolver->query($ip, 'PTR'); my $hostname = ''; if ($reply) { foreach ($reply->answer()) { my $rr = $_; next unless $rr->type() eq 'PTR'; $hostname = $rr->ptrdname(); } } if ($hostname) { push @{$self->{data}->{down}->{$network}}, { rootID => -2, description => $hostname }; } } return 1; } sub show { my $self = shift; my $show = { HEADER => sprintf( _gettext("Cross Root Net Trace for %s"), $self->{inetnum} ), }; my $ipnet = $self->{inetnum}; my $result = $self->{data}; my @sorted = sort { ipSort($_) } keys %{ $result->{up} }; for my $net (@sorted) { my $ndata = $self->{data}->{up}->{$net}; my ( $ipaddress, $cidr ) = split m{/}msx, $net; push @{ $show->{BODY} }, { elements => [ { align => 'center', colspan => 3, target => 'value', type => 'vline', }, ] }; push @{ $show->{BODY} }, { elements => [ { bold => 1, underline => 1, align => 'center', colspan => 3, color => ( $net eq $ipnet ? 'blue' : 'black' ), target => 'value', type => 'label', value => _gettext( ( $net eq $ipnet ? ">> $net <<" : $net ) ) } ] }; for my $data ( sort { $a->{rootID} cmp $b->{rootID} } @{$ndata} ) { my $rootID = $data->{rootID}; my $rootName = ($rootID == -2) ? 'DNS' : &rootID2Name($rootID); push @{ $show->{BODY} }, $self->makeKeyVal( $rootName, $data->{description}, "?jumpToButton=1&rootIDJump=$rootID&jumpTo=$ipaddress" ); } } @sorted = sort { ipSort($_) } keys %{ $result->{down} }; for my $cidr ( sort { $a <=> $b } keys %{ $result->{cidr} } ) { if ( scalar keys %{ $result->{cidr}->{$cidr} } == 1 ) { my $net = [ keys %{ $result->{cidr}->{$cidr} } ]->[0]; my $ndata = $result->{down}->{$net}; my ( $ipaddress, $cidr ) = split m{/}msx, $net; push @{ $show->{BODY} }, { elements => [ { bold => 1, underline => 1, align => 'center', colspan => 3, target => 'value', type => 'label', color => ( $net eq $ipnet ? 'blue' : 'black' ), value => _gettext( ( $net eq $ipnet ? ">> $net <<" : $net ) ) } ] }; for my $data ( sort { $a->{rootID} cmp $b->{rootID} } @{$ndata} ) { my $rootID = $data->{rootID}; my $rootName = ($rootID == -2) ? 'DNS' : &rootID2Name($rootID); push @{ $show->{BODY} }, $self->makeKeyVal( $rootName, $data->{description}, "?jumpToButton=1&rootIDJump=$rootID&jumpTo=$ipaddress" ); } } else { my $netCount = {}; for my $net ( keys %{ $result->{cidr}->{$cidr} } ) { for my $d ( @{ $result->{cidr}->{$cidr}->{$net} } ) { my $rootID = $d->{rootID}; my $root = &rootID2Name($rootID); $netCount->{$root}++; } } push @{ $show->{BODY} }, { elements => [ { bold => 1, underline => 1, align => 'center', colspan => 3, target => 'value', type => 'label', value => _gettext("/$cidr") } ] }; for my $root ( keys %{$netCount} ) { push @{ $show->{BODY} }, $self->makeKeyVal( $root, $netCount->{$root} ); } } } return $show; } 1; HaCi/modules/HaCi/Plugins/DNSInfo.pm0000644000175000000000000000256411341614314016542 0ustar fighterrootpackage HaCi::Plugins::DNSInfo; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::GUI::gettext qw/_gettext/; our $INFO = { name => 'DNSInfo', version => '0.1', onDemand => 1, recurrent => 0, description => _gettext('This Plugin queries on demand the DNS PTR-Record for the current IP adress and displays it.') }; sub run_onDemand { my $self = shift; my $networkRef = shift; my $networkDec = $networkRef->{network}; my $network = ($networkRef->{ipv6ID}) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network))[0]; unless ($ip =~ /^[\w\.\:]+$/) { $self->warnl("'$ip' is not a valid ip address!"); return 0; } $self->{IP} = $ip; eval { require Net::Nslookup; }; if ($@) { $self->warnl("Error while loading module 'Net::Nslookup': $@"); return 0; } my $ptr = Net::Nslookup->nslookup(host => $ip, type => "PTR"); $ptr ||= ''; $self->{PTR} = $ptr; return 1; } sub show { my $self = shift; my $show = { HEADER => sprintf(_gettext("DNS Info for %s"), $self->{IP}), BODY => [ { elements => [ { target => 'key', type => 'label', value => _gettext("Reverse DNS lookup"), }, { target => 'value', type => 'label', value => $self->{PTR}, } ] }, ] }; return $show; } 1; HaCi/modules/HaCi/Plugins/DNSInfoForNetworks.pm0000644000175000000000000000536112377724510020756 0ustar fighterrootpackage HaCi::Plugins::DNSInfoForNetworks; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::Utils qw/netID2Stuff/; use HaCi::GUI::gettext qw/_gettext/; our $INFO = { name => 'DNSInfoForNetworks', version => '0.2', recurrent => 1, onDemand => 0, description => _gettext('This Plugin runs recurrent in background and collects DNS PTR-Records for its associated IP Addresses an saves them in DB. In the Output will all IP Adresses be listet with their corresponding PTR-Records.'), api => [ { name => 'PTR', descr => _gettext('DNS PTR-Record for current IP address'), }, ], }; sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; eval { require Net::Nslookup; }; if ($@) { $self->warnl("Error while loading module 'Net::Nslookup': $@"); return 0; } my $lastUpdatedCheck = {}; foreach (@$networks) { my $netID = $_->{ID}; my $origin = $_->{origin}; my $ipv6 = (exists $_->{ipv6} && $_->{ipv6}) ? 1 : 0; my $networkDec = $_->{network}; my $descr = $_->{description}; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network, 2))[0]; $self->saveValue($netID, $origin, 'NAME', $network); $self->saveValue($netID, $origin, 'DESCR', $descr); $self->saveValue($netID, $origin, 'ID', $networkDec); $self->saveValue(-1, $origin, 'lastUpdated', scalar localtime) unless exists $lastUpdatedCheck->{$origin}; my $ptr = ''; eval { $ptr = Net::Nslookup->nslookup(host => $ip, type => "PTR"); }; if ($@) { $self->warnl("Error while running Net::Nslookup(host => $ip, type => 'PTR'): $@"); next; } $ptr ||= ''; $self->saveValue($netID, $origin, 'PTR', $ptr); } return 1; } sub show { my $self = shift; my $netID = shift; my $values = $self->getValues($netID); my $results = {}; my $lastUpdated = $self->getValue(-1, 'lastUpdated'); my $show = { HEADER => "DNS Records (Last updated: $lastUpdated)", BODY => [ ] }; my $unsorted = {}; foreach (sort {$a<=>$b} keys %{$values}) { my $netID = $_; my $id = $values->{$netID}->{ID}; $unsorted->{$id} = $netID; } my @sorted = sort {$a<=>$b} keys %{$unsorted}; foreach (@sorted) { my $id = $_; my $netID = $unsorted->{$id}; my $name = $values->{$netID}->{NAME} || ''; my $descr = $values->{$netID}->{DESCR} || ''; my $ptr = $values->{$netID}->{PTR} || ''; push @{$show->{BODY}}, ( { elements => [ { target => 'key', type => 'label', value => $name . ' (' . $descr . ')', }, { target => 'value', type => 'label', value => $ptr, }, ] }, ) if $name ne ''; } return $show; } 1; HaCi/modules/HaCi/Plugins/GenZonefile.pm0000644000175000000000000002002711606636771017520 0ustar fighterrootpackage HaCi::Plugins::GenZonefile; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::Utils qw/netID2Stuff getNetworkChilds getMaintInfosFromNet/; use HaCi::GUI::gettext qw/_gettext/; use Net::Ping; our $conf; *conf = \$HaCi::Conf::conf; our $INFO = { name => 'GenZonefile', version => '0.1', recurrent => 1, onDemand => 1, description => _gettext('This Plugin generates a zonefile which contains all ip addresses of this network.'), api => [ { name => 'STATUS', descr => _gettext('Generated zonefile'), }, ], menuOnDemand => [ { NAME => 'origin', DESCR => 'Origin', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => 'example.com' }, { NAME => 'primaryns', DESCR => 'Primary Nameserver', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => 'ns1.example.com' }, { NAME => 'secondaryns', DESCR => 'Secondary Nameserver', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => 'ns2.example.com' }, { NAME => 'primarymx', DESCR => 'Primary Mailexchanger with priority', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => '10 mx1.example.com' }, { NAME => 'secondarymx', DESCR => 'Secondary Mailexchanger with priority', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => '20 mx2.example.com' }, { NAME => 'mailaddress', DESCR => 'Email address', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 128, VALUE => 'hostmaster.example.com' }, { NAME => 'ttl', DESCR => 'Default TTL', TYPE => 'textbox', SIZE => 4, MAXLENGTH => 10, VALUE => '3600' }, { NAME => 'refresh', DESCR => 'Refresh', TYPE => 'textbox', SIZE => 5, MAXLENGTH => 10, VALUE => '86400' }, { NAME => 'retry', DESCR => 'Retry', TYPE => 'textbox', SIZE => 4, MAXLENGTH => 10, VALUE => '7200' }, { NAME => 'expire', DESCR => 'Expire', TYPE => 'textbox', SIZE => 7, MAXLENGTH => 10, VALUE => '3600000' }, { NAME => 'minimum', DESCR => 'Minimum', TYPE => 'textbox', SIZE => 6, MAXLENGTH => 10, VALUE => '172800' }, { NAME => 'arr', DESCR => 'A-RR', TYPE => 'checkbox', CHECKED => '1' }, { NAME => 'ptrrr', DESCR => 'PTR-RR', TYPE => 'checkbox', CHECKED => '0' }, ], globMenuRecurrent => [ { NAME => 'targetDir', DESCR => 'Target directory', TYPE => 'textbox', SIZE => 25, MAXLENGTH => 255, VALUE => $conf->{static}->{path}->{workdir} . '/spool/GenZonefile' }, ] }; sub run_onDemand { my $self = shift; my $networkRef = shift; my $confValues = shift; my $networkDec = $networkRef->{network}; my $ipv6 = ($networkRef->{ipv6ID}) ? 1 : 0; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $zonefiles = $self->genZonefile($networkRef, $confValues); my $zonefile = ''; my $origins = ''; foreach (@{$zonefiles}) { my $origin = $_->{ORIGIN}; my $content = $_->{ZONEFILE}; $zonefile .= "\n" . ' ' x 80 . "\n" if $zonefile ne ''; $origins .= ' and ' if $origins ne ''; $zonefile .= $content; $origins .= $origin; } $self->{zonefile} = $zonefile; $self->{origin} = $origins; $self->{network} = $network; return 1; } sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; my $destDir = $config->{-1}->{targetDir}; unless (-d $destDir || mkdir $destDir) { $self->warnl("Cannot create Directory '$destDir': $!", 1); }; foreach (@$networks) { my $netID = $_->{ID}; my $zonefiles = $self->genZonefile($_, $config->{$netID}, 1); foreach (@{$zonefiles}) { my $origin = $_->{ORIGIN}; my $content = $_->{ZONEFILE}; my $zoneFile = $destDir . '/' . $origin; unless (open ZONE, '>' . $zoneFile) { $self->warnl("Cannot open Zonefile '$zoneFile' for writing: $!", 1); } print ZONE $content; close ZONE; } } } sub genZonefile { my $self = shift; my $networkRef = shift; my $confValues = shift; my $recurrent = shift || 0; my $networkDec = $networkRef->{network}; my $netID = $networkRef->{ID}; my $ipv6ID = $networkRef->{ipv6ID}; my $origin = $confValues->{origin} || 'example.com'; my $primNS = $confValues->{primaryns} || 'ns1.example.com'; my $secNS = $confValues->{secondaryns} || 'ns2.example.com'; my $primMX = $confValues->{primarymx} || '10 mx1.example.com'; my $secMX = $confValues->{secondarymx} || '20 mx2.example.com'; my $mail = $confValues->{mailaddress} || 'hostmaster.example.com'; my $ttl = $confValues->{ttl} || '3600'; my $refresh = $confValues->{refresh} || '86400'; my $retry = $confValues->{retry} || '7200'; my $expire = $confValues->{expire} || '3600000'; my $minimum = $confValues->{minimum} || '172800'; my $arr = $confValues->{arr}; my $ptrrr = $confValues->{ptrrr}; my $ipv6 = ($ipv6ID) ? 1 : 0; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my @networks = &getNetworkChilds($netID); my @time = localtime(); my $serial = sprintf("%04i%02i%02i%02i", $time[5] + 1900, $time[4] + 1, $time[3], 1); my ($ip, $cidr) = split(/\//, $network, 2); my $zonefiles = []; my $zonefileA = qq{ \$TTL 3600 $origin.\tIN SOA $primNS. $mail. ( \t$serial ; serial \t$refresh ; refresh \t$retry ; retry \t$expire ; expire \t$minimum ; minimum \t) ; \tIN NS $primNS. \tIN NS $secNS. ; \tIN MX $primMX. \tIN MX $secMX. ; }; my $nets = (); foreach (@networks) { $nets->{$_->{network}} = $_; } my @as = (); my @ptrs = (); foreach (sort {$a<=>$b} keys %{$nets}) { my $net = $nets->{$_}; my $netID = $net->{ID}; my $networkDec = $net->{network}; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my ($ip, $cidr) = split(/\//, $network, 2); next if ($ipv6 && $cidr != 128) || (!$ipv6 && $cidr != 32); my $netInfos = &getMaintInfosFromNet($netID); my $descr = $netInfos->{description}; $descr =~ s/\s/_/g; $descr =~ s/\W//g; $descr =~ s/_/-/g; $descr =~ s/$origin$//; push @as, "$descr\tIN " . (($ipv6) ? 'AAAA' : 'A') . " $ip\n" if $arr; $ip =~ s/.*[\.:](\w+)/$1/; $ip = join('.', reverse split//, $ip) if $ipv6; push @ptrs, "$ip\tIN PTR $descr.$origin.\n" } $zonefileA .= join('', @as); push @{$zonefiles}, { ORIGIN => $origin, ZONEFILE => $zonefileA }; my $originPTR = ''; if ($ptrrr) { if ($cidr == (($ipv6) ? 112 : 24)) { $ip =~ s/[\.:]\w+$//; $ip =~ s/://g; my $ipRef = join('.', reverse (($ipv6) ? split//, $ip : split/\./, $ip)); $originPTR = $ipRef . '.' . (($ipv6) ? 'ip6' : 'in-addr') . '.arpa'; my $zonefilePTR .= qq{ \$TTL 3600 $originPTR.\tIN SOA $primNS. $mail. ( \t$serial ; serial \t$refresh ; refresh \t$retry ; retry \t$expire ; expire \t$minimum ; minimum \t) ; \tIN NS $primNS. \tIN NS $secNS. ; \tIN MX $primMX. \tIN MX $secMX. ; }; $zonefilePTR .= join('', @ptrs); push @{$zonefiles}, { ORIGIN => $originPTR, ZONEFILE => $zonefilePTR }; } else { if ($recurrent) { $self->warnl("Reverse zonefile can only be generated for /24 (IPv4) and /112 (IPv6) networks! ($network)\n", 2); } else { push @{$zonefiles}, { ORIGIN => $originPTR, ZONEFILE => "Reverse zonefile can only be generated for /24 (IPv4) and /112 (IPv6) networks!" }; } } } my @origins = (); push @origins, $origin if $arr; push @origins, $originPTR if $ptrrr && $cidr == (($ipv6) ? 112 : 24); return $zonefiles; } sub show { my $self = shift; my $show = { HEADER => sprintf("Generated zonefile for '%s'", $self->{origin}), BODY => [ { elements => [ { target => 'single', type => 'label', value => '
' . $self->{zonefile} . '
' }, ] }, ] }; return $show; } 1; HaCi/modules/HaCi/Plugins/PingInfo.pm0000644000175000000000000000355012000412523016776 0ustar fighterrootpackage HaCi::Plugins::PingInfo; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::GUI::gettext qw/_gettext/; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw( $INFO ); our $INFO = { name => 'PingInfo', version => '0.1', onDemand => 1, recurrent => 0, description => _gettext('This Plugins pings the current IP adress on Demand and displays its status.'), }; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub run_onDemand { my $self = shift; my $networkRef = shift; my $networkDec = $networkRef->{network}; my $network = ($networkRef->{ipv6ID}) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network, 2))[0]; unless ($ip =~ /^[\w\:\.]+$/) { &warnl($self, "This IP '$ip' is not a valid IP Address!"); return 0; } $self->{IP} = $ip; eval { require Net::Ping; }; if ($@) { &warnl($self, "Error while loading module 'Net::Ping': $@"); return 0; } my $p = Net::Ping->new(); $self->{ALIVE} = ($p->ping($ip)) ? 1 : 0; $p->close(); } sub show { my $self = shift; my $show = { HEADER => sprintf(_gettext("Ping Info for %s"), $self->{IP}), BODY => [ { elements => [ { target => 'key', type => 'label', value => _gettext("Status of Host"), }, { target => 'value', type => 'label', value => (($self->{ALIVE}) ? _gettext("Alive") : _gettext('Dead')), color => (($self->{ALIVE}) ? '#00AA00' : '#AA0000'), } ] }, ] }; return $show; } sub warnl { my $self = shift; my $msg = shift; my $toUser = shift; $toUser = 0 unless defined $toUser; unless ($toUser) { warn $msg; return; } $self->{ERRORSTR} .= "\n" if $self->{ERROR}; $self->{ERROR} = 1; $self->{ERRORSTR} .= $msg; } 1; HaCi/modules/HaCi/Plugins/PingNetworks.pm0000644000175000000000000000646512377724510017752 0ustar fighterrootpackage HaCi::Plugins::PingNetworks; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::Utils qw/netID2Stuff/; use HaCi::GUI::gettext qw/_gettext/; use Net::Ping; our $INFO = { name => 'PingNetworks', version => '0.2', recurrent => 1, onDemand => 0, description => _gettext('This Plugin runns recurrent in background and pings all associated IP addresses. In the Output it will display all IP addresses with their Status.'), api => [ { name => 'STATUS', descr => _gettext('Ping Status of current ipaddress (dead|alive)'), }, ], globMenuRecurrent => [ { NAME => 'proto', DESCR => 'Protocoll', TYPE => 'popupmenu', VALUE => ['tcp', 'udp', 'icmp', 'stream', 'syn', 'external'], DEFAULT => 'tcp', }, { NAME => 'port', DESCR => 'Port', TYPE => 'textbox', SIZE => 3, MAXLENGTH => 5, VALUE => '7', }, { NAME => 'timeout', DESCR => 'Timeout', TYPE => 'textbox', SIZE => 3, MAXLENGTH => 10, VALUE => '5', }, { NAME => 'dataSize', DESCR => 'Nr of Bytes (0-1024)', TYPE => 'textbox', SIZE => 3, MAXLENGTH => 4, VALUE => '', }, ], }; sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; my $p; eval { $p = Net::Ping->new( $config->{-1}->{proto} || '', $config->{-1}->{timeout} || '', $config->{-1}->{dataSize} ); }; if ($@) { $self->warnl($@, 1); return 0; } $p->{port_num} = $config->{-1}->{port} if $config->{-1}->{port}; my $lastUpdatedCheck = {}; foreach (@$networks) { my $netID = $_->{ID}; my $origin = $_->{origin}; my $ipv6 = (exists $_->{ipv6} && $_->{ipv6}) ? 1 : 0; my $networkDec = $_->{network}; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network, 2))[0]; $self->saveValue($netID, $origin, 'NAME', $network); $self->saveValue($netID, $origin, 'ID', $networkDec); $self->saveValue(-1, $origin, 'lastUpdated', scalar localtime) unless exists $lastUpdatedCheck->{$origin}; eval { if ($p->ping($ip)) { $self->saveValue($netID, $origin, 'STATUS', 'alive'); } else { $self->saveValue($netID, $origin, 'STATUS', 'dead'); } }; if ($@) { $self->warnl($@, 1); next; } } return 1; } sub show { my $self = shift; my $netID = shift; my $values = $self->getValues($netID); my $results = {}; my $lastUpdated = $self->getValue(-1, 'lastUpdated'); my $show = { HEADER => "Ping Statistics (last updated: $lastUpdated)", BODY => [ ] }; my $unsorted = {}; foreach (sort {$a<=>$b} keys %{$values}) { my $netID = $_; my $id = $values->{$netID}->{ID}; $unsorted->{$id} = $netID; } my @sorted = sort {$a<=>$b} keys %{$unsorted}; foreach (@sorted) { my $id = $_; my $netID = $unsorted->{$id}; my $name = $values->{$netID}->{NAME}; my $status = $values->{$netID}->{STATUS}; push @{$show->{BODY}}, ( { elements => [ { target => 'key', type => 'label', value => $name, }, { target => 'value', type => 'label', value => ($status eq 'alive') ? 'alive' : 'dead', }, ] }, ) if $name ne ''; } return $show; } 1; HaCi/modules/HaCi/Plugins/PingSNMPNetworks.pm0000644000175000000000000001737711341614314020442 0ustar fighterrootpackage HaCi::Plugins::PingSNMPNetworks; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::Utils qw/netID2Stuff/; use HaCi::GUI::gettext qw/_gettext/; use HaCi::SnmpUtils qw/getSNMPSession getSNMPTree/; use Net::Ping; our $INFO = { name => 'PingSNMPNetworks', version => '0.1', recurrent => 1, onDemand => 0, network => 1, description => _gettext('This Plugin will ping associated IP adresses and saves its status in DB. After that, it will collect the corresponding MAC addresses with SNMP from a specific router and save the result in DB, too. In the Output there were all IP addresses listed with their status and MAC address if available. With this method you catch all available Hosts, even if they deny ping.'), api => [ { name => 'STATUS', descr => _gettext("Ping Status of current ipaddress (dead|alive)"), }, { name => 'MAC', descr => _gettext('Mac Address related to the IP address'), }, ], globMenuRecurrent => [ { TYPE => 'label', VALUE => 'Ping', }, { NAME => 'ping_proto', DESCR => _gettext('Protocoll'), TYPE => 'popupmenu', VALUE => ['tcp', 'udp', 'icmp', 'stream', 'syn', 'external'], DEFAULT => 'tcp', }, { NAME => 'ping_port', DESCR => _gettext('Port'), TYPE => 'textbox', SIZE => 3, MAXLENGTH => 5, VALUE => '7', }, { NAME => 'ping_timeout', DESCR => _gettext('Timeout'), TYPE => 'textbox', SIZE => 3, MAXLENGTH => 10, VALUE => '5', }, { NAME => 'ping_dataSize', DESCR => _gettext('Nr of Bytes (0-1024)'), TYPE => 'textbox', SIZE => 3, MAXLENGTH => 4, VALUE => '', }, ], menuRecurrent => [ { TYPE => 'label', VALUE => 'SNMP', }, { NAME => 'snmp_router', DESCR => _gettext('Router for SNMP-Connection'), TYPE => 'textbox', SIZE => 15, MAXLENGTH => 255, VALUE => '', }, { NAME => 'snmp_port', DESCR => _gettext('Port'), TYPE => 'textbox', SIZE => 5, MAXLENGTH => 5, VALUE => 161, }, { NAME => 'snmp_community', DESCR => _gettext('Community'), TYPE => 'textbox', SIZE => 10, MAXLENGTH => 64, VALUE => 'public', }, { NAME => 'snmp_version', DESCR => _gettext('SNMP Version'), TYPE => 'textbox', SIZE => 6, MAXLENGTH => 6, VALUE => 'snmpv2', }, { NAME => 'snmp_arp-table', DESCR => _gettext('SNMP ARP Table (ipNetToMediaPhysAddress)'), TYPE => 'textbox', SIZE => 15, MAXLENGTH => 64, VALUE => '.1.3.6.1.2.1.4.22.1.2', }, ], }; sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; &pingNetworks($self, $networks, $config); &getArpTable($self, $networks, $config); return 1; } sub getArpTable { my $self = shift; my $networks = shift; my $config = shift; my $arpTable = {}; foreach (@$networks) { my $netID = $_->{ID}; my $origin = $_->{origin}; my $ipv6 = (exists $_->{ipv6} && $_->{ipv6}) ? 1 : 0; my $networkDec = $_->{network}; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network, 2))[0]; my $host = $config->{$origin || $netID}->{snmp_router} || ''; my $comm = $config->{$origin || $netID}->{snmp_community} || 'public'; my $version = $config->{$origin || $netID}->{snmp_version} || 'snmpv2'; my $port = $config->{$origin || $netID}->{snmp_port} || 161; my $snmpArpTable = $config->{$origin || $netID}->{'snmp_arp-table'} || '.1.3.6.1.2.1.4.22.1.2'; next unless $host; unless (exists $arpTable->{$host}) { my $session = &getSNMPSession($host, $comm, { port => $port, version => $version }); unless (defined $session) { $self->warnl("Cannot initiate SNMP-Session for $host\n", 1); next; } $arpTable->{$host} = &getSNMPTree($session, $snmpArpTable); $session->close(); } my $ips = {}; foreach (keys %{$arpTable->{$host}}) { my $key = $_; if ($key =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) { $ips->{$1} = $arpTable->{$host}->{$key}; } } if (exists $ips->{$ip}) { my $mac = ''; my $cnter = 2; while ($cnter < length($ips->{$ip})) { $mac .= ':' if $mac; $mac .= substr($ips->{$ip}, $cnter, 2); $cnter += 2; } $self->saveValue($netID, $origin, 'MAC', $mac); } else { } } return 1; } sub pingNetworks { my $self = shift; my $networks = shift; my $config = shift; my $p; eval { $p = Net::Ping->new( $config->{-1}->{ping_proto} || '', $config->{-1}->{ping_timeout} || '', $config->{-1}->{ping_dataSize} ); }; if ($@) { $self->warnl($@, 1); return 0; } $p->{port_num} = $config->{-1}->{ping_port} if $config->{-1}->{ping_port}; my $lastUpdatedCheck = {}; foreach (@$networks) { my $netID = $_->{ID}; my $origin = $_->{origin}; my $ipv6 = (exists $_->{ipv6} && $_->{ipv6}) ? 1 : 0; my $networkDec = $_->{network}; my $network = ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network, 2))[0]; $self->saveValue($netID, $origin, 'NAME', $network); $self->saveValue(-1, $origin, 'lastUpdated', scalar localtime) unless exists $lastUpdatedCheck->{$origin}; eval { if ($p->ping($ip)) { $self->saveValue($netID, $origin, 'STATUS', 'alive'); } else { $self->saveValue($netID, $origin, 'STATUS', 'dead'); } }; if ($@) { $self->warnl($@, 1); next; } } return 1; } sub show { my $self = shift; my $netID = shift; my $values = $self->getValues($netID); my $results = {}; my $lastUpdated = $self->getValue(-1, 'lastUpdated'); my $show = { HEADER => "Ping/ARP Statistics (last updated: $lastUpdated)", BODY => [ { elements => [ { target => 'single', type => 'label', value => 'IP', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => 'Ping Status', bold => 1, align => 'center', }, { target => 'single', type => 'label', value => _gettext('ARP Entry'), bold => 1, align => 'center', }, ] }, { value => { type => 'hline', colspan => 3, } }, ] }; my $aliveCnter = 0; my $macCnter = 0; my $total = 0; foreach (sort {$a<=>$b} keys %{$values}) { my $netID = $_; my $name = $values->{$netID}->{NAME} || ''; my $mac = $values->{$netID}->{MAC} || ''; my $status = $values->{$netID}->{STATUS} || 'dead'; $aliveCnter++ if $status eq 'alive'; $macCnter++ if $mac ne ''; if ($name ne '') { $total++; push @{$show->{BODY}}, ( { elements => [ { target => 'key', type => 'label', value => $name, }, { target => 'single', type => 'label', value => ($status eq 'alive') ? 'alive' : 'dead', }, { target => 'value', type => 'label', value => "($mac)", }, ] }, ); } } my $percA = sprintf("%.2i", (($aliveCnter * 100)/($total||1))); my $percM = sprintf("%.2i", (($macCnter * 100)/($total||1))); push @{$show->{BODY}}, ( { value => { type => 'hline', colspan => 3, } }, { elements => [ { target => 'single', type => 'label', value => $total . ' ' . _gettext("IP addresses"), bold => 1, }, { target => 'single', type => 'label', value => $aliveCnter . ' ' . _gettext("alive") . " ($percA%)", bold => 1, }, { target => 'single', type => 'label', value => $macCnter . ' ' . _gettext("MACs found") . " ($percM%)", bold => 1, }, ] }, ); return $show; } 1; HaCi/modules/HaCi/Plugins/RIPEInfo.pm0000644000175000000000000000316011341614314016646 0ustar fighterrootpackage HaCi::Plugins::RIPEInfo; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::GUI::gettext qw/_gettext/; our $conf; *conf = \$HaCi::Conf::conf; our $INFO = { name => 'RIPEInfo', version => '0.1', onDemand => 1, description => _gettext('This Plugin queries on Demand the Whois DB for the current network and displays the Result.'), }; sub run_onDemand { my $self = shift; my $networkRef = shift; my $networkDec = $networkRef->{network}; my $network = ($networkRef->{ipv6ID}) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); unless ($network =~ /^[\w\:\.\/]+$/) { $self->warnl("This '$network' doesn't look like a network!"); return 0; } unless (-x $conf->{static}->{path}->{whois}) { $self->warnl("Whois Executable not found! ($conf->{static}->{path}->{whois})"); return 0; } my @whois = qx($conf->{static}->{path}->{whois} -h $conf->{static}->{misc}->{ripedb} -B $network); my $route = 0; foreach (@whois) { push @{$self->{data}}, {key =>$1, value => $2} if $self->{inetnum} && /^([\w\-]+):\s+(.*)$/; $self->{inetnum} = $1 if /^inet6?num:\s+(.*)$/; last if $self->{inetnum} && /^\s*$/; } return 1; } sub show { my $self = shift; my $show = {}; foreach (@{$self->{data}}) { push @{$show->{BODY}}, { elements => [ { target => 'key', type => 'label', value => $_->{key}, }, { target => 'value', type => 'label', value => $_->{value}, } ] }; } $show->{HEADER} = sprintf(_gettext("RIPE Info for %s"), $self->{inetnum}); return $show; } 1; HaCi/modules/HaCi/Plugins/TEMPLATE.pm.tmpl0000644000175000000000000000302211341614314017456 0ustar fighterrootpackage HaCi::Plugins::TEMPLATE; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::Utils qw/netID2Stuff/; use HaCi::GUI::gettext qw/_gettext/; our $INFO = { name => 'TEMPLATE', version => '0.1', recurrent => 1, onDemand => 1, description => _gettext('TEMPLATE'), api => [ { name => 'TEMPLATE', descr => _gettext("TEMPLATE"), }, ... ], globMenuRecurrent => [ { TYPE => 'label', VALUE => 'TEMPLATE', }, ... ], globMenuOnDemand => [ { TYPE => 'label', VALUE => 'TEMPLATE', }, ... ], menuRecurrent => [ { TYPE => 'label', VALUE => 'TEMPLATE', }, ... ], menuOnDemand => [ { TYPE => 'label', VALUE => 'TEMPLATE', }, ... ], }; sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; ... return 1; } sub run_onDemand { my $self = shift; my $networkRef = shift; my $networkDec = $networkRef->{network}; my $network = ($networkRef->{ipv6ID}) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); my $ip = (split(/\//, $network))[0]; ... return 1; } sub show { my $self = shift; my $netID = shift; my $values = $self->getValues($netID); my $results = {}; my $lastUpdated = $self->getValue(-1, 'lastUpdated'); my $show = { HEADER => "TEMPLATE", BODY => [ { elements => [ { target => 'single', type => 'label', value => 'TEMPLATE', }, ... ] }, ] }; return $show; } 1; HaCi/modules/HaCi/Plugins/WhoisInfo.pm0000644000175000000000000000321511714055665017215 0ustar fighterrootpackage HaCi::Plugins::WhoisInfo; use strict; use warnings; use base qw/HaCi::Plugin/; use HaCi::Mathematics qw/dec2net netv6Dec2net/; use HaCi::GUI::gettext qw/_gettext/; our $conf; *conf = \$HaCi::Conf::conf; our $INFO = { name => 'WhoisInfo', version => '0.1', onDemand => 1, description => _gettext('This Plugin queries on Demand a whois directory service for the current network and displays the Result.'), }; sub run_onDemand { my $self = shift; my $networkRef = shift; my $networkDec = $networkRef->{network}; my $network = ($networkRef->{ipv6ID}) ? &netv6Dec2net($networkDec) : &dec2net($networkDec); unless ($network =~ /^[\w\:\.\/]+$/) { $self->warnl("This '$network' doesn't look like a network!"); return 0; } unless (-x $conf->{static}->{path}->{whois}) { $self->warnl("Whois Executable not found! ($conf->{static}->{path}->{whois})"); return 0; } my @whois = qx($conf->{static}->{path}->{whois} $network); my $route = 0; foreach (@whois) { push @{$self->{data}}, {key =>$1, value => $2} if $self->{inetnum} && /^([\w\-]+):\s+(.*)$/; $self->{inetnum} = $1 if /^inet6?num:\s+(.*)$/ || /^CIDR:\s+(.*)$/; last if $self->{inetnum} && /^\s*$/; } $self->{inetnum} ||= $network; return 1; } sub show { my $self = shift; my $show = {}; foreach (@{$self->{data}}) { push @{$show->{BODY}}, { elements => [ { target => 'key', type => 'label', value => $_->{key}, }, { target => 'value', type => 'label', value => $_->{value}, } ] }; } $show->{HEADER} = sprintf(_gettext("Whois Info for %s"), $self->{inetnum}); return $show; } 1; HaCi/modules/HaCi/SOAP/0000755000175000000000000000000012475447616014100 5ustar fighterrootHaCi/modules/HaCi/SOAP/Type/0000755000175000000000000000000012475447616015021 5ustar fighterrootHaCi/modules/HaCi/SOAP/Type/network.pm0000644000175000000000000000111312451674757017046 0ustar fighterrootpackage HaCi::SOAP::Type::network; =begin WSDL _ATTR rootName $string Name of Root _ATTR network $string Network _ATTR description $string Description _ATTR state $string State _ATTR tags $string Tags =cut sub new { my $class = shift; my $rootName = shift || ''; my $network = shift || ''; my $description = shift || ''; my $state = SOAP::Data->type(string => (shift || 0)); my $tags = shift || ''; bless { rootName => $rootName, network => $network, description => $description, state => $state, tags => $tags, }, $class; } 1; HaCi/modules/HaCi/Tables/0000755000175000000000000000000012475447616014550 5ustar fighterrootHaCi/modules/HaCi/Tables/mysql/0000755000175000000000000000000012475447616015715 5ustar fighterrootHaCi/modules/HaCi/Tables/mysql/audit.pm0000644000175000000000000000163011772171324017346 0ustar fighterrootpackage HaCi::Tables::mysql::audit; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'audit' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `username` varchar(255) NOT NULL default '', `accessGroups` varchar(255) NOT NULL default '', `action` varchar(255) NOT NULL default '', `object` varchar(255) NOT NULL default '', `value` text NOT NULL, `error` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/group.pm0000644000175000000000000000174411705140310017365 0ustar fighterrootpackage HaCi::Tables::mysql::group; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'squad' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `permissions` integer NOT NULL default '0', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY (`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/network.pm0000644000175000000000000000251012420356150017720 0ustar fighterrootpackage HaCi::Tables::mysql::network; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'network' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `rootID` int (11) NOT NULL default '0', `network` bigint(20) UNSIGNED NOT NULL default '0', `description` varchar(255) NOT NULL default '', `state` smallint(6) NOT NULL default '0', `defSubnetSize` TINYINT(4) UNSIGNED NOT NULL default '0', `tmplID` integer NOT NULL default '0', `ipv6ID` varbinary(22) NOT NULL default '', `searchStr` varchar(255) default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), INDEX (`network`), INDEX (`description`), INDEX (`searchStr`), UNIQUE KEY (`rootID`,`ipv6ID`, `network`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/mysql/networkAC.pm0000644000175000000000000000152111705140310020117 0ustar fighterrootpackage HaCi::Tables::mysql::networkAC; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'networkAC' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `rootID` integer NOT NULL default '0', `network` bigint(40) NOT NULL default '0', `netID` integer NOT NULL default '0', `groupID` integer NOT NULL default '0', `ACL` integer NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY (`netID`, `groupID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/networkLock.pm0000644000175000000000000000201412377736314020547 0ustar fighterrootpackage HaCi::Tables::mysql::networkLock; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'networkLock' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `ts` datetime NOT NULL default '0000-00-00 00:00:00', `duration` integer NOT NULL default '0', `rootID` int NOT NULL default 0, `networkPrefix` bigint UNSIGNED NOT NULL default 0, `hostPart` bigint UNSIGNED NOT NULL default 0, `cidr` smallint NOT NULL default 0, `ipv6` smallint NOT NULL default 0, PRIMARY KEY (`ID`), UNIQUE KEY (`rootID`, `networkPrefix`,`hostPart`,`cidr`, `ipv6`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/mysql/networkPlugin.pm0000644000175000000000000000146511705140310021101 0ustar fighterrootpackage HaCi::Tables::mysql::networkPlugin; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'networkPlugin' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `netID` integer NOT NULL default '0', `pluginID` integer NOT NULL default '0', `sequence` integer NOT NULL default '0', `newLine` tinyint NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY (`netID`, `pluginID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/networkTag.pm0000644000175000000000000000132612451674757020404 0ustar fighterrootpackage HaCi::Tables::mysql::networkTag; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'networkTag' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `netID` integer NOT NULL default '0', `tag` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY (`netID`, `tag`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/networkV6.pm0000644000175000000000000000153511705140310020134 0ustar fighterrootpackage HaCi::Tables::mysql::networkV6; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'networkV6' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` varbinary(22) NOT NULL default '', `rootID` int NOT NULL default 0, `networkPrefix` bigint UNSIGNED NOT NULL default 0, `hostPart` bigint UNSIGNED NOT NULL default 0, `cidr` smallint NOT NULL default 0, PRIMARY KEY (`ID`, `rootID`), UNIQUE KEY (`rootID`, `networkPrefix`,`hostPart`,`cidr`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/plugin.pm0000644000175000000000000000160711705140310017525 0ustar fighterrootpackage HaCi::Tables::mysql::plugin; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'plugin' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `filename` varchar(255) NOT NULL default '', `active` tinyint NOT NULL default 0, `lastRun` datetime NOT NULL default '0000-00-00 00:00:00', `runTime` int NOT NULL default 0, `lastError` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY (`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/pluginConf.pm0000644000175000000000000000137211705140310020332 0ustar fighterrootpackage HaCi::Tables::mysql::pluginConf; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'pluginConf' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` INT NOT NULL AUTO_INCREMENT, `netID` INT NOT NULL , `pluginID` INT NOT NULL , `name` VARCHAR( 255 ) NOT NULL , `value` TEXT NOT NULL , PRIMARY KEY (`ID`), UNIQUE KEY (`netID`,`pluginID`,`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/pluginValue.pm0000644000175000000000000000142511705140310020520 0ustar fighterrootpackage HaCi::Tables::mysql::pluginValue; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'pluginValue' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` INT NOT NULL AUTO_INCREMENT, `netID` INT NOT NULL , `pluginID` INT NOT NULL , `origin` INT NOT NULL , `name` VARCHAR( 255 ) NOT NULL , `value` TEXT NOT NULL , PRIMARY KEY (`ID`), UNIQUE KEY (`netID`,`pluginID`,`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/root.pm0000644000175000000000000000173111705140310017210 0ustar fighterrootpackage HaCi::Tables::mysql::root; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'root' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `ipv6` tinyint(1) NOT NULL default 0, `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY (`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/rootAC.pm0000644000175000000000000000136711705140310017421 0ustar fighterrootpackage HaCi::Tables::mysql::rootAC; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'rootAC' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `rootID` integer NOT NULL default 0, `groupID` integer NOT NULL default '0', `ACL` integer NOT NULL default '0', PRIMARY KEY (`ID`), UNIQUE KEY (`rootID`, `groupID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/setting.pm0000644000175000000000000000141011705140310017674 0ustar fighterrootpackage HaCi::Tables::mysql::setting; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'setting' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `userID` integer NOT NULL default 0, `param` varchar(255) NOT NULL default '', `value` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`), UNIQUE KEY (`userID`, `param`, `value`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/template.pm0000644000175000000000000000166011705140310020041 0ustar fighterrootpackage HaCi::Tables::mysql::template; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'template' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `type` varchar(64) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY (`name`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/templateEntry.pm0000644000175000000000000000164311770177430021103 0ustar fighterrootpackage HaCi::Tables::mysql::templateEntry; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'templateEntry' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `tmplID` int(11) NOT NULL default '0', `type` integer NOT NULL default '0', `position` integer NOT NULL default '0', `description` varchar(255) NOT NULL default '', `size` integer NOT NULL default '1', `entries` text NOT NULL, `rows` integer NOT NULL default '1', `cols` integer NOT NULL default '1', PRIMARY KEY (`ID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/templateValue.pm0000644000175000000000000000146011705140310021034 0ustar fighterrootpackage HaCi::Tables::mysql::templateValue; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'templateValue' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` integer NOT NULL auto_increment, `tmplID` integer NOT NULL default '0', `tmplEntryID` integer NOT NULL default '0', `netID` integer NOT NULL default '0', `value` blob NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY (`netID`,`tmplID`,`tmplEntryID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/mysql/user.pm0000644000175000000000000000203111705140310017175 0ustar fighterrootpackage HaCi::Tables::mysql::user; use base 'DBIEasy::mysql'; sub TABLE { #Table Name 'user' } sub SETUPTABLE { # Create Table unless it doesn't exists? 1 } sub CREATETABLE { # Table Create Definition q{ `ID` int(11) NOT NULL auto_increment, `username` varchar(100) NOT NULL default '', `password` varchar(255) NOT NULL default '', `groupIDs` varchar(255) NOT NULL default '', `description` varchar(255) NOT NULL default '', `createFrom` varchar(255) NOT NULL default '', `createDate` datetime NOT NULL default '0000-00-00 00:00:00', `modifyFrom` varchar(255) NOT NULL default '', `modifyDate` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`ID`), UNIQUE KEY (`username`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::mysql::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::mysql::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/postgresql/0000755000175000000000000000000012475447616016753 5ustar fighterrootHaCi/modules/HaCi/Tables/postgresql/audit.pm0000644000175000000000000000100711772171324020402 0ustar fighterrootpackage HaCi::Tables::postgresql::audit; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'audit' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', ts => 'ts', username => 'username', accessGroups => 'access_groups', action => 'action', object => 'object', value => 'value', error => 'error', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/group.pm0000644000175000000000000000104311706125776020437 0ustar fighterrootpackage HaCi::Tables::postgresql::group; use base 'DBIEasy::postgresql'; sub TABLE { #table name 'squad' } sub SETUPTABLE { # create table unless it doesn't exists? 0 } sub CREATETABLE { # table create definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', name => 'name', description => 'description', permissions => 'permissions', createFrom => 'create_from', createDate => 'create_date', modifyFrom => 'modify_from', modifyDate => 'modify_date', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/network.pm0000644000175000000000000000127612403710743020771 0ustar fighterrootpackage HaCi::Tables::postgresql::network; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', rootID => 'root_id', network => 'network', description => 'description', state => 'state', defSubnetSize => 'def_subnet_size', tmplID => 'tmpl_id', ipv6ID => 'ipv6_id', searchStr => 'search_str', createFrom => 'create_from', createDate => 'create_date', modifyFrom => 'modify_from', modifyDate => 'modify_date', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/networkAC.pm0000644000175000000000000000071611706125776021206 0ustar fighterrootpackage HaCi::Tables::postgresql::networkAC; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network_ac' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', rootID => 'root_id', network => 'network', netID => 'net_id', groupID => 'group_id', ACL => 'acl' } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/networkLock.pm0000644000175000000000000000100312377724510021575 0ustar fighterrootpackage HaCi::Tables::postgresql::networkLock; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network_lock' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', ts => 'ts', duration => 'duration', rootID => 'root_id', networkPrefix => 'network_prefix', hostPart => 'host_part', cidr => 'cidr', ipv6 => 'ipv6', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/networkPlugin.pm0000644000175000000000000000071011706125776022153 0ustar fighterrootpackage HaCi::Tables::postgresql::networkPlugin; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network_plugin' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', netID => 'net_id', pluginID => 'plugin_id', sequence => 'sequence', newLine => 'new_line', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/networkTag.pm0000644000175000000000000000060512451674757021441 0ustar fighterrootpackage HaCi::Tables::postgresql::networkTag; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network_tag' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', netID => 'net_id', tag => 'tag' } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/networkV6.pm0000644000175000000000000000071511706125776021215 0ustar fighterrootpackage HaCi::Tables::postgresql::networkV6; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'network_v6' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', rootID => 'root_id', networkPrefix => 'network_prefix', hostPart => 'host_part', cidr => 'cidr' } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/plugin.pm0000644000175000000000000000077211706125776020611 0ustar fighterrootpackage HaCi::Tables::postgresql::plugin; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'plugin' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', name => 'name', filename => 'filename', active => 'active', lastRun => 'last_run', runTime => 'run_time', lastError => 'last_error', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/pluginConf.pm0000644000175000000000000000070211706125776021410 0ustar fighterrootpackage HaCi::Tables::postgresql::pluginConf; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'plugin_conf' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', netID => 'net_id', pluginID => 'plugin_id', name => 'name', value => 'value', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/pluginValue.pm0000644000175000000000000000073511706125776021605 0ustar fighterrootpackage HaCi::Tables::postgresql::pluginValue; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'plugin_value' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', netID => 'net_id', pluginID => 'plugin_id', origin => 'origin', name => 'name', value => 'value', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/root.pm0000644000175000000000000000103611706125776020270 0ustar fighterrootpackage HaCi::Tables::postgresql::root; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'root' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', name => 'name', description => 'description', ipv6 => 'ipv6', createFrom => 'create_from', createDate => 'create_date', modifyFrom => 'modify_from', modifyDate => 'modify_date', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/rootAC.pm0000644000175000000000000000064111706125776020475 0ustar fighterrootpackage HaCi::Tables::postgresql::rootAC; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'root_ac' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', rootID => 'root_id', groupID => 'group_id', ACL => 'acl', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/setting.pm0000644000175000000000000000064111706125776020763 0ustar fighterrootpackage HaCi::Tables::postgresql::setting; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'setting' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', userID => 'user_id', param => 'param', value => 'value', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/template.pm0000644000175000000000000000100511706125776021114 0ustar fighterrootpackage HaCi::Tables::postgresql::template; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'template' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', name => 'name', type => 'type', createFrom => 'create_from', createDate => 'create_date', modifyFrom => 'modify_from', modifyDate => 'modify_date', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/templateEntry.pm0000644000175000000000000000105611706125776022144 0ustar fighterrootpackage HaCi::Tables::postgresql::templateEntry; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'template_entry' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', tmplID => 'tmpl_id', type => 'type', position => 'position', description => 'description', size => 'size', entries => 'entries', rows => 'rows', cols => 'cols', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/templateValue.pm0000644000175000000000000000072211706125776022116 0ustar fighterrootpackage HaCi::Tables::postgresql::templateValue; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'template_value' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', tmplID => 'tmpl_id', tmplEntryID => 'tmpl_entry_id', netID => 'net_id', value => 'value', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/postgresql/user.pm0000644000175000000000000000110711706125776020262 0ustar fighterrootpackage HaCi::Tables::postgresql::user; use base 'DBIEasy::postgresql'; sub TABLE { #Table Name 'user' } sub SETUPTABLE { # Create Table unless it doesn't exists? 0 } sub CREATETABLE { # Table Create Definition } sub columnMapping { # map DBIEasy methods to table column names { ID => 'id', username => 'username', password => 'password', groupIDs => 'group_ids', description => 'description', createFrom => 'create_from', createDate => 'create_date', modifyFrom => 'modify_from', modifyDate => 'modify_date', } } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/Log/0000755000175000000000000000000012475447616013253 5ustar fighterrootHaCi/modules/Log/LogLite.pm0000644000175000000000000002054210670645573015150 0ustar fighterrootpackage Log::LogLite; use strict; use vars qw($VERSION); $VERSION = 0.82; use Carp; use IO::LockedFile 0.21; my $TEMPLATE = '[] <> '; my $LOG_LINE_NUMBERS = 0; # by default we do not log the line numbers ########################################## # new($filepath) # new($filepath,$level) # new($filepath,$level,$default_message) ########################################## # the constructor sub new { my $proto = shift; # get the class name my $class = ref($proto) || $proto; my $self = {}; # private data $self->{FILE_PATH} = shift; # get the file path of the config file $self->{LEVEL} = shift || 5; # the default level is 5 # report when: # 0 the application is unusable # 1 the application is going to be unusable # 2 critical conditions # 3 error conditions # 4 warning conditions # 5 normal but significant condition # 6 informational # 7+ debug-level messages $self->{DEFAULT_MESSAGE} = shift || ""; # the default message $self->{TEMPLATE} = shift || $TEMPLATE; # the template $self->{LOG_LINE_NUMBERS} = $LOG_LINE_NUMBERS; # we create IO::LockedFile object that can be locked later $self->{FH} = new IO::LockedFile({ lock => 0 }, ">>".$self->{FILE_PATH}); unless ($self->{FH}->opened) { croak("Log::LogLite: Cannot open the log file $self->{FILE_PATH}"); } bless ($self, $class); return $self; } # of new ########################## # write($message, $level) ########################## # will log the message in the log file only if $level>=LEVEL sub write { my $self = shift; my $message = shift; # get the message are informational my $level = shift || "-"; if ($level ne "-" && $level > $self->{LEVEL}) { # if the level of this message is higher # then the deafult level - do nothing return; } # lock the log file before we append $self->{FH}->lock(); # parse the template my $line = $self->{TEMPLATE}; $line =~ s!!date_string()!igoe; $line =~ s!!$level!igo; $line =~ s!!$self->called_by()!igoe; $line =~ s!!$self->{DEFAULT_MESSAGE}!igo; $line =~ s!!$message!igo; print {$self->{FH}} $line; # unlock the file $self->{FH}->unlock(); } # of write ########################## # template() # template($template) ########################## sub template { my $self = shift; if (@_) { $self->{TEMPLATE} = shift } return $self->{TEMPLATE}; } # of template ########################## # level() # level($level) ########################## # an interface to LEVEL sub level { my $self = shift; if (@_) { $self->{LEVEL} = shift } return $self->{LEVEL}; } # of level ########################### # default_message() # default_message($message) ########################### # an interface to DEFAULT_MESSAGE sub default_message { my $self = shift; if (@_) { $self->{DEFAULT_MESSAGE} = shift } return $self->{DEFAULT_MESSAGE}; } # of default_message ########################## # log_line_numbers() # log_line_numbers($log_line_numbers) ########################## # an interface to LOG_LINE_NUMBERS sub log_line_numbers { my $self = shift; if (@_) { $self->{LOG_LINE_NUMBERS} = shift } return $self->{LOG_LINE_NUMBERS}; } # of log_line_numbers ####################### # date_string() ####################### sub date_string { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); # note that there is no Y2K bug here. see localtime in perlfunc. return sprintf("%02d/%02d/%04d %02d:%02d:%02d", $mday, $mon + 1, $year + 1900, $hour, $min, $sec); } # of date_string ####################### # called_by ####################### sub called_by { my $self = shift; my $depth = 2; my $args; my $pack; my $file; my $line; my $subr; my $has_args; my $wantarray; my $evaltext; my $is_require; my $hints; my $bitmask; my @subr; my $str = ""; while (1) { ($pack, $file, $line, $subr, $has_args, $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller($depth); unless (defined($subr)) { last; } $depth++; $line = ($self->{LOG_LINE_NUMBERS}) ? "$file:".$line."-->" : ""; push(@subr, $line.$subr); } @subr = reverse(@subr); foreach $subr (@subr) { $str .= $subr; $str .= " > "; } $str =~ s/ > $/: /; return $str; } # of called_by 1; __END__ ############################################################################ =head1 NAME Log::LogLite - The C class helps us create simple logs for our application. =head1 SYNOPSIS use Log::LogLite; my $LOG_DIRECTORY = "/where/ever/our/log/file/should/be"; my $ERROR_LOG_LEVEL = 6; # create new Log::LogLite object my $log = new Log::LogLite($LOG_DIRECTORY."/error.log", $ERROR_LOG_LEVEL); ... # we had an error $log->write("Could not open the file ".$file_name.": $!", 4); =head1 DESCRIPTION In order to have a log we have first to create a C object. The c object is created with a logging level. The default logging level is 5. After the C object is created, each call to the C method may write a new line in the log file. If the level of the message is lower or equal to the logging level, the message will be written to the log file. The format of the logging messages can be controled by changing the template, and by defining a default message. The class uses the IO::LockedFile class. =head1 CONSTRUCTOR =over 4 =item new ( FILEPATH [,LEVEL [,DEFAULT_MESSAGE ]] ) The constructor. FILEPATH is the path of the log file. LEVEL is the defined logging level - the LEVEL data member. DEFAULT_MESSAGE will define the DEFAULT_MESSAGE data member - a message that will be added to the message of each entry in the log (according to the TEMPLATE data member, see below). The levels can be any levels that the user chooses to use. There are, though, recommended levels: 0 the application is unusable 1 the application is going to be unusable 2 critical conditions 3 error conditions 4 warning conditions 5 normal but significant condition 6 informational 7+ debug-level messages The default value of LEVEL is 5. The default value of DEFAULT_MESSAGE is "". Returns the new object. =back =head1 METHODS =over 4 =item write( MESSAGE [, LEVEL ] ) If LEVEL is less or equal to the LEVEL data member, or if LEVEL is undefined, the string in MESSAGE will be written to the log file. Does not return anything. =item level( [ LEVEL ] ) Access method to the LEVEL data member. If LEVEL is defined, the LEVEL data member will get its value. Returns the value of the LEVEL data member. =item default_message( [ MESSAGE ] ) Access method to the DEFAULT_MESSAGE data member. If MESSAGE is defined, the DEFAULT_MESSAGE data member will get its value. Returns the value of the DEFAULT_MESSAGE data member. =item log_line_numbers( [ BOOLEAN ] ) If this flag is set to true, the string will hold the file that calls the subroutine and the line where the call is issued. The default value is zero. =item template( [ TEMPLATE ] ) Access method to the TEMPLATE data member. The TEMPLATE data member is a string that defines how the log entries will look like. The default TEMPLATE is: '[] <> ' Where: will be replaced by a string that represent the date. For example: 09/01/2000 17:00:13 will be replaced by the level of the entry. will be replaced by a call trace string. For example: CGIDaemon::listen > MyCGIDaemon::accepted will be replaced by the value of the DEFAULT_MESSAGE data member. will be replaced by the message string that is sent to the C method. Returns the value of the TEMPLATE data member. =head1 AUTHOR Rani Pinchuk, rani@cpan.org =head1 COPYRIGHT Copyright (c) 2001-2002 Ockham Technology N.V. & Rani Pinchuk. All rights reserved. This package is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L =cut HaCi/modules/Math/0000755000175000000000000000000012475447616013423 5ustar fighterrootHaCi/modules/Math/Base85.pm0000644000175000000000000000467310672146763015016 0ustar fighterrootpackage Math::Base85; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $base85_digits); use Carp; use Exporter; use Math::BigInt qw(:constant); $VERSION = '0.2'; @ISA = qw(Math::BigInt); @EXPORT = qw(); @EXPORT_OK = qw(from_base85 to_base85); =head1 NAME Math::Base85 - Perl extension for base 85 numbers, as referenced by RFC 1924 =head1 SYNOPSIS use Math::Base85; $bigint = from_base85($number); $b85str = to_base85($bigint); =head1 DESCRIPTION RFC 1924 describes a compact, fixed-size representation of IPv6 addresses which uses a base 85 number system. This module handles some of the uglier details of it. The base 85 numbers (from 0 to 84) are as follows: 0..9 A..Z a..z ! # $ % & ( ) * + - ; < = > ? @ ^ _ ` { | } ~ At the moment, there's not much in this module. But it should be sufficient for the purposes of RFC 1924. This module has a variable called C<$Math::Base85::base85_digits>, which is a string containing the digits of the base 85 alphabet from lowest (0) to highest (~), in that order. Additionally, the following two functions are defined for general use. (They will be exported upon request.) =cut $Math::Base85::base85_digits = join('', '0' .. '9', 'A' .. 'Z', 'a' .. 'z', '!', '#', qw/$ % & ( ) * + - ; < = > ? @ ^ _ ` { | } ~/, ); # Maybe we can make this a little more general... use constant B85_BASE => 85; =pod =head1 from_base85 =head2 Parameters A string composed of valid base 85 digits. =head2 Returns A C object representing the number. =cut sub from_base85 { my $num = shift; my @digits = split(//, $num); my $answer = new Math::BigInt "0"; my $n; my $d; while (defined($d = shift @digits)) { $answer = $answer * B85_BASE; $n = index($base85_digits, $d); if ($n < 0) { croak __PACKAGE__ . "::from_base85 -- invalid base 85 digit $d"; } $answer = $answer + $n; } return $answer; } =pod =head1 to_base85 =head2 Parameters A C object. =head2 Returns A string of base 85 digits representing the number. =cut sub to_base85 { my $num = shift; my @digits; my $q; my $r; my $d; while ($num > 0) { $q = $num / B85_BASE; $r = $num % B85_BASE; $d = substr($base85_digits, $r, 1); unshift @digits, $d; $num = $q; } unshift @digits, '0' unless (@digits); return join('', @digits); } =head1 AUTHOR Tony Monroe =head1 SEE ALSO perl(1). =cut 1; __END__ HaCi/modules/MySQL/0000755000175000000000000000000012475447616013477 5ustar fighterrootHaCi/modules/MySQL/Database.pm0000644000175000000000000000732510672146763015544 0ustar fighterrootpackage MySQL::Database; use strict; use Carp qw(:DEFAULT cluck); use MySQL::Utils qw(debug auth_args_string read_file); use MySQL::Table; sub new { my $class = shift; my %p = @_; my $self = {}; bless $self, ref $class || $class; debug(2, " constructing new MySQL::Database\n"); $self->parse_auth_args($p{auth}); if ($p{file}) { $self->canonicalise_file($p{file}); } elsif ($p{db}) { $self->read_db($p{db}); } else { confess "MySQL::Database::new called without db or file params"; } $self->_parse_defs(); return $self; } sub parse_auth_args { my $self = shift; my ($args) = @_; my $string = auth_args_string(%$args); debug(3, " auth args: $string\n"); $self->{_source}{auth} = $string; } sub canonicalise_file { my $self = shift; my ($file) = @_; $self->{_source}{file} = $file; debug(3, " fetching table defs from file $file\n"); # FIXME: option to avoid create-and-dump bit # create a temporary database using defs from file ... # hopefully the temp db is unique! my $temp_db = sprintf "HaCi_mysqldiff_temp_%d_%d", time(), int(rand(999999)); debug(3, " creating temporary database $temp_db\n"); my $defs = join '', read_file($file); die "$file contains dangerous command '$1'; aborting.\n" if $defs =~ /;\s*(use|((drop|create)\s+database))\s/i; my $args = $self->auth_args; open(MYSQL, "| mysql $args") or die "Couldn't execute `mysql$args': $!\n"; print MYSQL <_get_defs($temp_db); debug(3, " dropping temporary database $temp_db\n"); open(MYSQL, "| mysql $args") or die "Couldn't execute `mysql$args': $!\n"; print MYSQL "DROP DATABASE $temp_db;\n"; close(MYSQL); } sub read_db { my $self = shift; my ($db) = @_; $self->{_source}{db} = $db; debug(3, " fetching table defs from db $db\n"); $self->_get_defs($db); } sub auth_args { my $self = shift; return $self->{_source}{auth}; } sub _get_defs { my $self = shift; my ($db) = @_; my $args = $self->auth_args; open(MYSQLDUMP, "mysqldump -d $args $db |") or die "Couldn't read ${db}'s table defs via mysqldump: $!\n"; debug(3, " running mysqldump -d $args $db\n"); my $defs = $self->{_defs} = [ ]; close(MYSQLDUMP); } sub _parse_defs { my $self = shift; return if $self->{_tables}; debug(3, " parsing table defs\n"); my $defs = join '', grep ! /^\s*(\#|--)/, @{$self->{_defs}}; my @tables = split /(?=^\s*create\s+table\s+)/im, $defs; $self->{_tables} = []; foreach my $table (@tables) { next unless $table =~ /create\s+table/i; my $obj = MySQL::Table->new(source => $self->{_source}, def => $table); push @{$self->{_tables}}, $obj; $self->{_by_name}{$obj->name()} = $obj; } } sub name { my $self = shift; return $self->{_source}{file} || $self->{_source}{db}; } sub tables { return @{$_[0]->{_tables}}; } sub table_by_name { my $self = shift; my ($name) = @_; return $self->{_by_name}{$name}; } sub source_type { my $self = shift; return 'file' if $self->{_source}{file}; return 'db' if $self->{_source}{db}; } sub summary { my $self = shift; if ($self->{_source}{file}) { return "file: " . $self->{_source}{file}; } elsif ($self->{_source}{db}) { my $args = $self->{_source}{auth}; $args =~ tr/-//d; $args =~ s/\bpassword=\S+//; $args =~ s/^\s*(.*?)\s*$/$1/; my $summary = " db: " . $self->{_source}{db}; $summary .= " ($args)" if $args; return $summary; } else { return 'unknown'; } } 1; HaCi/modules/MySQL/Diff.pm0000644000175000000000000001453310672146763014707 0ustar fighterrootpackage MySQL::Diff; use strict; use base qw(Exporter); use vars qw(@EXPORT_OK); @EXPORT_OK = qw(parse_arg diff_dbs); use MySQL::Database; use MySQL::Utils qw(parse_arg debug); our $VERSION = '0.30'; sub diff_dbs { my ($opts, @db) = @_; my %opts = %$opts; my $table_re = $opts{'table-re'}; debug(1, "comparing databases\n"); my @changes = (); foreach my $table1 ($db[0]->tables()) { my $name = $table1->name(); if ($table_re && $name !~ $table_re) { debug(2, " table `$name' didn't match /$table_re/; ignoring\n"); next; } debug(2, " looking at tables called `$name'\n"); if (my $table2 = $db[1]->table_by_name($name)) { debug(4, " comparing tables called `$name'\n"); push @changes, diff_tables($opts, $table1, $table2); } else { debug(3, " table `$name' dropped\n"); push @changes, "DROP TABLE $name;\n\n" unless $opts{'only-both'} || $opts{'keep-old-tables'}; } } foreach my $table2 ($db[1]->tables()) { my $name = $table2->name(); if ($table_re && $name !~ $table_re) { debug(2, " table `$name' matched $opts{'table-re'}; ignoring\n"); next; } if (! $db[0]->table_by_name($name)) { debug(3, " table `$name' added\n"); push @changes, $table2->def() . "\n" unless $opts{'only-both'}; } } my $out = ''; if (@changes) { $out .= diff_banner(@db); $out .= join '', @changes; } return $out; } sub diff_banner { my @db = @_; my $summary1 = $db[0]->summary(); my $summary2 = $db[1]->summary(); my $now = scalar localtime(); return <name(); my %fields1 = $table1->fields; my %fields2 = $table2->fields; my @changes = (); foreach my $field (keys %fields1) { debug(5, " table1 had field `$field'\n"); my $f1 = $fields1{$field}; if (my $f2 = $fields2{$field}) { if ($f1 ne $f2) { if (not $opts{tolerant} or (($f1 !~ m/$f2\(\d+,\d+\)/) and ($f1 ne "$f2 DEFAULT '' NOT NULL") and ($f1 ne "$f2 NOT NULL") )) { debug(4, " field `$field' changed\n"); my $change = "ALTER TABLE $name1 CHANGE COLUMN $field $field $f2;"; $change .= " # was $f1" unless $opts{'no-old-defs'}; $change .= "\n"; push @changes, $change; } } } else { debug(4, " field `$field' removed\n"); my $change = "ALTER TABLE $name1 DROP COLUMN $field;"; $change .= " # was $fields1{$field}" unless $opts{'no-old-defs'}; $change .= "\n"; push @changes, $change; } } foreach my $field (keys %fields2) { if (! $fields1{$field}) { debug(4, " field `$field' added\n"); push @changes, "ALTER TABLE $name1 ADD COLUMN $field $fields2{$field};\n"; } } return @changes; } sub diff_indices { my ($opts, $table1, $table2) = @_; my %opts = %$opts; my $name1 = $table1->name(); my %indices1 = %{ $table1->indices() }; my %indices2 = %{ $table2->indices() }; my @changes = (); foreach my $index ($table1->indices_keys) { my $old_type = $table1->unique_index($index) ? 'UNIQUE' : 'INDEX'; if ($indices2{$index}) { if ($indices1{$index} ne $indices2{$index} || ($table1->unique_index($index) xor $table2->unique_index($index))) { debug(4, " index `$index' changed\n"); my $new_type = $table2->unique_index($index) ? 'UNIQUE' : 'INDEX'; my $changes = ''; if ($indices1{$index}) { $changes .= "ALTER TABLE $name1 DROP INDEX $index;"; $changes .= " # was $old_type ($indices1{$index})" unless $opts{'no-old-defs'}; $changes .= "\n"; } $changes .= <name(); my $primary1 = $table1->primary_key(); my $primary2 = $table2->primary_key(); return () unless $primary1 || $primary2; my @changes = (); if ($primary1 && ! $primary2) { debug(4, " primary key `$primary1' dropped\n"); my $change = "ALTER TABLE $name1 DROP PRIMARY KEY;"; $change .= " # was $primary1" unless $opts{'no-old-defs'}; return ( "$change\n" ); } if (! $primary1 && $primary2) { debug(4, " primary key `$primary2' added\n"); return ("ALTER TABLE $name1 ADD PRIMARY KEY $primary2;\n"); } if ($primary1 ne $primary2) { debug(4, " primary key changed\n"); my $change = "ALTER TABLE $name1 DROP PRIMARY KEY;"; $change .= " # was $primary1" unless $opts{'no-old-defs'}; $change .= <options(); my $options2 = $table2->options(); my $name = $table1->name(); my @changes = (); if ($opts->{tolerant}) { $options1 =~ s/AUTO_INCREMENT=\d+\s*//; $options2 =~ s/AUTO_INCREMENT=\d+\s*//; } if ($options1 ne $options2) { my $change = "ALTER TABLE $name $options2;"; $change .= " # was " . ($options1 || 'blank') unless $opts{'no-old-defs'}; $change .= "\n"; push @changes, $change; } return @changes; } 1; HaCi/modules/MySQL/Table.pm0000644000175000000000000000605711026726044015056 0ustar fighterrootpackage MySQL::Table; use strict; use Carp qw(:DEFAULT cluck); use Class::MakeMethods::Template::Hash 'new --and_then_init' => 'new', 'scalar' => 'name def source primary_key options', 'array --get_set_ref' => 'lines', 'hash' => 'fields indices unique_index fulltext', ; use MySQL::Utils qw(debug); sub init { my $self = shift; debug(4, " constructing new MySQL::Table\n"); croak "MySQL::Table::new called without def params" unless $self->def; $self->parse; } sub parse { my $self = shift; (my $def = $self->def) =~ s/\n+/\n/; $self->def($def); $self->lines([ grep ! /^\s*$/, split /(?=^)/m, $def ]); my @lines = $self->lines; debug(5, " parsing table def\n"); my $name; if ($lines[0] =~ /^\s*create\s+table\s+(\S+)\s+\(\s*$/i) { $self->name($name = $1); debug(5, " got table name `$name'\n"); shift @lines; } else { croak "couldn't figure out table name"; } while (@lines) { $_ = shift @lines; s/^\s*(.*?),?\s*$/$1/; # trim whitespace and trailing commas debug(7, " line: [$_]\n"); if (/^PRIMARY\s+KEY\s+(.+)$/) { my $primary = $1; croak "two primary keys in table `$name': `$primary', `", $self->primary_key, "'\n" if $self->primary_key; $self->primary_key($primary); debug(6, " got primary key $primary\n"); next; } if (/^(KEY|UNIQUE(?: KEY)?)\s+(\S+?)\s*\((.*)\)$/) { my ($type, $key, $val) = ($1, $2, $3); croak "index `$key' duplicated in table `$name'\n" if $self->indices($key); $self->indices_push($key, $val); my $unique = $type =~ /unique/i; $self->unique_index($key, $unique); debug(6, " got ", $unique ? 'unique ' : '', "index key `$key': ($val)\n"); next; } if (/^(FULLTEXT(?: KEY|INDEX)?)\s+(\S+?)\s*\((.*)\)$/) { my ($type, $key, $val) = ($1, $2, $3); croak "FULLTEXT index `$key' duplicated in table `$name'\n" if $self->fulltext($key); $self->fulltext_push($key, $val); debug(6, " got FULLTEXT index `$key': ($val)\n"); next; } if (/^\)\s*(.*?);$/) { # end of table definition my $options = $self->options($1); debug(6, " got table options `$options'\n"); last; } if (/^(\S+)\s*(.*)/) { my ($field, $def) = ($1, $2); croak "definition for field `$field' duplicated in table `$name'\n" if $self->fields($field); $self->fields_push($field, $def); debug(6, " got field def `$field': $def\n"); next; } croak "unparsable line in definition for table `$name':\n$_"; } warn "table `$name' didn't have terminator\n" unless defined $self->options; map { s/\/\*.*\*\///; s/^;*$//; s/DROP TABLE IF EXISTS.*//; s/SET character_set_client.*//; s/SET \@saved_cs_client.*//; s/^\n*$//; shift @lines if /^\s$/; } @lines; warn "table `$name' had trailing garbage:\n", join '', @lines if @lines && join('', @lines) !~ /^\s*$/; } 1; HaCi/modules/MySQL/Utils.pm0000644000175000000000000000423710672146763015137 0ustar fighterrootpackage MySQL::Utils; use strict; use base qw(Exporter); our @EXPORT_OK = qw(parse_arg auth_args_string read_file debug_level debug); sub parse_arg { my ($opts, $arg, $num) = @_; my %opts = %$opts; debug(1, "parsing arg $num: `$arg'\n"); my $authnum = $num + 1; my %auth = (); for my $auth (qw/host port user password socket/) { $auth{$auth} = $opts{"$auth$authnum"} || $opts{$auth}; delete $auth{$auth} unless $auth{$auth}; } if ($arg =~ /^db:(.*)/) { return new MySQL::Database(db => $1, auth => \%auth); } if ($opts{"host$authnum"} || $opts{"port$authnum"} || $opts{"user$authnum"} || $opts{"password$authnum"} || $opts{"socket$authnum"}) { return new MySQL::Database(db => $arg, auth => \%auth); } if (-f $arg) { return new MySQL::Database(file => $arg, auth => \%auth); } my %dbs = available_dbs(%auth); debug(2, " available databases: ", (join ', ', keys %dbs), "\n"); if ($dbs{$arg}) { return new MySQL::Database(db => $arg, auth => \%auth); } return "`$arg' is not a valid file or database.\n"; } sub available_dbs { my %auth = @_; my $args = auth_args_string(%auth); # evil but we don't use DBI because I don't want to implement -p properly # not that this works with -p anyway ... open(MYSQLSHOW, "mysqlshow$args |") or die "Couldn't execute `mysqlshow$args': $!\n"; my @dbs = (); while () { next unless /^\| (\w+)/; push @dbs, $1; } close(MYSQLSHOW) or die "mysqlshow$args failed: $!"; return map { $_ => 1 } @dbs; } sub auth_args_string { my %auth = @_; my $args = ''; for my $arg (qw/host port user password socket/) { $args .= " --$arg=$auth{$arg}" if $auth{$arg}; } return $args; } sub read_file { my ($file) = @_; open(FILE, $file) or die "Couldn't open `$file': $!\n"; my @contents = ; close(FILE); return @contents; } { my $debug_level = 0; sub debug_level { my ($new_debug_level) = @_; $debug_level = $new_debug_level if defined $new_debug_level; return $debug_level; } sub debug { my $level = shift; print STDERR @_ if ($debug_level >= $level) && @_; } } 1; HaCi/modules/MySQL/mysqldiff.pl0000755000175000000000000001006510672146763016033 0ustar fighterroot#!/usr/bin/perl -w # # mysqldiff # # Utility to compare table definitions in two MySQL databases, # and output a patch in the format of ALTER TABLE statements # which converts the first database structure into in the second. # # Developed as part of the http://www.guideguide.com/ project. # If you like hacking Perl in a cool environment, come and work for us! # # See http://adamspiers.org/computing/mysqldiff/ for the # latest version. # # Copyright (c) 2000 Adam Spiers . All rights # reserved. This program is free software; you can redistribute it # and/or modify it under the same terms as Perl itself. # use strict; require 5.004; use Carp qw(:DEFAULT cluck); use FindBin qw($RealBin $Script); use lib $RealBin; use Getopt::Long; use MySQL::Diff qw(parse_arg diff_dbs); use MySQL::Utils qw(debug_level); my %opts = (); GetOptions(\%opts, "help|?", "debug|d:i", "apply|A", "batch-apply|B", "keep-old-tables|k", "no-old-defs|n", "only-both|o", "table-re|t=s", "host|h=s", "port|P=s", "user|u=s", "password|p:s", "host1|h1=s", "port1|P1=s", "user1|u1=s", "password1|p1:s", "host2|h2=s", "port2|P2=s", "user2|u2=s", "password2|p2:s", "socket|s=s", "socket1|s1=s", "socket2|s2=s", "tolerant|i" ); if (@ARGV != 2 or $opts{help}) { usage(); exit 1; } $opts{debug}++ if exists $opts{debug} && $opts{debug} == 0; debug_level($opts{debug} || 0); my $table_re; $table_re = qr/$opts{'table-re'}/ if $opts{'table-re'}; my @db = (); for my $num (0, 1) { my $new_db = parse_arg(\%opts, $ARGV[$num], $num); usage($new_db) unless ref $new_db; $db[$num] = $new_db; } $| = 1; my $diffs = diff_dbs(\%opts, @db); print $diffs; apply($diffs) if $opts{apply} || $opts{'batch-apply'}; exit 0; ############################################################################## sub usage { print STDERR @_, "\n" if @_; die < Options: -?, --help show this help -A, --apply interactively patch database1 to match database2 -B, --batch-apply non-interactively patch database1 to match database2 -d, --debug[=N] enable debugging [level N, default 1] -o, --only-both only output changes for tables in both databases -k, --keep-old-tables don't output DROP TABLE commands -n, --no-old-defs suppress comments describing old definitions -t, --table-re=REGEXP restrict comparisons to tables matching REGEXP -i, --tolerant ignore DEFAULT and formatting changes -h, --host=... connect to host -P, --port=... use this port for connection -u, --user=... user for login if not current user -p, --password[=...] password to use when connecting to server -s, --socket=... socket to use when connecting to server for only, where N == 1 or 2, -hN, --hostN=... connect to host -PN, --portN=... use this port for connection -uN, --userN=... user for login if not current user -pN, --passwordN[=...] password to use when connecting to server -sN, --socketN=... socket to use when connecting to server Databases can be either files or database names. If there is an ambiguity, the file will be preferred; to prevent this prefix the database argument with `db:'. EOF } sub apply { my ($diffs) = @_; if (! $diffs) { print "No differences to apply.\n"; exit 0; } my $db0 = $db[0]->name; if ($db[0]->source_type ne 'db') { die "$db0 is not a database; cannot apply changes.\n"; } unless ($opts{'batch-apply'}) { print "\nApply above changes to $db0 [y/N] ? "; print "\n(CAUTION! Changes contain DROP TABLE commands.) " if $diffs =~ /\bDROP TABLE\b/i; my $reply = ; return unless $reply =~ /^y(es)?$/i; } print "Applying changes ... "; my $args = $db[0]->auth_args; my $pipe = "mysql$args $db0"; open(PATCH, "|$pipe") or die "Couldn't open pipe to '$pipe': $!\n"; print PATCH $diffs; close(PATCH) or die "Couldn't close pipe: $!\n"; print "done.\n"; } HaCi/modules/Net/0000755000175000000000000000000012475447616013260 5ustar fighterrootHaCi/modules/Net/Nslookup.pm0000644000175000000000000002072710474157163015430 0ustar fighterrootpackage Net::Nslookup; # ------------------------------------------------------------------- # $Id: Nslookup.pm,v 1.1.1.1 2006/08/26 23:51:15 larsux Exp $ # ------------------------------------------------------------------- # Net::Nslookup - Provide nslookup(1)-like capabilities # Copyright (C) 2002 darren chamberlain # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; version 2. # # This program 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 # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # ------------------------------------------------------------------- use strict; use vars qw($VERSION $DEBUG @EXPORT $TIMEOUT $MX_IS_NUMERIC $WIN32); use base qw(Exporter); $VERSION = 1.16; @EXPORT = qw(nslookup); $DEBUG = 0 unless defined $DEBUG; $TIMEOUT = 15 unless defined $TIMEOUT; $MX_IS_NUMERIC = 0 unless defined $MX_IS_NUMERIC; # Win32 doesn't implement alarm; what about MacOS? # Added check based on bug report from Roland Bauer # (not RT'ed) $WIN32 = $^O =~ /win/i; use Carp; use Exporter; use Socket qw/ inet_ntoa inet_aton /; my %_lookups = ( 'a' => \&_lookup_a, 'cname' => \&_lookup_a, 'mx' => \&_lookup_mx, 'ns' => \&_lookup_ns, 'ptr' => \&_lookup_ptr, 'txt' => \&_lookup_txt, ); # ---------------------------------------------------------------------- # qslookup($term) # # "quick" nslookup, doesn't require Net::DNS. # # ---------------------------------------------------------------------- # Bugs: # # * RT#1947 (Scott Schnieder) # The qslookup subroutine fails if no records for the domain # exist, because inet_ntoa freaks out about inet_aton not # returning anything. # ---------------------------------------------------------------------- # Context! sub qslookup($) { my $a = inet_aton $_[0]; return $a ? inet_ntoa $a : ''; } # ---------------------------------------------------------------------- # nslookup(%args) # # Does the actual lookup, deferring to helper functions as necessary. # ---------------------------------------------------------------------- sub nslookup { my $options = isa($_[0], 'HASH') ? shift : @_ % 2 ? { 'host', @_ } : { @_ }; my ($term, $type, $server, @answers, $sub); # Some reasonable defaults. $term = lc ($options->{'term'} || $options->{'host'} || $options->{'domain'} || return); $type = lc ($options->{'type'} || $options->{'qtype'} || "A"); $server = $options->{'server'} || ''; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $TIMEOUT unless $WIN32; $sub = $_lookups{$type}; defined $sub ? @answers = $sub->($term, $server) : die "Invalid type '$type'"; alarm 0 unless $WIN32; }; if ($@) { die "Bad things happened: $@" unless $@ eq "alarm\n"; carp qq{Timeout: nslookup("type" => "$type", "host" => "$term")}; } return $answers[0] if (@answers == 1); return (wantarray) ? @answers : $answers[0]; } sub _lookup_a { my ($term, $server) = @_; debug("Performing 'A' lookup on `$term'"); return qslookup($term); } sub _lookup_mx { my ($term, $server) = @_; my $res = ns($server); my (@mx, $rr, @answers); debug("Performing 'MX' lookup on `$term'"); @mx = mx($res, $term); unless($MX_IS_NUMERIC) { for $rr (@mx) { push(@answers, $rr->exchange); } return @answers; } for $rr (@mx) { push @answers, nslookup(type => "A", host => $rr->exchange); } return @answers; } sub _lookup_ns { my ($term, $server) = @_; my $res = ns($server); my (@answers, $query, $rr); debug("Performing 'NS' lookup on `$term'"); $query = $res->search($term, "NS") || return; for $rr ($query->answer) { push @answers, nslookup(type => "A", host => $rr->nsdname); } return @answers; } sub _lookup_ptr { my ($term, $server) = @_; my $res = ns($server); my (@answers, $query, $rr); debug("Performing 'PTR' lookup on `$term'"); $query = $res->search($term, "PTR") || return; for $rr ($query->answer) { push @answers, $rr->ptrdname; } return @answers; } sub _lookup_txt ($\@) { my ($term, $server) = @_; my $res = ns($server); my (@answers, $query, $rr); debug("Performing 'TXT' lookup on `$term'"); $query = $res->search($term, "TXT") || return; for $rr ($query->answer) { push @answers, $rr->rdatastr(); } return @answers; } { my %res; sub ns { my $server = shift || ""; unless (defined $res{$server}) { require Net::DNS; import Net::DNS; $res{$server} = Net::DNS::Resolver->new; # $server might be empty if ($server) { if (ref($server) eq 'ARRAY') { $res{$server}->nameservers(@$server); } else { $res{$server}->nameservers($server); } } } return $res{$server}; } sub dump_res { require Data::Dumper; return Data::Dumper::Dumper(\%res); } } sub isa { &UNIVERSAL::isa } sub debug { carp @_ if ($DEBUG) } 1; __END__ =head1 NAME Net::Nslookup - Provide nslookup(1)-like capabilities =head1 SYNOPSIS use Net::Nslookup; my @addrs = nslookup $host; my @mx = nslookup(qtype => "MX", domain => "perl.org"); =head1 DESCRIPTION Net::Nslookup provides the capabilities of the standard UNIX command line tool nslookup(1). Net::DNS is a wonderful and full featured module, but quite often, all you need is `nslookup $host`. This module provides that functionality. Net::Nslookup exports a single function, called C. C can be used to retrieve A, PTR, CNAME, MX, and NS records. my $a = nslookup(host => "use.perl.org", type => "A"); my @mx = nslookup(domain => "perl.org", type => "MX"); my @ns = nslookup(domain => "perl.org", type => "NS"); my $name = nslookup(host => "206.33.105.41", type => "PTR"); C takes a hash of options, one of which should be I, and performs a DNS lookup on that term. The type of lookup is determined by the I (or I) argument. If I is specified (it should be an IP address, or a reference to an array of IP addresses), that server will be used for lookups. If only a single argument is passed in, the type defaults to I, that is, a normal A record lookup. This form is significantly faster than using the full version, as it doesn't load Net::DNS for this. If C is called in a list context, and there is more than one address, an array is returned. If C is called in a scalar context, and there is more than one address, C returns the first address. If there is only one address returned (as is usually the case), then, naturally, it will be the only one returned, regardless of the calling context. I and I are synonyms for I, and can be used to make client code more readable. For example, use I when getting NS records, and use I for A records; both do the same thing. I should be a single IP address or a reference to an array of IP addresses: my @a = nslookup(host => 'boston.com', server => '4.2.2.1'); my @a = nslookup(host => 'boston.com', server => [ '4.2.2.1', '128.103.1.1' ]) By default, C returns addresses when looking up MX records; however, the Unix tool C returns names. Set $Net::Nslookup::MX_IS_NUMERIC to a true value to have MX lookups return numbers instead of names. This is a change in behavior from previous versions of C, and is more consistent with other DNS tools. =head1 TIMEOUTS Lookups timeout after $Net::Nslookup::TIMEOUT seconds (default 15). Set this to something more reasonable for your site or script. =head1 DEBUGGING Set $Net::Nslookup::DEBUG to a true value to get debugging messages carped to STDERR. =head1 TODO =over 4 =item * Support for TXT and SOA records. =back =head1 AUTHOR darren chamberlain HaCi/modules/Net/IMAP/0000755000175000000000000000000012475447616014006 5ustar fighterrootHaCi/modules/Net/IMAP/Simple.pm0000644000175000000000000005450610524232130015557 0ustar fighterrootpackage Net::IMAP::Simple; use strict; use IO::File; use IO::Socket; use vars qw[$VERSION]; $VERSION = $1 if('$Id: Simple.pm,v 1.1 2006/11/08 01:35:20 larsux Exp $' =~ /,v ([\d_.]+) /); =head1 NAME Net::IMAP::Simple - Perl extension for simple IMAP account handling. =head1 SYNOPSIS # Duh use Net::IMAP::Simple; use Email::Simple; # Create the object my $imap = Net::IMAP::Simple->new('imap.example.com') || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n"; # Log on if(!$imap->login('user','pass')){ print STDERR "Login failed: " . $imap->errstr . "\n"; exit(64); } # Print the subject's of all the messages in the INBOX my $nm = $imap->select('INBOX'); for(my $i = 1; $i <= $nm; $i++){ if($imap->seen($i)){ print "*"; } else { print " "; } my $es = Email::Simple->new(join '', @{ $imap->top($i) } ); printf("[%03d] %s\n", $i, $es->header('Subject')); } $imap->quit; =head1 DESCRIPTION This module is a simple way to access IMAP accounts. =head1 OBJECT CREATION METHOD =over 4 =item new my $imap = Net::IMAP::Simple->new( $server [ :port ]); OR my $imap = Net::IMAP::Simple->new( $server [, option_name => option_value ] ); This class method constructs a new C object. It takes one required parameter which is the server to connect to, and additional optional parameters. The server parameter may specify just the server, or both the server and port number. To specify an alternate port, seperate it from the server with a colon (C<:>), C. On success an object is returned. On failure, nothing is returned and an error message is set to $Net::IMAP::Simple. =head2 OPTIONS: Options are provided as a hash to new() =item port => int Assign the port number (default: 143) =item timeout => int (default: 90) Connection timeout in seconds. =item retry => int (default: 1) Attempt to retry the connection attmpt (x) times before giving up =item retry_delay => int (default: 5) Wait (x) seconds before retrying a connection attempt =item use_v6 => BOOL If set to true, attempt to use IPv6 sockets rather than IPv4 sockets. This option requires the IO::Socket::INET6 module =item bindaddr => str Assign a local address to bind =item use_select_cache => BOOL Enable select() caching internally =item select_cache_ttl => int The number of seconds to allow a select cache result live before running $imap->select() again. =item debug => BOOL | \*HANDLE Enable debugging output. If \*HANDLE is a valid file handle, debugging will be written to it. Otherwise debugging will be written to STDOUT =cut sub new { my ( $class, $server, %opts) = @_; my $self = bless { count => -1, } => $class; my ($srv, $prt) = split(/:/, $server, 2); $prt ||= ($opts{port} ? $opts{port} : $self->_port); $self->{server} = $srv; $self->{port} = $prt; $self->{timeout} = ($opts{timeout} ? $opts{timeout} : $self->_timeout); $self->{use_v6} = ($opts{use_v6} ? 1 : 0); $self->{retry} = ($opts{retry} ? $opts{retry} : $self->_retry); $self->{retry_delay} = ($opts{retry_delay} ? $opts{retry_delay} : $self->_retry_delay); $self->{bindaddr} = $opts{bindaddr}; $self->{use_select_cache} = $opts{use_select_cache}; $self->{select_cache_ttl} = $opts{select_cache_ttl}; $self->{debug} = $opts{debug}; # Pop the port off the address string if it's not an IPv6 IP address if(!$self->{use_v6} && $self->{server} =~ /^[A-Fa-f0-9]{4}:[A-Fa-f0-9]{4}:/ && $self->{server} =~ s/:(\d+)$//g){ $self->{port} = $1; } my $c; for(my $i = 0; $i <= $self->{retry}; $i++){ if($self->{sock} = $self->_connect){ $c = 1; last; } elsif ($i < $self->{retry}) { select(undef, undef, undef, $self->{retry_delay}); } } if(!$c){ $@ =~ s/IO::Socket::INET6?: //g; $Net::IMAP::Simple::errstr = "connection failed $@"; return; } $self->_sock->getline(); return $self; } sub _connect { my ($self) = @_; my $sock; if($self->{use_v6}){ require 'IO::Socket::INET6'; import IO::Socket::INET6; } $sock = $self->_sock_from->new( PeerAddr => $self->{server}, PeerPort => $self->{port}, Timeout => $self->{timeout}, Proto => 'tcp', ($self->{bindaddr} ? { LocalAddr => $self->{bindaddr} } : ()) ); return $sock; } sub _port { 143 } sub _sock { $_[0]->{sock} } sub _count { $_[0]->{count} } sub _last { $_[0]->{last} } sub _timeout { 90 } sub _retry { 1 } sub _retry_delay { 5 } sub _sock_from { $_[0]->{use_v6} ? 'IO::Socket::INET6' : 'IO::Socket::INET' } =pod =head1 METHODS =item login my $inbox_msgs = $imap->login($user, $passwd); This method takes two required parameters, a username and password. This pair is authenticated against the server. If authentication is successful TRUE (1) will be returned Nothing is returned on failure and the errstr() error handler is set with the error message. =cut sub login { my ( $self, $user, $pass ) = @_; return $self->_process_cmd ( cmd => [LOGIN => qq[$user "$pass"]], final => sub { 1 }, process => sub { }, ); } =pod =item select my $num_messages = $imap->select($folder); Selects a folder named in the single required parameter. The number of messages in that folder is returned on success. On failure, nothing is returned and the errstr() error handler is set with the error message. =cut sub select { my ( $self, $mbox ) = @_; $mbox = 'INBOX' unless $mbox; $self->{working_box} = $mbox; if($self->{use_select_cache} && (time - $self->{BOXES}->{ $mbox }->{proc_time}) <= $self->{select_cache_ttl}){ return $self->{BOXES}->{$mbox}->{messages}; } $self->{BOXES}->{$mbox}->{proc_time} = time; my $t_mbox = $mbox; $self->_process_cmd( cmd => [SELECT => _escape($t_mbox)], final => sub { $self->{last} = $self->{BOXES}->{$mbox}->{messages} }, process => sub { if($_[0] =~ /^\*\s+(\d+)\s+EXISTS/i){ $self->{BOXES}->{$mbox}->{messages} = $1; } elsif($_[0] =~ /^\*\s+FLAGS\s+\((.*?)\)/i){ $self->{BOXES}->{$mbox}->{flags} = [ split(/\s+/, $1) ]; } elsif($_[0] =~ /^\*\s+(\d+)\s+RECENT/i){ $self->{BOXES}->{$mbox}->{recent} = $1; } elsif($_[0] =~ /^\*\s+OK\s+\[(.*?)\s+(.*?)\]/i){ my ($flag, $value) = ($1, $2); if($value =~ /\((.*?)\)/){ $self->{BOXES}->{$mbox}->{sflags}->{$flag} = [split(/\s+/, $1)]; } else { $self->{BOXES}->{$mbox}->{oflags}->{$flag} = $value; } } }, ) || return; return $self->{last} } =pod =item messages print "Messages in Junk Mail -- " . $imap->messages("INBOX.Junk Mail") . "\n"; This method is an alias for $imap->select =cut sub messages { my ($self, $folder) = @_; return $self->select($folder); } =pod =item flags print "Avaliable server flags: " . join(", ", $imap->flags) . "\n"; This method accepts an optional folder name and returns the current avaliable server flags as a list, for the selected folder. If no folder name is provided the last folder $imap->select'ed will be used. This method uses caching. =cut sub flags { my ($self, $folder) = @_; $self->select($folder); return @{ $self->{BOXES}->{ $self->current_box }->{flags} }; } =pod =item recent print "Recent messages value: " . $imap->recent . "\n"; This method accepts an optional folder name and returns the 'RECENT' value provided durning a SELECT result set. If no folder name is provided the last folder $imap->select'ed will be used. This method uses caching. =cut sub recent { my ($self, $folder) = @_; $self->select($folder); return $self->{BOXES}->{ $self->current_box }->{recent}; } =pod =item current_box print "Current Mail Box folder: " . $imap->current_box . "\n"; This method returns the current working mail box folder name. =cut sub current_box { my ($self) = @_; return ($self->{working_box} ? $self->{working_box} : 'INBOX'); } =pod =item top my $header = $imap->top( $message_number ); print for @{$header}; This method accepts a message number as its required parameter. That message will be retrieved from the currently selected folder. On success this method returns a list reference containing the lines of the header. Nothing is returned on failure and the errstr() error handler is set with the error message. =cut sub top { my ( $self, $number ) = @_; my @lines; $self->_process_cmd( cmd => [FETCH => qq[$number rfc822.header]], final => sub { \@lines }, process => sub { push @lines, $_[0] if $_[0] =~ /^(?: \s+\S+ | [^:]+: )/x }, ); } =pod =item seen print "Seen it!" if $imap->seen( $message_number ); A message number is the only required parameter for this method. The message's C<\Seen> flag will be examined and if the message has been seen a true value is returned. All other failures return a false value and the errstr() error handler is set with the error message. =cut sub seen { my ( $self, $number ) = @_; my $lines = ''; $self->_process_cmd( cmd => [FETCH=> qq[$number (FLAGS)]], final => sub { $lines =~ /\\Seen/i }, process => sub { $lines .= $_[0] }, ); } =pod =item list my $message_size = $imap->list($message_number); my $mailbox_sizes = $imap->list; This method returns size information for a message, as indicated in the single optional parameter, or all messages in a mailbox. When querying a single message a scalar value is returned. When listing the entire mailbox a hash is returned. On failure, nothing is returned and the errstr() error handler is set with the error message. =cut sub list { my ( $self, $number ) = @_; my $messages = $number || '1:' . $self->_last; my %list; $self->_process_cmd( cmd => [FETCH => qq[$messages RFC822.SIZE]], final => sub { $number ? $list{$number} : \%list }, process => sub { if ($_[0] =~ /^\*\s+(\d+).*RFC822.SIZE\s+(\d+)/i) { $list{$1} = $2; } }, ); } =pod =item get my $message = $imap->get( $message_number ); print for @{$message}; This method fetches a message and returns its lines in a list reference. On failure, nothing is returned and the errstr() error handler is set with the error message. =cut sub get { my ( $self, $number ) = @_; my @lines; $self->_process_cmd( cmd => [FETCH => qq[$number rfc822]], final => sub { pop @lines; \@lines }, process => sub { if($_[0] !~ /^\* \d+ FETCH/){ push @lines, join(' ', @_); } }, ); } =pod =item getfh my $file = $imap->getfh( $message_number ); print <$file>; On success this method returns a file handle pointing to the message identified by the required parameter. On failure, nothing is returned and the errstr() error handler is set with the error message. =cut sub getfh { my ( $self, $number ) = @_; my $file = IO::File->new_tmpfile; my $buffer; $self->_process_cmd( cmd => [FETCH => qq[$number rfc822]], final => sub { seek $file, 0, 0; $file }, process => sub { if($_[0] !~ /^\* \d+ FETCH/){ defined($buffer) and print $file $buffer; $buffer = $_[0]; } }, ); } =pod =item quit $imap->quit; OR $imap->quit(BOOL); This method logs out of the IMAP server, expunges the selected mailbox, and closes the connection. No error message will ever be returned from this method. Optionally if BOOL is TRUE (1) then a hard quit is preformed which closes the socket connection. This hard quit will still issue both EXPUNGE and LOGOUT commands however the response is ignored and the socket is closed after issuing the commands. =cut sub quit { my ( $self, $hq ) = @_; $self->_send_cmd('EXPUNGE'); if(!$hq){ $self->_process_cmd(cmd => ['LOGOUT'], final => sub {}, process => sub{}); } else { $self->_send_cmd('LOGOUT'); } $self->_sock->close; return 1; } =pod =item last my $message_number = $imap->last; This method retuns the message number of the last message in the selected mailbox, since the last time the mailbox was selected. On failure, nothing is returned and the errstr() error handler is set with the error message. =cut sub last { shift->_last } =pod =item delete print "Gone!" if $imap->delete( $message_number ); This method deletes a message from the selected mailbox. On success it returns true. False on failure and the errstr() error handler is set with the error message. =cut sub delete { my ( $self, $number ) = @_; $self->_process_cmd( cmd => [STORE => qq[$number +FLAGS (\\Deleted)]], final => sub { 1 }, process => sub { }, ); } sub _process_list { my ($self, $line) = @_; $self->_debug(caller, __LINE__, '_process_list', $line) if $self->{debug}; my @list; if ( $line =~ /^\*\s+(LIST|LSUB).*\s+\{\d+\}\s*$/i ) { chomp( my $res = $self->_sock->getline ); $res =~ s/\r//; _escape($res); push @list, $res; $self->_debug(caller, __LINE__, '_process_list', $res) if $self->{debug}; } elsif ( $line =~ /^\*\s+(LIST|LSUB).*\s+(\".*?\")\s*$/i || $line =~ /^\*\s+(LIST|LSUB).*\s+(\S+)\s*$/i ) { push @list, $2; } @list; } =pod =item mailboxes my @boxes = $imap->mailboxes; my @folders = $imap->mailboxes("Mail/%"); my @lists = $imap->mailboxes("lists/perl/*", "/Mail/"); This method returns a list of mailboxes. When called with no arguments it recurses from the IMAP root to get all mailboxes. The first optional argument is a mailbox path and the second is the path reference. RFC 3501 section 6.3.8 has more information. On failure nothing is returned and the errstr() error handler is set with the error message. =cut sub mailboxes { my ( $self, $box, $ref ) = @_; $ref ||= '""'; my @list; if ( ! defined $box ) { # recurse, should probably follow # RFC 2683: 3.2.1.1. Listing Mailboxes return $self->_process_cmd( cmd => [LIST => qq[$ref *]], final => sub { _unescape($_) for @list; @list }, process => sub { push @list, $self->_process_list($_[0]);}, ); } else { return $self->_process_cmd( cmd => [LIST => qq[$ref $box]], final => sub { _unescape($_) for @list; @list }, process => sub { push @list, $self->_process_list($_[0]) }, ); } } =pod =item mailboxes_subscribed my @boxes = $imap->mailboxes_subscribed; my @folders = $imap->mailboxes_subscribed("Mail/%"); my @lists = $imap->mailboxes_subscribed("lists/perl/*", "/Mail/"); This method returns a list of mailboxes subscribed to. When called with no arguments it recurses from the IMAP root to get all mailboxes. The first optional argument is a mailbox path and the second is the path reference. RFC 3501 has more information. On failure nothing is returned and the errstr() error handler is set with the error message. =cut sub mailboxes_subscribed { my ( $self, $box, $ref ) = @_; $ref ||= '""'; my @list; if ( ! defined $box ) { # recurse, should probably follow # RFC 2683: 3.2.2. Subscriptions return $self->_process_cmd( cmd => [LSUB => qq[$ref *]], final => sub { _unescape($_) for @list; @list }, process => sub { push @list, $self->_process_list($_[0]) }, ); } else { return $self->_process_cmd( cmd => [LSUB => qq[$ref $box]], final => sub { _unescape($_) for @list; @list }, process => sub { push @list, $self->_process_list($_[0]) }, ); } } =pod =item create_mailbox print "Created" if $imap->create_mailbox( "/Mail/lists/perl/advocacy" ); This method creates the mailbox named in the required argument. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub create_mailbox { my ( $self, $box ) = @_; _escape( $box ); return $self->_process_cmd( cmd => [CREATE => $box], final => sub { 1 }, process => sub { }, ); } =pod =item expunge_mailbox print "Expunged" if $imap->expunge_mailbox( "/Mail/lists/perl/advocacy" ); This method removes all mail marked as deleted in the mailbox named in the required argument. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub expunge_mailbox { my ($self, $box) = @_; return if !$self->select($box); return $self->_process_cmd( cmd => ['EXPUNGE'], final => sub { 1 }, process => sub { }, ); } =pod =item delete_mailbox print "Deleted" if $imap->delete_mailbox( "/Mail/lists/perl/advocacy" ); This method deletes the mailbox named in the required argument. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub delete_mailbox { my ( $self, $box ) = @_; _escape( $box ); return $self->_process_cmd( cmd => [DELETE => $box], final => sub { 1 }, process => sub { }, ); } =pod =item rename_mailbox print "Renamed" if $imap->rename_mailbox( $old => $new ); This method renames the mailbox in the first required argument to the mailbox named in the second required argument. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub rename_mailbox { my ( $self, $old_box, $new_box ) = @_; _escape( $old_box ); _escape( $new_box ); return $self->_process_cmd( cmd => [RENAME => qq[$old_box $new_box]], final => sub { 1 }, process => sub { }, ); } =pod =item folder_subscribe print "Subscribed" if $imap->folder_subscribe( "/Mail/lists/perl/advocacy" ); This method subscribes to the folder. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub folder_subscribe { my ($self, $box) = @_; $self->select($box); # XXX does it matter if this fails? _escape($box); return $self->_process_cmd( cmd => [SUBSCRIBE => $box], final => sub { 1 }, process => sub { }, ); } =pod =item folder_unsubscribe print "Unsubscribed" if $imap->folder_unsubscribe( "/Mail/lists/perl/advocacy" ); This method unsubscribes to the folder. Returns true on success, false on failure and the errstr() error handler is set with the error message. =cut sub folder_unsubscribe { my ($self, $box) = @_; $self->select($box); _escape($box); return $self->_process_cmd( cmd => [UNSUBSCRIBE => $box], final => sub { 1 }, process => sub { }, ); } =pod =item copy print "copied" if $imap->copy( $message_number, $mailbox ); This method copies the message number in the currently seleted mailbox to the fold specified in the second argument. Both arguments are required. On success this method returns true. Returns false on failure and the errstr() error handler is set with the error message. =cut sub copy { my ( $self, $number, $box ) = @_; _escape( $box ); return $self->_process_cmd( cmd => [COPY => qq[$number $box]], final => sub { 1 }, process => sub { }, ); } =pod =item errstr print "Login ERROR: " . $imap->errstr . "\n" if !$imap->login($user, $pass); Return the last error string captured for the last operation which failed. =cut sub errstr { return $_[0]->{_errstr}; } sub _nextid { ++$_[0]->{count} } sub _escape { $_[0] =~ s/\\/\\\\/g; $_[0] =~ s/\"/\\\"/g; $_[0] = "\"$_[0]\""; } sub _unescape { $_[0] =~ s/^"//g; $_[0] =~ s/"$//g; $_[0] =~ s/\\\"/\"/g; $_[0] =~ s/\\\\/\\/g; } sub _send_cmd { my ( $self, $name, $value ) = @_; my $sock = $self->_sock; my $id = $self->_nextid; my $cmd = "$id $name" . ($value ? " $value" : "") . "\r\n"; $self->_debug(caller, __LINE__, '_send_cmd', $cmd) if $self->{debug}; { local $\; print $sock $cmd; } return ($sock => $id); } sub _cmd_ok { my ($self, $res) = @_; my $id = $self->_count; $self->_debug(caller, __LINE__, '_send_cmd', $res) if $self->{debug}; if($res =~ /^$id\s+OK/i){ return 1; } elsif($res =~ /^$id\s+(?:NO|BAD)(?:\s+(.+))?/i){ $self->_seterrstr($1 || 'unknown error'); return 0; } else { $self->_seterrstr("warning unknown return string: $res"); return; } } sub _read_multiline { my ($self, $sock, $count) = @_; my @lines; my $read_so_far = 0; while ($read_so_far < $count) { push @lines, $sock->getline; $read_so_far += length($lines[-1]); } if($self->{debug}){ for(my $i = 0; $i < @lines; $i++){ $self->_debug(caller, __LINE__, '_read_multiline', "[$i] $lines[$i]"); } } return @lines; } sub _process_cmd { my ($self, %args) = @_; my ($sock, $id) = $self->_send_cmd(@{$args{cmd}}); my $res; while ( $res = $sock->getline ) { $self->_debug(caller, __LINE__, '_process_cmd', $res) if $self->{debug}; if ( $res =~ /^\*.*\{(\d+)\}$/ ) { $args{process}->($res); $args{process}->($_) foreach $self->_read_multiline($sock, $1); } else { my $ok = $self->_cmd_ok($res); if ( defined($ok) && $ok == 1 ) { return $args{final}->($res); } elsif ( defined($ok) && ! $ok ) { return; } else { $args{process}->($res); } } } } sub _seterrstr { my ($self, $err) = @_; $self->{_errstr} = $err; $self->_debug(caller, __LINE__, '_seterrstr', $err) if $self->{debug}; return; } sub _debug { my ($self, $package, $filename, $lineNr, $dline, $routine, $str) = @_; $str =~ s/\n/\\n/g; $str =~ s/\r/\\r/g; $str =~ s/\cM/^M/g; my $line = "[$package :: $filename :: $lineNr\@$dline -> $routine] $str\n"; if(ref($self->{debug}) eq 'GLOB'){ my $FILE = *$self->{debug}; print $FILE $line; } else { print STDOUT $line; } } =pod =back =cut 1; __END__ =head1 AUTHOR Colin Faber, >. Casey West, >. Joao Fonseca, >. =head1 SEE ALSO L, L, L =head1 COPYRIGHT Copyright (c) 2005 Colin Faber. Copyright (c) 2004 Casey West. Copyright (c) 1999 Joao Fonseca. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut HaCi/spool/0000775000175000000410000000000012475462500012762 5ustar fighterwww-dataHaCi/ChangeLog0000644000175000000000000001271512475461776012645 0ustar fighterroot2015-03-04 * CSV-Import bugfix in order to allow import networks with non-asccii characters * enhance (database) documentation and test cases 2015-02-16 * add test suite for XMLRPC-API * add root methods (add, edit, delete) to XMLRPC-API 2015-02-15 * add simple REST wrapper * add config option showNetBorders * add config option removeCIDRFromIPs * minor Bugfixes 2015-02-13 * Add multiple authentication methods for login at once 2015-02-07 * add some flushes to the session object * add timeout to 'showStatus'-Ajax-call, in order to circumvent a race condition (expanded net don't stay expanded) * when you define a minimal network cidr, you can expand that net even if it has no childs 2015-01-03 * Add tag support for networks 2014-12-31 * remove potential XSS attack vectors 2014-10-27 * fix import status issue * limit height of warning window 2014-10-18 * add export subnets for supernets and roots * add wrapper for accessing CGI params 2014-09-14 * added simple 'exportSubnet' method added new config variable 'gui/showNrOfFreeSubnetsWithMinCIDR' 2014-09-09 * Bugfix (calculation of free subnets) * add 'editNet' function to XMLRPC-API * add functionality to search for networks (with wildcards) 2014-09-04 * add new XMLRPC-API method: editNet 2014-08-28 * add DB-Docu * add mysql support for networkLock * some Bugfixes * add default network lock configuration item 2014-08-27 * add locking for network deletion * add new API method: XMLRPC * improve rights delegation while editing networks * add new button 'Search and assign free networks' * add possibility to configure multiple server to ldap authentication * inherit ACS from super net / root * sort user and group field * improve robustness against concurrent requests 2012-09-26 * added search for free network in GUI * several Bugfixes 2012-07-23 * added audit functionality 2012-06-20 * added possibility to show amount of free subnets in search view * added possibility to change a template name * added yum support for checkPerlDependencies * enhance ldap authentication support (authenticate a DN after searching for it in LDAP) * Expanded entries field in templateEntry to 1024 chars 2012-01-31 * enhanced generic database support * added postgresql support * some speedup enhancements 2012-01-17 * start enhancing database abstraction in order to implement other database accessors 2012-01-13 * several Bugfixes 2011-07-11 * added recurrent mode for plugin 'GenZonefile' in order to create zonefiles automatically 2011-07-09 * added parameter for onDemand execution of plugins * added genZonefile plugin 2011-07-07 * added ldap authentication module 2011-06-12 * force string type in SOAP-API (Patch-ID:3314242) 2011-02-15 * Bugfix 3168045, 3111556 2010-09-24 * add support for 32 bit as numbers 2010-06-23 * Bugfixes (editRoot, copyNets, moveNets) * Enhancements (showStatus, importASN, importCSV) 2010-06-22 * cleanup database tool 2010-03-19 * Bugfixes (checkNetworkState, css-id Dups) 2010-02-26 * remove circular dependencies 2010-02-24 * several Bugfixes (ula/bit mask issues) * test-page for dependency checking 2010-02-15 * CSS - bugfixes 2010-02-13 * Rewrite of database upgrade code (in order to make MySQL::Diff obsolete) 2010-02-11 * Unicode handling (Support for multibyte languages) 2010-01-12 * add italien localization (thanks to Pf) * improve csv-importer * bugfix - deleting networks/roots 2010-01-10 * Validation of a consistent network state while creating a new network (i.e. only one assignment per allocation) * update available network states 2009-11-30 * Foundry Config importer * enhance Cisco Config importer 2009-10-31 * Juniper Config importer 2009-05-5 * Improved checking if DB has changed 2008-11-12 * HaCiAPI - SOAP Interface for HaCi 2008-10-22 * Search for Template Values 2008-08-07 * Performance Tuning (Improved Cache Management) 2008-07-19 * DNS Import improved (error messages, ptr and aaaa records) 2008-06-27 * Configuration per User (-Interface) * Ability to change own Password * Filter Search with Network States 2008-06-25 * Show Tree Structure * Fuzzy Search 2008-06-20 * option to define default Subnet Size (in CIDR notation) 2008-06-14 * show (free) Subnets (with fixed CIDR) 2008-04-23 * New Configfile Format 2008-04-10 * New Configfile Mechanism 2008-03-30 * HaCid Infos 2008-03-18 * Dependencies Checking Script 2008-03-07 * floating Windows 2008-03-05 * 6 Plugins available 2008-02-28 * add dynamic Descriptions with Plugin Variables 2008-01-08 * HaCi Daemon 2007-12-20 * add Plugin Infrastructure 2007-10-31 * advanced Status Messages (Ajax) 2007-10-20 * Buttons with Images * Layout optimations 2007-10-15 * add third column to treeview for direct access to network functions * rewrite of HaCiBox Templates 2007-10-08 * add IPv6 Support * recurrent Plugin execution * one more color Scheme * automatic modification of Database Schemas 2007-09-03 * add Combine functionality 2007-09-01 * Bugfix (deleting networks) * Possibility to split networks 2007-08-31 * add possibility to modify also netaddress and netmask of networks 2007-08-29 * expand states to Free, Reserved, Locked * Add State Colors/Images 2007-08-27 * add AJAX Support 2007-03-29 * add 'Flush Cache' Button * fix ACL Handling 2006-11-18 * import CSV Configs with "net-Type" support * ACL Caching Machanism (No 'Flush ACL' needet any more) * Some Bugfixes & GUI update 2006-11-15 * First Release