简单的MIME Base64解码器

时间:2011-07-11 18:59:03

标签: delphi email base64 mime thunderbird

我正在寻找一种简单的方法,不需要使用像Indy这样的东西来解码包含来自Thunderbird的消息的Base64。

示例:

WW91ciBtZXNzYWdlDQoNCiAgVG86ICAgICAgeHh4QHh4eC5jb20NCiAgU3ViamVjdDogSXh4eA0KICBTZW50OiAgICBUaHUsIDIyIE9jdCAyMDA5IDAxOjE0OjM0IC0wNDAwDQoNCmRpZCBub3QgcmVhY2ggdGhlIGZvbGxvd2luZyByZWNpcGllbnQocyk6DQoNCnh4eEBneHh4IG9uIFR1ZSwgMjcgT2N0IDIwMDkgMDE6NDA6NDQgLTA0MDANCiAgICBUaGUgZS1tYWlsIHN5c3RlbSB3YXMgdW5hYmxlIHRvIGRlbGl2ZXIgdGhlIG1lc3NhZ2UsIGJ1dCBkaWQgbm90DQpyZXBvcnQgYSBzcGVjaWZpYyByZWFzb24uICBDaGVjayB0aGUgYWRkcmVzcyBhbmQgdHJ5IGFnYWluLiAgSWYgaXQgc3RpbGwNCmZhaWxzLCBjb250YWN0IHlvdXIgc3lzdGVtIGFkbWluaXN0cmF0b3IuDQogICAgPCBDaW54eHguY29tICM1LjAuMCBzbXRwOyA1LjQuNyAtIERlbGl2ZXJ5IGV4cGlyZWQNCihtZXNzYWdlIHRvbyBvbGQpICd0aW1lb3V0JyAoZGVsaXZlcnkgYXR0ZW1wdHM6IDApPg==

变为: 您的留言

  To:      xxx@xxx.com
  Subject: Ixxx
  Sent:    Thu, 22 Oct 2009 01:14:34 -0400

did not reach the following recipient(s):

xxx@gxxx on Tue, 27 Oct 2009 01:40:44 -0400
    The e-mail system was unable to deliver the message, but did not
report a specific reason.  Check the address and try again.  If it still
fails, contact your system administrator.
    < Cinxxx.com #5.0.0 smtp; 5.4.7 - Delivery expired
(message too old) 'timeout' (delivery attempts: 0)>

编辑:似乎MIME Base64行的最大长度可以是76.我必须解码每个76位长的行,而不是一次解码整个消息。感谢。

4 个答案:

答案 0 :(得分:8)

自Delphi 6以来,Delphi已包含EncdDecd单元,其中包含DecodeString函数,该函数将包含Input文本的base64字符串解码为Result带有实际解码数据的字符串(可能是二进制):

function DecodeString(const Input: string): string;

自Delphi 2009以来,它还包含DecodeBase64函数,该函数将Input AnsiString解码为TBytes的二进制Result

function  DecodeBase64(const Input: AnsiString): TBytes;

除了我给出的earlier Delphi/PHP base64 answer之外,我现在写了一个小单元测试,显示了DecodeString和EncodeString的工作原理:

unit Base64TestCaseUnit;

interface

uses
  Classes, SysUtils, TestFrameWork, Generics.Collections;

{
 base64 test vectors: http://tools.ietf.org/html/rfc4648
   BASE64("") = ""
   BASE64("f") = "Zg=="
   BASE64("fo") = "Zm8="
   BASE64("foo") = "Zm9v"
   BASE64("foob") = "Zm9vYg=="
   BASE64("fooba") = "Zm9vYmE="
   BASE64("foobar") = "Zm9vYmFy"
}
const
  Key_ = '';
  Value_ = '';
  Key_f = 'f';
  Value_f = 'Zg==';
  Key_fo = 'fo';
  Value_fo = 'Zm8=';
  Key_foo = 'foo';
  Value_foo = 'Zm9v';
  Key_foob = 'foob';
  Value_foob = 'Zm9vYg==';
  Key_fooba = 'fooba';
  Value_fooba = 'Zm9vYmE=';
  Key_foobar = 'foobar';
  Value_foobar = 'Zm9vYmFy';
// binary test vector
  Key_binary = #1#2#3#4;
  Value_binary = 'AQIDBA==';

type
  TStringStringDictionary = TDictionary<string, string>;
  TBase64TestCase = class(TTestCase)
  strict private
    FBase64VectorsDictionary: TStringStringDictionary;
  strict protected
    procedure DecodeTestOneKey(const ExpectedKey: string); virtual;
    procedure EncodeTestOneKey(const ActualKey: string); virtual;
    property Base64VectorsDictionary: TStringStringDictionary read FBase64VectorsDictionary;
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  published
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_binary;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_f;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_fo;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_foo;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_foob;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_fooba;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_DecodeString_Test_foobar;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_binary;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_f;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_fo;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_foo;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_foob;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_fooba;
    {$IFDEF CLR}[Test]{$ENDIF}
    procedure Base64_EncodeString_Test_foobar;
  end;

