Geçerli bir Base64 kodlu dizeyi kontrol etme


127

C # 'da bir dizenin Base 64 kodlu olup olmadığını görmenin, onu dönüştürmeye çalışmak ve bir hata olup olmadığını görmek dışında bir yolu var mı? Bunun gibi kod kodum var:

// Convert base64-encoded hash value into a byte array.
byte[] HashBytes = Convert.FromBase64String(Value);

Değer geçerli bir temel 64 dizesi değilse meydana gelen "Base-64 dizesinde geçersiz karakter" özel durumundan kaçınmak istiyorum. Bir istisnayı ele almak yerine sadece kontrol edip yanlış döndürmek istiyorum çünkü bazen bu değerin temel 64 dizesi olmayacağını umuyorum. Convert.FromBase64String işlevini kullanmadan önce kontrol etmenin bir yolu var mı?

Teşekkürler!

Güncelleme:
Tüm yanıtlarınız için teşekkürler. İşte şimdiye kadar kullanabileceğiniz bir uzantı yöntemi, dizenizin istisnasız Convert.FromBase64String'i geçeceğinden emin gibi görünüyor. Görünüşe göre .NET, 64 tabanına dönüştürürken tüm son ve son boşlukları yok sayıyor, bu nedenle "1234" geçerli ve "1234" de öyle.

public static bool IsBase64String(this string s)
{
    s = s.Trim();
    return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

}

Test performansına karşı yakalama ve istisnayı merak edenler için, çoğu durumda bu temel 64 şey için kontrol etmek, belirli bir uzunluğa ulaşana kadar istisnayı yakalamaktan daha hızlıdır. Uzunluk ne kadar küçükse o kadar hızlıdır

Bilimsel olmayan testimde: 100.000 - 110000 karakter uzunluğu için 10000 yineleme için ilk önce test etmek 2.7 kat daha hızlıydı.

Toplam 16.000 test için 1-16 karakter uzunluğundaki karakterlerin 1000 yinelemesinde 10.9 kat daha hızlıydı.

İstisna tabanlı yöntemle test etmenin daha iyi hale geldiği bir nokta olduğundan eminim. Bunun hangi noktada olduğunu bilmiyorum.


1
Çekin ne kadar "eksiksiz" olmasını istediğinize bağlıdır. Diğerlerinin yanıtladığı gibi bir normal ifade kullanarak bazı ön doğrulamayı kullanabilirsiniz, ancak tek gösterge bu değildir. base64 kodlaması, bazı durumlarda =işareti kullanarak dolgu gerektirir . Dolgu yanlışsa, girdi bir ifadeyle eşleşse bile hata verecektir.
vcsjones

1
Koşulunuz yalnızca base64 dizelerini karşılamıyor. Dizeyi düşünün \n\fLE16- yönteminiz bunun için yanlış bir pozitif sonuç verecektir. Kusursuz bir yöntem okuyan ve arayan herkes için; FormatException'ı yakalamanızı veya spesifikasyona uygun bir RegEx kullanmanızı tavsiye ederim, bkz. Stackoverflow.com/questions/475074/… .
null yapılabilir

Yukarıdaki yöntem false döndürürse, dizeyi doğru uzunluğa nasıl doldurabilirim?
Paul Alexander

3
@"^[a-zA-Z0-9\+/]*={0,2}$"
Normal İfadenin şöyle

Bu çözüm güvenilir değil. Aynı 4 karakter dizisi eklerseniz başarısız olur.
Bettimms

Yanıtlar:


49

Bir Base64 dizesini tanımak oldukça kolaydır, çünkü yalnızca karakterlerden oluşacaktır 'A'..'Z', 'a'..'z', '0'..'9', '+', '/'ve uzunluğu 4'ün katı yapmak için genellikle sonunda üçe kadar '=' ile doldurulur. Ancak bunları karşılaştırmak yerine, siz ' Olursa, istisnayı görmezden gelmek daha iyi olur.


