Python: Ignorieren Sie den Fehler 'Falsches Auffüllen' beim Base64-Decodieren

111

Ich habe einige Base64-codierte Daten, die ich wieder in Binärdaten konvertieren möchte, auch wenn ein Auffüllfehler vorliegt. Wenn ich benutze

base64.decodestring(b64_string)

Es wird ein Fehler "Falsches Auffüllen" ausgelöst. Gibt es eine andere Art und Weise?

UPDATE: Danke für all das Feedback. Um ehrlich zu sein, klangen alle genannten Methoden ein bisschen schlecht und so entschied ich mich, openssl auszuprobieren. Der folgende Befehl war ein Vergnügen:

openssl enc -d -base64 -in b64string -out binary_data
FunLovinCoder
quelle
5
Haben Sie tatsächlich versucht, zu verwenden base64.b64decode(strg, '-_')? Dies ist a priori, ohne dass Sie sich die Mühe machen, Beispieldaten bereitzustellen, die wahrscheinlichste Python-Lösung für Ihr Problem. Die vorgeschlagenen "Methoden" waren DEBUG-Vorschläge, die angesichts des Mangels an gelieferten Informationen unbedingt "Hit and Miss" waren.
John Machin
2
@ John Machin: Ja, ich habe deine Methode ausprobiert, aber es hat nicht funktioniert. Die Daten sind vertraulich.
FunLovinCoder
3
Versuchen Siebase64.urlsafe_b64decode(s)
Daniel F
Könnten Sie die Ausgabe davon liefern: sorted(list(set(b64_string)))bitte? Ohne irgendetwas unternehmensvertrauliches preiszugeben, sollte dies Aufschluss darüber geben, welche Zeichen zum Codieren der Originaldaten verwendet wurden, was wiederum genügend Informationen liefern kann, um eine Non-Hit-or-Miss-Lösung bereitzustellen.
Brian Carcich
Ja, ich weiß, dass es bereits gelöst ist, aber um ehrlich zu sein, klingt die openssl-Lösung für mich auch ein Hit oder Miss.
Brian Carcich

Antworten:

79

Wie in anderen Antworten erwähnt, gibt es verschiedene Möglichkeiten, wie base64-Daten beschädigt werden können.

Wie Wikipedia jedoch sagt, ist das Entfernen der Auffüllung (die '=' Zeichen am Ende der Base64-codierten Daten) "verlustfrei":

Aus theoretischer Sicht wird das Auffüllzeichen nicht benötigt, da die Anzahl der fehlenden Bytes aus der Anzahl der Base64-Ziffern berechnet werden kann.

Wenn dies also wirklich das einzige ist, was mit Ihren base64-Daten "falsch" ist, kann das Auffüllen einfach wieder hinzugefügt werden. Ich habe mir das ausgedacht, um "Daten" -URLs in WeasyPrint analysieren zu können, von denen einige base64 ohne Auffüllen waren:

import base64
import re

def decode_base64(data, altchars=b'+/'):
    """Decode base64, padding being optional.

    :param data: Base64 data as an ASCII byte string
    :returns: The decoded byte string.

    """
    data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data)  # normalize
    missing_padding = len(data) % 4
    if missing_padding:
        data += b'='* (4 - missing_padding)
    return base64.b64decode(data, altchars)

Tests für diese Funktion: weasyprint / tests / test_css.py # L68

Simon Sapin
quelle
2
Hinweis: ASCII nicht Unicode, um sicher zu gehen, möchten Sie vielleichtstr(data)
MarkHu
4
Dies ist gut mit einer Einschränkung. base64.decodestring ist veraltet, verwenden Sie base64.b64_decode
ariddell
2
Zur Verdeutlichung von @ariddell wurde der Kommentar in Py3 base64.decodestringveraltet, aber aus base64.decodebytesGründen der Versionskompatibilität besser zu verwenden base64.b64decode.
Cas
Da das base64Modul ungültige Nicht-Base64-Zeichen in der Eingabe ignoriert, müssen Sie zuerst die Daten normalisieren . Entfernen Sie alles, was kein Buchstabe, keine Ziffer /oder ist +, und fügen Sie dann die Polsterung hinzu.
Martijn Pieters
39