implementation

uses
  EncdDecd;

procedure TBase64TestCase.Base64_DecodeString_Test_;
begin
  DecodeTestOneKey(Key_);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_binary;
begin
  DecodeTestOneKey(Key_binary);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_f;
begin
  DecodeTestOneKey(Key_f);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_fo;
begin
  DecodeTestOneKey(Key_fo);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_foo;
begin
  DecodeTestOneKey(Key_foo);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_foob;
begin
  DecodeTestOneKey(Key_foob);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_fooba;
begin
  DecodeTestOneKey(Key_fooba);
end;

procedure TBase64TestCase.Base64_DecodeString_Test_foobar;
begin
  DecodeTestOneKey(Key_foobar);
end;

procedure TBase64TestCase.SetUp;
begin
  FBase64VectorsDictionary := TStringStringDictionary.Create();
  FBase64VectorsDictionary.Add(Key_, Value_);
  FBase64VectorsDictionary.Add(Key_f, Value_f);
  FBase64VectorsDictionary.Add(Key_fo, Value_fo);
  FBase64VectorsDictionary.Add(Key_foo, Value_foo);
  FBase64VectorsDictionary.Add(Key_foob, Value_foob);
  FBase64VectorsDictionary.Add(Key_fooba, Value_fooba);
  FBase64VectorsDictionary.Add(Key_foobar, Value_foobar);
  FBase64VectorsDictionary.Add(Key_binary, Value_binary);
end;

procedure TBase64TestCase.TearDown;
begin
  FBase64VectorsDictionary.Free();
  FBase64VectorsDictionary := nil;
end;

procedure TBase64TestCase.Base64_EncodeString_Test_;
begin
  EncodeTestOneKey(Key_);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_binary;
begin
  EncodeTestOneKey(Key_binary);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_f;
begin
  EncodeTestOneKey(Key_f);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_fo;
begin
  EncodeTestOneKey(Key_fo);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_foo;
begin
  EncodeTestOneKey(Key_foo);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_foob;
begin
  EncodeTestOneKey(Key_foob);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_fooba;
begin
  EncodeTestOneKey(Key_fooba);
end;

procedure TBase64TestCase.Base64_EncodeString_Test_foobar;
begin
  EncodeTestOneKey(Key_foobar);
end;

procedure TBase64TestCase.DecodeTestOneKey(const ExpectedKey: string);
var
  ActualKey: string;
  ExpectedValue: string;
begin
  ExpectedValue := FBase64VectorsDictionary[ExpectedKey];
  ActualKey := DecodeString(ExpectedValue);
  Self.CheckEquals(ExpectedKey, ActualKey, Format('base64 decode of "%s" should be "%s"', [ExpectedValue, ExpectedKey]));
end;

procedure TBase64TestCase.EncodeTestOneKey(const ActualKey: string);
var
  ActualValue: string;
  ExpectedValue: string;
begin
  ExpectedValue := FBase64VectorsDictionary[ActualKey];
  ActualValue := EncodeString(ActualKey);
  Self.CheckEquals(ExpectedValue, ActualValue, Format('base64 of "%s" should be "%s"', [ActualKey, ExpectedValue]));
end;

initialization
  RegisterTest('', TBase64TestCase.Suite);
end.

答案 1 :(得分:5)

解码并不难编码,但这里有一个博客,里面有一些Delphi的示例代码

http://www.delphifaq.net/how-to-base-64-mime-encode-and-decode-a-string/

基本思想是有64个字符,每个字符代表6位的位模式。用表查找将字符转回6位,然后取出你的位列表,然后将它们分成8位块来制作字节。

答案 2 :(得分:1)

uses
  Math;

var
  Base64: array[0..63] of AnsiChar = (
    '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', '+', '/');

function IndexOfBase64(const C: AnsiChar): Integer;
begin
  for Result := Low(Base64) to High(Base64) do
    if Base64[Result] = C then
      EXIT;
  Result := -1;
end;

function DecodeBase64(Value: AnsiString): AnsiString;
var
  iC, iB: Integer;
  B: array of Integer;
  C: array[0..3] of Integer;
begin
  SetLength(B, Floor(Length(Value) / 4) * 3);

  iC := 1;
  iB := 0;
  while iC <= (Length(Value) - 3) do
  begin
    C[0] := IndexOfBase64(Value[iC]);
    C[1] := IndexOfBase64(Value[iC + 1]);
    C[2] := IndexOfBase64(Value[iC + 2]);
    C[3] := IndexOfBase64(Value[iC + 3]);

    B[iB]     := (C[0] shl 2) or (C[1] shr 4);
    B[iB + 1] := ((C[1] and 15) shl 4) or (C[2] shr 2);
    B[iB + 2] := ((C[2] and 3) shl 6) or C[3];

    Inc(iC, 4);
    Inc(iB, 3);
  end;

  SetLength(B, Length(B) - (Length(B) mod 16));

  for iB := 0 to High(B) do
    Result := Result + Chr(B[iB]);
end;

答案 3 :(得分:0)

如果您只想要一个现成的解决方案,请尝试DIMime