1
Ben doğru yolda olduğunu düşünüyorum. Bazı testler yaptım ve görünen o ki 3 yerine 4'ün katları.
Chris Mullins

1
Başarılı bir kodlama için kodlama sırasında uzunluğunun 3'ün katı olması gerekir! Bunun için üzgünüm ... ve evet, haklısın ... Kodlanmış dizenin uzunluğu 4'ün katıdır. Bu yüzden 3 '=' 'e kadar doldururuz.
Anirudh Ramanathan

4
Doğru olarak işaretlendi çünkü birden çok şeyden ilk önce siz bahsediyorsunuz. Sorumu bir çözüm uygulamasıyla güncelledim, onunla ilgili herhangi bir sorun görürseniz bana bildirin.
Chris Mullins

48

Convert. TryFromBase64String'i C # 7.2'den kullanın

public static bool IsBase64String(string base64)
{
   Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
   return Convert.TryFromBase64String(base64, buffer , out int bytesParsed);
}

1
Bunun bir şey olduğunu bilmiyordum. Sanırım c # 7.2 kullanılıyorsa yeni cevap bu olmalı
Chris Mullins

4
Yalnızca .NET Core 2.1+ veya .NET Standard 2.1+ ile çalışır
Cyrus

C # bir derleyicidir ve TryFromBase64String, .NET çerçevesinin
API'sidir

Bu burada, dolu olmayan dizeler için return false olacak bir düzeltmedir: Convert.TryFromBase64String(base64.PadRight(base64.Length / 4 * 4 + (base64.Length % 4 == 0 ? 0 : 4), '='), new Span<byte>(new byte[base64.Length]), out _). Teşekkür ederim.
rvnlord

44

Bir istisna yakalamak istemediğini söylediğini biliyorum. Ancak, bir istisna yakalamak daha güvenilir olduğu için, devam edip bu cevabı göndereceğim.

public static bool IsBase64(this string base64String) {
     // Credit: oybek https://stackoverflow.com/users/794764/oybek
     if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
        || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
        return false;

     try{
         Convert.FromBase64String(base64String);
         return true;
     }
     catch(Exception exception){
     // Handle the exception
     }
     return false;
}

Güncelleme: Güvenilirliği daha da artırmak için oybek sayesinde durumu güncelledim .


1
base64String.Containsbirden çok kez çağrı yapmak base64String, büyük bir dizge olması durumunda düşük performansa neden olabilir .
NucS

@NucS Haklısın, burada derlenmiş bir normal ifade kullanabiliriz.
harsimranb

1
Eğer kontrol edebilirsiniz base64String== null || base64String.Length == 0ilestring.IsNullOrEmpty(base64String)
Danıël Tulp

Bir Base64'ün sorunsuz bir boşluk (örneğin satır sonları) içerebileceğini unutmayın. Ayrıştırıcı tarafından yok sayılırlar.
Timothy

2
.NET kaynak koduna erişimimiz olduğundan, artık FromBase64String () işlevinin tüm bu kontrolleri yaptığını görebiliriz. reference.microsoft.com/#mscorlib/system/… Geçerli bir base64 dizesiyse, onu iki kez kontrol ediyorsunuz. Belki istisnayı denemek / yakalamak daha iyidir.
iheartcsharp

16

Normal ifadenin şöyle olması gerektiğine inanıyorum:

    Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,2}$")

Yalnızca bir veya iki sondaki '=' işaretiyle eşleşiyor, üç değil.

skontrol edilecek dizge olmalıdır. ad Regexalanının bir parçasıdır System.Text.RegularExpressions.


2
dizgi uzunluğunun 4 = 0 mod olup olmadığını kontrol etmez
calingasan

7

Neden istisnayı yakalayıp False döndürmüyorsunuz?

Bu, genel durumda ek yükü önler.


