Schön, dass Sie Ihren Weg hierher gefunden haben.

Auf diesen Seiten schreibe ich hin und wieder Dinge, mit denen ich während meiner Arbeit in Berührung gekommen bin und von denen ich denke, dass sie für andere interessant sein könnten.

Enjoy!

IIS7: Domänen-Zertifikat erstellen: Die Zertifikatanforderung wurde an die Online-Zertifizierungsstelle gesendet, das Zertifikat jedoch nicht ausgestellt.

Wer im Informationsdienste-Manager der IIS7 versucht, ein Domänen-Zertifikat als lokaler Administrator (nicht Domänen-Administrator!) für den lokalen PC auszustellen, stolpert vielleicht über die folgende Fehlermeldung:

Die Zertifikatanforderung wurde an die Online-Zertifizierungsstelle gesendet, das Zertifikat jedoch nicht ausgestellt. Die Anforderung wurde abgelehnt.

Ein Blick in die Ereignisanzeige des Servers, der die Zertifizierungsstelle beherbergt, eröffnet das Folgende:

Die Anforderung 40 wurde abgelehnt, da Die Berechtigungen auf dieser Zertifikatvorlage lassen nicht zu, dass der aktuelle Benutzer sich für diesen Zertifikattyp einschreibt. 0x80094012 (-2146877422). Die Anforderung war für CN=***.***.local, OU=***, O=***, L=***, S=***, C=***. Weitere Informationen: Verweigert vom Richtlinienmodul

Sieht man einmal von dem etwas gewöhnungsbedürftigem Deutsch ab, lässt sich daraus schließen, dass eine Berechtigung fehlt. Wem bis hierher noch nicht der Gedanke gekommen ist, dass wohl der Domänen-Administrator für diesen Vorgang notwendig ist, dem sollte er jetzt kommen.

Mit der folgenden Eingabe lässt sich der lokale IIS-Manager als Domänen-Administrator ausführen, was das Problem letztendlich beseitigt:

runas /noprofile /user:DOMÄNE\Administrator "%windir%\system32\inetsrv\InetMgr.exe"
Geschrieben von Philip am 09.05.2012 | Kommentare weiterlesen...

Apticron sendet keine Mails an externe E-Mail-Adressen

Auf einem Server sollte Apticron per E-Mail ausstehende Updates an eine externe E-Mail-Adresse melden. Als Mailserver war Exim installiert, jedoch kamen keine Update-Mails.

Die Exim-Logdatei:

tail -n 20 -f /var/log/exim4/mainlog

Enthielt folgenden Eintrag:

2012-02-17 11:29:21 1RyL45-0006Jl-Qo ** email@domain.tld R=nonlocal: Mailing to remote domains not supported

wobei email@domain.tld die externe E-Mail-Adresse war, woran die Update-Meldungen gesendet werden sollten.

Das bedeutet, dass Exim die E-Mail nicht auslieferte, da dieser nicht für externe Domains konfiguriert war.

dpkg-reconfigure exim4

löste letztendlich das Problem, indem Exim angewiesen wurde, E-Mails an externe Adressen auszuliefern.

Geschrieben von Philip am 17.02.2012 | Kommentare weiterlesen...

WebDAV extrem langsam unter Windows 7

Nachdem der Zugriff auf ein WebDAV-Laufwerk unter Linux problemlos und schnell möglich war, unter Windows 7 jedoch für die Anzeige jedes einzelnen Ordners extrem lang brauchte, löste folgender Tipp das Problem:

Wenn Webdav unter Windows 7 verblüffend langsam ist: Bei den Proxy-Einstellungen im IE die automatische Suche abschalten

Geschrieben von Philip am 28.11.2011 | Kommentare weiterlesen...

Einem WCF Dienst aus PHP ein Dictionary als Parameter übergeben

Einem WCF Dienst ein Dictionary (hier Dictionary<string, string>) als Parameter zu übergeben, kann z.B. sinnvoll sein, wenn der Dienst eine Suche in einer SQL-Datenbank durchführen soll, wobei die SQL-Abfrage, die dieser Suche zu Grunde liegt, beliebig viele SQL-Parameter enthalten kann:

SELECT
    *
FROM
    [TABLE]
WHERE
    FIELD1 LIKE @Search1
    FIELD2 LIKE @Search2
    FIELD3 LIKE @Search3
    ...
    FIELDN LIKE @SearchN

Die entsprechende Methode des Dienstes könnte dann so aussehen (die Deklaration als void um so einfach wie möglich zu bleiben):

