From cc8a26960a670ce8795d9575c5fc6417c40e3eb1 Mon Sep 17 00:00:00 2001
From: Paolo Asperti <paolo@asperti.com>
Date: Mon, 18 Jul 2016 14:41:10 +0200
Subject: [PATCH] PHP rewrite, first release

---
 da_integrare.spec                             |  97 +++++
 defaults/access                               |   1 -
 doraemon.ini                                  |  16 -
 doraemon.logrotate                            |   6 -
 doraemon.py                                   | 234 -----------
 doraemon/EsmithDatabase.php                   | 391 ++++++++++++++++++
 doraemon/index.php                            | 254 ++++++++++++
 initrd                                        |  79 ----
 .../defaults/doraemon/DefaultRole             |   1 +
 .../defaults/doraemon/DomainFile              |   1 +
 .../defaults/doraemon/ManagementKeyFile       |   1 +
 .../defaults/doraemon/NamingBase              |   1 +
 .../defaults/doraemon/NamingDigits            |   1 +
 .../configuration/defaults/doraemon}/TCPPort  |   0
 .../defaults/doraemon/VaultPassFile           |   1 +
 .../db/configuration/defaults/doraemon/access |   1 +
 .../configuration/defaults/doraemon}/status   |   0
 .../db/configuration/defaults/doraemon}/type  |   0
 .../doraemon/httpd.conf/00template_defaults   |  10 +
 .../etc/httpd/doraemon/httpd.conf/20Manager   | 127 ++++++
 .../httpd/doraemon/httpd.conf/40DefaultAccess |  16 +
 .../doraemon/httpd.conf/60DoraemonInterface   |  23 ++
 .../etc/httpd/doraemon/httpd.conf/90include   |   1 +
 root/etc/httpd/doraemon/.gitignore            |   0
 root/etc/logrotate.d/doraemon                 |   9 +
 root/etc/sysconfig/doraemon                   |   8 +
 .../systemd/system/doraemon-reload.service    |   9 +
 .../lib/systemd/system/doraemon-reload.timer  |   9 +
 root/usr/lib/systemd/system/doraemon.service  |  14 +
 root/usr/sbin/doraemon                        |   1 +
 30 files changed, 976 insertions(+), 336 deletions(-)
 create mode 100644 da_integrare.spec
 delete mode 100644 defaults/access
 delete mode 100644 doraemon.ini
 delete mode 100644 doraemon.logrotate
 delete mode 100644 doraemon.py
 create mode 100644 doraemon/EsmithDatabase.php
 create mode 100644 doraemon/index.php
 delete mode 100644 initrd
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits
 rename {defaults => root/etc/e-smith/db/configuration/defaults/doraemon}/TCPPort (100%)
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile
 create mode 100644 root/etc/e-smith/db/configuration/defaults/doraemon/access
 rename {defaults => root/etc/e-smith/db/configuration/defaults/doraemon}/status (100%)
 rename {defaults => root/etc/e-smith/db/configuration/defaults/doraemon}/type (100%)
 create mode 100644 root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults
 create mode 100644 root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager
 create mode 100644 root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess
 create mode 100644 root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface
 create mode 100644 root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include
 create mode 100644 root/etc/httpd/doraemon/.gitignore
 create mode 100644 root/etc/logrotate.d/doraemon
 create mode 100644 root/etc/sysconfig/doraemon
 create mode 100644 root/usr/lib/systemd/system/doraemon-reload.service
 create mode 100644 root/usr/lib/systemd/system/doraemon-reload.timer
 create mode 100644 root/usr/lib/systemd/system/doraemon.service
 create mode 120000 root/usr/sbin/doraemon

