https://boethin.net/Sebastian BöthinVivitiCMS2013-04-29T18:32:00+02:00Sebastian Böthintag:boethin.net,2016-09-24:/entries/1019220Geotargeting, ccTLDs und der SEO-Faktor2013-04-29T18:32:00+02:002016-10-31T15:22:08+01:00<p>Google-Mythen sind bekanntlich eine moderne Form von Aberglauben. Zwar hat sich nun weitgehend herumgesprochen, dass raffinierte Backlink-Konstruktionen, magische Keywordverstecke und hochgezüchtete Linkfarmen nur noch eine der leichten Übungen für die Anti-Spamteams sind, dennoch herrscht weiterhin ein wildes Durcheinander von Experten, die wechelseitig neue Theorien ins Feld führen oder aber wieder mal “mit Google-Mythen aufräumen”. Und gelegentlich äußert sich <del datetime="2013-04-29T15:15:28+00:00">der Vatikan</del> Mountain View in seiner unerforschlichen Weisheit von der Kanzel herab mit der Verkündigung neuer Algorithmen zur Unterscheidung von schwarzen und weißen Schäfchen in der Gemeinde.</p>
<p><p><a href="http://www.youtube.com/embed/xh2pndFKtsg?feature=oembed">http://www.youtube.com/embed/xh2pndFKtsg?feature=oembed</a></p></p>
<p>Dabei ist doch eines ganz klar: Suchmaschinenoptimierung bedeutet zunächst einfach, eine gut strukturierte Seite mit interessantem und authentischem Inhalt zu machen und dabei darauf zu achten, dass sich die optisch dargestellte Semantik und Strukturierung auch im Markup wiederspiegelt. Suchmaschinen bewerten die Seiten danach, als wie gut sie für ihre menschliche Kundschaft geeignet erscheinen, und das tun sie mittlerweile ziemlich intelligent. Man sollte einfach nicht versuchen, sie auszutricksen, denn an den Webcrawlern arbeiten die besten Programmierer der Welt. Die kennen die Tricks alle schon, die billigen und die teuren.</p>
<p>Auch klar ist, dass zum Beispiel der legendäre <a href="https://de.wikipedia.org/wiki/PageRank" target="_blank">Pagerank</a> oft massiv überschätzt wird. Es ist eher irreführend, überhaupt global vom Rang einer Webseite zu sprechen und dabei den Kontext zu unterschlagen. Suchmaschinen antworten auf Suchanfragen, also auf eine kleine Textbotschaft, die in einer bestimmten Sprache und aus einer bestimmten Region der Welt gesendet wird. Der Rang einer Webseite ist somit keine Konstante, sondern eine Funktion mit mehreren Parametern, unter anderem dem regionalen Kontext:</p>
<blockquote>
<p>"Google’s goal is to return the most relevant and useful sites in response to a user query. As a result, the results we show to a user in Ireland may vary from the results returned to a user in France."</p>
</blockquote>
<p><a href="http://support.google.com/webmasters/bin/answer.py?hl=en&answer=62399" target="_blank">– Google</a></p>
<p>Interessant ist nun, wie die regionale Zuordnung eigentlich funktioniert. Wenn man ein Produkt in Russland verkaufen möchte, macht man am besten dazu eine Webseite in russischer Sprache. Wenn man die Seite aber auf einem Server in Deutschland (d.h. mit deutscher IP-Adresse) hostet, wird Google sie zunächst als eine <em>russische Seite aus Deutschland</em> einstufen und den Google-Benutzern in Russland vielleicht doch zuerst die russischen Seiten aus Russland anbieten. Tatsächlich kann man aber Abhilfe schaffen, wenn man eine geeignete ccTLD mit regionalm Bezug wählt:</p>
<blockquote>
<p>"if your site has a geographic TLD/ccTLD (like .co.nz) then we will not use the location of the server as well. Doing that would be a bit confusing, we can’t really „average“ between New Zealand and the USA… At any rate, if you are using a ccTLD like .co.nz you really don’t have to worry about where you’re hosting your website, the ccTLD is generally a much stronger signal than the server’s location could ever be."</p>
</blockquote>
<p><a href="http://productforums.google.com/forum/#%21category-topic/webmasters/chit-chat/ekkowjmHfIo" target="_blank">– John Mueller, Webmaster Trends Analyst</a></p>
<p>Zu beachten ist, dass Google hier zwischen <em>echten ccTLDs</em> und “generischen TLDs mit Länder-Code” (<em>gccTLDs</em>) unterscheidet. Z.B. haben ja die wenigsten .me-Domains etwas mit Montenegro zu tun und es gibt wohl auch viel mehr .tv-Domains als Tuvalu Einwohner hat. Einen Überblick über die Einstufung gibt Google in der <a href="http://support.google.com/webmasters/bin/answer.py?hl=de&answer=1347922" target="_blank">Dokumentation zu den Webmaster-Tools</a>.</p>
<p>Wer also Webseiten betreibt, die im Ausland gefunden werden sollen, sollte erwägen, sich entsprechende ccTLDs zuzulegen. Unser Support kennt sich mit den Registrierungsbedingungen aller ccTLDs weltweit bestens aus und hilft Ihnen gerne dabei, ein entsprechendes Portfolio zusammenzustellen.</p>
tag:boethin.net,2016-09-23:/entries/1019035Off-Topic: Das NULL-Dilemma2012-09-05T14:40:00+02:002016-09-23T14:57:26+02:00<p>Manchmal streiten sich Mathematiker und Programmierer. Zum Beispiel über die Null. Der Programmierer wundert sich, wenn er folgendes SQL-Statement ausführt:</p>
<p><code>SELECT CASE WHEN null = null THEN 'YES' ELSE 'NO' END</code></p>
<p>Denn das Ergebnis ist <code>'NO'</code>. Also ist <code>null</code> nicht gleich <code>null</code>. Und, schlimmer noch,</p>
<p><code>SELECT CASE WHEN <strong>NOT</strong> null = null THEN 'YES' ELSE 'NO' END</code></p>
<p>ergibt ebenfalls <code>'NO'</code>. Das ist ein ziemlicher Skandal: <em>ein boolescher Ausdruck, der mit einem NOT davor das gleiche Ergebnis liefert!</em> Und <code>null</code> ist also weder gleich <code>null</code>, noch ungleich <code>null</code>.</p>
<p>Erst mit einem schmutzigen Trick kann der Programmierer seine Welt wieder in Ordnung bringen:</p>
<p><code>SET ANSI_NULLS OFF</code></p>
<p>Der Mathematiker hingegen kommentiert das gleiche Phänomen mit einem zustimmenden Kopfnicken und erinnert sich sofort an diese Vorlesung im 1. Semester, als der Begriff der <em>Äquivalenzrelation </em>definiert wurde.</p>
<p>Also, wir haben die Menge aller Variablen in einem Programm, die in einem bestimmten Zustand der Maschine mit Werten belegt sind (oder auch nicht, wenn sie NULL sind!). Nun wollen wir diese Variablen in Klassen aufteilen, und zwar so, dass eine Klasse jeweils genau alle Variablen enthält, die mit dem gleichen Wert belegt sind. Dies ergibt den Vergleichsoperator, der dann einfach testet, ob zwei Variablen der gleichen Klasse angehören.</p>
<p>Eine solche Relation hat folgende simple Eigenschaften:</p>
<ul>
<li>
<em>Reflexivität</em>: Für alle Variablen a gilt: a = a.</li>
<li>
<em>Symmetrie</em>: Für alle Variablen a und b gilt: Wenn a = b, dann auch b = a.</li>
<li>
<em>Transitivität</em>: Für alle Variablen a, b und c gilt: Wenn a = b und b = c, dann auch a = c.</li>
</ul>
<p>In untypisierten Programmiersprachen ist die Sache ganz klar. Vollkommen trivial ist zum Beispiel in PHP der folgende Code:</p>
<p><code>if (null == null) {<br>
// sure!<br>
}</code></p>
<p>Untypisiert bedeutet ja nicht, dass die Werte keinen Typ haben, sondern, dass der Typ an den Wert und nicht an die Variable gebunden ist. Den Wert <code>null</code> kann man sich daher einfach als mit einem speziellen Null-Typ verknüpft denken, der keinen anderen Wert als <code>null</code> annehmen kann.</p>
<p>In typisierten Sprachen, wie z.B. SQL, sind die Variablen jedoch von vorne herein in Typ-Klassen aufgeteilt, unterliegen also einer gröberen Äquivalenzrelation, bei der je zwei Variablen in Relation zueinander stehen, wenn sie vom gleichen Typ sind. Die Vergleichsrelation muss eine Verfeinerung der Typrelation bilden, denn sonst kommt man ja in die merkwürdige Situation, dass Werte verschiedenen Typs gleich sein können.</p>
<p><code>DECLARE @x [int];<br>
DECLARE @y [varchar](100);<br>
IF @x = @y<br>
BEGIN <br>
SELECT 'Argh! [int] = [varchar]??'<br>
END</code></p>
<p>In objektorientierten Programmiersprachen wie C++ oder C# kann man ja sogar selbst definieren, wann zwei Objekte gleich sein sollen, etwa indem man den Operator <code>==</code> oder die Funktion <code>Equals</code> für eine Klasse implementiert. Das ändert jedoch nichts am Dilemma mit der Null. In Visual Basic fragt man daher wohlweislich:</p>
<p><code>If x Is Nothing Then<br>
' x is nothing<br>
EndIf</code></p>
<p>Hier wird anstelle des Vergleichsoperators der typübergreifende Operator <code>Is</code> benutzt. In C# schreibt man hingegen:</p>
<p><code>if (x == null)<br>
{<br>
// x is null<br>
}</code></p>
<p>Das ist eine fragwürdige Syntax, denn folgender Code ist nicht kompilierbar:</p>
<p><code>string x = null;<br>
System.Uri y = null;<br>
if (x == y)<br>
{<br>
// Compiler Error:<br>
// Operator '==' cannot be applied to operands<br>
// of type 'string' and 'System.Uri'.<br>
}</code></p>
<p>Somit ist der Operator <code>==</code> in C# nicht wirklich eine Äquivalenzrelation, sondern eher eine Funktion. Man wollte offenbar die Syntax aus der Ursprache C übernehmen und hat das logische Problem gelöst, indem man den Vergleich von Variablen verschiedenen Typs schlichtweg verboten hat. In C war das Konzept allerdings viel einfacher, denn wir lesen in <code>stddef.h</code>:</p>
<p><code>#define NULL (void*)0</code></p>
<p><code>NULL</code> ist also ein untypisierter Zeiger, der auf die Speicheradresse 0 verweist.</p>
<p><code>int *x = NULL;<br>
char *y = NULL;<br>
if (x == y)<br>
{ <br>
/* sure! */<br>
}</code></p>
<p>Da jeder Zeiger implizit in den Typ <code>void*</code> konvertierbar ist und der Vergleich von Zeigern einfach ein numerischer Vergleich der Speicheradressen ist, gibt es hier kein Problem.</p>
<p>Gut, dass wir mal darüber gesprochen haben. Nun können wir weiter programmieren. ;)</p>
tag:boethin.net,2016-10-31:/entries/1028788Domains in Zeiten des Internet2012-05-30T15:08:00+02:002016-10-31T15:12:24+01:00<p>Ist eigentlich mal ein Science-Fiction-Autor auf die Idee gekommen, dass es einen milliardenschweren Markt für Maschinennamen geben könnte?</p>
<p>Wir sprechen heute von einer Domainindustrie, um Macht und Produktivität dieses ökonomischen Systems auszudrücken, dabei ist das industrielle Zeitalter doch längst virtualisiert. Wir haben heute Teile unserer intellektuellen Fähigkeiten in Maschinenwolken überführt, denen automatische Arbeiter innewohnen, die in einem sich irrsinnig steigernden globalen Diskurs Daten produzieren. Merkwürdigerweise ergibt sich aus dieser Tätigkeit ein monitärer Mehrwert, woraus schließlich die Maschinennamen, also die Wort-Nummer- oder Mensch-Maschine-Schnittstellen, selbst wiederum ihren Gegenwert beziehen.</p>
<p>Wie wir Menschen so sind, versuchen wir noch immer etwas hilflos zu verstehen, was wir da nun eigentlich wieder gebaut haben. Die Eisenbahn haben wir ja jetzt einigermaßen verstanden. Die fuhr von A nach B und transportierte Kartoffeln, Ideen, Kulturen und Revolutionen. Man maß noch Kraft in Pferdestärken, und fürchtete, eine Dampflok müsse schon allein aufgrund ihrer atemberaubenden Geschwindigkeit auseinanderbrechen. Viel mehr jedoch – und zu Recht – fürchteten die alten Könige um ihre Macht, denn die Maschinen transportierten insgeheim die Macht des Kapitals in alle Welt, jenes vielköpfige Ungeheuer, das andere, unsagbar schreckliche Monster gebar, bis die ganze mörderische Schwerindustrie beinahe die ganze Welt in Fetzen riss.</p>
<p>Immerhin, die Eisenbahn fuhr geradeaus. Nun sind wir aber offenbar ins Zeitalter der Netzwerke eingetreten und mit der Berechnung der nichtlinearen Dynamik nach wie vor überfordert. Wieder werden Ideen, Kulturen und Revolutionen in schwindelerregendem Tempo um die Welt gejagt, aber früher war doch irgendwie alles einfacher. Es fehlen noch geeignete Begriffe, aber man wird sich wohl vielfach über neue Regeln einigen müssen, denn vielleicht ist das Pferd als Kraftmaß für die Eisenbahn ähnlich ungeeignet wie die überkommene Regulierung des geistigen Eigentums in Zeiten von Youtube.</p>
<p>Der eigentliche Kampf jedoch geht weit über die Tantiemenfrage hinaus. Das Internet ist viel interessanter als die Eisenbahn. Hinter uns liegen zwei, drei Dekaden freies Internet, die gezeigt haben, zu was für einer fantastischen Entfaltung von Kreativität die Menschen fähig sind, wieviel sie wissen, erfinden, diskutieren und gestalten wollen, wenn man einfach so ein paar eilig in der Uni zusammengebastelte Protokolle ins Netz stellt und ein paar Endgeräte dazu verkauft.</p>
<p>Mittlerweile sind jedoch die Zyklopen erwacht, die nun argwöhnisch und verständnislos diese Spielwiese betrachten und immer lauter ihre Hoheitsrechte beanspruchen. Es werden eifrig Pläne geschmiedet und Regulationsmechanismen konstruiert. Es geht um Strafverfolgung, Landesverteidigung und Ähnliches, und da heiligt der Zweck alle Mittel, immer der alte Trick, da werden schon wieder mit jener unverschämten Fadenscheinigkeit Notwendigkeiten postuliert, mit der man schon ganze Kriege gerechtfertigt hat.</p>
<p>Tja, wer nichts zu verbergen hat, muss ja Kontrolle nicht fürchten. Aber falls von den Maximalforderungen, die derzeit bei der ICANN auf dem Tisch liegen, auch nur die Hälfte umgesetzt wird, wird zwar kein einziger Terrorist in seiner Tätigkeit eingeschränkt sein, dafür wird aber jeder Administrator beleumundet, jede Schnittstelle zertifiziert, jede Telefonnummer verifiziert und jedes Byte archiviert sein, was die Domainregistrierung und alle zugehörigen Dienstleistungen so teuer machen wird, dass weder Horst sich seine geliebte Homepage noch wir uns unsere geliebte Firma werden leisten können. Dieser Kampf findet genau jetzt statt.</p>
tag:boethin.net,2016-10-31:/entries/1028791Die Sache mit der Tasse2011-10-18T15:16:00+02:002016-10-31T15:19:31+01:00<p>Neulich habe ich mir eine Tasse gekauft. So eine schöne große, mit buntem Aufdruck. Daraus kann ich ja nun trinken, was ich will. Meine Mutter würde zu Kräutertee raten, meistens trinke ich aber doch Kaffee. Ich könnte aber auch Brause, Wasser oder sogar Schnaps daraus trinken. Oder ich könnte sie zum Erfinden neuer Mixgetränke nutzen. Auf Nachfrage hat mir der Tassenverkäufer versichert, dass ihm das völlig egal sei, Hauptsache die Tasse sei verkauft.</p>
<p>Die meisten Leute trinken Kaffee aus diesen Tassen. Es trinken derartig viele Leute diesen Kaffee, dass die Kaffeefabrik nun schon so groß und mächtig geworden ist, dass sie sogar Anteile an der Tassenfabrik gekauft hat. Und der Tassenfabrikant weiß auch, dass er ohne den großen Erfolg des Kaffeeherstellers nicht so viele Tassen verkaufen könnte, also muss er sich mit ihm gut stellen.</p>
<p>Nun hat mir der Kaffeehersteller geschrieben, dass er sich ja doch Gedanken um meine Gesundheit mache, immerhin könnte mir jemand etwas Ungutes in den Kaffee mischen und ich könne ja auch nie sicher sein, dass guter Kaffee in meiner Tasse wäre, wenn jeder Hergelaufene alles mögliche in die Tasse füllen könne. Er habe sich daher mit dem Tassenhersteller zusammengesetzt und eine ganz neuartige Beschichtung entwickelt, die bewirken könne, dass man nur noch den hochwertigen Kaffee des Kaffeeherstellers aus so einer Tasse trinken könne. Jede andere Flüssigkeit würde im Bruchteil einer Sekunde verdampfen und so könne man sicher sein, immer den guten Kaffee in der Tasse zu haben, wenn nur erst alle Tassen mit der Beschichtung versehen seien.</p>
<p>Ich fand das eigentlich vernünftig, denn schließlich können heutzutage nur noch Experten wirklich gute und schlechte Getränke unterscheiden. Allerdings würde ich dann keine Brause mehr trinken können, was schade wäre, und etwas ungerecht gegenüber den anderen Getränkeherstellern kam mir das auch vor. Auf Nachfrage versicherten mir Kaffee- und Tassenhersteller jedoch, dass man natürlich auch zukünftig noch einige andere Getränke aus den Tassen würde trinken können, eine Liste der zulässigen Getränke sei bereits in Arbeit. Ein Fachmann würde die Beschichtung wahrscheinlich sogar deaktivieren können, jedenfalls könne man sich gegen Aufpreis eine Deaktivierung der Beschichtung einrichten lassen. Na also, ist doch eine tolle Idee, oder?</p>
<p>Ein ähnlicher Fall ist <a href="http://www.heise.de/open/artikel/Die-Woche-Linux-wird-nicht-ausgebootet-1348799.html" target="_blank">dieser</a>.</p>
<p>Also, <em>#OccupyYourHardware</em> und: <a href="http://www.fsf.org/news/secure-boot-vs-restricted-boot-in-windows-8" target="_blank">Stand up for your freedom to install free software!</a> ;)</p>
tag:boethin.net,2016-09-24:/entries/1019222Danke für C!2011-10-14T18:43:00+02:002016-09-24T18:46:37+02:00<p><img alt="Dennis Ritchie" src="http://vignette3.wikia.nocookie.net/wwstechnology/images/b/bb/225px-Dennis_MacAlistair_Ritchie_.jpg/revision/latest?cb=20111014054507" style="float:left"></p>
<blockquote>
<p>When I read commentary about suggestions for where C should go, I often think back and give thanks that it wasn’t developed under the advice of a worldwide crowd.</p>
</blockquote>
<p><a href="http://www.cs.bell-labs.com/who/dmr/" target="_blank">Dennis Ritchie</a> hat sich unsterblich gemacht.</p>
tag:boethin.net,2016-09-24:/entries/1019225Gnu Libidn EsZett Hotfix2010-11-01T18:48:00+01:002016-09-24T18:50:29+02:00<p><strong>Dies ist kein Update der Libidn auf IDNA2008.</strong> Ziel ist es, mit einfachen Mitteln das IDNA2003-Mapping von Codepoints der Kategorie <code>PVALID</code> (RFC 5892), insbesondere also des "ß" (<code>U+00DF; LATIN SMALL LETTER SHARP S</code>), bei Bedarf unterdrücken zu können. Ausgangspunkt ist die Erfordernis, kurzfristig <a href="http://www.autobugfix.com/2010/10/lokalisiert-das-kleine-eszett-im-world-wide-web.html">Domainnamen mit "ß"</a> innerhalb der DE-Zone verabeiten zu können.</p>
<p>Die Änderungen werden hier nur für die C#-Version in <a href="http://ftp.gnu.org/gnu/libidn/" target="_blank">libidn-1.9</a> erwähnt und sind ggf. auf andere Sprachen oder Versionen zu übertragen.</p>
<p>Ziel ist es, die Funktionen <code>ToAscii</code> und <code>ToUnicode</code> um einen Parameter <code>bool useIDNA2008</code> zu erweitern, der die Wirkung hat, dass <code>PVALID</code> Codepoints vom Mapping ausgenommen werden.</p>
<p>(1) Hinzufügen einer Klasse IDNA2008, die alle <code>PVALID</code> Codepoints enthält:</p>
<pre style="background:#eee">
<code>
// IDNA2008.cs
namespace Gnu.Inet.Encoding {
class IDNA2008
{
// rfc5892 PVALID codepoints
public static char[] PVALID = new char[] {
'u00DF', // LATIN SMALL LETTER SHARP S
'u03C2', // GREEK SMALL LETTER FINAL SIGMA
'u06FD', // ARABIC SIGN SINDHI AMPERSAND
'u06FE', // ARABIC SIGN SINDHI POSTPOSITION ME
'u0F0B', // TIBETAN MARK INTERSYLLABIC TSHEG
'u3007' // IDEOGRAPHIC NUMBER ZERO
};
}
}
</code></pre>
<p>(2) Überladen der Funktion <code>Map</code> (Stringprep.cs) mit einem dritten Parameter, der zu ignorierende Codepoints bezeichnet:</p>
<pre style="background:#eee">
<code>
internal static void Map(StringBuilder s, char[] search, string[] replace, <span style="color:red">char[] ignore</span>)
{
for (int i = 0; i < search.Length; i++)
{
char c = search[i];
<span style="color:red">// check if c should be ignored
bool ign = false;
for (int t = 0; t < ignore.Length; t++)
{
if (ignore[t] == c)
{
ign = true;
break;
}
}
if (ign)
continue;</span>
int j = 0;
while (j < s.Length)
{
if (c == s[j])
{
//s.deleteCharAt(j);
s.Remove(j, 1);
if (null != replace[i])
{
s.Insert(j, replace[i]);
j += replace[i].Length - 1;
}
}
else
{
j++;
}
}
}
}
</code></pre>
<p>(3) Überladen der Funktion Nameprep (Strinprep.cs) mit einem dritten Parameter <code>bool useIDNA2008</code>:</p>
<pre style="background:#eee">
<code>
public static string NamePrep(string input, bool allowUnassigned, <span style="color:red">bool useIDNA2008</span>)
{
if (input == null)
{
throw new System.NullReferenceException();
}
StringBuilder s = new StringBuilder(input);
if (!allowUnassigned && Contains(s, RFC3454.A1))
{
throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
}
Filter(s, RFC3454.B1);
// EsZett Hotfix
if (useIDNA2008)
<span style="color:red">Map(s, RFC3454.B2search, RFC3454.B2replace, IDNA2008.PVALID);</span>
else
Map(s, RFC3454.B2search, RFC3454.B2replace);
s = new StringBuilder(NFKC.NormalizeNFKC(s.ToString()));
// B.3 is only needed if NFKC is not used, right?
// map(s, RFC3454.B3search, RFC3454.B3replace);
if (Contains(s, RFC3454.C12) || Contains(s, RFC3454.C22) || Contains(s, RFC3454.C3) || Contains(s, RFC3454.C4) || Contains(s, RFC3454.C5) || Contains(s, RFC3454.C6) || Contains(s, RFC3454.C7) || Contains(s, RFC3454.C8))
{
// Table C.9 only contains code points > 0xFFFF which Java
// doesn't handle
throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
}
// Bidi handling
bool r = Contains(s, RFC3454.D1);
bool l = Contains(s, RFC3454.D2);
// RFC 3454, section 6, requirement 1: already handled above (table C.8)
// RFC 3454, section 6, requirement 2
if (r && l)
{
throw new StringprepException(StringprepException.BIDI_BOTHRAL);
}
// RFC 3454, section 6, requirement 3
if (r)
{
if (!Contains(s[0], RFC3454.D1) || !Contains(s[s.Length - 1], RFC3454.D1))
{
throw new StringprepException(StringprepException.BIDI_LTRAL);
}
}
return s.ToString();
}
</code></pre>
<p>(4) Überladen der Funktionen <code>ToAscii</code> und <code>ToUnicode</code> (IDNA.cs), um den Parameter<br>
<code>bool useIDNA2008</code> an <code>Nameprep</code> durchzureichen:</p>
<pre style="background:#eee">
<code>
public static string ToASCII(string input, bool allowUnassigned, bool useSTD3ASCIIRules, <span style="color:red">bool useIDNA2008</span>)
{
// Step 1: Check if the string contains code points outside
// the ASCII range 0..0x7c.
bool nonASCII = false;
for (int i = 0; i < input.Length; i++)
{
int c = input[i];
if (c > 0x7f)
{
nonASCII = true;
break;
}
}
// Step 2: Perform the nameprep operation.
if (nonASCII)
{
try
{
input = Stringprep.NamePrep(input, allowUnassigned, <span style="color:red">useIDNA2008</span>);
}
catch (StringprepException e)
{
// TODO
throw new IDNAException(e);
}
}
// Step 3: - Verify the absence of non-LDH ASCII code points
// (char) 0..0x2c, 0x2e..0x2f, 0x3a..0x40, 0x5b..0x60,
// (char) 0x7b..0x7f
// - Verify the absence of leading and trailing
// hyphen-minus
if (useSTD3ASCIIRules)
{
for (int i = 0; i < input.Length; i++)
{
int c = input[i];
if ((c <= 0x2c) || (c >= 0x2e && c <= 0x2f) || (c >= 0x3a && c <= 0x40) || (c >= 0x5b && c <= 0x60) || (c >= 0x7b && c <= 0x7f))
{
throw new IDNAException(IDNAException.CONTAINS_NON_LDH);
}
}
if (input.StartsWith("-") || input.EndsWith("-"))
{
throw new IDNAException(IDNAException.CONTAINS_HYPHEN);
}
}
// Step 4: If all code points are inside 0..0x7f, skip to step 8
nonASCII = false;
for (int i = 0; i < input.Length; i++)
{
int c = input[i];
if (c > 0x7f)
{
nonASCII = true;
break;
}
}
string output = input;
if (nonASCII)
{
// Step 5: Verify that the sequence does not begin with the ACE prefix.
if (input.StartsWith(ACE_PREFIX))
{
throw new IDNAException(IDNAException.CONTAINS_ACE_PREFIX);
}
// Step 6: Punycode
try
{
output = Punycode.Encode(input);
}
catch (PunycodeException e)
{
// TODO
throw new IDNAException(e);
}
// Step 7: Prepend the ACE prefix.
output = ACE_PREFIX + output;
}
// Step 8: Check that the length is inside 1..63.
if (output.Length < 1 || output.Length > 63)
{
throw new IDNAException(IDNAException.TOO_LONG);
}
return output;
}
public static string ToUnicode(string input, bool allowUnassigned, bool useSTD3ASCIIRules, <span style="color:red">bool useIDNA2008</span>)
{
string original = input;
bool nonASCII = false;
// Step 1: If all code points are inside 0..0x7f, skip to step 3.
for (int i = 0; i < input.Length; i++)
{
int c = input[i];
if (c > 0x7f)
{
nonASCII = true;
break;
}
}
// Step 2: Perform the Nameprep operation.
if (nonASCII)
{
try
{
input = Stringprep.NamePrep(input, allowUnassigned, <span style="color:red">useIDNA2008</span>);
}
catch (StringprepException e)
{
// ToUnicode never fails!
return original;
}
}
// Step 3: Verify the sequence starts with the ACE prefix.
if (!input.StartsWith(ACE_PREFIX))
{
// ToUnicode never fails!
return original;
}
string stored = input;
// Step 4: Remove the ACE prefix.
input = input.Substring(ACE_PREFIX.Length);
// Step 5: Decode using punycode
string output;
try
{
output = Punycode.Decode(input);
}
catch (PunycodeException e)
{
// ToUnicode never fails!
return original;
}
// Step 6: Apply toASCII
string ascii;
try
{
ascii = ToASCII(output, allowUnassigned, useSTD3ASCIIRules, <span style="color:red">useIDNA2008</span>);
}
catch (IDNAException e)
{
// ToUnicode never fails!
return original;
}
// Step 7: Compare case-insensitively.
if (!ascii.ToUpper().Equals(stored.ToUpper()))
{
// ToUnicode never fails!
return original;
}
// Step 8: Return the result.
return output;
}
</code></pre>
<p>(5) Testen von <code>ToAscii</code> und <code>ToUnicode</code> mit und ohne Anwendung von IDNA2008:</p>
<pre style="background:#eee">
<code>
[TestMethod()]
public void Test030_EsZett_IDNA2003()
{
string u1 = "täßt";
// Nameprep IDNA2203 should send "täßt" to "tässt"
string u2 = Stringprep.NamePrep(u1, false, false);
Assert.AreEqual("tässt", u2);
// ToAscii IDNA2003 should send both "täßt" and "tässt" to "xn--tsst-loa"
string a1 = IDNA.ToASCII(u1, false, true, false);
Assert.AreEqual("xn--tsst-loa", a1);
string a2 = IDNA.ToASCII(u2, false, true, false);
Assert.AreEqual(a1, a2);
// ToUnicode IDNA2003 should send "xn--tsst-loa" to "tässt"
string u3 = IDNA.ToUnicode(a1, false, true, false);
Assert.AreEqual(u2, u3);
}
[TestMethod()]
public void Test040_EsZett_IDNA2008()
{
string u1 = "täßt";
// Nameprep IDNA2208 should send "täßt" to "täßt"
string u2 = Stringprep.NamePrep(u1, false, true);
Assert.AreEqual(u1, u2);
// ToAscii IDNA2008 should send "täßt" to "xn--tt-giat"
string a1 = IDNA.ToASCII(u1, false, true, true);
Assert.AreEqual("xn--tt-giat", a1);
string a2 = IDNA.ToASCII(u2, false, true, true);
Assert.AreEqual(a1, a2);
// ToUnicode IDNA2003 should send "xn--tt-giat.de" to "täßt"
string u3 = IDNA.ToUnicode(a1, false, true, true);
Assert.AreEqual(u2, u3);
}
</code></pre>
tag:boethin.net,2016-09-24:/entries/1019228Lokalisiert: Das kleine EsZett im World Wide Web2010-10-31T18:57:00+01:002016-09-28T16:55:01+02:00<p>Wie immer, wenn etwas immer größer und komplizierter wird, zeichnet sich ein Trend zur Lokalisierung ab. Das Internet ist ein topologischer Raum, der so hochdimensional geworden ist, dass man ihn nur noch als Überdeckung eines unfassbaren Etwas durch lokale Landkarten erklären kann. Das Kraftwerk der Globalisierung sehnt sich heute nach Semantik, sucht soziale Kontakte und organische Strukturen.Es möchte den Menschen nahe sein, ihre Gegend kennen und ihren Dialekt sprechen.</p>
<p>Die alten globalen und normativen Lösungen werden verfeinert und den Bedürfnissen angepasst. Zum Beispiel der IDNA-Standard. Die Intention von IDNA ist es, Abbildungen zu definieren, die eine Kommunikation zwischen den unterschiedlichen und eigenartigen Informationsspektren beliebiger lokaler Entitäten auf der Basis uralter und schwer veränderbarer Protokolle wie dem DNS-System ermöglichen.</p>
<p>Im August 2010 veröffentlichte die IETF die IDNA2008-Spezifizierungen in RFC <a href="http://tools.ietf.org/html/rfc5890" target="_blank">5890</a> – <a href="http://tools.ietf.org/html/rfc5894" target="_blank">5894</a>. Eine Übersicht über die Unterschiede zwischen IDNA2003 und IDNA2008 bietet der <a href="http://unicode.org/reports/tr46/" target="_blank">Unicode Technical Standard #46 (Unicode IDNA Compatibility Processing)</a>. Bis dahin war IDNA eine globale Funktion, im Wesentlichen bestehend aus dem Nameprep-Mapping und dem Punycode-Algorithmus, wobei Nameprep effektiv durch eine Reihe von Tabellen definiert ist, die z.B. Groß- auf Kleinbuchstaben und "ß" auf „ss“ abbilden. Der alte Standard hatte damit die Verantwortlichkeit für die konsistente Behandlung unterschiedlicher Sprachen innhalb des IDNA-Protokolls angesiedelt. Diese Lösung war global, einfach und unflexibel.</p>
<p>Mit IDNA2008 wurde eine neue Terminolgie geschaffen, die den Anforderungen an die lokale Unterschiedlichkeit von Applikationen und Benutzern gerecht werden und die Offenheit gegenüber zukünftigen Unicode-Versionen garantieren soll. Das Eszett, namentlich der Codepoint <code>U+00DF (LATIN SMALL LETTER SHARP S)</code>, wurde auf Betreiben von DENIC als Ausnahme in die Kategorie <code>PVALID</code> (Protocol Valid) aufgenommen. Während also die deutsche Ligatur <em>ß</em> in der DE-Zone nun mit <code>xn--zca</code> aufgelöst wird, kann es gleichzeitig in einer anderen Zone mit <code>ss</code> aufgelöst werden. Ein Beitrag zur Lokalisierung und zur Konjunktur bei ISPs und Anwälten.</p>
<p>Dieser Codepoint bildet nun eine <a href="http://unicode.org/reports/tr46/#Deviations" target="_blank">Deviation</a>, d.h. unterschiedliche Applikationen können ihn abweichend verarbeiten. Man stelle sich vor, Alice greift von zu Hause aus auf ihr Konto unter <em>http://www.sparkasse-gießen.de</em> zu. Ihr Browser unterstützt IDNA2003, bildet also auf <em>http://www.sparkasse-giessen.de</em> ab und löst auf die IP-Adresse des Sparkassenservers auf. Nun besucht sie ihren Freund Bob und prüft dort ihren Kontostand. Bobs Browser unterstützt IDNA2008, benutzt also bei gleicher Eingabe stattdessen <em>http://www.xn--sparkasse-gieen-2ib.de</em>, was auf eine ganz andere IP-Adresse aufgelöst werden kann. Unter dieser könnte der Phishing-Server von Eve antworten, die so die Zugangsdaten von Alice ausspionieren kann.</p>
<p>Und wenn schließlich der Browser am Encoding der Diskussionsbeiträge verzweifelt, so wirkt das irgendwie selbstreferenziell:</p>
<p><img alt="discussions on ietf.idnabis" src="https://boethin.net/files/resized/71955/555;260;31383dcf107bcce4c7e0abc50bcc0a332ed042fd.jpg"></p>
<p>Ãh??? <img alt=":-)" class="wp-smiley" src="http://www.http.net/wp-includes/images/smilies/simple-smile.png" style="height:1em; max-height:1em"></p>
<p>Für den ISP-Developer ist Lokalisierung allerdings stets eine Herausforderung. Er ist dafür zuständig, dass die lokalen Einheiten untereinander und miteinander kommunizieren können. Und wer will sich heute schon noch in ASCII unterhalten.</p>
<p>Das kleine "ß" landet also eines Tages auf dem Schreibtisch und erklärt sich für gültig. Natürlich weiß die an Hunderten von Stellen tief in alle Systeme eingegrabene IDNA-Software noch lange nichts von den neuen RFCs. Der .NET <code>System.Globalization</code>-Namespace, der bis dahin zuverlässige Dienste beim Normalisieren, Validieren und Konvertieren auch der absonderlichsten Zeichen leistete, ist unter keinen Umständen dazu zu bewegen, ein "ß" in Domain-Namen zu akzeptieren. Und was so eine DLL nicht kann, das kann sie eben nicht – tja, auf hoher See, vor Gericht und vor Microsoft …</p>
<p>Am 26. Oktober 2010 kündigte DENIC an, praktisch ab sofort IDNA2008 zu unterstützen und zunächst in einer Sunrise-Phase allen Inhabern von Domains, die ein „ss“ enthalten, die Gelegenheit zu geben, das Pendant mit "ß" zu registrieren. Es musste innherhalb von wenigen Stunden eine Ad-hoc-Lösung her. Und das einzig Naheliegende war, eine automatische Abfrage des <a href="http://www.denic.de/domains/internationalized-domain-names/idn-konvertierung.html" target="_blank">DENIC-Web-Tools</a> zu programmieren, das in dem Moment den einzigen bekannten ß-fähigen Konvertierungsmechanismus bot. <a href="http://blog.http.net/author/emonninger/">Unsere Auszubildende</a> setzte das prompt um, während eilig RFCs studiert und nach einer tragfähigen Lösung gesucht wurde. Diese bietet die GNU IDN Library <a href="http://www.gnu.org/software/libidn/" target="_blank">Libidn</a>. Die kann zwar auch noch kein "ß", ist aber fix gepatcht. Der Code liegt in C, Java und C# vor, da ist also für jeden etwas dabei.</p>
<p><img alt="LibIDN source code" src="https://boethin.net/files/resized/71956/555;467;50a7ee2fd432de8c8ee2a2fe359a97e04eb050eb.jpg"></p>
<p>Also wird man auch in Zukunft allen globalen Anforderungen und lokalen Bedürfnissen des Internets technisch gewachsen sein<br>
Hier ist ein <a class="navigation_page_link" href="https://boethin.net/entries/general/gnu-libidn-eszett-hotfix" target="_blank">IDNA Hotfix für GNU Libidn</a>.</p>
tag:boethin.net,2016-09-28:/entries/1020341xn--bullshit2010-07-05T16:57:00+02:002016-09-28T17:01:23+02:00<p>Ich dachte zuerst, unsere Testfunktion für <a href="http://de.wikipedia.org/wiki/Internationalizing_Domain_Names_in_Applications" target="_blank">ACE-Strings</a> ist buggy.</p>
<p>Aber nein! Die spontan für ungültig gehaltene Eingabe <em>xn--bullshit</em> ist tatsächlich gültig. Die IDN-Form lautet: 㟰㟦㟳㟮㟬㟠 (U+37F0 U+37E6 U+37F3 U+37EE U+37EC U+37E0). Das ist bullshit in <a href="http://titus.uni-frankfurt.de/unicode/utf8site/37055.htm" target="_blank">Han-chinesisch</a> ;).</p>
<p><del datetime="2010-07-05T11:39:22+00:00">Scheint auch noch weitgehend verfügbar zu sein</del>, falls noch jemand einen lustigen Domainnamen sucht … :D</p>
<p>Update:<br>
<a href="http://xn--bullshit.com/" target="_blank">xn--bullshit.com</a> und <a href="http://xn--bullshit.net/" target="_blank">xn--bullshit.net</a> wurden jetzt erstmal registriert. Falls jemand eine überzeugende Idee hat, was man damit anfangen kann, lässt sich über eine Übernahme reden!</p>
tag:boethin.net,2016-09-28:/entries/1020348Internet Explorer und der „div“-Tag2010-05-21T17:12:00+02:002016-09-28T17:12:44+02:00<p>Vor kurzem stand ich vor folgendem Szenario bei der Entwicklung mit ASP.NET MVC und JQuery:</p>
<p>Ich hatte eine Website erstellt, die dynamisch Inhalte vom Server nachgeladen hat. Gut, werden sich jetzt einige denken, das ist doch inzwischen Standard und sollte keine Probleme bereiten. Für gewöhnlich ist dies auch so, jedoch gab es hier eine Ausnahme: den Internet Explorer.</p>
<p>Man denke sich folgenden vereinfachten Quellcodeabschnitt:</p>
<pre>
<input type="text" onchange="$.get("/myController/Search",
{query : $(this).val()},
function (resultValue)
{ $('#result_placeholder').html(resultValue); } />
<div id="result_placeholder" />
<input type="submit" value="Suche starten" /></pre>
<p>Mit eigenen Worten beschrieben: Gibt man etwas in das Textfeld ein, wird eine Anfrage mit dem Inhalt der Textbox an den Server gesendet und Resultate in das div mit der Id result_placeholder hineingeschrieben.</p>
<p>Das klappte auch wunderbar. Jedoch war daraufhin mein „Suche starten“-Knopf verschwunden und ich konnte mir nicht erklären warum. Durch eine Analyse des zur Laufzeit vorliegenden Quelltextes konnte ich dann auch sehen, dass mein Submit-Button tatsächlich nicht mehr vorhanden war. Es stellte sich mir also die Frage, wieso er im Internet Explorer verschwand und in allen anderen Browsern weiterhin vorhanden war.</p>
<p>Die Lösung des Ganzen ist relativ einfach. Anstatt <strong><div id=“result_placeholder“ /> </strong>schreibt man <strong><div id=“result_placeholder“></div></strong> in den Quelltext, dann wird der Inhalt richtig gesetzt und auch der „Suche starten“-Button ist weiterhin vorhanden. <img alt=":-)" class="wp-smiley" src="http://www.http.net/wp-includes/images/smilies/simple-smile.png" style="height:1em; max-height:1em"></p>
<p>Edit:</p>
<blockquote>
<p><!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Strict//EN“ „<a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd</a>„><br>
<html xmlns=“<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>„></p>
</blockquote>
<p>Diesen DOCTYPE verwende ich bei dem oben genannten Problem. Laut W3C ist damit das Dokument als XHTML ausgezeichnet und mein „<div />“ standardmäßig erlaubt. In dem Zusammenhang ist auch meine Beobachtung zu betrachten.</p>