public void SendDictString(Dictionary<string, string> dParam)
{
    SqlCommand sc = new SqlCommand();
    sc.CommandText = ...; // Die SQL-Abfrage, z.B. aus Datei geladen

    Dictionary<string,string>.Enumerator e = dParam.GetEnumerator();
    while (e.MoveNext())
    {
        Debug("{0}:{1}", e.Current.Key.ToString(), e.Current.Value.ToString());

        sc.Parameters.Add(new SqlParameter(e.Current.Key, e.Current.Value));
    }
    SqlDataReader sdr = sc.ExecuteReader();
}

Betrachtet man die resultierende WSDL-Datei, sieht diese so aus:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<xs:complexType name="ArrayOfKeyValueOfstringstring">
<xs:annotation>
<xs:appinfo>
<IsDictionary xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</IsDictionary>
</xs:appinfo>
</xs:annotation>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="KeyValueOfstringstring">
<xs:complexType>
<xs:sequence>
<xs:element name="Key" nillable="true" type="xs:string"/>
<xs:element name="Value" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:element name="ArrayOfKeyValueOfstringstring" nillable="true" type="tns:ArrayOfKeyValueOfstringstring"/>
</xs:schema>

Interessant an dieser Beschreibung ist dieser Teil:

<xs:sequence>
<xs:element name="Key" nillable="true" type="xs:string"/>
<xs:element name="Value" nillable="true" type="xs:string"/>
</xs:sequence>

Mit einer PHP-Anwendung ließe sich der Dienst entsprechend wie folgt konsumieren:

