From c26bec99cfa41bff83bc05c53c13d53b9ba09cb1 Mon Sep 17 00:00:00 2001 From: Ben Klang Date: Wed, 16 Sep 2009 12:16:18 -0400 Subject: [PATCH] Import Beatnik from CVS --- beatnik/autogenerate.php | 46 +++ beatnik/commit.php | 54 +++ beatnik/config/.cvsignore | 4 + beatnik/config/autogenerate.php.dist | 78 ++++ beatnik/config/conf.xml | 62 +++ beatnik/config/prefs.php.dist | 45 +++ beatnik/delete.php | 55 +++ beatnik/docs/CHANGES | 5 + beatnik/docs/CREDITS | 32 ++ beatnik/docs/INSTALL | 215 ++++++++++ beatnik/docs/RELEASE_NOTES | 49 +++ beatnik/docs/TODO | 17 + beatnik/editrec.php | 56 +++ beatnik/index.php | 21 + beatnik/js/beatnik.js | 3 + beatnik/js/src/beatnik.js | 14 + beatnik/lib/Application.php | 46 +++ beatnik/lib/Beatnik.php | 590 ++++++++++++++++++++++++++++ beatnik/lib/Driver.php | 330 ++++++++++++++++ beatnik/lib/Driver/ldap2dns.php | 526 +++++++++++++++++++++++++ beatnik/lib/Driver/pdnsgsql.php | 518 ++++++++++++++++++++++++ beatnik/lib/Driver/sql.php | 284 +++++++++++++ beatnik/lib/Forms/Autogenerate.php | 33 ++ beatnik/lib/Forms/DeleteRecord.php | 41 ++ beatnik/lib/Forms/EditRecord.php | 63 +++ beatnik/lib/base.php | 93 +++++ beatnik/lib/version.php | 1 + beatnik/listzones.php | 70 ++++ beatnik/locale/en_US/help.xml | 40 ++ beatnik/locale/sl_SI/LC_MESSAGES/beatnik.mo | Bin 0 -> 4972 bytes beatnik/po/beatnik.pot | 549 ++++++++++++++++++++++++++ beatnik/po/sl_SI.po | 503 ++++++++++++++++++++++++ beatnik/scripts/export_config.php | 331 ++++++++++++++++ beatnik/scripts/sql/beatnik.mysql.php | 132 +++++++ beatnik/templates/common-header.inc | 41 ++ beatnik/templates/listzones/footer.inc | 13 + beatnik/templates/listzones/header.inc | 54 +++ beatnik/templates/listzones/row.inc | 32 ++ beatnik/templates/menu.inc | 42 ++ beatnik/templates/view/footer.inc | 12 + beatnik/templates/view/header.inc | 54 +++ beatnik/templates/view/record.inc | 45 +++ beatnik/themes/graphics/beatnik.png | Bin 0 -> 882 bytes beatnik/themes/graphics/commit-all.png | Bin 0 -> 430 bytes beatnik/themes/screen.css | 14 + beatnik/viewzone.php | 56 +++ 46 files changed, 5269 insertions(+) create mode 100644 beatnik/autogenerate.php create mode 100644 beatnik/commit.php create mode 100644 beatnik/config/.cvsignore create mode 100644 beatnik/config/autogenerate.php.dist create mode 100644 beatnik/config/conf.xml create mode 100644 beatnik/config/prefs.php.dist create mode 100644 beatnik/delete.php create mode 100644 beatnik/docs/CHANGES create mode 100644 beatnik/docs/CREDITS create mode 100644 beatnik/docs/INSTALL create mode 100644 beatnik/docs/RELEASE_NOTES create mode 100644 beatnik/docs/TODO create mode 100644 beatnik/editrec.php create mode 100644 beatnik/index.php create mode 100644 beatnik/js/beatnik.js create mode 100644 beatnik/js/src/beatnik.js create mode 100644 beatnik/lib/Application.php create mode 100644 beatnik/lib/Beatnik.php create mode 100644 beatnik/lib/Driver.php create mode 100644 beatnik/lib/Driver/ldap2dns.php create mode 100644 beatnik/lib/Driver/pdnsgsql.php create mode 100644 beatnik/lib/Driver/sql.php create mode 100644 beatnik/lib/Forms/Autogenerate.php create mode 100644 beatnik/lib/Forms/DeleteRecord.php create mode 100644 beatnik/lib/Forms/EditRecord.php create mode 100644 beatnik/lib/base.php create mode 100644 beatnik/lib/version.php create mode 100644 beatnik/listzones.php create mode 100644 beatnik/locale/en_US/help.xml create mode 100644 beatnik/locale/sl_SI/LC_MESSAGES/beatnik.mo create mode 100644 beatnik/po/beatnik.pot create mode 100644 beatnik/po/sl_SI.po create mode 100644 beatnik/scripts/export_config.php create mode 100644 beatnik/scripts/sql/beatnik.mysql.php create mode 100644 beatnik/templates/common-header.inc create mode 100644 beatnik/templates/listzones/footer.inc create mode 100644 beatnik/templates/listzones/header.inc create mode 100644 beatnik/templates/listzones/row.inc create mode 100644 beatnik/templates/menu.inc create mode 100644 beatnik/templates/view/footer.inc create mode 100644 beatnik/templates/view/header.inc create mode 100644 beatnik/templates/view/record.inc create mode 100644 beatnik/themes/graphics/beatnik.png create mode 100644 beatnik/themes/graphics/commit-all.png create mode 100644 beatnik/themes/screen.css create mode 100644 beatnik/viewzone.php diff --git a/beatnik/autogenerate.php b/beatnik/autogenerate.php new file mode 100644 index 000000000..8e2deed6d --- /dev/null +++ b/beatnik/autogenerate.php @@ -0,0 +1,46 @@ + + */ + +define('BEATNIK_BASE', dirname(__FILE__)); +require_once BEATNIK_BASE . '/lib/base.php'; +require_once BEATNIK_BASE . '/lib/Forms/Autogenerate.php'; + +$viewurl = Horde::applicationUrl('viewzone.php'); + +$vars = Horde_Variables::getDefaultVariables(); +$form = new Autogenerate($vars); + +if ($form->validate($vars)) { + if (Horde_Util::getFormData('submitbutton') == _("Autogenerate")) { + $result = Beatnik::autogenerate($vars); + if (is_a($result, 'PEAR_Error')) { + $notification->push($zonedata, 'horde.error'); + header('Location:' . Horde::applicationUrl('listzones.php')); + exit; + } + } else { + $notification->push(_("Autogeneration not performed"), 'horde.warning'); + } + + header('Location: ' . $viewurl); + exit; +} + +$title = _("Autogenerate"); +require BEATNIK_BASE . '/templates/common-header.inc'; +require BEATNIK_BASE . '/templates/menu.inc'; + +$form->renderActive(null, null, Horde::applicationUrl('autogenerate.php'), 'post'); + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/beatnik/commit.php b/beatnik/commit.php new file mode 100644 index 000000000..5aadd1234 --- /dev/null +++ b/beatnik/commit.php @@ -0,0 +1,54 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +define('BEATNIK_BASE', dirname(__FILE__)); +require_once BEATNIK_BASE . '/lib/base.php'; +require_once BEATNIK_BASE . '/lib/Forms/EditRecord.php'; + +$domains = array(); +if (Horde_Util::getGet('domain') == 'current') { + $url = Horde::applicationUrl('viewzone.php'); + $domains[] = $_SESSION['beatnik']['curdomain']; +} elseif (Horde_Util::getGet('domain') == 'all') { + $url = Horde::applicationUrl('listzones.php'); + foreach (Beatnik::needCommit() as $domain) { + $domains[] = $beatnik_driver->getDomain($domain); + } +} + +foreach ($domains as $domain) { + $_SESSION['beatnik']['curdomain'] = $domain; + $vars = new Horde_Variables; + + $vars->set('rectype', 'soa'); + foreach ($domain as $field => $value) { + $vars->set($field, $value); + } + $vars->set('serial', Beatnik::incrementSerial($domain['serial'])); + + $form = new EditRecord($vars); + $form->useToken(false); + $form->setSubmitted(true); + if ($form->validate($vars)) { + $form->getInfo($vars, $info); + $result = $beatnik_driver->saveRecord($info); + + if (is_a($result, 'PEAR_Error')) { + $notification->push($result->getMessage() . ': ' . $result->getDebugInfo(), 'horde.error'); + } else { + $notification->push(sprintf(_('Zone serial for %s incremented.'), $domain['zonename']), 'horde.success'); + } + } else { + $notification->push(sprintf(_("Unable to construct valid SOA for %s. Not incrementing serial."), $domain['zonename']), 'horde.error'); + } +} + +header('Location: ' . $url); +exit; diff --git a/beatnik/config/.cvsignore b/beatnik/config/.cvsignore new file mode 100644 index 000000000..841867b70 --- /dev/null +++ b/beatnik/config/.cvsignore @@ -0,0 +1,4 @@ +autogenerate.php +conf.php +conf.bak.php +prefs.php diff --git a/beatnik/config/autogenerate.php.dist b/beatnik/config/autogenerate.php.dist new file mode 100644 index 000000000..770426148 --- /dev/null +++ b/beatnik/config/autogenerate.php.dist @@ -0,0 +1,78 @@ + + * + * NOTE: Template is an multidimensional array. + * The first level key define record types + * For each type you can specify the replacement behavior and how + * autogenerate will treat old records. 'all' deletes all records + * of the same type, 'match' deletes those which have the same hostname, + * and 'none' simply adds more records leaving everything existing in + * place. + * The 'records' element is an array of arrays of Beatnick::getRecFields() + * key => value pairs. + * + * EXAMPLE: + * + * $template['cname'][] = array('hostname' => 'www', + * 'pointer' => 'server1', + * 'ttl' => 3600); + * $template['mx'][] = array('pointer' => 'server2', + * 'pref' => 10); + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Duck + * @package Beatnik + */ +$templates['example'] = array( + 'description' => _("Example Template"), + 'types' => array( + + // Example 'NS' records + 'ns' => array( + 'replace' => 'all', // Set to 'all' to remove all pre-existing + // NS records + + 'records' => array( // Array of records to be created + array('hostname' => 'ns1', 'pointer' => '10.0.0.1' ), + array('hostname' => 'ns2', 'pointer' => '10.0.0.2' ), + ), + ), + + // Example 'MX' record + 'mx' => array( + 'replace' => 'none', // Set to 'none' to leave all existing + // MX records alone + 'records' => array( + array('pointer' => 'mail', 'pref' => 10 ), + ), + ), + + + // Example 'A' record + 'a' => array( + 'replace' => 'match', // Set to 'match' to remove all records + // which share the same hostname + // (ie. 'www') + + 'records' => array( + // Notice the trailing '.' on the A record for the + // domain itself. For all other records, use the short + // hostname and do not append a '.' + array('hostname' => + $_SESSION['beatnik']['curdomain']['zonename'] . '.', + 'ipaddr' => '10.0.0.3', + ), + + array('hostname' => 'www', 'ipaddr' => '10.0.0.3' ), + ), + ), + ), +); + diff --git a/beatnik/config/conf.xml b/beatnik/config/conf.xml new file mode 100644 index 000000000..a319fa5e1 --- /dev/null +++ b/beatnik/config/conf.xml @@ -0,0 +1,62 @@ + + + + + Beatnik Storage + + + + + + + + + + + + localhost + + + + 3 + 3 + 2 + 3 + + + + objectclass + + dnszone + + + + + + + + + + + + + + + + + Menu Settings + + + + + + + diff --git a/beatnik/config/prefs.php.dist b/beatnik/config/prefs.php.dist new file mode 100644 index 000000000..f12c496d3 --- /dev/null +++ b/beatnik/config/prefs.php.dist @@ -0,0 +1,45 @@ + _("Options"), + 'label' => _("Display Preferences"), + 'desc' => _("Set default display parameters."), + 'members' => array('domain_groups', 'domains_perpage') +); + +$prefGroups['defaults'] = array( + 'column' => _("Options"), + 'label' => _("Record Defaults"), + 'desc' => _("Set default record parameters."), + 'members' => array('default_ttl') +); + +// user domain groups +$_prefs['domain_groups'] = array( + 'value' => '', + 'locked' => false, + 'shared' => false, + 'type' => 'implicit' +); + +// listing +$_prefs['domains_perpage'] = array( + 'value' => 20, + 'locked' => false, + 'shared' => false, + 'type' => 'number', + 'desc' => _("How many domain to display per page.") +); + +$_prefs['default_ttl'] = array( + 'value' => '86400', + 'locked' => false, + 'shared' => false, + 'type' => 'number', + 'desc' => _("Default Time-To-Live for new records.") +); diff --git a/beatnik/delete.php b/beatnik/delete.php new file mode 100644 index 000000000..8146a655e --- /dev/null +++ b/beatnik/delete.php @@ -0,0 +1,55 @@ +getRecord(Horde_Util::getFormData('id')); + +$form = new DeleteRecord($vars); + +if ($form->validate($vars)) { + $form->getInfo($vars, $info); + if (Horde_Util::getFormData('submitbutton') == _("Delete")) { + $result = $beatnik_driver->deleteRecord($info); + if (is_a($result, 'PEAR_Error')) { + $notification->push($result->getMessage() . ': ' . $result->getDebugInfo(), 'horde.error'); + header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('viewzone.php'), $info)); + } else { + $notification->push(_("Record deleted"), 'horde.success'); + if ($info['rectype'] == 'soa') { + header('Location: ' . Horde::applicationUrl('listzones.php')); + } else { + header('Location: ' . Horde::applicationUrl('viewzone.php')); + } + } + } else { + $notification->push(_("Record not deleted"), 'horde.warning'); + header('Location: ' . Horde_Util::addParameter(Horde::applicationUrl('viewzone.php'), $info)); + } + exit; +} elseif (!$form->isSubmitted() && $record) { + foreach ($record as $field => $value) { + $vars->set($field, $value); + } +} + + +$title = _("Delete"); +require BEATNIK_BASE . '/templates/common-header.inc'; +require BEATNIK_BASE . '/templates/menu.inc'; + +$form->renderActive(null, null, Horde::applicationUrl('delete.php'), 'post'); + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/beatnik/docs/CHANGES b/beatnik/docs/CHANGES new file mode 100644 index 000000000..dd5f72e14 --- /dev/null +++ b/beatnik/docs/CHANGES @@ -0,0 +1,5 @@ +--- +0.1 +--- + +[beatnik] Initial Release diff --git a/beatnik/docs/CREDITS b/beatnik/docs/CREDITS new file mode 100644 index 000000000..6e32ed47b --- /dev/null +++ b/beatnik/docs/CREDITS @@ -0,0 +1,32 @@ +=========================== + Beatnik Development Team +=========================== + +:Last update: $Date: 2007/04/22 04:50:38 $ +:Revision: $Revision: 1.6 $ + + +Core Developers +=============== +Ben Klang +Duck + + +Drivers +======= +ldap2dns - Ben Klang +sql - Duck + + +Localization +============ +Slovenian Duck + +===================== ====================================================== +===================== ====================================================== + + +Contributions +============= + +$Horde: beatnik/docs/CREDITS,v 1.6 2007/04/22 04:50:38 chuck Exp $ diff --git a/beatnik/docs/INSTALL b/beatnik/docs/INSTALL new file mode 100644 index 000000000..2a835b838 --- /dev/null +++ b/beatnik/docs/INSTALL @@ -0,0 +1,215 @@ +========================= + Installing Beatnik 1.0 +========================= + +:Last update: $Date: 2007/06/19 09:56:38 $ +:Revision: $Revision: 1.4 $ + +.. contents:: Contents +.. section-numbering:: + +This document contains instructions for installing the Beatnik ... + +For information on the capabilities and features of Beatnik, see the file +README_ in the top-level directory of the Beatnik distribution. + + +Obtaining Beatnik +================== + +Beatnik can be obtained from the Horde website and FTP server, at + + http://www.horde.org/beatnik/ + + ftp://ftp.horde.org/pub/beatnik/ + +Or use the mirror closest to you: + + http://www.horde.org/mirrors.php + +Bleeding-edge development versions of Beatnik are available via CVS; see the +file `horde/docs/HACKING`_ in the Horde distribution, or the website +http://www.horde.org/source/, for information on accessing the Horde CVS +repository. + + +Prerequisites +============= + +To function properly, Beatnik **requires** the following: + +1. A working Horde installation. + + Beatnik runs within the `Horde Application Framework`_, a set of common + tools for Web applications written in PHP. You must install Horde before + installing Beatnik. + + .. Important:: Beatnik 1.0 requires version 3.0+ of the Horde Framework - + earlier versions of Horde will **not** work. + + .. _`Horde Application Framework`: http://www.horde.org/horde/ + + The Horde Framework can be obtained from the Horde website and FTP server, + at + + http://www.horde.org/horde/ + + ftp://ftp.horde.org/pub/horde/ + + Many of Beatnik's prerequisites are also Horde prerequisites. + + .. Important:: Be sure to have completed all of the steps in the + `horde/docs/INSTALL`_ file for the Horde Framework before + installing Beatnik. + +2. The following PHP capabilities: + + a. FOO support ``--with-foo`` [OPTIONAL] + + Description of Foo and what it is used for. + +3. The following PEAR modules: + (See `horde/docs/INSTALL`_ for instructions on installing PEAR modules) + + a. PEAR_Package x.x.x [OPTIONAL] + + Beatnik uses the Foo_Bar class for... + +4. Something else. + +The following items are not required, but are strongly **recommended**: + +1. Yet something else. + + +Installing Beatnik +=================== + +Beatnik is written in PHP, and must be installed in a web-accessible +directory. The precise location of this directory will differ from system to +system. Conventionally, Beatnik is installed directly underneath Horde in +the web server's document tree. + +Since Beatnik is written in PHP, there is no compilation necessary; simply +expand the distribution where you want it to reside and rename the root +directory of the distribution to whatever you wish to appear in the URL. For +example, with the Apache web server's default document root of +``/usr/local/apache/htdocs``, you would type:: + + cd /usr/local/apache/htdocs/horde + tar zxvf /path/to/beatnik-x.y.z.tar.gz + mv beatnik-x.y.z beatnik + +and would then find Beatnik at the URL:: + + http://your-server/horde/beatnik/ + + +Configuring Beatnik +==================== + +1. Configuring Horde for Beatnik + + a. Register the application + + In ``horde/config/registry.php``, find the ``applications['beatnik']`` + stanza. The default settings here should be okay, but you can change + them if desired. If you have changed the location of Beatnik relative + to Horde, either in the URL, in the filesystem or both, you must update + the ``fileroot`` and ``webroot`` settings to their correct values. + +2. Configuring Beatnik + + To configure Beatnik, change to the ``config/`` directory of the installed + distribution, and make copies of all of the configuration ``dist`` files + without the ``dist`` suffix:: + + cd config/ + for foo in *.dist; do cp $foo `basename $foo .dist`; done + + Or on Windows:: + + copy *.dist *. + + Documentation on the format and purpose of those files can be found in each + file. You may edit these files if you wish to customize Beatnik's + appearance and behavior. With one exception (``foo.php``) the defaults + will be correct for most sites. + + You must login to Horde as a Horde Administrator to finish the + configuration of Beatnik. Use the Horde ``Administration`` menu item to + get to the administration page, and then click on the ``Configuration`` + icon to get the configuration page. Select ``Beatnik Name`` from the + selection list of applications. Fill in or change any configuration values + as needed. When done click on ``Generate Beatnik Name Configuration`` to + generate the ``conf.php`` file. If your web server doesn't have write + permissions to the Beatnik configuration directory or file, it will not be + able to write the file. In this case, go back to ``Configuration`` and + choose one of the other methods to create the configuration file + ``beatnik/config/conf.php``. + + Note for international users: Beatnik uses GNU gettext to provide local + translations of text displayed by applications; the translations are found + in the ``po/`` directory. If a translation is not yet available for your + locale (and you wish to create one), see the ``horde/po/README`` file, or + if you're having trouble using a provided translation, please see the + `horde/docs/TRANSLATIONS`_ file for instructions. + +3. More instructions, upgrading, securing, etc. + +4. Testing Beatnik + + Once you have configured Beatnik, bring up the included test page in your + Web browser to ensure that all necessary prerequisites have been met. See + the `horde/docs/INSTALL`_ document for further details on Horde test + scripts. If you installed Beatnik as described above, the URL to the test + page would be:: + + http://your-server/horde/beatnik/test.php + + The test script will also allow you to test... + + Next, use Beatnik to.... Test at least the following: + + - Foo + - Bar + + +Known Problems +============== + +... + + +Obtaining Support +================= + +If you encounter problems with Beatnik, help is available! + +The Horde Frequently Asked Questions List (FAQ), available on the Web at + + http://www.horde.org/faq/ + +The Horde Project runs a number of mailing lists, for individual applications +and for issues relating to the project as a whole. Information, archives, and +subscription information can be found at + + http://www.horde.org/mail/ + +Lastly, Horde developers, contributors and users may also be found on IRC, +on the channel #horde on the Freenode Network (irc.freenode.net). + +Please keep in mind that Beatnik is free software written by volunteers. +For information on reasonable support expectations, please read + + http://www.horde.org/support.php + +Thanks for using Beatnik! + +The Beatnik team + + +.. _README: ?f=README.html +.. _`horde/docs/HACKING`: ../../horde/docs/?f=HACKING.html +.. _`horde/docs/INSTALL`: ../../horde/docs/?f=INSTALL.html +.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/?f=TRANSLATIONS.html diff --git a/beatnik/docs/RELEASE_NOTES b/beatnik/docs/RELEASE_NOTES new file mode 100644 index 000000000..0702c7565 --- /dev/null +++ b/beatnik/docs/RELEASE_NOTES @@ -0,0 +1,49 @@ +notes['fm']['focus'] = 4; + +/* Mailing list release notes. */ +$this->notes['ml']['changes'] = <<notes['fm']['changes'] = <<notes['name'] = 'Skeleton'; +$this->notes['fm']['project'] = 'skeleton'; +$this->notes['fm']['branch'] = 'Default'; diff --git a/beatnik/docs/TODO b/beatnik/docs/TODO new file mode 100644 index 000000000..c74dc4a44 --- /dev/null +++ b/beatnik/docs/TODO @@ -0,0 +1,17 @@ +================================ + Beatnik Development TODO List +================================ +$Horde: beatnik/docs/TODO,v 1.5 2007/04/12 01:46:42 bklang Exp $ + +:Last update: $Date: 2007/04/12 01:46:42 $ +:Revision: $Revision: 1.5 $ +:Contact: + + +* Create an api which can be used by XML-RPC where remote clients can + authenticate to horde and update their own DNS entries (subject to built-in + permissions scheme). + +* Allow sorting on arbitrary fields + +* Fix deleting entire domains diff --git a/beatnik/editrec.php b/beatnik/editrec.php new file mode 100644 index 000000000..0a0488b65 --- /dev/null +++ b/beatnik/editrec.php @@ -0,0 +1,56 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +define('BEATNIK_BASE', dirname(__FILE__)); +require_once BEATNIK_BASE . '/lib/base.php'; +require_once BEATNIK_BASE . '/lib/Forms/EditRecord.php'; + +$vars = Horde_Variables::getDefaultVariables(); +$url = Horde::applicationUrl('editrec.php'); +list($type, $record) = $beatnik_driver->getRecord(Horde_Util::getFormData('id')); + +$form = new EditRecord($vars); + +if ($form->validate($vars)) { + $form->getInfo($vars, $info); + $result = $beatnik_driver->saveRecord($info); + + if (is_a($result, 'PEAR_Error')) { + $notification->push($result->getMessage() . ': ' . $result->getDebugInfo(), 'horde.error'); + } else { + $notification->push('Record data saved.', 'horde.success'); + + // Check to see if this is a new domain + $edit = $vars->get('id'); + if ($info['rectype'] == 'soa' && !$edit) { + // if added a soa redirect to the autogeneration page + $url = Horde_Util::addParameter(Horde::applicationUrl('autogenerate.php'), + array('rectype' => 'soa', 'curdomain' => $info['zonename']), false, false); + } else { + $url = Horde::applicationUrl('viewzone.php'); + } + } + + header('Location: ' . $url); + exit; + +} elseif (!$form->isSubmitted() && $record) { + foreach ($record as $field => $value) { + $vars->set($field, $value); + } +} + +$title = $form->getTitle(); +require BEATNIK_TEMPLATES . '/common-header.inc'; +require BEATNIK_TEMPLATES . '/menu.inc'; + +$form->renderActive(null, null, $url, 'post'); + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/beatnik/index.php b/beatnik/index.php new file mode 100644 index 000000000..7464ecfb9 --- /dev/null +++ b/beatnik/index.php @@ -0,0 +1,21 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +define('BEATNIK_BASE', dirname(__FILE__)); +$beatnik_configured = (is_readable(BEATNIK_BASE . '/config/conf.php') && + is_readable(BEATNIK_BASE . '/config/prefs.php')); + +if (!$beatnik_configured) { + require BEATNIK_BASE . '/../lib/Test.php'; + Horde_Test::configFilesMissing('Beatnik', BEATNIK_BASE, + array('conf.php', 'prefs.php')); +} + +require BEATNIK_BASE . '/listzones.php'; diff --git a/beatnik/js/beatnik.js b/beatnik/js/beatnik.js new file mode 100644 index 000000000..051114ba4 --- /dev/null +++ b/beatnik/js/beatnik.js @@ -0,0 +1,3 @@ + +var loading;function domainSubmit(clear) +{if(document.menu.domainSelector[document.menu.domainSelector.selectedIndex].value!=''){if((loading==null)||(clear!=null)){loading=true;document.menu.submit();}}} \ No newline at end of file diff --git a/beatnik/js/src/beatnik.js b/beatnik/js/src/beatnik.js new file mode 100644 index 000000000..cb7221a15 --- /dev/null +++ b/beatnik/js/src/beatnik.js @@ -0,0 +1,14 @@ +// $Horde: beatnik/js/src/beatnik.js,v 1.3 2008/08/20 08:56:53 duck Exp $ + +// Menu Domain submit +var loading; +function domainSubmit(clear) +{ + if (document.menu.domainSelector[document.menu.domainSelector.selectedIndex].value != '') { + if ((loading == null) || (clear != null)) { + loading = true; + document.menu.submit(); + } + } +} + diff --git a/beatnik/lib/Application.php b/beatnik/lib/Application.php new file mode 100644 index 000000000..0cffe8eff --- /dev/null +++ b/beatnik/lib/Application.php @@ -0,0 +1,46 @@ +getDomains() as $domain) { + $perms['tree']['beatnik']['domains'][$domain['zonename']] = false; + $perms['title']['beatnik:domains:' . $domain['zonename']] = $domain['zonename']; + } + + return $perms; + } + + /** + * Generate the menu to use on the prefs page. + * + * @return Horde_Menu A Horde_Menu object. + */ + public function prefsMenu() + { + return Beatnik::getMenu(); + } +} diff --git a/beatnik/lib/Beatnik.php b/beatnik/lib/Beatnik.php new file mode 100644 index 000000000..b60325ae0 --- /dev/null +++ b/beatnik/lib/Beatnik.php @@ -0,0 +1,590 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Ben Klang + * @package Beatnik + */ +class Beatnik { + + /** + * Build Beatnik's list of menu items. + */ + function getMenu($returnType = 'object') + { + // We are editing rather than adding if an ID was passed + $editing = Horde_Util::getFormData('id'); + $editing = !empty($editing); + + $hordeImgDir = $GLOBALS['registry']->getImageDir('horde'); + $beatnikImgDir = $GLOBALS['registry']->getImageDir(); + $menu = new Horde_Menu(); + + $menu->add(Horde::applicationUrl('listzones.php'), _('List Domains'), 'website.png', $hordeImgDir); + if (!empty($_SESSION['beatnik']['curdomain'])) { + $menu->add(Horde_Util::addParameter(Horde::applicationUrl('editrec.php'), 'curdomain', $_SESSION['beatnik']['curdomain']['zonename']), ($editing) ? _("Edit Record") : _("Add Record"), 'edit.png', $hordeImgDir); + } else { + $menu->add(Horde::applicationUrl('editrec.php?rectype=soa'), _("Add Zone"), 'edit.png', $hordeImgDir); + } + + $url = Horde_Util::addParameter(Horde::selfUrl(true), array('expertmode' => 'toggle')); + $menu->add($url, _('Expert Mode'), 'hide_panel.png', $hordeImgDir, '', null, ($_SESSION['beatnik']['expertmode']) ? 'current' : ''); + + if (count(Beatnik::needCommit())) { + $url = Horde_Util::addParameter(Horde::applicationUrl('commit.php'), array('domain' => 'all')); + $menu->add($url, _('Commit All'), 'commit-all.png', $beatnikImgDir); + } + + if ($returnType == 'object') { + return $menu; + } else { + return $menu->render(); + } + } + + /** + * Get possible records + * + * The keys of this array are the IDs of the record type. The values + * are a human friendly description of the record type. + */ + function getRecTypes() + { + $records = array( + 'soa' => _("SOA (Start of Authority)"), + 'ns' => _("NS (Name Server)"), + 'a' => _("A (Address)"), + 'ptr' => _("PTR (Reverse DNS)"), + 'cname' => _("CNAME (Alias)"), + 'mx' => _("MX (Mail eXchange)"), + 'srv' => _("SRV (Service Record)"), + 'txt' => _("TXT (Text Record)"), + ); + + return array_merge($records, $GLOBALS['beatnik_driver']->getRecDriverTypes()); + } + + /** + */ + function getRecFields($recordtype) + { + // Record Format: + // $recset is an array of fields. The field IDs are the keys. + // Each field is an array with the following keys: + // 'name': The short name of the field. This key is also used + // to reference the help system. + // 'description': Long description of the field + // 'type': Field type. Choose from any available from Horde_Form + // 'maxlength': Maximum field length. 0 is unlimited + // 'required': If true, the field will be required by the form + // 'infoset': one of 'basic' or 'advanced'. This is used to help keep + // the forms simple for non-power-users. If 'required' is true and + // 'infoset' is false then 'default' MUST be specified + // 'default': the default value of the field. + // 'index': Crude sort ordering. Lower means show higher in the group + + // Attempt to return cached results. + static $recset = array(); + + if (isset($recset[$recordtype])) { + return $recset[$recordtype]; + } + $recset[$recordtype] = array(); + + $recset[$recordtype]['id'] = array( + 'name' => _("UID"), + 'description' => _("Unique Identifier (Used as Record ID)"), + 'type' => 'hidden', + 'maxlength' => 0, + 'required' => false, // Empty for "new" entries + 'infoset' => 'basic', + 'index' => 0, + ); + + switch (strtolower($recordtype)) { + case 'soa': + $recset[$recordtype]['zonename'] = array( + 'name' => _("Domain Name"), + 'description' => _("Zone Domain Name"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['zonens'] = array( + 'name' => _("Primary Nameserver"), + 'description' => _("Primary nameserver for this zone"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + $recset[$recordtype]['zonecontact'] = array( + 'name' => _("Zone Contact"), + 'description' => _("Contact e-mail address for this zone"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + $recset[$recordtype]['serial'] = array( + 'name' => _("Serial"), + 'description' => _("Zone Serial Number"), + 'type' => 'int', + 'default' => date('Ymd'). '00', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 3, + ); + $recset[$recordtype]['refresh'] = array( + 'name' => 'Refresh', + 'description' => _("Zone Refresh"), + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 4, + ); + $recset[$recordtype]['retry'] = array( + 'name' => _("Retry"), + 'description' => _("Zone Retry"), + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 5, + ); + $recset[$recordtype]['expire'] = array( + 'name' => _("Expiration"), + 'description' => _("Zone Expiry"), + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 6, + ); + $recset[$recordtype]['minimum'] = array( + 'name' => _("Minimum"), + 'description' => _("Zone Minimum"), + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 7, + ); + break; + + case 'a': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Hostname"), + 'description' => _("Short hostname for this record"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['ipaddr'] = array( + 'name' => _("IP Address"), + 'description' => _("IPv4 Network Address"), + 'type' => 'ipaddress', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + + case 'ptr': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Hostname"), + 'description' => _("IP in Reverse notation (.in-addr.arpa)"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['pointer'] = array( + 'name' => _("Hostname Target"), + 'description' => _("Hostname for Reverse DNS"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + + case 'mx': + $recset[$recordtype]['pointer'] = array( + 'name' => _("Hostname Target"), + 'description' => _("Hostname of Mail eXchanger"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['pref'] = array( + 'name' => _("Preference"), + 'description' => _("MX Preference (lower is more preferred)"), + 'type' => 'int', + 'default' => 0, + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + + case 'cname': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Hostname"), + 'description' => _("Short hostname for this record"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['pointer'] = array( + 'name' => _("Hostname Target"), + 'description' => _("Hostname for CNAME alias"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + + case 'ns': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Domain Name"), + 'description' => _("Short sub-domain for NS record (leave blank unless creating a subdomain)"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => false, + // If we have a current domain name, use it for the default val + 'default' => @$GLOBALS['curdomain']['zonename'], + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['pointer'] = array( + 'name' => _("Hostname Target"), + 'description' => _("Hostname of Authoritative Name Server"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + + case 'srv': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Hostname"), + 'description' => _("Short hostname for this record"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['pointer'] = array( + 'name' => _("Hostname Target"), + 'description' => _("Hostname for DNS Service Record"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + $recset[$recordtype]['priority'] = array( + 'name' => _("SRV Priority"), + 'description' => _("DNS Service Record Priority"), + 'type' => 'int', + 'default' => 0, + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 3, + ); + $recset[$recordtype]['weight'] = array( + 'name' => _("SRV Weight"), + 'description' => _("DNS Service Record Weight"), + 'type' => 'int', + 'default' => 0, + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 4, + ); + $recset[$recordtype]['port'] = array( + 'name' => _("SRV Port"), + 'description' => _("DNS Service Record Port Number"), + 'type' => 'int', + 'default' => 0, + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 5, + ); + break; + + case 'txt': + $recset[$recordtype]['hostname'] = array( + 'name' => _("Hostname"), + 'description' => _("Short hostname for this record"), + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset[$recordtype]['text'] = array( + 'name' => 'Text', + 'description' => _("String payload for DNS TXT"), + 'type' => 'text', + 'maxlength' => 256, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + } + + $recset[$recordtype]['ttl'] = array( + 'name' => _("TTL"), + 'description' => _("Record Time-To-Live (seconds)"), + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 100, + 'default' => $GLOBALS['prefs']->getValue('default_ttl') + ); + + $recset[$recordtype] = array_merge($recset[$recordtype], $GLOBALS['beatnik_driver']->getRecDriverFields($recordtype)); + uasort($recset[$recordtype], array('Beatnik', 'fieldSort')); + + return $recset[$recordtype]; + } + + /** + * Check or set a flag to show that a domain has outstanding changes that + * need to be committed. + * + * @param optional string $domain Domain to check whether a commit is + * necessary + * @param optional boolean $needcommit true adds the domain to the list + * that needs committing; false removes + * the domain from the list + * + * @return mixed Array of domains needing committing if no arguments are + * passed. + * Boolean if only a $domain is passed: True if $domain has + * outstanding changes, false if not. + * Mixed if both $domain and $needcommit are passed. True + * on success, PEAR::Error on error. + */ + function needCommit($domain = null, $needcommit = null) + { + // Make sure we have a valid array with which to work + if (!isset($_SESSION['beatnik']['needcommit'])) { + $_SESSION['beatnik']['needcommit'] = array(); + } + + if ($domain === null && $needcommit === null) { + // Return the stored list of domains needing changes committed. + return array_keys($_SESSION['beatnik']['needcommit']); + } elseif ($domain !== null && $needcommit === null) { + // Check if domain need committing + return isset($_SESSION['beatnik']['needcommit'][$domain]); + } elseif ($domain !== null && is_bool($needcommit)) { + // Flag domain for committing + if ($needcommit) { + if(!isset($_SESSION['beatnik']['needcommit'][$domain])) { + $_SESSION['beatnik']['needcommit'][$domain] = true; + } + } else { + if (isset($_SESSION['beatnik']['needcommit'][$domain])) { + unset($_SESSION['beatnik']['needcommit'][$domain]); + } + } + return true; + } else { + // Somebody sent something they should not have... + return PEAR::raiseError(_("Unable to determine if domain needs committing: invalid parameter.")); + } + } + + /** + * Checks for the given permissions for the current user on the given + * permissions node. Optionally check for the requested permssion for a + * given number of steps up the tree. + * + * @param string $permname Name of the permission to check + * + * @param optional int $permmask Bitfield of permissions to check for + * + * @param options int $numparents Check for the same permissions this + * many levels up the tree + * + * @return boolean True if the user has permission, False if not + */ + function hasPermission($permname, $permmask = null, $numparents = 0) + { + if (Horde_Auth::isAdmin()) { + return true; + } + + $perms = Perms::singleton(); + if ($permmask === null) { + $permmask = PERMS_SHOW|PERMS_READ; + } + + # Default deny all permissions + $user = 0; + $superadmin = 0; + + $superadmin = $perms->hasPermission('beatnik:domains', Horde_Auth::getAuth(), $permmask); + + while ($numparents >= 0) { + $tmpuser = $perms->hasPermission($permname, Horde_Auth::getAuth(), $permmask); + + $user = $user | $tmpuser; + if ($numparents > 0) { + $pos = strrpos($permname, ':'); + if ($pos) { + $permname = substr($permname, 0, $pos); + } + } + $numparents--; + } + return (($superadmin | $user) & $permmask); + } + + /** + * Autogenerate a set of records from a template defined in + * config/autogenerate.php + * + * @param object $vars Horde_Variables object from Autogenerate form + * + * @return mixed true on success, PEAR::Error on failure + */ + function autogenerate(&$vars) + { + + require BEATNIK_BASE . '/config/autogenerate.php'; + $template = $templates[$vars->get('template')]; + $zonedata = $GLOBALS['beatnik_driver']->getRecords($_SESSION['beatnik']['curdomain']['zonename']); + if (is_a($zonedata, 'PEAR_Error')) { + return $zonedata; + } + + foreach ($template['types'] as $rectype => $definitions) { + // Only attempt to delete records if the type is already defined + if (isset($zonedata[$rectype])) { + // Check for collisions and handle as requested + switch($definitions['replace']) { + case 'all': + foreach ($zonedata[$rectype] as $record) { + $result = $GLOBALS['beatnik_driver']->deleteRecord($record); + if (is_a($result, 'PEAR_Error')) { + $GLOBALS['notification']->push($result); + } + } + break; + + case 'match': + foreach ($zonedata[$rectype] as $record) { + // Check every record in the template to see if the + // hostname matches + foreach ($definitions['records'] as $Trecord) { + if ($record['hostname'] == $Trecord['hostname']) { + $result = $GLOBALS['beatnik_driver']->deleteRecord($record); + if (is_a($result, 'PEAR_Error')) { + $GLOBALS['notification']->push($result); + } + } + } + } + break; + + #case 'none': + #default: + } + } + + $defaults = array('rectype' => $rectype, + 'zonename'=> $_SESSION['beatnik']['curdomain']['zonename']); + foreach ($definitions['records'] as $info) { + if ($GLOBALS['beatnik_driver']->recordExists($info, $rectype)) { + $GLOBALS['notification']->push(_("Skipping existing identical record")); + continue; + } + $result = $GLOBALS['beatnik_driver']->saveRecord(array_merge($defaults, $info)); + if (is_a($result, 'PEAR_Error')) { + $GLOBALS['notification']->push($result->getMessage() . ': ' . $result->getDebugInfo(), 'horde.error'); + } else { + $GLOBALS['notification']->push(sprintf(_('Record added: %s/%s'), $rectype, $info['hostname']), 'horde.success'); + } + } + } + return true; + } + + /** + * Increments a domain serial number. + * + * @param int $serial Serial number to be incremented + * + * @return int Incremented serial number + */ + function incrementSerial($serial) + { + // Create a serial number of the ad-hoc standard YYYYMMDDNN + // where YYYYMMDD is the year/month/day of the last update to this + // odmain and NN is an incrementer to handle multiple updates in a + // given day. + $newserial = (int) (date('Ymd') . '00'); + if ($serial < $newserial) { + return $newserial; + } else { + return ++$serial; + } + } + + /** + * Callback for usort to make field data print in a friendly order + * + * @param mixed $a First sort variable + * @param mixed $b Second sort variable + * + * @return int -1, 0, 1 based on relative sort order + */ + function fieldSort($a, $b) + { + if ($a['index'] < $b['index']) { + return -1; + } elseif ($a['index'] > $b['index']) { + return 1; + } else { + return 0; + } + } + +} diff --git a/beatnik/lib/Driver.php b/beatnik/lib/Driver.php new file mode 100644 index 000000000..62e4e09d4 --- /dev/null +++ b/beatnik/lib/Driver.php @@ -0,0 +1,330 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Ben Klang + * @version $Revision: 1.21 $ + * @package Beatnik + */ +class Beatnik_Driver { + + /** + * Hash containing connection parameters. + * + * @var array $_params + */ + var $_params = array(); + + function Beatnik_Driver($params = array()) + { + $this->_params = $params; + } + + /** + * Get any record types available specifically in this driver. + * + * @return array Records available only to this driver + */ + function getRecDriverTypes() + { + return array(); + } + + + /** + * Get any fields available specifically in this driver by record type. + * + * @param string $type Record type for which fields should be returned + * + * @return array Fields specific to this driver + */ + function getRecDriverFields($type) { + + return array(); + } + + /** + * Gets domains from driver for which the user has the specified + * permission. + * + * @param int $perms Permissions filter for domain result set + * + * @return array Possibly empty array of domain information + */ + function getDomains($perms = PERMS_SHOW) + { + $domains = $this->_getDomains(); + if (is_a($domains, 'PEAR_Error')) { + $GLOBALS['notification']->push($domains->getMessage() . ': ' . $domains->getDebugInfo(), 'horde.warning'); + return array(); + } + + if (!Horde_Auth::isAdmin() && + !$GLOBALS['perms']->hasPermission('beatnik:domains', Horde_Auth::getAuth(), $perms)) { + foreach ($domains as $id => $domain) { + if (!$GLOBALS['perms']->hasPermission('beatnik:domains:' . $domain['zonename'], Horde_Auth::getAuth(), $perms)) { + unset($domains[$id]); + } + } + } + + if (empty($domains)) { + $GLOBALS['notification']->push(_("You are not permitted to view any domains."), 'horde.warning'); + return array(); + } + + // Sort the resulting list by domain name + // TODO: Allow sorting by other columns + require_once 'Horde/Array.php'; + Horde_Array::arraySort($domains, 'zonename'); + + return $domains; + } + + /** + * Return SOA for a single domain + * + * @param string $domain Domain for which to return SOA information + * + * @return mixed Array of SOA information for domain or PEAR_Error + * on failure. + */ + function getDomain($domainname) + { + $domains = $this->getDomains(PERMS_SHOW | PERMS_READ); + + foreach ($domains as $domain) { + if ($domain['zonename'] == $domainname) { + return $domain; + } + } + return PEAR::raiseError(sprintf(_("Unable to read requested domain %s"), $domainname)); + } + + /** + * Gets a specific record from the backend. This method may be overridden + * in specific backend drivers if there is a performance or other benefit + * for doing so. + * + * @return array Array of type and record information + */ + function getRecord($id) + { + if ($id === null) { + return false; + } + + $zonedata = $this->getRecords($_SESSION['beatnik']['curdomain']['zonename']); + // Search for the requested record id + foreach ($zonedata as $type => $records) { + foreach ($records as $record) { + if ($record['id'] == $id) { + // Found the record we're looking for + break; + } + $type = false; + $record = false; + } + if ($record) { + // Record found in nested loop. Break out of this one too. + break; + } + } + + if (!$record) { + // We may be editing the SOA. See if it matches + $record = $this->getDomain($_SESSION['beatnik']['curdomain']['zonename']); + if ($record['id'] == $id) { + $type = 'soa'; + } else { + $GLOBALS['notification']->push(_("Unable to locate requested record."), 'horde.error'); + $type = false; + $record = false; + } + } + + return array($type, $record); + } + + /** + * Try to determinate if the autogenerated record + * already exits. This method may be overridden in the backend driver + * for performance or other reasons. + * + * @param array $record record to check + * + * @return boolean if records exits or or not + */ + function recordExists($record, $rectype) + { + $zonedata = $this->getRecords($_SESSION['beatnik']['curdomain']['zonename']); + if (is_a($zonedata, 'PEAR_Error')) { + $notification->push($zonedata, 'horde.error'); + header('Location:' . Horde::applicationUrl('listzones.php')); + exit; + } + + if (isset($zonedata[$rectype])) { + foreach ($zonedata[$rectype] as $row) { + // Prune empty values from $row to aid in comparison + foreach ($row as $key => $value) { + if (empty($value)) { + unset($row[$key]); + } + } + + $same_values = array_intersect_assoc($row, $record); + if (count($same_values) == count($record)) { + return true; + } + } + } + + return false; + } + + /** + * Saves a record fo the configured driver and checks/sets needCommit() + * Also first checks to ensure permission to save record is available. + * + * @param array $info Data to be passed to backend driver for storage + * + * @return mixed True on success, PEAR::Error on error + */ + function saveRecord(&$info) + { + // Check to see if this is a new domain + if ($info['rectype'] == 'soa' && $info['zonename'] != $_SESSION['beatnik']['curdomain']['zonename']) { + // Make sure the user has permissions to add domains + if (!Beatnik::hasPermission('beatnik:domains', PERMS_EDIT)) { + return PEAR::raiseError(_('You do not have permission to create new domains.')); + } + + // Create a dummy old domain for comparison purposes + $oldsoa['serial'] = 0; + + } else { + $oldsoa =& $_SESSION['beatnik']['curdomain']; + + // Check for permissions to edit the record in question + if ($info['rectype'] == 'soa') { + $node = 'beatnik:domains:' . $info['zonename']; + if (!Beatnik::hasPermission($node, PERMS_EDIT, 1)) { + return PEAR::raiseError(_('You do not have permssion to edit the SOA of this zone.')); + } + } else { + $node = 'beatnik:domains:' . $_SESSION['beatnik']['curdomain']['zonename'] . ':' . $info['id']; + if (!Beatnik::hasPermission($node, PERMS_EDIT, 2)) { + return PEAR::raiseError(_('You do not have permssion to edit this record.')); + } + } + } + + // Save the changes to the backend + // FIXME: Modify saveRecord() to return the new (possibly changed) ID of the + // record and then use that ID to update permissions + $return = $this->_saveRecord($info); + + $oldsoa =& $_SESSION['beatnik']['curdomain']; + + if (is_a($return, 'PEAR_Error')) { + return $return; + } else { + if ($info['rectype'] == 'soa' && + ($oldsoa['serial'] < $info['serial'])) { + // Clear the commit flag (if set) + Beatnik::needCommit($oldsoa['zonename'], false); + } else { + Beatnik::needCommit($oldsoa['zonename'], true); + } + } + + // Check to see if an SOA was just added or updated. + // If so, make it the current domain. + if ($info['rectype'] == 'soa') { + $_SESSION['beatnik']['curdomain'] = $this->getDomain($info['zonename']); + } + + return true; + } + + /** + * Delete record from backend + * + * @access private + * + * @param array $info Reference to array of record information for deletion + * + * @return boolean true on success, PEAR::raiseError on error + */ + function deleteRecord(&$info) + { + $return = $this->_deleteRecord($info); + + $oldsoa =& $_SESSION['beatnik']['curdomain']; + + if (is_a($return, 'PEAR_Error')) { + return $return; + } else { + // No need to commit if the whole zone is gone + if ($info['rectype'] != 'soa') { + Beatnik::needCommit($oldsoa['zonename'], true); + } + } + } + + /** + * Attempts to return a concrete Beatnik_Driver instance based on + * $driver. + * + * @param string $driver The type of the concrete Beatnik_Driver subclass + * to return. The class name is based on the storage + * driver ($driver). The code is dynamically + * included. + * + * @param array $params (optional) A hash containing any additional + * configuration or connection parameters a + * subclass might need. + * + * @return mixed The newly created concrete Beatnik_Driver instance, or + * false on an error. + */ + function factory($driver = null, $params = null) + { + if ($driver === null) { + $driver = $GLOBALS['conf']['storage']['driver']; + } + + $driver = basename($driver); + if (empty($driver) || ($driver == 'none')) { + return new Horde_Lock(); + } + + if (is_null($params)) { + // Since we have more than one backend that uses SQL make sure + // all of them have a chance to inherit the site-wide config. + $sqldrivers = array('sql', 'pdnsgsql'); + if (in_array($driver, $sqldrivers)) { + $params = Horde::getDriverConfig('storage', 'sql'); + } else { + $params = Horde::getDriverConfig('storage', $driver); + } + } + + require_once dirname(__FILE__) . '/Driver/' . $driver . '.php'; + $class = 'Beatnik_Driver_' . $driver; + if (class_exists($class)) { + return new $class($params); + } else { + return PEAR::raiseError(_('Driver not found.')); + } + } + +} diff --git a/beatnik/lib/Driver/ldap2dns.php b/beatnik/lib/Driver/ldap2dns.php new file mode 100644 index 000000000..1ca8c6367 --- /dev/null +++ b/beatnik/lib/Driver/ldap2dns.php @@ -0,0 +1,526 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Ben Klang + * @package Beatnik + */ +class Beatnik_Driver_ldap2dns extends Beatnik_Driver +{ + /** + * Handle for the current database connection. + * @var object LDAP $_LDAP + */ + var $_LDAP; + + /** + * Boolean indicating whether or not we're connected to the LDAP + * server. + * @var boolean $_connected + */ + var $_connected = false; + + /** + * Constructs a new Beatnik LDAP driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Beatnik_Driver_ldap2dns($params = array()) + { + parent::Beatnik_Driver($params); + $this->_connect(); + } + + /** + * Get any record types available specifically in this driver. + * + * @return array Records available only to this driver + */ + function getRecDriverTypes() + { + return array( + 'a+ptr' => 'A + PTR', + ); + } + + /** + * Get any fields available specifically in this driver by record type. + * + * @param string $type Record type for which fields should be returned + * + * @return array Fields specific to this driver + */ + function getRecDriverFields($type) { + $recset = array(); + switch($type) { + case 'a+ptr': + $recset['hostname'] = array( + 'name' => 'Hostname', + 'description' => 'Hostname', + 'type' => 'text', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 1, + ); + $recset['cipaddr'] = array( + 'name' => 'IP Address', + 'description' => 'IP Address to be forward and reverse mapped', + 'type' => 'ipaddress', + 'maxlength' => 0, + 'required' => true, + 'infoset' => 'basic', + 'index' => 2, + ); + break; + } + + $recset['timestamp'] = array( + 'name' => 'Timestamp', + 'description' => '"Do Not Issue Before/After" Timestamp', + 'type' => 'int', + 'maxlength' => 0, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 100, + ); + $recset['location'] = array( + 'name' => 'Location', + 'description' => 'Location Restriction', + 'type' => 'text', + 'maxlength' => 2, + 'required' => false, + 'infoset' => 'advanced', + 'index' => 101, + ); + + return $recset; + } + + /** + * Gets all zones for accessible to the user matching the filter + * + * @access private + * + * @return array Array with zone records numerically indexed + */ + function _getDomains() + { + static $zonedata = array(); + if (count($zonedata) > 0) { + # If at least one element is in the array then we should have valid + # cached data. + return $zonedata; + } + + // Record format + // $zonedata = + // zone array ( # numerically indexed + // zonename => zone domain name + // serial => zone SOA serial number + // refresh => zone SOA refresh + // retry => zone SOA retry + // expire => zone SOA expiry + // minimum => zone SOA minimum + // admin => zone contact admin + // zonemaster => SOA master NS + // ) + + $res = ldap_list($this->_LDAP, + $this->_params['basedn'], + "(objectClass=dnszone)"); + + if ($res === false) { + return PEAR::raiseError("Unable to locate any DNS zones " . + "underneath ".$this->_params['basedn']); + } + + $res = ldap_get_entries($this->_LDAP, $res); + + if ($res === false) { + return PEAR::raiseError(sprintf(_("Unable to retrieve data from LDAP results: %s"), @ldap_error($this->_LDAP))); + } + + $fields = Beatnik::getRecFields('soa'); + $i = 0; + # FIXME: Add some way to handle missing zone data + # FIXME: Don't forget to remove error silencers (@whatever) + while ($i < $res['count']) { + $tmp = array(); + + foreach ($fields as $field => $fieldinfo) { + $key = strtolower($this->_getAttrByField($field)); + if ($key === null) { + // This is not a field we are concerned with. Skip it + continue; + } + // Special case for 'dn' as it's not treated as an array + if ($key == 'dn') { + $val = @ldap_explode_dn($res[$i]['dn'], 1); + $tmp[$field] = $val[0]; + continue; + } + @$tmp[$field] = $res[$i][$key][0]; + } + + # Push the zone on the stack + $zonedata[] = $tmp; + + # Next zone, please + $i++; + } + return $zonedata; + } + + /** + * Map LDAP Attributes to application record fields + * + * @access private + * + * @param $field string LDAP Attribute for which a record field should be + * returned + * + * @return string Application record field name + */ + function _getAttrByField($field) + { + $field = strtolower($field); + $fields = array( + 'hostname' => 'dnsdomainname', + 'zonename' => 'dnszonename', # FIXME This will go away for ldap2dns 0.4.x + 'serial' => 'dnsserial', + 'refresh' => 'dnsrefresh', + 'retry' => 'dnsretry', + 'expire' => 'dnsexpire', + 'minimum' => 'dnsminimum', + 'zonecontact' => 'dnsadminmailbox', + 'zonens' => 'dnszonemaster', + 'ttl' => 'dnsttl', + 'timestamp' => 'dnstimestamp', + 'location' => 'dnslocation', + 'ipaddr' => 'dnsipaddr', + 'cipaddr' => 'dnscipaddr', + 'pointer' => 'dnscname', + 'pref' => 'dnspreference', + 'priority' => 'dnssrvpriority', + 'weight' => 'dnssrvweight', + 'port' => 'dnssrvport', + 'text' => 'dnscname', # FIXME THIS WILL CHANGE IN ldap2dns 0.5.0!!! + 'id' => 'dn', + ); + + if (!isset($fields[$field])) { + return null; + } + + return $fields[$field]; + + } + + /** + * Gets all records associated with the given zone + * + * @param string $domain Retrieve records for this domain + * + * @return array Array with zone records + */ + function getRecords($domain) + { + $domain = $this->cleanFilterString($domain); + $dn = $this->_params['dn'].'='.$domain.','.$this->_params['basedn']; + $res = @ldap_list($this->_LDAP, $dn, '(objectClass=dnsrrset)'); + + if ($res === false) { + return PEAR::raiseError("Unable to locate any DNS data for $domain"); + } + + # FIXME Cache these results + $zonedata = array(); + $res = @ldap_get_entries($this->_LDAP, $res); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Internal error: %s"), @ldap_error($this->_LDAP))); + } + + $i = 0; + while ($i < $res['count']) { + $rectype = $res[$i]['dnstype'][0]; + // Special case for A+PTR records + if ($rectype == 'a' && isset($res[$i]['dnscipaddr'])) { + $rectype = 'a+ptr'; + } + if (!isset($zonedata[$rectype])) { + # Initialize this type if it hasn't already been done + $zonedata[$rectype] = array(); + } + $tmp = array(); + foreach (Beatnik::getRecFields($rectype) as $field => $fielddata) { + $key = $this->_getAttrByField($field); + if ($key === null) { + // Not a key we care about + continue; + } + // Special case for 'dn' as it's not treated as an array + if ($key == 'dn') { + $val = @ldap_explode_dn($res[$i]['dn'], 1); + $tmp[$field] = $val[0]; + continue; + } + + // Only the first value is used. All other are ignored. + $tmp[$field] = @$res[$i][$key][0]; + } + # Push the record on the stack + $zonedata[$rectype][] = $tmp; + + # Next entry, please. + $i++; + } + + return $zonedata; + } + + /** + * Delete record from backend + * + * @access private + * + * @param array $info Reference to array of record information for deletion + * + * @return boolean true on success, PEAR::raiseError on error + */ + function _deleteRecord(&$info) + { + // Ensure we have a record ID before continuing + if (!isset($info['id'])) { + return PEAR::raiseError(_("Unable to delete record: No record ID specified.")); + } + + // Attribute used to identify objects + $dnattr = $this->_params['dn']; + + $suffix = $dnattr . '=' . $_SESSION['beatnik']['curdomain']['zonename'] . ',' . $this->_params['basedn']; + if ($info['rectype'] == 'soa') { + // FIXME: Add recursion + return PEAR::raiseError(_("Unsupported recursive delete.")); + + $domain = $this->cleanDNString($info['zonename']); + $dn = $suffix; + } else { + $domain = $this->cleanDNString($_SESSION['beatnik']['curdomain']['zonename']); + // Strip the array fluff and add the attribute + $dn = $dnattr . '=' . $this->cleanDNString($info['id']) . ',' . $suffix; + } + + $res = @ldap_delete($this->_LDAP, $dn); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Unable to delete record. Reason: %s"), @ldap_error($this->_LDAP))); + } + return true; + } + + /** + * Saves a new or edited record to the DNS backend + * + * @access private + * + * @param array $info Array from Horde_Form with record data + * + * @return mixed The new or modified record ID on success; + * PEAR_Error on error + */ + function _saveRecord($info) + { + // Make sure we have a valid record type + $rectype = strtolower($info['rectype']); + $rdata = false; + foreach (Beatnik::getRecTypes() as $rtype => $rdata) { + if ($rectype == $rtype) { + break; + } + $rdata = false; + } + + if (!$rdata) { + return PEAR::raiseError(_("Invalid record type specified.")); + } + + $recfields = Beatnik::getRecFields($rectype); + + $entry = array(); + + if ($rectype == 'a+ptr') { + // Special case for A+PTR Records + $entry['dnstype'] = 'a'; + } else { + $entry['dnstype'] = $rectype; + } + + $id = strtoupper($rectype); + + // Apply each piece of submitted data to the new/updated object + foreach ($recfields as $field => $fdata) { + // Translate the key to an LDAP attribute + $key = $this->_getAttrByField($field); + + if ($key === null || $key == 'dn') { + // Skip the DN or any other key we don't care about + continue; + } + + if (!isset($info[$field]) && isset($fdata['default'])) { + // No value specified. Use the default + $val = $fdata['default']; + } else { + // Only populate the field if there is actual data + if (isset($info[$field]) && strlen($info[$field])) { + $entry[$key] = $info[$field]; + } else { + // $info[$field] was possibly unset + $info[$field] = ''; + // If the record previously had data, we have to send an + // empty array to remove the attribute. However, always + // sending an empty attribute causes PHP to return with + // "Protocol Error". Hence this somewhat expensive check: + if (isset($info['id'])) { + list($type, $record) = $this->getRecord($info['id']); + if ($record && isset($record[$field])) { + $entry[$key] = array(); + } + } + } + } + + if (!isset($entry[$key]) && $fdata['required']) { + // No value available but required field + return PEAR::raiseError(sprintf(_("Missing required field %s to save record."), $fdata['name'])); + } + + // Construct an ID for this object as a tuple of its data. + // This guarantees uniqueness. + $id .= '-'.$this->cleanDNString($info[$field]); + } + + // Create and populate the DN + $key = $this->_params['dn']; + $dn = ''; + // Special case for SOA records. + if ($rectype == 'soa') { + $domain = $this->cleanDNString($info['zonename']); + $entry[$key] = $domain; + $id = $domain; + $dn = $key.'='.$domain; + $suffix = $this->_params['basedn']; + } else { + // Everything else gets full id for DN + $id = $this->cleanDNString($id); + $entry[$key] = $id; + $dn = $key.'='.$id; + // The domain is held in the session + $domain = $this->cleanDNString($_SESSION['beatnik']['curdomain']['zonename']); + // Prepare the DN suffix + $suffix = $key.'='.$domain.','.$this->_params['basedn']; + } + + // Check to see if this is a modification + if (isset($info['id'])) { + // Get the base name of the old object + $oldRDN = $key . '=' . $this->cleanDNString($info['id']); + if ($dn != $oldRDN) { + // We have an old DN but it doesn't match the new DN. + // Need to rename the old object + if ($rectype == 'soa') { + return PEAR::raiseError(_("Unsupported operation: cannot rename a domain.")); + } + $res = @ldap_rename($this->_LDAP, $oldRDN . ',' . $suffix, + $dn, $suffix, true); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Unable to rename old object. Reason: %s"), @ldap_error($this->_LDAP))); + } + } + + // Finish appending the DN suffix information + $dn .= ',' . $suffix; + + // Modify the existing record + $res = @ldap_mod_replace($this->_LDAP, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Unable to modify record. Reason: %s"), @ldap_error($this->_LDAP))); + } + + } else { + // Must be a new record + // Append the suffix to the DN to make it fully qualified + $dn .= ',' . $suffix; + // Create the necessary objectClass definitions + $entry['objectclass'] = array(); + $entry['objectclass'][] = 'top'; + $entry['objectclass'][] = 'dnszone'; + if ($rectype != 'soa') { + // An objectclass to hold the non-SOA record information + $entry['objectclass'][] = 'dnsrrset'; + } + $res = @ldap_add($this->_LDAP, $dn, $entry); + if ($res === false) { + return PEAR::raiseError(sprintf(_("Unable to add record to LDAP. Reason: %s"), @ldap_error($this->_LDAP))); + } + } + + return $id; + } + + function cleanFilterString($string) { + return preg_replace( + array('/\*/', '/\(/', '/\)/', '/\x00/'), + array('\2a', '\28', '\29', '\00'), + $string + ); + } + + function cleanDNString($string) { + return preg_replace( + array('/=/', '/,/', '/\+/'), + array('-', '~', ''), + $string); + } + + /** + * Attempts to open a connection to the LDAP server. + * + * @access private + * + * @return boolean True on success; exits (Horde::fatal()) on error. + * + * @access private + */ + function _connect() + { + if (!$this->_connected) { + Horde::assertDriverConfig($this->_params, 'storage', + array('hostspec', 'basedn', 'binddn', 'password', 'dn')); + + $port = (isset($this->_params['port'])) ? + $this->_params['port'] : 389; + + $this->_LDAP = ldap_connect($this->_params['hostspec'], $port); + if (!$this->_LDAP) { + Horde::fatal("Unable to connect to LDAP server $hostname on $port", __FILE__, __LINE__); + } + $res = ldap_set_option($this->_LDAP, LDAP_OPT_PROTOCOL_VERSION, $this->_params['version']); + if ($res === false) { + return PEAR::raiseError("Unable to set LDAP protocol version"); + } + $res = ldap_bind($this->_LDAP, $this->_params['binddn'], $this->_params['password']); + if ($res === false) { + return PEAR::raiseError("Unable to bind to the LDAP server. Check authentication credentials."); + } + + $this->_connected = true; + } + return true; + } +} diff --git a/beatnik/lib/Driver/pdnsgsql.php b/beatnik/lib/Driver/pdnsgsql.php new file mode 100644 index 000000000..0fb90db9a --- /dev/null +++ b/beatnik/lib/Driver/pdnsgsql.php @@ -0,0 +1,518 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Ben Klang + * @package Beatnik + */ + +class Beatnik_Driver_pdnsgsql extends Beatnik_Driver +{ + /** + * Hash containing connection parameters. + * + * @var array + */ + var $_params = array(); + + /** + * Handle for the current database connection. + * + * @var DB + */ + var $_db; + + /** + * Handle for the current database connection, used for writing. Defaults + * to the same handle as $_db if a separate write database is not required. + * + * @var DB + */ + var $_write_db; + + /** + * Boolean indicating whether or not we're connected to the SQL server. + * + * @var boolean + */ + var $_connected = false; + + /** + * Constructs a new Beatnik DB driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Beatnik_Driver_pdnsgsql($params = array()) + { + parent::Beatnik_Driver($params); + } + + /** + * Gets all zones + * + * @access private + * + * @return array Array with zone records numerically indexed + */ + function _getDomains() + { + if (is_a(($result = $this->_connect()), 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal database error. Details have been logged for the administrator.")); + } + + $query = 'SELECT d.id, d.name AS name, r.content AS content, ' . + 'r.ttl AS ttl FROM ' . $this->_params['domains_table'] . + ' AS d JOIN ' . $this->_params['records_table'] . ' AS r ON ' . + 'r.domain_id = d.id WHERE r.type = \'SOA\''; + Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::_getDomains(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $domainlist = $this->_db->getAll($query, null, DB_FETCHMODE_ASSOC); + if (is_a($domainlist, 'PEAR_Error')) { + Horde::logMessage($domainlist, __FILE__, __LINE__, PEAR_LOG_ERR); + return array(); + } + + $results = array(); + foreach ($domainlist as $info) { + $soa = explode(' ', $info['content']); + if (count($soa) != 7) { + Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), __FILE__, __LINE__, PEAR_LOG_WARNING); + continue; + } + + $d = array(); + $d['id'] = $info['id']; + $d['zonename'] = $info['name']; + $d['zonemaster'] = $d['zonens'] = $soa[0]; + $d['admin'] = $d['zonecontact'] = $soa[1]; + $d['serial'] = $soa[2]; + $d['refresh'] = $soa[3]; + $d['retry'] = $soa[4]; + $d['expire'] = $soa[5]; + $d['minimum'] = $soa[6]; + $results[] = $d; + } + + return $results; + } + + /** + * Return SOA for a single domain + * + * @param string $domain Domain for which to return SOA information + * + * @return array Domain SOA + */ + function getDomain($domainname) + { + if (is_a(($result = $this->_connect()), 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal database error. Details have been logged for the administrator.")); + } + + $query = 'SELECT d.id AS id, d.name AS name, r.content AS content, ' . + 'r.ttl AS ttl FROM ' . $this->_params['domains_table'] . + ' AS d JOIN ' . $this->_params['records_table'] . ' AS r ON ' . + 'r.domain_id = d.id WHERE r.type = \'SOA\' AND d.name = ?'; + $values = array($domainname); + Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::getDomain(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $result = $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("An error occurred while searching the database. Details have been logged for the administrator."), __FILE__, __LINE__, PEAR_LOG_ERR); + } + + if (count($result) != 1) { + return PEAR::raiseError(_("Too many domains matched that name. Contact your administrator.")); + } + + $info = $result[0]; + + $soa = explode(' ', $info['content']); + if (count($soa) != 7) { + Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), __FILE__, __LINE__, PEAR_LOG_WARN); + return PEAR::raiseError(_("Corrupt SOA found for zone. Contact your administrator."), __FILE__, __LINE__, PEAR_LOG_ERR); + } + + $ret = array(); + $ret['id'] = $info['id']; + $ret['zonename'] = $info['name']; + $ret['zonemaster'] = $soa[0]; + $ret['admin'] = $soa[1]; + $ret['serial'] = $soa[2]; + $ret['refresh'] = $soa[3]; + $ret['retry'] = $soa[4]; + $ret['expire'] = $soa[5]; + $ret['minimum'] = $soa[6]; + + return $ret; + } + + /** + * Gets all records associated with the given zone + * + * @param string $domain Retrieve records for this domain + * + * @return array Array with zone records + */ + function getRecords($domain) + { + if (is_a(($result = $this->_connect()), 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal database error. Details have been logged for the administrator.")); + } + + $zonedata = array(); + + $query = 'SELECT d.id AS domain_id, r.id AS id, d.name AS domain, ' . + 'r.name AS name, r.type AS type, r.content AS content, ' . + 'r.ttl AS ttl, r.prio AS prio FROM ' . + $this->_params['domains_table'] . ' AS d JOIN ' . + $this->_params['records_table'] . ' AS r ON ' . + 'd.id = r.domain_id AND d.name = ?'; + $values = array($domain); + + Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::getRecords(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("An error occurred while searching the database. Details have been logged for the administrator."), __FILE__, __LINE__, PEAR_LOG_ERR); + } + + foreach ($result as $rec) { + $type = strtolower($rec['type']); + if (!isset($zonedata[$type])) { + $zonedata[$type] = array(); + } + + $tmp = array(); + $tmp['id'] = $rec['id']; + $tmp['ttl'] = $rec['ttl']; + switch($type) { + case 'soa': + $soa = explode(' ', $rec['content']); + if (count($soa) != 7) { + Horde::logMessage(sprintf('Invalid SOA found for %s, skipping.', $info['name']), __FILE__, __LINE__, PEAR_LOG_WARNING); + } + + $tmp['zonename'] = $rec['name']; + $tmp['zonens'] = $soa[0]; + $tmp['zonecontact'] = $soa[1]; + $tmp['serial'] = $soa[2]; + $tmp['refresh'] = $soa[3]; + $tmp['retry'] = $soa[4]; + $tmp['expire'] = $soa[5]; + $tmp['minimum'] = $soa[6]; + break; + + case 'a': + $tmp['hostname'] = $rec['name']; + $tmp['ipaddr'] = $rec['content']; + break; + + case 'ptr': + $tmp['hostname'] = $rec['name']; + $tmp['pointer'] = $rec['content']; + break; + + case 'mx': + $tmp['pointer'] = $rec['content']; + $tmp['pref'] = $rec['prio']; + break; + + case 'cname': + $tmp['hostname'] = $rec['name']; + $tmp['pointer'] = $rec['content']; + break; + + case 'ns': + $tmp['hostname'] = $rec['name']; + $tmp['pointer'] = $rec['content']; + break; + + case 'srv': + $srv = preg_split('/\s+/', trim($rec['content'])); + if (count($srv) != 3) { + Horde::logMessage(sprintf('Invalid SRV data found for %s, skipping.', $rec['name']), __FILE__, __LINE__, PEAR_LOG_WARNING); + continue; + } + $tmp['hostname'] = $rec['name']; + $tmp['weight'] = $srv[0]; + $tmp['port'] = $srv[1]; + $tmp['pointer'] = $srv[2]; + $tmp['priority'] = $rec['prio']; + break; + + case 'txt': + $tmp['hostname'] = $rec['name']; + $tmp['text'] = $rec['content']; + break; + } + + $zonedata[$type][] = $tmp; + } + + return $zonedata; + } + + /** + * Saves a new or edited record to the DNS backend + * + * @access private + * + * @param array $info Array of record data + * + * @return boolean true on success, PEAR::raiseError on error + */ + function _saveRecord($info) + { + if (is_a(($result = $this->_connect()), 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal database error. Details have been logged for the administrator.")); + } + + $change_date = time(); + $domain_id = $_SESSION['beatnik']['curdomain']['id']; + + switch($info['rectype']) { + case 'soa': + if (empty($info['refresh'])) { + // 24 hours + $info['refresh'] = 86400; + } + if (empty($info['retry'])) { + // 2 hours + $info['retry'] = 7200; + } + if (empty($info['expire'])) { + // 1000 hours + $info['expire'] = 3600000; + } + if (empty($info['minimum'])) { + // 2 days + $info['miniumum'] = 172800; + } + + $name = $info['zonename']; + $type = 'SOA'; + $content = $info['zonens'] . ' ' . $info['zonecontact'] . ' ' . + $info['serial'] . ' ' . $info['refresh'] . ' ' . + $info['retry'] . ' ' . $info['expire'] . ' ' . + $info['minimum']; + $ttl = $info['ttl']; + $prio = null; + break; + + case 'a': + $name = $info['hostname']; + $type = 'A'; + $content = $info['ipaddr']; + $ttl = $info['ttl']; + $prio = null; + break; + + case 'ptr': + $name = $info['hostname']; + $type = 'PTR'; + $content = $info['pointer']; + $ttl = $info['ttl']; + $prio = null; + break; + + case 'mx': + $name = $_SESSION['beatnik']['curdomain']['zonename']; + $type = 'MX'; + $content = $info['pointer']; + $ttl = $info['ttl']; + $prio = $info['pref']; + break; + + case 'cname': + $name = $info['hostname']; + $type = 'CNAME'; + $content = $info['pointer']; + $ttl = $info['ttl']; + $prio = null; + break; + + case 'ns': + $name = $info['hostname']; + $type = 'NS'; + $content = $info['pointer']; + $ttl = $info['ttl']; + $prio = null; + break; + + case 'srv': + $name = $info['hostname']; + $type = 'SRV'; + $content = $info['weight'] . ' ' . $info['port'] . ' ' . + $info['pointer']; + $ttl = $info['ttl']; + $prio = $info['priority']; + break; + + case 'txt': + $name = $info['hostname']; + $type = 'TXT'; + $content = $info['text']; + $ttl = $info['ttl']; + $prio = null; + break; + } + + if (!empty($info['id'])) { + $query = 'UPDATE ' . $this->_params['records_table'] . ' SET ' . + 'name = ?, type = ?, content = ?, ttl = ?, ' . + 'prio = ' . (empty($prio) ? 'NULL' : $prio) . ', ' . + 'change_date = ? WHERE id = ?'; + $values = array($name, $type, $content, $ttl); + if (!empty($prio)) { + $values[] = $prio; + } + $values[] = $change_date; + $values[] = $info['id']; + } else { + $query = 'INSERT INTO ' . $this->_params['records_table'] . ' ' . + '(domain_id, name, type, content, ttl, prio, ' . + 'change_date) VALUES (?, ?, ?, ?, ?, ' . + (empty($prio) ? 'NULL' : '?') . ', ?)'; + $values = array($domain_id, $name, $type, $content, $ttl); + if (!empty($prio)) { + $values[] = $prio; + } + $values[] = $change_date; + } + + Horde::logMessage('SQL Query by Beatnik_Driver_pdnsgsql::_saveRecord(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $result = $this->_write_db->query($query, $values); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /** + * Delete record from backend + * + * @access private + * + * @param array $data Reference to array of record data to be deleted + * + * @return boolean true on success, PEAR::Error on error + */ + function _deleteRecord($data) + { + if (is_a(($result = $this->_connect()), 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal database error. Details have been logged for the administrator.")); + } + + return PEAR::raiseError(_("Not implemented.")); + } + + /** + * Attempts to open a persistent connection to the SQL server. + * + * @access private + * + * @return boolean True on success; exits (Horde::fatal()) on error. + */ + function _connect() + { + if ($this->_connected) { + return true; + } + + $result = Horde_Util::assertDriverConfig($this->_params, array('phptype'), + 'PowerDNS Generic SQL', + array('driver' => 'pdnsgsql')); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR); + return $result; + } + + if (!isset($this->_params['domains_table'])) { + $this->_params['domains_table'] = 'domains'; + } + if (!isset($this->_params['records_table'])) { + $this->_params['records_table'] = 'records'; + } + + /* Connect to the SQL server using the supplied parameters. */ + require_once 'DB.php'; + $this->_write_db = &DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if (is_a($this->_write_db, 'PEAR_Error')) { + Horde::fatal($this->_write_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_write_db->phptype) { + case 'mssql': + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + /* Check if we need to set up the read DB connection seperately. */ + if (!empty($this->_params['splitread'])) { + $params = array_merge($this->_params, $this->_params['read']); + $this->_db = &DB::connect($params, + array('persistent' => !empty($params['persistent']))); + if (is_a($this->_db, 'PEAR_Error')) { + Horde::fatal($this->_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + } else { + /* Default to the same DB handle for the writer too. */ + $this->_db =& $this->_write_db; + } + + $this->_connected = true; + + return true; + } + + /** + * Disconnects from the SQL server and cleans up the connection. + * + * @access private + * + * @return boolean True on success, false on failure. + */ + function _disconnect() + { + if ($this->_connected) { + $this->_connected = false; + $this->_db->disconnect(); + $this->_write_db->disconnect(); + } + + return true; + } + +} diff --git a/beatnik/lib/Driver/sql.php b/beatnik/lib/Driver/sql.php new file mode 100644 index 000000000..4676ac195 --- /dev/null +++ b/beatnik/lib/Driver/sql.php @@ -0,0 +1,284 @@ + + * @package Beatnik + */ + +class Beatnik_Driver_sql extends Beatnik_Driver +{ + /** + * Hash containing connection parameters. + * + * @var array + */ + var $_params = array(); + + /** + * Handle for the current database connection. + * + * @var DB + */ + var $_db; + + /** + * Handle for the current database connection, used for writing. Defaults + * to the same handle as $_db if a separate write database is not required. + * + * @var DB + */ + var $_write_db; + + /** + * Boolean indicating whether or not we're connected to the SQL server. + * + * @var boolean + */ + var $_connected = false; + + /** + * Constructs a new Beatnik DB driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Beatnik_Driver_sql($params = array()) + { + parent::Beatnik_Driver($params); + $this->_connect(); + } + + /** + * Get any record types available specifically in this driver. + * + * @return array Records available only to this driver + */ + function getRecDriverTypes() + { + return array(); + } + + + /** + * Get any fields available specifically in this driver by record type. + * + * @param string $type Record type for which fields should be returned + * + * @return array Fields specific to this driver + */ + function getRecDriverFields($type) { + + return array(); + } + + /** + * Gets all zones + * + * @access private + * + * @return array Array with zone records numerically indexed + */ + function _getDomains() + { + $query = 'SELECT * FROM beatnik_soa ORDER BY zonename'; + return $this->_db->getAll($query, null, DB_FETCHMODE_ASSOC); + } + + /** + * Return SOA for a single domain + * + * @param string $domain Domain for which to return SOA information + * + * @return array Domain SOA + */ + function getDomain($domainname) + { + $query = 'SELECT * FROM beatnik_soa WHERE zonename = ? ORDER BY zonename'; + return $this->_db->getRow($query, array($domainname), DB_FETCHMODE_ASSOC); + } + + /** + * Gets all records associated with the given zone + * + * @param string $domain Retrieve records for this domain + * + * @return array Array with zone records + */ + function getRecords($domain) + { + $zonedata = array(); + $params = array($domain); + + foreach (array_keys(Beatnik::getRecTypes()) as $type) { + if ($type == 'soa') { + continue; + } + if ($type == 'mx') { + $order = 'pointer'; + } else { + $order = 'hostname'; + } + + $query = 'SELECT * FROM beatnik_' . $type . ' WHERE zonename = ? ORDER BY ' . $order . ' ASC'; + $result = $this->_db->getAll($query, $params, DB_FETCHMODE_ASSOC); + if (is_a($result, 'PEAR_Error') || empty($result)) { + continue; + } + + $zonedata[$type] = $result; + } + + return $zonedata; + } + + /** + * Saves a new or edited record to the DNS backend + * + * @access private + * + * @param array $info Array of record data + * + * @return boolean true on success, PEAR::raiseError on error + */ + function _saveRecord($info) + { + $fields = array_keys(Beatnik::getRecFields($info['rectype'])); + $params = array(); + foreach ($fields as $i => $key) { + if (!isset($info[$key])) { + unset($fields[$i]); + continue; + } + $params[$key] = $info[$key]; + } + + if (isset($params['id']) && $params['id']) { + unset($params['id'], $fields[0]); + $query = 'UPDATE beatnik_' . $info['rectype'] . ' SET '; + foreach ($fields as $key) { + $query .= $key . ' = ?, '; + $params[$key] = $info[$key]; + } + $query = substr($query, 0, -2) . ' WHERE id = ?'; + $params['id'] = $info['id']; + } else { + unset($params['id'], $fields[0]); + if ($info['rectype'] != 'soa') { + $fields[] = 'zonename'; + $params['zonename'] = $_SESSION['beatnik']['curdomain']['zonename']; + } + $query = 'INSERT INTO beatnik_' . $info['rectype'] . ' (' . implode(', ', $fields) . ') ' . + ' VALUES (' . substr(str_repeat('?, ', sizeof($params)), 0, -2) . ')'; + } + + return $this->_write_db->query($query, $params); + } + + /** + * Delete record from backend + * + * @access private + * + * @param array $data Reference to array of record data to be deleted + * + * @return boolean true on success, PEAR::Error on error + */ + function _deleteRecord($data) + { + // delete just one record + if ($data['rectype'] != 'soa') { + return $this->_write_db->query('DELETE FROM beatnik_' . $data['rectype'] . ' WHERE id = ?', array($data['id'])); + } + + // delete all subrecords + $params = array($data['curdomain']); + foreach (array_keys(Beatnik::getRecTypes()) as $type) { + if ($type == 'soa') { + continue; + } + $result = $this->_write_db->query('DELETE FROM beatnik_' . $type . ' WHERE zonename = ?', $params); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + } + + // we are cuccesfull so, delete even soa + return $this->_write_db->query('DELETE FROM beatnik_soa WHERE zonename = ?', $params); + } + + /** + * Attempts to open a persistent connection to the SQL server. + * + * @access private + * + * @return boolean True on success; exits (Horde::fatal()) on error. + */ + function _connect() + { + if ($this->_connected) { + return true; + } + + Horde::assertDriverConfig($this->_params, 'storage', + array('phptype', 'charset')); + + if (!isset($this->_params['database'])) { + $this->_params['database'] = ''; + } + if (!isset($this->_params['username'])) { + $this->_params['username'] = ''; + } + if (!isset($this->_params['hostspec'])) { + $this->_params['hostspec'] = ''; + } + + /* Connect to the SQL server using the supplied parameters. */ + require_once 'DB.php'; + $this->_write_db = &DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if (is_a($this->_write_db, 'PEAR_Error')) { + Horde::fatal($this->_write_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_write_db->phptype) { + case 'mssql': + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + /* Check if we need to set up the read DB connection seperately. */ + if (!empty($this->_params['splitread'])) { + $params = array_merge($this->_params, $this->_params['read']); + $this->_db = &DB::connect($params, + array('persistent' => !empty($params['persistent']))); + if (is_a($this->_db, 'PEAR_Error')) { + Horde::fatal($this->_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + } else { + /* Default to the same DB handle for the writer too. */ + $this->_db =& $this->_write_db; + } + + $this->_connected = true; + + return true; + } +} diff --git a/beatnik/lib/Forms/Autogenerate.php b/beatnik/lib/Forms/Autogenerate.php new file mode 100644 index 000000000..735192dd0 --- /dev/null +++ b/beatnik/lib/Forms/Autogenerate.php @@ -0,0 +1,33 @@ + + * + * See the enclosed file LICENSE for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Ben Klang + * @package Beatnik + */ +class Autogenerate extends Horde_Form +{ + /** + */ + function Autogenerate(&$vars) + { + require BEATNIK_BASE . '/config/autogenerate.php'; + + parent::Horde_Form($vars, _("Choose a template for autogenerating the records:"), 'autogenerate'); + $this->setButtons(array(_("Autogenerate"), _("Cancel"))); + + // Create an array of template => description for the enum + $template_keys = array_keys($templates); + foreach ($template_keys as $template) { + $t[$template] = $templates[$template]['description']; + } + $this->addVariable(_("Template"), 'template', 'enum', true, false, null, array($t, true)); + + return true; + } +} diff --git a/beatnik/lib/Forms/DeleteRecord.php b/beatnik/lib/Forms/DeleteRecord.php new file mode 100644 index 000000000..7db53762b --- /dev/null +++ b/beatnik/lib/Forms/DeleteRecord.php @@ -0,0 +1,41 @@ + + * + * See the enclosed file LICENSE for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Ben Klang + * @package Beatnik + */ +class DeleteRecord extends Horde_Form +{ + /** + */ + function DeleteRecord(&$vars) + { + parent::Horde_Form($vars, _("Are you sure you want to delete this record?")); + + $rectype = $vars->get('rectype'); + $types = Beatnik::getRecTypes(); + + $this->addHidden('', 'id', 'text', $vars->get('id')); + $this->addHidden('', 'curdomain', 'text', $vars->get('curdomain')); + $this->addHidden('', 'rectype', 'text', $vars->get('rectype')); + $this->addVariable(_("Type"), 'rectype', 'text', false, true); + + $recset = Beatnik::getRecFields($rectype); + foreach ($recset as $field => $fdata) { + if ($fdata['type'] != 'hidden' && ($fdata['infoset'] == 'basic' || $_SESSION['beatnik']['expertmode'])) { + $this->addVariable(_($fdata['description']), $field, $fdata['type'], false, true); + } + + } + + $this->setButtons(array(_("Delete"), _("Cancel"))); + + return true; + } +} diff --git a/beatnik/lib/Forms/EditRecord.php b/beatnik/lib/Forms/EditRecord.php new file mode 100644 index 000000000..c3b78882c --- /dev/null +++ b/beatnik/lib/Forms/EditRecord.php @@ -0,0 +1,63 @@ + + * + * See the enclosed file LICENSE for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Ben Klang + * @package Beatnik + */ +class EditRecord extends Horde_Form +{ + /** + */ + function EditRecord(&$vars) + { + $isnew = !$vars->exists('id'); + $rectype = $vars->get('rectype'); + $recset = Beatnik::getRecFields($rectype); + if ($isnew) { + // Pre-load the field defaults on a new record + foreach ($recset as $field => $fdata) { + if (isset($fdata['default'])) { + $vars->set($field, $fdata['default']); + } + } + } + + parent::Horde_Form($vars, $isnew ? _("Add DNS Record") : _("Edit DNS Record")); + + $types = Beatnik::getRecTypes(); + if (empty($_SESSION['beatnik']['curdomain'])) { + // Without an active domain, limit the form to creating a new zone. + $types = array('soa' => _('SOA (Start of Authority)')); + } + $action = &Horde_Form_Action::factory('reload'); + $select = &$this->addVariable(_("Record Type"), 'rectype', 'enum', true, + false, null, array($types, true)); + $select->setAction($action); + $select->setOption('trackchange', true); + + // Do not show record-specific fields until a record type is chosen + if (!$rectype) { + return true; + } + + foreach ($recset as $field => $fdata) { + if ($fdata['type'] == 'hidden' || ($fdata['infoset'] != 'basic' && + !$_SESSION['beatnik']['expertmode'])) { + $this->addHidden(_($fdata['description']), $field, 'text', + $fdata['required']); + } else { + $this->addVariable(_($fdata['description']), $field, + $fdata['type'], $fdata['required']); + } + + } + + return true; + } +} diff --git a/beatnik/lib/base.php b/beatnik/lib/base.php new file mode 100644 index 000000000..99fc5bd91 --- /dev/null +++ b/beatnik/lib/base.php @@ -0,0 +1,93 @@ + + * + * This file brings in all of the dependencies that every Beatnik + * script will need and sets up objects that all scripts use. + * + * @author Ben Klang + * @package Beatnik + */ + +// Check for a prior definition of HORDE_BASE (perhaps by an +// auto_prepend_file definition for site customization). +if (!defined('HORDE_BASE')) { + define('HORDE_BASE', dirname(__FILE__) . '/../..'); +} +// Load the Horde Framework core, and set up inclusion paths. +require_once HORDE_BASE . '/lib/core.php'; + +// Registry. +$registry = Horde_Registry::singleton(); + +try { + $registry->pushApp('beatnik', array('check_perms' => (Horde_Util::nonInputVar('beatnik_authentication') != 'none'))); +} catch (Horde_Exception $e) { + if ($e->getCode() == Horde_Registry::PERMISSION_DENIED) { + Horde_Auth::authenticateFailure('beatnik', $e); + } + Horde::fatal($e, __FILE__, __LINE__, false); +} + +$conf = &$GLOBALS['conf']; +define('BEATNIK_TEMPLATES', $registry->get('templates')); + +// Find the base file path of Beatnik. +if (!defined('BEATNIK_BASE')) { + define('BEATNIK_BASE', dirname(__FILE__) . '/..'); +} + +// Notification system. +$notification = Horde_Notification::singleton(); +$notification->attach('status'); + +// Beatnik base libraries. +require_once BEATNIK_BASE . '/lib/Beatnik.php'; +require_once BEATNIK_BASE . '/lib/Driver.php'; + +$GLOBALS['beatnik_driver'] = Beatnik_Driver::factory(); +if (is_a($GLOBALS['beatnik_driver'], 'PEAR_Error')) { + Horde::fatal($GLOBALS['beatnik_driver'], __FILE__, __LINE__); +} + +// Get a list of domains to work with +$domains = $GLOBALS['beatnik_driver']->getDomains(); + +// Jump to new domain +if (Horde_Util::getFormData('curdomain') !== null && !empty($domains)) { + $domain = $GLOBALS['beatnik_driver']->getDomain(Horde_Util::getFormData('curdomain')); + if (is_a($domain, 'PEAR_Error')) { + $notification->push($domain->getMessage() . ': ' . $domain->getDebugInfo(), 'horde.error'); + $domain = $domains[0]; + } + + $_SESSION['beatnik']['curdomain'] = $domain; +} + +// Determine if the user should see basic or advanced options +if (!isset($_SESSION['beatnik']['expertmode'])) { + $_SESSION['beatnik']['expertmode'] = false; +} elseif (Horde_Util::getFormData('expertmode') == 'toggle') { + if ($_SESSION['beatnik']['expertmode']) { + $notification->push(_('Expert Mode off'), 'horde.message'); + $_SESSION['beatnik']['expertmode'] = false; + } else { + $notification->push(_('Expert Mode ON'), 'horde.warning'); + $_SESSION['beatnik']['expertmode'] = true; + } +} + +// Initialize the page marker +if (!isset($_SESSION['beatnik']['curpage'])) { + $_SESSION['beatnik']['curpage'] = 0; +} + +// Start output compression. +if (!Horde_Util::nonInputVar('no_compress')) { + Horde::compressOutput(); +} + diff --git a/beatnik/lib/version.php b/beatnik/lib/version.php new file mode 100644 index 000000000..914e747f6 --- /dev/null +++ b/beatnik/lib/version.php @@ -0,0 +1 @@ + diff --git a/beatnik/listzones.php b/beatnik/listzones.php new file mode 100644 index 000000000..7f55e1538 --- /dev/null +++ b/beatnik/listzones.php @@ -0,0 +1,70 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +require_once dirname(__FILE__) . '/lib/base.php'; +require_once 'Horde/Prefs/CategoryManager.php'; + +// Unset the current domain since we are generating a zone list +$_SESSION['beatnik']['curdomain'] = null; + +// Set up categories +$cManager = new Prefs_CategoryManager(); +$categories = $cManager->get(); +$colors = $cManager->colors(); +$fgcolors = $cManager->fgColors(); + +// Page results +// Check for and store the current page in the session +$page = Horde_Util::getGet('page', $_SESSION['beatnik']['curpage']); +$_SESSION['beatnik']['curpage'] = $page; + +// Create the Pager UI +$pager_vars = Horde_Variables::getDefaultVariables(); +$pager_vars->set('page', $page); +$perpage = $prefs->getValue('domains_perpage'); +$pager = new Horde_UI_Pager('page', $pager_vars, + array('num' => count($domains), + 'url' => 'listzones.php', + 'page_count' => 10, + 'perpage' => $perpage)); + +// Limit the domain list to the current page +$domains = array_slice($domains, $page*$perpage, $perpage); + +$img_dir = $registry->getImageDir('horde'); + +// Hide fields that the user does not want to see +$fields = Beatnik::getRecFields('soa'); +foreach ($fields as $field_id => $field) { + if ($field['type'] == 'hidden' || + ($field['infoset'] != 'basic' && !$_SESSION['beatnik']['expertmode'])) { + unset($fields[$field_id]); + } +} + +// Add javascript navigation and striping +Horde::addScriptFile('beatnik.js'); +Horde::addScriptFile('stripe.js', 'horde', true); + +// Initialization complete. Render the page. +require BEATNIK_TEMPLATES . '/common-header.inc'; +require BEATNIK_TEMPLATES . '/menu.inc'; + +require BEATNIK_TEMPLATES . '/listzones/header.inc'; +foreach ($domains as $domain) { + $autourl = Horde_Util::addParameter(Horde::applicationUrl('autogenerate.php'), array('rectype' => 'soa', 'curdomain' => $domain['zonename'])); + $deleteurl = Horde_Util::addParameter(Horde::applicationUrl('delete.php'), array('rectype' => 'soa', 'curdomain' => $domain['zonename'])); + $viewurl = Horde_Util::addParameter(Horde::applicationUrl('viewzone.php'), 'curdomain', $domain['zonename']); + $editurl = Horde_Util::addParameter(Horde::applicationUrl('editrec.php'), array('curdomain' => $domain['zonename'], 'id' => $domain['id'], 'rectype' => 'soa')); + require BEATNIK_TEMPLATES . '/listzones/row.inc'; +} +require BEATNIK_TEMPLATES . '/listzones/footer.inc'; + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/beatnik/locale/en_US/help.xml b/beatnik/locale/en_US/help.xml new file mode 100644 index 000000000..a0c7ac737 --- /dev/null +++ b/beatnik/locale/en_US/help.xml @@ -0,0 +1,40 @@ + + + + + + Beatnik Overview + + What is Beatnik? + Beatnik manages DNS records in a convenient web interface. + + + + Record Types + + + + Record Fields + + Timestamp + This field is available only with the "ldap2dns" backend. + + The Timestamp field provides tell the DNS server "Do Not Issue Before" + or "Do Not Issue After" a particular time. If the "ttl" field is + nonzero or omitted, "timestamp" indicates a starting time for the record + to be valid. If "ttl" is 0 the timestamp is an ending time, after which + the record will no longer be served. tinydns + will automatically adjust the ttl so that the record should not be + cached for more than a few seconds beyond the expiry. + + + The format of the record is the number of seconds since midnight of + January 1st, 1970 expressed in TAI64, a 16 character hexadecimal string. + See + + http://cr.yp.to/libtai/tai64.html#tai64 + for more information on TAI64. + + + + diff --git a/beatnik/locale/sl_SI/LC_MESSAGES/beatnik.mo b/beatnik/locale/sl_SI/LC_MESSAGES/beatnik.mo new file mode 100644 index 0000000000000000000000000000000000000000..2a7b2b090a37ee227056bf6972ef235263633cb7 GIT binary patch literal 4972 zcma)yS_d;HTjX*-kMB&O*!Z8F_m>Nd${lifs8DCO+jv$JRB z&OOs}?@Y1__CY~J>4S*zK?I9pp)Wq@$bU`1AAYu`0QGD=0EDHK0{`vgwJu|c0 zB+$#=`?=@b-}&?V{(k42*|+YvI3i}@O|(<@HV*X zy-H2NJ@6rT5BwlJ38kM4kUur#F$2F0#r`Ky+W#DW9{vjQr{3mqJNy^?5ZuLJABJ~A z8GkR7@elEksU3x4--Po047>|I13wKfLFwmm{QeS@aj!sW_bQb6{07QA-i+m&ZdK|I z%Dds+P($(S7{o-Kg!|wMlyRSr-@gH+|L;O+|6_<$>Su5_d?kK=9g4qyfcL;Z!_UH- z->(#}>OLs`?T35eAt>{E22R7TK(W6P>wg!^|Au0JH%`^ylduj4Q1ti_d;q=*B_4l+ z55s$KLhQZ-KL@`C#ow2q==W2|pZaC2|1A{%{uJx~36H&p3L4R+I*zmA_X#LEpMjVt z1Mh`B_!0Q+SbhmIb@e(F|K5ONe=A~2{P#eK>wQr4X~K`gr{JBi1!bO_P{#iz6n!s4 znaB5`%;Sep`n>`_0bhZ8;P2!2w9KVM3*mc%l!9q%Ox?8>u`ngDRGck$|b(ZC0fpLi$Q-Q6-qrN z$K+~OD4)l9Hn=5ja*+z^W89>&dX!r(Su^F;)O(vr5^SFDQ_WW4e3mOJ^xWdIK5JV( zBxP~*8m!*%1Dy}z%SDqFy6`%&sV%H7)?Kbc+3c8V4hrA4nGL3}+Y5}LGhgVw4QoF1 zY@%jO*0QOZUH3k>+UUae`YEk6-?f=^JoP_w?*bA@iEG_GD}*G<;8ImN6n ztwP&c&$v{Z%Ez+9a*~@qvuduKwVHD|!)@S1fo(o6r!FrTk_t~JnVw}zZ66%n$C^dc zqnGk)uI3Y0l#lJ=N##h$^1U_gM4K$pEuZ>Owbe;AOgs2NKi~27l0guwSE4Rz{u1I9 z`jk)X_IG`HaYxawtx4Gt0Yq0{^cQu{WH?$4Byo$b)1B^{wymp$B^?t&Eo6lanMt(` z!H0&vH>YZ7QkTRC=whR9b>6ovx8`h8S0_ z$$eIeH!+OMrRuJ(--S^_FZxm63v=5wpY)n~Nv=|7n->Tj`&Sc_o=@D`#^}Qh1`gIp z+vf&VFELw){-mE^GX&F~IQrENn%~HYU<(!uF-}Zj^jh$}QWv_<2|ylo$p`(u4>Dy> zCYIUS&@GcmgyT>qW^J8WOw93c5OOE|mutDMF0efrcBi<*usuk(1ZU{%MHba4`-!xe zCcd;>mo#MPp5!X~6ZS1+K+TL+l>#?tF>YXGsmu1Xe7dRqTD2LYDBXtDR$^5uPp*^*9dZjiiGSW|N4mBJ4!1VOt+Vmr}nS*-fK;yu}4^C5q zVePCPIvH<=-J#mSX+1ODIC$W}=^5-!n!Kp31e4_)53(gCgI4#rUp1+zXSU#b)*i5p z)mE(OHFTanyS$)}?lgLQ>eRxi`H6co_30_mku_eE_1xh2xm4`uL~z-oqI$?}@z|hP zs~wrx$rRRXsLd0ggw3L%kF2_4N^zWKCQtovpE}nyn~uXFF`e>2*ff172Zxla_p>(7 z+-omnUT?328y2nYU{wr#=-8}duUDrtr&nF->0=nSUbLB4 zOTl(i25@R&lR(P=TQ9O#dHUKkY`xcXT#f06G>}f59_DuZp7(?8SyD!wki)fGFrqC_ zS2|~Ib1s|W#6-z zT3~-E-^vq~-x2B@hli`4+Y~KpqoUP&chVJ$Wz~dXY^&R0z=1WcTneto!52lbObr(k z+{z59W$!F&=7z+2*iC&uB@)*dbSne5Iks5xg?;S>kt}sx&RcPf?mLwx#JxvJ=LHFM zOwQgadqzhu#lGp9a+gFpbnHATMmg;Fq-_h`i%w=a)Ox;m!m5rl>&Pa#6 zJIEO~Bi=C|t4WqQLw3rkEmCtC$;v_zs6 z-^pl^7#qY2GKd_|-J(jOYkMkZ+V#We7sY;{@qh)};P8un?&!E$mEx8(7Hc+|LD+PX zj+G*ObUisf`+>FI!W)TSJLMdc)rmdpAujLh*p7EP4(Fj;uMG1pZ>7wBc%3%Y_2O(w&P8?y, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: dev@lists.horde.org\n" +"POT-Creation-Date: 2009-07-06 10:53+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: scripts/export_config.php:139 +msgid "-h, --help Show this help" +msgstr "" + +#: scripts/export_config.php:141 +msgid "-p, --password[=password] Horde login password" +msgstr "" + +#: scripts/export_config.php:143 +msgid "-r, --rpc[=http://example.com/horde/rpc.php] Remote url" +msgstr "" + +#: scripts/export_config.php:142 +msgid "-t, --type[=type] Export format" +msgstr "" + +#: scripts/export_config.php:140 +msgid "-u, --username[=username] Horde login username" +msgstr "" + +#: lib/Beatnik.php:63 +msgid "A (Address)" +msgstr "" + +#: templates/listzones/header.inc:45 templates/view/header.inc:43 +msgid "Actions" +msgstr "" + +#: lib/Forms/EditRecord.php:31 +msgid "Add DNS Record" +msgstr "" + +#: lib/Beatnik.php:32 +msgid "Add Record" +msgstr "" + +#: lib/Beatnik.php:34 +msgid "Add Zone" +msgstr "" + +#: lib/Driver/pdnsgsql.php:134 lib/Driver/pdnsgsql.php:191 +msgid "" +"An error occurred while searching the database. Details have been logged " +"for the administrator." +msgstr "" + +#: lib/Forms/DeleteRecord.php:19 +msgid "Are you sure you want to delete this record?" +msgstr "" + +#: autogenerate.php:25 autogenerate.php:40 templates/listzones/row.inc:16 +#: lib/Forms/Autogenerate.php:22 +msgid "Autogenerate" +msgstr "" + +#: autogenerate.php:33 +msgid "Autogeneration not performed" +msgstr "" + +#: lib/Beatnik.php:65 +msgid "CNAME (Alias)" +msgstr "" + +#: lib/Forms/DeleteRecord.php:37 lib/Forms/Autogenerate.php:22 +msgid "Cancel" +msgstr "" + +#: lib/Forms/Autogenerate.php:21 +msgid "Choose a template for autogenerating the records:" +msgstr "" + +#: lib/Beatnik.php:42 +msgid "Commit All" +msgstr "" + +#: templates/view/header.inc:13 +msgid "Commit Changes" +msgstr "" + +#: lib/Beatnik.php:133 +msgid "Contact e-mail address for this zone" +msgstr "" + +#: lib/Driver/pdnsgsql.php:146 +msgid "Corrupt SOA found for zone. Contact your administrator." +msgstr "" + +#: scripts/export_config.php:44 +msgid "Couldn't read command-line options." +msgstr "" + +#: lib/Beatnik.php:337 +msgid "DNS Service Record Port Number" +msgstr "" + +#: lib/Beatnik.php:317 +msgid "DNS Service Record Priority" +msgstr "" + +#: lib/Beatnik.php:327 +msgid "DNS Service Record Weight" +msgstr "" + +#: config/prefs.php.dist:44 +msgid "Default Time-To-Live for new records." +msgstr "" + +#: delete.php:24 delete.php:49 templates/listzones/row.inc:18 +#: templates/view/record.inc:18 templates/view/header.inc:25 +#: lib/Forms/DeleteRecord.php:37 +msgid "Delete" +msgstr "" + +#: config/prefs.php.dist:10 +msgid "Display Preferences" +msgstr "" + +#: templates/listzones/header.inc:15 +msgid "Domain Categories" +msgstr "" + +#: lib/Beatnik.php:114 lib/Beatnik.php:275 +msgid "Domain Name" +msgstr "" + +#: lib/api.php:46 +msgid "Domains" +msgstr "" + +#: lib/Driver.php:326 +msgid "Driver not found." +msgstr "" + +#: templates/listzones/row.inc:20 templates/view/record.inc:16 +#: templates/view/header.inc:23 +msgid "Edit" +msgstr "" + +#: lib/Forms/EditRecord.php:31 +msgid "Edit DNS Record" +msgstr "" + +#: lib/Beatnik.php:32 +msgid "Edit Record" +msgstr "" + +#: templates/listzones/header.inc:20 +msgid "Edit categories and colors" +msgstr "" + +#: templates/listzones/header.inc:19 +msgid "Edit domain groups and colors" +msgstr "" + +#: config/autogenerate.php.dist:34 +msgid "Example Template" +msgstr "" + +#: lib/Beatnik.php:38 +msgid "Expert Mode" +msgstr "" + +#: lib/base.php:76 +msgid "Expert Mode ON" +msgstr "" + +#: lib/base.php:73 +msgid "Expert Mode off" +msgstr "" + +#: lib/Beatnik.php:169 +msgid "Expiration" +msgstr "" + +#: scripts/export_config.php:117 +msgid "Have noting to do." +msgstr "" + +#: lib/Beatnik.php:190 lib/Beatnik.php:211 lib/Beatnik.php:254 +#: lib/Beatnik.php:298 lib/Beatnik.php:349 +msgid "Hostname" +msgstr "" + +#: lib/Beatnik.php:220 lib/Beatnik.php:232 lib/Beatnik.php:263 +#: lib/Beatnik.php:286 lib/Beatnik.php:307 +msgid "Hostname Target" +msgstr "" + +#: lib/Beatnik.php:264 +msgid "Hostname for CNAME alias" +msgstr "" + +#: lib/Beatnik.php:308 +msgid "Hostname for DNS Service Record" +msgstr "" + +#: lib/Beatnik.php:221 +msgid "Hostname for Reverse DNS" +msgstr "" + +#: lib/Beatnik.php:287 +msgid "Hostname of Authoritative Name Server" +msgstr "" + +#: lib/Beatnik.php:233 +msgid "Hostname of Mail eXchanger" +msgstr "" + +#: config/prefs.php.dist:36 +msgid "How many domain to display per page." +msgstr "" + +#: lib/Beatnik.php:199 +msgid "IP Address" +msgstr "" + +#: lib/Beatnik.php:212 +msgid "IP in Reverse notation (.in-addr.arpa)" +msgstr "" + +#: lib/Beatnik.php:200 +msgid "IPv4 Network Address" +msgstr "" + +#: lib/Driver/pdnsgsql.php:71 lib/Driver/pdnsgsql.php:121 +#: lib/Driver/pdnsgsql.php:174 lib/Driver/pdnsgsql.php:283 +#: lib/Driver/pdnsgsql.php:421 +msgid "" +"Internal database error. Details have been logged for the administrator." +msgstr "" + +#: lib/Driver/ldap2dns.php:246 +#, php-format +msgid "Internal error: %s" +msgstr "" + +#: lib/Driver/ldap2dns.php:349 +msgid "Invalid record type specified." +msgstr "" + +#: lib/Beatnik.php:30 +msgid "List Domains" +msgstr "" + +#: scripts/export_config.php:109 +#, php-format +msgid "Logged in successfully as \"%s\"." +msgstr "" + +#: scripts/export_config.php:105 +msgid "Login is incorrect." +msgstr "" + +#: lib/Beatnik.php:66 +msgid "MX (Mail eXchange)" +msgstr "" + +#: lib/Beatnik.php:242 +msgid "MX Preference (lower is more preferred)" +msgstr "" + +#: scripts/export_config.php:137 +msgid "" +"Mandatory arguments to long options are mandatory for short options too." +msgstr "" + +#: lib/Beatnik.php:178 +msgid "Minimum" +msgstr "" + +#: lib/Driver/ldap2dns.php:400 +#, php-format +msgid "Missing required field %s to save record." +msgstr "" + +#: lib/Beatnik.php:62 +msgid "NS (Name Server)" +msgstr "" + +#: lib/Driver/pdnsgsql.php:424 +msgid "Not implemented." +msgstr "" + +#: config/prefs.php.dist:9 config/prefs.php.dist:16 +msgid "Options" +msgstr "" + +#: lib/Beatnik.php:64 +msgid "PTR (Reverse DNS)" +msgstr "" + +#: lib/Beatnik.php:241 +msgid "Preference" +msgstr "" + +#: lib/Beatnik.php:123 +msgid "Primary Nameserver" +msgstr "" + +#: lib/Beatnik.php:124 +msgid "Primary nameserver for this zone" +msgstr "" + +#: config/prefs.php.dist:17 +msgid "Record Defaults" +msgstr "" + +#: lib/Beatnik.php:371 +msgid "Record Time-To-Live (seconds)" +msgstr "" + +#: lib/Forms/EditRecord.php:39 +msgid "Record Type" +msgstr "" + +#: lib/Beatnik.php:543 +#, php-format +msgid "Record added: %s/%s" +msgstr "" + +#: delete.php:30 +msgid "Record deleted" +msgstr "" + +#: delete.php:38 +msgid "Record not deleted" +msgstr "" + +#: lib/Beatnik.php:160 +msgid "Retry" +msgstr "" + +#: lib/Beatnik.php:61 lib/Forms/EditRecord.php:36 +msgid "SOA (Start of Authority)" +msgstr "" + +#: lib/Beatnik.php:67 +msgid "SRV (Service Record)" +msgstr "" + +#: lib/Beatnik.php:336 +msgid "SRV Port" +msgstr "" + +#: lib/Beatnik.php:316 +msgid "SRV Priority" +msgstr "" + +#: lib/Beatnik.php:326 +msgid "SRV Weight" +msgstr "" + +#: templates/menu.inc:17 +msgid "Select Domain" +msgstr "" + +#: templates/menu.inc:11 templates/menu.inc:18 +msgid "Select _Domain" +msgstr "" + +#: lib/Beatnik.php:141 +msgid "Serial" +msgstr "" + +#: config/prefs.php.dist:11 +msgid "Set default display parameters." +msgstr "" + +#: config/prefs.php.dist:18 +msgid "Set default record parameters." +msgstr "" + +#: lib/Beatnik.php:191 lib/Beatnik.php:255 lib/Beatnik.php:299 +#: lib/Beatnik.php:350 +msgid "Short hostname for this record" +msgstr "" + +#: lib/Beatnik.php:276 +msgid "" +"Short sub-domain for NS record (leave blank unless creating a subdomain)" +msgstr "" + +#: lib/Beatnik.php:536 +msgid "Skipping existing identical record" +msgstr "" + +#: lib/Beatnik.php:359 +msgid "String payload for DNS TXT" +msgstr "" + +#: lib/Beatnik.php:370 +msgid "TTL" +msgstr "" + +#: lib/Beatnik.php:68 +msgid "TXT (Text Record)" +msgstr "" + +#: lib/Forms/Autogenerate.php:29 +msgid "Template" +msgstr "" + +#: lib/Driver/pdnsgsql.php:138 +msgid "Too many domains matched that name. Contact your administrator." +msgstr "" + +#: templates/view/header.inc:44 lib/Forms/DeleteRecord.php:27 +msgid "Type" +msgstr "" + +#: lib/Beatnik.php:102 +msgid "UID" +msgstr "" + +#: lib/Driver/ldap2dns.php:469 +#, php-format +msgid "Unable to add record to LDAP. Reason: %s" +msgstr "" + +#: commit.php:49 +#, php-format +msgid "Unable to construct valid SOA for %s. Not incrementing serial." +msgstr "" + +#: lib/Driver/ldap2dns.php:321 +#, php-format +msgid "Unable to delete record. Reason: %s" +msgstr "" + +#: lib/Driver/ldap2dns.php:300 +msgid "Unable to delete record: No record ID specified." +msgstr "" + +#: lib/Beatnik.php:430 +msgid "Unable to determine if domain needs committing: invalid parameter." +msgstr "" + +#: lib/Driver.php:147 +msgid "Unable to locate requested record." +msgstr "" + +#: lib/Driver/ldap2dns.php:452 +#, php-format +msgid "Unable to modify record. Reason: %s" +msgstr "" + +#: lib/Driver.php:108 +#, php-format +msgid "Unable to read requested domain %s" +msgstr "" + +#: lib/Driver/ldap2dns.php:442 +#, php-format +msgid "Unable to rename old object. Reason: %s" +msgstr "" + +#: lib/Driver/ldap2dns.php:146 +#, php-format +msgid "Unable to retrieve data from LDAP results: %s" +msgstr "" + +#: lib/Beatnik.php:103 +msgid "Unique Identifier (Used as Record ID)" +msgstr "" + +#: lib/Driver/ldap2dns.php:437 +msgid "Unsupported operation: cannot rename a domain." +msgstr "" + +#: lib/Driver/ldap2dns.php:309 +msgid "Unsupported recursive delete." +msgstr "" + +#: scripts/export_config.php:135 +#, php-format +msgid "Usage: %s [OPTIONS]..." +msgstr "" + +#: lib/Driver.php:79 +msgid "You are not permitted to view any domains." +msgstr "" + +#: lib/Driver.php:207 +msgid "You do not have permission to create new domains." +msgstr "" + +#: lib/Driver.php:220 +msgid "You do not have permssion to edit the SOA of this zone." +msgstr "" + +#: lib/Driver.php:225 +msgid "You do not have permssion to edit this record." +msgstr "" + +#: templates/common-header.inc:15 +#, php-format +msgid "You have uncommitted changes in %s." +msgstr "" + +#: lib/Beatnik.php:132 +msgid "Zone Contact" +msgstr "" + +#: lib/Beatnik.php:115 +msgid "Zone Domain Name" +msgstr "" + +#: lib/Beatnik.php:170 +msgid "Zone Expiry" +msgstr "" + +#: lib/Beatnik.php:179 +msgid "Zone Minimum" +msgstr "" + +#: lib/Beatnik.php:152 +msgid "Zone Refresh" +msgstr "" + +#: lib/Beatnik.php:161 +msgid "Zone Retry" +msgstr "" + +#: lib/Beatnik.php:142 +msgid "Zone Serial Number" +msgstr "" + +#: commit.php:46 +#, php-format +msgid "Zone serial for %s incremented." +msgstr "" diff --git a/beatnik/po/sl_SI.po b/beatnik/po/sl_SI.po new file mode 100644 index 000000000..1357c6d85 --- /dev/null +++ b/beatnik/po/sl_SI.po @@ -0,0 +1,503 @@ +# Slovenian translations for Beatnik packaga +# Slovenski prevod Beatnik paketa +# Copyright (C) 2006 Horde Project +# This file is distributed under the same license as the horde package. +# Automatically generated, 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: dev@lists.horde.org\n" +"POT-Creation-Date: 2006-09-13 12:27+0200\n" +"PO-Revision-Date: 2006-04-30 10:32+0100\n" +"Last-Translator: duck@obala.net\n" +"Language-Team: sl_SI \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: scripts/commands.php:133 scripts/tinydns.php:167 scripts/apache.php:142 +#: scripts/unixhosts.php:123 +msgid "-h, --help Show this help" +msgstr "" + +#: scripts/commands.php:135 scripts/tinydns.php:169 scripts/apache.php:144 +#: scripts/unixhosts.php:125 +msgid "-p, --password[=password] Horde login password" +msgstr "" + +#: scripts/commands.php:134 scripts/tinydns.php:168 scripts/apache.php:143 +#: scripts/unixhosts.php:124 +msgid "-u, --username[=username] Horde login username" +msgstr "" + +#: lib/Beatnik.php:61 +msgid "A (Address)" +msgstr "A (naslov)" + +#: templates/listzones/header.inc:44 templates/view/header.inc:45 +msgid "Actions" +msgstr "Ukazi" + +#: lib/Forms/EditRecord.php:22 +msgid "Add DNS Record" +msgstr "Dodaj DNS zapis" + +#: lib/Beatnik.php:33 +msgid "Add Record" +msgstr "Dodaj zapis" + +#: lib/Forms/DeleteRecord.php:22 +msgid "Are you sure you want to delete this record?" +msgstr "Resnično želite izbrisati ta zapis?" + +#: autogenerate.php:28 autogenerate.php:43 templates/listzones/row.inc:16 +#: lib/Forms/Autogenerate.php:28 +msgid "Autogenerate" +msgstr "Samodejno tvorjenje" + +#: autogenerate.php:36 +msgid "Autogeneration not performed" +msgstr "Samodejno tvorjenje ni bilo izvršeno" + +#: lib/Beatnik.php:63 +msgid "CNAME (Alias)" +msgstr "" + +#: lib/Forms/DeleteRecord.php:40 lib/Forms/Autogenerate.php:28 +msgid "Cancel" +msgstr "Prekliči" + +#: lib/Forms/Autogenerate.php:27 +msgid "Choose a template for autogenerating the records:" +msgstr "Izberite šablono za samodejno tvorjenje zapisov" + +#: lib/Beatnik.php:40 +msgid "Commit All" +msgstr "Izvrši vse" + +#: templates/view/header.inc:13 +msgid "Commit Changes" +msgstr "Izvrši spremembe" + +#: lib/Beatnik.php:131 +msgid "Contact e-mail address for this zone" +msgstr "Kontaktni email za to zono" + +#: scripts/commands.php:47 scripts/tinydns.php:50 scripts/apache.php:47 +#: scripts/unixhosts.php:47 +msgid "Couldn't read command-line options." +msgstr "" + +#: lib/Beatnik.php:335 +msgid "DNS Service Record Port Number" +msgstr "" + +#: lib/Beatnik.php:315 +msgid "DNS Service Record Priority" +msgstr "" + +#: lib/Beatnik.php:325 +msgid "DNS Service Record Weight" +msgstr "" + +#: delete.php:29 delete.php:54 templates/listzones/row.inc:18 +#: templates/view/record.inc:18 templates/view/header.inc:25 +#: lib/Forms/DeleteRecord.php:40 +msgid "Delete" +msgstr "Izbriši" + +#: config/prefs.php.dist:10 +msgid "Display details" +msgstr "Podrobnosti prikazovanja" + +#: config/prefs.php.dist:9 +msgid "Display listings" +msgstr "Nastavitve izpisovanja" + +#: templates/listzones/header.inc:15 +msgid "Domain Categories" +msgstr "Kategorije domen" + +#: lib/Beatnik.php:112 lib/Beatnik.php:273 +msgid "Domain Name" +msgstr "Ime domene" + +#: lib/api.php:45 +msgid "Domains" +msgstr "Domene" + +#: templates/listzones/row.inc:20 templates/view/record.inc:16 +#: templates/view/header.inc:23 +msgid "Edit" +msgstr "Uredi" + +#: lib/Forms/EditRecord.php:22 +msgid "Edit DNS Record" +msgstr "Uredi DNS zapis" + +#: lib/Beatnik.php:33 +msgid "Edit Record" +msgstr "Uredi zapis" + +#: templates/listzones/header.inc:20 +msgid "Edit categories and colors" +msgstr "Uredi kategorije in barve" + +#: templates/listzones/header.inc:19 +msgid "Edit domain groups and colors" +msgstr "Uredi grupe in barve" + +#: config/autogenerate.php.dist:34 +msgid "Example Template" +msgstr "Primer šablone" + +#: lib/Beatnik.php:36 +msgid "Expert Mode" +msgstr "Napredni način" + +#: lib/base.php:79 +msgid "Expert Mode ON" +msgstr "Napredni način je bil vklopljen" + +#: lib/base.php:76 +msgid "Expert Mode off" +msgstr "Napredni način ke bil izklopljen" + +#: lib/Beatnik.php:167 +msgid "Expiration" +msgstr "Poteče" + +#: lib/Beatnik.php:188 lib/Beatnik.php:209 lib/Beatnik.php:252 +#: lib/Beatnik.php:296 lib/Beatnik.php:347 +msgid "Hostname" +msgstr "" + +#: lib/Beatnik.php:218 lib/Beatnik.php:230 lib/Beatnik.php:261 +#: lib/Beatnik.php:284 lib/Beatnik.php:305 +msgid "Hostname Target" +msgstr "" + +#: lib/Beatnik.php:262 +msgid "Hostname for CNAME alias" +msgstr "" + +#: lib/Beatnik.php:306 +msgid "Hostname for DNS Service Record" +msgstr "" + +#: lib/Beatnik.php:219 +msgid "Hostname for Reverse DNS" +msgstr "" + +#: lib/Beatnik.php:285 +msgid "Hostname of Authoritative Name Server" +msgstr "" + +#: lib/Beatnik.php:231 +msgid "Hostname of Mail eXchanger" +msgstr "" + +#: config/prefs.php.dist:28 +msgid "How many domain to display per page." +msgstr "Koliko domen naj prikažem na stran?" + +#: lib/Beatnik.php:197 +msgid "IP Address" +msgstr "IP naslov" + +#: lib/Beatnik.php:210 +msgid "IP in Reverse notation (.in-addr.arpa)" +msgstr "" + +#: lib/Beatnik.php:198 +msgid "IPv4 Network Address" +msgstr "" + +#: lib/Driver/ldap2dns.php:267 +#, php-format +msgid "Internal error: %s" +msgstr "Interna napaka: %s" + +#: lib/Driver/ldap2dns.php:370 +msgid "Invalid record type specified." +msgstr "Podan je bil neveljavni način zapisa." + +#: lib/Beatnik.php:32 +msgid "List Domains" +msgstr "Spisek domen" + +#: scripts/commands.php:87 scripts/tinydns.php:90 scripts/apache.php:87 +#: scripts/unixhosts.php:87 +#, php-format +msgid "Logged in successfully as \"%s\"." +msgstr "" + +#: scripts/commands.php:83 scripts/tinydns.php:86 scripts/apache.php:83 +#: scripts/unixhosts.php:83 +msgid "Login is incorrect." +msgstr "" + +#: lib/Beatnik.php:64 +msgid "MX (Mail eXchange)" +msgstr "" + +#: lib/Beatnik.php:240 +msgid "MX Preference (lower is more preferred)" +msgstr "" + +#: scripts/commands.php:131 scripts/tinydns.php:165 scripts/apache.php:140 +#: scripts/unixhosts.php:121 +msgid "" +"Mandatory arguments to long options are mandatory for short options too." +msgstr "" + +#: lib/Beatnik.php:176 +msgid "Minimum" +msgstr "Najmanj" + +#: lib/Driver/ldap2dns.php:425 +#, php-format +msgid "Missing required field %s to save record." +msgstr "Ne morem shraniti zapisa saj manjka zahtevano polje %s." + +#: lib/Beatnik.php:60 +msgid "NS (Name Server)" +msgstr "" + +#: lib/Beatnik.php:62 +msgid "PTR (Reverse DNS)" +msgstr "" + +#: lib/Beatnik.php:239 +msgid "Preference" +msgstr "" + +#: lib/Beatnik.php:121 +msgid "Primary Nameserver" +msgstr "" + +#: lib/Beatnik.php:122 +msgid "Primary nameserver for this zone" +msgstr "" + +#: lib/Beatnik.php:369 +msgid "Record Time-To-Live (seconds)" +msgstr "" + +#: lib/Forms/EditRecord.php:28 +msgid "Record Type" +msgstr "Tip zapisa" + +#: lib/Beatnik.php:532 +msgid "Record added" +msgstr "Zapis je bil dodan" + +#: delete.php:35 +msgid "Record deleted" +msgstr "Zapis je bil izbrisan" + +#: delete.php:43 +msgid "Record not deleted" +msgstr "Zapis ni bil izbrisan" + +#: lib/Beatnik.php:158 +msgid "Retry" +msgstr "Poskusi znova" + +#: lib/Beatnik.php:59 +msgid "SOA (Start of Authority)" +msgstr "" + +#: lib/Beatnik.php:65 +msgid "SRV (Service Record)" +msgstr "" + +#: lib/Beatnik.php:334 +msgid "SRV Port" +msgstr "" + +#: lib/Beatnik.php:314 +msgid "SRV Priority" +msgstr "" + +#: lib/Beatnik.php:324 +msgid "SRV Weight" +msgstr "" + +#: templates/menu.inc:17 +msgid "Select Domain" +msgstr "Izberi domeno" + +#: templates/menu.inc:11 templates/menu.inc:18 +msgid "Select _Domain" +msgstr "Izberi Domeno" + +#: lib/Beatnik.php:139 +msgid "Serial" +msgstr "Serijska št." + +#: config/prefs.php.dist:11 +msgid "Set default display parameters." +msgstr "Nastavite prenstavljene parameterje prikaza." + +#: lib/Beatnik.php:189 lib/Beatnik.php:253 lib/Beatnik.php:297 +#: lib/Beatnik.php:348 +msgid "Short hostname for this record" +msgstr "" + +#: lib/Beatnik.php:274 +msgid "Short sub-domain for NS record" +msgstr "" + +#: lib/Beatnik.php:525 +msgid "Skipping existing identical record" +msgstr "Preskoži že obstoječe zapise" + +#: lib/Beatnik.php:357 +msgid "String payload for DNS TXT" +msgstr "" + +#: lib/Beatnik.php:368 +msgid "TTL" +msgstr "" + +#: lib/Beatnik.php:66 +msgid "TXT (Text Record)" +msgstr "" + +#: lib/Forms/Autogenerate.php:35 +msgid "Template" +msgstr "Šablona" + +#: templates/view/header.inc:46 lib/Forms/DeleteRecord.php:30 +msgid "Type" +msgstr "Tip" + +#: lib/Beatnik.php:100 +msgid "UUID" +msgstr "" + +#: lib/Driver/ldap2dns.php:488 +#, php-format +msgid "Unable to add record to LDAP. Reason: %s" +msgstr "Ne morem dodati zapisa v LDAP: %s" + +#: commit.php:51 +#, php-format +msgid "Unable to construct valid SOA for %s. Not incrementing serial." +msgstr "" + +#: lib/Driver/ldap2dns.php:342 +#, php-format +msgid "Unable to delete record. Reason: %s" +msgstr "Nemorem dodati zapisa: %s" + +#: lib/Driver/ldap2dns.php:321 +msgid "Unable to delete record: No record ID specified." +msgstr "Ne morem izbrisati zapisa. Niste podatli IDja." + +#: lib/Beatnik.php:427 +msgid "Unable to determine if domain needs committing: invalid parameter." +msgstr "" + +#: lib/Driver.php:100 +msgid "Unable to locate requested record." +msgstr "Ne morem najti izbranega zapisa" + +#: lib/Driver/ldap2dns.php:479 +#, php-format +msgid "Unable to modify record. Reason: %s" +msgstr "Ne morem ažurirati zapisa: %s" + +#: lib/Driver/ldap2dns.php:198 +#, php-format +msgid "Unable to read requested domain %s" +msgstr "Ne morem prebrati zahtevane domene %s" + +#: lib/Driver/ldap2dns.php:469 +#, php-format +msgid "Unable to rename old object. Reason: %s" +msgstr "Ne preimenovati starega objekta: %s" + +#: lib/Driver/ldap2dns.php:148 +#, php-format +msgid "Unable to retrieve data from LDAP results: %s" +msgstr "Ne morem prebrati podatkov: %s" + +#: lib/Beatnik.php:101 +msgid "Universally Unique Identifier (Used as Record ID)" +msgstr "" + +#: lib/Driver/ldap2dns.php:464 +msgid "Unsupported operation: cannot rename a domain." +msgstr "Nepodprti ukaz: ne morem preimenovati domene." + +#: lib/Driver/ldap2dns.php:330 +msgid "Unsupported recursive delete." +msgstr "Nepodarti rekurzivni izbris." + +#: scripts/commands.php:129 scripts/tinydns.php:163 scripts/apache.php:138 +#: scripts/unixhosts.php:119 +#, php-format +msgid "Usage: %s [OPTIONS]..." +msgstr "" + +#: lib/Driver.php:57 +msgid "You are not permitted to view any domains." +msgstr "Nimate pravic za pregled katerekoli domene." + +#: lib/Driver.php:164 +msgid "You do not have permission to create new domains." +msgstr "Nimate pravic za tvorjenje novih domenskih zapisov." + +#: lib/Driver.php:177 +msgid "You do not have permssion to edit the SOA of this zone." +msgstr "Nimate pravic za urejanje novih domenskih zapisov." + +#: lib/Driver.php:182 +msgid "You do not have permssion to edit this record." +msgstr "Nimate pravic za tvorjenje urejanje tega zapisa." + +#: templates/common-header.inc:15 +#, php-format +msgid "You have uncommitted changes in %s." +msgstr "Preklicali ste spremembe za %s." + +#: lib/Beatnik.php:130 +msgid "Zone Contact" +msgstr "Kontakt" + +#: lib/Beatnik.php:113 +msgid "Zone Domain Name" +msgstr "Ime domene" + +#: lib/Beatnik.php:168 +msgid "Zone Expiry" +msgstr "Poteče" + +#: lib/Beatnik.php:177 +msgid "Zone Minimum" +msgstr "" + +#: lib/Beatnik.php:150 +msgid "Zone Refresh" +msgstr "Osvežitev" + +#: lib/Beatnik.php:159 +msgid "Zone Retry" +msgstr "" + +#: lib/Beatnik.php:140 +msgid "Zone Serial Number" +msgstr "Serijska številka" + +#: commit.php:48 +#, php-format +msgid "Zone serial for %s incremented." +msgstr "" + +#: lib/Forms/EditRecord.php:39 lib/Forms/EditRecord.php:41 +#: lib/Forms/DeleteRecord.php:35 +msgid "description" +msgstr "opis" diff --git a/beatnik/scripts/export_config.php b/beatnik/scripts/export_config.php new file mode 100644 index 000000000..4b7e3fd9a --- /dev/null +++ b/beatnik/scripts/export_config.php @@ -0,0 +1,331 @@ + hosts + * + * $Horde: beatnik/scripts/export_config.php,v 1.2 2009/07/15 15:05:33 duck Exp $ + * + * Copyright 2008 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Duck + * @package Beatnik + */ + +define('AUTH_HANDLER', true); +define('HORDE_BASE', dirname(__FILE__) . '/../../'); +define('BEATNIK_BASE', HORDE_BASE . '/beatnik'); + +// Do CLI checks and environment setup first. +require_once HORDE_BASE . '/lib/core.php'; +require_once 'Horde/CLI.php'; + +// Make sure no one runs this from the web. +if (!Horde_CLI::runningFromCLI()) { + exit("Must be run from the command line\n"); +} + +// Load the CLI environment. +Horde_CLI::init(); +$cli = &Horde_CLI::singleton(); + +// We accept the user name on the command-line. +require_once 'Console/Getopt.php'; +$ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'h:u:p:t:r', + array('help', 'username=', 'password=', 'type=', 'rpc=')); + +if (is_a($ret, 'PEAR_Error')) { + $error = _("Couldn't read command-line options."); + Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $cli->fatal($error); +} + +// Show help and exit if no arguments were set. +list($opts, $args) = $ret; +if (!$opts) { + showHelp(); + exit; +} + +foreach ($opts as $opt) { + list($optName, $optValue) = $opt; + switch ($optName) { + case 'u': + case '--username': + $username = $optValue; + break; + + case 'p': + case '--password': + $password = $optValue; + break; + + case 't': + case '--type': + $type = $optValue; + break; + + case 'r': + case '--rpc': + $rpc = $optValue; + break; + + case 'h': + case '--help': + showHelp(); + exit; + } +} + +// We will fetch data from RPC +if (!empty($rpc)) { + + require_once 'Horde/RPC.php'; + $domains = Horde_RPC::request('xmlrpc', $rpc, + 'dns.getDomains', + array(), + array('user' => $username, + 'pass' => $password)); + if (is_a($result, 'PEAR_Error')) { + $cli->fatal($domains); + } + +// Login to horde if username & password are set and load module. +} elseif (!empty($username) && !empty($password)) { + + require_once HORDE_BASE . '/lib/base.php'; + $auth = &Horde_Auth::singleton($conf['auth']['driver']); + if (!$auth->authenticate($username, array('password' => $password))) { + $error = _("Login is incorrect."); + Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_ERR); + $cli->fatal($error); + } else { + $msg = sprintf(_("Logged in successfully as \"%s\"."), $username); + Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_DEBUG); + $cli->message($msg, 'cli.success'); + } + + require_once BEATNIK_BASE . '/lib/base.php'; + +} else { + $msg = _("Have noting to do."); + $cli->fatal($msg); +} + +// Steps +if (empty($type)) { + $type = 'tinydns'; +} +$function = '_' . $type; +echo $function(); + +/** + * Show the command line arguments that the script accepts. + */ +function showHelp() +{ + global $cli; + + $cli->writeln(sprintf(_("Usage: %s [OPTIONS]..."), basename(__FILE__))); + $cli->writeln(); + $cli->writeln(_("Mandatory arguments to long options are mandatory for short options too.")); + $cli->writeln(); + $cli->writeln(_("-h, --help Show this help")); + $cli->writeln(_("-u, --username[=username] Horde login username")); + $cli->writeln(_("-p, --password[=password] Horde login password")); + $cli->writeln(_("-t, --type[=type] Export format")); + $cli->writeln(_("-r, --rpc[=http://example.com/horde/rpc.php] Remote url")); + $cli->writeln(); +} + +/** + * Get domain records + */ +function _getRecords($domain) +{ + if (empty($GLOBALS['rpc'])) { + $result = $GLOBALS['beatnik_driver']->getRecords($domain); + } else { + $result = Horde_RPC::request('xmlrpc', $GLOBALS['rpc'], + 'dns.getRecords', + array($domain), + array('user' => $GLOBALS['username'], + 'pass' => $GLOBALS['password'])); + } + + if (is_a($result, 'PEAR_Error')) { + $GLOBALS['cli']->fatal($result); + } else { + return $result; + } +} + +/** + * Generate unix hosts file + */ +function _hosts() +{ + $c = "# Generated with beatnik on " . date('Y-m-d h:i:s') . " by " . $GLOBALS['username'] . "\n\n"; + foreach ($GLOBALS['domains'] as $domain) { + + $zonename = $domain['zonename']; + $tld = substr($zonename, strrpos($zonename, '.')+1); + $domain = substr($zonename, 0, strrpos($zonename, '.')); + + foreach ($zonedata['cname'] as $id => $values) { + extract($values); + if (!empty($hostname)) { + $hostname .= '.'; + } + $c .= "$pointer $hostname.$domain.$tld\n"; + } + } + + return $c; +} + +/** + * Generate bash script + */ +function _bash() +{ + $c = "# Generated with beatnik on " . date('Y-m-d h:i:s') . " by " . $GLOBALS['username'] . "\n\n"; + foreach ($GLOBALS['domains'] as $domain) { + + $zonename = $domain['zonename']; + $tld = substr($zonename, strrpos($zonename, '.')+1); + $domain = substr($zonename, 0, strrpos($zonename, '.')); + + $c .= "useradd $zonename" . "_$tld -d /var/www/$zonename" . "_$tld -s /bin/false" . "\n"; + $c .= "mkdir /var/www/$zonename" . "_$tld/" . "\n"; + $c .= "chown -R $zonename" . "_$tld:apache /var/www/$zonename" . "_$tld/" . "\n"; + + foreach ($zonedata['cname'] as $id => $values) { + extract($values); + if (empty($hostname)) { + // use empty hostname as alias for www + $c .= "ln -s /var/www/$zonename" . "_$tld/$zonename.$tld " . + "/var/www/$zonename" . "_$tld/$hostname.$zonename.$tld" . "\n"; + continue; + } + + $c .= "mkdir /var/www/$zonename" . "_$tld/$hostname.$zonename.$tld" . "\n"; + $c .= "mkdir /var/tmp/www/$hostname.$zonename.$tld" . "\n"; + $c .= "mkdir /var/log/apache2/$hostname.$zonename.$tld" . "\n"; + } + } + + return $c; +} + +/** + * Generate apache host defs + */ +function _apache() +{ + $c = "# Generated with beatnik on " . date('Y-m-d h:i:s') . " by " . $GLOBALS['username'] . "\n\n"; + foreach ($GLOBALS['domains'] as $domain) { + // Get default data and skip if no cnames + $zonedata = _getRecords($domain['zonename']); + if (!isset($zonedata['cname'])) { + continue; + } + + // data + $zonename = $domain['zonename']; + $tld = substr($zonename, strrpos($zonename, '.')+1); + $domain = substr($zonename, 0, strrpos($zonename, '.')); + foreach ($zonedata['cname'] as $id => $values) { + + extract($values); + if (empty($hostname)) { + continue; // use empty hostname as alias for www + } + + $c .= "\n"; + $c .= "\n"; + $c .= " DocumentRoot /var/www/$domain" . "_$tld/$hostname.$zonename\n"; + $c .= " ServerName $hostname.$zonename\n"; + if ($hostname == 'www') { + $c .= " ServerAlias $zonename\n"; + } + $c .= " ErrorLog logs/$hostname.$zonename/error_log\n"; + $c .= " TransferLog logs/$hostname.$zonename/access_log\n"; + $c .= " php_admin_value upload_tmp_dir \"/var/tmp/www/$hostname.$zonename\"\n"; + $c .= " php_admin_value open_basedir \".:/usr/lib/php:/var/tmp/www/$hostname.$zonename:/var/www/$domain" . "_$tld/$hostname.$zonename\"\n"; + $c .= "\n"; + } + } + + return $c; +} + +/** + * Generate tinydns defs + */ +function _tinydns() +{ + $c = "# Generated with beatnik on " . date('Y-m-d h:i:s') . " by " . $GLOBALS['username'] . "\n\n"; + foreach ($GLOBALS['domains'] as $domain) { + // Get zone data + $zonedata = _getRecords($domain['zonename']); + $c .= '# ' . $domain['zonename'] . "\n"; + + // default SOA, NS + $c .= '.' . $domain['zonename'] . ':' . gethostbyname($domain['zonens']) . ':' . $domain['zonens'] . ':' . $domain['ttl'] . "\n"; + + // NS records + if (isset($zonedata['ns'])) { + foreach ($zonedata['ns'] as $id => $values) { + $c .= '.' . $domain['zonename'] . ':'. gethostbyname($values['pointer']) . ':' . $values['pointer'] . ':' . $values['ttl'] . "\n"; + } + } + + // MX records + if (isset($zonedata['mx'])) { + foreach ($zonedata['mx'] as $id => $values) { + $c .= '@' . $domain['zonename'] . ':'. gethostbyname($values['pointer']) . ':' . $values['pointer'] . ':' . $values['pref'] . ':' . $values['ttl'] . "\n"; + } + } + + // PTR records + if (isset($zonedata['ptr'])) { + foreach ($zonedata['ptr'] as $id => $values) { + $c .= '='; + $c .= $values['hostname'] . '.' . $domain['zonename'] . ':' . $values['pointer'] . ':' . $values['ttl'] . "\n"; + } + } + + // A records + if (isset($zonedata['a'])) { + foreach ($zonedata['a'] as $id => $values) { + $c .= '+'; + if ($values['hostname']) { + $c .= $values['hostname'] . '.'; + } + $c .= $domain['zonename'] . ':' . $values['ipaddr'] . ':' . $values['ttl'] . "\n"; + } + } + + // CNAME records + if (isset($zonedata['cname'])) { + foreach ($zonedata['cname'] as $id => $values) { + $c .= 'C'; + if ($values['hostname']) { + $c .= $values['hostname'] . '.'; + } + $c .= $domain['zonename'] . ':' . $values['pointer'] . ':' . $values['ttl'] . "\n"; + } + } + + $c .= "\n"; + } + + return $c; +} diff --git a/beatnik/scripts/sql/beatnik.mysql.php b/beatnik/scripts/sql/beatnik.mysql.php new file mode 100644 index 000000000..3c2f9d864 --- /dev/null +++ b/beatnik/scripts/sql/beatnik.mysql.php @@ -0,0 +1,132 @@ +-- +-- $Horde: beatnik/scripts/sql/beatnik.mysql.php,v 1.5 2006/08/13 18:52:47 duck Exp $ +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_a` +-- + +CREATE TABLE `beatnik_a` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) NOT NULL, + `ipaddr` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_cname` +-- + +CREATE TABLE `beatnik_cname` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) NOT NULL, + `pointer` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_mx` +-- + +CREATE TABLE `beatnik_mx` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `pointer` varchar(255) NOT NULL, + `pref` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_ns` +-- + +CREATE TABLE `beatnik_ns` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) default NULL, + `pointer` varchar(255) default '', + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_ptr` +-- + +CREATE TABLE `beatnik_ptr` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) NOT NULL, + `pointer` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_soa` +-- + +CREATE TABLE `beatnik_soa` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `zonens` varchar(255) NOT NULL, + `zonecontact` varchar(255) default NULL, + `serial` varchar(255) default NULL, + `refresh` int(10) unsigned default NULL, + `retry` int(10) unsigned default NULL, + `expire` varchar(255) default NULL, + `minimum` varchar(255) default NULL, + `ttl` int(11) NOT NULL default '3600', + PRIMARY KEY (`id`), + UNIQUE KEY `zonename` (`zonename`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_srv` +-- + +CREATE TABLE `beatnik_srv` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) NOT NULL, + `pointer` varchar(255) NOT NULL, + `priority` varchar(255) NOT NULL, + `weight` varchar(255) NOT NULL, + `port` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `beatnik_txt` +-- + +CREATE TABLE `beatnik_txt` ( + `id` int(10) unsigned NOT NULL auto_increment, + `zonename` varchar(255) NOT NULL, + `hostname` varchar(255) NOT NULL, + `text` varchar(255) NOT NULL, + `ttl` varchar(255) default NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/beatnik/templates/common-header.inc b/beatnik/templates/common-header.inc new file mode 100644 index 000000000..2c2253254 --- /dev/null +++ b/beatnik/templates/common-header.inc @@ -0,0 +1,41 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +// This check has to come after the page has finished all work in case the +// status has changed due to a now-completed edit. +if (count(Beatnik::needCommit())) { + foreach(Beatnik::needCommit() as $domain) { + $notification->push(sprintf(_("You have uncommitted changes in %s."), $domain)); + } +} +?> + + + + + +' : '' ?> + +get('name'); +if (!empty($title)) $page_title .= ' :: ' . $title; +if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) { + echo "\n"; +} + +Horde::includeScriptFiles(); + +?> +<?php echo htmlspecialchars($page_title) ?> + + + +> diff --git a/beatnik/templates/listzones/footer.inc b/beatnik/templates/listzones/footer.inc new file mode 100644 index 000000000..cd37a4b74 --- /dev/null +++ b/beatnik/templates/listzones/footer.inc @@ -0,0 +1,13 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ +?> + + +render(); ?> diff --git a/beatnik/templates/listzones/header.inc b/beatnik/templates/listzones/header.inc new file mode 100644 index 000000000..62e0b98a8 --- /dev/null +++ b/beatnik/templates/listzones/header.inc @@ -0,0 +1,54 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + */ +?> +
+

+isLocked('categories') || !$prefs->isLocked('category_colors'))) { + $categoryUrl = Horde_Util::addParameter(Horde::url($registry->get('webroot', 'horde') . '/services/prefs.php'), + array('app' => 'horde', 'group' => 'categories')); + echo Horde::link($categoryUrl, _("Edit domain groups and colors"), 'widget', '_blank') . + Horde::img('colorpicker.png', _("Edit categories and colors"), array('align' => 'absmiddle'), $registry->getImageDir('horde')) . ''; +} +?> +