Fügen Sie einfach nach Bedarf Polster hinzu. Beachten Sie jedoch Michaels Warnung.

b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh
badp
quelle
1
Es gibt sicherlich etwas Einfacheres, das 0 zu 0, 2 zu 1 und 1 zu 2
abbildet
2
Warum erweitern Sie auf ein Vielfaches von 3 statt 4?
Michael Mrozek
Das scheint der Wikipedia-Artikel auf base64 zu implizieren.
Badp
1
@bp: Bei der Base64-Codierung wird jeder 24-Bit-Binäreingang (3 Byte) als 4-Byte-Ausgang codiert. output_len% 3 macht keinen Sinn.
John Machin
8
Das Anhängen ===funktioniert immer. Alle zusätzlichen =Zeichen werden von Python scheinbar sicher verworfen.
Acumenus
30

Es scheint, dass Sie Ihren Bytes vor dem Dekodieren nur eine Auffüllung hinzufügen müssen. Es gibt viele andere Antworten auf diese Frage, aber ich möchte darauf hinweisen (zumindest in Python 3.x).base64.b64decode jede zusätzliche Auffüllung abgeschnitten wird, vorausgesetzt, es gibt überhaupt genug.

Also so etwas wie: b'abc='funktioniert genauso gut wie b'abc=='(wie auch b'abc=====').

Dies bedeutet, dass Sie einfach die maximale Anzahl von Füllzeichen hinzufügen können, die Sie jemals benötigen würden b'==='- nämlich drei ( ) - und base64 alle unnötigen Zeichen abschneidet.

So können Sie schreiben:

base64.b64decode(s + b'===')

Das ist einfacher als:

base64.b64decode(s + b'=' * (-len(s) % 4))
Henry Woody
quelle
1
Okay, das ist nicht zu "hässlich", danke :) Übrigens, ich denke, du brauchst nie mehr als 2 Polsterzeichen. Der Base64-Algorithmus funktioniert für Gruppen mit jeweils 3 Zeichen und muss nur aufgefüllt werden, wenn Ihre letzte Gruppe von Zeichen nur 1 oder 2 Zeichen lang ist.
Otto
@Otto das Auffüllen hier ist für die Dekodierung, die auf Gruppen von 4 Zeichen funktioniert. Base64- Codierung funktioniert auf Gruppen von 3 Zeichen :)
Henry Woody
Wenn Sie jedoch wissen, dass während der Codierung maximal 2 hinzugefügt werden, was später "verloren" gehen kann und Sie gezwungen sind, diese vor dem Decodieren erneut hinzuzufügen, müssen Sie auch während des Decodierens maximal 2 hinzufügen. #ChristmasTimeArgumentForTheFunOfIt
Otto
@Otto Ich glaube du hast recht. Während eine Base64-codierte Zeichenfolge mit einer Länge von beispielsweise 5 3 Auffüllzeichen erfordern würde, ist eine Zeichenfolge mit der Länge 5 nicht einmal eine gültige Länge für eine Base64-codierte Zeichenfolge. Sie würden den Fehler bekommen : binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4. Vielen Dank für den Hinweis!
Henry Woody
24

"Falsche Polsterung" kann nicht nur "fehlende Polsterung" bedeuten, sondern auch (ob Sie es glauben oder nicht) "falsche Polsterung".

Wenn die vorgeschlagenen Methoden zum Hinzufügen von Auffüllungen nicht funktionieren, entfernen Sie einige nachfolgende Bytes:

lens = len(strg)
lenx = lens - (lens % 4 if lens % 4 else 4)
try:
    result = base64.decodestring(strg[:lenx])
except etc

Update: Jegliches Herumspielen am Auffüllen oder Entfernen möglicherweise fehlerhafter Bytes am Ende sollte NACH dem Entfernen von Leerzeichen erfolgen, da sonst die Längenberechnungen gestört werden.

Es wäre eine gute Idee, wenn Sie uns eine (kurze) Stichprobe der Daten zeigen würden, die Sie wiederherstellen müssen. Bearbeiten Sie Ihre Frage und kopieren Sie das Ergebnis von print repr(sample) .

