HaCi/0000755000175000017500000000000011410502244011515 5ustar fighterfighterHaCi/LICENSE0000644000175000017500000000017210527616642012542 0ustar fighterfighterLICENSE * This program is licensed under the terms of the GNU General Public License. (See the file COPYING.) HaCi/docs/0000755000175000017500000000000011410502172012445 5ustar fighterfighterHaCi/docs/manualDatabaseUpgrades.txt0000644000175000017500000000330311341103627017607 0ustar fighterfighter# 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.9.7 there's a config option 'autoUpgradeDatabase', which gives you the opportunity to enable resp. disable the automatic upgrade function. 0.7.5 -> 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.9.1 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.9.1 -> 0.9.5 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.9.5 -> 0.9.6 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.9.6 -> 0.9.7 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'; HaCi/docs/HaCiAPI.txt0000644000175000017500000000747311322231572014364 0ustar fighterfighter__HaCiAPI__ 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 _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 _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/HaCiSoapClient.pl0000755000175000017500000000230211273202213015570 0ustar fighterfighter#!/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://demo.haci.larsux.de/cgi-bin/HaCiAPI.cgi?getWSDL'; my $user = 'admin'; my $pass = 'admin'; 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/Template/0000755000175000017500000000000011410502172013270 5ustar fighterfighterHaCi/Template/HaCiSplitNet.tmpl0000644000175000017500000000262110767624523016501 0ustar fighterfighter