diff --git a/da_integrare.spec b/da_integrare.spec
new file mode 100644
index 0000000..0742cb1
--- /dev/null
+++ b/da_integrare.spec
@@ -0,0 +1,97 @@
+Summary: apache/mod_php stack for Doraemon
+Name: doraemon
+Version: 1.3.0.1
+Release: 1%{?dist}
+License: GPL
+URL: %{url_prefix}/%{name}
+BuildArch: noarch
+
+Source0: %{name}-%{version}.tar.gz
+
+
+BuildRequires: nethserver-devtools
+
+BuildRequires: systemd
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+
+Requires: httpd, php, sudo, php-xml, php-mcrypt, nethserver-httpd-admin
+Requires: nethserver-base, nethserver-php
+Requires: nethserver-lang-en
+
+%description
+Runs an Apache instance on port 3000 that serves
+the doraemon web application
+
+%prep
+%setup
+
+
+# Nethgui:
+cd %{_builddir}/nethgui-%{nethgui_commit}
+
+%build
+perl createlinks
+mkdir -p root/%{_nseventsdir}/%{name}-update
+
+%install
+(cd root ; find . -depth -print | cpio -dump %{buildroot})
+rm -f %{name}-%{version}-%{release}-filelist
+%{genfilelist} %{buildroot} \
+    > %{name}-%{version}-%{release}-filelist
+mkdir -p %{buildroot}/%{_localstatedir}/log/doraemon
+mkdir -p %{buildroot}/%{_localstatedir}/cache/doraemon
+
+# Copy the server-manager dir
+mkdir -p %{buildroot}%{_nsuidir}
+cp -av nethserver-manager %{buildroot}%{_nsuidir}/nethserver-manager
+
+%files -f %{name}-%{version}-%{release}-filelist
+%defattr(-,root,root)
+%doc %{extradocs}
+
+%{_nsuidir}/nethserver-manager
+
+
+%dir %{_nseventsdir}/%{name}-update
+%dir %{_sysconfdir}/httpd/doraemon.d
+
+%attr(0750,srvmgr,srvmgr) %dir %{_localstatedir}/cache/doraemon
+%attr(0644,root,root) %ghost %{_sysconfdir}/init/doraemon.conf
+%attr(0644,root,root) %ghost %{_sysconfdir}/httpd/doraemon/httpd.conf
+%attr(0700,root,root) %dir %{_localstatedir}/log/doraemon
+%attr(0644,root,root) %config %ghost %{_localstatedir}/log/doraemon/access_log
+%attr(0644,root,root) %config %ghost %{_localstatedir}/log/doraemon/error_log
+%config(noreplace) /etc/sysconfig/doraemon
+
+%post
+%systemd_post doraemon.service
+
+%preun
+%systemd_preun doraemon.service
+
+%postun
+%systemd_postun
+
+%changelog
+* Tue Oct 27 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.1-2.ns6
+- Packing corrections to rpm to fix upgrading issue
+
+* Tue Oct 27 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.1-1.ns6
+- Packing new minor correction on how packages are extracted from db
+
+* Mon Sep 11 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.0-1.ns6
+- Packing new version with ansible dynamic inventory support, epoptes server role support.
+
+* Mon Sep 07 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.1.1-1.ns6
+- Packing new version with correct roles.
+
+* Sun Sep 06 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.1.0-1.ns6
+- Packing new version with new functionalities.
+
+* Wed Jul 15 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.0.0-2.ns6
+- Added logrotate file.
+
+* Mon Jul 13 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.0.0-1.ns6
+- Unique binary for both ansiblehelper and mac2hostname.
diff --git a/defaults/access b/defaults/access
deleted file mode 100644
index 3e18ebf..0000000
--- a/defaults/access
+++ /dev/null
@@ -1 +0,0 @@
-private
diff --git a/doraemon.ini b/doraemon.ini
deleted file mode 100644
index a9bbdc8..0000000
--- a/doraemon.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[Daemon]
-Database = /var/lib/doraemon/doraemon.db
-LogFile = /var/log/doraemon.log
-PIDFile = /var/run/doraemon.pid
-BindAddress = 0.0.0.0
-Port = 3000
-
-[NameSettings]
-Base = lab
-Role = client
-Digits = 2
-
-[Files]
-Domain = /etc/domain.yml
-MgmtKey = /home/amgmt/.ssh/id_rsa.pub
-VaultPassFile = /home/amgmt/.ansible/vault.txt
diff --git a/doraemon.logrotate b/doraemon.logrotate
deleted file mode 100644
index 443f997..0000000
--- a/doraemon.logrotate
+++ /dev/null
@@ -1,6 +0,0 @@
-/var/log/doraemon.log {
-	daily
-	rotate 15
-	compress
-	missingok
-}	
diff --git a/doraemon.py b/doraemon.py
deleted file mode 100644
index 459c316..0000000
--- a/doraemon.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""Helps a client to join domain and maintain itself."""
-__author__ = 'BgLUG'
-__email__ = 'info@bglug.it'
-
-from contextlib import contextmanager, closing
-from subprocess import Popen, PIPE
-from sqlite3 import connect
-from bottle import Bottle, request
-from json import dumps
-from ConfigParser import ConfigParser
-from Crypto.Hash import MD5
-from Crypto.Cipher import AES
-import base64
-import json
-import sys
-import re
-import os
-
-class MyApp:
-  def __init__(self, configfile = '/etc/mac2hostname.ini'):
-    if not os.path.isfile(configfile):
-      print "Cannot find main file, %s. Exiting." % configfile
-      sys.exit(1)
-
-    # Loads configurations
-    config = ConfigParser()
-    config.read(configfile)
-    # Populates private properties with configuration
-    self.__dbfile = config.get('Daemon', 'Database') if config.has_option('Daemon', 'Database') else '/var/lib/doraemon/doraemon.db'
-    self.__logdir = config.get('Daemon', 'LogFile') if config.has_option('Daemon', 'LogFile') else '/var/log/doraemon.log'
-    self.__pidfile = config.get('Daemon', 'PIDFile') if config.has_option('Daemon', 'PIDFile') else '/var/run/doraemon.pid'
-    self.__bindaddress = config.get('Daemon', 'BindAddress') if config.has_option('Daemon', 'BindAddress') else '127.0.0.1'
-    self.__port = config.getint('Daemon', 'Port') if config.has_option('Daemon',
-    'Port') else 8080
-    self.__defaultbase = config.get('NameSettings', 'Base') if config.has_option('NameSettings', 'Base') else 'client'
-    self.__defaultrole = config.get('NameSettings', 'Role') if config.has_option('NameSettings', 'Role') else 'client'
-    self.__namedigits = config.get('NameSettings', 'Digits') if config.has_option('NameSettings', 'Digits') else '2'
-    self.__domainfile = config.get('Files', 'Domain') if config.has_option('Files', 'Domain') else None
-    self.__mgmtfile = config.get('Files', 'MgmtKey') if config.has_option('Files', 'MgmtKey') else None
-    self.__vaultpassfile = config.get('Files', 'VaultPassFile') if config.has_option('Files', 'VaultPassFile') else None
-    self.__app = Bottle()
-    # Applies routes
-    self.__route()
-    # Assures table creation
-    self.__init_tables()
-
-  @contextmanager
-  def __getcursor(self):
-    with connect(self.__dbfile) as connection:
-      with closing(connection.cursor()) as cursor:
-        yield cursor
-
-  # Private methods
-  def __route(self):
-    self.__app.get('/mac2hostname', callback=self.mac2hostname)
-    self.__app.get('/whatsmyhostname', callback=self.whatsmyhostname)
-    self.__app.get('/hosts', callback=self.hosts)
-    self.__app.get('/domain', callback=self.domain)
-    self.__app.get('/mgmtkey', callback=self.mgmtkey)
-    self.__app.get('/vaultpass', callback=self.vaultpass)
-    self.__app.get('/ansible_list', callback=self.ansible_list)
-    self.__app.get('/ansible_host', callback=self.ansible_host)
-    self.__app.get('/epoptes-srv', callback=self.epoptes_srv)
-
-  def __init_tables(self):
-    with self.__getcursor() as cursor:
-      cursor.execute('CREATE TABLE IF NOT EXISTS client (id INT PRIMARY KEY,'
-                       'hostname TEXT NOT NULL UNIQUE, mac TEXT UNIQUE, role TEXT)')
-      cursor.execute('CREATE INDEX IF NOT EXISTS idxmac ON client(mac)')
-
-  def __getmac(self, ip):
-    Popen(['ping', '-c1', '-t2', ip], stdout=PIPE).communicate()
-    arp = Popen(['arp', '-n', ip], stdout=PIPE).communicate()[0]
-    return re.search(r'(([\da-fA-F]{1,2}\:){5}[\da-fA-F]{1,2})', arp).group(1).lower()
-    
-  def __normalizemac(self, mac):
-    return ':'.join(x.zfill(2) for x in mac.split('_')).lower()
-
-  def __gethostname(self, mac, base = None, role = None):
-    if base == None:
-      base = self.__defaultbase
-    if role == None:
-      role = self.__defaultrole
-
-    with self.__getcursor() as cursor:
-      (newid,) = cursor.execute('SELECT COALESCE(MAX(id) + 1, 1) FROM client').fetchone()
-      # Constucts the hostname format
-      formatstring = '%s-%0' + self.__namedigits + 'd'
-      data = (newid, formatstring % (base, newid), mac, role)
-      # Since MAC is Unique, this fails with the same MAC address
-      cursor.execute('INSERT OR IGNORE INTO client VALUES (?, ?, ?, ?)', data)
-      (hostname,) = cursor.execute('SELECT hostname FROM client WHERE mac = "%s"' % mac).fetchone()
-      return hostname
-
-  # This should be used only after registration of the client
-  # (whatsmyhostname)
-  def __getrole(self):
-    macaddress = self.__normalizemac(self.__getmac(request['REMOTE_ADDR']))
-    hostname = self.__gethostname(macaddress)
-
-    with self.__getcursor() as cursor:
-      (role,) = cursor.execute("SELECT role FROM client WHERE hostname = '%s' AND mac = '%s'" % (hostname, macaddress)).fetchone()
-    return role
-
-  # Variables returned to the client
-  def __rolevars(self, role=None):
-    retval = {'role': role, 'addpkg': [], 'delpkg': []}
-    try:
-      db = Popen(['/sbin/e-smith/db', 'roles', 'getjson'], stdout=PIPE).communicate()[0]
-      for data in json.loads(db):
-          if data['name'] == role:
-              retval['addpkg'] = re.split(r'[\s,;]+', data['props']['Addpkg'])
-              retval['delpkg'] = re.split(r'[\s,;]+', data['props']['Delpkg'])
-              continue
-    except: pass
-    return retval
-
-  # Instance methods AKA routes
-  def mac2hostname(self):
-    # Ensure required parameters have been passed
-    if not request.query.mac:
-      return "Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ]"
-    # Sets up variables for possible parameters
-    mac = self.__normalizemac(request.query.mac)
-    base = request.query.base or self.__defaultbase
-    role = request.query.role or self.__defaultrole
-    return self.__gethostname(mac, base, role)
-
-  def whatsmyhostname(self):
-    # No required parameters
-    ip = request.query.ip or request['REMOTE_ADDR']
-    base = request.query.base or self.__defaultbase
-    role = request.query.role or self.__defaultrole
-    return self.__gethostname(self.__getmac(ip), base, role)
-
-  def hosts(self):
-    # Default where clause: no where specifications
-    where = ''
-    # If a role parameter is passed, list the hosts for that role
-    if request.query.role: 
-      where = "WHERE role = '%s'" % request.query.role
-    with self.__getcursor() as cursor:
-      return dumps([dict((meta[0], data)
-        for meta, data in zip(cursor.description, row))
-          for row in cursor.execute('SELECT role, hostname, mac FROM client '
-              + where + 'ORDER BY role ASC, hostname ASC')], indent=4)
-
-  def domain(self):
-    with open(self.__domainfile, 'r') as f:
-      return f.read()
-
-  def mgmtkey(self):
-    with open(self.__mgmtfile, 'r') as f:
-      return f.read()
-
-  def vaultpass(self):
-    with open(self.__vaultpassfile, 'r') as f:
-      ip = request.query.ip or request['REMOTE_ADDR']
-      base = request.query.base or self.__defaultbase
-      role = request.query.role or self.__defaultrole
-      hostname = self.__gethostname(self.__getmac(ip), base, role)
-      # Encryption key
-      key = MD5.new(hostname).digest()
-      # Secret
-      secret = f.read().strip()
-      # Creating a secret that is multiple of 16 in length
-      i = 16 - (len(secret) % 16)
-      lengthy_secret = secret + i * 'x'
-      cipher = AES.new(key, AES.MODE_ECB)
-      crypted = cipher.encrypt(lengthy_secret)
-      # Encode in base64 because of unicode strings
-      return base64.b64encode(crypted)
-
-  def ansible_list(self):
-    # Faking calls
-    if request.query.role:
-      role = request.query.role
-    else:
-      role = self.__getrole()
-
-    result = {
-        'localhost': {
-          'hosts':  [ 'localhost' ],
-          'vars': {
-            'ansible_connection': 'local'
-          }
-        },
-        '_meta': {
-          'hostvars': {
-            'localhost': self.__rolevars(role)
-          }
-        }
-    }
-    return dumps(result)
-
-  def ansible_host(self):
-    # Ignoring 'host' parameter, since it should always be 'localhost'.
-    # Instead, for testing purposes, if asked for a role, use it
-    if request.query.role:
-      role = request.query.role
-    else:
-      role = self.__getrole()
-    return dumps(self.__rolevars(role))
-
-  def epoptes_srv(self):
-    hostname = None
-    try:
-      with self.__getcursor() as cursor:
-        (hostname,) = cursor.execute('SELECT hostname FROM client WHERE role = \'docenti\' ORDER BY id DESC LIMIT 1').fetchone()
-    except TypeError:
-      return 'none'
-
-    return hostname
-
-  def start(self):
-    # Opens up a PID file
-    pid = os.getpid()
-    pidfile = open(self.__pidfile, 'w')
-    pidfile.write('%s' % pid)
-    pidfile.close()
-
-    # Runs Bottle, at last.
-    self.__app.run(host=self.__bindaddress, port=self.__port)
-
-# Main body
-if __name__ == '__main__':
-  cfgfile = '/etc/doraemon.ini'
-  if len(sys.argv) > 1:
-    cfgfile = sys.argv[1]
-  app = MyApp(cfgfile)
-  app.start()
diff --git a/doraemon/EsmithDatabase.php b/doraemon/EsmithDatabase.php
new file mode 100644
index 0000000..e0e0268
--- /dev/null
+++ b/doraemon/EsmithDatabase.php
@@ -0,0 +1,391 @@
+<?php
+
+/*
+ * Copyright (C) 2011 Nethesis S.r.l.
+ * 
+ * This script is part of NethServer.
+ * 
+ * NethServer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * NethServer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with NethServer.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Read and write parameters into SME DB
+ *
+ * Ths class implements an interface to SME database executing the command /sbin/e-smith/db with sudo.
+ * The class needs /etc/sudoers configurazione. In the sudoers file you must have something like this:
+ * <code>
+ * Cmnd_Alias SME = /sbin/e-smith/db, /sbin/e-smith/signal-event
+ * www ALL=NOPASSWD: SME
+ * </code>
+ *
+ *
+ * @author Giacomo Sanchietti <giacomo.sanchietti@nethesis.it>
+ * @author Davide Principi <davide.principi@nethesis.it>
+ * @since 1.0
+ * @internal
+ */
+class EsmithDatabase
+{
+
+    /**
+     * @var SME DB database command
+     * */
+    private $command = "/usr/bin/sudo /sbin/e-smith/db";
+
+    /**
+     * @var $db Database name, it's translated into the db file path. For example: /home/e-smith/db/testdb
+     * */
+    private $db;
+
+    /**
+     * This static class variable shares a socket connection to smwingsd
+     *
+     * @var resource
+     */
+    private static $socket;
+
+
+
+
+    /**
+     * Construct an object to access a SME Configuration database file
+     * with $user's privileges.
+     * 
+     * @param string $database Database name
+     */
+    public function __construct($database)
+    {
+        if ( ! $database)
+            sprintf("%s: You must provide a valid database name.", get_class($this));
+
+        $this->db = $database;
+    }
+
+    public function getAll($type = NULL)
+    {
+        $output = "";
+
+        $ret = $this->dbRead('getjson', array(), $output);
+        if ($ret !== 0) {
+            sprintf("%s: internal database command failed!", __CLASS__);
+        }
+
+        $data = json_decode($output, TRUE);
+        if ( ! is_array($data)) {
+            sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
+        }
+
+        $result = array();
+
+        foreach ($data as $item) {
+            // Apply type check filter:
+            if (isset($type) && $type !== $item['type']) {
+                continue;
+            }
+            $props = isset($item['props']) ? $item['props'] : array();
+            $result[$item['name']] = array_merge($props, array('type' => $item['type']));
+        }
+
+        return $result;
+    }
+
+    public function getAllByProp($propName = NULL, $propValue = NULL)
+    {
+        $output = "";
+
+        $ret = $this->dbRead('getjson', array(), $output);
+        if ($ret !== 0) {
+            sprintf("%s: internal database command failed!", __CLASS__);
+        }
+
+        $data = json_decode($output, TRUE);
+        if ( ! is_array($data)) {
+            sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
+        }
+
+        $result = array();
+
+        foreach ($data as $item) {
+            $item['props']['type'] = $item['type'];
+            $item['props']['name'] = $item['name'];
+            
+            if (isset($propName) && isset($propValue)) {
+                if (isset($item['props'][$propName]) && $propValue == $item['props'][$propName]) {
+                    $result[$item['name']] = $item['props'];  
+                }
+            } else {
+                $result[$item['name']] = $item['props'];  
+            }
+        }
+
+        return $result;
+    }
+    
+    public function getKey($key)
+    {
+        $output = '';
+
+        $ret = $this->dbRead('getjson', array($key), $output);
+        if ($ret !== 0) {
+            sprintf("%s: internal database command failed", __CLASS__);
+        }
+
+        $data = json_decode($output, TRUE);
+
+        if ($data === 1) {
+            // Key has not been found
+            return array();
+        } elseif ( ! is_array($data)) {
+            sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
+        } 
+        
+        $data['props']['type'] = $data['type'];
+        $data['props']['name'] = $data['name'];
+        return $data['props'];
+    }
+    
+    public function getKeyValue($key)
+    {
+        $output = '';
+
+        $ret = $this->dbRead('getjson', array($key), $output);
+        if ($ret !== 0) {
+            sprintf("%s: internal database command failed", __CLASS__);
+        }
+
+        $data = json_decode($output, TRUE);
+
+        if ($data === 1) {
+            // Key has not been found
+            return array();
+        } elseif ( ! is_array($data)) {
+            sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
+        } 
+        
+        return $data['type'];
+    }
+
+    public function setKey($key, $type, $props)
+    {
+        $output = NULL;
+        $ret = $this->dbExec('set', $this->prepareArguments($key, $type, $props), $output);
+        return ($ret == 0);
+    }
+
+    public function deleteKey($key)
+    {
+        $output = NULL;
+        $output = NULL;
+        $ret = $this->dbExec('delete', $this->prepareArguments($key), $output);
+        return ($ret == 0);
+    }
+
+    /**
+     * Return the type of a key
+     * Act like: /sbin/e-smith/db dbfile gettype key
+     * 
+     * @param string $key the key to retrieve
+     * @access public
+     * @return string the type of the key
+     */
+    public function getType($key)
+    {
+        $output = NULL;
+        $ret = $this->dbRead('gettype', array($key), $output);
+        return trim($output);
+    }
+
+    /**
+     * Set the type of a key 
+     * Act like: /sbin/e-smith/db dbfile settype key type
+     * 
+     * @param string $key the key to change
+     * @param string $type the new type
+     * @access public
+     * @return bool true on success, FALSE otherwise
+     */
+    public function setType($key, $type)
+    {
+        $output = NULL;
+        $ret = $this->dbExec('settype', $this->prepareArguments($key, $type), $output);
+        return ($ret == 0);
+    }
+
+    public function getProp($key, $prop)
+    {
+        $output = NULL;
+        $ret = $this->dbRead('getprop', array($key, $prop), $output);
+        return trim($output);
+    }
+
+    public function setProp($key, $props)
+    {
+        $output = NULL;
+        $ret = $this->dbExec('setprop', $this->prepareArguments($key, $props), $output);
+        return ($ret == 0);
+    }
+
+    public function delProp($key, $props)
+    {
+        $output = NULL;
+        $ret = $this->dbExec('delprop', array_merge(array($key), array_values($props)), $output);
+        return ($ret == 0);
+    }
+
+    /**
+     * Execute the db command
+     * @param string $command The command to invoke
+     * @param array $args The command arguments
+     * @param string &$output The output from the command process
+     * @return int  The command exit code
+     */
+    private function dbExec($command, $args, &$output)
+    {
+        // prepend the database name and command
+        array_unshift($args, $this->db, $command);
+        $exitCode = 0;
+        $oArr = array();
+        exec($this->command . ' ' . implode(' ', array_map('escapeshellarg', $args)), $oArr, $exitCode);
+        $output = implode("\n", $oArr);
+        return $exitCode;
+    }
+
+    /**
+     * Read db data from memory cache daemon
+     * @param string $command The command to invoke
+     * @param array $args The command arguments
+     * @param string &$output The output from the read socket
+     * @return int  The command exit code
+     */
+    private function dbRead($command, $args, &$output)
+    {
+        if ( ! isset(self::$socket) ) {
+            $socketPath = '/var/run/smwingsd.sock';
+            $errno = 0;
+            $errstr = '';
+            self::$socket = fsockopen('unix://' . $socketPath, -1, $errno, $errstr);
+            if( ! is_resource(self::$socket)) {
+                sprintf("Invalid socket (%d): %s. Fall back to exec().", $errno, $errstr);
+            }
+        }
+         
+        if ( ! is_resource(self::$socket)) {
+            return $this->dbExec($command, call_user_func_array(array($this, 'prepareArguments'), $args), $output);            
+        }
+
+        // prepend the database name and command
+        array_unshift($args, $this->db, $command);
+
+        $ret =  $this->sendMessage(self::$socket, 0x10, $args);
+        if ( ! $ret ) {
+            return 1;
+        }
+        $ret = $this->recvMessage(self::$socket);
+        if ( ! $ret ) {
+            return 1;
+        }
+        if ($command === 'getjson') {
+            $output = $ret;
+        } else {
+            $output = json_decode($ret, TRUE);
+        }
+        
+        return 0;
+    }
+
+    private function sendMessage($socket, $type, $args = array())
+    {
+        $payload = json_encode($args);
+        $data = pack('CN', (int) $type, strlen($payload)) . $payload;
+        $written = fwrite($socket, $data);
+        if ($written !== strlen($data)) {
+            echo 'Socket write error';
+        }
+        return TRUE;
+    }
+
+    private function recvMessage($socket)
+    {
+        $buf = $this->safeRead($socket, 5);
+        if ($buf === FALSE) {
+            echo 'Socket read error';
+            return false;
+        }
+
+        $header = unpack('Ctype/Nsize', $buf);
+        if ( ! is_array($header)) {
+            echo 'Invalid message header';
+            return false;
+        }
+
+        $message = $this->safeRead($socket, $header['size']);
+        if ($message === FALSE) {
+            echo 'Socket read error';
+            return false;
+        }
+
+        if ($header['type'] & 0x02) {
+            return NULL;
+        }
+
+        return $message;
+    }
+
+    private function safeRead($socket, $size) 
+    {
+        $buffer = "";
+        $count = 0;
+        while($count < $size) {
+            if(feof($socket)) {
+                return FALSE;
+            }
+            $chunk = fread($socket, $size - $count);
+            $count += strlen($chunk);
+            if($chunk === FALSE) {
+                return FALSE;
+            }
+            $buffer .= $chunk;
+        }
+        return $buffer;
+    }
+
+
+    /**
+     * Take arbitrary arguments and flattenize to an array
+     *
+     * @param mixed $_
+     * @return array
+     */
+    private function prepareArguments()
+    {
+        $args = array();
+
+        foreach (func_get_args() as $arg) {
+            if (is_array($arg)) {
+                foreach ($arg as $propName => $propValue) {
+                    $args[] = $propName;
+                    $args[] = $propValue;
+                }
+            } else {
+                $args[] = (String) $arg;
+            }
+        }
+
+        return $args;
+    }
+
+
+
+
+}
\ No newline at end of file
diff --git a/doraemon/index.php b/doraemon/index.php
new file mode 100644
index 0000000..41daa53
--- /dev/null
+++ b/doraemon/index.php
@@ -0,0 +1,254 @@
+<?php
+include_once("EsmithDatabase.php");
+define('CONFIG_KEY', 'doraemon');
+
+$hosts_db = new EsmithDatabase('hosts');
+$roles_db = new EsmithDatabase('roles');
+$config_db = new EsmithDatabase('configuration');
+
+$uri = $_SERVER['REQUEST_URI'];
+// add leading '/' to uri string, works if $uri is empty
+$uri = (substr($uri, 0, 1) == '/') ? $uri : '/' . $uri;
+$pos = strpos($uri, '?');
+if (false !== $pos) {
+    $route = substr($uri, 0, (int) $pos);
+} else {
+    $route = $uri;
+}
+// remove trailing '/'
+$route = rtrim($route, '/');
+
+$query_string = $_SERVER['QUERY_STRING'];
+$get_params = array();
+parse_str($query_string, $get_params);
+
+// determine internal function name (all lowercase, dash2underscore, named ROUTE_routename )
+$function = preg_replace(':\-:', '_', strtolower($route));
+$function = preg_replace(':^/:', 'ROUTE_', $function);
+
+if (function_exists($function)) {
+    $function();
+} else {
+    die('Go away.');
+}
+
+function varsForRole($role, $json=true) {
+    global $roles_db;
+    $result = $roles_db->getKey($role);
+    $return = array('role'=>$role,'Delpkg'=>'','Addpkg'=>'');
+    if (is_array($result)) {
+        if (is_string($result['Addpkg'])) $return['Addpkg'] = $result['Addpkg'];
+        if (is_string($result['Delpkg'])) $return['Delpkg'] = $result['Delpkg'];
+    }
+    if ($json) {
+        return json_encode($return);
+    } else {
+        return $return;
+    }
+}
+
+function getClient($mac=false) {
+    global $hosts_db;
+    if (!$mac) {
+        $mac = getClientMac();
+        if (!$mac) return false;
+    }
+    $hosts = $hosts_db->getAllByProp('MacAddress',$mac);
+    if (count($hosts)>0) {
+        return array_pop($hosts);
+    } else {
+        return false;
+    }
+}
+
+function getClientMac($ip=false) {
+    if (false === $ip) {
+        $ip = $_SERVER['REMOTE_ADDR'];
+    }
+    $result = pingHost($ip,1);
+    if (!$result) return false;
+    $macAddr=false;
+    $arptable=`arp -n $ip`;
+    $lines=explode("\n", $arptable);
+    foreach($lines as $line) {
+       $cols=preg_split('/\s+/', trim($line));
+       if ($cols[0]==$ip)
+       {
+           return $cols[2];
+       }
+    }
+}
+
+function pingHost($host, $timeout=1) {
+    $pingresult = exec("/bin/ping -c 1 -t 2 -W $timeout $host", $output, $retvar);
+    return (0 == $retvar);    
+}
+
+
+
+function ROUTE_domain() {
+    global $config_db;
+    $theFile = $config_db->getProp(CONFIG_KEY,'DomainFile');
+    $content = file_get_contents($theFile);
+    if (false !== $content) {
+        echo $content;
+    }   
+}
+
+function ROUTE_mgmtkey() {
+    global $config_db;
+    $theFile = $config_db->getProp(CONFIG_KEY,'ManagementKeyFile');
+    $content = file_get_contents($theFile);
+    if (false !== $content) {
+        echo $content;
+    }   
+}
+
+function ROUTE_epoptes_srv() {
+    global $hosts_db;
+    $hosts = $hosts_db->getAllByProp('Role','docenti');
+    if (count($hosts) == 0) {
+        echo 'none';
+    } else {
+        $host = array_pop($hosts);
+        echo json_encode($host);
+    }
+}
+
+function ROUTE_ansible_host() {
+    global $get_params;
+    if (isset($get_params['role'])) {
+        $role = $get_params['role'];
+    } elseif ($client = getClient()) {
+        $role = $client['Role'];
+    } else {
+        $role = 'unknown';
+    }
+    echo varsForRole($role);
+}
+
+function ROUTE_hosts() {
+    global $hosts_db;
+    global $get_params;
+    if (isset($get_params['role'])) {
+        $items = $hosts_db->getAllByProp('Role', $get_params['role']);
+    } else {
+        $items = $hosts_db->getAll();
+    }
+    $results=array();
+    foreach ($items as $name=>$item) {
+        $results[]=array(
+            'mac'=>$item['MacAddress'],
+            'hostname'=>$name,
+            'role'=>$item['Role']
+        );
+    }
+    echo json_encode($results);
+}
+
+
+function ROUTE_ansible_list() {
+    global $get_params;
+    if (isset($get_params['role'])) {
+        $role = $get_params['role'];
+    } elseif ($client = getClient()) {
+        $role = $client['Role'];
+    } else {
+        $role = 'unknown';
+    }
+    $vars = varsForRole($role, false);
+    echo json_encode(array(
+        'localhost'=>array(
+            'hosts'=>array('localhost'),
+            'vars'=>array('ansible_connection'=>'local')
+            ),
+        '_meta'=> array('hostvars'=>array('localhost'=>$vars))
+    ));
+}
+
+
+function ROUTE_vaultpass() {
+    global $config_db;
+    $theFile = $config_db->getProp(CONFIG_KEY,'VaultPassFile');
+    $content = trim(file_get_contents($theFile));
+    if (false === $content) {
+        echo 'no file'; return;
+    }   
+    if (!$client = getClient()) {
+        echo 'no client'; return;
+    }
+    $key = md5($client['name']);
+    $padlenght = 16 - (strlen($content) % 16);
+    $content_padded = str_repeat('x', $padlenght); 
+    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $content_padded, MCRYPT_MODE_ECB);
+    echo base64_encode($ciphertext);
+}
+
+function ROUTE_whatsmyhostname() {
+    global $config_db;
+    $mac = getClientMac();
+    if (!$mac) return false;
+    $base = $config_db->getProp(CONFIG_KEY,'NamingBase');
+    $role = $config_db->getProp(CONFIG_KEY,'DefaultRole');
+    echo newHostname($mac, $base, $role);
+}
+
+function ROUTE_mac2hostname() {
+    global $config_db;
+    global $get_params;
+    $mac = getClientMac();
+    if (!$mac) {
+        echo 'Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ]';
+        return;
+    }
+
+    if (isset($get_params['role'])) {
+        $role = $get_params['role'];
+    } else {
+        $role = $config_db->getProp(CONFIG_KEY,'DefaultRole');
+    }
+    
+    if (isset($get_params['base'])) {
+        $base = $get_params['base'];
+    } else {
+        $base = $config_db->getProp(CONFIG_KEY,'NamingBase');
+    }
+
+    echo newHostname($mac, $base, $role);
+}
+
+function newHostname($mac, $base, $role) {
+    global $config_db;
+    global $hosts_db;
+    if(!preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $mac)) {
+        echo "invalid mac address";
+        return;
+    }
+    
+    if ($client=getClient($mac)) {
+        echo $client['name'];
+        return;
+    }
+    
+    $domainName = $config_db->getKeyValue('DomainName');
+    $digits = $config_db->getProp(CONFIG_KEY,'NamingDigits');
+    $formatstring = '%s-%0'.$digits.'d';
+    $nr=0;
+    $hostname = '';
+    do {
+        $nr++;
+        $hostname = sprintf($formatstring, $base, $nr);
+        if ($domainName != '') {
+            $hostname .= '.' . $domainname;
+        }
+    } while ($hosts_db->getKey($hostname));
+    
+    $hosts_db->setKey($hostname, 'remote', array(
+		'MacAddress'    => $mac,
+		'Role'   		=> $role,
+		'Description' 	=> ''
+    ));
+
+    echo $hostname;
+}
+
diff --git a/initrd b/initrd
deleted file mode 100644
index 5453b61..0000000
--- a/initrd
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/sh
-
-# chkconfig: 2345 20 80
-# description: Manage assignation of hostnames for clients
-# pidfile: /var/run/doraemon.pid
-# config: /etc/doraemon.ini
-
-### BEGIN INIT INFO
-# Provides:          doraemon
-# Required-Start:    $remote_fs $syslog
-# Required-Stop:     $remote_fs $syslog
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Helps client to join the domain and maintain itself
-# Description:       Helps client to join the domain and maintain itself
-### END INIT INFO
-
-# Change the next 3 lines to suit where you install your script and what you want to call it
-DAEMON=/usr/bin/doraemon.py
-DAEMON_NAME=doraemon
-
-# Add any command line options for your daemon here
-DAEMON_OPTS=""
-
-# This next line determines what user the script runs as.
-# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
-#DAEMON_USER=mac2hostname
-#DAEMON_GROUP=mac2hostname
-
-# The process ID of the script when it runs is stored here:
-LOGFILE=/var/log/$DAEMON_NAME.log
-PIDFILE=/var/run/$DAEMON_NAME.pid
-lockfile=/var/lock/subsys/$DAEMON_NAME
-
-. /etc/rc.d/init.d/functions
-. /etc/sysconfig/network
-
-[ "$NETWORKING" = "no" ] && exit 0
-
-do_start () {
-  echo -n $"Starting $DAEMON_NAME: "
-  daemon --pidfile=$PIDFILE "python $DAEMON >>$LOGFILE 2>/dev/null &"
-  RETVAL=$?
-  echo
-  [ $RETVAL -eq 0 ] && touch $lockfile
-  return $RETVAL
-}
-
-do_stop () {
-  echo -n $"Stopping $DAEMON_NAME: "
-  killproc -p $PIDFILE python
-  RETVAL=$?
-  echo
-  [ $RETVAL -eq 0 ] && rm -f $lockfile
-  return $RETVAL
-}
-
-case "$1" in
-
-    start|stop)
-        do_${1}
-        ;;
-
-    restart|reload|force-reload)
-        do_stop
-        do_start
-        ;;
-
-    status)
-        status -p $PIDFILE python
-        ;;
-
-    *)
-        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
-        exit 1
-        ;;
-
-esac
-exit 0
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole
new file mode 100644
index 0000000..b051c6c
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole
@@ -0,0 +1 @@
+client
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile b/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile
new file mode 100644
index 0000000..e978cbd
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile
@@ -0,0 +1 @@
+/etc/domain.yml
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile
new file mode 100644
index 0000000..a2d5166
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile
@@ -0,0 +1 @@
+/home/amgmt/.ssh/id_rsa.pub
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase
new file mode 100644
index 0000000..10441b4
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase
@@ -0,0 +1 @@
+lab
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits
@@ -0,0 +1 @@
+2
diff --git a/defaults/TCPPort b/root/etc/e-smith/db/configuration/defaults/doraemon/TCPPort
similarity index 100%
rename from defaults/TCPPort
rename to root/etc/e-smith/db/configuration/defaults/doraemon/TCPPort
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile b/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile
new file mode 100644
index 0000000..65f2cd9
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile
@@ -0,0 +1 @@
+/home/amgmt/.ansible/vault.txt
diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/access b/root/etc/e-smith/db/configuration/defaults/doraemon/access
new file mode 100644
index 0000000..a48cf0d
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/doraemon/access
@@ -0,0 +1 @@
+public
diff --git a/defaults/status b/root/etc/e-smith/db/configuration/defaults/doraemon/status
similarity index 100%
rename from defaults/status
rename to root/etc/e-smith/db/configuration/defaults/doraemon/status
diff --git a/defaults/type b/root/etc/e-smith/db/configuration/defaults/doraemon/type
similarity index 100%
rename from defaults/type
rename to root/etc/e-smith/db/configuration/defaults/doraemon/type
diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults
new file mode 100644
index 0000000..4c1a258
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults
@@ -0,0 +1,10 @@
+{
+    #
+    # 00template_defaults
+    #
+
+    $localAccess = '';   
+    $port = ${'doraemon'}{TCPPort} || "3000";
+
+    "";
+}
\ No newline at end of file
diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager
new file mode 100644
index 0000000..b879b6a
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager
@@ -0,0 +1,127 @@
+{
+
+    $OUT .= <<HERE;
+Listen $port
+HostnameLookups off
+
+ServerAdmin root@$DomainName
+ServerRoot /etc/httpd
+ServerTokens ProductOnly
+
+User srvmgr
+Group srvmgr
+
+ErrorLog /var/log/doraemon/error_log
+LogLevel warn
+HERE
+
+foreach (qw(
+    env
+    log_config
+    mime
+    negotiation
+    include
+    autoindex
+    dir
+    alias
+    rewrite
+    access
+    authz_host
+    expires
+    headers
+    setenvif
+    ssl
+    ))
+{
+    next unless -f "/usr/lib/httpd/modules/mod_${_}.so" || 
+                -f "/usr/lib64/httpd/modules/mod_${_}.so";
+    $OUT .= "LoadModule ${_}_module modules/mod_${_}.so\n";
+}
+
+$OUT .= <<HERE;
+PidFile /var/run/doraemon.pid
+ScoreBoardFile /var/run/doraemon.scoreboard
+UseCanonicalName off
+LogFormat "%h %l %u %t \\"%r\\" %>s %b" common
+LogFormat "%{User-agent}i" agent
+
+CustomLog /var/log/doraemon/access_log common
+
+KeepAlive On
+MaxKeepAliveRequests 100
+KeepAliveTimeout 15
+
+MaxClients 150
+MaxRequestsPerChild 100
+
+MinSpareServers 1
+MaxSpareServers 5
+StartServers 1
+Timeout 300
+
+DefaultIcon /icons/unknown.gif
+DirectoryIndex index.htm index.html index.shtml index.cgi
+IndexOptions FancyIndexing VersionSort NameWidth=*
+IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
+AccessFileName .htaccess
+
+AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
+AddIconByType (TXT,/icons/text.gif) text/*
+AddIconByType (IMG,/icons/image2.gif) image/*
+AddIconByType (SND,/icons/sound2.gif) audio/*
+AddIconByType (VID,/icons/movie.gif) video/*
+DefaultType text/plain
+TypesConfig /etc/mime.types
+
+AddEncoding x-compress Z
+AddEncoding x-gzip gz
+
+AddIcon /icons/binary.gif .bin .exe
+AddIcon /icons/binhex.gif .hqx
+AddIcon /icons/tar.gif .tar
+AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
+AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
+AddIcon /icons/a.gif .ps .ai .eps
+AddIcon /icons/layout.gif .html .shtml .htm .pdf
+AddIcon /icons/text.gif .txt
+AddIcon /icons/c.gif .c
+AddIcon /icons/p.gif .pl .py
+AddIcon /icons/f.gif .for
+AddIcon /icons/dvi.gif .dvi
+AddIcon /icons/uuencoded.gif .uu
+AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
+AddIcon /icons/tex.gif .tex
+AddIcon /icons/bomb.gif core
+
+AddIcon /icons/back.gif ..
+AddIcon /icons/hand.right.gif README
+AddIcon /icons/folder.gif ^^DIRECTORY^^
+AddIcon /icons/blank.gif ^^BLANKICON^^
+
+AddLanguage en .en
+AddLanguage fr .fr
+AddLanguage de .de
+AddLanguage da .da
+AddLanguage el .el
+AddLanguage it .it
+
+LanguagePriority en it de fr
+
+AddType text/html .shtml
+AddType application/x-pkcs7-crl    .crl
+
+AddType application/x-x509-ca-cert .crt
+
+BrowserMatch "Mozilla/2" nokeepalive
+BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+BrowserMatch "RealPlayer 4\.0" force-response-1.0
+BrowserMatch "Java/1\.0" force-response-1.0
+BrowserMatch "JDK/1\.0" force-response-1.0
+
+AddHandler cgi-script .cgi
+AddHandler server-parsed .shtml
+AddHandler imap-file map
+
+HERE
+}
+
diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess
new file mode 100644
index 0000000..fe3528e
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess
@@ -0,0 +1,16 @@
+
+# First, we configure the "default" to be a very restrictive set of 
+# permissions.  
+
+<Directory />
+    Options None
+    AllowOverride None
+    order deny,allow
+    deny from all
+    allow from none
+</Directory>
+
+<Directory /var/www/icons>
+    Allow from all
+</Directory>
+
diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface
new file mode 100644
index 0000000..743bbf3
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface
@@ -0,0 +1,23 @@
+
+DocumentRoot /usr/share/doraemon
+Alias /icons/ /var/www/icons/
+
+<Directory /usr/share/doraemon/>
+       Options +Indexes
+       Order Deny,Allow
+       Deny from all
+       Allow from { $localAccess || 'all' }
+       AddType application/x-httpd-php .php
+       Options +FollowSymlinks
+       RewriteEngine on 
+       RewriteBase /
+       RewriteRule ^(js/|fonts/|css/|images/|icons/|.+\.php) - [PT,L]
+       RewriteRule ^favicon.ico$ images/favicon.ico [L]
+       RewriteRule ^(.*)$ index.php/$1 [L] 
+       php_value session.save_path /var/cache/doraemon
+       php_flag magic_quotes_gpc off
+</Directory>
+   
+
+
+
diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include
new file mode 100644
index 0000000..e7f3cdb
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include
@@ -0,0 +1 @@
+Include conf.d/php.conf
diff --git a/root/etc/httpd/doraemon/.gitignore b/root/etc/httpd/doraemon/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/root/etc/logrotate.d/doraemon b/root/etc/logrotate.d/doraemon
new file mode 100644
index 0000000..d409c39
--- /dev/null
+++ b/root/etc/logrotate.d/doraemon
@@ -0,0 +1,9 @@
+/var/log/doraemon/*log {
+    missingok
+    notifempty
+    sharedscripts
+    delaycompress
+    postrotate
+        /sbin/reload doraemon > /dev/null 2>/dev/null || true
+    endscript
+}
\ No newline at end of file
diff --git a/root/etc/sysconfig/doraemon b/root/etc/sysconfig/doraemon
new file mode 100644
index 0000000..007b91f
--- /dev/null
+++ b/root/etc/sysconfig/doraemon
@@ -0,0 +1,8 @@
+#
+# This file can be used to set additional environment variables for
+# the httpd-admin process, or pass additional options to the
+# httpd-admin executable. See httpd-admin.service unit.
+#
+
+LANG=C
+
diff --git a/root/usr/lib/systemd/system/doraemon-reload.service b/root/usr/lib/systemd/system/doraemon-reload.service
new file mode 100644
index 0000000..320c054
--- /dev/null
+++ b/root/usr/lib/systemd/system/doraemon-reload.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Graceful restart of doraemon service
+Documentation=https://github.com/bglug-it/doraemon
+Requisite=doraemon.service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -k graceful
+
diff --git a/root/usr/lib/systemd/system/doraemon-reload.timer b/root/usr/lib/systemd/system/doraemon-reload.timer
new file mode 100644
index 0000000..7531f12
--- /dev/null
+++ b/root/usr/lib/systemd/system/doraemon-reload.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Delayed graceful restart of doraemon service
+Documentation=https://github.com/bglug-it/doraemon
+Conflicts=doraemon-reload.service
+
+[Timer]
+OnActiveSec=5
+
+
diff --git a/root/usr/lib/systemd/system/doraemon.service b/root/usr/lib/systemd/system/doraemon.service
new file mode 100644
index 0000000..d4eeb35
--- /dev/null
+++ b/root/usr/lib/systemd/system/doraemon.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=Doraemon httpd instance
+Documentation=https://github.com/bglug-it/doraemon
+After=network.target remote-fs.target nss-lookup.target
+
+[Service]
+Type=simple
+EnvironmentFile=/etc/sysconfig/doraemon
+ExecStart=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -c 'MaxConnectionsPerChild 12' -DFOREGROUND
+ExecReload=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -k graceful
+LimitNOFILE=4096
+
+[Install]
+WantedBy=multi-user.target
diff --git a/root/usr/sbin/doraemon b/root/usr/sbin/doraemon
new file mode 120000
index 0000000..558a907
--- /dev/null
+++ b/root/usr/sbin/doraemon
@@ -0,0 +1 @@
+httpd
\ No newline at end of file