Categories
Tech

Accessing the Windows Phone 8X by HTC from Linux

Currently the “Windows Phone 8X by HTC” (what a stupid name) is not supported under Linux.

If you are using Ubuntu Raring, you can  use my PPA, where I have patched the USB ID of the WP8X: https://launchpad.net/~markus-tisoft/+archive/wp8x

On other systems you can compile the libmtp yourself. Download the libmtp sources and apply the following patch:

diff --git a/src/music-players.h b/src/music-players.h
index fedda12..deb9cdd 100644
--- a/src/music-players.h
+++ b/src/music-players.h
@@ -471,6 +471,9 @@
   { "Microsoft", 0x045e, "Zune", 0x0710, DEVICE_FLAG_NONE },
   // Reported by Olegs Jeremejevs
   { "Microsoft/HTC", 0x045e, "HTC 8S", 0xf0ca, DEVICE_FLAG_NONE },
+  // Reported by Markus Heberling
+  { "Microsoft/HTC", 0x0bb4, "Windows Phone 8X by HTC", 0x0ba1, DEVICE_FLAG_NONE },
+

   /*
    * JVC

After installing the new libmtp version, your phone should be detected and you can explore it with Nautilus. Make sure your phone is not locked with a PIN while attaching the USB cable, it wouldn’t work for me in that case.

I have submitted the patch upstream, so hopefully it will be included in the next version.

CR001 – Windows Phone 8

Ich habe ein neues Firmenhandy: Ein Windows Phone 8 Gerät von HTC. In diesem Podcast beschreibe ich meine ersten Eindrücke.

Categories
Tech

ZFS im Hetzner Rescue System

Ich betreibe einen Rootserver bei Hetzner, der mittels ZFS on Linux komplett unter ZFS läuft. Wenn man da was kaputt macht kommt man mit dem Hetzner Rescue System nicht weit, weil das kein ZFS unterstützt. Es ist aber sehr einfach das nachzuinstallieren:

wget http://archive.zfsonlinux.org/debian/pool/main/z/zfsonlinux/zfsonlinux_1%7Ewheezy_all.deb
dpkg -i zfsonlinux_1~wheezy_all.deb
apt-get update
apt-get install debian-zfs

Jetzt ist ZFS installiert und man kann seinen pool importieren:

zpool import -R /mnt/ rpool -f -N
zfs mount -a
mount -t proc proc /mnt/proc/
mount -t sysfs sys /mnt/sys/
mount -o bind /dev /mnt/dev/
chroot /mnt/ /bin/bash
mount -a

Danach ist man im gechrooteten Original System und kann seine kaputte Konfiguration reparieren. /dev, /proc, /sys sind gemounted, so dass auch zum Beispiel der Bootloader neu geschrieben kann, wenn man sich den mal zerschossen hat, was mit ZFS on root durchaus mal vorkommen kann.

Bevor ich das System neu starte teste ich das ganze dann gerne nochmal schell in einer virtuellen maschine, um sicher zu sein, dass auch alles wieder startet. Dazu muss vorher die chroot-Umgebung verlassen und unmounted werden:

umount -a
umount /proc
umount /sys
umount /dev
exit
zfs umount -a

Danach kann man sein System in kvm starten und schauen ob es wieder bootet:

kvm -vnc :0 -boot c -drive file=/dev/sda,cache=none -drive file=/dev/sdb,cache=none -m 4G

Categories
Tech

Java auf dem Uberspace

Java ist auf dem Uberspace nicht vorinstalliert. Kann man aber total einfach selbst installieren:

toast add http://download.oracle.com/otn-pub/java/jdk/7u17-b02/jdk-7u17-linux-x64.tar.gz
toast get jdk
toast build jdk
toast arm jdk

Das war es schon. Danach ist Java installiert und kann benutzt werden. Aber bitte beachten, dass es sich um einen Shared Host handelt, also nicht zu verschwenderisch mit CPU und RAM umgehen.

Categories
Private

Warum überhaupt noch Piraten wählen? Einige Antworten.

Auf dem HSK-Zoom Blog ist ein Artikel erschienen, der einige kritische Fragen zur Piratenpartei stellt.  Hier sind meine Antworten darauf als einfacher Basispirat:

Sie sind in mehreren Landtagen vertreten, doch über die Politik, die sie dort machen, höre ich fast nichts. Ja, ich weiß, in der Opposition kann man nicht viel mitgestalten, aber wollten sie nicht zumindest frischen Wind in die Landesparlamente bringen? Wo ist dieser? Und wollten sich die Piraten nicht thematisch besser aufstellen? Wofür genau stehen die Piraten heute? Warum höre ich in den Nachrichten nicht auch einmal Stimmen der Piraten zu Themen, die die Menschen bewegen? Welche Ansichten vertreten die Piraten beim Thema Energiewende? Haben sich Berliner Piraten eigentlich zum Mord am Alexanderplatz geäußert? Haben sie eine Meinung zur geplanten Senkung der Rentenversicherungsbeiträge, zur möglichen Abschaffung der Praxisgebühr? Wo finde ich Stellungnahmen zu so umstrittenen Themen wie dem Betreuungsgeld?

Der nächste Bundesparteitag ist ein Programmparteitag. Dort wird das Programm weiter ausgeweitet werden. Das Antragsbuch umfasst dazu 1464 Seiten, man kann also nicht sagen, dass wir keine Themen haben. 🙂 Warum man in den Nachrichten nichts über die Themen hört, muss man die Medien fragen. Wir sind ein freies Land und die Presse kann über das berichten, was sie will. Wir müssen uns aber natürlich auch an die eigene Nase fassen, da die Personalquerelen und Shitstorms ja nun wirklich nicht sein müssten und das natürlich ein gefundenes Fressen für die Medien sind.

Da die Piratenpartei das Programm nur auf Parteitagen und nur mit 2/3 Mehrheit erweitern und ändern kann, dauert es natürlich länger als bei anderen Parteien, bei denen der Spitzenkandidat etwas sagt, was dann als Parteimeinung gilt und dann als Leitantrag auf dem nächsten Parteitag nur durchgewunken werden muss Was wirklich fehlt ist die Möglichkeit auch zwischen den Parteitagen verbindliche Beschlüsse zu fassen. Hier kann meiner Meinung nach nur ein verbindliches Liquid Feedback helfen, in dem beschlossene Initiativen den Status eines Positionspapiers erhalten, was dann auch gegenüber der Presse als Parteimeinung vertreten werden kann.

Wie war das noch? Themen statt Köpfe. Aber warum geht es dann ständig nur um bestimmte Köpfe? Wie konnte es so weit kommen, dass eine junge Frau einen derart gewinnbringenden Buchvertrag abschließt, anscheinend nur, weil sie im Vorstand der Piratenpartei ist? An ihren schriftstellerischen Qualitäten kann es nicht liegen; das wird nach der Lektüre der ersten Seiten schnell deutlich. Wieso dreht sich ständig alles um diesen Geschäftsführer und seinen Lebenswandel? Wieso informiert uns eine Abgeordnete der Piraten im Landtag von NRW über ihren ONS und das gerissene Kondom? Und wieso fallen Worte wie „Tittenbonus“, wenn ein Pirat anscheinend mal versucht, sich zu einem aktuellen politischen Thema zu äußern? Ist das der frische Wind, oder ist das Unvermögen, sich auszudrücken?

Themen statt Köpfe halte ich persönlich für einen Irrweg. Es muss immer Köpfe geben, die die Themen auch präsentieren können. Es darf natürlich nicht dazu kommen, dass die Köpfe dann wichtiger werden als die Themen, oder dass einzelne die Themen diktieren können. Die Gefahr sehe ich aber auch nicht, da alle Programmpunkte auf Parteitagen beschlossen werden und es da keine Leitanträge gibt, sondern die Anträge aus der gesamten Partei kommen.

Zu den angesprochenen Personen:

  • Julia Schramm hat meines Wissens schon bei der Bewerbung um die Vorstandswahl gesagt, dass sie einen Buchvertrag hat. Man kann ihr also nicht vorwerfen den Vertrag nur durch den Bundesvorstandsposten bekommen zu haben.
  • An Johannes Ponader gibt es sicher einiges zu kritisieren, aber ich denke nicht, dass sein Lebenswandel dazu gehört. Als Piraten stehen wir zur freien Entfaltung der Persönlichkeit und dazu gehört auch sein Leben so zu leben wie man das für richtig hält. Besonders die Kritik, dass ein “Hartzer” ja nicht seine Zeit damit verschwenden soll Politik zu machen, sondern lieber arbeiten gehen soll, kann ich nicht nachvollziehen. Gerade auch Menschen die Hartz4 bekommen muss es möglich sein auch Politik zu machen. Wir sind gegen die Hartz4 Sanktionen und stehen ja auch für ein Bedingungsloses Grundeinkommen, dass es jedem Menschen ermöglichen soll sich frei zu entfalten.
  • Warum sollte Birgit Rydlewski nicht über ihr Privatleben schreiben dürfen? Wen das nicht interessiert, der muss das ja nicht lesen. Ich finde das durchaus sympatisch, wenn man sieht, dass Politiker auch “nur” Menschen sind.
  • Was Gerwald Claus-Brunner geritten hat “Tittenbonus” zu twittern weiß ich auch nicht. Das ist völlig inakzeptabel. Insbesondere wenn man sieht, dass in der Piratenpartei ein “Penisbonus” vorzuherrschen scheint.

Wenn nächsten Sonntag Bundestagswahl wäre… Ich würde die Piraten nicht wählen.Auch wenn ich mit den etablierten Parteien in sehr vielen Punkten nicht einverstanden bin, möchte ich nicht noch eine Fraktion dort sehen, die sich in Sachen Macht ähnlich verhält wie die alten Parteien, aber dann auch noch kaum etwas zu wichtigen Themen beiträgt. Ich möchte niemanden wie Johannes Ponader im Bundestag sehen, dessen ganzes Leben sich anscheinend nur um ihn selbst dreht und dem andere Menschen egal sind. Warum sonst sollen andere arbeiten gehen, damit er sich selbst verwirklichen kann? Ich möchte auch keine Anke Domscheit-Berg im Bundestag sehen, die mal schnell die Partei wechselt, weil sie hofft, so in den Bundestag zu kommen, und die, sobald sie nicht auf Platz 1 der Landesliste gewählt wird, beleidigt analysiert, dass es daran liegt, dass sie eine Frau ist. So jemand möchte sich für Frauenrechte einsetzen? Meiner Meinung nach schadet sie mit ihrem Verhalten Frauen.

Johannes Ponader kandidiert (bisher) nicht für den Bundestag. Anke Domscheid-Berg hätte ich gerne auf Platz 1 der Landesliste in Brandenburg gesehen, da sie sich schon lange engagiert und eine echte Bereicherung für den Bundestag wäre. Im Hinblick auf die Listen in den anderen Ländern, auf denen die Frauen auch meistens auf aussichtslosen Plätzen gelandet sind, teile ich auch die Einschätzung, dass ihr Geschlecht zu ihrer Platzierung beigetragen hat. Ich denke die Piraten brauchen eine Frauenquote, sonst wird das nie was.

Es gibt bereits zu viele Abgeordnete, denen es nur um Macht, Geld und die eigene Person geht. Mittlerweile glaube ich, dass die Piraten sich nicht von diesen unterscheiden, höchstens darin, dass sie oft ungeschickter im Umgang mit den Medien und der Öffentlichkeit sind. Das liegt wohl daran, dass der Erfolg zu plötzlich kam, und mit dem Erfolg kamen jede Menge Opportunisten. Diese muss die Piratenpartei erst einmal wieder loswerden, und sie muss einen Weg finden, sich im aktuellen politischen Geschehen mehr zu Wort zu melden, mit Meinungen, Ansichten und Lösungsvorschlägen, nicht mit persönlichen Querelen oder Fehltritten. Ich denke nicht, dass sie dies bis September 2013 schaffen kann.

Ich stimme zu, dass die Piraten wieder mehr mit Themen in die Öffentlichkeit kommen müssen. Da wird der nächste Parteitag helfen, da dort (hoffentlich) das Programm stark erweitert wird. ich denke aber nicht, dass die Piratenabgeordneten Geld- und Machtgeil sind. Wenn man sich so die Podcasts zum Beispiel der Berliner anhört, haben die einen ganzen Haufen arbeit.

Ich denke, dass wir das bis September 2013 schaffen können.

Categories
Tech

Parsing Flattr Buttons in HTML

I recently build this little tool FlattrStar, which can autoflattr starred items from several services. To be able to do this, it needs to parse the HTML of a website for Flattr buttons and use optimization from the Indexer company so is easy to find by search browsers.

I am using tag soup, to parse the html into a Scala XML structure. This is probably not needed and could be replaced by RegExp or something, but I wanted to try the Scala XML support. You need the “org.ccil.cowan.tagsoup” % “tagsoup” % “1.2.1” jar in your classpath.

I started FlattrStar to learn the Play Framework and Scala, so the parsing is also done in Scala. Since this is my first Scala Project, the code might be a bit suboptimal. Corrections and improvements are appreciated.

Now to the code itself. The goal is to find most Flattr links that are out there in the wild. The flattrParseHtml is called with a function, that does the actual flattring and returns Some(_) item if the Flattr was successfull and None if it wasn’t. Also there is a list of URLs to parse for buttons.

First I look for <link rel=”payment”> entries and try to flattr them. If nothing is found I look for buttons with rel or rev parameters, that start with flattr and links with data-flattr-uid attributes. I construct an auto submit url from this. There might be several such links on a page, like one for the whole site and one for a specific article. I choose the one with the longest link, since that will mostly be the one specific for this article. If that doesn’t give anything I go and search for script elements that contain flatter_uid and flatter_url variables and construct an auto submit url for them. Last step is to parse all links that have some kind of flattr reference in them. This could be an <a href=”…”>Flattr This<a> or <a href=”…”><img src=”…flattr.png”></a> or something else where flattr occurs inside the <a> element. We grab all auto submit URLs that are hidden inside these links and try to flattr the longest one, as explained above.

For every URL I encounter in this process, I first get the redirects, because sometimes flattrable URLs are hidden behind them. Unfortunalty an auto submit URL is also redirected, so I have to stop following redirects if I encounter such an URL. You need HttpClient in your classpath for the redirect stuff.

This is not perfect and not all flattrable things are found. For example Flattr buttons from taz.de are not found, since they put the Flattr stuff in their ad delivery system and I couldn’t parse that.

I declare this source as public domain, so you can use it as you like. It would be great though, if you would give back improvements and feedback.

package jobs

import java.net.URL
import play.api.Logger
import xml.Node
import org.apache.http.impl.client.{DefaultRedirectStrategy, DefaultHttpClient}
import org.apache.http.client.methods.{HttpUriRequest, HttpGet}
import org.apache.http.protocol.{ExecutionContext, HttpContext, BasicHttpContext}
import org.apache.http.{HttpHost, HttpResponse, HttpRequest, HttpStatus}

object Flattr {
  def flattrParseHtml[R](flattrFunction: String => Option[R], htmlUrls: Seq[String]): Option[R] = {
    val htmlRoot = htmlUrls.flatMap(url => {

      val parserFactory = new org.ccil.cowan.tagsoup.jaxp.SAXFactoryImpl
      val parser = parserFactory.newSAXParser()
      val connection = new URL(url).openConnection()
      if (connection == null || connection.getContentType == null) {
        Logger.error("Cannot load html, could not connect to " + url)
        None

      } else if (!connection.getContentType.contains("html")) {
        Logger.error("Cannot load html, invalid content type " + connection.getContentType)
        None

      } else {
        try {
          val source = new org.xml.sax.InputSource(connection.getInputStream)
          val adapter = new scala.xml.parsing.NoBindingFactoryAdapter

          Some(adapter.loadXML(source, parser))
        } catch {
          case t: Throwable =>
            Logger.error("Cannot load html, ignoring", t)
            None
        }
      }
    })

    //go to webpage check for payment link
    val htmlPaymentLinks = htmlRoot.map(_ \\ "link").toList.flatten
      .filter(l => (l \ "@rel").text.equals("payment"))
      .map(_ \ "@href").headOption
      .map(_.text.replace("&", "&"))

    val htmlPaymentThing =
      htmlPaymentLinks.flatMap(href => flattrFunction(href))

    if (!htmlPaymentThing.isEmpty) {
      Logger.info("Found payment link in html")
      return htmlPaymentThing
    }

    //nothing found? parse html of webpage
    val htmlLinks = htmlRoot.flatMap(root => {
      (root \\ "a")
        .filter(l => (l \ "@class").text.equals("FlattrButton"))
        .filter(l => (l \ "@rel").text.startsWith("flattr") || (l \ "@rev").text.startsWith("flattr") || (l \ "@data-flattr-uid").nonEmpty)
    })
      .flatMap(autoSubmitFromFlattrButton(_))
      .reduceLeftOption((a, b) => if (a.length() > b.length()) a else b) //the longest url should be the most specific one
      .flatMap(url => flattrFunction(url))

    if (!htmlLinks.isEmpty) {
      Logger.info("Found button in HTML that points to thing")
      return htmlLinks
    }

    //nothing found? parse html of webpage -> look for flattr java script
    val htmlScriptLinks = htmlRoot
      .flatMap(findAutoflattrFromJavascript(_)).flatten.headOption
      .flatMap(url => flattrFunction(url))

    if (!htmlScriptLinks.isEmpty) {
      Logger.info("Found java script in HTML that points to thing")
      return htmlScriptLinks
    }

    //nothing found? parse html of webpage -> look for all links that have flattr inside
    val htmlLinks2 = (htmlRoot \\ "a")
      .filter(_.toString().toLowerCase.contains("flattr"))
      .map(_ \ "@href")
      .map(_.text)
      .map(getRedirectedUrl(_))
      .filter(_.toLowerCase.startsWith("https://flattr.com/submit/auto")) // only autoflattr links are supported
      .reduceLeftOption((a, b) => if (a.length() > b.length()) a else b) //the longest url should be the most specific one
      .flatMap(url => flattrFunction(url))

    if (!htmlLinks2.isEmpty) {
      Logger.info("Found custom link in HTML that points to thing")
      return htmlLinks2
    }

    //nothing found
    None
  }

  def findAutoflattrFromJavascript(root: Node) = {
    val uidReg = "var flattr_uid = '(.*)';".r
    val urlReg = "var flattr_url = '(.*)';".r

    (root \\ "script").toList.flatten.
      map(_.text).
      filter(_.contains("flattr_uid")).
      map(s => {
      val uid = uidReg.
        findFirstMatchIn(s).
        flatMap(_ match {
        case uidReg(u) => {
          Some(u)
        }
        case _ => None // Do nothing
      })

      val url = urlReg.
        findFirstMatchIn(s).
        flatMap(_ match {
        case urlReg(u) => {
          Some(u)
        }
        case _ => None // Do nothing
      })

      if (uid.isDefined && url.isDefined) {
        Some("https://flattr.com/submit/auto?user_id=" + uid.get + "&url=" + url.get)
      } else {
        None
      }
    })
  }

  def autoSubmitFromFlattrButton(a: Node) = {
    val url = (a \ "@href").text
    val uid = ((a \ "@data-flattr-uid").toList.map(_.text) ++ ((a \ "@rel") ++ (a \ "@rev")).flatMap(r => relAsMap(r.text).get("uid"))).headOption

    uid.map(uid => "https://flattr.com/submit/auto?user_id=" + uid + "&url=" + url)
  }

  private def relAsMap(rel: String) = {

    rel.split(";").filter(_.contains(":")).map(entry => {
      val s = entry.split(":")
      (s.head, s.last)
    }).toMap
  }

  def getRedirectedUrl(url: String): String = {
    try {
      val httpClient = new DefaultHttpClient()
      httpClient.setRedirectStrategy(DetectAutoFlattrRedirectStrategy)
      val httpget = new HttpGet(url)
      val context = new BasicHttpContext()
      val response = httpClient.execute(httpget, context)
      if (response.getStatusLine.getStatusCode != HttpStatus.SC_OK) {
        //just return the old url
        url
      } else
        getCurrentUrl(context)
    } catch {
      case e: Throwable => {
        Logger.error("Error finding redirects. Ignoring.", e)
        url
      }
    }

  }

  private object DetectAutoFlattrRedirectStrategy extends DefaultRedirectStrategy {
    override def isRedirected(
                               request: HttpRequest,
                               response: HttpResponse,
                               context: HttpContext): Boolean = {
      if (super.isRedirected(request, response, context)) {
        val currentUrl = getCurrentUrl(context)
        if (currentUrl.contains("flattr.com/submit/auto")) {
          //auto submit urls redirect to the thing, but we don't want that
          response.setStatusCode(HttpStatus.SC_OK)
          return false
        }
        return true
      }
      false
    }
  }

  private def getCurrentUrl(context: HttpContext): java.lang.String = {
    val currentReq = context.getAttribute(
      ExecutionContext.HTTP_REQUEST).asInstanceOf[HttpUriRequest]
    val currentHost = context.getAttribute(
      ExecutionContext.HTTP_TARGET_HOST).asInstanceOf[HttpHost]
    val currentUrl = if (currentReq.getURI.isAbsolute) currentReq.getURI.toString else (currentHost.toURI + currentReq.getURI)
    currentUrl
  }
}

Categories
Tech

SOGo, GOSa, Postfix, Amavis, Dovecot auf Ubuntu Server 12.04

Ich habe kürzlich mein Mailsystem auf LDAP umgestellt und dann auch gleich noch SOGo installiert um CalDAV und CardDAV zu haben. Hauptsächlich zur Nachvollziehbarkeit für mich selbst sind hier die Schritte, die ich auf einem frisch installierten Ubuntu 12.04 durchgeführt habe. Vielleicht hilft es ja auch jemand anderem. Ich hoffe es ist alles komplett. Wenn das mal jemand nachgemacht hat, würde ich mich über einen Kommentar freuen. Auch für Verbesserungsvorschläge der Konfiguration wäre ich dankbar.

Alle Shell eingaben sind als root durchzuführen, wenn nicht anders angegeben. Als hostname verwende ich hier mail.example.com. Die LDAP Base ist dc=example,dc=com.

Zunächst habe ich erstmal etckeeper installiert, da ich meine Konfiguratinsdateien gerne unter Versionsverwaltung habe:

apt-get install etckeeper git
#/etc/etckeeper/etckeeper.conf editieren
etckeeper init
etckeeper commit
diff -u etckeeper.conf.orig etckeeper.conf
--- etckeeper.conf.orig	2012-05-30 21:40:37.536487058 +0200
+++ etckeeper.conf	2012-05-21 16:52:50.197848685 +0200
@@ -1,7 +1,7 @@
 # The VCS to use.
 #VCS="hg"
-#VCS="git"
-VCS="bzr"
+VCS="git"
+#VCS="bzr"
 #VCS="darcs"

 # Options passed to git commit when run by etckeeper.

GOsa wird zur Verwaltung des LDAP Systems genutzt.

apt-get install slapd gosa-schema gosa gosa-plugin-systems gosa-plugin-mail ldap-utils

Ich bevorzuge das rfc2307bis Schema für LDAP-Gruppen. Wer das nis Schema benutzen möchte, kann den nächsten Schritt weglassen.

service slapd stop
rm /etc/ldap/slapd.d/cn=config/cn=schema/cn={2}nis.ldif
cp /etc/ldap/schema/gosa/rfc2307bis.ldif /etc/ldap/slapd.d/cn=config/cn=schema/cn={2}rfc2307bis.ldif
#rfc2307bis.ldif editieren siehe diff
service slapd start

 

--- ldap/schema/gosa/rfc2307bis.ldif	2011-04-11 13:41:29.000000000 +0200
+++ ldap/slapd.d/cn=config/cn=schema/cn={2}rfc2307bis.ldif	2012-05-23 14:33:14.664485332 +0200
@@ -1,7 +1,7 @@
 #
 ################################################################################
 #
-dn: cn=rfc2307bis,cn=schema,cn=config
+dn: cn=rfc2307bis
 objectClass: olcSchemaConfig
 cn: rfc2307bis
 #

Hier geht es für alle weiter. Es werden die von GOsa benötigten Schemata zum LDAP hinzugefügt:

ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/gosystem.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/gofon.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/goto.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/goserver.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/samba3.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/gosa/gosa-samba3.ldif

#index setzen
ldapmodify -Y EXTERNAL -H ldapi:///
dn: olcDatabase={1}hdb,cn=config
add: olcDbIndex
olcDbIndex: uid eq
olcDbIndex: mail eq
olcDbIndex: cn eq
olcDbIndex: gosaMailAlternateAddress eq
olcDbIndex: postfixMyDestinations eq

Auf http://mail.example.com/gosa den Anweisungen folgen. Je nachdem ob man die rfc2307bis Gruppen im LDAP aktiviert hat, oder nicht muss die entsprechende Einstellung im GOsa Setup gewählt werden.

Nachdem man die generierte GOsa Konfiguration gespeichert hat, muss zunächst ein neues System in GOsa angelegt werden:

In diesem System kann nun ein neuer Mailserver angelegt werden:

Hierbei ist es wichtig, dass unter dem Punkt “Domänen, für die Mail angenommen wird” der Hostname des Servers eingetragen wird. In diesem Fall also mail.example.com.

Als nächstes werden postfix, dovecot und amavisd-new installiert. pyzor und razor verbessern die SPAM-Erkennungsrate, können aber weggelassen werden, wenn man das möchte. clamav-unofficial-sigs sind SPAM-Signaturen für clamav. mutt ist optional, aber hilfreich beim testen. phpldapadmin ist auch optimal, aber wenn man mal schnell in der LDAP-Datenbank rumeditieren will, ist es ganz hilfreich.

 apt-get install postfix postfix-ldap postfix-policyd-spf-python dovecot-ldap dovecot-imapd dovecot-pop3d dovecot-sieve dovecot-managesieved amavisd-new-postfix pyzor razor clamav-unofficial-sigs mutt phpldapadmin

#alte exim configs entfernen
apt-get --purge remove exim4 exim4-base exim4-config exim4-daemon-light

#damit clamav auf die von amavis bereitgestellten Mails zugreifen darf
adduser clamav amavis

#Mail wird als User vmail unter /var/lib/vmail gespeichert
groupadd -g 800 vmail
useradd -m -u 800 -g 800 -d /var/lib/vmail -s /bin/false vmail

Das auth_username_format wird geändert, damit man sich mit der uid anmelden kann. Ausserdem muss die system Konfiguartion durch die ldap Konfiguration ersetzt werden.

diff --git a/dovecot/conf.d/10-auth.conf b/dovecot/conf.d/10-auth.conf
index d5d2a45..87a9137 100644
--- a/dovecot/conf.d/10-auth.conf
+++ b/dovecot/conf.d/10-auth.conf
@@ -47,7 +47,7 @@
 # the standard variables here, eg. %Lu would lowercase the username, %n would
 # drop away the domain if it was given, or "%n-AT-%d" would change the '@' into
 # "-AT-". This translation is done after auth_username_translation changes.
-#auth_username_format =
+auth_username_format = %n

 # If you want to allow master users to log in by specifying the master
 # username within the normal username string (ie. not using SASL mechanism's
@@ -118,9 +118,9 @@ auth_mechanisms = plain
 #!include auth-deny.conf.ext
 #!include auth-master.conf.ext

-!include auth-system.conf.ext
+#!include auth-system.conf.ext
 #!include auth-sql.conf.ext
-#!include auth-ldap.conf.ext
+!include auth-ldap.conf.ext
 #!include auth-passwdfile.conf.ext
 #!include auth-checkpassword.conf.ext
 #!include auth-vpopmail.conf.ext

In /var/lib/vmail/mail werden die Postfächer der Benutzer gespeichert. Unter /var/lib/vmail/home werden sieve scripts gespeichert.

diff --git a/dovecot/conf.d/10-mail.conf b/dovecot/conf.d/10-mail.conf
index 9c05710..f51402f 100644
--- a/dovecot/conf.d/10-mail.conf
+++ b/dovecot/conf.d/10-mail.conf
@@ -28,6 +28,8 @@
 #
 #
 #mail_location =
+mail_location = maildir:/var/lib/vmail/mail/%u
+mail_home = /var/lib/vmail/home/%u

 # If you need to set multiple mailbox locations or want to change default
 # namespace settings, you can do it by defining namespace sections.
@@ -108,6 +110,8 @@
 # or names.
 #mail_uid =
 #mail_gid =
+mail_uid = vmail
+mail_gid = vmail

 # Group to enable temporarily for privileged operations. Currently this is
 # used only with INBOX when either its initial creation or dotlocking fails.

Die Auth-Listener von Dovecot müssen die richtigen Berechtigungen haben:

diff --git a/dovecot/conf.d/10-master.conf b/dovecot/conf.d/10-master.conf
index f0e79ab..96ec2e5 100644
--- a/dovecot/conf.d/10-master.conf
+++ b/dovecot/conf.d/10-master.conf
@@ -81,13 +81,18 @@ service auth {
   unix_listener auth-userdb {
     #mode = 0600
     #user =
-    #group =
+    #group =
+    user = vmail
+    group = vmail
   }

   # Postfix smtp-auth
-  #unix_listener /var/spool/postfix/private/auth {
+  unix_listener /var/spool/postfix/private/auth {
   #  mode = 0666
-  #}
+    mode = 0660
+    user = postfix
+    group = postfix
+  }

   # Auth process is run as this user.
   #user = $default_internal_user

Dovecot LDAP Konfiguration:

diff --git a/dovecot/dovecot-ldap.conf.ext b/dovecot/dovecot-ldap.conf.ext
index 7dcc748..21e4204 100644
--- a/dovecot/dovecot-ldap.conf.ext
+++ b/dovecot/dovecot-ldap.conf.ext
@@ -14,7 +14,7 @@
 #        by * none

 # Space separated list of LDAP hosts to use. host:port is allowed too.
-#hosts =
+hosts = localhost

 # LDAP URIs to use. You can use this instead of hosts list. Note that this
 # setting isn't supported by all LDAP libraries.
@@ -22,10 +22,10 @@

 # Distinguished Name - the username used to login to the LDAP server.
 # Leave it commented out to bind anonymously (useful with auth_bind=yes).
-#dn = 
+dn = cn=admin,dc=example,dc=com

 # Password for LDAP server, if dn is specified.
-#dnpass = 
+dnpass = LDAP_PASSWORD

 # Use SASL binding instead of the simple binding. Note that this changes
 # ldap_version automatically to be 3 if it's lower. Also note that SASL binds
@@ -64,7 +64,7 @@
 # The pass_filter is used to find the DN for the user. Note that the pass_attrs
 # is still used, only the password field is ignored in it. Before doing any
 # search, the binding is switched back to the default DN.
-#auth_bind = no
+auth_bind = yes

 # If authentication binding is used, you can save one LDAP request per login
 # if users' DN can be specified with a common template. The template can use
@@ -87,7 +87,7 @@

 # LDAP base. %variables can be used here.
 # For example: dc=mail, dc=example, dc=org
-base =
+base = ou=people,dc=example,dc=com

 # Dereference: never, searching, finding, always
 #deref = never
@@ -112,6 +112,7 @@ base =
 #   %n - user part in user@domain, same as %u if there's no domain
 #   %d - domain part in user@domain, empty if user there's no domain
 #user_filter = (&(objectClass=posixAccount)(uid=%u))
+user_filter = (&(objectClass=gosaMailAccount)(uid=%n))

 # Password checking attributes:
 #  user: Virtual user name (user@domain), if you wish to change the
@@ -120,6 +121,7 @@ base =
 # There are also other special fields which can be returned, see
 # http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
 #pass_attrs = uid=user,userPassword=password
+pass_attrs = uid=user,userPassword=password

 # If you wish to avoid two LDAP lookups (passdb + userdb), you can use
 # userdb prefetch instead of userdb ldap in dovecot.conf. In that case you'll
@@ -130,10 +132,11 @@ base =

 # Filter for password lookups
 #pass_filter = (&(objectClass=posixAccount)(uid=%u))
+pass_filter = (&(objectClass=gosaMailAccount)(|(uid=%u)(mail=%u)))

 # Attributes and filter to get a list of all users
-#iterate_attrs = uid=user
-#iterate_filter = (objectClass=posixAccount)
+iterate_attrs = uid=user
+iterate_filter = (objectClass=gosaMailAccount)

 # Default password scheme. "{scheme}" before password overrides this.
 # List of supported schemes is in: http://wiki2.dovecot.org/Authentication

Mailaddressen zu alias auflösen:

diff --git a/postfix/ldap-virtual_alias_maps.cf b/postfix/ldap-virtual_alias_maps.cf
new file mode 100644
index 0000000..4f5362c
--- /dev/null
+++ b/postfix/ldap-virtual_alias_maps.cf
@@ -0,0 +1,8 @@
+server_host = ldap://localhost
+search_base = ou=people,dc=example,dc=com
+bind = no
+
+query_filter = (&(objectclass=gosaMailAccount)(|(|(mail=%s)(gosaMailAlternateAddress=%s))(uid=%s)))
+result_attribute = uid
+#domain = domain.tld
+

LDAP-Gruppen auflösen:

diff --git a/postfix/ldap-virtual_alias_maps_groups.cf b/postfix/ldap-virtual_alias_maps_groups.cf
new file mode 100644
index 0000000..8b0ca10
--- /dev/null
+++ b/postfix/ldap-virtual_alias_maps_groups.cf
@@ -0,0 +1,8 @@
+server_host = ldap://localhost
+search_base = ou=groups,dc=example,dc=com
+bind = no
+
+query_filter = (&(objectclass=gosaMailAccount)(|(mail=%s)(gosaMailAlternateAddress=%s)))
+result_attribute = memberUid, gosaMailForwardingAddress
+#domain = domain.tld
+

LDAP Abfrage um die Domains herauszufinden, für die wir Mail akzeptieren:

diff --git a/postfix/ldap-virtual_mailbox_domains.cf b/postfix/ldap-virtual_mailbox_domains.cf
new file mode 100644
index 0000000..fe94808
--- /dev/null
+++ b/postfix/ldap-virtual_mailbox_domains.cf
@@ -0,0 +1,7 @@
+server_host = ldap://localhost
+search_base = ou=systems,dc=example,dc=com
+bind = no
+
+query_filter = (|(postfixMyDestinations=%s))
+result_attribute = postfixMyDestinations
+

LDAP Abfrage um den Mailboxnamen zu einer Mailaddresse herauszufinden:

diff --git a/postfix/ldap-virtual_mailbox_maps.cf b/postfix/ldap-virtual_mailbox_maps.cf
new file mode 100644
index 0000000..8b8322a
--- /dev/null
+++ b/postfix/ldap-virtual_mailbox_maps.cf
@@ -0,0 +1,7 @@
+server_host = ldap://localhost
+search_base = ou=people,dc=example,dc=com
+bind = no
+
+query_filter = (&(objectclass=gosaMailAccount)(|(mail=%s)(gosaMailAlternateAddress=%s)))
+result_attribute = uid
+

Die LDAP Abfragen dem Postfix bekannt machen und Dovecot als Delivery und SASL Autentifizeirung konfigurieren:

diff --git a/postfix/main.cf b/postfix/main.cf
index c1acd07..a5b3c34 100644
--- a/postfix/main.cf
+++ b/postfix/main.cf
@@ -31,12 +31,33 @@ myhostname = mail.example.com
 alias_maps = hash:/etc/aliases
 alias_database = hash:/etc/aliases
 myorigin = /etc/mailname
-mydestination = mail.example.com, localhost.example.com, , localhost
+mydestination = localhost.example.com, , localhost
 relayhost =
 mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
 mailbox_size_limit = 0
 recipient_delimiter = +
 inet_interfaces = all
+inet_protocols = all
+
+smtpd_sasl_type = dovecot
+
+# Can be an absolute path, or relative to $queue_directory
+# Debian/Ubuntu users: Postfix is setup by default to run chrooted, so it is best to leave it as-is below
+smtpd_sasl_path = private/auth
+
+# and the common settings to enable SASL:
+smtpd_sasl_auth_enable = yes
+smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policy-spf
+
 content_filter = smtp-amavis:[localhost]:10024
 policy-spf_time_limit = 3600s
-smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination, check_policy_service unix:private/policy-spf
+
+virtual_mailbox_maps = ldap:/etc/postfix/ldap-virtual_mailbox_maps.cf
+virtual_mailbox_domains = ldap:/etc/postfix/ldap-virtual_mailbox_domains.cf
+virtual_alias_maps = ldap:/etc/postfix/ldap-virtual_alias_maps_groups.cf, ldap:/etc/postfix/ldap-virtual_alias_maps.cf
+
+virtual_gid_maps = static:vmail
+virtual_uid_maps = static:vmail
+virtual_transport = dovecot
+dovecot_destination_recipient_limit = 1
+

Dovecot als delivery agent dem Postfix bekannt machen:

diff --git a/postfix/master.cf b/postfix/master.cf
index 35512c9..746d5b2 100644
--- a/postfix/master.cf
+++ b/postfix/master.cf
@@ -110,6 +110,9 @@ scalemail-backend unix      -       n       n       -       2       pipe
 mailman   unix  -       n       n       -       -       pipe
   flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
   ${nexthop} ${user}
+#dovecot
+dovecot   unix  -       n       n       -       -       pipe
+    flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient} -f ${sender}

 # ==========================================================================
 # service type  private unpriv  chroot  wakeup  maxproc command + args

Virenscanner und SPAM-Checker im amavis aktivieren:

diff --git a/amavis/conf.d/15-content_filter_mode b/amavis/conf.d/15-content_filter_mode
index 57c62c8..7b1a2ac 100644
--- a/amavis/conf.d/15-content_filter_mode
+++ b/amavis/conf.d/15-content_filter_mode
@@ -10,8 +10,8 @@ use strict;
 # If You wish to enable it, please uncomment the following lines:

-#@bypass_virus_checks_maps = (
-#   \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
+@bypass_virus_checks_maps = (
+   \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);

 #
@@ -21,7 +21,7 @@ use strict;
 # If You wish to enable it, please uncomment the following lines:

-#@bypass_spam_checks_maps = (
-#   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
+@bypass_spam_checks_maps = (
+   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

 1;  # ensure a defined return

Sieve support konfigurieren:

diff --git a/dovecot/conf.d/15-lda.conf b/dovecot/conf.d/15-lda.conf
index 609023a..4b7a2de 100644
--- a/dovecot/conf.d/15-lda.conf
+++ b/dovecot/conf.d/15-lda.conf
@@ -44,5 +44,5 @@

 protocol lda {
   # Space separated list of plugins to load (default is global mail_plugins).
-  #mail_plugins = $mail_plugins
+  mail_plugins = $mail_plugins sieve
 }

SOGo nutzt den Port 2000 für Sieve:

diff --git a/dovecot/conf.d/20-managesieve.conf b/dovecot/conf.d/20-managesieve.conf
index a73a417..e5e17cf 100644
--- a/dovecot/conf.d/20-managesieve.conf
+++ b/dovecot/conf.d/20-managesieve.conf
@@ -9,9 +9,9 @@ service managesieve-login {
   #  port = 4190
   #}

-  #inet_listener sieve_deprecated {
-  #  port = 2000
-  #}
+  inet_listener sieve_deprecated {
+    port = 2000
+  }

   # Number of connections to handle before starting a new process. Typically
   # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0

SOGo benötigt für einige Regeln imapflags:

diff --git a/dovecot/conf.d/90-sieve.conf b/dovecot/conf.d/90-sieve.conf
index 516ac46..79de623 100644
--- a/dovecot/conf.d/90-sieve.conf
+++ b/dovecot/conf.d/90-sieve.conf
@@ -45,6 +45,7 @@ plugin {
   # deprecated imapflags extension in addition to all extensions thatwere
   # already enabled by default.
   #sieve_extensions = +notify +imapflags
+  sieve_extensions = +imapflags

   # The Pigeonhole Sieve interpreter can have plugins of its own. Using this
   # setting, the used plugins can be specified. Check the Dovecot wiki

An dieser stelle sollte das Mail-System funktionieren.

Zunächst muss das SOGo repository in apt eingebunden werden. Ich habe den nightly build von SOGo 2.0 genommen, die Konfiguration sollte aber auch für SOGo 1.x passen.

/etc/apt/sources.list.d/sogo.list:

deb http://inverse.ca/ubuntu-nightly precise precise
#deb http://inverse.ca/ubuntu precise precise

Apache kann automatisch konfiguriert werden. Für mysql sollte ein Passwort gesetzt werden. phpmyadmin ist optional, aber hilfreich wenn man mal in der Datenbank rumfuhrwerken will. Ich werde spöter phpmyadmin dazu benutzen die SOGo Datenbank anzulegen, aber das kann man natürich auch über die shell machen.

apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4
apt-get update
apt-get install sogo mysql-server phpmyadmin memcached

Es müssen noch ein paar module für den apache aktiviert werden:

a2enmod proxy
a2enmod proxy_http
a2enmod rewrite
a2enmod substitute

SOGo legt seine config im home directory des sogo Benutzers an. Das liegt allerdings nicht unter /etc und wird daher nicht vom etckeeper verwaltet. Das ist doof. Deshalb verschiebe ich das home directory nach /etc/sogo:

/etc/init.d/sogo stop
usermod -m -d /etc/sogo sogo

Jetzt wird auch die SOGo Datenbank angelegt. Ich habe phpmyadmin dazu benutzt:


Und hier ist meine SOGo Konfigurationsdatei.:

diff --git a/sogo/GNUstep/Defaults/.GNUstepDefaults b/sogo/GNUstep/Defaults/.GNUstepDefaults
index 8c01f2e..e2f0438 100644
--- a/sogo/GNUstep/Defaults/.GNUstepDefaults
+++ b/sogo/GNUstep/Defaults/.GNUstepDefaults
@@ -7,6 +7,102 @@
     </dict>
     <key>sogod</key>
     <dict>
+        <key>OCSFolderInfoURL</key>
+        <string>mysql://sogo:MYSQL_PASSWORD@localhost:3306/sogo/sogo_folder_info</string>
+        <key>OCSSessionsFolderURL</key>
+        <string>mysql://sogo:MYSQL_PASSWORD@localhost:3306/sogo/sogo_sessions_folder</string>
+        <key>SOGoACLsSendEMailNotifications</key>
+        <string>YES</string>
+        <key>SOGoAppointmentSendEMailNotifications</key>
+        <string>YES</string>
+        <key>SOGoAuthenticationMethod</key>
+        <string>LDAP</string>
+        <key>SOGoPasswordChangeEnabled</key>
+        <string>YES</string>
+        <key>SOGoEnablePublicAccess</key>
+        <string>YES</string>
+        <key>SOGoFirstDayOfWeek</key>
+        <string>1</string>
+        <key>SOGoFirstWeekOfYear</key>
+        <string>First4DayWeek</string>
+        <key>SOGoFoldersSendEMailNotifications</key>
+        <string>YES</string>
+        <key>SOGoLanguage</key>
+        <string>German</string>
+        <key>SOGoMailDomain</key>
+        <string>example.com</string>
+        <key>SOGoMailMessageCheck</key>
+        <string>every_minute</string>
+        <key>SOGoProfileURL</key>
+        <string>mysql://sogo:MYSQL_PASSWORD@localhost:3306/sogo/sogo_user_profile</string>
+        <key>SOGoSieveScriptsEnabled</key>
+        <string>YES</string>
+        <key>SOGoSieveServer</key>
+        <string>sieve://localhost:2000</string>
+        <key>SOGoTimeZone</key>
+        <string>Europe/Berlin</string>
+        <key>SOGoUserSources</key>
+        <array>
+            <dict>
+                <key>CNFieldName</key>
+                <string>cn</string>
+                <key>IMAPHostFieldName</key>
+                <string>gosaMailServer</string>
+                <key>UIDFieldName</key>
+                <string>uid</string>
+                <key>IMAPLoginFieldName</key>
+                <string>uid</string>
+                <key>baseDN</key>
+                <string>ou=people,dc=example,dc=com</string>
+                <key>bindDN</key>
+                <string>cn=admin,dc=example,dc=com</string>
+                <key>bindFields</key>
+                <string>uid</string>
+                <key>bindPassword</key>
+                <string>LDAP_PASSWORD</string>
+                <key>canAuthenticate</key>
+                <string>YES</string>
+                <key>displayName</key>
+                <string>Shared Addresses</string>
+                <key>hostname</key>
+                <string>localhost</string>
+                <key>id</key>
+                <string>public</string>
+                <key>isAddressBook</key>
+                <string>YES</string>
+                <key>port</key>
+                <string>389</string>
+                <key>type</key>
+                <string>ldap</string>
+            </dict>
+           <dict>
+                <key>CNFieldName</key>
+                <string>cn</string>
+                <key>IDFieldName</key>
+                <string>cn</string>
+                <key>UIDFieldName</key>
+                <string>cn</string>
+                <key>baseDN</key>
+                <string>ou=groups,dc=example,dc=com</string>
+                <key>bindDN</key>
+                <string>cn=admin,dc=example,dc=com</string>
+                <key>bindPassword</key>
+                <string>LDAP_PASSWORD</string>
+                <key>canAuthenticate</key>
+                <string>YES</string>
+                <key>displayName</key>
+                <string>Groups</string>
+                <key>hostname</key>
+                <string>127.0.0.1</string>
+                <key>id</key>
+                <string>t-srv_groups</string>
+                <key>isAddressBook</key>
+                <string>YES</string>
+                <key>port</key>
+                <string>389</string>
+            </dict>
+        </array>
     </dict>
 </dict>
-</plist>
\ No newline at end of file
+</plist>
+

Die Apache Konfiguration für SOGo muss noch angepasst werden. Den Teil mit der Erstellung von SSL Zertifikaten habe ich hier mal rausgelassen.

diff --git a/apache2/conf.d/SOGo.conf b/apache2/conf.d/SOGo.conf
index 040814e..64ed4a2 100644
--- a/apache2/conf.d/SOGo.conf
+++ b/apache2/conf.d/SOGo.conf
@@ -30,6 +30,14 @@ ProxyRequests Off
 SetEnv proxy-nokeepalive 1
 ProxyPreserveHost On

+#timezone fix
+RequestHeader unset Accept-Encoding
+
+AddOutputFilterByType SUBSTITUTE text/calendar text/xml
+Substitute 's|^RRULE:([A-Z].+[A-Za-z0-9]);?FREQ=([A-Za-z]*)(;?.*)$|RRULE:FREQ=$2;$1$3|' 
+#Substitute 's|RRULE:BYDAY=-1SU;FREQ=YEARLY;BYMONTH=10|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10|n'
+#Substitute 's|RRULE:BYDAY=-1SU;FREQ=YEARLY;BYMONTH=3|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3|n'
+
 # When using CAS, you should uncomment this and install cas-proxy-validate.py
 # in /usr/lib/cgi-bin to reduce server overloading
 #
@@ -65,3 +73,55 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
 # header of emails.
 RewriteEngine On
 RewriteRule ^/SOGo/(.*)$ /SOGo/$1 [env=REMOTE_HOST:%{REMOTE_ADDR},PT]
+
+
+Listen 8843
+NameVirtualHost 0.0.0.0:8843
+<VirtualHost 0.0.0.0:8843>
+ ServerName mail.example.com
+ SSLEngine On
+
+        #mitigate beast attack
+        SSLHonorCipherOrder On
+        SSLCipherSuite ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
+
+        #   A self-signed (snakeoil) certificate can be created by installing
+        #   the ssl-cert package. See
+        #   /usr/share/doc/apache2.2-common/README.Debian.gz for more info.
+        #   If both key and certificate are stored in the same file, only the
+        #   SSLCertificateFile directive is needed.
+        SSLCertificateFile    /etc/ssl/private/mail.example.com.pem
+        SSLCertificateKeyFile /etc/ssl/private/mail.example.com.pem
+
+        #   Server Certificate Chain:
+        #   Point SSLCertificateChainFile at a file containing the
+        #   concatenation of PEM encoded CA certificates which form the
+        #   certificate chain for the server certificate. Alternatively
+        #   the referenced file can be the same as SSLCertificateFile
+        #   when the CA certificates are directly appended to the server
+        #   certificate for convinience.
+        SSLCertificateChainFile /etc/ssl/private/mail.example.com.pem
+
+ProxyRequests Off
+SetEnv proxy-nokeepalive 1
+ProxyPreserveHost On
+
+ProxyPassInterpolateEnv On
+#for CardDAV
+ProxyPass /principals http://127.0.0.1:20000/SOGo/dav/ interpolate
+ProxyPass /SOGo/dav/ http://127.0.0.1:20000/SOGo/dav/ interpolate
+ProxyPass / http://127.0.0.1:20000/SOGo/dav/ interpolate
+
+<Proxy http://127.0.0.1:20000>
+ RequestHeader set "x-webobjects-server-port" "8843"
+ RequestHeader set "x-webobjects-server-name" "mail.example.com:8843"
+ RequestHeader set "x-webobjects-server-url" "https://mail.example.com:8843"
+ RequestHeader set "x-webobjects-server-protocol" "HTTP/1.0"
+ RequestHeader set "x-webobjects-remote-host" "127.0.0.1"
+ AddDefaultCharset UTF-8
+ Order allow,deny
+ Allow from all
+</Proxy>
+
+</VirtualHost>
+

Jetzt muss noch SOGo gestartet und der Apache neu gestartet werden:

service sogo start
service apache2 restart

Ich hoffe mal, dass ich alle relevanten Einstellungen beschrieben habe. Kommentare gerne in den Kommentaren. 🙂

Categories
Private

Review der Kinderlieder-App beim Kinderappgarten

Heute wurde meine Kinderlieder-App vom Kinderappgarten reviewed. Hier geht’s zur Review.

Auch sonst kann ich den Kinderappgarten sehr empfehlen. Da gibt es Reviews zu sehr vielen Apps die für Kinder geeignet sind. Für alle Eltern eine riesige Fundgrube.

Categories
Tech

MouseOver mit Selenium-WebDriver

Manchmal™ möchte man Webseiten testen, die ein Menü einblenden, wenn man mit der Maus über einem Element ist. Folgender Code für Selenium WebDriver macht genau das:

protected void mouseOver(WebElement element) {
    String code = "var fireOnThis = arguments[0];"
                + "var evObj = document.createEvent('MouseEvents');"
                + "evObj.initEvent( 'mouseover', true, true );"
                + "fireOnThis.dispatchEvent(evObj);";
    ((JavascriptExecutor) driver).executeScript(code, element);
}

Categories
Private

Weihnachtslieder als kostenloser Download für die Kinderlieder-App

Die Weihnachtslieder der Musikpiraten sind jetzt als kostenloser Download für meine Kinderlieder-App verfügbar.

Es sind folgende Lieder enthalten:

  1. Alle Jahre wieder
  2. Der Christbaum ist der schönste Baum
  3. Der Christbaum ist der schönste Baum (Chorsatz)
  4. Es wird scho Klei dumpf
  5. En gardant ma bergerie
  6. Fröhliche Weihnacht überall
  7. Ich steh an deiner Krippe hier
  8. Ich lag und schlief, da träumte mir
  9. Ihr Kinderlein kommet
  10. In dulci jubilo
  11. Kommet, ihr Hirten
  12. Lasst uns froh und munter sein
  13. Leise rieselt der Schnee
  14. Macht hoch die Tür
  15. Morgen, Kinder, wird’s was geben
  16. O Tannenbaum
  17. O du fröhliche
  18. Still, still, still
  19. Stille Nacht, heilige Nacht
  20. Vom Himmel hoch, da komm’ ich her
  21. Herbei, o ihr Gläub’gen – Adeste Fideles
  22. Herbei, o ihr Gläub’gen – Adeste Fideles (Chorsatz)
  23. Am Weihnachtsbaume die Lichter brennen
  24. Der Heiland ist geborenEs kommt ein Schiff geladen
  25. Es ist ein Ros’ entsprungen
  26. Es ist ein Ros’ entsprungen (Chorsatz)
  27. Süßer die Glocken nie klingen
  28. Jingle Bells
  29. Joseph, lieber Joseph mein
  30. Kling, Glöckchen, klingelingeling
  31. Morgen kommt der Weihnachtsmann
  32. Schneeflöckchen
  33. Tochter Zion, freue dich
  34. Tochter Zion, freue dich (Chorsatz)
  35. We Wish You A Merry Christmas
  36. Zu Bethlehem geboren

 

Zusätzlich gibt es den Gesang zu den ursprünglichen Liedern als In-App-Kauf.

Viel Spass damit und frohe Weihnachten!