+

+

    + +
  • + + +
  • + +
+

+
+ + + + + +' . $field['name'] . ''; +} +?> + + + + diff --git a/beatnik/templates/listzones/row.inc b/beatnik/templates/listzones/row.inc new file mode 100644 index 000000000..7146d397b --- /dev/null +++ b/beatnik/templates/listzones/row.inc @@ -0,0 +1,32 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + */ +?> + + + $field) { + if ($key == 'zonename') { + echo ''; + } else { + echo ''; + } +} +?> + diff --git a/beatnik/templates/menu.inc b/beatnik/templates/menu.inc new file mode 100644 index 000000000..172f9ca60 --- /dev/null +++ b/beatnik/templates/menu.inc @@ -0,0 +1,42 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +$accesskey = $prefs->getValue('widget_accesskey') ? Horde::getAccessKey(_("Select _Domain")) : ''; +$menu_view = $prefs->getValue('menu_view'); +?> + +notify(array('listeners' => 'status')) ?> diff --git a/beatnik/templates/view/footer.inc b/beatnik/templates/view/footer.inc new file mode 100644 index 000000000..02dda0e55 --- /dev/null +++ b/beatnik/templates/view/footer.inc @@ -0,0 +1,12 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ +?> + +
+ ' . + Horde::link($deleteurl) . + Horde::img('delete.png', _("Delete"), '', $img_dir) . ' ' . + Horde::link($editurl) . + Horde::img('edit.png', _("Edit"), '', $img_dir) . ''; + ?> +' . Horde::link($viewurl) . $domain['zonename'] . '' . $domain[$key] . '
diff --git a/beatnik/templates/view/header.inc b/beatnik/templates/view/header.inc new file mode 100644 index 000000000..78b63f456 --- /dev/null +++ b/beatnik/templates/view/header.inc @@ -0,0 +1,54 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +if (Beatnik::needCommit($_SESSION['beatnik']['curdomain']['zonename'])) { + $commit = Horde_Util::addParameter(Horde::applicationUrl('commit.php'), array('domain' => 'current')); + echo Horde::link($commit, _('Commit Changes'), 'button') . _('Commit Changes') . '

'; +} +?> + +
+ $_SESSION['beatnik']['curdomain']['id'], + 'rectype' => 'soa'); +echo $_SESSION['beatnik']['curdomain']['zonename'] . ' ' + . Horde::link(Horde_Util::addParameter($edit, $params)) + . Horde::img('edit.png', _("Edit"), '', $img_dir) . ' ' + . Horde::link(Horde_Util::addParameter($delete, $params)) + . Horde::img('delete.png', _("Delete"), '', $img_dir) . ''; +?> +
+ +' . "\n"; + foreach (Beatnik::getRecFields('soa') as $key => $value) { + if (isset($_SESSION['beatnik']['curdomain'][$key])) { + echo '' . $value['name'] . '' . $_SESSION['beatnik']['curdomain'][$key] . '' . "\n"; + } + } + echo '
' . "\n"; +} +?> + + + + + + $fdata) { + if ((($fdata['infoset'] == 'basic') || $_SESSION['beatnik']['expertmode']) && $fdata['type'] != 'hidden') { + echo '' . "\n"; + } + } + ?> + + + diff --git a/beatnik/templates/view/record.inc b/beatnik/templates/view/record.inc new file mode 100644 index 000000000..489e8b5c9 --- /dev/null +++ b/beatnik/templates/view/record.inc @@ -0,0 +1,45 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +foreach ($zonedata[$type] as $record) { + + $params = array('id' => $record['id'], 'rectype' => $type); + echo '' . "\n"; + echo ''; + echo ''; + + foreach ($fields as $field => $fdata) { + + if ((($fdata['infoset'] != 'basic') && !$_SESSION['beatnik']['expertmode']) || $fdata['type'] == 'hidden') { + continue; + } + + echo '' . "\n"; + } + + echo '' . "\n"; +} diff --git a/beatnik/themes/graphics/beatnik.png b/beatnik/themes/graphics/beatnik.png new file mode 100644 index 0000000000000000000000000000000000000000..7020eb9906160df3a436b04cee62596d8e99d508 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl4821PGgt!7}c3u%Kc~dq)838R9 z35y6Z<3K6fcwRYu3A0c(UJ+Tl1O=xQRkt(=EgSv7B3V^kdslC3JIBnNd}Wtp4WC>Y zQy)#w4E@k@UU3DZurlM&V!PxvyVy#ff{C6v-7ZP>e))ZJ>iV_Q*BM7t==!AFrL@>3 zH+vOKh-h3CT{ShXc6#lct+L7*6ISi-TfWD^JLK4EyoFD~41YW+q+`-!o3$~Z#(JiAHL?)Jri5!wMVb5J$iN3p-U?dUfgr_ z`I4O{)*rpP=hDMN*PrdaaL>xY<-o-?4Ao z{-f6(JP!yC&1{>w`o!%`=N~QFb9U*zbNg?;TzB;H?n@8$U4C%<&dVb=o}9S-{Me1h zCvHADcmLI$mmhauym#r|)4JA9b6bbmE7o6q^2XWKEj%ufjT02J|NsAAG{tulkTta= z$S)Yk9dv-fEJfM^=)kj{E{-7*;mI?a*d!cUP9HhN>D1&^&=~V@)1-5M4ixGD;vLmD|*77IdMw4zxP(tixWN; zUVX;GDbYQ%@=DgqmEK3(P8&6|EHmiZ{nC52M`u)|v0;O(+04~mjqTwr(Npt3rF2|Y tXD{Bcaq}jL#vcObeP{Bpaoks8X4of|ZmU}Mw;1R#22WQ%mvv4FO#q@-?)Crx literal 0 HcmV?d00001 diff --git a/beatnik/themes/graphics/commit-all.png b/beatnik/themes/graphics/commit-all.png new file mode 100644 index 0000000000000000000000000000000000000000..5237dab4f6b3e2996238f500ce59950454fbde26 GIT binary patch literal 430 zcmV;f0a5;mP); z=jZeD^RUmFxYVTa@$s0Ln5oH=;^N};^z@sXo5RDygt4qgolTRmX?;1l9Io_zo@9F$=$nDcRj}3x092T z)6T$!g@xwk=J4?Ff`WqI-`|~`ozmmR?Ck7=gM-b@&B@-oh=_>m>+AOR_LP*A?d|RQ z`udfXm6w;7hK7d1!ovIe`+$IezP`S^yu4t6NREz<`T6s2b!+kolp-)!Rl^W3VR$|aq6op+ z2EldY`>Uy@Jwyc3d}4Ko&3D`nH8Z2dQ!`PWkcHd!Pr|K1ruPnKiyLXiR&uL1gK6aW#CxWJFhT3&Zn39 Y07^tI{L1O>r~m)}07*qoM6N<$f+yzda{vGU literal 0 HcmV?d00001 diff --git a/beatnik/themes/screen.css b/beatnik/themes/screen.css new file mode 100644 index 000000000..c7b4f1970 --- /dev/null +++ b/beatnik/themes/screen.css @@ -0,0 +1,14 @@ +/** + * $Horde: beatnik/themes/screen.css,v 1.5 2006/12/18 05:56:52 chuck Exp $ + */ + +.zoneData { + border: 1px solid #000; + padding: 2px; +} + +.soa { + border: 1px solid #111; + padding: 2px; + font-size: 120% +} diff --git a/beatnik/viewzone.php b/beatnik/viewzone.php new file mode 100644 index 000000000..a4459e723 --- /dev/null +++ b/beatnik/viewzone.php @@ -0,0 +1,56 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + */ + +define('BEATNIK_BASE', dirname(__FILE__)); +require_once BEATNIK_BASE . '/lib/base.php'; +require_once BEATNIK_BASE . '/lib/Beatnik.php'; + +$zonedata = $beatnik_driver->getRecords($_SESSION['beatnik']['curdomain']['zonename']); +if (is_a($zonedata, 'PEAR_Error')) { + $notification->push($zonedata, 'horde.error'); + header('Location:' . Horde::applicationUrl('listzones.php')); + exit; +} + +$title = $_SESSION['beatnik']['curdomain']['zonename']; +Horde::addScriptFile('stripe.js', 'horde', true); +require BEATNIK_TEMPLATES . '/common-header.inc'; +require BEATNIK_TEMPLATES . '/menu.inc'; + +// Get a list of all the fields for all record typess we'll be processing +$fields = array(); +foreach ($zonedata as $type => $data) { + $fields = array_merge($fields, Beatnik::getRecFields($type)); +} + +// Remove fields that should not be shown +foreach ($fields as $field_id => $field) { + if ($field['type'] == 'hidden' || + ($field['infoset'] != 'basic' && !$_SESSION['beatnik']['expertmode'])) { + unset($field[$field_id]); + } +} + +$img_dir = $registry->getImageDir('horde'); +$delete = Horde_Util::addParameter(Horde::applicationUrl('delete.php'), 'curdomain', $_SESSION['beatnik']['curdomain']['zonename']); +$edit = Horde_Util::addParameter(Horde::applicationUrl('editrec.php'), 'curdomain', $_SESSION['beatnik']['curdomain']['zonename']); +$rectypes = Beatnik::getRecTypes(); + +require BEATNIK_TEMPLATES . '/view/header.inc'; +foreach ($rectypes as $type => $typedescr) { + if (!isset($zonedata[$type])) { + continue; + } + require BEATNIK_TEMPLATES . '/view/record.inc'; +} +require BEATNIK_TEMPLATES . '/view/footer.inc'; + + +require $registry->get('templates', 'horde') . '/common-footer.inc'; -- 2.11.0
' . $fdata['name'] . '
' . Horde::link(Horde_Util::addParameter($edit, $params)) + . Horde::img('edit.png', _("Edit"), '', $img_dir) . ' ' + . Horde::link(Horde_Util::addParameter($delete, $params)) + . Horde::img('delete.png', _("Delete"), '', $img_dir) . '' . $rectypes[$type] . '' . "\n"; + + if (!isset($record[$field])) { + continue; + } + + if (is_array($record[$field])) { + foreach ($record[$field] as $entry) { + echo $entry; + } + } else { + echo $record[$field]; + } + + echo '