Update 2: Möglicherweise wurde die Codierung auf URL-sichere Weise durchgeführt. In diesem Fall können Sie Minus- und Unterstrichzeichen in Ihren Daten sehen und diese mit dekodierenbase64.b64decode(strg, '-_')

Wenn Sie in Ihren Daten keine Minus- und Unterstrichzeichen sehen können, aber Plus- und Schrägstriche sehen können, haben Sie ein anderes Problem und benötigen möglicherweise die Tricks zum Hinzufügen oder Auffüllen von Cruft.

Wenn Sie in Ihren Daten kein Minus, keinen Unterstrich, kein Plus und keinen Schrägstrich sehen können, müssen Sie die beiden alternativen Zeichen bestimmen. Sie sind diejenigen, die nicht in [A-Za-z0-9] enthalten sind. Dann müssen Sie experimentieren, um zu sehen, in welcher Reihenfolge sie im 2. Argument von verwendet werden müssenbase64.b64decode()

Update 3 : Wenn Ihre Daten "vertraulich" sind:
(a) Sie sollten dies im Voraus sagen.
(B) Wir können andere Wege zum Verständnis des Problems untersuchen, die höchstwahrscheinlich damit zusammenhängen, welche Zeichen anstelle von +und /in verwendet werden das Kodierungsalphabet oder durch andere Formatierungen oder fremde Zeichen.

Eine solche Möglichkeit wäre, zu untersuchen, welche nicht "Standard" -Zeichen in Ihren Daten enthalten sind, z

from collections import defaultdict
d = defaultdict(int)
import string
s = set(string.ascii_letters + string.digits)
for c in your_data:
   if c not in s:
      d[c] += 1
print d
John Machin
quelle
Die Daten bestehen aus dem Standard-Base64-Zeichensatz. Ich bin mir ziemlich sicher, dass das Problem darin besteht, dass 1 oder mehr Zeichen fehlen - daher der Auffüllfehler. Sofern es in Python keine robuste Lösung gibt, werde ich meine Lösung zum Aufrufen von openssl verwenden.
FunLovinCoder
1
Eine "Lösung", die Fehler stillschweigend ignoriert, verdient den Begriff "robust" kaum. Wie ich bereits erwähnt habe, waren die verschiedenen Python-Vorschläge Methoden zum DEBUGGING, um herauszufinden, wo das Problem liegt, als Vorbereitung auf eine PRINCIPLED-Lösung. Sind Sie nicht an so etwas interessiert?
John Machin
7
Meine Anforderung ist NICHT, das Problem zu lösen, warum die base64 beschädigt ist - sie stammt aus einer Quelle, über die ich keine Kontrolle habe. Meine Anforderung besteht darin, Informationen über die empfangenen Daten bereitzustellen, auch wenn diese beschädigt sind. Eine Möglichkeit, dies zu tun, besteht darin, die Binärdaten aus der beschädigten base64 zu holen, damit ich Informationen aus dem zugrunde liegenden ASN.1 abrufen kann. Strom. Ich habe die ursprüngliche Frage gestellt, weil ich eine Antwort auf diese Frage wollte, nicht die Antwort auf eine andere Frage - zum Beispiel, wie man korrupte base64 debuggt.
FunLovinCoder
Normalisieren Sie einfach die Zeichenfolge und entfernen Sie alles, was kein Base64-Zeichen ist. Überall, nicht nur am Anfang oder am Ende.
Martijn Pieters
24

Verwenden

string += '=' * (-len(string) % 4)  # restore stripped '='s

Kredit geht zu einem Kommentar irgendwo hier.

>>> import base64

>>> enc = base64.b64encode('1')

>>> enc
>>> 'MQ=='

>>> base64.b64decode(enc)
>>> '1'

>>> enc = enc.rstrip('=')

>>> enc
>>> 'MQ'

>>> base64.b64decode(enc)
...
TypeError: Incorrect padding

>>> base64.b64decode(enc + '=' * (-len(enc) % 4))
>>> '1'

>>> 
warvariuc
quelle
4
Er meint diesen Kommentar: stackoverflow.com/questions/2941995/…
jackyalcine
22