[% 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/HaCiImportASNRoutes.tmpl0000644000175000017500000000154410706272523017750 0ustar fighterfighter
[% importASNRoutesHeader %]
[% INCLUDE HaCiBox.tmpl menu = importASNRoutesMenu parentFormName = importASNRoutesFormName hiddens = importASNRoutesHiddens %]

[% INCLUDE HaCiBox.tmpl menu = importASNRoutesButtons parentFormName = importASNRoutesFormName %]
HaCi/Template/HaCiAddNet.tmpl0000644000175000017500000000660111320500404016051 0ustar fighterfighter

[% 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/HaCiShowPlugins.tmpl0000644000175000017500000000255211410502172017201 0ustar fighterfighter

[% 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/HaCiCompare.tmpl0000644000175000017500000000135510706272523016320 0ustar fighterfighter

[% compareHeader %]

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

[% INCLUDE HaCiBox.tmpl menu = compareButtons parentFormName = compareFormName hiddens = compareHiddens %]
HaCi/Template/HaCiLogin.tmpl0000644000175000017500000000237511410500775016001 0ustar fighterfighter

[% loginHeader %]

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

[% warnlHeader %]

[% INCLUDE HaCiBox.tmpl menu = warnlMenu %]
HaCi/Template/HaCiShowPluginConf.tmpl0000644000175000017500000000053110732507146017633 0ustar fighterfighter

[% pluginConfHeader %]

[% INCLUDE HaCiBox.tmpl menu = pluginConfMenu hiddens = pluginConfHiddens formName = pluginConfFormName %]
HaCi/Template/HaCiSearch.tmpl0000644000175000017500000000322111077653716016142 0ustar fighterfighter

[% searchHeader %]

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

[% IF searchResult %]

[% searchResultHeader %]

[% cnter = 0 %] [% FOREACH descrEntry IN tmplDescr %] [% IF noSearchResult %] [% ELSE %] [% FOREACH entry IN searchResult %] [% cnter = cnter + 1 %] [% IF cnter mod 2 %] [% bgColor = '#AAFFAA' %] [% ELSE %] [% bgColor = '#DDFFDD' %] [% END %] [% netID = entry.netID %] [% FOREACH descrEntry IN tmplDescr %] [% descrValue = descrEntry.value %] [% END %] [% END %] [% END %]
[% gettext_rootName %] [% gettext_network %] [% gettext_description %] [% gettext_state %][% descrEntry.value %] [% END %]
[% gettext_nothing_found %]
[% entry.rootName %] [% entry.network %] [% entry.description %] [% entry.state %][% tmplInfos.$netID.$descrValue %]
[% END %]
HaCi/Template/HaCiMain.tmpl0000644000175000017500000000514211410500775015610 0ustar fighterfighter
[% 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 == '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 %] [% END %]

[% warnlHeader %]

[% INCLUDE HaCiBox.tmpl menu = warnlMenu %]
HaCi/Template/HaCiShowTemplates.tmpl0000644000175000017500000000132010706272524017522 0ustar fighterfighter

[% newTemplateHeader %]

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

[% netTypeTemplatesHeader %]

[% INCLUDE HaCiBox.tmpl menu = netTypeTemplatesMenu formName = netTypeTemplatesFormName hiddens = netTypeTemplatesHiddens %]
HaCi/Template/HaCiShowViewSettings.tmpl0000644000175000017500000000057211031215110020203 0ustar fighterfighter

[% viewSettingsHeader %]

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

HaCi/Template/HaCiNetwork.tmpl0000644000175000017500000000307210777271203016363 0ustar fighterfighter[% 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/HaCiChanceOwnPW.tmpl0000644000175000017500000000054611031215106017031 0ustar fighterfighter

[% chOwnPWHeader %]

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

HaCi/Template/HaCiCombineNets.tmpl0000644000175000017500000000141510706272523017135 0ustar fighterfighter

[% combineNetsHeader %]

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

[% combineNetsButtonsHeader %]

[% INCLUDE HaCiBox.tmpl menu = combineNetsButtonsMenu parentFormName = combineNetsFormName %]
HaCi/Template/HaCiTreeRoot.tmpl0000644000175000017500000000151510777271203016475 0ustar fighterfighter
[% 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/Template/HaCiImportDNS.tmpl0000644000175000017500000000376210712127545016555 0ustar fighterfighter

[% 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/HaCiShowPlugin.tmpl0000644000175000017500000000026510704777333017037 0ustar fighterfighter

[% plugin.HEADER %]

[% INCLUDE HaCiBox.tmpl menu = plugin.BODY %]
HaCi/Template/HaCiDelNet.tmpl0000644000175000017500000000041410704777333016107 0ustar fighterfighter

[% delNetHeader %]

[% INCLUDE HaCiBox.tmpl menu = delNetMenu formName = delNetFormName hiddens = delNetHiddens %]
HaCi/Template/HaCiDelUser.tmpl0000644000175000017500000000042010704777333016274 0ustar fighterfighter

[% delUserHeader %]

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

[% 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/HaCiShowUsers.tmpl0000644000175000017500000000124410706272524016672 0ustar fighterfighter

[% newUserHeader %]

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

[% showUsersHeader %]

[% INCLUDE HaCiBox.tmpl menu = showUsersMenu formName = showUsersFormName hiddens = showUsersHiddens %]
HaCi/Template/HaCiTree.tmpl0000644000175000017500000000341310706272524015627 0ustar fighterfighter

[% 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/HaCiEditUser.tmpl0000644000175000017500000000223610706272523016455 0ustar fighterfighter

[% 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' %]
[% gettext_userpassword %]
HaCi/Template/HaCiDelGroup.tmpl0000644000175000017500000000042410704777333016456 0ustar fighterfighter

[% delGroupHeader %]

[% INCLUDE HaCiBox.tmpl menu = delGroupMenu formName = delGroupFormName hiddens = delGroupHiddens %]
HaCi/Template/HaCiEditTemplate.tmpl0000644000175000017500000000310510706272523017306 0ustar fighterfighter

[% templateHeader %]

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

[% editTemplateHeader %]

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

[% editTemplateEntryHeader %]

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



[% templatePreviewHeader %]

[% INCLUDE HaCiBox.tmpl menu = templatePreviewMenu hiddens = templatePreviewHiddens parentFormName = editTemplateFormName %]
HaCi/Template/HaCiBox.tmpl0000644000175000017500000002066011077653716015473 0ustar fighterfighter[% IF formName %]
[% formCreated = 1 %] [% ELSE %] [% formName = parentFormName %] [% END %] [% trOpen = 0 %] [% FOREACH entry IN menu %] [% UNLESS trOpen %] [% trOpen = 1 %] [% END %] [% IF entry.value.type == 'hline' %] [% 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 %]
[% 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/HaCiShowSettings.tmpl0000644000175000017500000000107311031215110017345 0ustar fighterfighter

[% 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/HaCiImportConfig.tmpl0000644000175000017500000000653311410500775017331 0ustar fighterfighter
[% 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/HaCiShowGroups.tmpl0000644000175000017500000000125410706272524017051 0ustar fighterfighter

[% newGroupHeader %]

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

[% showGroupsHeader %]

[% INCLUDE HaCiBox.tmpl menu = showGroupsMenu formName = showGroupsFormName hiddens = showGroupsHiddens %]
HaCi/Template/HaCiFooter.tmpl0000644000175000017500000000057111350543632016165 0ustar fighterfighter

Status: loading  

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

HaCi/Template/HaCiMenu.tmpl0000644000175000017500000000115210677351441015635 0ustar fighterfighter
[% FOREACH submenu IN menu %] [% END %]
HaCi/Template/HaCiShowStatus.tmpl0000644000175000017500000000047310555523334017057 0ustar fighterfighter
[% statTitle %]
0%
100%
[% statDetail %]
HaCi/Template/HaCiAddRoot.tmpl0000644000175000017500000000202610706272523016262 0ustar fighterfighter

[% 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/HaCiDelTemplate.tmpl0000644000175000017500000000030410526326505017123 0ustar fighterfighter

[% delTmplHeader %]

[% INCLUDE HaCiBox.tmpl menu = delTmplMenu formName = delTmplFormName hiddens = delTmplHiddens %]
HaCi/Template/HaCiShowSubnets.tmpl0000644000175000017500000000273011031215110017171 0ustar fighterfighter

[% showSubnetsHeader %]

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

[% IF freeSubnets %]

[% freeSubnetsHeader %]

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

Version: [% version %]


  HaCi - IP Address Administration                             
    Copyright (C) 2006-2010 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/HaCi.tmpl0000644000175000017500000000214411336301620014775 0ustar fighterfighter[% 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/HaCiDelRoot.tmpl0000644000175000017500000000042010704777333016301 0ustar fighterfighter

[% delRootHeader %]

[% INCLUDE HaCiBox.tmpl menu = delRootMenu formName = delRootFormName hiddens = delRootHiddens %]
HaCi/Template/HaCiShowNet.tmpl0000644000175000017500000000260510761422074016317 0ustar fighterfighter

[% 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/HaCiShowRoot.tmpl0000644000175000017500000000113410704777333016520 0ustar fighterfighter

[% rootInfoHeader %]

[% INCLUDE HaCiBox.tmpl menu = rootInfo %]

[% rootFunctionsHeader %]

[% INCLUDE HaCiBox.tmpl menu = rootFunctions formName = rootFunctionFormName hiddens = rootFunctionHiddens %]
HaCi/Template/HaCiTreeNetwork.tmpl0000644000175000017500000000625611030471134017176 0ustar fighterfighter[% 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 %] [% network.editAlt %] [% network.delAlt %] [% END %] [% END %]
[% IF network.parent %][% UNLESS network.expanded %]
[% END %][% END %] [% IF network.parentBroadcast %] [% i = [ 1 .. network.parentBroadcast ] %] [% FOREACH i %] [% END %] [% END %] [% END %] HaCi/COPYING0000644000175000017500000004311010527616642012567 0ustar fighterfighter 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/cgi-bin/0000755000175000017500000000000011410502172013025 5ustar fighterfighterHaCi/cgi-bin/HaCi.cgi0000755000175000017500000000520011322231572014322 0ustar fighterfighter#!/usr/bin/perl -w ############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2010 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/test.cgi0000755000175000017500000000432411345231162014503 0ustar fighterfighter#!/usr/bin/perl use strict; use warnings; use CGI; 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'; my $q = new CGI(@_); my $title = 'HaCi - IP Address Administration'; my $ver = '0.9.7a'; 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/; 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/; 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}); 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; # vim:ts=4:sw=4 HaCi/cgi-bin/HaCiAPI.cgi0000644000175000017500000000622011322231572014654 0ustar fighterfighter#!/usr/bin/perl ############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2010 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/README0000644000175000017500000001616011410134302012375 0ustar fighterfighter############################################################################### ## HaCi - IP Address Administration # ## Copyright (C) 2006-2010 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) 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 - DBD::mysql - Digest::MD5 - Digest::SHA - DNS::ZoneParse (optional for importing Zonefiles) - Encode - Encode::Guess - File::Temp - HTML::Entities - IO::Socket::INET6 (optional for IMAP authentication support) - Locale::gettext - Log::LogLite - Math::BigInt::GMP (optional, but proposed for more performance!) - Math::BigInt v1.87 - Net::CIDR - Net::DNS (optional for importing Zonefiles) - Net::IPv6Addr - Net::Ping (optional for Ping-Plugin) - Net::SNMP - 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) - Storable - Template - Text::CSV_XS (optional for importing CSV-Files) - Time::Local - Apache::DBI (optional for getting connection persistence when using mod-perl) - 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. 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< --------------------- - 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 ---------------------- 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 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. 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. AUTHOR Lars Wildemann haci@larsux.de # vim:ts=8 HaCi/bin/0000755000175000017500000000000011410502172012265 5ustar fighterfighterHaCi/bin/checkPerlOptionals.bash0000755000175000017500000000544611345231162016736 0ustar fighterfighter#!/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 SQL::Translator::Diff Text::CSV_XS Apache::DBI" aptget=`which apt-get 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 optional Perl dependencies are available..." for modt in $modules do if echo $modt | grep -q '#'; then mod=`echo $modt|cut -d '#' -f 1`; ver=`echo $modt|cut -d '#' -f 2`; else mod=$modt ver='' fi echo -n "$mod" if echo $modt | grep -q '#'; then echo -n " v$ver"; fi echo -n ': ' if perl -e "use $mod $ver" 2>/dev/null; then echo "OK"; else echo "NO"; failed[$index]=$mod ((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 echo "${failed[$modIndex]}" 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 [ "$bDebian" -eq 0 ] && [ "$bCpan" -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 echo "${failed[$modIndex]}" if [ "$bCpan" -eq 1 ]; then $cpan ${failed[$modIndex]} elif [ "$bDebian" -eq 1 ]; then debPack=${failed[$modIndex]} $aptget -y install lib${debPack//::/-}-perl elif [ "$bYast2" -eq 1 ]; then yastPack=${failed[$modIndex]} $yast2 -i perl-${yastPack//::/-} 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 HaCi/bin/cleanupDatabase.pl0000755000175000017500000004253211410500775015716 0ustar fighterfighter#!/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 Getopt::Std; use Data::Dumper qw/Dumper/; use HaCi::Conf; 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 $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"; if (system("which mysqldump >/dev/null") == 0) { my $md = qx/which mysqldump/; 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"; print "Found 'mysqldump':\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"; 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 @networksNR = $networkTable->search(['ID', 'network', 'description', 'rootID', 'ipv6ID'], {}, 0, "WHERE rootID 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 @networkV6sNR = $networkV6Table->search(['ID', 'rootID'], {}, 0, "WHERE rootID 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 networkAC my @networkACsNR = $networkACTable->search(['ID', 'rootID'], {}, 0, "WHERE rootID NOT IN ($roots)"); foreach (@networkACsNR) { my $ac = $_; print "\r" . $busy[$bc++%5]; print "\r $ac->{ID} : $ac->{rootID}: Stale network ACL entry (root doesn't exists)\n" if $debug; &delEntry($networkACTable, $ac->{ID}); } print "\r " . ($#networkACsNR + 1) . " stale network ACL entrys found without matching root\n"; # Get stale rootAC my @rootACsNR = $rootACTable->search(['ID', 'rootID'], {}, 0, "WHERE rootID 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.bash0000755000175000017500000000555311341116466017360 0ustar fighterfighter#!/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 DBD::mysql 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 Time::Local" aptget=`which apt-get 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 modt in $modules do if echo $modt | grep -q '#'; then mod=`echo $modt|cut -d '#' -f 1`; ver=`echo $modt|cut -d '#' -f 2`; else mod=$modt ver='' fi echo -n "$mod" if echo $modt | grep -q '#'; then echo -n " v$ver"; fi echo -n ': ' if perl -e "use $mod $ver" 2>/dev/null; then echo "OK"; else echo "NO"; failed[$index]=$mod ((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 echo "${failed[$modIndex]}" 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 [ "$bDebian" -eq 0 ] && [ "$bCpan" -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 echo "${failed[$modIndex]}" if [ "$bCpan" -eq 1 ]; then $cpan ${failed[$modIndex]} elif [ "$bDebian" -eq 1 ]; then debPack=${failed[$modIndex]} $aptget -y install lib${debPack//::/-}-perl elif [ "$bYast2" -eq 1 ]; then yastPack=${failed[$modIndex]} $yast2 -i perl-${yastPack//::/-} 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 HaCi/bin/HaCid.pl0000755000175000017500000002743611410134302013605 0ustar fighterfighter#!/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); warn "WORKDIR: $workDir\n"; &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 { &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: $!", 2); 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/etc/0000755000175000017500000000000011410502172012270 5ustar fighterfighterHaCi/etc/internal.conf0000644000175000017500000001506711410500775014773 0ustar fighterfighter# This config is for internal use only. # Only touch if you know what you are doing! workdir = $workdir templateIncludePath = $workdir/Template templateCompilePath = $workdir/spool spoolPath = $workdir/spool authModules = $workdir/modules/HaCi/Authentication plugins = $workdir/modules/HaCi/Plugins localePath = $workdir/locale statusFile = $workdir/spool/status cacheFile = $workdir/spool/cache tableHashFile = $workdir/spool/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 titleShort = HaCi titleLong = HaCi - IP Address Administration version = 0.97c 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 ripeDB = 193.0.6.135 sessionTimeout = +1h maxMemConsumption = 100000 disableCache = 0 searchLimit = 1000 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 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/etc/HaCi.conf.sample0000644000175000017500000000275711345231162015243 0ustar fighterfighter # database parameters dbHost = localhost dbName = HaCi dbUser = HaCi dbPass = geheim! # GUI parameters root = FF0000 new = 777777 reserved = FF00F0 locked = AAAAAA 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 # 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 # authentication parameters ## authentication modules # internal: internal auth # imap : IMAP auth authModule = internal # authenticationmodule parameters # host = imap.server.tld # authtype = login # vim:ts=8 HaCi/etc/startup.pl0000644000175000017500000000060411345231162014334 0ustar fighterfighteruse strict; use FindBin; $ENV{MOD_PERL} or die "not running under mod_perl!"; 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.sample0000644000175000017500000000041311003701764015613 0ustar fighterfighteruse 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/logs/0000775000175000000410000000000011410502173012557 5ustar fighterwww-dataHaCi/logs/.log_directory0000644000175000017500000000000010526336525015333 0ustar fighterfighterHaCi/locale/0000755000175000017500000000000011410502173012755 5ustar fighterfighterHaCi/locale/de/0000755000175000017500000000000011410502173013345 5ustar fighterfighterHaCi/locale/de/LC_MESSAGES/0000755000175000017500000000000011410502173015132 5ustar fighterfighterHaCi/locale/de/LC_MESSAGES/HaCi.po0000644000175000017500000005204211341116466016312 0ustar fighterfighterdomain "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 "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 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 "Fuzzy Search" msgstr "Unscharfe Suche" 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" #: 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 "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 "Functions" msgstr "Funktionen" msgid "Reverse DNS lookup" msgstr "Rückwärtsauflösung" msgid "Type" msgstr "Typ" msgid "Type Infos of Network %s" msgstr "Typ Informationen des Netzes %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" #: 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 "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" HaCi/locale/de/LC_MESSAGES/HaCi.mo0000644000175000017500000005337211341116466016316 0ustar fighterfighterÞ•.Œ ™ü` xQ™2ëT.9ƒU½YYm Ç Ñ Ûæ ì ÷ +4:Icz&‘.¸&çS$b‡Œ‘ ¤°ÀÛûa5M—åì '/@W_f …¥­² ¹ ÆÑè%ø # 4 < P n Œ “ ¥ ¹ Å Ø õ 1!5A!0w!4¨!0Ý!" " "*" A"K"#_"ƒ" ‰"—"'³"Û":ù"*4#_#e#y# Š# –#1¢# Ô# á# ë#ø#$$-$?$P$W$ ]$ h$u$|$ Ž$œ$°$È$â$ø$ÿ$%%%1$%V%\% c%%n% ”%  %ª% ¯% ½% É%Õ%Ú% Þ% ë%ö%&& &(&,& 1&;& D& Q&^&g&x& ‡&&¨&Ï&é&'ü&$$'I' i'(w' '¤'©'5¾'+ô' (.(C( V(c(€(’(¢(¨( ±(¼(Å(Ù(3ð( $)0).A)p) ‚))–)#ž) Â)Ì) Ý)é)ý)**'*6*I*O* T*^*y*ˆ**•*§*®* ½*Ç*Ú*ã*++#+ 8+F+ X+ c+p+ „+(’+»+À+&Û+),),, V,"w,"š,½,Ä, Ê, Ø,ù, --- -/>-9n-¨-"È-ë- .+.3. :.$F.k.. .§.¬. µ.¿.ß.Nû.XJ/"£/Æ/ä/ù/0[)0[…0’á0Ít1nB2K±3ý3&4*D4'o4—4Ÿ4¤4©4É4Ú4ê4ó4ù4 þ4 55&575P5f5m5 s5~5†5 Œ5–5 Ÿ5©5¬5 ±5»5¿5Â5È5ß5eÿ5@e6¦6\¶6I7r]7uÐ7oF8 ¶8 Â8 Ì8 ×8 á8ë8 ú899/9E9N9`9}9™9/¸95è9A:p`: Ñ:ò:ù:þ: ; ;!@;,b;;=­;|ë;ch<Ì<Ó<ë<===*=?= Z=f=m= =›=´=¼= Å= Ï= Ü=è=ü=+ >8><>N>W>#m>$‘>¶>¾>Õ> é>ö>?".?1Q?1ƒ?5µ?2ë?5@ T@ _@j@z@”@ª@"Ê@í@ô@A% AFAAcAR¥AøAþAB0B CB6PB‡B –B¡B±BÐB×BêBÿBCC C +C 7CCCZCmC†C¥CÃCáC êCõCýCD3DHDQD ZDhD ƒDŽDD ¢D ¬D ¹D ÅDÑDÕDëDüD E E)E2E6E H JH TH^HgH}HA›HÝHäH2õH(I 9IDIMI,VI ƒIIžI®IÅI ÞIëIôI JJ %J 0J;JZJiJpJyJ’J™J ­J·J ÓJáJ!K#K*K@KOKkKK‘K§K+¸KäKêK'L$/L TLuL"L°LËL ÒLÜL"ðLM$M -M7M?M-]M6‹M ÂM!ãMN0$N UNaN fN#pN”N§NÆNÏN ÔNàN$éNOM,OhzO!ãOP"P7PPPckPbÏP2QäÐQºµRQpT/ÂT&òT-U)GUqU„U‰U&U´UÉU ÜUéUòUúU VV/V$CVhVV…V ŽVšV £V ­V¸V ÇVÑVÖV ÜVçVëV ðVX:ÃÇ©¶zSý–®%$ä&÷Å‚¯réðw ‘.ç7ÛØ-8Ö4jªøá£,u?ošÙÜò_)•µŠÊº¤ ­ÿ“±¬ /)ï*ô¢sT{n'c,iaŽûk~ÎpÔ¼+=õdŒ9À´€æ§D×|îgë5 ¹»#Mfü"F[¸Ì›vŸ`(ˆ¨ú>3e¿l!ß<¾+C6 !½b«W’¦*É žÑqR$¥˜xPKœ”‹ þNËY E&1Ú-ÐíÕ—#Ó¡LI㉠‡ Zåtݳâìö]U(°%…Í™'².È^}Òà èyh·óJÏ "0ÄAmê;2„O†Á\HÞVÆ@BùƒñGQ# 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!Automatic upgrade of database disabled. Please upgrade the database schema by hand.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': %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 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 Network %sEdit RootEdit Root %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'Successfully updated Table '%s'.SupportTargetTarget RootTemplate '%s' for '%s'Template ManagementTemplate for the DescriptionsTemplatesTextTextareaTextfieldThe 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 Demandorreadrecurrentsectowrite# verfügbarer Adressen# freier Subnetze mit CIDR '%s'%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%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 istARP EintragAS NummerASN RoutenAbbrechenÜber HaCiZugriffsrechteHinzufügenNetz hinzufügenNetz %s hinzufügenStartpunkt hinzufügenLebendigBenutzeranmeldungAuthentifizierungsverwaltungAuthentifizierungs 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 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 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 bearbeitenNetz %s bearbeitenStartpunkt bearbeitenStartpunkt %s bearbeitenEinträge (getrennt mit Semikolons)FehlerFehler DetailsFehler Details für Plugin %sFehler beim Löschen '%s' von '%s': %sFehler beim Löschen '%s': %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'Bearbeiten 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'EinfügenSpringe zuSpracheLinksLokales ZonefileAngemeldet als %sAnmeldenAbmeldenMACs gefundenMac Adresse der IP AdresseVerwaltungMaximale TiefeMenüSonstigesGeändert vonGeändert amVerschiebenNEUName des StartpunktesNetzwerk AdresseNetzadresse passt nicht!Netz MaskeNetz TypNetzwerkNeuNeue 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 freien Subnetze mit dieser CIDR verfügbarNr.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 SubnetzeAltes PasswortAltes Passwort ist nicht richtig!Nur IP AdressenNur Neue hinzufügenReihenfolgeÜbersichtParameterPasswortPasswort 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 resettenRückwärtsauflösungRechtsStartpunktStartpunktRouter für die SNMP VerbindungRouting PrefixReihenLaufzeitErgebnis speichern unterSuchenSuchen, VergleichenSeparatorIhre Sitzung ist abgelaufenEinstellungenEinstellungen für das neue NetzEinstellungen für die neuen NetzeZeigenNetz Details anzeigenNetze anzeigenStartpunkt Details anzeigenStartpunke anzeigenSubnetze anzeigenBaumstruktur anzeigenZeige alle NetzeZeige freie Subnetze von '%s' mit CIDR '%s'GrößeBenutzername nicht gefunden!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!QuelleAufteilenAufteilungs 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 Gruppe '%s' gelöschtErfolgreich Vorlage '%s' gelöschtUser '%s' erfolgreich gelöschtDie Tabelle '%s' wurde erfolgreich aktualisiert.UnterstütztZielZielpunktVorlage '%s' für '%s'VorlagenverwaltungVorlage für die BeschreibungenVorlagenTextTextbereichTextfeldDer 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überschreitungBaumTypTyp Informationen des Netzes %sBenutzer '%s'BenutzerverwaltungBenutzernameBenutzerAnsichtAnsichtseinstellungenPlatzhalterInklusive SubnetzeMit allen SubnetzenZonefile transfer fehlgeschlagen: %s[ Zugriff verweigert ]aktivlebendigkomprimiertstandardforcierenunsichtbarletzter Aufrufauf AbrufoderlesenregelmäßigseknachschreibenHaCi/locale/it/0000755000175000017500000000000011410502173013371 5ustar fighterfighterHaCi/locale/it/LC_MESSAGES/0000755000175000017500000000000011410502173015156 5ustar fighterfighterHaCi/locale/it/LC_MESSAGES/HaCi.po0000644000175000017500000005026311323136206016333 0ustar fighterfighterdomain "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/locale/it/LC_MESSAGES/HaCi.mo0000644000175000017500000005161411323136206016331 0ustar fighterfighterÞ•+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”</Å<õ<þ<=%=@=S="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Õ5\” t'¹(GkbhÞ>Я‹¬µ%L¶ÍQ7Ã,Â_×òó§ NJ¤ý“6*xÀFÚ fg¼ .)o4¡Æ£!<z²–2 +š3ÁIävmþ&SO(ÔËês¥#l+@æùâŸ]/ö!DÜ®íR˜Zãw|H"-X÷­¸ ûÒY?´»õV;Aé¿ ¦*`10jqß[=È$‘ü„ø©ÙeÉ}îEÖŠyÅa^dÛnWÝi›MàT«‚¾•á‰ÎU èÿŒ—'ô ¢ˆ#†  ØŽñ8~¨c {rpʳ 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 Network %sEdit RootEdit Root %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 ManagementTemplate for the DescriptionsTemplatesTextTextareaTextfieldThe 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 Rete %sModifica la RadiceModifica la Radice %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'Gestione TemplateMaschera per le DescrizioniMaschereTestoArea 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/html/0000755000175000017500000000000011410502172012461 5ustar fighterfighterHaCi/html/HaCi_black.css0000644000175000017500000001670411410500775015152 0ustar fighterfighterhtml { 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:#FFFF00; 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; } #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{ } HaCi/html/Images/0000755000175000017500000000000011410502173013667 5ustar fighterfighterHaCi/html/Images/close_small.png0000644000175000017500000000157210704777334016721 0ustar fighterfighter‰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/password_small.png0000644000175000017500000000110011031215111017407 0ustar fighterfighter‰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/HaCi_Logo_String.png0000644000175000017500000000377110677351441017535 0ustar fighterfighter‰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/showSettingsView_small.png0000644000175000017500000000131611031215111021112 0ustar fighterfighter‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÕ(WrÔW[IDAT8Ë’ßKSq‡Ÿï9›;›;›GËY–™R–…]”¶”ˆ ‚¬.»5¡ºË ¼+Ȩ»ÊnºQp‘$˜Pj¿ì(êDiꦙζ³íìtm è ïåçyÞ÷¥Ö_¬Ít­¿& +N³™’$ €eY躾¡pYYƒx<¾a—Ë•5hní _ŸÇéHñÓ°ý7X]á£íÖù¬išT4Þ§­E¡¼d'÷žŽ1¤»3cu™µ‰Nf§G‰.GÐ4 Õ£’•d–“‡[%/usÌF\X– ô9ÀÀëvÚŸ?£²²Šd*MMX ù˜ÄçûÊ ÞÜLͦ٪¹}×ÉÂä[ô‘z>Ìð û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/ipv6.png0000644000175000017500000001653310675407754015316 0ustar fighterfighter‰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/HaCi_Logo2.png0000644000175000017500000002236010677351441016264 0ustar fighterfighter‰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ˆ~øôù+ËÏ? Œ@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/compare_small.png0000644000175000017500000000135610704777334017242 0ustar fighterfighter‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœRIDATxÚ¥SË‹’Q¿ŽñÁøJeŸ#¢ˆ Jà"áÂÀMP‹ QZº™D¤ÁU!7¶‚FÌ$q!D!´è/p1 (Ä2ZÒHÄéœoü&•vsàòÝ{ÎùýÎócÀ®sص l6#‰F£wK¥Ò ¼êív;[FÃD"ѽíííWøTnccƒ1³ÙÌ9 ƒ d„ÑhZ­6²N€À#—ËN§x(.  …¼Óé°T*U……´Ûí ¸¹¹I‘ÃÜn7˜L&Ì›ùɆ$RV«Õ>Öëõ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/plus.gif0000644000175000017500000000155610474157163015366 0ustar fighterfighterGIF89a ÷€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿ3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿ!ù, Ka¥2d(U¡B†`úǰá?U©¢`˜à¿mÿPäóW ¡? AúS¥Êß¶|(¶i£æ‘5+§¤6mš3›Ó )TeÐÀTª;HaCi/html/Images/reserved_small.png0000644000175000017500000000025210665316126017417 0ustar fighterfighter‰PNG  IHDRíÝâRPLTEÿÿÿÿÿÿÀÿ@€/ˆPtRNS@æØfFIDATxœc`À @„1³ˆ0f0R‰°*)9€)©@T2+@FP³ «2„áÀ‘jT1L€Xn<€þ"šC¤²IEND®B`‚HaCi/html/Images/insert_small.png0000644000175000017500000000711310761422075017105 0ustar fighterfighter‰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/config_small.png0000644000175000017500000000167510732507147017057 0ustar fighterfighter‰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/copy_small.png0000644000175000017500000000166310704777334016567 0ustar fighterfighter‰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/key_small.png0000644000175000017500000000107210665316126016371 0ustar fighterfighter‰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/HaCi_Logo.png0000644000175000017500000010126510515547760016206 0ustar fighterfighter‰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/logout.png0000644000175000017500000000662010670113276015723 0ustar fighterfighter‰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/jumpTo_small.png0000644000175000017500000000113310704777334017063 0ustar fighterfighter‰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/minus.gif0000644000175000017500000000151510474157163015531 0ustar fighterfighterGIF89a ÷€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, 2°HÀþ¨¡½ö"J|È΀‹@œhOcE hp¤I$  ;HaCi/html/Images/free_small.png0000644000175000017500000000156310665316126016527 0ustar fighterfighter‰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/showSubnets_small.png0000644000175000017500000000103011024623161020105 0ustar fighterfighter‰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/submit_small.png0000644000175000017500000000156310704777334017117 0ustar fighterfighter‰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/noLine.png0000644000175000017500000000017011030471134015617 0ustar fighterfighter‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`£€ªSZ¶€!IEND®B`‚HaCi/html/Images/showAll.png0000644000175000017500000000700610670113276016022 0ustar fighterfighter‰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/add-user_small.png0000644000175000017500000000240510704777333017313 0ustar fighterfighter‰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»Ò_êŸêþÒ"û¾¸ 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/vertLine.gif0000644000175000017500000000147511030471134016155 0ustar fighterfighterGIF87a çÿ€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, "í ´` À Úûw0áBƒtÈ¢D‹^4x1 ;HaCi/html/Images/horizLine.gif0000644000175000017500000000146611030471134016330 0ustar fighterfighterGIF87a çÿ€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, ÿ H° A‚öÚ[È0!€‡!œH±¢À€;HaCi/html/Images/config_small_disabled.png0000644000175000017500000000106210732507147020674 0ustar fighterfighter‰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/combine_small.png0000644000175000017500000000051710704777334017226 0ustar fighterfighter‰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/addNet.png0000644000175000017500000001230010670113276015601 0ustar fighterfighter‰PNG  IHDR00Wù‡bKGDÿÿÿ ½§“ pHYs  šœtIME× /q…´›MIDAThÞÕzi×uÞwνÝýÖÙlC€ ‚›†”D™¡$ÛR,YaY²%®Xqʉª8v¤Ê/•ãÊö#qœØV\Š#É•8.ÙqIŽJ¡Y²H’(@û6ÀìË›·¿~ýz¹÷žüè÷ MZŒ+U)ß©žž©~uëûÎ=ç;K?üU^Œ¿âK½áüb©41³gÎ+§>ö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/tLine.gif0000644000175000017500000000147711030471134015442 0ustar fighterfighterGIF87a çÿ€€€€€€€€€ÀÀÀÀÜÀ¦Êð@ ` €   À à @ @@@`@€@ @À@à@` `@```€` `À`à`€ €@€`€€€ €À€à€   @ ` €   À à À À@À`À€À ÀÀÀàÀà à@à`à€à àÀààà@ @@@`@€@ @À@à@ @ @@ @` @€ @  @À @à @@@ @@@@@`@@€@@ @@À@@à@@`@ `@@`@``@€`@ `@À`@à`@€@ €@@€@`€@€€@ €@À€@à€@ @  @@ @` @€ @  @À @à @À@ À@@À@`À@€À@ À@ÀÀ@àÀ@à@ à@@à@`à@€à@ à@Àà@àà@€ €@€`€€€ €À€à€ € €@ €` €€ €  €À €à €@€ @€@@€`@€€@€ @€À@€à@€`€ `€@`€``€€`€ `€À`€à`€€€ €€@€€`€€€€€ €€À€€à€€ €  €@ €` €€ €  €À €à €À€ À€@À€`À€€À€ À€ÀÀ€àÀ€à€ à€@à€`à€€à€ à€Àà€àà€À À@À`À€À ÀÀÀàÀ À À@ À` À€ À  ÀÀ Àà À@À @À@@À`@À€@À @ÀÀ@Àà@À`À `À@`À``À€`À `ÀÀ`Àà`À€À €À@€À`€À€€À €ÀÀ€Àà€À À  À@ À` À€ À  ÀÀ Àà ÀÀÀ ÀÀ@ÀÀ`ÀÀ€ÀÀ ÀÀÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ, $í ´` À üw0!Bƒ BL ¢Äˆ^„¸ÑàÆ€;HaCi/html/Images/edit_small.png0000644000175000017500000000216710704777334016542 0ustar fighterfighter‰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/login_small.png0000644000175000017500000000134010706272524016706 0ustar fighterfighter‰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/back_small.png0000644000175000017500000000162611031215111016462 0ustar fighterfighter‰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а±± 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/cancel_small.png0000644000175000017500000000145410704777334017040 0ustar fighterfighter‰PNG  IHDRóÿagAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœIDATxÚS]H“a=Îe³$¥ù—eiBÍ)ˆ‰ó?ÿ&^(KCÁPðB(Š ]ȈAbB u‘Ž ¼’À‹HMM ÁDÓÒ®¡m³ÓómkLLèÀÃ÷ð~ç9ïó¾çy¡` ÁÝðp”Ç,àÒmÀbU«ûïCõÀ-pNù_rÌÏEˆD•Ä-¹TÌêt[4™Èòrº²²¸‘A—Ä—ØXvïBS§…APvnÌ´XÈÑQÒl¦G<==t¶·ÓQTÄŸ¹¹t¤¦ÒPv7¨ƒò€RööR«µ•î´4î–”ð/\ tvtpÓhä†NÇÇ""e‘½þW€ìpp«­®®.îÃî.?75qN«¥ xá-Î.²¹y?w9××i+-åŠ顯‹#¸ t°¦†ÎÁAºy0Á'Â[«¬äfm-‡£¢¥Ü¿U£Ø).æ×ÌLN××óûÌ ÿC`Çãá¬\ªÒíªð_ÆÄ0Q̃E£éÛ*,¤½¬ŒÛò]6hŸš:¸·›öþ~Ž$&òpu*•Ù™Íu±Í)·Ìº:þºóò(3dT&+eYüý&¶ýÈϧ{``qinŽócc{ÖF:;Yå»D׉ï\‘#¸¤ O8!»-ùçd~r’f)Ö}9©Š{ïí`¶¢‚‹6›—<1>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/split_small.png0000644000175000017500000000051010704777334016736 0ustar fighterfighter‰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/HaCi_Logo_small.png0000644000175000017500000000777310665316126017403 0ustar fighterfighter‰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/info_small_disabled.png0000644000175000017500000000076510765147574020405 0ustar fighterfighter‰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/check_small.png0000644000175000017500000000177710704777334016700 0ustar fighterfighter‰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/HaCi_Logo2_small.png0000644000175000017500000000342610677351441017456 0ustar fighterfighter‰PNG  IHDR@@% æ‰ pHYs  šœtIME× ;Ïû~µIDAThÞíYmLSg>÷Þ¶´¥¡ÜÒ–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/flushACL.png0000644000175000017500000001042710677351442016061 0ustar fighterfighter‰PNG  IHDR00Wù‡gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFbKGDÿÿÿ ½§“ pHYs  šœ{IDATxÚÕ˜yTœ×yÆ/’0Ú#§mÒ»=q'ÎQÓ6NâãÖiº±7µrZ;鉤®«:^$Çv,9¶£Dе+Ú7$¡IHìH0€Ø‡}`V± 0f˜åû¾ÛßECå¡Hêsžs¿åÞï¾Ïû>ï{ï!¥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/change_small.png0000644000175000017500000000165610704777334017044 0ustar fighterfighter‰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/HaCi_Logo.jpg0000644000175000017500000011024410513214352016161 0ustar fighterfighterÿØÿà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/addRoot.png0000644000175000017500000001271010670113276016003 0ustar fighterfighter‰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/compare.png0000644000175000017500000000610310670113276016034 0ustar fighterfighter‰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íçÛ í£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/logo.png0000644000175000017500000020160010474157163015352 0ustar fighterfighter‰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/tLine.png0000644000175000017500000000023111030471134015444 0ustar fighterfighter‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœ0IDAT(Ïc`@ÿј0”0¬"¬„ah+a$.,XÂE[6% aC9\‡ UÍ g¾ÿIEND®B`‚HaCi/html/Images/info_right_small.png0000644000175000017500000000152610761422075017733 0ustar fighterfighter‰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/search.png0000644000175000017500000000744710670113276015667 0ustar fighterfighter‰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/del_small.png0000644000175000017500000000165510704777334016362 0ustar fighterfighter‰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/vertLine.png0000644000175000017500000000020111030471134016156 0ustar fighterfighter‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`@ÿј‚Q%£Jàf` òk•KIEND®B`‚HaCi/html/Images/add-group_small.png0000644000175000017500000000242610704777333017474 0ustar fighterfighter‰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/replace_small.png0000644000175000017500000000200710704777334017221 0ustar fighterfighter‰PNG  IHDRóÿa pHYs  šœgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF}IDATxÚbdà“`N>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/impASNRoutes.png0000644000175000017500000001256410670113276016747 0ustar fighterfighter‰PNG  IHDR00Wù‡gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<IDATxÚbüÿÿ?ÃPÄÄ0Ä@ yã@; ©ñ¬âæÀÿþ30ùþúõcûŠv³H ÔW þsâÄ Ã?~0988\òÿ1Jš º{ ·ûšÀŸ?¿þÿcøÇÀdÿÿ?£œ˜;ƒœ7+'+ÃÎí÷^Zˆë2666åàà°b¹OŸ>°±± 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Í~ µ–„ƒ‹£ 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/editClose_small.png0000644000175000017500000000165310704777334017527 0ustar fighterfighter‰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/curveLine.png0000644000175000017500000000020511030471134016326 0ustar fighterfighter‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`@ÿј‚á§„‘p¸Œìƒ3™¿ë0IEND®B`‚HaCi/html/Images/move_small.png0000644000175000017500000000030710704777334016555 0ustar fighterfighter‰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/add-template_small.png0000644000175000017500000000133110704777333020145 0ustar fighterfighter‰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/pluginMgmt.png0000644000175000017500000001114410670113276016532 0ustar fighterfighter‰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,ÀšÀÜ_`›é°äµŒÁì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/info_small.png0000644000175000017500000000165210761422075016536 0ustar fighterfighter‰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/groupMgmt.png0000644000175000017500000000433110474157163016375 0ustar fighterfighter‰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/HaCi.png0000644000175000017500000020160010474157163015216 0ustar fighterfighter‰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/impConfig.png0000644000175000017500000001256410670113276016331 0ustar fighterfighter‰PNG  IHDR00Wù‡gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<IDATxÚbüÿÿ?ÃPÄÄ0Ä@ yã@; ©ñ¬âæÀÿþ30ùþúõcûŠv³H ÔW þsâÄ Ã?~0988\òÿ1Jš º{ ·ûšÀŸ?¿þÿcøÇÀdÿÿ?£œ˜;ƒœ7+'+ÃÎí÷^Zˆë2666åàà°b¹OŸ>°±± 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Í~ µ–ÝàóùØ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/horizLine.png0000644000175000017500000000017711030471134016345 0ustar fighterfighter‰PNG  IHDR‘dqsRGB®ÎébKGDÿ‡Ì¿ pHYs  šœIDAT(Ïc`Ø#Ã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.js0000644000175000017500000004677411410500775013654 0ustar fighterfightervar 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.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 (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("option").length; i++) { var tagTitle = document.getElementsByTagName("option")[i].title; if (tagTitle == 'tmplEntryID_' + ID) { var tagValue = document.getElementsByTagName("option")[i].value; if (optionStr != '') optionStr += ';'; optionStr += tagValue; } } 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 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 HaCi/html/favicon.ico0000644000175000017500000000526610677351441014632 0ustar fighterfighter .  ( \         #   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/HaCi.css0000644000175000017500000001677011410500775014021 0ustar fighterfighterhtml { 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:#FFFF00; 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; } #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{ } HaCi/spool/0000775000175000000410000000000011410502173012747 5ustar fighterwww-dataHaCi/spool/.spool_directory0000644000175000017500000000000010526656363016103 0ustar fighterfighterHaCi/ChangeLog0000644000175000017500000000524511410503055013276 0ustar fighterfighter2010-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 HaCi/modules/0000755000175000017500000000000011410502173013166 5ustar fighterfighterHaCi/modules/Net/0000755000175000017500000000000011410502173013714 5ustar fighterfighterHaCi/modules/Net/Nslookup.pm0000644000175000017500000002072710474157163016112 0ustar fighterfighterpackage 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/0000755000175000017500000000000011410502173014442 5ustar fighterfighterHaCi/modules/Net/IMAP/Simple.pm0000644000175000017500000005450610524232130016241 0ustar fighterfighterpackage 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/modules/MySQL/0000755000175000017500000000000011410502173014133 5ustar fighterfighterHaCi/modules/MySQL/Diff.pm0000644000175000017500000001453310672146763015371 0ustar fighterfighterpackage 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.pm0000644000175000017500000000605711026726044015540 0ustar fighterfighterpackage 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.pm0000644000175000017500000000423710672146763015621 0ustar fighterfighterpackage 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/Database.pm0000644000175000017500000000732510672146763016226 0ustar fighterfighterpackage 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/mysqldiff.pl0000755000175000017500000001006510672146763016515 0ustar fighterfighter#!/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/Math/0000755000175000017500000000000011410502173014057 5ustar fighterfighterHaCi/modules/Math/Base85.pm0000644000175000017500000000467310672146763015500 0ustar fighterfighterpackage 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/Log/0000755000175000017500000000000011410502173013707 5ustar fighterfighterHaCi/modules/Log/LogLite.pm0000644000175000017500000002054210670645573015632 0ustar fighterfighterpackage 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/HaCi/0000755000175000017500000000000011410502262013771 5ustar fighterfighterHaCi/modules/HaCi/Tables/0000755000175000017500000000000011410502173015204 5ustar fighterfighterHaCi/modules/HaCi/Tables/template.pm0000644000175000017500000000161510512363354017370 0ustar fighterfighterpackage HaCi::Tables::template; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/root.pm0000644000175000017500000000166610672146763016560 0ustar fighterfighterpackage HaCi::Tables::root; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/plugin.pm0000644000175000017500000000154411341116466017055 0ustar fighterfighterpackage HaCi::Tables::plugin; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/setting.pm0000644000175000017500000000134511031215116017217 0ustar fighterfighterpackage HaCi::Tables::setting; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/user.pm0000644000175000017500000000176211116201247016527 0ustar fighterfighterpackage HaCi::Tables::user; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/templateValue.pm0000644000175000017500000000141511336301760020361 0ustar fighterfighterpackage HaCi::Tables::templateValue; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/networkPlugin.pm0000644000175000017500000000142210761422077020425 0ustar fighterfighterpackage HaCi::Tables::networkPlugin; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/networkV6.pm0000644000175000017500000000147211341116466017464 0ustar fighterfighterpackage HaCi::Tables::networkV6; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/network.pm0000644000175000017500000000227711341116466017254 0ustar fighterfighterpackage HaCi::Tables::network; use base 'DBIEasy'; 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(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` integer 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`), INDEX (`network`), UNIQUE KEY (`rootID`,`ipv6ID`, `network`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Tables/pluginValue.pm0000644000175000017500000000136210752460203020044 0ustar fighterfighterpackage HaCi::Tables::pluginValue; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/group.pm0000644000175000017500000000167310526656357016732 0ustar fighterfighterpackage HaCi::Tables::group; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/rootAC.pm0000644000175000017500000000132410526656360016750 0ustar fighterfighterpackage HaCi::Tables::rootAC; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/networkAC.pm0000644000175000017500000000145610674660307017464 0ustar fighterfighterpackage HaCi::Tables::networkAC; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/templateEntry.pm0000644000175000017500000000162310526656360020420 0ustar fighterfighterpackage HaCi::Tables::templateEntry; use base 'DBIEasy'; 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` varchar(255) NOT NULL default '', `rows` integer NOT NULL default '1', `cols` integer NOT NULL default '1', PRIMARY KEY (`ID`) } } sub _log { my $self = shift; my @msg = @_; DBIEasy::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Tables/pluginConf.pm0000644000175000017500000000132710732507160017660 0ustar fighterfighterpackage HaCi::Tables::pluginConf; use base 'DBIEasy'; 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::_log($self, @msg); } sub _carp { my $self = shift; my ($message, %info) = @_; DBIEasy::_carp($self, $message, %info); } sub _croak { my $self = shift; my ($message, %info) = @_; DBIEasy::_croak($self, $message, %info); } 1; HaCi/modules/HaCi/Importer/0000755000175000017500000000000011410502173015573 5ustar fighterfighterHaCi/modules/HaCi/Importer/Cisco.pm0000644000175000017500000000531311323136211017172 0ustar fighterfighterpackage 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.pm0000644000175000017500000000556711305012064017572 0ustar fighterfighterpackage 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.pm0000644000175000017500000001143111305012064017543 0ustar fighterfighterpackage 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/Authentication/0000755000175000017500000000000011410502173016751 5ustar fighterfighterHaCi/modules/HaCi/Authentication/imap.pm0000644000175000017500000000442211341614314020243 0ustar fighterfighterpackage 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 @EXPORT_OK = qw(getCryptPassword); 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.pm0000644000175000017500000001367011345231162021136 0ustar fighterfighterpackage HaCi::Authentication::internal; use strict; 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) { $conf->{var}->{authenticationError} = HaCi::Utils::_gettext("Authentication failed!"); return 0; } else { $self->{sess}->param('username', $self->user()); } my $password = $user->{password}; 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 $user = ($userTable->search(['ID'], {username => $self->{sess}->param('username')}))[0]; if (!defined $user || !exists $user->{ID}) { $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 no 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->ID($DB->{'ID'}); $userTable->modifyFrom($session->param('username')); $userTable->modifyDate(&HaCi::Utils::currDate('datetime')); &HaCi::Utils::debug("Change User 'admin'\n"); return $userTable->replace(); } else { $userTable->ID(undef); $userTable->createFrom($session->param('username')); $userTable->createDate(&HaCi::Utils::currDate('datetime')); return $userTable->insert(); } } } 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->ID($DB->{'ID'}); $groupTable->modifyFrom($session->param('username')); $groupTable->modifyDate(&HaCi::Utils::currDate('datetime')); &HaCi::Utils::debug("Change Group 'Administrator'\n"); return $groupTable->replace(); } else { $groupTable->ID(undef); $groupTable->createFrom($session->param('username')); $groupTable->createDate(&HaCi::Utils::currDate('datetime')); return $groupTable->insert(); } } 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 = 0; my $cnter = 0; foreach (split//, reverse $bin) { $dec += 2 ** $cnter if $_; $cnter++; } return $dec; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Conf.pm0000644000175000017500000000355411341614314015230 0ustar fighterfighterpackage 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/Tree.pm0000644000175000017500000003645111341614314015244 0ustar fighterfighterpackage HaCi::Tree; use strict; use constant LOG2 => log(2); use HaCi::Mathematics qw/ dec2net net2dec getBroadcastFromNet getIPFromDec dec2ip ip2dec netv6Dec2net netv6Dec2NextNetDec netv6Dec2ip netv6Dec2IpCidr ipv6DecCidr2netv6Dec getV6BroadcastIP /; use HaCi::Utils qw/checkSpelling_Net debug checkRight networkStateID2Name fillHoles nd dn quoteHTML/; 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 = 0; if ($ipv6) { @nets = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} @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}; $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); 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 (keys %{$self->{TREE}}) { my $rootID = $_; my $rootName = $self->{TREE}->{$rootID}->{NAME} || ''; $sortBox->{$rootName} = $rootID; } foreach (sort keys %{$sortBox}) { my $rootName = $_; 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 $rootName = "eHTML($self->{TREE}->{$rootID}->{NAME}) || '???'; my $rootDescr = "eHTML($self->{TREE}->{$rootID}->{DESCR}) || ''; my $rootExpanded = $self->{TREE}->{$rootID}->{EXPANDED}; my $rootParent = $self->{TREE}->{$rootID}->{PARENT}; my $ipv6 = $self->{TREE}->{$rootID}->{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 $ipv6 = $self->{TREE}->{$rootID}->{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 %{$self->{TREE}->{$rootID}->{NETWORKS}}; my @nets = (); if ($ipv6) { @nets = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} @netst; } else { @nets = sort {$a<=>$b} @netst; } my $netCnter = 0; my $lastParents = {}; my $parentMarker = {}; foreach (@nets) { my $networkDec = $_; my $network = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{NETWORK}; my $netID = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{NETID}; my $descr = "eHTML($self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{DESCR}); my $expanded = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{EXPANDED}; my $parent = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{PARENT}; my $fillNet = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{FILLNET} || 0; my $status = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{STATUS} || 'assigned'; my $invisible = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{INVISIBLE} || 0; my $defSubnetSize = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{DEFSUBNETSIZE} || 0; $ipv6 = $self->{TREE}->{$rootID}->{NETWORKS}->{$networkDec}->{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) { if ($_ < $ipaddressDec) { $level--; $parentBroadcast = pop @broadcasts; $parentCidr = pop @parentCidrs; my $net = pop @{$networks}; $net->{parentBroadcast} = 1; push @{$networks}, $net; } } } 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');showStatus(1);"; $bNoDiv = 1 if $networkDec == $origNetworkDec; my $statePic = 0; my $stateAlt = ''; if (uc($status) eq 'LOCKED') { $statePic = 'key_small.png'; $stateAlt = _gettext("This Network is locked"); } elsif (uc($status) eq 'RESERVED') { $statePic = 'reserved_small.png'; $stateAlt = _gettext("This Network is reserved"); } elsif (uc($status) 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; } 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, editPic => 'edit_small.png', editAlt => _gettext('Edit'), editNetUrl => "a('editNet', '$netID',0,0,0);", 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 (sort {$a<=>$b} keys %{$parentMarker}) { my $netCnter = $_; 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; HaCi/modules/HaCi/Mathematics.pm0000644000175000017500000003073511410165772016611 0ustar fighterfighterpackage 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 ); 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!\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; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/Log.pm0000644000175000017500000000124611341614314015060 0ustar fighterfighterpackage 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/HaCi.pm0000644000175000017500000007106711410500775015155 0ustar fighterfighterpackage 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 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 checkNetworkACTable checkNetACL getRoots mkPluginConfig initTables initCache chOwnPW updSettings updateSettingsInSession flushACLCache flushNetCache searchAndGetFreeSubnets getParam /; 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 = $_; foreach ($q->param($k)) { warn " $k => $_ \n"; } } } # Dump if ($q->param('func')) { if ($q->param('func') eq 'logout') { if ($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()) { &getRights(); &$soapPreRun($soapArgs, $q) if $soap && defined $soapPreRun; &checkRights(); &main(); } else { &checkTables(); &checkNetworkACTable(); &HaCi::GUI::authentication::login(); $t->{V}->{page} = 'login'; } my $warnings = ''; $warnings = &finalize(); return &$soapPostRun($soapArgs, $q, $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 ($q->param('fname')) { # AJAX call return; } my $t = $HaCi::GUI::init::t; my $q = $HaCi::HaCi::q; if ($q->param('submitAddRoot')) { if (&addRoot( &getParam(1, undef, $q->param('name')), &getParam(1, undef, $q->param('descr')), &getParam(1, 0, $q->param('ipv6')), &getParam(1, undef, $q->param('rootID')), )) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param( 'func', ((&getParam(1, 0, $q->param('editRoot'))) ? 'editRoot' : 'addRoot') ); } } elsif ($q->param('submitAddNet')) { if (&addNet( &getParam(1, 0, $q->param('netID')), &getParam(1, undef, $q->param('rootID')), &getParam(1, undef, $q->param('netaddress')), &getParam(1, undef, $q->param('cidr')), &getParam(1, '', $q->param('descr')), &getParam(1, 0, $q->param('state')), &getParam(1, 0, $q->param('tmplID')), &getParam(1, 0, $q->param('defSubnetSize')), &getParam(1, 0, $q->param('forceState')), )) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'addNet'); } } elsif ($q->param('abortAddRoot')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('abortAddNet')) { if ($q->param('editNet')) { $q->delete('func'); $q->param('func', 'showNet'); } else { $q->delete('func'); $q->param('func', 'showAllNets'); } } elsif ($q->param('checktAddNet')) { &checkNet( &getParam(1, undef, $q->param('netaddress')), &getParam(1, undef, $q->param('cidr')), ); if ($q->param('editNet')) { $q->delete('func'); $q->param('func', 'editNet'); } else { $q->delete('func'); $q->param('func', 'addNet'); } } elsif ($q->param('submitImportASNRoutes')) { if ( &importASNRoutes( &getParam(1, undef, $q->param('asn')) ) ) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importASNRoutes'); } } elsif ($q->param('submitImpDNSTrans')) { if (&importDNSTrans()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importDNS'); } } elsif ($q->param('submitImpDNSLocal')) { if (&importDNSLocal()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importDNS'); } } elsif ($q->param('submitImpConfig')) { if (&importConfig()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importConfig'); } } elsif ($q->param('impCSVChangeSep')) { $q->delete('func'); $q->param('func', 'importConfig'); } elsif ($q->param('impCSVChangeType')) { $q->delete('func'); $q->param('func', 'importConfig'); } elsif ($q->param('abortImpCSV')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('submitImpCSV')) { if (&importCSV()) { $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'importConfig'); } } elsif ($q->param('editPluginConf')) { $q->delete('func'); $q->param('func', 'showPluginConf'); } elsif ($q->param('editNet')) { $q->delete('func'); $q->param('func', 'editNet'); } elsif ($q->param('splitNet')) { $q->delete('func'); $q->param('func', 'splitNet'); } elsif ($q->param('submitSplitNet')) { &splitNet( &getParam(1, undef, $q->param('netID')), &getParam(1, undef, $q->param('splitCidr')), &getParam(1, '', $q->param('descrTemplate')), &getParam(1, 0, $q->param('state')), &getParam(1, 0, $q->param('tmplID')), &getParam(1, 0, $q->param('delParentNet')), ); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('submitCombineNets')) { &combineNets(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('abortCombineNets')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('abortSplitNet')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif ($q->param('combineNets')) { my @nets = $q->param('selectedNetworks'); if ($#nets > 0) { $q->delete('func'); $q->param('func', 'combineNets'); } } elsif ($q->param('abortEditRoot')) { $q->delete('func'); $q->param('func', 'showRoot'); } elsif ($q->param('editRoot')) { $q->delete('func'); $q->param('func', 'editRoot'); } elsif ($q->param('abortDelRoot')) { $q->delete('func'); $q->param('func', 'showRoot'); } elsif ($q->param('abortDelNet')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif ($q->param('delRoot')) { if ($q->param('commitDelRoot')) { &delRoot( &getParam(1, undef, $q->param('rootID')) ); $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'delRoot'); } } elsif ($q->param('delNet')) { if ($q->param('commitDelNet')) { &delNet( &getParam(1, undef, $q->param('netID')), ((defined $q->param('withSubnets')) ? 1 : 0) ); $q->delete('func'); $q->param('func', 'showAllNets'); } else { $q->delete('func'); $q->param('func', 'delNet'); } } elsif ($q->param('reduceRoot')) { &expand( '-', 'root', &getParam(1, undef, $q->param('reduceRoot')) ); $session->param('currNet', ''); $session->param( 'currRootID', &getParam(1, undef, $q->param('reduceRoot')) ); } elsif ($q->param('expandRoot')) { &expand( '+', 'root', &getParam(1, undef, $q->param('expandRoot')) ); $session->param('currNet', ''); $session->param( 'currRootID', &getParam(1, undef, $q->param('expandRoot')) ); } elsif ($q->param('reduceNetwork')) { &expand( '-', 'network', &getParam(1, undef, $q->param('reduceNetwork')), &getParam(1, undef, $q->param('rootID')) ); $session->param( 'currNet', &getParam(1, undef, $q->param('reduceNetwork')) ); $session->param( 'currRootID', &getParam(1, undef, $q->param('rootID')) ); } elsif ($q->param('expandNetwork')) { &expand( '+', 'network', &getParam(1, undef, $q->param('expandNetwork')), &getParam(1, undef, $q->param('rootID')) ); $session->param( 'currNet', &getParam(1, undef, $q->param('expandNetwork')) ); $session->param( 'currRootID', &getParam(1, undef, $q->param('rootID')) ); } elsif ($q->param('closeTree')) { &expand('-', 'ALL', 'ALL'); } elsif ($q->param('jumpToButton')) { &expandTo( &getParam(1, undef, $q->param('rootIDJump')), &getParam(1, undef, $q->param('jumpTo')) ); } elsif ($q->param('genRandBranch')) { &genRandBranch(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('finishEditTree')) { $q->delete('editTree'); } elsif ($q->param('copyNetsTo')) { ©NetsTo( &getParam(1, undef, $q->param('copyToRootID')), [$q->param('selectedNetworks')], 0 ); } elsif ($q->param('moveNetsTo')) { ©NetsTo( &getParam(1, undef, $q->param('copyToRootID')), [$q->param('selectedNetworks')], 1 ); } elsif ($q->param('deleteNets')) { &delNets([$q->param('selectedNetworks')]); } elsif ($q->param('searchButton')) { &search(); $q->delete('func'); $q->param('func', 'search'); } elsif ($q->param('compareButton')) { &compare(); $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('newTmpl')) { if ($q->param('tmplName')) { $q->delete('tmplID'); my $tmplName = &getParam(1, '', $q->param('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 ($q->param('editNetTypeTmpl')) { unless (defined $q->param('tmplID')) { $q->delete('func'); $q->param('func', 'showTemplates'); } else { $q->delete('func'); $q->param('func', 'editTmpl'); } } elsif ($q->param('abortEditTmpl')) { $q->delete('func'); $q->param('func', 'showTemplates'); } elsif ($q->param('submitAddTmplEntry')) { my $tmplID = &saveTmpl(); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif ($q->param('submitEditTmplEntry')) { my $tmplID = &saveTmpl(1); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif ($q->param('submitDeleteTmplEntry')) { my $tmplID = &saveTmpl(2); $q->delete('tmplID'); $q->param('tmplID', $tmplID); $q->delete('func'); $q->param('func', 'editTmpl'); } elsif ($q->param('delTmpl')) { if ($q->param('commitDelTmpl')) { &delTmpl(&getParam(1, undef, $q->param('tmplID'))); $q->delete('func'); $q->param('func', 'showTemplates'); } elsif ($q->param('abortDelTmpl')) { $q->delete('func'); $q->param('func', 'showTemplates'); } else { if ($q->param('tmplID')) { $q->delete('func'); $q->param('func', 'delTmpl'); } else { $q->delete('func'); $q->param('func', 'showTemplates'); } } } elsif ($q->param('newGroup')) { if ($q->param('groupName')) { $q->delete('groupID'); my $groupName = &getParam(1, '', $q->param('groupName')); $groupName =~ s/[<>&"']//g; $q->delete('groupName'), $q->param('groupName', $groupName); $q->delete('func'); $q->param('func', 'editGroup'); } else { $q->delete('func'); $q->param('func', 'showGroups'); } } elsif ($q->param('editGroup')) { unless (defined $q->param('groupID')) { $q->delete('func'); $q->param('func', 'showGroups'); } else { $q->delete('func'); $q->param('func', 'editGroup'); } } elsif ($q->param('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 ($q->param('abortEditGroup')) { $q->delete('func'); $q->param('func', 'showGroups'); } elsif ($q->param('delGroup')) { if ($q->param('commitDelGroup')) { &delGroup(&getParam(1, undef, $q->param('groupID'))); $q->delete('func'); $q->param('func', 'showGroups'); } elsif ($q->param('abortDelGroup')) { $q->delete('func'); $q->param('func', 'showGroups'); } else { if ($q->param('groupID')) { my $groupName = &groupID2Name(&getParam(1, undef, $q->param('groupID'))); if ($groupName eq 'Administrator') { $q->delete('func'); $q->param('func', 'showGroups'); } else { $q->delete('func'); $q->param('func', 'delGroup'); } } else { $q->delete('func'); $q->param('func', 'showGroups'); } } } elsif ($q->param('newUser')) { if ($q->param('userName')) { $q->delete('userID'); my $userName = &getParam(1, '', $q->param('userName')); $userName =~ s/[<>&"']//g; $q->delete('userName'), $q->param('userName', $userName); $q->delete('func'); $q->param('func', 'editUser'); } else { $q->delete('func'); $q->param('func', 'showUsers'); } } elsif ($q->param('editUser')) { unless (defined $q->param('userID')) { $q->delete('func'); $q->param('func', 'showUsers'); } else { $q->delete('func'); $q->param('func', 'editUser'); } } elsif ($q->param('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 ($q->param('abortEditUser')) { $q->delete('func'); $q->param('func', 'showUsers'); } elsif ($q->param('delUser')) { if ($q->param('commitDelUser')) { &delUser(&getParam(1, undef, $q->param('userID'))); $q->delete('func'); $q->param('func', 'showUsers'); } elsif ($q->param('abortDelUser')) { $q->delete('func'); $q->param('func', 'showUsers'); } else { if ($q->param('userID')) { $q->delete('func'); $q->param('func', 'delUser'); } else { $q->delete('func'); $q->param('func', 'showUsers'); } } } elsif ($q->param('showSubnets')) { $q->delete('func'); $q->param('func', 'showSubnets'); } elsif ($q->param('abortShowSubnets')) { $q->delete('func'); $q->param('func', 'showNet'); } elsif ($q->param('checkDB')) { &checkDB(); } elsif ($q->param('submitshowPlugins')) { &updatePluginDB(); $q->delete('func'); $q->param('func', 'showPlugins'); } elsif ($q->param('editPluginGlobConf')) { $q->delete('func'); $q->param('func', 'showPluginGlobConf'); } elsif ($q->param('submitPluginConfig')) { my $global = &getParam(1, 0, $q->param('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 ($q->param('abortPluginConfig')) { my $global = &getParam(1, 0, $q->param('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 ($q->param('abortShowSettings')) { $q->delete('func'); $q->param('func', 'showAllNets'); } elsif ($q->param('commitChOwnPW')) { unless (&chOwnPW()) { $q->param('changeOwnPW', 1); } } elsif ($q->param('commitViewSettings')) { unless (&updSettings()) { $q->param('showViewSettings', 1); } &HaCi::GUI::init::setUserVars(); } elsif ($q->param('searchAndGetFreeSubnets')) { &searchAndGetFreeSubnets(); } if (defined $q->param('func')) { if ($q->param('func') eq 'addNet') { my $roots = &getRoots(); if ($#{$roots} == -1) { &warnl("Cannot find any Root! Please create some before you create networks\n"); $q->delete('func'); $q->param('func', 'addRoot'); } } elsif ($q->param('func') eq 'addNet' && $q->param('networkDec') && $q->param('rootID') && !($q->param('editNet'))) { my $rootID = &getParam(1, undef, $q->param('rootID')); my $networkDec = &getParam(1, undef, $q->param('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'); } } } elsif ($q->param('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(); if ($q->param('newWindow') && $q->param('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 $ipaddress = shift || ''; my $ipv6 = &rootID2ipv6($rootID); $ipaddress =~ s/[^\w\.\:]//g; return 0 unless $ipaddress; unless (&checkSpelling_IP($ipaddress, $ipv6)) { warn "Cannot jump to. No IP Address: '$ipaddress'\n"; return 0; } my $networkDec = ($ipv6) ? &netv62Dec($ipaddress . '/128') : &net2dec($ipaddress . '/32'); 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 $bestMatch = 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 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 $sid = &getParam(1, undef, $q->param('sid')); my $sessID = (exists $cookies{CGISESSID}) ? $cookies{CGISESSID}->value : undef; eval { $session = CGI::Session->load($sessID) or die CGI::Session->errstr; }; if ($@) { warn $@; $conf->{var}->{authenticationError} = _gettext("Session is broken"); $conf->{var}->{relogin} = 1; return; } if ( $session->is_expired ) { $conf->{var}->{authenticationError} = _gettext("Session is expired"); $conf->{var}->{relogin} = 1; } my @params = $q->param; if ( $session->is_empty && $#params != -1) { $session = $session->new($sid); } $session->expire($conf->{static}->{misc}->{sessiontimeout}); ($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 ($q->param('locale')) { $session->param('locale', $q->param('locale')); } $q->delete('jumpTo') if defined $q->param('jumpTo') && $q->param('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}; my $sid = &getParam(1, undef, $q->param('sid')); $session = CGI::Session->new($sid) or die CGI::Session->errstr; $session->expire($conf->{static}->{misc}->{sessiontimeout}); if ($q->param('locale')) { $session->param('locale', $q->param('locale')); } } sub authentication { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; use HaCi::Authentication::internal; my $authAdmin = new HaCi::Authentication::internal; $authAdmin->session($session); $authAdmin->init(); my $auth; if (($q->param('login') && $q->param('username') eq 'admin') || ($session->param('username') && $session->param('username') eq 'admin') ) { $auth = $authAdmin; } else { my $temp = (exists $conf->{user}->{auth}->{authmodule} && $conf->{user}->{auth}->{authmodule}) ? $conf->{user}->{auth}->{authmodule} : 'internal'; $temp = 'internal' if $temp eq 'HaCi'; # fix to be backward compatible my $authModule = "HaCi::Authentication::$temp"; my $authModuleFile = $conf->{static}->{path}->{authmodules} . '/' . $temp . '.pm'; eval { require $authModuleFile; }; if ($@) { $conf->{var}->{authenticationError} = _gettext("Authentication Error"); &warnl($@); return 0; } $auth = $authModule->new(); } $auth->session($session); if ($q->param('login')) { my $user = &getParam(1, undef, $q->param('username')); my $pass = &getParam(1, undef, $q->param('password')); unless (defined $user) { $conf->{var}->{authenticationError} = _gettext("No User given"); return 0; } $auth->user($user); $auth->pass($pass); if ($auth->authenticate()) { $conf->{var}->{authenticated} = 1; &updateSettingsInSession(); return 1; } else { return 0; } } else { if ($auth->isAutenticated()) { &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 = $q->cookie( -name => $session->name, -value => $session->id ); 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 " $_ -> " . $q->param($_); } $q->param; } # Dump print '' if $conf->{var}->{reloadPage}; print '' if $conf->{var}->{closePage}; } # 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); } $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') || ( /^submitImportASNRoutes$/ ) && !&checkRight('impASNRoutes') || ( /^getFreeSubnets$/ ) && !&checkRight('showNetDet') || ( /^submitImpDNSTrans$/ || /^submitImpDNSLocal$/ ) && !&checkRight('impDNS') || ( /^submitImpConfig$/ || /^impCSVChangeSep$/ || /^submitImpCSV$/ ) && !&checkRight('impConfig') } } 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); } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/SOAP/0000755000175000017500000000000011410502173014534 5ustar fighterfighterHaCi/modules/HaCi/SOAP/Type/0000755000175000017500000000000011410502173015455 5ustar fighterfighterHaCi/modules/HaCi/SOAP/Type/network.pm0000644000175000017500000000074111064230600017504 0ustar fighterfighterpackage HaCi::SOAP::Type::network; =begin WSDL _ATTR rootName $string Name of Root _ATTR network $string Network _ATTR description $string Description _ATTR state $string State =cut sub new { my $class = shift; my $rootName = shift || ''; my $network = shift || ''; my $description = shift || ''; my $state = shift || 0; bless { rootName => $rootName, network => $network, description => $description, state => $state, }, $class; } 1; HaCi/modules/HaCi/Utils.pm0000644000175000017500000054215311410502262015441 0ustar fighterfighterpackage 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 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 /; 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 getMaintInfosFromNet editRoot delNet genRandBranch delRoot copyNetsTo delNets search checkSpelling_IP checkSpelling_CIDR networkStateName2ID networkStateID2Name getNetworkTypes getTemplate saveTmpl prWarnl getTemplateEntries tmplID2Name delTmpl getTemplateData getGroups getGroup saveGroup groupID2Name delGroup getUsers getUser userID2Name saveUser currDate lwd dec2bin checkRight importDNSTrans importDNSLocal fillHoles _gettext getParam netID2Stuff getNetworkParentFromDB importConfig delUser getRights parseCSVConfigfile removeStatus expand splitNet combineNets getDBNetworkBefore getNetID getPlugins updatePluginDB getPluginsForNet pluginID2Name getTable checkTables updatePluginLastRun rootID2ipv6 checkRootACL checkNetACL checkNetworkACTable netv6Dec2ipv6ID getPluginInfos getPluginConfMenu mkPluginConfig getNetworksForPlugin getPluginConfValues getNetworkChilds initTables initCache finalizeTables getPluginLastRun pluginName2ID getPluginValue getHaCidInfo nd dn quoteHTML 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; my $q = $HaCi::HaCi::q; my $session = $HaCi::HaCi::session; my $editRoot = (defined $q->param('editRoot') && $q->param('editRoot')) ? 1 : 0; my $bImp = (defined $q->param('submitImportASNRoutes') && $q->param('submitImportASNRoutes')) ? 1 : 0; $bImp ||= (defined $q->param('submitImportDNS') && $q->param('submitImportDNS')) ? 1 : 0; $descr ||= ''; $ipv6 ||= 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})) { &warnl("Cannot update Root: " . $rootTable->errorStrs, $bImp); return 0; } } 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'}})) { &warnl("Cannot update Root: " . $rootTable->errorStrs, $bImp); return 0; } } else { $rootTable->ID(undef); $rootTable->createFrom($session->param('username')); $rootTable->createDate(&currDate('datetime')); unless ($rootTable->insert()) { &warnl("Cannot insert new Root: " . $rootTable->errorStrs, $bImp); return 0; } } } $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() unless $bImp; } &removeACLEntry($rootID, 'root', $_->{groupID}); } &warnl($errors) if $errors; my $box = {}; foreach (split(/, /, $session->param('groupIDs'))) { s/\D//g; $box->{$_} = 3; } 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 = ''; foreach (keys %{$box}) { $rootACTable->clear(); $rootACTable->rootID($rootID); $rootACTable->ID(undef); $rootACTable->groupID($_); $rootACTable->ACL($box->{$_}); unless ($rootACTable->insert()) { $errors .= $rootACTable->errorStrs unless $bImp; } &removeACLEntry($rootID, 'root', $_); } &warnl("Errors while setting Root ACLs: " . $errors, $bImp) if $errors; } warn "Successfully created Root $name"; 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 $q = $HaCi::HaCi::q; my $session = $HaCi::HaCi::session; my $bImp = (defined $q->param('submitImportASNRoutes') && $q->param('submitImportASNRoutes')) ? 1 : 0; $bImp ||= (defined $q->param('submitImpDNSTrans') && $q->param('submitImpDNSTrans')) ? 1 : 0; $bImp ||= (defined $q->param('submitImpDNSLocal') && $q->param('submitImpDNSLocal')) ? 1 : 0; $bImp ||= (defined $q->param('submitImpConfig') && $q->param('submitImpConfig')) ? 1 : 0; $bImp ||= (defined $q->param('submitImpCSV') && $q->param('submitImpCSV')) ? 1 : 0; my $onlyNew = (defined $q->param('onlyNew') && $q->param('onlyNew')) ? 1 : 0; my $editNet = (defined $q->param('editNet') && $q->param('editNet')) ? 1 : 0; my @netPluginActives = (defined $q->param('netPluginActives')) ? $q->param('netPluginActives') : (); my @netPluginNewLines = (defined $q->param('netPluginNewLines')) ? $q->param('netPluginNewLines') : (); 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; } 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; } 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'), $bImp); return 0; } unless (defined $netaddress && $netaddress) { &warnl(sprintf(_gettext("Sorry, you have to give me a %s!"), 'netaddress'), $bImp); 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), $bImp); return 0; } my $network = $netaddress . '/' . $cidr; &debug((($editNet) ? 'Edit' : 'Add') . " Network $network on " . &rootID2Name($rootID)); unless (&checkSpelling_Net($network, $ipv6)) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'network', $network), $bImp); return 0; } my $networkDec = ($ipv6) ? &netv62Dec($network) : &net2dec($network); 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), $bImp); return 0; } unless ($q->param('submitImpCSV')) { &debug("Flushing Cache for: " . &rootID2Name($rootID)); &removeFromNetcache($rootID); } } else { &warnl("Without a netID I cannot edit this Net!", $bImp); 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), $bImp); 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); my $origTmplID = undef; if ($editNet) { my $DBNET = ($networkTable->search(['ID', 'tmplID', 'ipv6ID'], {ID => $netID}))[0]; unless (defined $DBNET) { &warnl("Cannot update Net: No such Network exists ($netID)", $bImp); return 0; } $origTmplID = $DBNET->{'tmplID'}; $networkTable->modifyFrom($session->param('username')); $networkTable->modifyDate(&currDate('datetime')); unless ($networkTable->update({ID => $netID})) { &warnl("Cannot update Net: " . $networkTable->errorStrs, $bImp); return 0; } if ($ipv6) { unless (defined $DBNET) { &warnl("Cannot update Net: No such Network exists ($netID)", $bImp); return 0; } unless ($networkV6Table->update({ID => $DBNET->{'ipv6ID'}, rootID => $rootID})) { &warnl("Cannot update V6 Net: " . $networkV6Table->errorStrs, $bImp); return 0; } } unless ($q->param('submitImpCSV')) { &debug("Flushing Cache for: " . &rootID2Name($rootID)); &removeFromNetcache($rootID); } } else { my $DBNET = ($networkTable->search(['ID'], {network => $networkDec, rootID => $rootID, ipv6ID => $ipv6ID}))[0]; if (defined $DBNET) { &warnl(sprintf(_gettext("This Network '%s' allready exists!"), $network), $bImp); return 0; } $networkTable->ID(undef); $networkTable->createFrom($session->param('username')); $networkTable->createDate(&currDate('datetime')); unless ($networkTable->insert()) { &warnl("Cannot add Net: " . $networkTable->errorStrs, $bImp); return 0; } if ($ipv6) { unless ($networkV6Table->insert()) { &warnl("Cannot add V6 Net: " . $networkV6Table->errorStrs, $bImp); return 0; } } unless ($q->param('submitImpCSV')) { &debug("Flushing Cache for: " . &rootID2Name($rootID)); &removeFromNetcache($rootID); } } $netID = &getNetID($rootID, $networkDec, $ipv6ID) unless $editNet; 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() unless $bImp; } &removeACLEntry($netID, 'net', $groupID); } &warnl($errors) if $errors; my $box = {}; foreach (split(/, /, $session->param('groupIDs'))) { s/\D//g; my $acl = &checkNetACL($netID, 'ACL', $_); $box->{$_} = ($acl == 3) ? -1 : 3; } foreach ($q->param('accGroup')) { my $groupID = $_; my $acl = &checkNetACL($netID, 'ACL', $groupID); $box->{$groupID} = 0; foreach ('r', 'w') { my $right = $_; if ($q->param('accGroup_' . $right . '_' . $groupID)) { $box->{$groupID} += (($right eq 'r') ? 1 : (($right eq 'w') ? 2 : 0)); } } $box->{$groupID} = -1 if $box->{$groupID} == $acl; } $errors = ''; foreach (keys %{$box}) { my $groupID = $_; next if $box->{$groupID} == -1; $networkACTable->clear(); $networkACTable->netID($netID); $networkACTable->ID(undef); $networkACTable->groupID($groupID); $networkACTable->ACL($box->{$_}); unless ($networkACTable->insert()) { $errors .= $networkACTable->errorStrs unless $bImp; } &removeACLEntry($netID, 'net', $groupID); } &warnl("Errors while setting Network ACLs: " . $errors, $bImp) if $errors; } if ($tmplID) { my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { warn "Cannot add Template Value. DB Error (templateValue)\n"; return 0; } foreach ($q->param) { if (/^tmplEntryID_(\d+)$/) { my $tmplEntryID = $1; next unless $tmplEntryID; $tmplValueTable->clear(); $tmplValueTable->netID($netID); $tmplValueTable->tmplID($tmplID); $tmplValueTable->tmplEntryID($tmplEntryID); $tmplValueTable->value(&getParam(1, '', $q->param('tmplEntryID_' . $tmplEntryID))); 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'}})) { &warnl("Cannot update Template Value: " . $tmplValueTable->errorStrs); return 0; } } else { $tmplValueTable->ID(undef); unless ($tmplValueTable->insert()) { &warnl("Cannot add Template Value: " . $tmplValueTable->errorStrs); return 0; } } } } } 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; } 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 = &getParam(1, 0, $q->param('pluginOrder_' . $pluginID)); my $entry = ($networkPluginTable->search(['ID'], {netID => $netID, pluginID => $pluginID}))[0]; $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 $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) { $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 = (defined $q->param('delOld') && $q->param('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,5}$/) { &warnl(sprintf(_gettext("Sorry, this doesn't look like a %s: '%s'!"), 'AS Number', $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]; $rootTable->delete({ID => $root->{ID}}) if defined $root && exists $root->{ID}; } unless ($ipv6Used) { my $rootV6 = ($rootTable->search(['ID'], {name => $asn . '_IPv6'}))[0]; $rootTable->delete({ID => $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, '', $q->param('nameserver')); my $domain = &getParam(1, '', $q->param('domain')); my $status = &getParam(1, 0, $q->param('state')); my $origin = &getParam(1, '', $q->param('origin')); my $targetRootID = &getParam(1, -1, $q->param('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, $q->param('config')); my $source = &getParam(1, undef, $q->param('source')); my $state = &getParam(1, 0, $q->param('state')); my $targetRootID = &getParam(1, -1, $q->param('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 =~ s/_v6$//; $rootName ||= $file; my @cnter = (0, 0); if ($#$box > -1) { my $rootID = -1; my $rootIDv4 = &rootName2ID($rootName) || -1; my $rootIDv6 = &rootName2ID($rootName . '_v6') || -1; my $v4 = 1; foreach my $entry (@$box) { if ($entry->{ip} =~ /^[\d\.]+$/) { if ($rootIDv4 == -1) { unless (&addRoot($rootName)) { warn "AddRoot failed!\n"; return 0; } $rootIDv4 = &rootName2ID($rootName); } $rootID = $rootIDv4; $v4 = 1; } elsif ($entry->{ip} =~ /^[\w\:]+$/) { if ($rootIDv6 == -1) { unless (&addRoot($rootName . '_v6', '', 1)) { warn "AddRoot failed!\n"; return 0; } $rootIDv6 = &rootName2ID($rootName . '_v6'); } $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}, $status, 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, $q->param('configFileID')); my $fileName = &getParam(1, undef, $q->param('config')); my $status = &getParam(1, 0, $q->param('status')); my $targetRootID = &getParam(1, -1, $q->param('targetRoot')); my $tmplID = &getParam(1, 0, $q->param('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} = $q->param($_); } } my $colCnter = 0; my @cnter = (0, 0); my $rootIDs = {}; foreach (@data) { my $cnter = 0; my @cols = @{$_}; my $network = ''; my $status = 0; my $description = ''; my $percent = int(($colCnter++ / ($#data + 1)) * 100); foreach (@cols) { $cnter++; $network = $_ if $cnv->{$cnter} == -1; $status = $_ if $cnv->{$cnter} == -2; $description = $_ if $cnv->{$cnter} == -3; if ($cnv->{$cnter} > 0) { $q->delete('tmplEntryID_' . $cnv->{$cnter}); $q->param('tmplEntryID_' . $cnv->{$cnter}, $_); } } next unless $network; $stat->{DATA} = "Importing Network '$network' ($description)"; $stat->{PERCENT} = $percent; &setStatus(); my ($ip, $cidr) = split/\//, $network; my $rootName = (($targetRootID == -1) ? $fileName : &rootID2Name($targetRootID)); $rootName =~ s/_v6$//; my $rootID = -1; my $rootIDv4 = &rootName2ID($rootName) || -1; my $rootIDv6 = &rootName2ID($rootName . '_v6') || -1; my $v = 4; if (&checkSpelling_Net($network, 0)) { if ($rootIDv4 == -1) { unless (&checkIfRootExists($rootName)) { unless (&addRoot($rootName, '', 0)) { warn "AddRoot failed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } } $rootIDv4 = &rootName2ID($rootName); } $rootID = $rootIDv4; $v = 4; } elsif (&checkSpelling_Net($network, 1)) { if ($rootIDv6 == -1) { unless (&checkIfRootExists($rootName . '_v6')) { unless (&addRoot($rootName . '_v6', '', 1)) { warn "AddRoot failed!\n"; $stat->{STATUS} = 'ERROR'; $stat->{DATA} = ''; $stat->{PERCENT} = 100; &setStatus(); return 0; } } $rootIDv6 = &rootName2ID($rootName . '_v6'); } $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, 0, 1); } 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, 0, 1); } } 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, $q->param('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_XS; }; if ($@) { warn $@; return (); } my $csv = Text::CSV_XS->new({ 'sep_char' => $sep }); 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()); } unless ($line % ($maxLines / 4)) { $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, $q->param('zonefile')); my $status = &getParam(1, 0, $q->param('state')); my $origin = &getParam(1, '', $q->param('origin')); my $targetRootID = &getParam(1, -1, $q->param('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; } if ($inNetwork) { my $broadcastNetDec = &getV6BroadcastNet($networkDec, 128); my ($bNet, $bHost, undef) = &netv6Dec2PartsDec($broadcastNetDec); $broadcastStr = "AND (networkPrefix < $bNet || (networkPrefix = $bNet AND hostPart < $bHost) || (networkPrefix = $bNet AND hostPart = $bHost AND cidr <= 128))"; } $networkT = ($networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( (networkPrefix > $net) OR (networkPrefix = $net AND hostPart > $host) OR (networkPrefix = $net AND hostPart = $host AND cidr > $cidr) ) $broadcastStr ORDER BY networkPrefix, hostPart, cidr 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 = ''; if ($inNetwork) { my $broadcast = &net2dec(&dec2ip(&getBroadcastFromNet($networkDec)) . '/32'); # <= damit /32er auch in einem /31er angezeigt werden $broadcastStr = "AND network <= $broadcast"; } $networkT = ($networkTable->search( ['ID', 'network', 'description', 'state', 'defSubnetSize'], {rootID => $rootID, ipv6ID => ''}, 0, "AND network > $networkDec $broadcastStr ORDER BY network 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; } $networkT = ($networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( (networkPrefix < $net) OR (networkPrefix = $net AND hostPart < $host) OR (networkPrefix = $net AND hostPart = $host AND cidr < $cidr) ) ORDER BY networkPrefix DESC, hostPart DESC, cidr 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 { $networkT = ($networkTable->search( ['ID', 'network'], {rootID => $rootID, ipv6ID => ''}, 0, "AND network < $networkDec ORDER BY network 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; } return unless $configFile; 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; $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); @childs = $networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( (networkPrefix > $net) OR (networkPrefix = $net AND hostPart > $host) OR (networkPrefix = $net AND hostPart = $host AND cidr >" . (($withParent) ? '=' : '') . " $cidr) ) AND ( (networkPrefix < $netB) OR (networkPrefix = $netB AND hostPart < $hostB) OR (networkPrefix = $netB AND hostPart = $hostB AND cidr <= $cidrB) ) ORDER BY networkPrefix, hostPart, cidr"); if (@childs) { my @newChilds = (); foreach (@childs) { my $child = $_; my $net = ($networkTable->search(['ID', 'network', 'state'], {ipv6ID => $child->{ID}, network => 0, rootID => $rootID}))[0]; $net->{network} = &ipv6Parts2NetDec($child->{networkPrefix}, $child->{hostPart}, $child->{cidr}); push @newChilds, $net; } @childs = @newChilds; } } else { @childs = $networkTable->search(['ID', 'network', 'state'], {rootID => $rootID, ipv6ID => ''}, 0, "AND network >" . (($withParent) ? '=' : '') . " $networkDec AND network <= $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; 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; } @nrs = $networkV6Table->search( ['ID'], {rootID => $rootID}, 0, "AND ( (networkPrefix > $net) OR (networkPrefix = $net AND hostPart > $host) OR (networkPrefix = $net AND hostPart = $host AND cidr > $cidr) ) AND ( (networkPrefix < $netB) OR (networkPrefix = $netB AND hostPart < $hostB) OR (networkPrefix = $netB AND hostPart = $hostB AND cidr <= $cidrB) ) ORDER BY networkPrefix, hostPart, cidr"); } else { @nrs = $networkTable->search(['ID'], {rootID => $rootID, ipv6ID => ''}, 0, "AND network > $networkDec AND network <= $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}; 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; } } 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 $errors = ''; my $ipv6 = 0; 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 $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); &debug("Delete Network " . (($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec)) . " 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); } $tmplValueTable->clear(); $tmplValueTable->delete({netID => $netID}); if ($tmplValueTable->error) { warn sprintf("Error while deleting Templates for '%s' from '%s': %s", $network, $rootName, $tmplValueTable->errorStrs); } } &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); &warnl(sprintf(_gettext("Successfully deleted '%s' from '%s' (%i Networks deleted)"), $network, $rootName, $rows)); } return ($bLocal) ? $rows : 1; } } 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); 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)); } } 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); 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 $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); $error .= $rows if $rows !~ /^\d+$/; } else { my $rows = &delNet($netID, 1, 1); $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 $q = $HaCi::HaCi::q; my $search = &getParam(1, '', $q->param('search')); my $bLike = (defined $q->param('exact') ? 0 : 1); my $bFuzzy = (defined $q->param('fuzzy') ? 1 : 0); my $state = &getParam(1, undef, $q->param('state')); my $tmplID = &getParam(1, -1, $q->param('tmplID')); my $searchLimit = $conf->{static}->{misc}->{searchlimit} || 1000; $bLike = (defined $bLike) ? $bLike : 1; return unless $search; my $tmplBox = {}; if ($tmplID > 0) { foreach ($q->param) { $tmplBox->{$1} = $q->param($_) if /^tmplEntryID_(\d+)$/ && $q->param($_) ne ''; } } my $tmplResultBox = {}; if ($tmplID ne -1) { my $tmplSearch = '0'; foreach (keys %{$tmplBox}) { my $search = $tmplBox->{$_}; $search =~ s/'/\\'/g; $search =~ s/"/\\"/g; $search =~ s/;/\\;/g; $search =~ s/%/\\%/g; $search =~ s/([^\\]|^)\*/%/g; $tmplSearch .= " OR (tmplEntryID=$_ AND CONVERT(value USING latin1) " . (($bLike) ? "like '%$search%'" : "='$search'") . ')'; } my $tmplValueTable = $conf->{var}->{TABLES}->{templateValue}; unless (defined $tmplValueTable) { &warnl("Cannot get Template Values. DB Error (templateValue)"); return ''; } my @results = $tmplValueTable->search(['netID', 'tmplEntryID'], $tmplSearch); foreach (@results) { $tmplResultBox->{$_->{netID}}->{$_->{tmplEntryID}} = 1; } } my $t = $HaCi::GUI::init::t; my $bNet = 1 if &checkSpelling_Net($search, 0); my $bIP = 1 if &checkSpelling_IP($search, 0); my $ipv6 = 1 if $search =~ /\:/; $search .= (($ipv6) ? '/128' : '/32') if $bIP; $search = (($ipv6) ? &netv62Dec($search) : &net2dec($search)) if $bNet || $bIP; $search =~ s/'/\\'/g; $search =~ s/"/\\"/g; $search =~ s/;/\\;/g; $search =~ s/%/\\%/g; $search =~ s/([^\\]|^)\*/%/g; $search = '%' . $search . '%' if $bLike && !$bFuzzy; $state = ($state eq '-1') ? undef : int($state); my $fuzzySearch = '(substring(soundex(description), 2) LIKE ' . (($bLike) ? "concat('%', " : '') . "substring(soundex('$search'), 2)" . (($bLike) ? ", '%')" : '') . (($bNet || $bIP) ? ( ') OR (substring(soundex(network), 2) LIKE ' . (($bLike) ? "concat('%', " : '') . "substring(soundex('$search'), 2)" . (($bLike) ? ", '%')" : '') . ')') : ')'); $fuzzySearch .= " AND state='$state'" if defined $state; $fuzzySearch .= " AND tmplID='$tmplID'" unless $tmplID == -1; my $normSearch = "(description LIKE '$search'"; $normSearch .= " OR network LIKE '$search'" if $bNet || $bIP; $normSearch .= ')'; $normSearch .= " AND state='$state'" if defined $state; $normSearch .= " AND tmplID='$tmplID'" unless $tmplID == -1; $t->{V}->{'gettext_rootName'} = _gettext("Root Name"); my $networkTable = $conf->{var}->{TABLES}->{network}; unless (defined $networkTable) { warn "Cannot search. DB Error (network)\n"; return 0; } my $networkV6Table = $conf->{var}->{TABLES}->{networkV6}; unless (defined $networkV6Table) { warn "Cannot search. DB Error (networkV6)\n"; return 0; } my $qryRef = ($bFuzzy) ? $fuzzySearch : $normSearch; my @results = $networkTable->search( ['ID', 'network', 'description', 'rootID', 'ipv6ID', 'state'], $qryRef, $bLike, "ORDER BY network LIMIT $searchLimit" ); my $tmp = {}; my $tmplInfos = {}; foreach (@results) { my $net = $_; my $rootID = $net->{rootID}; 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 ($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; } foreach my $rootID (keys %{$tmp->{A}}) { my $ipv6 = $tmp->{A}->{$rootID}->{IPV6}; my @nets = (); if ($ipv6) { @nets = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} 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 = (split/\//, $network)[0]; my $rootName = &rootID2Name($rootID); if ($rootName ne '') { push @{$tmp->{B}}, { netID => $tmp->{A}->{$rootID}->{NETS}->{$_}->{ID}, network => $network, description => "eHTML($tmp->{A}->{$rootID}->{NETS}->{$_}->{description}), url => "$conf->{var}->{thisscript}?jumpToButton=1&rootIDJump=$rootID&jumpTo=$ipaddress", rootName => "eHTML($rootName), state => &networkStateID2Name($tmp->{A}->{$rootID}->{NETS}->{$_}->{state}), } } } } 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; } 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 @netTypes = $tmplTable->search(['ID', 'name'], {type => 'Nettype'}, 0, "ORDER BY `name`"); 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 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, $q->param('tmplID')); my $tmplType = &getParam(1, undef, $q->param('tmplType')); my $tmplTable = $conf->{var}->{TABLES}->{template}; my $position = &getParam(1, 0, $q->param('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!"); } if ($tmplID < 0) { unless (defined $q->param('tmplName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return 0; } my $tmplName = &getParam(1, '', $q->param('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()) { &warnl("Cannot create Template-Entry '$tmplName': " . $tmplTable->errorStrs()); } } my $newTmpl = ($tmplTable->search(['ID'], {name => $tmplName}))[0]; $tmplID = $newTmpl->{ID}; } if ($type == 2) { my $tmplEntryID = &getParam(1, undef, $q->param('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 position > $position"); foreach (@entries) { $tmplEntryTable->clear(); $tmplEntryTable->position(($_->{position} - 1)); unless ($tmplEntryTable->update({ID => $_->{ID}})) { warn "Cannot update TmplEntryTable: " . $tmplEntryTable->errorStrs(); } } } else { unless ($type) { my @entries = $tmplEntryTable->search(['ID', 'position'], {tmplID => $tmplID}, 0, "AND position >= $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, $q->param('TmplEntryType'))); $tmplEntryTable->position($position); $tmplEntryTable->description(&getParam(1, '', $q->param('TmplEntryParamDescr'))); $tmplEntryTable->size(&getParam(1, 1, $q->param('TmplEntryParamSize'))); $tmplEntryTable->entries(&getParam(1, '', $q->param('TmplEntryParamEntries'))); $tmplEntryTable->rows(&getParam(1, 1, $q->param('TmplEntryParamRows'))); $tmplEntryTable->cols(&getParam(1, 1, $q->param('TmplEntryParamCols'))); my $bError = 0; if ($type == 1) { my $tmplEntryID = &getParam(1, undef, $q->param('tmplEntryID')); unless (defined $tmplEntryID) { &warnl("No Template Entry ID!"); return $tmplID; } unless ($tmplEntryTable->update({ID => $tmplEntryID})) { &warnl("Cannot update Template-Entry for Template '$tmpl->{name}': " . $tmplEntryTable->errorStrs()); $bError = 1; } } else { $tmplEntryTable->ID(undef); unless ($tmplEntryTable->insert()) { &warnl("Cannot insert Template-Entry for Template '$tmpl->{name}': " . $tmplEntryTable->errorStrs()); $bError = 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 $q->param('tmplEntryID_' . $ID)) ? $q->param('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 $q->param('tmplEntryID_' . $ID)) ? $q->param('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 $q->param('tmplEntryID_' . $ID)) ? [$q->param('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->{$_}} sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} 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 @values = $tmplValueTable->search(['ID', 'netID'], {tmplID => $tmplID}, 0, "Group by tmplID"); 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)); } } } 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 => $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 => $descr, colspan => 2, }, ] } } else { $value =~ s/\n/
/g; push @{$tmplEntries}, { elements => [ { target => 'key', type => 'label', value => $descr }, { target => 'value', type => 'label', value => $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 @groups = $groupTable->search(['*']); foreach (@groups) { push @$groups, { ID => $_->{ID}, name => $_->{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, $q->param('groupID')); my $groupTable = $conf->{var}->{TABLES}->{group}; unless (defined $groupTable) { &warnl("Cannot save Group. DB Error (group)"); } if ($groupID < 0) { unless (defined $q->param('groupName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return undef; } } my $groupName = &getParam(1, '', $q->param('groupName')); my $permStr = '1'; foreach (sort {$a<=>$b} keys %{$conf->{static}->{rights}}) { $permStr .= ($groupName eq 'Administrator') ? 1 : (defined $q->param('groupPerm_' . $_) && $q->param('groupPerm_' . $_)) ? 1 : 0; } my $cryptStr = &lwe(&bin2dec($permStr)); $groupTable->clear(); $groupTable->name($groupName); $groupTable->description(&getParam(1, '', $q->param('groupDescr'))); $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'}})) { &warnl("Cannot update Group '$groupName': " . $groupTable->errorStrs()); } } else { $groupTable->ID(undef); $groupTable->createFrom($s->param('username')); $groupTable->createDate(&currDate('datetime')); unless ($groupTable->insert()) { &warnl("Cannot create Group '$groupName': " . $groupTable->errorStrs()); } } 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)); } } } 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 @users = $userTable->search(['*']); foreach (@users) { push @$users, { ID => $_->{ID}, name => $_->{username} }; } return $users; } sub getUser { warn 'SUB: ' . (caller(0))[3] . ' (' . (caller(1))[3] . ")\n" if $conf->{var}->{showsubs}; my $userID = shift; 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, $q->param('userID')); my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { &warnl("Cannot save User. DB Error (user)"); } if ($userID < 0) { unless (defined $q->param('userName')) { &warnl(_gettext('Sorry, you have to give me a Name!')); return undef; } } my $userName = &getParam(1, '', $q->param('userName')); my $groupStr = ''; foreach ($q->param) { if (/^userGroup_(\d+)/) { my $userID = $1; $groupStr .= ' ' . $userID . ','; } } my $pw = &getParam(1, undef, $q->param('password1')); my $pw1 = &getParam(1, undef, $q->param('password2')); $userTable->clear(); if ($userID > 0 && (!defined $pw || $pw eq '')) { &debug("Okay, no Password Change!"); } else { unless ($pw && $pw) { &warnl("No Password given"); return undef; } if ($pw ne $pw1) { &warnl("Passwords are not equal"); return undef; } my $crypt = &getCryptPassword($pw); $userTable->password($crypt); } $userTable->username($userName); $userTable->description(&getParam(1, '', $q->param('userDescr'))); $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'}})) { &warnl("Cannot update User '$userName': " . $userTable->errorStrs()); } } else { $userTable->ID(undef); $userTable->createFrom($s->param('username')); $userTable->createDate(&currDate('datetime')); unless ($userTable->insert()) { &warnl("Cannot create User '$userName': " . $userTable->errorStrs()); } } 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 = ''; my $log2 = log(2); my $hash = {}; my $highest = 0; while ($dec) { my $curr = int(log($dec)/$log2); $dec -= 2 ** $curr; $highest = $curr unless $highest; $hash->{$curr} = 1; } for (0 .. $highest) { $bin .= (exists $hash->{$_}) ? 1 : 0; } $bin = reverse $bin; 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); } $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 $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; 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); } } if (defined $acl) { if ($acl->{ACL} == 1 || $acl->{ACL} == 3) { $acls->{$netID}->{$groupID}->{r} ||= 1; } if ($acl->{ACL} == 2 || $acl->{ACL} == 3) { $acls->{$netID}->{$groupID}->{w} ||= 1; } } else { $acls->{$netID}->{$groupID}->{r} ||= &checkRootACL($rootID, 'r', $groupID); $acls->{$netID}->{$groupID}->{w} ||= &checkRootACL($rootID, 'w', $groupID); } 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; $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 $return; } 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; $ipv6 = &rootID2ipv6($rootID) unless defined $ipv6; my $parent = undef; 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 @potParents = $networkV6Table->search( ['ID', 'networkPrefix', 'hostPart', 'cidr'], {rootID => $rootID}, 0, "AND ( (networkPrefix < $net) OR (networkPrefix = $net AND hostPart < $host) OR (networkPrefix = $net AND hostPart = $host AND cidr < $cidr) ) ORDER BY networkPrefix DESC, hostPart DESC, cidr DESC"); return $parent unless @potParents; foreach (@potParents) { my $potParent = $_; $potParent->{network} = &ipv6Parts2NetDec($potParent->{networkPrefix}, $potParent->{hostPart}, $potParent->{cidr}); } foreach (reverse &sortDBEntriesBy(\@potParents, '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}; last; } else { warn "NetV6 found ($parent->{ID}) with no matching network!\n"; $parent = undef; } } } } else { my $broadcast = &getBroadcastFromNet($networkDec); # warn "BROADCAST (" . &dec2net($networkDec) . "): " . &dec2ip($broadcast) . "\n"; $parent = ($networkTable->search(['ID', 'network', 'description', 'state', 'defSubnetSize'], "rootID='$rootID' AND ipv6ID='' AND network<'$networkDec' AND (FLOOR(network / 256) + power(2, (32 - MOD(network, 256))) > $broadcast) ORDER BY network DESC LIMIT 1"))[0]; } 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')) { 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'; 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, $q->param('leftRootID')); my $rightID = &getParam(1, undef, $q->param('rightRootID')); my $rootName = &getParam(1, undef, $q->param('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); 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($newNetwork2); 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'; 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 $statID = $HaCi::HaCi::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; 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; my $parent = &getNextDBNetwork($rootID, $ipv6, $value, 1); if (defined $parent) { $expands->{$target}->{$rootID}->{$value} = ($type eq '+') ? 1 : 0; } } } $s->param('expands', $expands); } 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 => 'Runnung...'}; &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) 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; foreach ($q->param('combineNetsNr')) { my $cnter = $_; my $rootID = $q->param('combineNets_' . $cnter . '_rootID'); my $networkDec = $q->param('combineNets_' . $cnter . '_result'); my @sources = $q->param('combineNets_' . $cnter . '_source'); my $descr = $q->param('combineNets_' . $cnter . '_descr'); my $state = $q->param('combineNets_' . $cnter . '_state'); my $tmplID = $q->param('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); } } } } 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 = {}; foreach ($q->param('pluginActives')) { $box->{$_}->{ACTIVE} = 1; } foreach ($q->param('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->replace()) { &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 @plugins = $networkPluginTable->search(['*'], "netID='$netID' OR netID='-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}; eval { require "HaCi/Tables/$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::$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(); &debug("Checking Table: $tableName"); 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=>'MySQL', show_warnings=>0); my $t2 = SQL::Translator->new(parser=>'MySQL', show_warnings=>0); my $diff; { BEGIN { $^W = 0 } $diff = SQL::Translator::Diff::schema_diff( $t1->translate(\$orig), 'MySQL', $t2->translate(\$newTable), 'MySQL', { 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; &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 $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/$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 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}); unless (@pluginConfEntries) { my $pluginName = &pluginID2Name($pluginID); &debug("No Plugin Config for Plugin '$pluginName' found!\n"); return {}; } foreach (@pluginConfEntries) { my $pluginConfEntry = $_; $pluginConf->{$pluginConfEntry->{name}} = $pluginConfEntry->{value}; } 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, $q->param('pluginID')); my $netID = &getParam(1, undef, $q->param('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, $q->param('pluginConfName_' . $entry->{NAME})); $value = '' unless defined $value; &updatePluginConf($pluginID, $netID, $entry->{NAME}, $value); } my $resetLastRun = &getParam(1, 0, $q->param('pluginConfName_def_recurrent_resetLastRun')); $resetLastRun ||= &getParam(1, 0, $q->param('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('template'); &closeTable('templateEntry'); &closeTable('templateValue'); &closeTable('plugin'); &closeTable('pluginConf'); &closeTable('pluginValue'); &closeTable('setting'); } 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('template'); &getTable('templateEntry'); &getTable('templateValue'); &getTable('plugin'); &getTable('pluginConf'); &getTable('pluginValue'); &getTable('setting'); } 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 = new Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_NET' || '/tmp/HaCi.cache_NET', page_size => '1024k', num_pages => 3, ); unless (defined $netCache) { $netCache = new Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_NET' || '/tmp/HaCi.cache_NET', init_file => 1, page_size => '1024k', num_pages => 3, ); } $aclCache = new Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_ACL' || '/tmp/HaCi.cache_ACL', page_size => '1024k', num_pages => 3, ); unless (defined $aclCache) { $aclCache = new Cache::FastMmap->new( share_file => $conf->{static}->{path}->{cachefile} . '_ACL' || '/tmp/HaCi.cache_ACL', init_file => 1, page_size => '1024k', 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 $newNets = &getNetCacheEntry('FILL', 0, "$fromNetDec:$toNetDec:$ipv6:$defSubnetSize"); my @newNets = (); 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; 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) { 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; my $newNet = &ipv6DecCidr2netv6Dec($startIPDec, $cidr); push @newNets, $newNet; $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) { last if (($startIPDec % $offset) == 0) && ( (($startIPDec == $fromIPDec) && ((32 - $exp) > $startCidr)) || (!($startIPDec == $fromIPDec)) ); last if (($startIPDec + $offset) > $endIPDec); $offset = 2 ** --$exp; } my $cidr = 32 - $exp; my $newNet = &net2dec(&dec2ip($startIPDec) . "/$cidr"); push @newNets, $newNet; $startIPDec += 2 ** $exp; } } &updateNetcache('FILL', 0, "$fromNetDec:$toNetDec:$ipv6:$defSubnetSize", \@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; my $net = &getMaintInfosFromNet($netID); my $ipv6 = ($net->{ipv6ID}) ? 1 : 0; my $networkDec = $net->{network}; 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 = {}; my $subnetCnter = 0; $defSubnetSize = $subnetSize if defined $subnetSize; 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)) { my $network = ($ipv6) ? &netv6Dec2net($_) : &dec2net($_); my $cidr = (split/\//, $network, 2)[1]; $freeSubnetst->{$subnetCnter++} = $network if !$defSubnetSize || ($cidr == $defSubnetSize); } $startNet = ($ipv6) ? &netv6Dec2NextNetDec($childDec, 0) : &net2dec(&dec2ip(&getBroadcastFromNet($childDec) + 1) . '/0'); } foreach (&fillHoles($startNet, $nextNetDec, $ipv6, $defSubnetSize)) { my $network = ($ipv6) ? &netv6Dec2net($_) : &dec2net($_); my $cidr = (split/\//, $network, 2)[1]; $freeSubnetst->{$subnetCnter++} = $network if !$defSubnetSize || ($cidr == $defSubnetSize); } my @freeSubnets = (); my @sorted = (); if ($ipv6) { @sorted = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} keys %{$freeSubnetst}; } else { @sorted = sort {$a<=>$b} keys %{$freeSubnetst}; } map { push @freeSubnets, $freeSubnetst->{$_}; } @sorted; return ($scalar) ? $subnetCnter : @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, $q->param('oldPassword')); my $newPW = &getParam(1, undef, $q->param('newPassword')); my $newPWVal = &getParam(1, undef, $q->param('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)"); } foreach ($q->param) { if (/^setting_(.*)$/) { my $param = $1; @{$newSettings->{$param}} = ($q->param($_)); } } foreach ($q->param('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); } 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 $q = $HaCi::HaCi::q; my $t = $HaCi::GUI::init::t; my $subnetSize = &getParam(1, -1, $q->param('size')); &search(); my $result = $t->{V}->{searchResult}; my @freeSubnetst = (); if (defined $result && ref($result) eq 'ARRAY') { foreach (@{$result}) { my $network = $_; if ($subnetSize == -1) { $subnetSize = ($network->{network} =~ /:/) ? 128 : 32; } my @freeSubnets = &getFreeSubnets($network->{netID}, 0, $subnetSize); next unless @freeSubnets; push @freeSubnetst, @freeSubnets; } } my $freeSubnets = []; map { my $ipv6 = ($_ =~ /:/) ? 1 : 0; push @{$freeSubnets}, { net => $_, dec => ($ipv6) ? &netv62Dec($_) : &net2dec($_), }; } @freeSubnetst; $t->{V}->{freeSubnets} = $freeSubnets; } sub checkStateRules { 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 { 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 { 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 { my $onlyScalar = shift; my $ifUndef = shift; my @values = @_; return $ifUndef unless defined $values[0]; 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; # vim:ts=2:sts=2:sw=2 HaCi/modules/HaCi/Plugin.pm0000644000175000017500000000547611273202213015601 0ustar fighterfighterpackage 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/Plugins/0000755000175000017500000000000011410502173015413 5ustar fighterfighterHaCi/modules/HaCi/Plugins/RIPEInfo.pm0000644000175000017500000000316011341614314017330 0ustar fighterfighterpackage 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/DNSInfo.pm0000644000175000017500000000256411341614314017224 0ustar fighterfighterpackage 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/WhoisInfo.pm0000644000175000017500000000315411341614314017665 0ustar fighterfighterpackage 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*$/; } 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/Plugins/SearchAgent.pm0000644000175000017500000000174611341614314020151 0ustar fighterfighterpackage HaCi::Plugins::SearchAgent; 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 => 'Search Agent', version => '0.1', recurrent => 1, onDemand => 0, description => _gettext('This Plugin fills the Search-Database'), globMenuRecurrent => [], globMenuOnDemand => [], menuRecurrent => [], menuOnDemand => [], }; sub run_recurrent { my $self = shift; my $networks = shift; my $config = shift; warn "SearchAgent: I'm running...\n"; 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/TEMPLATE.pm.tmpl0000644000175000017500000000302211341614314020140 0ustar fighterfighterpackage 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/DNSInfoForNetworks.pm0000644000175000017500000000450511341614314021425 0ustar fighterfighterpackage 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 $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}; 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 => [ ] }; foreach (sort {$a<=>$b} keys %{$values}) { my $netID = $_; my $name = $values->{$netID}->{NAME} || ''; my $ptr = $values->{$netID}->{PTR} || ''; push @{$show->{BODY}}, ( { elements => [ { target => 'key', type => 'label', value => $name, }, { target => 'value', type => 'label', value => $ptr, }, ] }, ) if $name ne ''; } return $show; } 1; HaCi/modules/HaCi/Plugins/PingInfo.pm0000644000175000017500000000347711341614314017501 0ustar fighterfighterpackage HaCi::Plugins::PingInfo; use strict; use warnings; use HaCi::Mathematics qw/dec2net/; 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/PingSNMPNetworks.pm0000644000175000017500000001737711341614314021124 0ustar fighterfighterpackage 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/PingNetworks.pm0000644000175000017500000000605211341614314020412 0ustar fighterfighterpackage 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(-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 => [ ] }; foreach (sort {$a<=>$b} keys %{$values}) { my $netID = $_; 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/SnmpUtils.pm0000644000175000017500000000622411341614314016276 0ustar fighterfighterpackage 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/GUI/0000755000175000017500000000000011410502173014416 5ustar fighterfighterHaCi/modules/HaCi/GUI/authentication.pm0000644000175000017500000000416411341614314020004 0ustar fighterfighterpackage HaCi::GUI::authentication; use strict; use HaCi::Conf qw/getConfigValue/; 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 => (defined $q->param('username')) ? $q->param('username') : '', 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/init.pm0000644000175000017500000000434111341614314015725 0ustar fighterfighterpackage 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); } $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->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->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.pm0000644000175000017500000046423611410500775015725 0ustar fighterfighterpackage HaCi::GUI::main; use warnings; use strict; 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 /; use HaCi::Mathematics qw/ dec2net net2dec getBroadcastFromNet dec2ip getIPFromDec getNetmaskFromCidr getNetaddress getV6BroadcastNet ipv62dec 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 = $q->param('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 (defined $q->param('source') && $q->param('source') eq 'csv') { &mkImportCSV(); } else { &mkImportConfig(); } } elsif ($func eq 'showNet' && &checkRight('showNetDet')) { $t->{V}->{mainPage} = 'showNet'; my ($rootID, $networkDec) = &netID2Stuff($q->param('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 '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(); } } 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 = $q->param('editTree'); my $roots = &getRoots(); 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 => (($q->param('rootID')) ? [$q->param('rootID')] : []), onChange => 'javascript:checkIfIPv6(this.value, "TREE")', }, { target => 'single', type => 'textfield', name => 'jumpTo', size => 25, maxlength => 39, value => ((defined $q->param('jumpTo') && $q->param('jumpTo') ne '') ? $q->param('jumpTo') : '<' . _gettext('IP address') . '>'), style => ((defined $q->param('jumpTo')) ? '' : 'color:#AAAAAA'), onClick => ((defined $q->param('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 => (($q->param('rootID')) ? [$q->param('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') || {}; my $expandAll = (defined $q->param('expandAll') && $q->param('expandAll')) ? 1 : 0; 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} = (defined $q->param('editTree')) ? $q->param('editTree') : 0; } 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 = (defined $q->param('expandAll') && $q->param('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, 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; } } 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 =~ /\%{2}/; if (&checkSpelling_Net($network, $ipv6)) { $bACL = &checkNetACL($netID, 'r'); $tree->addNet($netID, $rootID, $ipv6, $networkDec, $description, ($status || 0), $networkDecOrig, 0, $defSubnetSize); $tree->setNetExpanded($rootID, $ipv6, $networkDec, ((($expandAll || $expands->{network}->{$rootID}->{$networkDec}) && $bACL) ? 1 : 0)); $tree->setNetParent($rootID, $ipv6, $networkDec, ((defined &getNextDBNetwork($rootID, $ipv6, $networkDec, 1)) ? 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"), $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("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("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 = $q->param('rootID'); 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 $q->param('name')) ? "eHTML($q->param('name')) : ''); my $descr = ((defined $q->param('descr')) ? "eHTML($q->param('descr')) : ''); my $ipv6 = ((defined $q->param('ipv6')) ? $q->param('ipv6') : 0); my $bEditRoot = (defined $q->param('editRoot')) ? 1 : 0; my $nrOfNetworks = ((defined $q->param('rootID')) ? &getNrOfChilds(0, $q->param('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()}) { $groups->{$_->{ID}} = $_->{name} unless $_->{name} eq 'Administrator' || $s->param('groupIDs') =~ / $_->{ID},/; } $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, } }, ]; foreach (sort {$a<=>$b} keys %{$groups}) { push @{$t->{V}->{rootGroupRightsMenu}}, ( { elements => [ { target => 'single', type => 'checkbox', name => 'accGroup_r_' . $_, value => 1, descr => '', checked => (defined $q->param('accGroup_r_' . $_)) ? $q->param('accGroup_r_' . $_) : 0, align => 'center', width => '3em', onChange => "javascript:setACLs(\"$_\",\"r\")", }, { target => 'single', type => 'checkbox', name => 'accGroup_w_' . $_, value => 1, descr => '', checked => (defined $q->param('accGroup_w_' . $_)) ? $q->param('accGroup_w_' . $_) : 0, width => '5em', align => 'center', onChange => "javascript:setACLs(\"$_\",\"w\")", }, { target => 'single', type => 'label', value => $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(); map {my $h=$_;$h->{name} = "eHTML($h->{name}); $_ = $h;} @{$roots}; 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 = (defined $q->param('editNet')) ? $q->param('editNet') : 0; my $fillNet = (defined $q->param('fillNet')) ? $q->param('fillNet') : 0; my $checktAddNet = (defined $q->param('checktAddNet')) ? $q->param('checktAddNet') : 0; my $chTmplID = (defined $q->param('chTmplID')) ? $q->param('chTmplID') : 0; my $submitAddNet = (defined $q->param('submitAddNet')) ? $q->param('submitAddNet') : 0; my $forceState = (defined $q->param('forceState')) ? $q->param('forceState') : 0; # Init variables... my $netaddress = ''; my $netmask = ''; my $cidr = ''; my $descr = ''; my $state = 0; my $defSubnetSize = 'min'; my $rootID = 0; my $tmplID = 0; # If Fillnet if ($fillNet) { if (defined $q->param('rootID') && defined $q->param('networkDec')) { $rootID = $q->param('rootID'); my $networkDec = $q->param('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 = $q->param('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; 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 = $q->param('netaddress'); $netmask = $q->param('netmask'); $cidr = $q->param('cidr'); $descr = $q->param('descr'); $state = $q->param('state'); $defSubnetSize = $q->param('defSubnetSize'); $rootID = $q->param('rootID'); $tmplID = $q->param('tmplID'); $t->{V}->{addNetHeader} = sprintf(_gettext((($editNet) ? 'Edit' : 'Add') . " Network %s"), $netaddress . '/' . $cidr); } # 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 => 'showDescrHelper', type => '', onClick => "showDescrHelper()", value => '1', img => 'info_small.png', picOnly => 1, title => _gettext("Available Variables from the Plugins"), }, ], }, ] }, { 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 => 'showStatusHelper', 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;submit()', colspan => 3, } ] }, ]; $t->{V}->{helpDescrHeader} = _gettext("Available Variables from the Plugins"); $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) { push @{$t->{V}->{addNetMenu}}, ( { value => { type => 'hline', colspan => 2, } }, ); (my $menu, undef) = &getTemplateEntries($tmplID, 1, 0, 0, 0, 1); push @{$t->{V}->{addNetMenu}}, @{$menu}; } my $groups = {}; { my $knowNet = 0; my $rootID = 0; my $networkDec = 0; my $netID = 0; if ($fillNet && defined $q->param('rootID') && defined $q->param('networkDec')) { $knowNet = 1; $rootID = $q->param('rootID'); $networkDec = $q->param('networkDec'); $networkDec = Math::BigInt->new($networkDec) if $ipv6; my $parent = &getNetworkParentFromDB($rootID, $networkDec, $ipv6); $netID = $parent->{ID}; } elsif ($editNet && defined $q->param('netID')) { $knowNet = 1; $netID = $q->param('netID'); } foreach (@{&getGroups()}) { my $groupID = $_->{ID}; next if $_->{name} eq 'Administrator' || $s->param('groupIDs') =~ / $_->{ID},/; $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} = (defined $q->param('accGroup_r_' . $groupID)) ? $q->param('accGroup_r_' . $groupID) : 0; $groups->{$_->{ID}}->{W} = (defined $q->param('accGroup_w_' . $groupID)) ? $q->param('accGroup_r_' . $groupID) : 0; } else { # If you know the network (fillnet, editNet) get ACLs if ($knowNet) { my $acl = &checkNetACL($netID, 'ACL', $groupID); $groups->{$_->{ID}}->{R} = ($acl == 1 || $acl == 3) ? 1 : 0; $groups->{$_->{ID}}->{W} = ($acl == 2 || $acl == 3) ? 1 : 0; } } } } $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, } }, ]; 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', width => '3em', onChange => "javascript:setACLs(\"$_\",\"r\")", }, { target => 'single', type => 'checkbox', name => 'accGroup_w_' . $_, value => 1, descr => '', checked => $groups->{$_}->{W}, width => '5em', align => 'center', onChange => "javascript:setACLs(\"$_\",\"w\")", }, { target => 'single', type => 'label', value => $groups->{$_}->{NAME}, width => '8em', } ] }, ); push @{$t->{V}->{addNetHiddens}}, ( { name => 'accGroup', value => $_ }, ); }; $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 => $q->param('networkDec') }, ) if defined $q->param('networkDec'); } if ($editNet) { push @{$t->{V}->{addNetHiddens}}, ( { name => 'netID', value => $q->param('netID') } ) if defined $q->param('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"), }, { target => 'value', type => 'textfield', name => 'asn', size => 5, maxlength => 5, value => ((defined $q->param('asn')) ? $q->param('asn') : ''), 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 = $q->param('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 ($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 = &getFreeSubnets($netID, 1); $state = &networkStateID2Name($state); $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}; } } $t->{V}->{netBasicInfoHeader} = sprintf(_gettext("Details of 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 } ] }, { 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, } ] }, { elements => [ { target => 'key', type => 'label', value => sprintf(_gettext("# of free Subnets with CIDR '%s'"), ($defSubnetSize) ? $defSubnetSize : 'min'), }, { target => 'value', type => 'label', value => $nrOfFreeSubnets, } ] }, { 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 => $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 => $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 => '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 Infos of 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 = $q->param('netID'); 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, }, ], }, { 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 = $q->param('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 => $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 => $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', 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 = $q->param('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 $searchValue = ((defined $q->param('search')) ? $q->param('search') : ''); my $stats = $conf->{static}->{misc}->{networkstates}; my $state = (defined $q->param('state')) ? $q->param('state') : -1; my $tmplID = (defined $q->param('tmplID')) ? $q->param('tmplID') : -1; my $types = &getNetworkTypes(1); $searchValue =~ s/"/"/g; unshift @{$types}, {ID => -1, name => '[ALL]'}; map {$_->{ID} = $_->{id}} @{$stats}; unshift @{$stats}, {ID => -1, name => '[ALL]'}; $t->{V}->{searchHeader} = _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}->{searchFormName} = 'search'; $t->{V}->{buttonFocus} = 'searchButton'; $t->{V}->{searchMenu} = [ { elements => [ { target => 'single', type => 'textfield', name => 'search', size => 40, maxlength => 255, value => $searchValue, focus => 1, onKeyDown => "submitOnEnter(event, 'searchButton')", colspan => 2, align => 'center', }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Wildcard"), }, { target => 'value', type => 'label', value => '*', bold => 1, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Exact"), }, { target => 'value', type => 'checkbox', name => 'exact', value => 1, descr => '', checked => ((defined $q->param('exact')) ? 1 : 0), }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Fuzzy Search"), }, { target => 'value', type => 'checkbox', name => 'fuzzy', value => 1, descr => '', checked => ((defined $q->param('fuzzy')) ? 1 : 0), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => [$state], } ] }, { 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, } ] }, ]; if ($tmplID) { push @{$t->{V}->{searchMenu}}, ( { value => { type => 'hline', colspan => 2, } }, ); (my $menu, undef) = &getTemplateEntries($tmplID, 1, 0, 0, 1, 1); push @{$t->{V}->{searchMenu}}, @{$menu}; } push @{$t->{V}->{searchMenu}}, ( { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'single', type => 'buttons', align => 'center', colspan => 2, buttons => [ { name => 'searchButton', type => 'submit', value => _gettext("Search"), img => 'search_small.png', }, ] } ] } ); push @{$t->{V}->{searchHiddens}}, ( { name => 'func', value => 'search' }, { 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(); $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, }, { 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 = (defined $q->param('tmplID')) ? $q->param('tmplID') : undef; my $tmplName = (defined $q->param('tmplName') && $q->param('tmplName')) ? $q->param('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 $q->param('newTmpl')) ? (_gettext("New")) : '') . sprintf(_gettext("Template '%s' for '%s'"), $tmplName, _gettext($q->param('tmplType') || '')); $t->{V}->{templateMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => $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 => $tmpl->{modifyFrom}, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Modified on"), }, { target => 'value', type => 'label', value => $tmpl->{modifyDate}, } ] }, ]; $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 => 'tmplName', value => $q->param('tmplName') || '', }, { name => 'tmplType', value => $q->param('tmplType') || '', }, { name => 'tmplEntryID', value => '' }, ]; push @{$t->{V}->{editTemplateHiddens}}, ( { name => 'tmplID', value => $q->param('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 => 255, }, ], }, { 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($q->param('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 = $q->param('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(); $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, }, { 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 = (defined $q->param('groupID')) ? $q->param('groupID') : undef; my $groupName = (defined $q->param('groupName') && $q->param('groupName')) ? $q->param('groupName') : &groupID2Name($groupID); my $group = &getGroup($groupID); my $bAdmin = 1 if $groupName eq 'Administrator'; if (defined $q->param('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}); 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 ($_ || $bAdmin) { $q->delete('groupPerm_' . $cnter); $q->param('groupPerm_' . $cnter, 1); } $cnter++; } } } my $groupDescr = ((defined $q->param('groupDescr')) ? $q->param('groupDescr') : ''); $groupDescr =~ s/"/"/g; $t->{V}->{editGroupHeader} = ((defined $q->param('newGroup')) ? (_gettext("New ")) : '') . sprintf(_gettext("Group '%s'"), $groupName); $t->{V}->{editGroupMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name"), }, { target => 'value', type => 'label', value => $groupName } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'groupDescr', size => 25, maxlength => 255, value => $groupDescr, }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => $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 => $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 => (defined $q->param('groupPerm_' . $cnter)) ? $q->param('groupPerm_' . $cnter) : 0, 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 => $q->param('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 = $q->param('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 = $q->param('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(); $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, }, { 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 = (defined $q->param('userID')) ? $q->param('userID') : undef; my $userName = (defined $q->param('userName') && $q->param('userName')) ? $q->param('userName') : &userID2Name($userID); my $user = &getUser($userID); my $groups = {}; foreach (@{&getGroups()}) { $groups->{$_->{ID}} = $_->{name}; } if (defined $q->param('editUser')) { my $userTable = $conf->{var}->{TABLES}->{user}; unless (defined $userTable) { warn "Cannot Edit User. DB Error\n"; return 0; } my $user = ($userTable->search(['*'], {ID => $userID}))[0]; $q->delete('userDescr'); $q->param('userDescr', $user->{description}); foreach (split(/, /, $user->{groupIDs})) { s/\D//g; $q->delete('userGroup_' . $_); $q->param('userGroup_' . $_, 1); } } my $userDescr = ((defined $q->param('userDescr')) ? $q->param('userDescr') : ''); $userDescr =~ s/"/"/g; $t->{V}->{gettext_userpassword} = _gettext("Password is only for buildin 'HaCi' authentication!"); $t->{V}->{editUserHeader} = ((defined $q->param('newUser')) ? (_gettext("New ")) : '') . sprintf(_gettext("User '%s'"), $userName); $t->{V}->{editUserMenu} = [ { elements => [ { target => 'key', type => 'label', value => _gettext("Name"), }, { target => 'value', type => 'label', value => $userName } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Password"), }, { target => 'value', type => 'passwordfield', name => 'password1', size => 25, maxlength => 255, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Password Validation"), }, { target => 'value', type => 'passwordfield', name => 'password2', size => 25, maxlength => 255, }, ], }, { elements => [ { target => 'key', type => 'label', value => _gettext("Description"), }, { target => 'value', type => 'textfield', name => 'userDescr', size => 25, maxlength => 255, value => $userDescr }, ], }, { value => { type => 'hline', colspan => 2, } }, { elements => [ { target => 'key', type => 'label', value => _gettext("Created from"), }, { target => 'value', type => 'label', value => $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 => $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 => ' ' . $groups->{$_}, checked => (defined $q->param('userGroup_' . $_)) ? $q->param('userGroup_' . $_) : 0, } ] }, ) }; $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 => $q->param('userID') }, ) if defined $userID; } 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(); unshift @{$roots}, {ID => -1, name => '[' . _gettext('NEW') . ']'}; map {my $h=$_;$h->{name} = "eHTML($h->{name}); $_ = $h;} @{$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 => ((defined $q->param('nameserver')) ? $q->param('nameserver') : ''), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Domain"), }, { target => 'value', type => 'textfield', name => 'domain', size => 10, maxlength => 256, value => ((defined $q->param('domain')) ? $q->param('domain') : ''), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined $q->param('targetRoot')) ? [$q->param('targetRoot')] : []), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined $q->param('state')) ? [$q->param('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 => ((defined $q->param('domain')) ? $q->param('domain') : ''), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Origin (optional)"), }, { target => 'single', type => 'textfield', name => 'origin', size => 25, maxlength => 255, value => ((defined $q->param('origin')) ? $q->param('origin') : ''), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined $q->param('targetRoot')) ? [$q->param('targetRoot')] : []), }, ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined $q->param('state')) ? [$q->param('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(); 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 => ((defined $q->param('config')) ? $q->param('config') : ''), focus => 1, } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Source"), }, { target => 'value', type => 'popupMenu', name => 'source', size => 1, values => $sources, selected => ((defined $q->param('source')) ? [$q->param('source')] : []), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Status"), }, { target => 'value', type => 'popupMenu', name => 'state', size => 1, values => $stats, selected => ((defined $q->param('state')) ? [$q->param('state')] : []), } ] }, { elements => [ { target => 'key', type => 'label', value => _gettext("Target Root"), }, { target => 'value', type => 'popupMenu', name => 'targetRoot', size => 1, values => $roots, selected => ((defined $q->param('targetRoot')) ? [$q->param('targetRoot')] : []), }, ] }, ]; $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 $q->param('configFileID')) ? $q->param('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); my $descrs = (defined $q->param('tmplID') && $q->param('tmplID')) ? &getTemplateEntries($q->param('tmplID'), 0, 0, 1, 0, 1) : {}; my @cols = ( {ID => 0, name => ''}, {ID => -1, name => 'network'}, {ID => -2, name => 'status'}, {ID => -3, name => 'description'}, ); 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 => ((defined $q->param('sep')) ? $q->param('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 $q->param('tmplID')) ? [$q->param('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 => $q->param('config') }, { name => 'source', value => $q->param('source') }, { name => 'state', value => $q->param('state') }, { name => 'targetRoot', value => $q->param('targetRoot') }, { 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(); map {my $h=$_;$h->{name} = "eHTML($h->{name}); $_ = $h;} @{$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 $q->param('leftRootID')) ? [$q->param('leftRootID')] : []), }, { target => 'key', type => 'label', value => _gettext("Target"), }, { target => 'value', type => 'popupMenu', name => 'rightRootID', size => 1, values => $roots, selected => ((defined $q->param('rightRootID')) ? [$q->param('rightRootID')] : []), }, { target => 'key', type => 'label', value => _gettext("Save result under"), }, { target => 'value', type => 'textfield', name => 'resultName', size => 10, maxlength => 256, value => ((defined $q->param('resultName')) ? $q->param('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 = $q->param('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 {$_->{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 => $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 => $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 => $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 => $descr . ' %d', }, { target => 'value', type => 'buttons', align => 'left', buttons => [ { name => 'showDescrHelper', type => '', onClick => "showDescrHelper()", value => '1', img => 'info_small.png', picOnly => 1, title => _gettext("Available Variables from the Plugins"), }, ], }, ] }, { 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 from the Plugins"); $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 {$_->{ID} = $_->{id}} @{$stats}; $t->{V}->{combineNetsHeader} = _gettext("Combine Networks"); $t->{V}->{combineNetsFormName} = 'combineNetsMenu'; my $box = {}; foreach ($q->param('selectedNetworks')) { 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; # warn "COMBINE $network -> " . &dec2net($parentDec) . "\n"; $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 = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} keys %{$box->{$rootID}->{PARENTS}}; } else { @parents = sort {$a<=>$b} keys %{$box->{$rootID}->{PARENTS}}; } foreach (@parents) { my $parent = $_; my @nets = (); if ($ipv6) { @nets = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} @{$box->{$rootID}->{PARENTS}->{$parent}}; } else { @nets = sort {$a<=>$b} @{$box->{$rootID}->{PARENTS}->{$parent}}; } foreach (@nets) { my $currNet = $_; 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)); # warn "First: " . &dec2ip($firstNetaddress) . " - " . &dec2net($last) ." (currIP: " . &dec2ip($currIP) . ") < (LASTBROADCAST: " . &dec2ip($lastBroadcast) . ")\n"; 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); # warn "NEW: " . &dec2net($newNetRevisedNetDec) . "\n"; 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 = sort {Math::BigInt->new($a)<=>Math::BigInt->new($b)} @{$box1->{$rootID}->{NETS}->{$cnter}}; } else { @nets = sort {$a<=>$b} @{$box1->{$rootID}->{NETS}->{$cnter}}; } foreach (@nets) { my $networkDec = $_; 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)) . ' (' . $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 => $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 $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); }; 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 = $q->param('pluginID'); my $netID = $q->param('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 = $q->param('netID'); my $net = &getMaintInfosFromNet($netID); my $ipv6 = ($net->{ipv6ID}) ? 1 : 0; my $subnetSize = (defined $q->param('subnetSize')) ? $q->param('subnetSize') : $net->{defSubnetSize}; 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}->{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 ($q->param('changeOwnPW')) { $subMenu = 'chOwnPW'; &mkChOwnPW(); } elsif ($q->param('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' }, ]; } 1; # vim:ts=2:sw=2:sws=2 HaCi/modules/HaCi/GUI/gettext.pm0000644000175000017500000000145511341614314016451 0ustar fighterfighterpackage 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; 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/HaCiAPI.pm0000644000175000017500000003275511341614314015506 0ustar fighterfighterpackage HaCi::HaCiAPI; use strict; use warnings; use vars qw/$workDir/; # Polluting Environment for Config::General $ENV{script_name} = $ENV{SCRIPT_NAME}; $workDir = &getWorkDir(); $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 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; } 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); $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 _RETURN @HaCi::SOAP::Type::network _DOC This is a search function =cut sub search { my ($class, $user, $pass, @funcArgs) = @_; &init(); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&search_pre, \&search_post); } 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 $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, { searchButton => 1, search => $searchStr || '', state => (($state eq '') ? -1 : &networkStateName2ID($state) || 0), exact => $exact || undef, fuzzy => $fuzzy || undef, tmplID => $tmplID, }); } 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}); } 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); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&getFreeSubnets_pre, \&getFreeSubnets_post); } 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, { showSubnets => 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 = $q->param('amount') || 0; &dieWarnings($warnings) if $warnings ne ''; if (defined $result && ref($result) eq 'ARRAY') { my $networks = []; my $cnter = 0; foreach (@{$result}) { push @{$networks}, new HaCi::SOAP::Type::network('', $_->{net}); return $networks if $amount && ++$cnter == $amount; } return $networks; } else { return []; } } sub dieWarnings { my $warnings = shift; warn "die " . SOAP::Fault->faultcode('HaCiAPI')->faultstring($warnings) . "\n"; # die SOAP::Fault->faultcode('HaCiAPI')->faultstring($warnings); die $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(); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&getFreeSubnetsFromSearch_pre, \&getFreeSubnets_post); } 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 _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); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&addNet_pre, \&addNet_post); } 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] || ''; $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, 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; if (defined $q->param('func') && $q->param('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); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&delNet_pre, \&delNet_post); } 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'; if (defined $q->param('func') && $q->param('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); &HaCi::HaCi::run(1, [$user, $pass, @funcArgs], \&template_pre, \&template_post); } 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; } 1; HaCi/modules/DBIEasy.pm0000644000175000017500000002507311410500775014761 0ustar fighterfighterpackage DBIEasy; use strict; use Carp qw/carp croak/; use DBI; my $dbHandles = {}; my $initConf = {}; my $timeout = 300; our $lastError = ''; BEGIN { no strict 'refs'; foreach my $attr (qw/error errorStrs dbhost dbname dbuser dbpass PK dbh/) { *{$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 &init($self); my $dbh = &getDBConn($self); if ($dbh) { $self->dbh($dbh); if (&initChecks($self)) { $self->PK(&getPK($self)); &initSubRefs($self); return $self; } } return undef; } sub init { my $self = shift; foreach ('dbhost', 'dbname', 'dbuser', 'dbpass') { my $key = $_; unless ($self->{$key}) { if ($self->{CONFIG}->{$key}) { $self->$key($self->{CONFIG}->{$key}); } else { $self->_carp("No '$key' defined"); return 0; } } } return 1; } sub initSubRefs { my $self = shift; no strict 'refs'; foreach (&getColoumns($self)) { 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 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) { # unless (&checkIfTableExists($self)) { $self->_carp("Mhh, that old Handle doesn't smell well. Better taking a new one!"); $dbh = undef; } } unless (defined $dbh) { my @DSN = ( 'DBI:mysql:' . 'database=' . $self->dbname . ';host=' . $self->dbhost, $self->dbuser, $self->dbpass ); $dbh = DBI->connect(@DSN, { PrintError => 0, AutoCommit => 1 }); $self->_croak($DBI::errstr) unless $dbh; &checkForOldHandles($dbh) if defined $dbh; } $dbHandles->{$$}->{$self->dbhost}->{$self->dbname} = $dbh; $self->dbh($dbh); return $dbh; } sub initChecks { my $self = shift; my $dbh = $self->dbh; my $error = 0; $error = 1 unless &checkIfTableExists($self); return ($error == 0) ? 1 : 0; } sub clear { my $self = shift; foreach (&getColoumns($self)) { $self->$_(undef); } $self->error(0); $self->errorStrs(''); } 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 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 @rows = (); my @whereStr = (); my $con = 'AND'; my $cs = ''; 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, $_ . (($bLike) ? ' LIKE ' : ' = ') . "'" . $qryRestr->{$_} . "'"; } } } elsif (ref $qryRestr eq '') { $whereStr[0] = $qryRestr; } } map {$_ = 'binary ' . $_;} @whereStr if $cs; my $prStr = "SELECT " . (($distinct) ? 'DISTINCT ' : '') . join(',', @$qryCols) . ' FROM ' . $self->TABLE . (($#whereStr > -1) ? ' WHERE ' . join(" $con ", @whereStr) : ''); $prStr .= ' ' . $appStr if $appStr; warn "STRING: " . join('', split(/\n/, $prStr)) . "\n" if 0; my $sth = $dbh->prepare($prStr); $self->_carp($dbh->errstr()) if $dbh->errstr(); $sth->execute; $self->_carp($dbh->errstr()) if $dbh->errstr(); my $hashRef = $sth->fetchall_hashref($pk); $self->_carp($dbh->errstr()) if $dbh->errstr(); my $pkCnter = 1; push @rows, &getRows($self, $hashRef, $pkCnter); 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}) { if ($pkCnter < ($#{$self->{PK}} + 1)) { push @rows, &getRows($self, $hashRef->{$_}, ($pkCnter + 1)); } else { push @rows, $hashRef->{$_}; } } return @rows; } sub insert { my $self = shift; return &modify($self, 'INSERT'); } sub replace { my $self = shift; return &modify($self, 'REPLACE'); } sub update { my $self = shift; my $rowRestr = shift; my $bLike = shift; my $appStr = shift || 0; return &modify($self, 'UPDATE', $rowRestr, $bLike, $appStr); } sub delete { my $self = shift; my $rowRestr = shift; my $bLike = shift; my $appStr = shift || 0; return &modify($self, '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 @adds = (); my @whereStr = (); my $modStr = $type; my $con = 'AND'; $modStr .= ' INTO' if $type eq 'INSERT' || $type eq 'REPLACE'; $modStr .= ' FROM' if $type eq 'DELETE'; $modStr .= ' ' . $table; $modStr .= ' SET ' if $type eq 'INSERT' || $type eq 'REPLACE' || $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, $row . (($bLike) ? ' LIKE ' : ' = ') . "'" . $rowRestr->{$row} . "'"; } } } elsif (ref $rowRestr eq '') { $whereStr[0] = $rowRestr; } } $self->_log("$type in $table") if 0; foreach (keys %{$self->{COLOUMNS}}) { my $value = $self->{COLOUMNS}->{$_}; if (defined $value) { $value = $dbh->quote($value); push @adds, "$_=$value"; } } $modStr .= join(', ', @adds) if $type eq 'INSERT' || $type eq 'REPLACE' || $type eq 'UPDATE'; $modStr .= ' WHERE ' . join(" $con ", @whereStr) if $#whereStr > -1; $modStr .= ' ' . $appStr if $appStr; warn $modStr if 0; my $rows = $dbh->do($modStr); $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 { _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] . "]: $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; } sub checkForOldHandles { 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} > $timeout) { my $id = $row->{Id}; $sth_kill->execute($id); } } return 1; } 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