$client->SendDictString(array(
	"dParam"	=>	array(
		array(
			"Key"	=>	"Search1",
			"Value"	=>	"Suchbegriff1"
		),
		array(
			"Key"	=>	"Search2",
			"Value"	=>	"Suchbegriff2"
		),
		array(
			"Key"	=>	"Search3",
			"Value"	=>	"Suchbegriff3"
		)
	)
)

Die Debug-Ausgabe des WCF Dienst sieht dann so aus:

Search1:Suchbegriff1
Search2:Suchbegriff2
Search3:Suchbegriff3
Geschrieben von Philip am 15.07.2011 | Kommentare weiterlesen...

Fehlermeldung „Error Fetching http headers“ bei der Abfrage eines WCF Dienstes, welcher einen benutzerdefinierten Typ zurückgibt

Bei der Entwicklung eines WCF WebService, definierte ich den folgenden ServiceContract:

[ServiceContract]
public interface IClient
{
    ...
    [OperationContract]
    WebUser GetUserData(string sWebUserHash);
    ...
}

Beim Abruf einer Implementierung des Dienstes mit Hilfe des PHP SoapClient kam es zu der Exception:

Error Fetching http headers

Zwar hat mich die Suche nach dem Ursprung unter anderem zu einem PHP Bug Report geführt, jedoch konnten die dort angegebenen Hilfen mein Problem nicht beheben.

Da die Kompilierung des WebService ohne Probleme verlief, die Dienstbeschreibung per HTTP abrufbar war und andere Methoden ohne Probleme funktionierten, vermutete ich das Problem beim PHP SoapClient. Jedoch stellte sich dann heraus, dass ich vergessen hatte, dem WebService den Typ WebUser bekanntzumachen.

Das Problem löste sich durch die simple Ergänzung des folgenden Attributs:

[ServiceKnownType(typeof(WebUser))]

Der funktionierende Quellcode sah dann so aus:

[ServiceContract]
[ServiceKnownType(typeof(WebUser))]
public interface IClient
{
    ...
    [OperationContract]
    WebUser GetUserData(string sWebUserHash);
    ...
}
Geschrieben von Philip am 30.06.2011 | Kommentare weiterlesen...

Microsoft SQL Server: Problem mit dem GO-Schlüsselwort beim Verwenden des JDBC-Treibers

Arbeitet man mit dem Microsoft SQL Server, so wird man viel und oft das Schlüsselwort GO verwenden. Sollen Serverabfragen, die das GO-Schlüsselwort enthalten, mit JDBC ausgeführt werden, kommt es jedoch zu einer Ausnahme (Exception):

Falsche Syntax in der Nähe von ‚GO‘.

Anstatt alle SQL-Skripte, die das GO-Schlüsselwort enthalten, umzuschreiben, gibt es auch eine einfachere Möglichkeit in Java: Man zerlegt ein SQL-Skript einfach anhand des GO-Schlüsselworts und packt jedes Teilelement in ein eigenes JDBC-Statement.

Wir gehen von folgendem SQL-Skript aus:

USE [someDatabase]
GO

CREATE VIEW [someSchema].[someView] AS SELECT * FROM [someSchema].[someTable]
GO

CREATE TABLE [someSchema].[someTable] (
	someField VARCHAR(50) NULL
)
GO

Dann lassen sich alle Teile wie folgt ausführen:

Connection con; // Eine aktive Verbindung zu einem SQL-Server via JDBC 
String sql; // Oben genanntes SQL-Skript

for(String batch: sql.split("GO")){
	con.createStatement(batch.trim()).execute();
}
Geschrieben von Philip am 09.02.2011 | Kommentare weiterlesen...

Google Analytics Alternative wegen rechtlicher Unsicherheit

Schon seit langem setze ich auf Projekt-Webseiten Google Analytics ein, weil es meiner Meinung nach ausreichende und übersichtliche Auswertungen für Webseiten-Zugriffe bietet.

Zwar bin ich erklärter Google-Fan und finde es total absurd, wie in letzter Zeit gegen Google vorgegangen wird (siehe StreetView etc.) während ähnliche Dienste von Microsoft (Bing) und anderen Anbietern nicht schlechtgeredet werden, jedoch bewegt man sich beim Einsatz von Google Analytics scheinbar auf einem rechtlich unsicheren Pfad.

Da Webseiten, die Analytics einbinden, Daten über Besucher (auch die IP-Adressen) an Google weiterleiten, ist das rechtlich nicht in Ordnung. Beim Einsatz von Analytics muss man also im schlimmsten Fall mit (teuren) Abmahnungen rechnen.

Deswegen habe ich mich auf die Suche nach einer Alternative gemacht und diese jetzt gefunden: Piwik, eine Open-Source Google Analytics Alternative, speichert nach einer Installation auf einem eigenen Server Daten unabhängig von Dritten und kann leicht so angepasst werden, dass die Analysierung der Besucherdaten mit Deutschem Recht vereinbar ist:

  • Nach der Installation von Piwik das Plugin AnonymizeIP aktivieren
  • Auf einer Datenschutz-Unterseite o.ä. der Webseite dem Benutzer die Möglichkeit bieten, sich von der Datenerfassung auszuschließen
Geschrieben von Philip am 11.01.2011 | Kommentare weiterlesen...

MySQLDu in neuer Version 1.1

Meine kleine MySQL-Backup-Lösung MySQLDu gibt es in der neuen Version 1.1.

Neben zwei kleinen Änderungen ist die neu hinzugefügte FTP-Unterstützung interessant. Backups können jetzt automatisch auf einen FTP-Server hochgeladen werden.

MySQLDu 1.1 herunterladen

Geschrieben von Philip am 16.08.2010 | Kommentare weiterlesen...

Automatisches MySQL-Backup per Mail

Wem seine Daten lieb sind, der sichert sie häufig. Ob Wöchentlich, täglich oder stündlich hängt dabei von der Änderungsrate ebendieser ab, völlig unabhängig davon ist aber, dass die manuelle Erstellung von Backups kostbare Zeit kostet, die anderweitig besser investiert wäre.

Bei Internetprojekten ist die frei verfügbare MySQL-Datenbank mittlerweile ein Standard, administriert wird sie meistens mit phpMyAdmin, einem ebenso kostenlosem Web-Interface. Wer MySQL-Datenbanken normalerweise sichert, der erstellt in phpMyAdmin ein Datenbank-Dump und lässt es sich wahlweise als Textdatei oder in einem Archiv verpackt liefern. Doch aufgrund der nötigen Autorisierung am Server dauert die Erstellung schon einige Zeit, wieso da nicht einfacher? Mit einem automatischen Backup zum Beispiel.

Mithilfe eines Cronjobs, dem Tipp, wie man mit wget ein PHP-Script aufruft und MySQLDu lässt sich einfach eine Backup-Lösung erstellen. Wie das geht wird im Folgenden erklärt.

Laden wir zunächst die aktuelle Version von MySQLDu (1.0) herunter und entpacken das Archiv auf den Webserver, auf dem auch die MySQL-Datenbank liegt.

Als nächstes löschen wir die Dateien testoutput.php, testmail.php, testfile.php, msdu.sql und das Unterverzeichnis dump/, da diese entweder zu Testzwecken dienen oder für die Sicherung per Mail nicht erforderlich sind.

Ganz wichtig ist, dass das Unterverzeichnis tmp/ durch den Webserver-Benutzer schreibbar ist (am Besten setzen Sie die Rechte auf 0777).

Jetzt erstellen wir eine neue Datei cronbackup.php mit folgendem Inhalt:

<?php
	define("MYSQLDU_MODE","mail"); // legt den Mail-Modus fest

	// Datenbankzugangsdaten
	define("MYSQLDU_DB","");
	define("MYSQLDU_USER","");
	define("MYSQLDU_PASSWORD","");
	define("MYSQLDU_HOST","localhost");	

	// Mail-Optionen
	define("MYSQLDU_MAIL_FROM","noreply@philip-ehret.de");
	define("MYSQLDU_MAIL_FROMNAME","MySQLDu V!version");
	define("MYSQLDU_MAIL_TITLE","MySQL dump of !database on !d.!m.!Y (!H:!i:!s)");
	define("MYSQLDU_MAIL_TEXT","Hi there, this mail contains your dump of database !database created by MySQLDu V!version on !d.!m.!Y (!H:!i:!s).\n".
		"See attached file!\n\n".
		"Thanks for using MySQLDu (http://philip-ehret.de/projekte/mysqldu/)\n".
		"See also mysqldump.php from Huang Kai (http://atutility.com/software/mysqldumpphp/) on which MySQLDu is based on\n".
		"This mail was automatically generated and sent by PHPMailer(http://phpmailer.worxware.com/)");
	define("MYSQLDU_MAIL_SENDTO","mail@philip-ehret.de");
	define("MYSQLDU_MAIL_ATTACHMENT_NAME","!server.!database.!date.dump.sql");

	require_once("mysqldu-1.0.php");
?>

Wichtig ist, dass Sie die Konstanten MYSQLDU_USER, MYSQLDU_HOST, MYSQLDU_PASSWORD, MYSQLDU_DB an die Zugangsdaten Ihrer MySQL-Datenbank anpassen und MYSQLDU_MAIL_FROM und MYSQLDU_MAIL_SENDTO passend setzen (Das erste ist die E-Mail-Adresse, die später als Absender der Backup-Mail erscheint, das zweite die E-Mail-Adresse, an die das Backup gesandt werden soll).

Die Konstanten MYSQLDU_MAIL_TITLE und MYSQLDU_MAIL_TEXT können Sie nach belieben anpassen, mögliche Platzhalter finden Sie in der mysqldu-1.0.php in der Variablen $MYSQLDU_REPLACE_ARRAY.

Rufen Sie das Script jetzt einmal mit Ihrem Browser auf. Die Ausgabe sollte bei Erfolg die folgende sein. Eventuell öffnet Ihr Browser auch einen Download-Dialog, die heruntergeladene Datei enthält dann die Ausgabe.

{"status":"success","text":"Mail was successfully delivered"}

Es handelt sich hierbei um eine JSON-codierte Ausgabe. Das ist deswegen so, damit andere Scripte, die das Backup-Script aufrufen könnten, die Rückmeldungen einfach verarbeiten können.

Sollten Sie eine andere Ausgabe erhalten, so sehen Sie sich den JSON-codierten Fehlertext an. Dieser gibt Rückschlüsse darauf, was falsch läuft. Überprüfen Sie die Schreibrechte für das Unterverzeichnis tmp/ und stellen Sie sicher, dass Sie die Datenbankzugangsdaten korrekt eingegeben haben.

Wenn die Ausgabe stimmt, gehen Sie in das E-Mail-Postfach der für MYSQLDU_MAIL_SENDTO angegebenen E-Mail-Adresse. Dort sollte bereits eine E-Mail auf Sie warten, welche die Datenbanksicherung als Anhang enthält.

Um das E-Mail-Backup jetzt auch noch vollautomatisch zu erhalten, führen Sie sich den Artikel zur Ausführung eines PHP-Scriptes mithilfe eines Cronjobs zu Gemüte, welcher nicht nur für Plesk seine Gültigkeit hat.

Meine persönlichen Datenbanken sind zum Großteil in Unicode, da das etliche Vorteile bietet, welche den gegenüber anderen Datencodierungen höheren Speicherverbrauch meiner Meinung nach durchaus rechtfertigen.
Sollten Sie eine andere Codierung haben und die Dump-Datei fehlerhaft sein, versuchen Sie das Problem zu beheben, indem Sie an den Anfang der cronbackup.php folgenden Code einfügen:

define("MYSQLDU_UTF8",0);
Geschrieben von Philip am 13.08.2010 | Kommentare weiterlesen...

Mit Plesk einen PHP Cronjob einrichten

Wer mit Plesk einen Cronjob einrichten möchte, der ein PHP-Script aufruft, kann das ohne umständliche Aufrufe mit dem Befehl wget machen, welcher normalerweise ein Dokument von einer URL lädt und abspeichert. Der Trick hierfür ist die Verwendung der Option spider, welche die Abspeicherung unterdrückt:

wget http://anydomain.tld/script.php?action=dosomething --spider
Geschrieben von Philip am 13.08.2010 | Kommentare weiterlesen...