Wenn ein Auffüllfehler auftritt, bedeutet dies wahrscheinlich, dass Ihre Zeichenfolge beschädigt ist. Base64-codierte Zeichenfolgen sollten ein Vielfaches von vier Längen haben. Sie können versuchen, das Auffüllzeichen ( =) selbst hinzuzufügen , um die Zeichenfolge zu einem Vielfachen von vier zu machen. Dies sollte jedoch bereits vorhanden sein, es sei denn, etwas stimmt nicht

Michael Mrozek
quelle
Die zugrunde liegenden Binärdaten sind ASN.1. Selbst bei Korruption möchte ich zur Binärdatei zurückkehren, da ich immer noch einige nützliche Informationen aus dem ASN.1-Stream erhalten kann.
FunLovinCoder
nicht wahr, wenn Sie ein JWT für Sicherheitsüberprüfungen dekodieren möchten, benötigen Sie es
DAG
4

Überprüfen Sie die Dokumentation der Datenquelle, die Sie dekodieren möchten. Ist es möglich, dass Sie base64.urlsafe_b64decode(s)statt verwenden wollten base64.b64decode(s)? Dies ist einer der Gründe, warum Sie diese Fehlermeldung möglicherweise gesehen haben.

Dekodieren Sie Zeichenfolgen mit einem URL-sicheren Alphabet, das - anstelle von + und _ anstelle von / im Standard-Base64-Alphabet ersetzt.

Dies ist beispielsweise bei verschiedenen Google-APIs wie dem Google Identity Toolkit und den Google Mail-Nutzdaten der Fall.

Daniel F.
quelle
1
Dies beantwortet die Frage überhaupt nicht. Plus, urlsafe_b64decodeerfordert auch Polsterung.
Rdb
Vor der Beantwortung dieser Frage gab es ein Problem, das mit dem Identity Toolkit von Google zusammenhängt. Ich habe den falschen Padding-Fehler erhalten (ich glaube, er war auf dem Server), obwohl das Padding korrekt zu sein schien. Es stellte sich heraus, dass ich verwenden musste base64.urlsafe_b64decode.
Daniel F
Ich bin damit einverstanden, dass es die Frage nicht beantwortet, rdb, aber es war genau das, was ich auch hören musste. Ich habe die Antwort auf einen etwas schöneren Ton umformuliert. Ich hoffe, das funktioniert für dich, Daniel.
Henrik Heimbuerger
Perfekt in Ordnung. Ich bemerkte nicht, dass es etwas unfreundlich klang, ich dachte nur, dass es die schnellste Lösung wäre, wenn es das Problem beheben würde, und aus diesem Grund sollte es das erste sein, was versucht werden sollte. Vielen Dank für Ihre Änderung, es ist willkommen.
Daniel F
Diese Antwort löste mein Problem beim Dekodieren eines von einem JWT abgeleiteten Google Access Token. Alle anderen Versuche führten zu "Falschem Auffüllen".
John Hanley
2

Das Hinzufügen der Polsterung ist ziemlich ... umständlich. Hier ist die Funktion, die ich mithilfe der Kommentare in diesem Thread geschrieben habe, sowie die Wiki-Seite für base64 (überraschend hilfreich) https://en.wikipedia.org/wiki/Base64#Padding .

import logging
import base64
def base64_decode(s):
    """Add missing padding to string and return the decoded base64 string."""
    log = logging.getLogger()
    s = str(s).strip()
    try:
        return base64.b64decode(s)
    except TypeError:
        padding = len(s) % 4
        if padding == 1:
            log.error("Invalid base64 string: {}".format(s))
            return ''
        elif padding == 2:
            s += b'=='
        elif padding == 3:
            s += b'='
        return base64.b64decode(s)
Bryan Lott
quelle
2

Sie können es einfach verwenden, base64.urlsafe_b64decode(data)wenn Sie versuchen, ein Webbild zu dekodieren. Es kümmert sich automatisch um die Polsterung.

Weinrebe
quelle
es hilft wirklich!
Mond
1