1
Bu alışılmadık bir durum, sanırım değeri nerede kullanacağım, temel 64 olmayacak, bu yüzden istisnanın ek yükünden kaçınmayı tercih ederim. Önceden kontrol etmek çok daha hızlıdır. Açık metin şifrelerinden miras aldığım eski bir sistemi karma değerlere dönüştürmeye çalışıyorum.
Chris Mullins

2
Normal ifadeler asla Tyler'ın önerdiğinden daha hızlı değildir.
Vincent Koeman

Yazımın altındaki yoruma bakın. Bence, çalıştığınız dizelerin uzunluğuna bağlı olarak, özellikle karma şifreler gibi küçük dizeler için ilk önce test etmek daha hızlı olabilir. Normal ifadeye ulaşmak için dizenin 4'ün katı olması gerekir ve daha sonra küçük bir dizedeki normal ifade, çok büyük bir dizeden daha hızlıdır.
Chris Mullins

2
Kusursuz bir dünyada, iş mantığı tasarlanmış veya istisnalar attığı bilinen kod yazılmamalıdır. İstisna dene / yakala bloğu bir karar bloğu olarak kullanılmak için çok pahalıdır.
Ismail Hawayel

7

Sırf bütünlük adına, biraz uygulama sağlamak istiyorum. Genel olarak, Regex pahalı bir yaklaşımdır, özellikle dizge büyükse (bu, büyük dosyalar aktarılırken olur). Aşağıdaki yaklaşım ilk önce en hızlı algılama yollarını dener.

public static class HelperExtensions {
    // Characters that are used in base64 strings.
    private static Char[] Base64Chars = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    /// <summary>
    /// Extension method to test whether the value is a base64 string
    /// </summary>
    /// <param name="value">Value to test</param>
    /// <returns>Boolean value, true if the string is base64, otherwise false</returns>
    public static Boolean IsBase64String(this String value) {

        // The quickest test. If the value is null or is equal to 0 it is not base64
        // Base64 string's length is always divisible by four, i.e. 8, 16, 20 etc. 
        // If it is not you can return false. Quite effective
        // Further, if it meets the above criterias, then test for spaces.
        // If it contains spaces, it is not base64
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;

        // 98% of all non base64 values are invalidated by this time.
        var index = value.Length - 1;

        // if there is padding step back
        if (value[index] == '=')
            index--;

        // if there are two padding chars step back a second time
        if (value[index] == '=')
            index--;

        // Now traverse over characters
        // You should note that I'm not creating any copy of the existing strings, 
        // assuming that they may be quite large
        for (var i = 0; i <= index; i++) 
            // If any of the character is not from the allowed list
            if (!Base64Chars.Contains(value[i]))
                // return false
                return false;

        // If we got here, then the value is a valid base64 string
        return true;
    }
}

DÜZENLE

Sam'in önerdiği gibi , kaynak kodunu da biraz değiştirebilirsiniz. Testlerin son adımı için daha iyi performans gösteren bir yaklaşım sağlar. Rutin

    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;

        // 1 - 9
        if (intValue >= 48 && intValue <= 57) 
            return false;

        // A - Z
        if (intValue >= 65 && intValue <= 90) 
            return false;

        // a - z
        if (intValue >= 97 && intValue <= 122) 
            return false;

        // + or /
        return intValue != 43 && intValue != 47;
    } 

if (!Base64Chars.Contains(value[i]))satırı ile değiştirmek için kullanılabilirif (IsInvalid(value[i]))

Sam'in geliştirmeleriyle birlikte tam kaynak kodu şöyle görünecektir (açıklık için yorumlar kaldırılmıştır)

