~/home of geeks

Automatic URL Decoding

· 447 Wörter · 3 Minute(n) Lesedauer

Da hatte ich letztens im Code eine Stelle, die einen String erhält, der evtl. einfach oder auch mehrfach URL kodiert sein könnte. Da ich keinen direkten Einfluss auf die generierende Stelle hatte, musste ich in die Tiefen der URL-Encoding Erkennung steigen.

Die Eingaben konnten Werte folgender Form sein: http://www.gigaco.de - Unkodiert http%3A%2F%2Fwww.gigaco.de - Einfach kodiert http%253A%252F%252Fwww.gigaco.de - Doppelt kodiert http%25253A%25252F%25252Fwww.gigaco.de - Dreifach kodiert …

Wie man erkennen kann, werden bei mehrfacher Kodierung lediglich die % Zeichen in %25 kodiert.

Jetzt sollte man meinen, dass es ganz einfach ist, zu erkennen, ob ein String URL kodiert ist oder nicht, um in einer Schleife so lange zu dekodieren, bis der String nicht mehr kodiert ist.

Es gibt Texte, bei denen man dies nicht bestimmen kann. Ein solcher Fall ist $t = "A+B";. Das Leerzeichen wird üblicherweise in ein + kodiert. Ob nun “A B” oder “A+B” (als arithmetische Operation) gemeint ist, kann nicht mehr gesagt werden.

In meinem Fall waren + Symbole ein Zeichen für unkodierte Strings, da die Strings keine freien Texte waren, sondern welche ohne Leerzeichen.

Eine Lösungsidee schlug vor, den Text zu dekodieren und mit dem Original zu vergleichen. Sollte sich dabei nichts geändert haben, so ist das Original unkodiert. Dieser Algorithmus würde aber das “A+B” in ein “A B” verwandeln, was bei mir unerwünscht war. Ein andere User schlug vor, dass man statt einem String-Vergleich zwischen kodiertem und original String einfach die Längen vergleicht. Wird ein String (ausgenommen + Symbole) dekodiert, so wird er kürzer, da aus einem %25 ein % wird.

Eine elegante und finale Lösung habe ich nicht gefunden, aber den zuletzt genannten Algorithmus akzeptiert. Ich habe lediglich noch eine Obergrenze von 10 Iterationen hinzugefügt, damit ein Quine nicht das System kompromittiert.

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Dekodiert einen String so Lange per URL-Decode, bis dieser nicht
 * mehr Kodiert ist.<br/>
 * Dies wird Anhand der Länge der dekodierten und des kodierten Strings bestimmt.
 * Diese Ändert sich nach erreichen des Raw-Zustandes nicht mehr.<br/>
 * Siehe auch <a href="http://stackoverflow.com/questions/2295223/how-to-find-out-if-string-has-already-been-url-encoded">How to find out if string has already been URL encoded?</a>
 */
public class AutomaticUrlDecoder {
    private static final Logger LOG = LoggerFactory.getLogger(AutomaticUrlDecoder.class);

    public static final String decode(final String s) {
        String result = s;
        if (StringUtils.isNotBlank(s)) {
            boolean done = false;
            int i = 0;
            while (!done) {
                String decoded;
                try {
                    decoded = URLDecoder.decode(result, "UTF-8");
                    if (decoded.length() == result.length()) {
                        done = true;
                    } else {
                        result = decoded;
                    }
                    i++;
                    if (i > 10) {
                        // Notfall Abbruch... 
                        done = true;
                    }
                } catch (final UnsupportedEncodingException e) {
                    LOG.error("Beim Dekodieren der Eingabe \"" + s + "\"", e);
                }
            }
        }
        return result;
    }
}