Es gibt zwei Möglichkeiten, die hier beschriebenen Eingabedaten zu korrigieren oder, genauer gesagt und im Einklang mit dem OP, die b64decode-Methode des Python-Moduls base64 in die Lage zu versetzen, die Eingabedaten zu etwas zu verarbeiten, ohne eine nicht abgefangene Ausnahme auszulösen :

  1. Fügen Sie == an das Ende der Eingabedaten an und rufen Sie base64.b64decode (...) auf.
  2. Wenn dies eine Ausnahme auslöst, dann

    ich. Fangen Sie es über versuchen / außer,

    ii. (R?) Entfernen Sie beliebige = Zeichen aus den Eingabedaten (Hinweis: Dies ist möglicherweise nicht erforderlich).

    iii. Fügen Sie A == an die Eingabedaten an (A == bis P == funktionieren),

    iv. Rufen Sie base64.b64decode (...) mit den A == - angehängten Eingabedaten auf

Das Ergebnis aus Punkt 1. oder Punkt 2. oben ergibt das gewünschte Ergebnis.

Vorsichtsmaßnahmen

Dies garantiert nicht, dass das decodierte Ergebnis das ist, was ursprünglich codiert wurde, aber es gibt (manchmal?) Dem OP genug, um damit zu arbeiten:

Selbst bei Korruption möchte ich zur Binärdatei zurückkehren, da ich immer noch einige nützliche Informationen aus dem ASN.1-Stream erhalten kann ").

Siehe Was wir wissen und Annahmen unten.

TL; DR

Aus einigen Schnelltests von base64.b64decode (...)

  1. es scheint, dass es Nicht- [A-Za-z0-9 + /] Zeichen ignoriert; das schließt das Ignorieren von = s ein, es sei denn sie sind die letzten Zeichen in einer analysierten Vierergruppe. In diesem Fall beenden die = s die Decodierung (a = b = c = d = ergibt das gleiche Ergebnis wie abc = und a = = b == c == ergibt das gleiche Ergebnis wie ab ==).

  2. Es scheint auch, dass alle angehängten Zeichen nach dem Punkt ignoriert werden, an dem base64.b64decode (...) die Dekodierung beendet, z. B. von an = als viertes in einer Gruppe.

Wie in mehreren Kommentaren oben erwähnt, sind am Ende der Eingabedaten entweder null oder eins oder zwei = s Auffüllung erforderlich, wenn der Wert [Anzahl der analysierten Zeichen bis zu diesem Punkt Modulo 4] 0 oder 3 ist. bzw. 2. Ab den obigen Punkten 3. und 4. werden durch Anhängen von zwei oder mehr = s an die Eingabedaten alle Probleme mit [falscher Auffüllung] in diesen Fällen behoben.

Die Decodierung kann jedoch nicht den Fall behandeln, in dem die [Gesamtzahl der analysierten Zeichen Modulo 4] 1 ist, da mindestens zwei codierte Zeichen erforderlich sind, um das erste decodierte Byte in einer Gruppe von drei decodierten Bytes darzustellen. In nicht beschädigten codierten Eingabedaten tritt dieser Fall [N modulo 4] = 1 nie auf, aber da das OP feststellte, dass möglicherweise Zeichen fehlen, kann dies hier passieren. Aus diesem Grund funktioniert das Anhängen von = s nicht immer, und das Anhängen von A == funktioniert, wenn das Anhängen von == nicht funktioniert. NB Die Verwendung von [A] ist alles andere als willkürlich: Es werden nur gelöschte (Null-) Bits zu den decodierten hinzugefügt, was möglicherweise korrekt ist oder nicht, aber dann ist das Objekt hier nicht die Richtigkeit, sondern die Vervollständigung durch base64.b64decode (...) ohne Ausnahmen .

Was wir aus dem OP und insbesondere aus nachfolgenden Kommentaren wissen, ist

  • Es wird vermutet, dass in den Base64-codierten Eingabedaten Daten (Zeichen) fehlen
  • Die Base64-Codierung verwendet die Standard-64-Platzwerte plus Auffüllung: AZ; az; 0-9; +; /; = ist Polsterung. Dies wird durch die Tatsache, dass es openssl enc ...funktioniert, bestätigt oder zumindest nahegelegt .