public static class HelperExtensions {
    public static Boolean IsBase64String(this String value) {
        if (value == null || value.Length == 0 || value.Length % 4 != 0
            || value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
            return false;
        var index = value.Length - 1;
        if (value[index] == '=')
            index--;
        if (value[index] == '=')
            index--;
        for (var i = 0; i <= index; i++)
            if (IsInvalid(value[i]))
                return false;
        return true;
    }
    // Make it private as there is the name makes no sense for an outside caller
    private static Boolean IsInvalid(char value) {
        var intValue = (Int32)value;
        if (intValue >= 48 && intValue <= 57)
            return false;
        if (intValue >= 65 && intValue <= 90)
            return false;
        if (intValue >= 97 && intValue <= 122)
            return false;
        return intValue != 43 && intValue != 47;
    }
}

4

Cevap, dizenin kullanımına bağlı olmalıdır. Çeşitli posterler tarafından önerilen sözdizimine göre "geçerli base64" olabilecek birçok dizge vardır, ancak bu, istisnasız, gereksiz olarak "doğru" şekilde kodunu çözebilir. Örnek: 8char dizesi Portlandgeçerli Base64'tür. Bunun Base64 olduğunu belirtmenin anlamı nedir? Sanırım bir noktada bu dizenin Base64 kodunun çözülüp çözülmemesi gerektiğini bilmek isteyeceksiniz.

Benim durumumda, aşağıdaki gibi düz metin halinde olabilecek Oracle bağlantı dizelerim var:

Data source=mydb/DBNAME;User Id=Roland;Password=.....`

veya base64 gibi

VXNlciBJZD1sa.....................................==

Sadece noktalı virgül olup olmadığını kontrol etmeliyim, çünkü bu onun base64 OLMADIĞINI kanıtlıyor, ki bu elbette yukarıdaki yöntemlerden daha hızlı.


Kabul ediyorum, vaka özellikleri ayrıca bazı ek hızlı kontroller de getirir. Düz metin bağlantı dizesi ile base64 kodlaması gibi.
Oybek

2

Knibb Yüksek futbol kuralları!

Bu nispeten hızlı ve doğru olmalı, ancak bunu kapsamlı bir testten geçirmediğimi kabul ediyorum, sadece birkaç tane.

Pahalı istisnaları, regex'i önler ve ayrıca doğrulama için ascii aralıkları kullanmak yerine bir karakter setinde döngü yapmaktan kaçınır.

public static bool IsBase64String(string s)
    {
        s = s.Trim();
        int mod4 = s.Length % 4;
        if(mod4!=0){
            return false;
        }
        int i=0;
        bool checkPadding = false;
        int paddingCount = 1;//only applies when the first is encountered.
        for(i=0;i<s.Length;i++){
            char c = s[i];
            if (checkPadding)
            {
                if (c != '=')
                {
                    return false;
                }
                paddingCount++;
                if (paddingCount > 3)
                {
                    return false;
                }
                continue;
            }
            if(c>='A' && c<='z' || c>='0' && c<='9'){
                continue;
            }
            switch(c){ 
              case '+':
              case '/':
                 continue;
              case '=': 
                 checkPadding = true;
                 continue;
            }
            return false;
        }
        //if here
        //, length was correct
        //, there were no invalid characters
        //, padding was correct
        return true;
    }

2
public static bool IsBase64String1(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return false;
            }
            try
            {
                Convert.FromBase64String(value);
                if (value.EndsWith("="))
                {
                    value = value.Trim();
                    int mod4 = value.Length % 4;
                    if (mod4 != 0)
                    {
                        return false;
                    }
                    return true;
                }
                else
                {

                    return false;
                }
            }
            catch (FormatException)
            {
                return false;
            }
        }

neden önce dönüştürmeyi
Snr

@Snr haklısın. Sanırım değiştirmesi gereken şey bu: if (value.EndsWith ("=")) {value = value.Trim (); int mod4 = değer.Uzunluk% 4; eğer (mod4! = 0) {yanlış dönüş; } Convert.FromBase64String (değer); doğruya dön; } else {yanlış dönüş; }
Wajid khan

2

Dönüştürme yöntemini tekrar çağırmama gerek kalmaması için böyle kullanacağım

   public static bool IsBase64(this string base64String,out byte[] bytes)
    {
        bytes = null;
        // Credit: oybek http://stackoverflow.com/users/794764/oybek
        if (string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0
           || base64String.Contains(" ") || base64String.Contains("\t") || base64String.Contains("\r") || base64String.Contains("\n"))
            return false;

        try
        {
             bytes=Convert.FromBase64String(base64String);
            return true;
        }
        catch (Exception)
        {
            // Handle the exception
        }

        return false;
    }

2

Çözün, yeniden kodlayın ve sonucu orijinal dizeyle karşılaştırın

public static Boolean IsBase64(this String str)
{
    if ((str.Length % 4) != 0)
    {
        return false;
    }

    //decode - encode and compare
    try
    {
        string decoded = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(str));
        string encoded = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(decoded));
        if (str.Equals(encoded, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    catch { }
    return false;
}

1

Imho, bu gerçekten mümkün değil. Gönderilen tüm çözümler "test" ve benzeri dizeler için başarısız olur . 4'e bölünebiliyorlarsa, null veya boş değillerse ve geçerli bir base64 karakter iseler, tüm testleri geçeceklerdir. Bu pek çok dizge olabilir ...

Bu nedenle , bunun 64 kodlu bir taban olduğunu bilmekten başka gerçek bir çözüm yoktur . Bulduğum şey şu:

if (base64DecodedString.StartsWith("<xml>")
{
    // This was really a base64 encoded string I was expecting. Yippie!
}
else
{
    // This is gibberish.
}

Çözülen dizenin belirli bir yapıyla başlamasını bekliyorum, bu yüzden kontrol ediyorum.


0

Elbette. Sadece emin her karakter içindedir yapmak a-z, A-Z, 0-9, /, veya +birlikte, ve dize biter ==. (En azından, bu en yaygın Base64 uygulamasıdır. Son iki karakterden farklı /veya +son iki karakter için karakterler kullanan bazı uygulamalar bulabilirsiniz .)


Anladıysam, bitiş karakterleri kodlanan metnin son uzunluğuna bağlıdır. Dolayısıyla, kodlanan metin% 4 uzunluğunda değilse, o zaman '=' eklenir.
Rafael Diego Nicoletti

0

Evet, Base64 ikili verileri sınırlı bir karakter kümesi kullanarak ASCII dizelerine kodladığından, bu normal ifadeyle kolayca kontrol edebilirsiniz:

/ ^ [A-Za-Z0-9 \ = \ + \ / \ s \ n] + $ / sn

bu, dizenin yalnızca AZ, az, 0-9, '+', '/', '=' ve boşluk içermesini sağlar.


Bu her zaman kesin bir anlatım şekli değildir. Base64 =, sondaki karakteri kullanarak sizin için biraz dolgu yapar . Bu dolgu geçersizse, normal ifadenizle eşleşse bile doğru bir base64 kodlaması değildir. =Sonunda 1 veya 2 olan bir 64 temel dize bularak , bunları kaldırarak ve kodunu çözmeye çalışarak bunu gösterebilirsiniz .
vcsjones

Bence OP'nin yasadışı karakterler için tuzak kurmasını istediğine inanıyorum, eğer dizge Base64 yasal ise değil. İkincisi ise, haklısınız, ancak Base64'teki dolgu hatalarının istisnalar kullanılarak yakalanması daha kolay.
Rob Raisch

Doğru değil, en azından base64 ayrıştırıcısının .Net sürümü dolguyu tamamen yok sayıyor.
Jay

0

İşi yapmak için bir normal ifade oluşturmanızı öneririm. Bunun gibi bir şeyi kontrol etmeniz gerekecek: [a-zA-Z0-9 + / =] Ayrıca dizenin uzunluğunu da kontrol etmeniz gerekecek. Bundan emin değilim, ancak bir şey kırpılırsa ("=" dolgu dışında) patlayacağından oldukça eminim.

Ya da daha iyisi bu stackoverflow sorusuna bakın


0

Kullanıcının bir <canvas>öğede bazı görüntü işlemleri yapmasına ve ardından alınan görüntüyü .toDataURL()arka uca göndermesine izin verdiğim çok benzer bir gereksinimim vardı . Görüntüyü kaydetmeden önce bir miktar sunucu doğrulaması yapmak istedim ve ValidationAttributediğer yanıtlardan bazı kodları kullanarak bir kod uyguladım :

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class Bae64PngImageAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null || string.IsNullOrWhiteSpace(value as string))
            return true; // not concerned with whether or not this field is required
        var base64string = (value as string).Trim();

        // we are expecting a URL type string
        if (!base64string.StartsWith("data:image/png;base64,"))
            return false;

        base64string = base64string.Substring("data:image/png;base64,".Length);

        // match length and regular expression
        if (base64string.Length % 4 != 0 || !Regex.IsMatch(base64string, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None))
            return false;

        // finally, try to convert it to a byte array and catch exceptions
        try
        {
            byte[] converted = Convert.FromBase64String(base64string);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}

Gördüğünüz gibi <canvas>, kullanırken döndürülen varsayılan değer olan bir resim / png türü dize bekliyorum .toDataURL().


0

Base64 veya normal dizeyi kontrol edin

public bool IsBase64Encoded (Dize dizesi)

{

try

{
    // If no exception is caught, then it is possibly a base64 encoded string
    byte[] data = Convert.FromBase64String(str);
    // The part that checks if the string was properly padded to the
    // correct length was borrowed from d@anish's solution
    return (str.Replace(" ","").Length % 4 == 0);
}
catch
{
    // If exception is caught, then it is not a base64 encoded string
   return false;
}

}


0

Tüm yanıtlar, sonuçlarının% 100 doğru olmasını sağlayan 1 işlevde sindirildi.


1) Aşağıdaki işlevi kullanın:

    string encoded = "WW91ckJhc2U2NHN0cmluZw==";
    msgbox("Is string base64=" + IsBase64(encoded));

2) Fonksiyon aşağıdadır:

  public bool IsBase64(string base64String)
    {
        try
        {
            if (!base64String.Length < 1)
            {
                if (!base64String.Equals(Convert.ToBase64String(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(Convert.FromBase64String(base64String)))), StringComparison.InvariantCultureIgnoreCase) & !System.Text.RegularExpressions.Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,2}$"))
                {
                    return false;
                    return;
                }
                if ((base64String.Length % 4) != 0 || string.IsNullOrEmpty(base64String) || base64String.Length % 4 != 0 || base64String.Contains(" ") || base64String.Contains(Constants.vbTab) || base64String.Contains(Constants.vbCr) || base64String.Contains(Constants.vbLf))
                {
                    return false;
                    return;
                }
            }
            else
            {
                return false;
                return;
            }

            return true;
            return;
        }
        catch (FormatException ex)
        {
            return false;
            return;
        }
    }

-1

Düzenli İfade Kontrolü Fikrini beğendim. Normal İfadeler hızlı olabilir ve zaman zaman kodlama ek yükünü kaydedebilir. Orijinal soruşturmada tam da bunu yapan bir güncelleme vardı. Yine de, dizelerin boş olmayacağını asla varsayamayacağımı anladım. Uzantı işlevini, kaynak dizesinde boş veya yalnızca boşluk karakteri olup olmadığını kontrol etmek için genişletirdim.

    public static bool IsBase64String(this string s)
    {
        if (string.IsNullOrWhiteSpace(s))
            return false;

        s = s.Trim();
        return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);

    }

Bu başarısız olur. 'Aaaa' gibi aynı 4 karaktere sahip bir dize geçirmeyi deneyin.
Bettimms
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.