Annahmen

  • Die Eingabedaten enthalten nur 7-Bit-ASCII-Daten
  • Die einzige Art der Beschädigung sind verschlüsselte Eingabedaten
  • Das OP kümmert sich zu keinem Zeitpunkt danach um decodierte Ausgabedaten, die fehlenden codierten Eingabedaten entsprechen

Github

Hier ist ein Wrapper zum Implementieren dieser Lösung:

https://github.com/drbitboy/missing_b64

Brian Carcich
quelle
1

Ein falscher Auffüllfehler wird verursacht, weil manchmal auch Metadaten in der codierten Zeichenfolge vorhanden sind. Wenn Ihre Zeichenfolge ungefähr so ​​aussieht: 'data: image / png; base64, ... base 64 stuff ....', müssen Sie die erste entfernen Teil vor dem Dekodieren.

Sagen Sie, wenn Sie eine Image Base64-codierte Zeichenfolge haben, versuchen Sie es unter dem folgenden Snippet.

from PIL import Image
from io import BytesIO
from base64 import b64decode
imagestr = 'data:image/png;base64,...base 64 stuff....'
im = Image.open(BytesIO(b64decode(imagestr.split(',')[1])))
im.save("image.png")
Sam
quelle
0

Fügen Sie einfach zusätzliche Zeichen wie "=" oder ein anderes hinzu und machen Sie ein Vielfaches von 4, bevor Sie versuchen, den Zielzeichenfolgenwert zu dekodieren. Etwas wie;

if len(value) % 4 != 0: #check if multiple of 4
    while len(value) % 4 != 0:
        value = value + "="
    req_str = base64.b64decode(value)
else:
    req_str = base64.b64decode(value)
Syed Mauze Rehan
quelle
0

Falls dieser Fehler von einem Webserver kam: Versuchen Sie, Ihren Beitragswert mit einer URL zu codieren. Ich habe über "curl" gepostet und festgestellt, dass ich meinen base64-Wert nicht url-codiert habe, sodass Zeichen wie "+" nicht maskiert wurden, sodass die URL-Decodierungslogik des Webservers automatisch url-decodierte und + in Leerzeichen konvertierte.

"+" ist ein gültiges base64-Zeichen und möglicherweise das einzige Zeichen, das durch eine unerwartete URL-Dekodierung entstellt wird.

Curtis Yallop
quelle
0

In meinem Fall trat dieser Fehler beim Parsen einer E-Mail auf. Ich habe den Anhang als base64-String erhalten und ihn über re.search extrahiert. Schließlich gab es am Ende einen seltsamen zusätzlichen Teilstring.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

Als ich --_=ic0008m4wtZ4TqBFd+sXC8--die Zeichenfolge löschte und entfernte, wurde das Parsen korrigiert.

Mein Rat ist also, sicherzustellen, dass Sie eine korrekte base64-Zeichenfolge dekodieren.

Daniil Mashkin
quelle
0

Du solltest benutzen

base64.b64decode(b64_string, ' /')

Standardmäßig sind die Altchars '+/'.

Quoc
quelle
1
Das funktioniert in Python 3.7 nicht. assert len ​​(altchars) == 2, repr (altchars)
Dat TT
0

Ich bin auch auf dieses Problem gestoßen und nichts hat funktioniert. Ich habe es endlich geschafft, die Lösung zu finden, die für mich funktioniert. Ich hatte Inhalte in base64 gezippt und dies geschah mit 1 von einer Million Datensätzen ...

Dies ist eine Version der von Simon Sapin vorgeschlagenen Lösung.

Falls der Abstand 3 fehlt, entferne ich die letzten 3 Zeichen.

Anstelle von "0gA1RD5L / 9AUGtH9MzAwAAA =="

Wir erhalten "0gA1RD5L / 9AUGtH9MzAwAA"

        missing_padding = len(data) % 4
        if missing_padding == 3:
            data = data[0:-3]
        elif missing_padding != 0:
            print ("Missing padding : " + str(missing_padding))
            data += '=' * (4 - missing_padding)
        data_decoded = base64.b64decode(data)   

Nach dieser Antwort Trailing Wie in base64 ist der Grund Nullen. Aber ich habe immer noch keine Ahnung, warum der Encoder das durcheinander bringt ...

Mitzi
quelle